├── MANIFEST.in ├── docs ├── .gitignore ├── _static │ ├── logo.png │ ├── favicon.png │ ├── examples.json │ ├── examples.js │ └── d3.v2.min.js ├── _themes │ └── daft │ │ ├── theme.conf │ │ ├── static │ │ ├── code.css │ │ ├── daft.css │ │ └── normalize.css │ │ ├── relations.html │ │ ├── searchbox.html │ │ ├── search.html │ │ └── layout.html ├── examples │ ├── index.rst │ ├── nocircles.rst │ ├── wordy.rst │ ├── mrf.rst │ ├── badfont.rst │ ├── classic.rst │ ├── nogray.rst │ ├── weaklensing.rst │ ├── recursive.rst │ └── exoplanets.rst ├── api.rst ├── index.rst ├── gen_example.py ├── Makefile └── conf.py ├── images ├── logo.png ├── wordy.pdf ├── wordy.png ├── classic.pdf ├── classic.png ├── nogray.pdf ├── nogray.png ├── nocircles.pdf ├── nocircles.png ├── weaklensing.pdf └── weaklensing.png ├── .gitignore ├── examples ├── fixed.py ├── bca.py ├── nocircles.py ├── logo.py ├── wordy.py ├── gaia.py ├── mrf.py ├── badfont.py ├── classic.py ├── yike.py ├── nogray.py ├── thicklines.py ├── weaklensing.py ├── huey_p_newton.py ├── recursive.py ├── exoplanets.py ├── galex.py └── astronomy.py ├── HISTORY.rst ├── README.rst ├── LICENSE.rst ├── setup.py └── daft.py /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE.rst 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | !*.png 3 | _static/examples/*.png 4 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/logo.png -------------------------------------------------------------------------------- /images/wordy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/wordy.pdf -------------------------------------------------------------------------------- /images/wordy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/wordy.png -------------------------------------------------------------------------------- /images/classic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/classic.pdf -------------------------------------------------------------------------------- /images/classic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/classic.png -------------------------------------------------------------------------------- /images/nogray.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/nogray.pdf -------------------------------------------------------------------------------- /images/nogray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/nogray.png -------------------------------------------------------------------------------- /docs/_static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/docs/_static/logo.png -------------------------------------------------------------------------------- /images/nocircles.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/nocircles.pdf -------------------------------------------------------------------------------- /images/nocircles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/nocircles.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.pdf 4 | build 5 | dist 6 | *.egg-info 7 | *~ 8 | *.png 9 | -------------------------------------------------------------------------------- /docs/_static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/docs/_static/favicon.png -------------------------------------------------------------------------------- /images/weaklensing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/weaklensing.pdf -------------------------------------------------------------------------------- /images/weaklensing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceshine/daft/master/images/weaklensing.png -------------------------------------------------------------------------------- /docs/_themes/daft/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = daft.css 4 | pygments_style = sphinx 5 | 6 | [options] 7 | tagline = Awesome. 8 | -------------------------------------------------------------------------------- /examples/fixed.py: -------------------------------------------------------------------------------- 1 | import daft 2 | 3 | pgm = daft.PGM([2, 1], observed_style="outer", aspect=3.2) 4 | pgm.add_node(daft.Node("fixed", r"Fixed!", 1, 0.5, observed=True)) 5 | pgm.render().figure.savefig("fixed.png", dpi=150) 6 | -------------------------------------------------------------------------------- /docs/_themes/daft/static/code.css: -------------------------------------------------------------------------------- 1 | .highlight-python { 2 | border-left: 2px solid #ccc; 3 | padding-left: 10px; 4 | margin-left: -10px; 5 | } 6 | 7 | .highlight { 8 | background: none; 9 | } 10 | 11 | .highlight pre { 12 | font-size: 12px; 13 | } 14 | -------------------------------------------------------------------------------- /docs/examples/index.rst: -------------------------------------------------------------------------------- 1 | .. _examples: 2 | 3 | Examples 4 | -------- 5 | 6 | .. raw:: html 7 | 8 |
9 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /docs/_static/examples.json: -------------------------------------------------------------------------------- 1 | { 2 | "classic": [ 50, 10], 3 | "nogray": [ 50, 10], 4 | "nocircles": [230, 85], 5 | "wordy": [230, 105], 6 | "weaklensing": [200, 85], 7 | "mrf": [ 18, 18], 8 | "exoplanets": [117, 140], 9 | "badfont": [ 15, 10], 10 | "recursive": [247, 350] 11 | } 12 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | .. :changelog: 2 | 3 | 0.0.3 (2012-10-04) 4 | ++++++++++++++++++ 5 | 6 | - Fixed rendering bug when ``aspect`` was used with angles other than 45 7 | degrees between nodes. 8 | - Added global ``aspect`` property to the rendering context. 9 | - Fixed the treatment of redundant keyword arguments that matplotlib 10 | allows. 11 | 12 | 13 | 0.0.2 (2012-09-28) 14 | ++++++++++++++++++ 15 | 16 | - Initial release. 17 | -------------------------------------------------------------------------------- /docs/_themes/daft/relations.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /docs/_themes/daft/searchbox.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://raw.github.com/davidwhogg/daft/master/images/logo.png 2 | 3 | **Daft** is a Python package that uses `matplotlib `_ 4 | to render pixel-perfect *probabilistic graphical models* for publication 5 | in a journal or on the internet. With a short Python script and an intuitive 6 | model-building syntax you can design directed and undirected graphs and save 7 | them in any formats that matplotlib supports. 8 | 9 | Get more information at: `daft-pgm.org `_ 10 | ************************************************************** 11 | -------------------------------------------------------------------------------- /examples/bca.py: -------------------------------------------------------------------------------- 1 | from matplotlib import rc 2 | rc("font", family="serif", size=12) 3 | rc("text", usetex=True) 4 | import daft 5 | 6 | if __name__ == "__main__": 7 | pgm = daft.PGM([1.1, 3.15], origin=[0.45, 2.2]) 8 | pgm.add_node(daft.Node("a", r"$a$", 1, 5)) 9 | pgm.add_node(daft.Node("b", r"$b$", 1, 4)) 10 | pgm.add_node(daft.Node("c", r"$c_n$", 1, 3, observed=True)) 11 | pgm.add_plate(daft.Plate([0.5, 2.25, 1, 1.25], label=r"data $n$")) 12 | pgm.add_edge("a", "b") 13 | pgm.add_edge("b", "c") 14 | pgm.render() 15 | pgm.figure.savefig("bca.pdf") 16 | pgm.figure.savefig("bca.png", dpi=150) 17 | -------------------------------------------------------------------------------- /examples/nocircles.py: -------------------------------------------------------------------------------- 1 | """ 2 | Nodes can go free 3 | ================= 4 | 5 | You don't need to put ellipses or circles around your node contents, 6 | if you don't want to. 7 | 8 | """ 9 | 10 | from matplotlib import rc 11 | rc("font", family="serif", size=12) 12 | rc("text", usetex=True) 13 | 14 | import daft 15 | 16 | pgm = daft.PGM([3.6, 2.4], origin = [1.15, 0.8], node_ec="none") 17 | pgm.add_node(daft.Node("cloudy", r"cloudy", 3, 3)) 18 | pgm.add_node(daft.Node("rain", r"rain", 2, 2)) 19 | pgm.add_node(daft.Node("sprinkler", r"sprinkler", 4, 2)) 20 | pgm.add_node(daft.Node("wet", r"grass wet", 3, 1)) 21 | pgm.add_edge("cloudy", "rain") 22 | pgm.add_edge("cloudy", "sprinkler") 23 | pgm.add_edge("rain", "wet") 24 | pgm.add_edge("sprinkler", "wet") 25 | pgm.render() 26 | pgm.figure.savefig("nocircles.pdf") 27 | pgm.figure.savefig("nocircles.png", dpi=150) 28 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | API 4 | === 5 | 6 | .. module:: daft 7 | 8 | The PGM Object 9 | -------------- 10 | 11 | All daft scripts will start with the creation of a :class:`PGM` object. This 12 | object contains a list of :class:`Node` objects and :class:`Edge` objects 13 | connecting them. You can also specify rendering parameters and other default 14 | parameters when you initialize your :class:`PGM`. 15 | 16 | .. autoclass:: PGM 17 | :inherited-members: 18 | 19 | 20 | Nodes 21 | ----- 22 | 23 | .. autoclass:: Node 24 | :inherited-members: 25 | 26 | 27 | Edges 28 | ----- 29 | 30 | .. autoclass:: Edge 31 | :inherited-members: 32 | 33 | 34 | Plates 35 | ------ 36 | 37 | .. autoclass:: Plate 38 | :inherited-members: 39 | 40 | 41 | The Rendering Context 42 | --------------------- 43 | 44 | .. autoclass:: _rendering_context 45 | :inherited-members: 46 | -------------------------------------------------------------------------------- /examples/logo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | That's an awfully DAFT logo! 4 | 5 | """ 6 | 7 | from matplotlib import rc 8 | rc("font", family="serif", size=12) 9 | rc("text", usetex=True) 10 | 11 | import os 12 | import sys 13 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 14 | 15 | import daft 16 | 17 | 18 | if __name__ == "__main__": 19 | # Instantiate the PGM. 20 | pgm = daft.PGM((3.7, 0.7), origin=(0.15, 0.15)) 21 | 22 | pgm.add_node(daft.Node("d", r"$D$", 0.5, 0.5)) 23 | pgm.add_node(daft.Node("a", r"$a$", 1.5, 0.5, observed=True)) 24 | pgm.add_node(daft.Node("f", r"$f$", 2.5, 0.5)) 25 | pgm.add_node(daft.Node("t", r"$t$", 3.5, 0.5)) 26 | 27 | pgm.add_edge("d", "a") 28 | pgm.add_edge("a", "f") 29 | pgm.add_edge("f", "t") 30 | 31 | pgm.render() 32 | pgm.figure.savefig("logo.pdf") 33 | pgm.figure.savefig("logo.png", dpi=200, transparent=True) 34 | -------------------------------------------------------------------------------- /examples/wordy.py: -------------------------------------------------------------------------------- 1 | """ 2 | Nodes can contain words 3 | ======================= 4 | 5 | We here at **Daft** headquarters tend to put symbols (variable 6 | names) in our graph nodes. But you don't have to if you don't 7 | want to. 8 | 9 | """ 10 | 11 | from matplotlib import rc 12 | rc("font", family="serif", size=12) 13 | rc("text", usetex=True) 14 | 15 | import daft 16 | 17 | pgm = daft.PGM([3.6, 2.7], origin=[1.15, 0.65]) 18 | pgm.add_node(daft.Node("cloudy", r"cloudy", 3, 3, aspect=1.8)) 19 | pgm.add_node(daft.Node("rain", r"rain", 2, 2, aspect=1.2)) 20 | pgm.add_node(daft.Node("sprinkler", r"sprinkler", 4, 2, aspect=2.1)) 21 | pgm.add_node(daft.Node("wet", r"grass wet", 3, 1, aspect=2.4, observed=True)) 22 | pgm.add_edge("cloudy", "rain") 23 | pgm.add_edge("cloudy", "sprinkler") 24 | pgm.add_edge("rain", "wet") 25 | pgm.add_edge("sprinkler", "wet") 26 | pgm.render() 27 | pgm.figure.savefig("wordy.pdf") 28 | pgm.figure.savefig("wordy.png", dpi=150) 29 | -------------------------------------------------------------------------------- /examples/gaia.py: -------------------------------------------------------------------------------- 1 | from matplotlib import rc 2 | rc("font", family="serif", size=12) 3 | rc("text", usetex=True) 4 | import daft 5 | 6 | if __name__ == "__main__": 7 | pgm = daft.PGM([3.7, 3.15], origin=[-0.35, 2.2]) 8 | pgm.add_node(daft.Node("omega", r"$\omega$", 2, 5)) 9 | pgm.add_node(daft.Node("true", r"$\tilde{X}_n$", 2, 4)) 10 | pgm.add_node(daft.Node("obs", r"$X_n$", 2, 3, observed=True)) 11 | pgm.add_node(daft.Node("alpha", r"$\alpha$", 3, 4)) 12 | pgm.add_node(daft.Node("Sigma", r"$\Sigma$", 0, 3)) 13 | pgm.add_node(daft.Node("sigma", r"$\sigma_n$", 1, 3)) 14 | pgm.add_plate(daft.Plate([0.5, 2.25, 2, 2.25], label=r"stars $n$")) 15 | pgm.add_edge("omega", "true") 16 | pgm.add_edge("true", "obs") 17 | pgm.add_edge("alpha", "true") 18 | pgm.add_edge("Sigma", "sigma") 19 | pgm.add_edge("sigma", "obs") 20 | pgm.render() 21 | pgm.figure.savefig("gaia.pdf") 22 | pgm.figure.savefig("gaia.png", dpi=150) 23 | -------------------------------------------------------------------------------- /examples/mrf.py: -------------------------------------------------------------------------------- 1 | """ 2 | An undirected graph 3 | =================== 4 | 5 | This makes the simple point that you don't have to have directions on 6 | your edges; you can have *undirected* graphs. (Also, the nodes don't 7 | need to have labels!) 8 | 9 | """ 10 | 11 | import itertools 12 | import numpy as np 13 | 14 | import daft 15 | 16 | # Instantiate the PGM. 17 | pgm = daft.PGM([3.6, 3.6], origin=[0.7, 0.7], node_unit=0.4, grid_unit=1, 18 | directed=False) 19 | 20 | for i, (xi, yi) in enumerate(itertools.product(range(1, 5), range(1, 5))): 21 | pgm.add_node(daft.Node(str(i), "", xi, yi)) 22 | 23 | 24 | for e in [(4, 9), (6, 7), (3, 7), (10, 11), (10, 9), (10, 14), 25 | (10, 6), (10, 7), (1, 2), (1, 5), (1, 0), (1, 6), (8, 12), (12, 13), 26 | (13, 14), (15, 11)]: 27 | pgm.add_edge(str(e[0]), str(e[1])) 28 | 29 | # Render and save. 30 | pgm.render() 31 | pgm.figure.savefig("mrf.pdf") 32 | pgm.figure.savefig("mrf.png", dpi=150) 33 | -------------------------------------------------------------------------------- /examples/badfont.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use arbitrarily shitty fonts 3 | ==================================== 4 | 5 | Any fonts that LaTeX or matplotlib supports can be used. Do not take 6 | this example as any kind of implied recommendation unless you plan on 7 | announcing a *huge* discovery! 8 | 9 | """ 10 | 11 | from matplotlib import rc 12 | 13 | ff = "comic sans ms" 14 | # ff = "impact" 15 | # ff = "times new roman" 16 | 17 | rc("font", family=ff, size=12) 18 | rc("text", usetex=False) 19 | 20 | import daft 21 | 22 | pgm = daft.PGM([3.6, 1.8], origin=[2.2, 1.6], aspect=2.1) 23 | pgm.add_node(daft.Node("confused", r"confused", 3.0, 3.0)) 24 | pgm.add_node(daft.Node("ugly", r"ugly font", 3.0, 2.0, observed=True)) 25 | pgm.add_node(daft.Node("bad", r"bad talk", 5.0, 2.0, observed=True)) 26 | pgm.add_edge("confused", "ugly") 27 | pgm.add_edge("ugly", "bad") 28 | pgm.add_edge("confused", "bad") 29 | pgm.render() 30 | pgm.figure.savefig("badfont.pdf") 31 | pgm.figure.savefig("badfont.png", dpi=150) 32 | -------------------------------------------------------------------------------- /docs/examples/nocircles.rst: -------------------------------------------------------------------------------- 1 | .. _nocircles: 2 | 3 | 4 | Nodes can go free 5 | ================= 6 | 7 | .. figure:: /_static/examples/nocircles.png 8 | 9 | 10 | Nodes can go free 11 | ================= 12 | 13 | You don't need to put ellipses or circles around your node contents, 14 | if you don't want to. 15 | 16 | 17 | 18 | :: 19 | 20 | 21 | from matplotlib import rc 22 | rc("font", family="serif", size=12) 23 | rc("text", usetex=True) 24 | 25 | import daft 26 | 27 | pgm = daft.PGM([3.6, 2.4], origin = [1.15, 0.8], node_ec="none") 28 | pgm.add_node(daft.Node("cloudy", r"cloudy", 3, 3)) 29 | pgm.add_node(daft.Node("rain", r"rain", 2, 2)) 30 | pgm.add_node(daft.Node("sprinkler", r"sprinkler", 4, 2)) 31 | pgm.add_node(daft.Node("wet", r"grass wet", 3, 1)) 32 | pgm.add_edge("cloudy", "rain") 33 | pgm.add_edge("cloudy", "sprinkler") 34 | pgm.add_edge("rain", "wet") 35 | pgm.add_edge("sprinkler", "wet") 36 | pgm.render() 37 | pgm.figure.savefig("nocircles.pdf") 38 | pgm.figure.savefig("nocircles.png", dpi=150) 39 | 40 | 41 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Daniel Foreman-Mackey, David W. Hogg, and contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | **THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.** 19 | -------------------------------------------------------------------------------- /docs/_static/examples.js: -------------------------------------------------------------------------------- 1 | function shuffle(array) { 2 | // Based on: http://d3js.org 3 | var m = array.length, t, i; 4 | 5 | // While there remain elements to shuffle 6 | while (m) { 7 | // Pick a remaining element… 8 | i = Math.floor(Math.random() * m--); 9 | 10 | // And swap it with the current element. 11 | t = array[m]; 12 | array[m] = array[i]; 13 | array[i] = t; 14 | } 15 | 16 | return array; 17 | } 18 | 19 | function show_examples(N) { 20 | d3.json("/_static/examples.json", function (examples) { 21 | var k, names = []; 22 | for (k in examples) { 23 | names.push(k); 24 | } 25 | 26 | if (typeof(N) !== "undefined") 27 | names = shuffle(names).slice(0, N); 28 | 29 | d3.select("#examples").selectAll(".example") 30 | .data(names) 31 | .enter().append("a") 32 | .attr("class", "example") 33 | .attr("href", function (d) { return "/examples/" + d + "/"; }) 34 | .append("img") 35 | .attr("src", function (d) { return "/_static/examples/" + d + "-thumb.png"; }); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /docs/examples/wordy.rst: -------------------------------------------------------------------------------- 1 | .. _wordy: 2 | 3 | 4 | Nodes can contain words 5 | ======================= 6 | 7 | .. figure:: /_static/examples/wordy.png 8 | 9 | 10 | Nodes can contain words 11 | ======================= 12 | 13 | We here at **Daft** headquarters tend to put symbols (variable 14 | names) in our graph nodes. But you don't have to if you don't 15 | want to. 16 | 17 | 18 | 19 | :: 20 | 21 | 22 | from matplotlib import rc 23 | rc("font", family="serif", size=12) 24 | rc("text", usetex=True) 25 | 26 | import daft 27 | 28 | pgm = daft.PGM([3.6, 2.7], origin=[1.15, 0.65]) 29 | pgm.add_node(daft.Node("cloudy", r"cloudy", 3, 3, aspect=1.8)) 30 | pgm.add_node(daft.Node("rain", r"rain", 2, 2, aspect=1.2)) 31 | pgm.add_node(daft.Node("sprinkler", r"sprinkler", 4, 2, aspect=2.1)) 32 | pgm.add_node(daft.Node("wet", r"grass wet", 3, 1, aspect=2.4, observed=True)) 33 | pgm.add_edge("cloudy", "rain") 34 | pgm.add_edge("cloudy", "sprinkler") 35 | pgm.add_edge("rain", "wet") 36 | pgm.add_edge("sprinkler", "wet") 37 | pgm.render() 38 | pgm.figure.savefig("wordy.pdf") 39 | pgm.figure.savefig("wordy.png", dpi=150) 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/examples/mrf.rst: -------------------------------------------------------------------------------- 1 | .. _mrf: 2 | 3 | 4 | An undirected graph 5 | =================== 6 | 7 | .. figure:: /_static/examples/mrf.png 8 | 9 | 10 | An undirected graph 11 | =================== 12 | 13 | This makes the simple point that you don't have to have directions on 14 | your edges; you can have *undirected* graphs. (Also, the nodes don't 15 | need to have labels!) 16 | 17 | 18 | 19 | :: 20 | 21 | 22 | import itertools 23 | import numpy as np 24 | 25 | import daft 26 | 27 | # Instantiate the PGM. 28 | pgm = daft.PGM([3.6, 3.6], origin=[0.7, 0.7], node_unit=0.4, grid_unit=1, 29 | directed=False) 30 | 31 | for i, (xi, yi) in enumerate(itertools.product(range(1, 5), range(1, 5))): 32 | pgm.add_node(daft.Node(str(i), "", xi, yi)) 33 | 34 | 35 | for e in [(4, 9), (6, 7), (3, 7), (10, 11), (10, 9), (10, 14), 36 | (10, 6), (10, 7), (1, 2), (1, 5), (1, 0), (1, 6), (8, 12), (12, 13), 37 | (13, 14), (15, 11)]: 38 | pgm.add_edge(str(e[0]), str(e[1])) 39 | 40 | # Render and save. 41 | pgm.render() 42 | pgm.figure.savefig("mrf.pdf") 43 | pgm.figure.savefig("mrf.png", dpi=150) 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/classic.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Quintessential PGM 3 | ====================== 4 | 5 | This is a demonstration of a very common structure found in graphical models. 6 | It has been rendered using Daft's default settings for all the parameters 7 | and it shows off how much beauty is baked in by default. 8 | 9 | """ 10 | 11 | from matplotlib import rc 12 | rc("font", family="serif", size=12) 13 | rc("text", usetex=True) 14 | 15 | import daft 16 | 17 | # Instantiate the PGM. 18 | pgm = daft.PGM([2.3, 2.05], origin=[0.3, 0.3]) 19 | 20 | # Hierarchical parameters. 21 | pgm.add_node(daft.Node("alpha", r"$\alpha$", 0.5, 2, fixed=True)) 22 | pgm.add_node(daft.Node("beta", r"$\beta$", 1.5, 2)) 23 | 24 | # Latent variable. 25 | pgm.add_node(daft.Node("w", r"$w_n$", 1, 1)) 26 | 27 | # Data. 28 | pgm.add_node(daft.Node("x", r"$x_n$", 2, 1, observed=True)) 29 | 30 | # Add in the edges. 31 | pgm.add_edge("alpha", "beta") 32 | pgm.add_edge("beta", "w") 33 | pgm.add_edge("w", "x") 34 | pgm.add_edge("beta", "x") 35 | 36 | # And a plate. 37 | pgm.add_plate(daft.Plate([0.5, 0.5, 2, 1], label=r"$n = 1, \cdots, N$", 38 | shift=-0.1)) 39 | 40 | # Render and save. 41 | pgm.render() 42 | pgm.figure.savefig("classic.pdf") 43 | pgm.figure.savefig("classic.png", dpi=150) 44 | -------------------------------------------------------------------------------- /docs/examples/badfont.rst: -------------------------------------------------------------------------------- 1 | .. _badfont: 2 | 3 | 4 | You can use arbitrarily shitty fonts 5 | ==================================== 6 | 7 | .. figure:: /_static/examples/badfont.png 8 | 9 | 10 | You can use arbitrarily shitty fonts 11 | ==================================== 12 | 13 | Any fonts that LaTeX or matplotlib supports can be used. Do not take 14 | this example as any kind of implied recommendation unless you plan on 15 | announcing a *huge* discovery! 16 | 17 | 18 | 19 | :: 20 | 21 | 22 | from matplotlib import rc 23 | 24 | ff = "comic sans ms" 25 | # ff = "impact" 26 | # ff = "times new roman" 27 | 28 | rc("font", family=ff, size=12) 29 | rc("text", usetex=False) 30 | 31 | import daft 32 | 33 | pgm = daft.PGM([3.6, 1.8], origin=[2.2, 1.6], aspect=2.1) 34 | pgm.add_node(daft.Node("confused", r"confused", 3.0, 3.0)) 35 | pgm.add_node(daft.Node("ugly", r"ugly font", 3.0, 2.0, observed=True)) 36 | pgm.add_node(daft.Node("bad", r"bad talk", 5.0, 2.0, observed=True)) 37 | pgm.add_edge("confused", "ugly") 38 | pgm.add_edge("ugly", "bad") 39 | pgm.add_edge("confused", "bad") 40 | pgm.render() 41 | pgm.figure.savefig("badfont.pdf") 42 | pgm.figure.savefig("badfont.png", dpi=150) 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/yike.py: -------------------------------------------------------------------------------- 1 | """ 2 | Yike's model 3 | ============ 4 | 5 | This is Yike Tang's model for weak lensing. 6 | 7 | """ 8 | 9 | from matplotlib import rc 10 | rc("font", family="serif", size=12) 11 | rc("text", usetex=True) 12 | 13 | import daft 14 | 15 | pgm = daft.PGM([5.20, 2.95], origin=[-1.70, 1.65]) 16 | pgm.add_node(daft.Node("obs", r"$\epsilon^{\mathrm{obs}}_n$", 2, 3, observed=True)) 17 | pgm.add_node(daft.Node("true", r"$\epsilon^{\mathrm{true}}_n$", 1, 3)) 18 | pgm.add_edge("true", "obs") 19 | pgm.add_node(daft.Node("alpha", r"$\alpha,\beta$", -0.25, 3)) 20 | pgm.add_edge("alpha", "true") 21 | pgm.add_node(daft.Node("shape prior", r"$p(\alpha, \beta)$", -1.25, 3, fixed=True)) 22 | pgm.add_edge("shape prior", "alpha") 23 | pgm.add_node(daft.Node("gamma", r"$\gamma_m$", 2, 4)) 24 | pgm.add_edge("gamma", "obs") 25 | pgm.add_node(daft.Node("gamma prior", r"$p(\gamma)$", -0.25, 4, fixed=True)) 26 | pgm.add_edge("gamma prior", "gamma") 27 | pgm.add_node(daft.Node("sigma", r"$\sigma_{\epsilon}$", 3.25, 3, fixed=True)) 28 | pgm.add_edge("sigma", "obs") 29 | pgm.add_plate(daft.Plate([0.5, 2.25, 2, 1.25], 30 | label=r"galaxies $n$")) 31 | pgm.add_plate(daft.Plate([0.25, 1.75, 2.5, 2.75], 32 | label=r"patches $m$")) 33 | pgm.render() 34 | pgm.figure.savefig("yike.pdf") 35 | pgm.figure.savefig("yike.png", dpi=150) 36 | -------------------------------------------------------------------------------- /examples/nogray.py: -------------------------------------------------------------------------------- 1 | """ 2 | Alternative Observed Node Styles 3 | ================================ 4 | 5 | .. module:: daft 6 | 7 | This model is the same as `the classic `_ model but the 8 | "observed" :class:`Node` is indicated by a double outline instead of shading. 9 | This particular example uses the ``inner`` style but ``outer`` is also an 10 | option for a different look. 11 | 12 | """ 13 | 14 | from matplotlib import rc 15 | rc("font", family="serif", size=12) 16 | rc("text", usetex=True) 17 | 18 | import daft 19 | 20 | pgm = daft.PGM([2.3, 2.05], origin=[0.3, 0.3], observed_style="inner") 21 | 22 | # Hierarchical parameters. 23 | pgm.add_node(daft.Node("alpha", r"$\alpha$", 0.5, 2, fixed=True)) 24 | pgm.add_node(daft.Node("beta", r"$\beta$", 1.5, 2)) 25 | 26 | # Latent variable. 27 | pgm.add_node(daft.Node("w", r"$w_n$", 1, 1)) 28 | 29 | # Data. 30 | pgm.add_node(daft.Node("x", r"$x_n$", 2, 1, observed=True)) 31 | 32 | # Add in the edges. 33 | pgm.add_edge("alpha", "beta") 34 | pgm.add_edge("beta", "w") 35 | pgm.add_edge("w", "x") 36 | pgm.add_edge("beta", "x") 37 | 38 | # And a plate. 39 | pgm.add_plate(daft.Plate([0.5, 0.5, 2, 1], label=r"$n = 1, \ldots, N$", 40 | shift=-0.1)) 41 | 42 | # Render and save. 43 | pgm.render() 44 | pgm.figure.savefig("nogray.pdf") 45 | pgm.figure.savefig("nogray.png", dpi=150) 46 | -------------------------------------------------------------------------------- /examples/thicklines.py: -------------------------------------------------------------------------------- 1 | """ 2 | T-shirt style 3 | ============= 4 | 5 | Don't like dainty thin lines? Need to make graphical-model-themed 6 | conference schwag? Then `line_width` is the parameter for you. Also 7 | check out the `preamble` option in the `matplotlib.rc` command. 8 | 9 | """ 10 | 11 | from matplotlib import rc 12 | rc("font", family="serif", size=14) 13 | rc("text", usetex=True) 14 | rc('text.latex', 15 | preamble="\usepackage{amssymb}\usepackage{amsmath}\usepackage{mathrsfs}") 16 | 17 | import daft 18 | 19 | # Instantiate the PGM. 20 | pgm = daft.PGM([2.3, 2.05], origin=[0.3, 0.3], line_width=2.5) 21 | 22 | # Hierarchical parameters. 23 | pgm.add_node(daft.Node("alpha", r"$\boldsymbol{\alpha}$", 0.5, 2, fixed=True)) 24 | pgm.add_node(daft.Node("beta", r"$\boldsymbol{\beta}$", 1.5, 2)) 25 | 26 | # Latent variable. 27 | pgm.add_node(daft.Node("w", r"$\boldsymbol{w_n}$", 1, 1)) 28 | 29 | # Data. 30 | pgm.add_node(daft.Node("x", r"$\boldsymbol{x_n}$", 2, 1, observed=True)) 31 | 32 | # Add in the edges. 33 | pgm.add_edge("alpha", "beta") 34 | pgm.add_edge("beta", "w") 35 | pgm.add_edge("w", "x") 36 | pgm.add_edge("beta", "x") 37 | 38 | # And a plate. 39 | pgm.add_plate(daft.Plate([0.5, 0.5, 2, 1], label=r"$\boldsymbol{n = 1, \cdots, N}$", 40 | shift=-0.1)) 41 | 42 | # Render and save. 43 | pgm.render() 44 | pgm.figure.savefig("thicklines.pdf") 45 | pgm.figure.savefig("thicklines.png", dpi=150) 46 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | try: 5 | from setuptools import setup, Extension 6 | setup, Extension 7 | except ImportError: 8 | from distutils.core import setup 9 | from distutils.extension import Extension 10 | setup, Extension 11 | 12 | import os 13 | import re 14 | import sys 15 | 16 | if sys.argv[-1] == "publish": 17 | os.system("python setup.py sdist upload") 18 | sys.exit() 19 | 20 | vre = re.compile("__version__ = \"(.*?)\"") 21 | m = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 22 | "daft.py")).read() 23 | version = vre.findall(m)[0] 24 | 25 | 26 | setup( 27 | name="daft", 28 | version=version, 29 | description="PGM rendering at its finest.", 30 | long_description=open("README.rst").read(), 31 | author="David W. Hogg & Daniel Foreman-Mackey", 32 | author_email="danfm@nyu.edu", 33 | url="http://daft-pgm.org", 34 | py_modules=["daft"], 35 | package_data={"": ["LICENSE.rst"]}, 36 | include_package_data=True, 37 | install_requires=[ 38 | "numpy", 39 | "matplotlib" 40 | ], 41 | classifiers=[ 42 | "Development Status :: 3 - Alpha", 43 | "Intended Audience :: Developers", 44 | "Intended Audience :: Science/Research", 45 | "License :: OSI Approved :: MIT License", 46 | "Operating System :: OS Independent", 47 | "Programming Language :: Python", 48 | ], 49 | ) 50 | -------------------------------------------------------------------------------- /docs/examples/classic.rst: -------------------------------------------------------------------------------- 1 | .. _classic: 2 | 3 | 4 | The Quintessential PGM 5 | ====================== 6 | 7 | .. figure:: /_static/examples/classic.png 8 | 9 | 10 | The Quintessential PGM 11 | ====================== 12 | 13 | This is a demonstration of a very common structure found in graphical models. 14 | It has been rendered using Daft's default settings for all the parameters 15 | and it shows off how much beauty is baked in by default. 16 | 17 | 18 | 19 | :: 20 | 21 | 22 | from matplotlib import rc 23 | rc("font", family="serif", size=12) 24 | rc("text", usetex=True) 25 | 26 | import daft 27 | 28 | # Instantiate the PGM. 29 | pgm = daft.PGM([2.3, 2.05], origin=[0.3, 0.3]) 30 | 31 | # Hierarchical parameters. 32 | pgm.add_node(daft.Node("alpha", r"$\alpha$", 0.5, 2, fixed=True)) 33 | pgm.add_node(daft.Node("beta", r"$\beta$", 1.5, 2)) 34 | 35 | # Latent variable. 36 | pgm.add_node(daft.Node("w", r"$w_n$", 1, 1)) 37 | 38 | # Data. 39 | pgm.add_node(daft.Node("x", r"$x_n$", 2, 1, observed=True)) 40 | 41 | # Add in the edges. 42 | pgm.add_edge("alpha", "beta") 43 | pgm.add_edge("beta", "w") 44 | pgm.add_edge("w", "x") 45 | pgm.add_edge("beta", "x") 46 | 47 | # And a plate. 48 | pgm.add_plate(daft.Plate([0.5, 0.5, 2, 1], label=r"$n = 1, \cdots, N$", 49 | shift=-0.1)) 50 | 51 | # Render and save. 52 | pgm.render() 53 | pgm.figure.savefig("classic.pdf") 54 | pgm.figure.savefig("classic.png", dpi=150) 55 | 56 | 57 | -------------------------------------------------------------------------------- /examples/weaklensing.py: -------------------------------------------------------------------------------- 1 | """ 2 | A model for weak lensing 3 | ======================== 4 | 5 | This is (**Daft** co-author) Hogg's model for the obsevational 6 | cosmology method known as *weak gravitational lensing*, if that method 7 | were properly probabilistic (which it usually isn't). Hogg put the 8 | model here for one very important reason: *Because he can*. Oh, and 9 | it demonstrates that you can represent non-trivial scientific projects 10 | with **Daft**. 11 | 12 | """ 13 | 14 | from matplotlib import rc 15 | rc("font", family="serif", size=12) 16 | rc("text", usetex=True) 17 | 18 | import daft 19 | 20 | pgm = daft.PGM([4.7, 2.35], origin=[-1.35, 2.2]) 21 | pgm.add_node(daft.Node("Omega", r"$\Omega$", -1, 4)) 22 | pgm.add_node(daft.Node("gamma", r"$\gamma$", 0, 4)) 23 | pgm.add_node(daft.Node("obs", r"$\epsilon^{\mathrm{obs}}_n$", 1, 4, 24 | observed=True)) 25 | pgm.add_node(daft.Node("alpha", r"$\alpha$", 3, 4)) 26 | pgm.add_node(daft.Node("true", r"$\epsilon^{\mathrm{true}}_n$", 2, 4)) 27 | pgm.add_node(daft.Node("sigma", r"$\sigma_n$", 1, 3)) 28 | pgm.add_node(daft.Node("Sigma", r"$\Sigma$", 0, 3)) 29 | pgm.add_node(daft.Node("x", r"$x_n$", 2, 3, observed=True)) 30 | pgm.add_plate(daft.Plate([0.5, 2.25, 2, 2.25], 31 | label=r"galaxies $n$")) 32 | pgm.add_edge("Omega", "gamma") 33 | pgm.add_edge("gamma", "obs") 34 | pgm.add_edge("alpha", "true") 35 | pgm.add_edge("true", "obs") 36 | pgm.add_edge("x", "obs") 37 | pgm.add_edge("Sigma", "sigma") 38 | pgm.add_edge("sigma", "obs") 39 | pgm.render() 40 | pgm.figure.savefig("weaklensing.pdf") 41 | pgm.figure.savefig("weaklensing.png", dpi=150) 42 | -------------------------------------------------------------------------------- /docs/examples/nogray.rst: -------------------------------------------------------------------------------- 1 | .. _nogray: 2 | 3 | 4 | Alternative Observed Node Styles 5 | ================================ 6 | 7 | .. figure:: /_static/examples/nogray.png 8 | 9 | 10 | Alternative Observed Node Styles 11 | ================================ 12 | 13 | .. module:: daft 14 | 15 | This model is the same as `the classic `_ model but the 16 | "observed" :class:`Node` is indicated by a double outline instead of shading. 17 | This particular example uses the ``inner`` style but ``outer`` is also an 18 | option for a different look. 19 | 20 | 21 | 22 | :: 23 | 24 | 25 | from matplotlib import rc 26 | rc("font", family="serif", size=12) 27 | rc("text", usetex=True) 28 | 29 | import daft 30 | 31 | pgm = daft.PGM([2.3, 2.05], origin=[0.3, 0.3], observed_style="inner") 32 | 33 | # Hierarchical parameters. 34 | pgm.add_node(daft.Node("alpha", r"$\alpha$", 0.5, 2, fixed=True)) 35 | pgm.add_node(daft.Node("beta", r"$\beta$", 1.5, 2)) 36 | 37 | # Latent variable. 38 | pgm.add_node(daft.Node("w", r"$w_n$", 1, 1)) 39 | 40 | # Data. 41 | pgm.add_node(daft.Node("x", r"$x_n$", 2, 1, observed=True)) 42 | 43 | # Add in the edges. 44 | pgm.add_edge("alpha", "beta") 45 | pgm.add_edge("beta", "w") 46 | pgm.add_edge("w", "x") 47 | pgm.add_edge("beta", "x") 48 | 49 | # And a plate. 50 | pgm.add_plate(daft.Plate([0.5, 0.5, 2, 1], label=r"$n = 1, \ldots, N$", 51 | shift=-0.1)) 52 | 53 | # Render and save. 54 | pgm.render() 55 | pgm.figure.savefig("nogray.pdf") 56 | pgm.figure.savefig("nogray.png", dpi=150) 57 | 58 | 59 | -------------------------------------------------------------------------------- /examples/huey_p_newton.py: -------------------------------------------------------------------------------- 1 | """ 2 | n-body particle inference 3 | ========================= 4 | 5 | Dude. 6 | """ 7 | 8 | from matplotlib import rc 9 | rc("font", family="serif", size=12) 10 | rc("text", usetex=True) 11 | 12 | import daft 13 | 14 | pgm = daft.PGM([5.4, 2.0], origin=[0.65, 0.35]) 15 | 16 | kx, ky = 1.5, 1. 17 | nx, ny = kx + 3., ky + 0. 18 | hx, hy, dhx = kx - 0.5, ky + 1., 1. 19 | 20 | pgm.add_node(daft.Node("dyn", r"$\theta_{\mathrm{dyn}}$", hx + 0. * dhx, hy + 0.)) 21 | pgm.add_node(daft.Node("ic", r"$\theta_{\mathrm{I.C.}}$", hx + 1. * dhx, hy + 0.)) 22 | pgm.add_node(daft.Node("sun", r"$\theta_{\odot}$", hx + 2. * dhx, hy + 0.)) 23 | pgm.add_node(daft.Node("bg", r"$\theta_{\mathrm{bg}}$", hx + 3. * dhx, hy + 0.)) 24 | pgm.add_node(daft.Node("Sigma", r"$\Sigma^2$", hx + 4. * dhx, hy + 0.)) 25 | 26 | pgm.add_plate(daft.Plate([kx - 0.5, ky - 0.6, 2., 1.1], label=r"model points $k$")) 27 | pgm.add_node(daft.Node("xk", r"$x_k$", kx + 0., ky + 0.)) 28 | pgm.add_edge("dyn", "xk") 29 | pgm.add_edge("ic", "xk") 30 | pgm.add_node(daft.Node("yk", r"$y_k$", kx + 1., ky + 0.)) 31 | pgm.add_edge("sun", "yk") 32 | pgm.add_edge("xk", "yk") 33 | 34 | pgm.add_plate(daft.Plate([nx - 0.5, ny - 0.6, 2., 1.1], label=r"data points $n$")) 35 | pgm.add_node(daft.Node("sigman", r"$\sigma^2_n$", nx + 1., ny + 0., observed=True)) 36 | pgm.add_node(daft.Node("Yn", r"$Y_n$", nx + 0., ny + 0., observed=True)) 37 | pgm.add_edge("bg", "Yn") 38 | pgm.add_edge("Sigma", "Yn") 39 | pgm.add_edge("Sigma", "Yn") 40 | pgm.add_edge("yk", "Yn") 41 | pgm.add_edge("sigman", "Yn") 42 | 43 | # Render and save. 44 | pgm.render() 45 | pgm.figure.savefig("huey_p_newton.pdf") 46 | pgm.figure.savefig("huey_p_newton.png", dpi=150) 47 | -------------------------------------------------------------------------------- /docs/_themes/daft/search.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% set title = _('Search') %} 4 | 5 | {% set script_files = script_files + ['_static/searchtools.js'] %} 6 | 7 | {% block extrahead %} 8 | 11 | {{ super() }} 12 | {% endblock %} 13 | 14 | {% block body %} 15 |

{{ _('Search') }}

16 |
17 | 18 |

19 | {% trans %}Please activate JavaScript to enable the search 20 | functionality.{% endtrans %} 21 |

22 |
23 |

24 | {% trans %}From here you can search these documents. Enter your search 25 | words into the box below. Note that the search 26 | function will automatically search for all of the words. Pages 27 | containing fewer words won't appear in the result list.{% endtrans %} 28 |

29 |
30 | 31 | 32 |
33 | {% if search_performed %} 34 |

{{ _('Search Results') }}

35 | {% if not search_results %} 36 |

{{ _('Your search did not match any results.') }}

37 | {% endif %} 38 | {% endif %} 39 |
40 | {% if search_results %} 41 |
    42 | {% for href, caption, context in search_results %} 43 |
  • {{ caption }} 44 |
    {{ context|e }}
    45 |
  • 46 | {% endfor %} 47 |
48 | {% endif %} 49 |
50 | {% endblock %} 51 | -------------------------------------------------------------------------------- /examples/recursive.py: -------------------------------------------------------------------------------- 1 | """ 2 | Recursively generated graph 3 | =========================== 4 | 5 | **Daft** is Python, so you can do anything Python can do. This graph is 6 | generated by recursive code. 7 | 8 | """ 9 | 10 | from matplotlib import rc 11 | rc("font", family="serif", size=12) 12 | rc("text", usetex=True) 13 | 14 | import daft 15 | 16 | def recurse(pgm, nodename, level, c): 17 | if level > 4: 18 | return nodename 19 | r = c / 2 20 | r1nodename = "r{0:02d}{1:04d}".format(level, r) 21 | if 2 * r == c: 22 | print("adding {0}".format(r1nodename)) 23 | pgm.add_node(daft.Node(r1nodename, r"reduce", 24 | 2 ** level * (r + 0.5) - 0.5, 25 | 3 - 0.7 * level, aspect=1.9)) 26 | pgm.add_edge(nodename, r1nodename) 27 | if 2 * r == c: 28 | return recurse(pgm, r1nodename, level + 1, r) 29 | 30 | pgm = daft.PGM([16.2, 8], origin=[-0.6, -1.5]) 31 | 32 | pgm.add_node(daft.Node("query", r'\texttt{"kittens?"}', 3, 6., aspect=3., 33 | plot_params={"ec": "none"})) 34 | pgm.add_node(daft.Node("input", r"input", 7.5, 6., aspect=3.)) 35 | pgm.add_edge("query", "input") 36 | 37 | for c in range(16): 38 | nodename = "map {0:02d}".format(c) 39 | pgm.add_node(daft.Node(nodename, str(nodename), c, 3., aspect=1.9)) 40 | pgm.add_edge("input", nodename) 41 | level = 1 42 | recurse(pgm, nodename, level, c) 43 | 44 | pgm.add_node(daft.Node("output", r"output", 7.5, -1., aspect=3.)) 45 | pgm.add_edge("r040000", "output") 46 | pgm.add_node(daft.Node("answer", r'\texttt{"http://dwh.gg/"}', 12., -1., 47 | aspect=4.5, plot_params={"ec": "none"})) 48 | pgm.add_edge("output", "answer") 49 | 50 | pgm.render() 51 | pgm.figure.savefig("recursive.pdf") 52 | pgm.figure.savefig("recursive.png", dpi=200) 53 | -------------------------------------------------------------------------------- /examples/exoplanets.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Fergus model of exoplanet detection 3 | ======================================= 4 | 5 | Besides being generally awesome, this example also demonstrates how you can 6 | color the nodes and add arbitrary labels to the figure. 7 | 8 | """ 9 | 10 | from matplotlib import rc 11 | rc("font", family="serif", size=12) 12 | rc("text", usetex=True) 13 | 14 | import daft 15 | 16 | # Colors. 17 | p_color = {"ec": "#46a546"} 18 | s_color = {"ec": "#f89406"} 19 | 20 | pgm = daft.PGM([3.6, 3.5], origin=[0.7, 0]) 21 | 22 | n = daft.Node("phi", r"$\phi$", 1, 3, plot_params=s_color) 23 | n.va = "baseline" 24 | pgm.add_node(n) 25 | pgm.add_node(daft.Node("speckle_coeff", r"$z_i$", 2, 3, plot_params=s_color)) 26 | pgm.add_node(daft.Node("speckle_img", r"$x_i$", 2, 2, plot_params=s_color)) 27 | 28 | pgm.add_node(daft.Node("spec", r"$s$", 4, 3, plot_params=p_color)) 29 | pgm.add_node(daft.Node("shape", r"$g$", 4, 2, plot_params=p_color)) 30 | pgm.add_node(daft.Node("planet_pos", r"$\mu_i$", 3, 3, plot_params=p_color)) 31 | pgm.add_node(daft.Node("planet_img", r"$p_i$", 3, 2, plot_params=p_color)) 32 | 33 | pgm.add_node(daft.Node("pixels", r"$y_i ^j$", 2.5, 1, observed=True)) 34 | 35 | # Edges. 36 | pgm.add_edge("phi", "speckle_coeff") 37 | pgm.add_edge("speckle_coeff", "speckle_img") 38 | pgm.add_edge("speckle_img", "pixels") 39 | 40 | pgm.add_edge("spec", "planet_img") 41 | pgm.add_edge("shape", "planet_img") 42 | pgm.add_edge("planet_pos", "planet_img") 43 | pgm.add_edge("planet_img", "pixels") 44 | 45 | # And a plate. 46 | pgm.add_plate(daft.Plate([1.5, 0.2, 2, 3.2], label=r"exposure $i$", 47 | shift=-0.1)) 48 | pgm.add_plate(daft.Plate([2, 0.5, 1, 1], label=r"pixel $j$", 49 | shift=-0.1)) 50 | 51 | # Render and save. 52 | pgm.render() 53 | pgm.figure.savefig("exoplanets.pdf") 54 | pgm.figure.savefig("exoplanets.png", dpi=150) 55 | -------------------------------------------------------------------------------- /docs/_themes/daft/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "basic/layout.html" %} 2 | 3 | {% if not title %} 4 | {% set title = project %} 5 | {% endif %} 6 | 7 | {%- block extrahead %} 8 | {{ super() }} 9 | 10 | 11 | 12 | 13 | {% endblock %} 14 | 15 | {%- block relbar1 %}{% endblock %} 16 | {%- block relbar2 %}{% endblock %} 17 | 18 | {%- block document %} 19 | 20 | 24 | 25 | {{ super() }} 26 | 27 | {% endblock %} 28 | 29 | {%- block footer %} 30 | 31 | 35 | 36 | 37 | Fork me on GitHub 40 | 41 | 42 | 55 | 56 | {%- endblock %} 57 | -------------------------------------------------------------------------------- /docs/examples/weaklensing.rst: -------------------------------------------------------------------------------- 1 | .. _weaklensing: 2 | 3 | 4 | A model for weak lensing 5 | ======================== 6 | 7 | .. figure:: /_static/examples/weaklensing.png 8 | 9 | 10 | A model for weak lensing 11 | ======================== 12 | 13 | This is (**Daft** co-author) Hogg's model for the obsevational 14 | cosmology method known as *weak gravitational lensing*, if that method 15 | were properly probabilistic (which it usually isn't). Hogg put the 16 | model here for one very important reason: *Because he can*. Oh, and 17 | it demonstrates that you can represent non-trivial scientific projects 18 | with **Daft**. 19 | 20 | 21 | 22 | :: 23 | 24 | 25 | from matplotlib import rc 26 | rc("font", family="serif", size=12) 27 | rc("text", usetex=True) 28 | rc("./weaklensing.tex") 29 | 30 | import daft 31 | 32 | pgm = daft.PGM([4.7, 2.35], origin=[-1.35, 2.2]) 33 | pgm.add_node(daft.Node("Omega", r"$\Omega$", -1, 4)) 34 | pgm.add_node(daft.Node("gamma", r"$\gamma$", 0, 4)) 35 | pgm.add_node(daft.Node("obs", r"$\epsilon^{\mathrm{obs}}_n$", 1, 4, observed=True)) 36 | pgm.add_node(daft.Node("alpha", r"$\alpha$", 3, 4)) 37 | pgm.add_node(daft.Node("true", r"$\epsilon^{\mathrm{true}}_n$", 2, 4)) 38 | pgm.add_node(daft.Node("sigma", r"$\sigma_n$", 1, 3)) 39 | pgm.add_node(daft.Node("Sigma", r"$\Sigma$", 0, 3)) 40 | pgm.add_node(daft.Node("x", r"$x_n$", 2, 3, observed=True)) 41 | pgm.add_plate(daft.Plate([0.5, 2.25, 2, 2.25], 42 | label=r"galaxies $n$")) 43 | pgm.add_edge("Omega", "gamma") 44 | pgm.add_edge("gamma", "obs") 45 | pgm.add_edge("alpha", "true") 46 | pgm.add_edge("true", "obs") 47 | pgm.add_edge("x", "obs") 48 | pgm.add_edge("Sigma", "sigma") 49 | pgm.add_edge("sigma", "obs") 50 | pgm.render() 51 | pgm.figure.savefig("weaklensing.pdf") 52 | pgm.figure.savefig("weaklensing.png", dpi=150) 53 | 54 | 55 | -------------------------------------------------------------------------------- /docs/examples/recursive.rst: -------------------------------------------------------------------------------- 1 | .. _recursive: 2 | 3 | 4 | Recursively generated graph 5 | =========================== 6 | 7 | .. figure:: /_static/examples/recursive.png 8 | 9 | 10 | Recursively generated graph 11 | =========================== 12 | 13 | **Daft** is Python, so you can do anything Python can do. This graph is 14 | generated by recursive code. 15 | 16 | 17 | 18 | :: 19 | 20 | 21 | from matplotlib import rc 22 | rc("font", family="serif", size=12) 23 | rc("text", usetex=True) 24 | 25 | import daft 26 | 27 | def recurse(pgm, nodename, level, c): 28 | if level > 4: 29 | return nodename 30 | r = c / 2 31 | r1nodename = "r{0:02d}{1:04d}".format(level, r) 32 | if 2 * r == c: 33 | print("adding {0}".format(r1nodename)) 34 | pgm.add_node(daft.Node(r1nodename, r"reduce", 35 | 2 ** level * (r + 0.5) - 0.5, 36 | 3 - 0.7 * level, aspect=1.9)) 37 | pgm.add_edge(nodename, r1nodename) 38 | if 2 * r == c: 39 | return recurse(pgm, r1nodename, level + 1, r) 40 | 41 | pgm = daft.PGM([16.2, 8], origin=[-0.6, -1.5]) 42 | 43 | pgm.add_node(daft.Node("query", r'\texttt{"kittens?"}', 3, 6., aspect=3., 44 | plot_params={"ec": "none"})) 45 | pgm.add_node(daft.Node("input", r"input", 7.5, 6., aspect=3.)) 46 | pgm.add_edge("query", "input") 47 | 48 | for c in range(16): 49 | nodename = "map {0:02d}".format(c) 50 | pgm.add_node(daft.Node(nodename, str(nodename), c, 3., aspect=1.9)) 51 | pgm.add_edge("input", nodename) 52 | level = 1 53 | recurse(pgm, nodename, level, c) 54 | 55 | pgm.add_node(daft.Node("output", r"output", 7.5, -1., aspect=3.)) 56 | pgm.add_edge("r040000", "output") 57 | pgm.add_node(daft.Node("answer", r'\texttt{"http://dwh.gg/"}', 12., -1., 58 | aspect=4.5, plot_params={"ec": "none"})) 59 | pgm.add_edge("output", "answer") 60 | 61 | pgm.render() 62 | pgm.figure.savefig("recursive.pdf") 63 | pgm.figure.savefig("recursive.png", dpi=200) 64 | 65 | 66 | -------------------------------------------------------------------------------- /docs/examples/exoplanets.rst: -------------------------------------------------------------------------------- 1 | .. _exoplanets: 2 | 3 | 4 | The Fergus model of exoplanet detection 5 | ======================================= 6 | 7 | .. figure:: /_static/examples/exoplanets.png 8 | 9 | 10 | The Fergus model of exoplanet detection 11 | ======================================= 12 | 13 | Besides being generally awesome, this example also demonstrates how you can 14 | color the nodes and add arbitrary labels to the figure. 15 | 16 | 17 | 18 | :: 19 | 20 | 21 | from matplotlib import rc 22 | rc("font", family="serif", size=12) 23 | rc("text", usetex=True) 24 | 25 | import daft 26 | 27 | # Colors. 28 | p_color = {"ec": "#46a546"} 29 | s_color = {"ec": "#f89406"} 30 | 31 | pgm = daft.PGM([3.6, 3.5], origin=[0.7, 0]) 32 | 33 | n = daft.Node("phi", r"$\phi$", 1, 3, plot_params=s_color) 34 | n.va = "baseline" 35 | pgm.add_node(n) 36 | pgm.add_node(daft.Node("speckle_coeff", r"$z_i$", 2, 3, plot_params=s_color)) 37 | pgm.add_node(daft.Node("speckle_img", r"$x_i$", 2, 2, plot_params=s_color)) 38 | 39 | pgm.add_node(daft.Node("spec", r"$s$", 4, 3, plot_params=p_color)) 40 | pgm.add_node(daft.Node("shape", r"$g$", 4, 2, plot_params=p_color)) 41 | pgm.add_node(daft.Node("planet_pos", r"$\mu_i$", 3, 3, plot_params=p_color)) 42 | pgm.add_node(daft.Node("planet_img", r"$p_i$", 3, 2, plot_params=p_color)) 43 | 44 | pgm.add_node(daft.Node("pixels", r"$y_i ^j$", 2.5, 1, observed=True)) 45 | 46 | # Edges. 47 | pgm.add_edge("phi", "speckle_coeff") 48 | pgm.add_edge("speckle_coeff", "speckle_img") 49 | pgm.add_edge("speckle_img", "pixels") 50 | 51 | pgm.add_edge("spec", "planet_img") 52 | pgm.add_edge("shape", "planet_img") 53 | pgm.add_edge("planet_pos", "planet_img") 54 | pgm.add_edge("planet_img", "pixels") 55 | 56 | # And a plate. 57 | pgm.add_plate(daft.Plate([1.5, 0.2, 2, 3.2], label=r"exposure $i$", 58 | shift=-0.1)) 59 | pgm.add_plate(daft.Plate([2, 0.5, 1, 1], label=r"pixel $j$", 60 | shift=-0.1)) 61 | 62 | # Render and save. 63 | pgm.render() 64 | pgm.figure.savefig("exoplanets.pdf") 65 | pgm.figure.savefig("exoplanets.png", dpi=150) 66 | 67 | 68 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Daft 2 | ==== 3 | 4 | .. raw:: html 5 | 6 |
7 |
8 | More… 9 |
10 | 11 | 12 | 15 | 16 | 17 | Summary 18 | ------- 19 | 20 | **Daft** is a Python package that uses `matplotlib `_ 21 | to render pixel-perfect *probabilistic graphical models* for publication 22 | in a journal or on the internet. With a short Python script and an intuitive 23 | model-building syntax you can design directed (Bayesian Networks, directed 24 | acyclic graphs) and undirected (Markov random fields) models and save 25 | them in any formats that matplotlib supports (including PDF, PNG, EPS and 26 | SVG). 27 | 28 | 29 | Installation 30 | ------------ 31 | 32 | Installing the most recent stable version of Daft should be pretty easy 33 | if you use `pip `_: 34 | 35 | :: 36 | 37 | pip install daft 38 | 39 | Otherwise, you can download the source (`tar 40 | `_, `zip 41 | `_) and run: 42 | 43 | :: 44 | 45 | python setup.py install 46 | 47 | in the root directory. 48 | 49 | Daft only depends on `matplotlib `_ and 50 | `numpy `_. These are standard components of the 51 | scientific Python stack but if you don't already have them installed ``pip`` 52 | will try to install them for you but sometimes it's easier to do that part 53 | yourself. 54 | 55 | 56 | Issues 57 | ------ 58 | 59 | If you have any problems or questions, `open an "issue" on Github 60 | `_. 61 | 62 | 63 | Authors & Contributions 64 | ----------------------- 65 | 66 | **Daft** is being developed and supported by `Dan Foreman-Mackey 67 | `_ and `David W. Hogg `_. 68 | 69 | For the hackers in the house, development happens on `Github 70 | `_ and we welcome pull requests. In particular, 71 | we'd love to see examples of how you're using Daft in your work. 72 | 73 | 74 | License 75 | ------- 76 | 77 | *Copyright 2012 Dan Foreman-Mackey, David W. Hogg, and contributors.* 78 | 79 | **Daft** is free software made available under the *MIT License*. For details 80 | see `the LICENSE file `_. 81 | 82 | If you use Daft in academic projects, acknowledgements are greatly 83 | appreciated. 84 | 85 | 86 | API 87 | --- 88 | 89 | .. toctree:: 90 | :maxdepth: 2 91 | 92 | api 93 | -------------------------------------------------------------------------------- /examples/galex.py: -------------------------------------------------------------------------------- 1 | """ 2 | The GALEX Photon Catalog 3 | ======================== 4 | 5 | This is the Hogg \& Schiminovich model for how photons turn into 6 | counts in the GALEX satellite data stream. Note the use of relative 7 | positioning. 8 | 9 | """ 10 | 11 | from matplotlib import rc 12 | rc("font", family="serif", size=12) 13 | rc("text", usetex=True) 14 | import daft 15 | pgm = daft.PGM([5.4, 5.4], origin=[1.2, 1.2]) 16 | wide = 1.5 17 | verywide = 1.5 * wide 18 | dy = 0.75 19 | 20 | # electrons 21 | el_x, el_y = 2., 2. 22 | pgm.add_plate(daft.Plate([el_x - 0.6, el_y - 0.6, 2.2, 2 * dy + 0.3], label="electrons $i$")) 23 | pgm.add_node(daft.Node("xabc", r"xa$_i$,xabc$_i$,ya$_i$,\textit{etc}", el_x + 0.5, el_y + 0 * dy, aspect=2.3 * wide, observed=True)) 24 | pgm.add_node(daft.Node("xyti", r"$x_i,y_i,t_i$", el_x + 1., el_y + 1 * dy, aspect=wide)) 25 | pgm.add_edge("xyti", "xabc") 26 | 27 | # intensity fields 28 | ph_x, ph_y = el_x + 2.5, el_y + 3 * dy 29 | pgm.add_node(daft.Node("Ixyt", r"$I_{\nu}(x,y,t)$", ph_x, ph_y, aspect=verywide)) 30 | pgm.add_edge("Ixyt", "xyti") 31 | pgm.add_node(daft.Node("Ixnt", r"$I_{\nu}(\xi,\eta,t)$", ph_x, ph_y + 1 * dy, aspect=verywide)) 32 | pgm.add_edge("Ixnt", "Ixyt") 33 | pgm.add_node(daft.Node("Iadt", r"$I_{\nu}(\alpha,\delta,t)$", ph_x, ph_y + 2 * dy, aspect=verywide)) 34 | pgm.add_edge("Iadt", "Ixnt") 35 | 36 | # s/c 37 | sc_x, sc_y = ph_x + 1.5, ph_y - 1.5 * dy 38 | pgm.add_node(daft.Node("dark", r"dark", sc_x, sc_y - 1 * dy, aspect=wide)) 39 | pgm.add_edge("dark", "xyti") 40 | pgm.add_node(daft.Node("flat", r"flat", sc_x, sc_y, aspect=wide)) 41 | pgm.add_edge("flat", "xyti") 42 | pgm.add_node(daft.Node("att", r"att", sc_x, sc_y + 3 * dy)) 43 | pgm.add_edge("att", "Ixnt") 44 | pgm.add_node(daft.Node("optics", r"optics", sc_x, sc_y + 2 * dy, aspect=wide)) 45 | pgm.add_edge("optics", "Ixyt") 46 | pgm.add_node(daft.Node("psf", r"psf", sc_x, sc_y + 1 * dy)) 47 | pgm.add_edge("psf", "xyti") 48 | pgm.add_node(daft.Node("fee", r"f.e.e.", sc_x, sc_y - 2 * dy, aspect=wide)) 49 | pgm.add_edge("fee", "xabc") 50 | 51 | # sky 52 | pgm.add_node(daft.Node("sky", r"sky", sc_x, sc_y + 4 * dy)) 53 | pgm.add_edge("sky", "Iadt") 54 | 55 | # stars 56 | star_x, star_y = el_x, el_y + 4 * dy 57 | pgm.add_plate(daft.Plate([star_x - 0.6, star_y - 0.6, 2.2, 2 * dy + 0.3], label="stars $n$")) 58 | pgm.add_node(daft.Node("star adt", r"$I_{\nu,n}(\alpha,\delta,t)$", star_x + 0.5, star_y + 1 * dy, aspect=verywide)) 59 | pgm.add_edge("star adt", "Iadt") 60 | pgm.add_node(daft.Node("star L", r"$L_{\nu,n}(t)$", star_x + 1, star_y, aspect=wide)) 61 | pgm.add_edge("star L", "star adt") 62 | pgm.add_node(daft.Node("star pos", r"$\vec{x_n}$", star_x, star_y)) 63 | pgm.add_edge("star pos", "star adt") 64 | 65 | # done 66 | pgm.render() 67 | pgm.figure.savefig("galex.pdf") 68 | pgm.figure.savefig("galex.png", dpi=150) 69 | -------------------------------------------------------------------------------- /docs/gen_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | 5 | import os 6 | import sys 7 | import json 8 | from subprocess import check_call 9 | 10 | 11 | this_path = os.path.dirname(os.path.abspath(__file__)) 12 | daft_path = os.path.dirname(this_path) 13 | sys.path.insert(0, daft_path) 14 | 15 | example_dir = os.path.join(daft_path, "examples") 16 | out_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "examples") 17 | img_out_dir = os.path.join(this_path, "_static", "examples") 18 | 19 | try: 20 | os.makedirs(out_dir) 21 | except os.error: 22 | pass 23 | 24 | try: 25 | os.makedirs(img_out_dir) 26 | except os.error: 27 | pass 28 | 29 | example_template = """.. _{example}: 30 | 31 | {title} 32 | 33 | .. figure:: /_static/examples/{example}.png 34 | 35 | {doc} 36 | 37 | :: 38 | 39 | {src} 40 | 41 | """ 42 | 43 | 44 | def main(fn, thumb_info): 45 | # Run the code. 46 | pyfn = os.path.join(example_dir, fn + ".py") 47 | src = open(pyfn).read() 48 | print("Executing: " + pyfn) 49 | 50 | ns = {} 51 | exec src in ns 52 | pgm = ns["pgm"] 53 | 54 | # Generate the RST source file. 55 | src = src.split("\n") 56 | if ns["__doc__"] is None: 57 | title = fn.title() + "\n" + "=" * len(fn) 58 | doc = "" 59 | else: 60 | doc = ns["__doc__"].split("\n") 61 | title = "\n".join(doc[:3]) 62 | doc = "\n".join(doc) 63 | src = src[len(ns["__doc__"].split("\n")):] 64 | 65 | fmt_src = "\n".join([" " + l for l in src]) 66 | img_path = os.path.join(img_out_dir, fn + ".png") 67 | thumb_path = os.path.join(img_out_dir, fn + "-thumb.png") 68 | 69 | rst = example_template.format(title=title, doc=doc, example=fn, 70 | src=fmt_src, img_path=img_path) 71 | 72 | # Write the RST file. 73 | rstfn = os.path.join(out_dir, fn + ".rst") 74 | print("Writing: " + rstfn) 75 | with open(rstfn, "w") as f: 76 | f.write(rst) 77 | 78 | # Remove the generated plots. 79 | try: 80 | os.remove(fn + ".png") 81 | except os.error: 82 | pass 83 | try: 84 | os.remove(fn + ".pdf") 85 | except os.error: 86 | pass 87 | 88 | # Save the new figure. 89 | print("Saving: " + img_path) 90 | pgm.figure.savefig(img_path, dpi=150) 91 | 92 | # Crop the thumbnail. 93 | cmd = " ".join(["convert", 94 | "-crop 190x190+{0[0]:d}+{0[1]:d}".format(thumb_info), 95 | img_path, thumb_path]) 96 | print(cmd) 97 | check_call(cmd, shell=True) 98 | 99 | 100 | if __name__ == "__main__": 101 | m = json.load(open(os.path.join(this_path, "_static", "examples.json"))) 102 | if len(sys.argv) == 1: 103 | # Build all the examples. 104 | argv = m.keys() 105 | else: 106 | argv = sys.argv[1:] 107 | 108 | for k in argv: 109 | assert k in m, "Add {0} to _static/examples.json".format(k) 110 | main(k, m[k]) 111 | -------------------------------------------------------------------------------- /docs/_themes/daft/static/daft.css: -------------------------------------------------------------------------------- 1 | @import url("normalize.css"); 2 | @import url(http://fonts.googleapis.com/css?family=Lato:100,300,700,300italic); 3 | @import url(http://fonts.googleapis.com/css?family=Source+Code+Pro:300,400,600); 4 | 5 | body { 6 | font-family: Lato, "Helvetica Neue", Helvetica, sans-serif; 7 | font-weight: 300; 8 | line-height: 1.55; 9 | font-size: 16px; 10 | color: #333; 11 | 12 | background: #dbe0df; 13 | background: -moz-linear-gradient(left, #dbe0df 0%, #ffffff 50%, #dbe0df 100%); 14 | background: -webkit-gradient(linear, left top, right top, color-stop(0%,#dbe0df), color-stop(50%,#ffffff), color-stop(100%,#dbe0df)); 15 | background: -webkit-linear-gradient(left, #dbe0df 0%,#ffffff 50%,#dbe0df 100%); 16 | background: -o-linear-gradient(left, #dbe0df 0%,#ffffff 50%,#dbe0df 100%); 17 | background: -ms-linear-gradient(left, #dbe0df 0%,#ffffff 50%,#dbe0df 100%); 18 | background: linear-gradient(to right, #dbe0df 0%,#ffffff 50%,#dbe0df 100%); 19 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#dbe0df', endColorstr='#dbe0df',GradientType=1 ); 20 | } 21 | 22 | a { 23 | color: #383; 24 | text-decoration: underline; 25 | } 26 | 27 | a:hover { 28 | color: #5a5; 29 | } 30 | 31 | a.headerlink { 32 | padding-left: 10px; 33 | font-size: 80%; 34 | text-decoration: none; 35 | color: #bbb; 36 | } 37 | 38 | a.headerlink:hover { 39 | color: #aaa; 40 | } 41 | 42 | .pre, pre { 43 | font-size: 14px; 44 | font-weight: 400; 45 | font-family: "Source Code Pro", Courier, monospace; 46 | } 47 | 48 | .document, .footer, .related { 49 | width: 600px; 50 | margin: 0 auto; 51 | } 52 | 53 | .body:first-child h1 { 54 | display: none; 55 | } 56 | 57 | .main-logo { 58 | padding: 30px 0 10px; 59 | border-bottom: 1px dashed #e1e1e1; 60 | text-align: center; 61 | } 62 | 63 | .main-logo h1 { 64 | font-size: 48px; 65 | font-weight: 300; 66 | line-height: 0; 67 | text-transform: uppercase; 68 | } 69 | 70 | .main-logo a, .main-logo a:hover { 71 | color: #333; 72 | text-decoration: none; 73 | } 74 | 75 | .main-logo h2 { 76 | color: #888; 77 | font-size: 16px; 78 | font-weight: 300; 79 | line-height: 1; 80 | text-transform: uppercase; 81 | } 82 | 83 | #examples h2 { 84 | display: none; 85 | } 86 | 87 | #examples { 88 | line-height: 0; 89 | margin-top: 5px; 90 | } 91 | 92 | #examples img { 93 | border: 1px solid #ccc; 94 | margin: 5px 0 5px 0; 95 | } 96 | 97 | #examples a:not(:nth-of-type(3n)) img { 98 | margin-right: 8px; 99 | } 100 | 101 | #summary h2 { 102 | display: none; 103 | } 104 | 105 | .related { 106 | margin: 10px 0 0; 107 | padding: 10px 0 0; 108 | border-top: 1px dashed #e1e1e1; 109 | } 110 | 111 | .related .previous { 112 | float: left; 113 | } 114 | 115 | .related .next { 116 | float: right; 117 | } 118 | 119 | .related:after { 120 | content: "."; 121 | display: block; 122 | clear: both; 123 | visibility: hidden; 124 | line-height: 0; 125 | height: 0; 126 | } 127 | 128 | #searchbox { 129 | margin: 10px 0; 130 | text-align: center; 131 | } 132 | 133 | input.searchfield { 134 | border: 1px solid #999; 135 | -moz-border-radius: 15px; 136 | border-radius: 15px; 137 | padding: 3px 10px; 138 | font-weight: normal; 139 | } 140 | 141 | input.searchfield:focus { 142 | outline: 0; 143 | outline: thin dotted \9; 144 | border-color: #ddd; 145 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 3px #5a5; 146 | -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 3px #5a5; 147 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 3px #5a5; 148 | } 149 | 150 | .footer { 151 | text-align: center; 152 | font-size: 11px; 153 | padding: 20px 0; 154 | } 155 | 156 | .figure { 157 | background: white; 158 | text-align: center; 159 | padding: 15px; 160 | margin: 15px 0; 161 | border: 1px solid #DDD; 162 | overflow: auto; 163 | } 164 | 165 | /* API */ 166 | 167 | .field-body { 168 | margin: 0; 169 | padding: 0; 170 | padding-left: 10px; 171 | } 172 | 173 | .field-body ul { 174 | margin: 0; 175 | padding: 0; 176 | list-style: none; 177 | } 178 | 179 | .field-body ul li { 180 | margin: 0; 181 | padding: 0; 182 | } 183 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Daft.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Daft.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Daft" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Daft" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | 155 | examples: ../examples/*.py _static/examples.json 156 | python gen_example.py 157 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import sys 5 | 6 | # If extensions (or modules to document with autodoc) are in another directory, 7 | # add these directories to sys.path here. If the directory is relative to the 8 | # documentation root, use os.path.abspath to make it absolute, like shown here. 9 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 10 | 11 | 12 | import daft 13 | 14 | 15 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", 16 | "sphinx.ext.mathjax"] 17 | 18 | # Add any paths that contain templates here, relative to this directory. 19 | templates_path = ["_templates"] 20 | 21 | # The suffix of source filenames. 22 | source_suffix = ".rst" 23 | 24 | # The master toctree document. 25 | master_doc = "index" 26 | 27 | # General information about the project. 28 | project = u"Daft" 29 | copyright = u"2012, Dan Foreman-Mackey & David W. Hogg" 30 | version = daft.__version__ 31 | release = daft.__version__ 32 | 33 | # There are two options for replacing |today|: either, you set today to some 34 | # non-false value, then it is used: 35 | #today = '' 36 | # Else, today_fmt is used as the format for a strftime call. 37 | #today_fmt = '%B %d, %Y' 38 | 39 | # List of patterns, relative to source directory, that match files and 40 | # directories to ignore when looking for source files. 41 | exclude_patterns = ["_build"] 42 | 43 | # If true, '()' will be appended to :func: etc. cross-reference text. 44 | #add_function_parentheses = True 45 | 46 | # If true, the current module name will be prepended to all description 47 | # unit titles (such as .. function::). 48 | #add_module_names = True 49 | 50 | # If true, sectionauthor and moduleauthor directives will be shown in the 51 | # output. They are ignored by default. 52 | #show_authors = False 53 | 54 | # The name of the Pygments (syntax highlighting) style to use. 55 | pygments_style = "sphinx" 56 | 57 | # A list of ignored prefixes for module index sorting. 58 | #modindex_common_prefix = [] 59 | 60 | # The theme to use for HTML and HTML Help pages. See the documentation for 61 | # a list of builtin themes. 62 | html_theme = "daft" 63 | 64 | # Theme options are theme-specific and customize the look and feel of a theme 65 | # further. For a list of options available for each theme, see the 66 | # documentation. 67 | html_theme_options = { 68 | "tagline": "Beautifully rendered probabilistic graphical models.", 69 | } 70 | 71 | # Add any paths that contain custom themes here, relative to this directory. 72 | html_theme_path = ["_themes"] 73 | 74 | # The name for this set of Sphinx documents. If None, it defaults to 75 | # " v documentation". 76 | #html_title = None 77 | 78 | # A shorter title for the navigation bar. Default is the same as html_title. 79 | #html_short_title = None 80 | 81 | # The name of an image file (relative to this directory) to place at the top 82 | # of the sidebar. 83 | #html_logo = None 84 | 85 | # The name of an image file (within the static path) to use as favicon of the 86 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 87 | # pixels large. 88 | #html_favicon = None 89 | 90 | # Add any paths that contain custom static files (such as style sheets) here, 91 | # relative to this directory. They are copied after the builtin static files, 92 | # so a file named "default.css" will overwrite the builtin "default.css". 93 | html_static_path = ["_static"] 94 | 95 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 96 | # using the given strftime format. 97 | #html_last_updated_fmt = '%b %d, %Y' 98 | 99 | # If true, SmartyPants will be used to convert quotes and dashes to 100 | # typographically correct entities. 101 | #html_use_smartypants = True 102 | 103 | # Custom sidebar templates, maps document names to template names. 104 | html_sidebars = { 105 | "**": ["relations.html", "searchbox.html"] 106 | } 107 | 108 | # Additional templates that should be rendered to pages, maps page names to 109 | # template names. 110 | #html_additional_pages = {} 111 | 112 | # If false, no module index is generated. 113 | #html_domain_indices = True 114 | 115 | # If false, no index is generated. 116 | #html_use_index = True 117 | 118 | # If true, the index is split into individual pages for each letter. 119 | #html_split_index = False 120 | 121 | # If true, links to the reST sources are added to the pages. 122 | html_show_sourcelink = False 123 | 124 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 125 | #html_show_sphinx = True 126 | 127 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 128 | #html_show_copyright = True 129 | 130 | # If true, an OpenSearch description file will be output, and all pages will 131 | # contain a tag referring to it. The value of this option must be the 132 | # base URL from which the finished HTML is served. 133 | #html_use_opensearch = '' 134 | 135 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 136 | #html_file_suffix = None 137 | 138 | # Output file base name for HTML help builder. 139 | htmlhelp_basename = 'Daftdoc' 140 | 141 | # LaTeX Options. 142 | latex_elements = { 143 | # The paper size ('letterpaper' or 'a4paper'). 144 | #'papersize': 'letterpaper', 145 | 146 | # The font size ('10pt', '11pt' or '12pt'). 147 | #'pointsize': '10pt', 148 | 149 | # Additional stuff for the LaTeX preamble. 150 | #'preamble': '', 151 | } 152 | 153 | # Grouping the document tree into LaTeX files. List of tuples 154 | # (source start file, target name, title, author, documentclass 155 | # [howto/manual]). 156 | latex_documents = [ 157 | ('index', 'Daft.tex', u'Daft Documentation', 158 | u'Dan Foreman-Mackey \\& David W. Hogg', 'manual'), 159 | ] 160 | 161 | # The name of an image file (relative to this directory) to place at the top of 162 | # the title page. 163 | #latex_logo = None 164 | 165 | # For "manual" documents, if this is true, then toplevel headings are parts, 166 | # not chapters. 167 | #latex_use_parts = False 168 | 169 | # If true, show page references after internal links. 170 | #latex_show_pagerefs = False 171 | 172 | # If true, show URL addresses after external links. 173 | #latex_show_urls = False 174 | 175 | # Documents to append as an appendix to all manuals. 176 | #latex_appendices = [] 177 | 178 | # If false, no module index is generated. 179 | #latex_domain_indices = True 180 | 181 | 182 | # One entry per manual page. List of tuples 183 | # (source start file, name, description, authors, manual section). 184 | man_pages = [ 185 | ('index', 'daft', u'Daft Documentation', 186 | [u'Dan Foreman-Mackey & David W. Hogg'], 1) 187 | ] 188 | 189 | # If true, show URL addresses after external links. 190 | #man_show_urls = False 191 | 192 | 193 | # Grouping the document tree into Texinfo files. List of tuples 194 | # (source start file, target name, title, author, 195 | # dir menu entry, description, category) 196 | texinfo_documents = [ 197 | ('index', 'Daft', u'Daft Documentation', 198 | u'Dan Foreman-Mackey & David W. Hogg', 'Daft', 199 | 'One line description of project.', 'Miscellaneous'), 200 | ] 201 | 202 | # Documents to append as an appendix to all manuals. 203 | #texinfo_appendices = [] 204 | 205 | # If false, no module index is generated. 206 | #texinfo_domain_indices = True 207 | 208 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 209 | #texinfo_show_urls = 'footnote' 210 | 211 | 212 | # Example configuration for intersphinx: refer to the Python standard library. 213 | intersphinx_mapping = {'http://docs.python.org/': None} 214 | -------------------------------------------------------------------------------- /examples/astronomy.py: -------------------------------------------------------------------------------- 1 | """ 2 | Astronomical imaging 3 | ==================== 4 | 5 | This is a model for every pixel of every astronomical image ever 6 | taken. It is incomplete! 7 | 8 | """ 9 | 10 | from matplotlib import rc 11 | rc("font", family="serif", size=12) 12 | rc("text", usetex=True) 13 | 14 | import daft 15 | 16 | pgm = daft.PGM([8, 6.75], origin=[0.5, 0.5], grid_unit=4., node_unit=1.4) 17 | 18 | # Start with the plates. 19 | tweak=0.02 20 | rect_params = {"lw": 2} 21 | pgm.add_plate(daft.Plate([1.5+tweak, 0.5+tweak, 6.0-2*tweak, 3.75-2*tweak], label=r"\Large telescope+camera+filter multiplets", rect_params=rect_params)) 22 | pgm.add_plate(daft.Plate([2.5+tweak, 1.0+tweak, 4.0-2*tweak, 2.75-2*tweak], label=r"\Large images", rect_params=rect_params)) 23 | pgm.add_plate(daft.Plate([3.5+tweak, 1.5+tweak, 2.0-2*tweak, 1.75-2*tweak], label=r"\Large pixel patches", rect_params=rect_params)) 24 | pgm.add_plate(daft.Plate([1.0+tweak, 4.25+tweak, 3.5-2*tweak, 1.75-2*tweak], label=r"\Large stars", rect_params=rect_params)) 25 | pgm.add_plate(daft.Plate([5.5+tweak, 4.25+tweak, 2.5-2*tweak, 1.75-2*tweak], label=r"\Large galaxies", rect_params=rect_params)) 26 | 27 | # ONLY pixels are observed 28 | asp = 2.3 29 | pgm.add_node(daft.Node("true pixels", r"~\\noise-free\\pixel patch", 5.0, 2.5, aspect=asp)) 30 | pgm.add_node(daft.Node("pixels", r"pixel patch", 4.0, 2.0, observed=True, aspect=asp)) 31 | pgm.add_edge("true pixels", "pixels") 32 | 33 | # The sky 34 | pgm.add_node(daft.Node("sky", r"sky model", 6.0, 2.5, aspect=asp)) 35 | pgm.add_edge("sky", "true pixels") 36 | pgm.add_node(daft.Node("sky prior", r"sky priors", 8.0, 2.5, fixed=True)) 37 | pgm.add_edge("sky prior", "sky") 38 | 39 | # Stars 40 | pgm.add_node(daft.Node("star patch", r"star patch", 4.0, 3.0, aspect=asp)) 41 | pgm.add_edge("star patch", "true pixels") 42 | pgm.add_node(daft.Node("star SED", r"~\\spectral energy\\distribution", 2.5, 4.75, aspect=asp+0.2)) 43 | pgm.add_edge("star SED", "star patch") 44 | pgm.add_node(daft.Node("star position", r"position", 4.0, 4.75, aspect=asp)) 45 | pgm.add_edge("star position", "star patch") 46 | pgm.add_node(daft.Node("temperature", r"temperature", 1.5, 5.25, aspect=asp)) 47 | pgm.add_edge("temperature", "star SED") 48 | pgm.add_node(daft.Node("luminosity", r"luminosity", 2.5, 5.25, aspect=asp)) 49 | pgm.add_edge("luminosity", "star SED") 50 | pgm.add_node(daft.Node("metallicity", r"metallicity", 1.5, 5.75, aspect=asp)) 51 | pgm.add_edge("metallicity", "star SED") 52 | pgm.add_edge("metallicity", "temperature") 53 | pgm.add_edge("metallicity", "luminosity") 54 | pgm.add_node(daft.Node("mass", r"mass", 2.5, 5.75, aspect=asp)) 55 | pgm.add_edge("mass", "temperature") 56 | pgm.add_edge("mass", "luminosity") 57 | pgm.add_node(daft.Node("age", r"age", 3.5, 5.75, aspect=asp)) 58 | pgm.add_edge("age", "temperature") 59 | pgm.add_edge("age", "luminosity") 60 | pgm.add_node(daft.Node("star models", r"star models", 1.0, 4.0, fixed=True)) 61 | pgm.add_edge("star models", "temperature") 62 | pgm.add_edge("star models", "luminosity") 63 | pgm.add_edge("star models", "star SED") 64 | 65 | # Galaxies 66 | pgm.add_node(daft.Node("galaxy patch", r"galaxy patch", 5.0, 3.0, aspect=asp)) 67 | pgm.add_edge("galaxy patch", "true pixels") 68 | pgm.add_node(daft.Node("galaxy SED", r"~\\spectral energy\\distribution", 6.5, 4.75, aspect=asp+0.2)) 69 | pgm.add_edge("galaxy SED", "galaxy patch") 70 | pgm.add_node(daft.Node("morphology", r"morphology", 7.5, 4.75, aspect=asp)) 71 | pgm.add_edge("morphology", "galaxy patch") 72 | pgm.add_node(daft.Node("SFH", r"~\\star-formation\\history", 7.5, 5.25, aspect=asp)) 73 | pgm.add_edge("SFH", "galaxy SED") 74 | pgm.add_edge("SFH", "morphology") 75 | pgm.add_node(daft.Node("galaxy position", r"~\\redshift\\ \& position", 6.0, 5.25, aspect=asp)) 76 | pgm.add_edge("galaxy position", "galaxy SED") 77 | pgm.add_edge("galaxy position", "morphology") 78 | pgm.add_edge("galaxy position", "galaxy patch") 79 | pgm.add_node(daft.Node("dynamics", r"orbit structure", 6.5, 5.75, aspect=asp)) 80 | pgm.add_edge("dynamics", "morphology") 81 | pgm.add_edge("dynamics", "SFH") 82 | pgm.add_node(daft.Node("galaxy mass", r"mass", 7.5, 5.75, aspect=asp)) 83 | pgm.add_edge("galaxy mass", "dynamics") 84 | pgm.add_edge("galaxy mass", "galaxy SED") 85 | pgm.add_edge("galaxy mass", "SFH") 86 | 87 | # Universals 88 | pgm.add_node(daft.Node("extinction model", r"~\\extinction\\model", 5.0, 4.75, aspect=asp)) 89 | pgm.add_edge("extinction model", "star patch") 90 | pgm.add_edge("extinction model", "galaxy patch") 91 | pgm.add_node(daft.Node("MW", r"~\\Milky Way\\formation", 4.0, 6.5, aspect=asp)) 92 | pgm.add_edge("MW", "metallicity") 93 | pgm.add_edge("MW", "mass") 94 | pgm.add_edge("MW", "age") 95 | pgm.add_edge("MW", "star position") 96 | pgm.add_edge("MW", "extinction model") 97 | pgm.add_node(daft.Node("galaxy formation", r"~\\galaxy\\formation", 5.0, 6.5, aspect=asp)) 98 | pgm.add_edge("galaxy formation", "MW") 99 | pgm.add_edge("galaxy formation", "dynamics") 100 | pgm.add_edge("galaxy formation", "galaxy mass") 101 | pgm.add_edge("galaxy formation", "extinction model") 102 | pgm.add_node(daft.Node("LSS", r"~\\large-scale\\structure", 6.0, 6.5, aspect=asp)) 103 | pgm.add_edge("LSS", "galaxy position") 104 | pgm.add_node(daft.Node("cosmology", r"~\\cosmological\\parameters", 6.0, 7.0, aspect=asp)) 105 | pgm.add_edge("cosmology", "LSS") 106 | pgm.add_edge("cosmology", "galaxy formation") 107 | pgm.add_node(daft.Node("god", r"God", 7.0, 7.0, fixed=True)) 108 | pgm.add_edge("god", "cosmology") 109 | 110 | # Sensitivity 111 | pgm.add_node(daft.Node("zeropoint", r"~\\zeropoint\\(photocal)", 3.0, 3.0, aspect=asp)) 112 | pgm.add_edge("zeropoint", "true pixels") 113 | pgm.add_node(daft.Node("exposure time", r"exposure time", 3.0, 2.5, observed=True, aspect=asp)) 114 | pgm.add_edge("exposure time", "zeropoint") 115 | 116 | # The PSF 117 | pgm.add_node(daft.Node("WCS", r"~\\astrometric\\calibration", 3.0, 2.0, aspect=asp)) 118 | pgm.add_edge("WCS", "star patch") 119 | pgm.add_edge("WCS", "galaxy patch") 120 | pgm.add_node(daft.Node("psf", r"PSF model", 3.0, 3.5, aspect=asp)) 121 | pgm.add_edge("psf", "star patch") 122 | pgm.add_edge("psf", "galaxy patch") 123 | pgm.add_node(daft.Node("optics", r"optics", 2.0, 3.0, aspect=asp-1.2)) 124 | pgm.add_edge("optics", "psf") 125 | pgm.add_edge("optics", "WCS") 126 | pgm.add_node(daft.Node("atmosphere", r"~\\atmosphere\\model", 1.0, 3.5, aspect=asp)) 127 | pgm.add_edge("atmosphere", "psf") 128 | pgm.add_edge("atmosphere", "WCS") 129 | pgm.add_edge("atmosphere", "zeropoint") 130 | 131 | # The device 132 | pgm.add_node(daft.Node("flatfield", r"flat-field", 2.0, 1.5, aspect=asp)) 133 | pgm.add_edge("flatfield", "pixels") 134 | pgm.add_node(daft.Node("nonlinearity", r"non-linearity", 2.0, 1.0, aspect=asp)) 135 | pgm.add_edge("nonlinearity", "pixels") 136 | pgm.add_node(daft.Node("pointing", r"~\\telescope\\pointing etc.", 2.0, 2.0, aspect=asp)) 137 | pgm.add_edge("pointing", "WCS") 138 | pgm.add_node(daft.Node("detector", r"detector priors", 1.0, 1.5, fixed=True)) 139 | pgm.add_edge("detector", "flatfield") 140 | pgm.add_edge("detector", "nonlinearity") 141 | pgm.add_node(daft.Node("hardware", r"hardware priors", 1.0, 2.5, fixed=True)) 142 | pgm.add_edge("hardware", "pointing") 143 | pgm.add_edge("hardware", "exposure time") 144 | pgm.add_edge("hardware", "optics") 145 | 146 | # Noise 147 | pgm.add_node(daft.Node("noise patch", r"noise patch", 5.0, 2.0, aspect=asp)) 148 | pgm.add_edge("noise patch", "pixels") 149 | pgm.add_edge("true pixels", "noise patch") 150 | pgm.add_node(daft.Node("noise model", r"noise model", 7.0, 2.0, aspect=asp)) 151 | pgm.add_edge("noise model", "noise patch") 152 | pgm.add_node(daft.Node("noise prior", r"noise priors", 8.0, 2.0, fixed=True)) 153 | pgm.add_edge("noise prior", "noise model") 154 | pgm.add_node(daft.Node("cosmic rays", r"~\\cosmic-ray\\model", 8.0, 1.5, aspect=asp)) 155 | pgm.add_edge("cosmic rays", "noise patch") 156 | 157 | # Render and save. 158 | pgm.render() 159 | pgm.figure.savefig("astronomy.pdf") 160 | pgm.figure.savefig("astronomy.png", dpi=150) 161 | -------------------------------------------------------------------------------- /docs/_themes/daft/static/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /* 8 | * Corrects `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | nav, 20 | section, 21 | summary { 22 | display: block; 23 | } 24 | 25 | /* 26 | * Corrects `inline-block` display not defined in IE 8/9. 27 | */ 28 | 29 | audio, 30 | canvas, 31 | video { 32 | display: inline-block; 33 | } 34 | 35 | /* 36 | * Prevents modern browsers from displaying `audio` without controls. 37 | * Remove excess height in iOS 5 devices. 38 | */ 39 | 40 | audio:not([controls]) { 41 | display: none; 42 | height: 0; 43 | } 44 | 45 | /* 46 | * Addresses styling for `hidden` attribute not present in IE 8/9. 47 | */ 48 | 49 | [hidden] { 50 | display: none; 51 | } 52 | 53 | /* ========================================================================== 54 | Base 55 | ========================================================================== */ 56 | 57 | /* 58 | * 1. Sets default font family to sans-serif. 59 | * 2. Prevents iOS text size adjust after orientation change, without disabling 60 | * user zoom. 61 | */ 62 | 63 | html { 64 | font-family: sans-serif; /* 1 */ 65 | -webkit-text-size-adjust: 100%; /* 2 */ 66 | -ms-text-size-adjust: 100%; /* 2 */ 67 | } 68 | 69 | /* 70 | * Removes default margin. 71 | */ 72 | 73 | body { 74 | margin: 0; 75 | } 76 | 77 | /* ========================================================================== 78 | Links 79 | ========================================================================== */ 80 | 81 | /* 82 | * Addresses `outline` inconsistency between Chrome and other browsers. 83 | */ 84 | 85 | a:focus { 86 | outline: thin dotted; 87 | } 88 | 89 | /* 90 | * Improves readability when focused and also mouse hovered in all browsers. 91 | */ 92 | 93 | a:active, 94 | a:hover { 95 | outline: 0; 96 | } 97 | 98 | /* ========================================================================== 99 | Typography 100 | ========================================================================== */ 101 | 102 | /* 103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, 104 | * Safari 5, and Chrome. 105 | */ 106 | 107 | h1 { 108 | font-size: 2em; 109 | } 110 | 111 | /* 112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome. 113 | */ 114 | 115 | abbr[title] { 116 | border-bottom: 1px dotted; 117 | } 118 | 119 | /* 120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 121 | */ 122 | 123 | b, 124 | strong { 125 | font-weight: bold; 126 | } 127 | 128 | /* 129 | * Addresses styling not present in Safari 5 and Chrome. 130 | */ 131 | 132 | dfn { 133 | font-style: italic; 134 | } 135 | 136 | /* 137 | * Addresses styling not present in IE 8/9. 138 | */ 139 | 140 | mark { 141 | background: #ff0; 142 | color: #000; 143 | } 144 | 145 | 146 | /* 147 | * Corrects font family set oddly in Safari 5 and Chrome. 148 | */ 149 | 150 | code, 151 | kbd, 152 | pre, 153 | samp { 154 | font-family: monospace, serif; 155 | font-size: 1em; 156 | } 157 | 158 | /* 159 | * Improves readability of pre-formatted text in all browsers. 160 | */ 161 | 162 | pre { 163 | white-space: pre; 164 | white-space: pre-wrap; 165 | word-wrap: break-word; 166 | } 167 | 168 | /* 169 | * Sets consistent quote types. 170 | */ 171 | 172 | q { 173 | quotes: "\201C" "\201D" "\2018" "\2019"; 174 | } 175 | 176 | /* 177 | * Addresses inconsistent and variable font size in all browsers. 178 | */ 179 | 180 | small { 181 | font-size: 80%; 182 | } 183 | 184 | /* 185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers. 186 | */ 187 | 188 | sub, 189 | sup { 190 | font-size: 75%; 191 | line-height: 0; 192 | position: relative; 193 | vertical-align: baseline; 194 | } 195 | 196 | sup { 197 | top: -0.5em; 198 | } 199 | 200 | sub { 201 | bottom: -0.25em; 202 | } 203 | 204 | /* ========================================================================== 205 | Embedded content 206 | ========================================================================== */ 207 | 208 | /* 209 | * Removes border when inside `a` element in IE 8/9. 210 | */ 211 | 212 | img { 213 | border: 0; 214 | } 215 | 216 | /* 217 | * Corrects overflow displayed oddly in IE 9. 218 | */ 219 | 220 | svg:not(:root) { 221 | overflow: hidden; 222 | } 223 | 224 | /* ========================================================================== 225 | Figures 226 | ========================================================================== */ 227 | 228 | /* 229 | * Addresses margin not present in IE 8/9 and Safari 5. 230 | */ 231 | 232 | figure { 233 | margin: 0; 234 | } 235 | 236 | /* ========================================================================== 237 | Forms 238 | ========================================================================== */ 239 | 240 | /* 241 | * Define consistent border, margin, and padding. 242 | */ 243 | 244 | fieldset { 245 | border: 1px solid #c0c0c0; 246 | margin: 0 2px; 247 | padding: 0.35em 0.625em 0.75em; 248 | } 249 | 250 | /* 251 | * 1. Corrects color not being inherited in IE 8/9. 252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 253 | */ 254 | 255 | legend { 256 | border: 0; /* 1 */ 257 | padding: 0; /* 2 */ 258 | } 259 | 260 | /* 261 | * 1. Corrects font family not being inherited in all browsers. 262 | * 2. Corrects font size not being inherited in all browsers. 263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome 264 | */ 265 | 266 | button, 267 | input, 268 | select, 269 | textarea { 270 | font-family: inherit; /* 1 */ 271 | font-size: 100%; /* 2 */ 272 | margin: 0; /* 3 */ 273 | } 274 | 275 | /* 276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in 277 | * the UA stylesheet. 278 | */ 279 | 280 | button, 281 | input { 282 | line-height: normal; 283 | } 284 | 285 | /* 286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 287 | * and `video` controls. 288 | * 2. Corrects inability to style clickable `input` types in iOS. 289 | * 3. Improves usability and consistency of cursor style between image-type 290 | * `input` and others. 291 | */ 292 | 293 | button, 294 | html input[type="button"], /* 1 */ 295 | input[type="reset"], 296 | input[type="submit"] { 297 | -webkit-appearance: button; /* 2 */ 298 | cursor: pointer; /* 3 */ 299 | } 300 | 301 | /* 302 | * Re-set default cursor for disabled elements. 303 | */ 304 | 305 | button[disabled], 306 | input[disabled] { 307 | cursor: default; 308 | } 309 | 310 | /* 311 | * 1. Addresses box sizing set to `content-box` in IE 8/9. 312 | * 2. Removes excess padding in IE 8/9. 313 | */ 314 | 315 | input[type="checkbox"], 316 | input[type="radio"] { 317 | box-sizing: border-box; /* 1 */ 318 | padding: 0; /* 2 */ 319 | } 320 | 321 | /* 322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. 323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome 324 | * (include `-moz` to future-proof). 325 | */ 326 | 327 | input[type="search"] { 328 | -webkit-appearance: textfield; /* 1 */ 329 | -moz-box-sizing: content-box; 330 | -webkit-box-sizing: content-box; /* 2 */ 331 | box-sizing: content-box; 332 | } 333 | 334 | /* 335 | * Removes inner padding and search cancel button in Safari 5 and Chrome 336 | * on OS X. 337 | */ 338 | 339 | input[type="search"]::-webkit-search-cancel-button, 340 | input[type="search"]::-webkit-search-decoration { 341 | -webkit-appearance: none; 342 | } 343 | 344 | /* 345 | * Removes inner padding and border in Firefox 4+. 346 | */ 347 | 348 | button::-moz-focus-inner, 349 | input::-moz-focus-inner { 350 | border: 0; 351 | padding: 0; 352 | } 353 | 354 | /* 355 | * 1. Removes default vertical scrollbar in IE 8/9. 356 | * 2. Improves readability and alignment in all browsers. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; /* 1 */ 361 | vertical-align: top; /* 2 */ 362 | } 363 | 364 | /* ========================================================================== 365 | Tables 366 | ========================================================================== */ 367 | 368 | /* 369 | * Remove most spacing between table cells. 370 | */ 371 | 372 | table { 373 | border-collapse: collapse; 374 | border-spacing: 0; 375 | } 376 | -------------------------------------------------------------------------------- /daft.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import division, print_function 4 | 5 | __all__ = ["PGM", "Node", "Edge", "Plate"] 6 | __version__ = "0.0.4-dev" 7 | 8 | import matplotlib.pyplot as plt 9 | from matplotlib.patches import Ellipse 10 | from matplotlib.patches import FancyArrow 11 | from matplotlib.patches import Rectangle as Rectangle 12 | 13 | import numpy as np 14 | 15 | 16 | class PGM(object): 17 | """ 18 | The base object for building a graphical model representation. 19 | 20 | :param shape: 21 | The number of rows and columns in the grid. 22 | 23 | :param origin: 24 | The coordinates of the bottom left corner of the plot. 25 | 26 | :param grid_unit: (optional) 27 | The size of the grid spacing measured in centimeters. 28 | 29 | :param node_unit: (optional) 30 | The base unit for the node size. This is a number in centimeters that 31 | sets the default diameter of the nodes. 32 | 33 | :param observed_style: (optional) 34 | How should the "observed" nodes be indicated? This must be one of: 35 | ``"shaded"``, ``"inner"`` or ``"outer"`` where ``inner`` and 36 | ``outer`` nodes are shown as double circles with the second circle 37 | plotted inside or outside of the standard one, respectively. 38 | 39 | :param node_ec: (optional) 40 | The default edge color for the nodes. 41 | 42 | :param directed: (optional) 43 | Should the edges be directed by default? 44 | 45 | :param aspect: (optional) 46 | The default aspect ratio for the nodes. 47 | 48 | :param label_params: (optional) 49 | Default node label parameters. 50 | 51 | """ 52 | def __init__(self, shape, origin=[0, 0], 53 | grid_unit=2, node_unit=1, 54 | observed_style="shaded", 55 | line_width=1, node_ec="k", 56 | directed=True, aspect=1.0, 57 | label_params={}): 58 | self._nodes = {} 59 | self._edges = [] 60 | self._plates = [] 61 | 62 | self._ctx = _rendering_context(shape=shape, origin=origin, 63 | grid_unit=grid_unit, 64 | node_unit=node_unit, 65 | observed_style=observed_style, 66 | line_width=line_width, 67 | node_ec=node_ec, directed=directed, 68 | aspect=aspect, 69 | label_params=label_params) 70 | 71 | def add_node(self, node): 72 | """ 73 | Add a :class:`Node` to the model. 74 | 75 | :param node: 76 | The :class:`Node` instance to add. 77 | 78 | """ 79 | self._nodes[node.name] = node 80 | return node 81 | 82 | def add_edge(self, name1, name2, directed=None, **kwargs): 83 | """ 84 | Construct an :class:`Edge` between two named :class:`Node` objects. 85 | 86 | :param name1: 87 | The name identifying the first node. 88 | 89 | :param name2: 90 | The name identifying the second node. If the edge is directed, 91 | the arrow will point to this node. 92 | 93 | :param directed: (optional) 94 | Should this be a directed edge? 95 | 96 | """ 97 | if directed is None: 98 | directed = self._ctx.directed 99 | 100 | e = Edge(self._nodes[name1], self._nodes[name2], directed=directed, 101 | plot_params=kwargs) 102 | self._edges.append(e) 103 | 104 | return e 105 | 106 | def add_plate(self, plate): 107 | """ 108 | Add a :class:`Plate` object to the model. 109 | 110 | """ 111 | self._plates.append(plate) 112 | return None 113 | 114 | def render(self): 115 | """ 116 | Render the :class:`Plate`, :class:`Edge` and :class:`Node` objects in 117 | the model. This will create a new figure with the correct dimensions 118 | and plot the model in this area. 119 | 120 | """ 121 | self.figure = self._ctx.figure() 122 | self.ax = self._ctx.ax() 123 | 124 | for plate in self._plates: 125 | plate.render(self._ctx) 126 | 127 | for edge in self._edges: 128 | edge.render(self._ctx) 129 | 130 | for name in self._nodes: 131 | self._nodes[name].render(self._ctx) 132 | 133 | return self.ax 134 | 135 | 136 | class Node(object): 137 | """ 138 | The representation of a random variable in a :class:`PGM`. 139 | 140 | :param name: 141 | The plain-text identifier for the node. 142 | 143 | :param content: 144 | The display form of the variable. 145 | 146 | :param x: 147 | The x-coordinate of the node in *model units*. 148 | 149 | :param y: 150 | The y-coordinate of the node. 151 | 152 | :param scale: (optional) 153 | The diameter (or height) of the node measured in multiples of 154 | ``node_unit`` as defined by the :class:`PGM` object. 155 | 156 | :param aspect: (optional) 157 | The aspect ratio width/height for elliptical nodes; default 1. 158 | 159 | :param observed: (optional) 160 | Should this be a conditioned variable? 161 | 162 | :param fixed: (optional) 163 | Should this be a fixed (not permitted to vary) variable? 164 | If `True`, modifies or over-rides ``diameter``, ``offset``, 165 | ``facecolor``, and a few other ``plot_params`` settings. 166 | This setting conflicts with ``observed``. 167 | 168 | :param offset: (optional) 169 | The ``(dx, dy)`` offset of the label (in points) from the default 170 | centered position. 171 | 172 | :param plot_params: (optional) 173 | A dictionary of parameters to pass to the 174 | :class:`matplotlib.patches.Ellipse` constructor. 175 | 176 | """ 177 | def __init__(self, name, content, x, y, scale=1, aspect=None, 178 | observed=False, fixed=False, 179 | offset=[0, 0], plot_params={}, label_params=None): 180 | # Node style. 181 | assert not (observed and fixed), \ 182 | "A node cannot be both 'observed' and 'fixed'." 183 | self.observed = observed 184 | self.fixed = fixed 185 | 186 | # Metadata. 187 | self.name = name 188 | self.content = content 189 | 190 | # Coordinates and dimensions. 191 | self.x, self.y = x, y 192 | self.scale = scale 193 | if self.fixed: 194 | self.scale /= 6.0 195 | self.aspect = aspect 196 | 197 | # Display parameters. 198 | self.plot_params = dict(plot_params) 199 | 200 | # Text parameters. 201 | self.offset = list(offset) 202 | if label_params is not None: 203 | self.label_params = dict(label_params) 204 | else: 205 | self.label_params = None 206 | 207 | def render(self, ctx): 208 | """ 209 | Render the node. 210 | 211 | :param ctx: 212 | The :class:`_rendering_context` object. 213 | 214 | """ 215 | # Get the axes and default plotting parameters from the rendering 216 | # context. 217 | ax = ctx.ax() 218 | 219 | # Resolve the plotting parameters. 220 | p = dict(self.plot_params) 221 | p["lw"] = _pop_multiple(p, ctx.line_width, "lw", "linewidth") 222 | 223 | p["ec"] = p["edgecolor"] = _pop_multiple(p, ctx.node_ec, 224 | "ec", "edgecolor") 225 | 226 | p["fc"] = _pop_multiple(p, "none", "fc", "facecolor") 227 | fc = p["fc"] 228 | 229 | p["alpha"] = p.get("alpha", 1) 230 | 231 | # And the label parameters. 232 | if self.label_params is None: 233 | l = dict(ctx.label_params) 234 | else: 235 | l = dict(self.label_params) 236 | 237 | l["va"] = _pop_multiple(l, "center", "va", "verticalalignment") 238 | l["ha"] = _pop_multiple(l, "center", "ha", "horizontalalignment") 239 | 240 | # Deal with ``fixed`` nodes. 241 | scale = self.scale 242 | if self.fixed: 243 | # MAGIC: These magic numbers should depend on the grid/node units. 244 | self.offset[1] += 6 245 | 246 | l["va"] = "baseline" 247 | l.pop("verticalalignment", None) 248 | l.pop("ma", None) 249 | 250 | if p["fc"] == "none": 251 | p["fc"] = "k" 252 | 253 | diameter = ctx.node_unit * scale 254 | if self.aspect is not None: 255 | aspect = self.aspect 256 | else: 257 | aspect = ctx.aspect 258 | 259 | # Set up an observed node. Note the fc INSANITY. 260 | if self.observed: 261 | # Update the plotting parameters depending on the style of 262 | # observed node. 263 | h = float(diameter) 264 | w = aspect * float(diameter) 265 | if ctx.observed_style == "shaded": 266 | p["fc"] = "0.7" 267 | elif ctx.observed_style == "outer": 268 | h = diameter + 0.1 * diameter 269 | w = aspect * diameter + 0.1 * diameter 270 | elif ctx.observed_style == "inner": 271 | h = diameter - 0.1 * diameter 272 | w = aspect * diameter - 0.1 * diameter 273 | p["fc"] = fc 274 | 275 | # Draw the background ellipse. 276 | bg = Ellipse(xy=ctx.convert(self.x, self.y), 277 | width=w, height=h, **p) 278 | ax.add_artist(bg) 279 | 280 | # Reset the face color. 281 | p["fc"] = fc 282 | 283 | # Draw the foreground ellipse. 284 | if ctx.observed_style == "inner" and not self.fixed: 285 | p["fc"] = "none" 286 | el = Ellipse(xy=ctx.convert(self.x, self.y), 287 | width=diameter * aspect, height=diameter, **p) 288 | ax.add_artist(el) 289 | 290 | # Reset the face color. 291 | p["fc"] = fc 292 | 293 | # Annotate the node. 294 | ax.annotate(self.content, ctx.convert(self.x, self.y), 295 | xycoords="data", 296 | xytext=self.offset, textcoords="offset points", 297 | **l) 298 | 299 | return el 300 | 301 | 302 | class Edge(object): 303 | """ 304 | An edge between two :class:`Node` objects. 305 | 306 | :param node1: 307 | The first :class:`Node`. 308 | 309 | :param node2: 310 | The second :class:`Node`. The arrow will point towards this node. 311 | 312 | :param directed: (optional) 313 | Should the edge be directed from ``node1`` to ``node2``? In other 314 | words: should it have an arrow? 315 | 316 | :param plot_params: (optional) 317 | A dictionary of parameters to pass to the plotting command when 318 | rendering. 319 | 320 | """ 321 | def __init__(self, node1, node2, directed=True, plot_params={}): 322 | self.node1 = node1 323 | self.node2 = node2 324 | self.directed = directed 325 | self.plot_params = dict(plot_params) 326 | 327 | def _get_coords(self, ctx): 328 | """ 329 | Get the coordinates of the line. 330 | 331 | :param conv: 332 | A callable coordinate conversion. 333 | 334 | :returns: 335 | * ``x0``, ``y0``: the coordinates of the start of the line. 336 | * ``dx0``, ``dy0``: the displacement vector. 337 | 338 | """ 339 | # Scale the coordinates appropriately. 340 | x1, y1 = ctx.convert(self.node1.x, self.node1.y) 341 | x2, y2 = ctx.convert(self.node2.x, self.node2.y) 342 | 343 | # Aspect ratios. 344 | a1, a2 = self.node1.aspect, self.node2.aspect 345 | if a1 is None: 346 | a1 = ctx.aspect 347 | if a2 is None: 348 | a2 = ctx.aspect 349 | 350 | # Compute the distances. 351 | dx, dy = x2 - x1, y2 - y1 352 | dist1 = np.sqrt(dy * dy + dx * dx / float(a1 ** 2)) 353 | dist2 = np.sqrt(dy * dy + dx * dx / float(a2 ** 2)) 354 | 355 | # Compute the fractional effect of the radii of the nodes. 356 | alpha1 = 0.5 * ctx.node_unit * self.node1.scale / dist1 357 | alpha2 = 0.5 * ctx.node_unit * self.node2.scale / dist2 358 | 359 | # Get the coordinates of the starting position. 360 | x0, y0 = x1 + alpha1 * dx, y1 + alpha1 * dy 361 | 362 | # Get the width and height of the line. 363 | dx0 = dx * (1. - alpha1 - alpha2) 364 | dy0 = dy * (1. - alpha1 - alpha2) 365 | 366 | return x0, y0, dx0, dy0 367 | 368 | def render(self, ctx): 369 | """ 370 | Render the edge in the given axes. 371 | 372 | :param ctx: 373 | The :class:`_rendering_context` object. 374 | 375 | """ 376 | ax = ctx.ax() 377 | 378 | p = self.plot_params 379 | p["linewidth"] = _pop_multiple(p, ctx.line_width, 380 | "lw", "linewidth") 381 | 382 | # Add edge annotation. 383 | if "label" in self.plot_params: 384 | x, y, dx, dy = self._get_coords(ctx) 385 | ax.annotate(self.plot_params["label"], 386 | [x + 0.5 * dx, y + 0.5 * dy], xycoords="data", 387 | xytext=[0, 3], textcoords="offset points", 388 | ha="center", va="center") 389 | 390 | if self.directed: 391 | p["ec"] = _pop_multiple(p, "k", "ec", "edgecolor") 392 | p["fc"] = _pop_multiple(p, "k", "fc", "facecolor") 393 | p["head_length"] = p.get("head_length", 0.25) 394 | p["head_width"] = p.get("head_width", 0.1) 395 | 396 | # Build an arrow. 397 | ar = FancyArrow(*self._get_coords(ctx), width=0, 398 | length_includes_head=True, 399 | **p) 400 | 401 | # Add the arrow to the axes. 402 | ax.add_artist(ar) 403 | return ar 404 | else: 405 | p["color"] = p.get("color", "k") 406 | 407 | # Get the right coordinates. 408 | x, y, dx, dy = self._get_coords(ctx) 409 | 410 | # Plot the line. 411 | line = ax.plot([x, x + dx], [y, y + dy], **p) 412 | return line 413 | 414 | 415 | class Plate(object): 416 | """ 417 | A plate to encapsulate repeated independent processes in the model. 418 | 419 | :param rect: 420 | The rectangle describing the plate bounds in model coordinates. 421 | 422 | :param label: (optional) 423 | A string to annotate the plate. 424 | 425 | :param label_offset: (optional) 426 | The x and y offsets of the label text measured in points. 427 | 428 | :param shift: (optional) 429 | The vertical "shift" of the plate measured in model units. This will 430 | move the bottom of the panel by ``shift`` units. 431 | 432 | :param position: (optional) 433 | One of ``"bottom left"`` or ``"bottom right"``. 434 | 435 | :param rect_params: (optional) 436 | A dictionary of parameters to pass to the 437 | :class:`matplotlib.patches.Rectangle` constructor. 438 | 439 | """ 440 | def __init__(self, rect, label=None, label_offset=[5, 5], shift=0, 441 | position="bottom left", rect_params={}, bbox={}): 442 | self.rect = rect 443 | self.label = label 444 | self.label_offset = label_offset 445 | self.shift = shift 446 | self.rect_params = dict(rect_params) 447 | self.bbox = dict(bbox) 448 | self.position = position 449 | 450 | def render(self, ctx): 451 | """ 452 | Render the plate in the given axes. 453 | 454 | :param ctx: 455 | The :class:`_rendering_context` object. 456 | 457 | """ 458 | ax = ctx.ax() 459 | 460 | s = np.array([0, self.shift]) 461 | r = np.atleast_1d(self.rect) 462 | bl = ctx.convert(*(r[:2] + s)) 463 | tr = ctx.convert(*(r[:2] + r[2:])) 464 | r = np.concatenate([bl, tr - bl]) 465 | 466 | p = self.rect_params 467 | p["ec"] = _pop_multiple(p, "k", "ec", "edgecolor") 468 | p["fc"] = _pop_multiple(p, "none", "fc", "facecolor") 469 | p["lw"] = _pop_multiple(p, ctx.line_width, "lw", "linewidth") 470 | 471 | rect = Rectangle(r[:2], *r[2:], **p) 472 | ax.add_artist(rect) 473 | 474 | if self.label is not None: 475 | offset = np.array(self.label_offset) 476 | if self.position == "bottom left": 477 | pos = r[:2] 478 | ha = "left" 479 | elif self.position == "bottom right": 480 | pos = r[:2] 481 | pos[0] += r[2] 482 | ha = "right" 483 | offset[0] -= 2 * offset[0] 484 | else: 485 | raise RuntimeError("Unknown positioning string: {0}" 486 | .format(self.position)) 487 | 488 | ax.annotate(self.label, pos, xycoords="data", 489 | xytext=offset, textcoords="offset points", 490 | bbox=self.bbox, 491 | horizontalalignment=ha) 492 | 493 | return rect 494 | 495 | 496 | class _rendering_context(object): 497 | """ 498 | :param shape: 499 | The number of rows and columns in the grid. 500 | 501 | :param origin: 502 | The coordinates of the bottom left corner of the plot. 503 | 504 | :param grid_unit: 505 | The size of the grid spacing measured in centimeters. 506 | 507 | :param node_unit: 508 | The base unit for the node size. This is a number in centimeters that 509 | sets the default diameter of the nodes. 510 | 511 | :param observed_style: 512 | How should the "observed" nodes be indicated? This must be one of: 513 | ``"shaded"``, ``"inner"`` or ``"outer"`` where ``inner`` and 514 | ``outer`` nodes are shown as double circles with the second circle 515 | plotted inside or outside of the standard one, respectively. 516 | 517 | :param node_ec: 518 | The default edge color for the nodes. 519 | 520 | :param directed: 521 | Should the edges be directed by default? 522 | 523 | :param aspect: 524 | The default aspect ratio for the nodes. 525 | 526 | :param label_params: 527 | Default node label parameters. 528 | 529 | """ 530 | def __init__(self, **kwargs): 531 | # Save the style defaults. 532 | self.line_width = kwargs.get("line_width", 1.0) 533 | 534 | # Make sure that the observed node style is one that we recognize. 535 | self.observed_style = kwargs.get("observed_style", "shaded").lower() 536 | styles = ["shaded", "inner", "outer"] 537 | assert self.observed_style in styles, \ 538 | "Unrecognized observed node style: {0}\n".format( 539 | self.observed_style) \ 540 | + "\tOptions are: {0}".format(", ".join(styles)) 541 | 542 | # Set up the figure and grid dimensions. 543 | self.shape = np.array(kwargs.get("shape", [1, 1])) 544 | self.origin = np.array(kwargs.get("origin", [0, 0])) 545 | self.grid_unit = kwargs.get("grid_unit", 2.0) 546 | self.figsize = self.grid_unit * self.shape / 2.54 547 | 548 | self.node_unit = kwargs.get("node_unit", 1.0) 549 | self.node_ec = kwargs.get("node_ec", "k") 550 | self.directed = kwargs.get("directed", True) 551 | self.aspect = kwargs.get("aspect", 1.0) 552 | self.label_params = dict(kwargs.get("label_params", {})) 553 | 554 | # Initialize the figure to ``None`` to handle caching later. 555 | self._figure = None 556 | self._ax = None 557 | 558 | def figure(self): 559 | if self._figure is not None: 560 | return self._figure 561 | self._figure = plt.figure(figsize=self.figsize) 562 | return self._figure 563 | 564 | def ax(self): 565 | if self._ax is not None: 566 | return self._ax 567 | 568 | # Add a new axis object if it doesn't exist. 569 | self._ax = self.figure().add_axes((0, 0, 1, 1), frameon=False, 570 | xticks=[], yticks=[]) 571 | 572 | # Set the bounds. 573 | l0 = self.convert(*self.origin) 574 | l1 = self.convert(*(self.origin + self.shape)) 575 | self._ax.set_xlim(l0[0], l1[0]) 576 | self._ax.set_ylim(l0[1], l1[1]) 577 | 578 | return self._ax 579 | 580 | def convert(self, *xy): 581 | """ 582 | Convert from model coordinates to plot coordinates. 583 | 584 | """ 585 | assert len(xy) == 2 586 | return self.grid_unit * (np.atleast_1d(xy) - self.origin) 587 | 588 | 589 | def _pop_multiple(d, default, *args): 590 | """ 591 | A helper function for dealing with the way that matplotlib annoyingly 592 | allows multiple keyword arguments. For example, ``edgecolor`` and ``ec`` 593 | are generally equivalent but no exception is thrown if they are both 594 | used. 595 | 596 | *Note: This function does throw a :class:`ValueError` if more than one 597 | of the equivalent arguments are provided.* 598 | 599 | :param d: 600 | A :class:`dict`-like object to "pop" from. 601 | 602 | :param default: 603 | The default value to return if none of the arguments are provided. 604 | 605 | :param *args: 606 | The arguments to try to retrieve. 607 | 608 | """ 609 | assert len(args) > 0, "You must provide at least one argument to 'pop'." 610 | 611 | results = [] 612 | for k in args: 613 | try: 614 | results.append((k, d.pop(k))) 615 | except KeyError: 616 | pass 617 | 618 | if len(results) > 1: 619 | raise TypeError("The arguments ({0}) are equivalent, you can only " 620 | .format(", ".join([k for k, v in results])) 621 | + "provide one of them.") 622 | 623 | if len(results) == 0: 624 | return default 625 | 626 | return results[0][1] 627 | -------------------------------------------------------------------------------- /docs/_static/d3.v2.min.js: -------------------------------------------------------------------------------- 1 | (function(){function e(e,t){try{for(var n in t)Object.defineProperty(e.prototype,n,{value:t[n],enumerable:!1})}catch(r){e.prototype=t}}function t(e){var t=-1,n=e.length,r=[];while(++t=0?e.substring(t):(t=e.length,""),r=[];while(t>0)r.push(e.substring(t-=3,t+3));return r.reverse().join(",")+n}function b(e,t){var n=Math.pow(10,Math.abs(8-t)*3);return{scale:t>8?function(e){return e/n}:function(e){return e*n},symbol:e}}function w(e){return function(t){return t<=0?0:t>=1?1:e(t)}}function E(e){return function(t){return 1-e(1-t)}}function S(e){return function(t){return.5*(t<.5?e(2*t):2-e(2-2*t))}}function x(e){return e}function T(e){return function(t){return Math.pow(t,e)}}function N(e){return 1-Math.cos(e*Math.PI/2)}function C(e){return Math.pow(2,10*(e-1))}function k(e){return 1-Math.sqrt(1-e*e)}function L(e,t){var n;return arguments.length<2&&(t=.45),arguments.length<1?(e=1,n=t/4):n=t/(2*Math.PI)*Math.asin(1/e),function(r){return 1+e*Math.pow(2,10*-r)*Math.sin((r-n)*2*Math.PI/t)}}function A(e){return e||(e=1.70158),function(t){return t*t*((e+1)*t-e)}}function O(e){return e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375}function M(){d3.event.stopPropagation(),d3.event.preventDefault()}function _(){var e=d3.event,t;while(t=e.sourceEvent)e=t;return e}function D(e){var t=new d,n=0,r=arguments.length;while(++n360?e-=360:e<0&&(e+=360),e<60?s+(o-s)*e/60:e<180?o:e<240?s+(o-s)*(240-e)/60:s}function i(e){return Math.round(r(e)*255)}var s,o;return e%=360,e<0&&(e+=360),t=t<0?0:t>1?1:t,n=n<0?0:n>1?1:n,o=n<=.5?n*(1+t):n+t-n*t,s=2*n-o,R(i(e+120),i(e),i(e-120))}function Y(e,t,n){return new Z(e,t,n)}function Z(e,t,n){this.h=e,this.c=t,this.l=n}function et(e,t,n){return tt(n,Math.cos(e*=Math.PI/180)*t,Math.sin(e)*t)}function tt(e,t,n){return new nt(e,t,n)}function nt(e,t,n){this.l=e,this.a=t,this.b=n}function rt(e,t,n){var r=(e+16)/116,i=r+t/500,s=r-n/200;return i=st(i)*ds,r=st(r)*vs,s=st(s)*ms,R(ut(3.2404542*i-1.5371385*r-.4985314*s),ut(-0.969266*i+1.8760108*r+.041556*s),ut(.0556434*i-.2040259*r+1.0572252*s))}function it(e,t,n){return Y(Math.atan2(n,t)/Math.PI*180,Math.sqrt(t*t+n*n),e)}function st(e){return e>.206893034?e*e*e:(e-4/29)/7.787037}function ot(e){return e>.008856?Math.pow(e,1/3):7.787037*e+4/29}function ut(e){return Math.round(255*(e<=.00304?12.92*e:1.055*Math.pow(e,1/2.4)-.055))}function at(e){return Ki(e,Ss),e}function ft(e){return function(){return gs(e,this)}}function lt(e){return function(){return ys(e,this)}}function ct(e,t){function n(){this.removeAttribute(e)}function r(){this.removeAttributeNS(e.space,e.local)}function i(){this.setAttribute(e,t)}function s(){this.setAttributeNS(e.space,e.local,t)}function o(){var n=t.apply(this,arguments);n==null?this.removeAttribute(e):this.setAttribute(e,n)}function u(){var n=t.apply(this,arguments);n==null?this.removeAttributeNS(e.space,e.local):this.setAttributeNS(e.space,e.local,n)}return e=d3.ns.qualify(e),t==null?e.local?r:n:typeof t=="function"?e.local?u:o:e.local?s:i}function ht(e){return new RegExp("(?:^|\\s+)"+d3.requote(e)+"(?:\\s+|$)","g")}function pt(e,t){function n(){var n=-1;while(++n0&&(e=e.substring(0,o)),t?i:r}function Et(e,t){for(var n=0,r=e.length;nt?c():(v.active=t,i.forEach(function(t,n){(n=n.call(e,m,u))&&h.push(n)}),s.start.call(e,m,u),l(r)||d3.timer(l,0,n),1)}function l(n){if(v.active!==t)return c();var r=(n-p)/d,i=o(r),a=h.length;while(a>0)h[--a].call(e,i);if(r>=1)return c(),ks=t,s.end.call(e,m,u),ks=0,1}function c(){return--v.count||delete e.__transition__,1}var h=[],p=e.delay,d=e.duration,v=(e=e.node).__transition__||(e.__transition__={active:0,count:0}),m=e.__data__;++v.count,p<=r?f(r):d3.timer(f,p,n)})},0,n),e}function Tt(e){var t=ks,n=Ds,r=Ms,i=_s;return ks=this.id,Ds=this.ease(),Et(this,function(t,n,r){Ms=t.delay,_s=t.duration,e.call(t=t.node,t.__data__,n,r)}),ks=t,Ds=n,Ms=r,_s=i,this}function Nt(e,t,n){return n!=""&&Ps}function Ct(e,t){return d3.tween(e,F(t))}function kt(){var e,t=Date.now(),n=Hs;while(n)e=t-n.then,e>=n.delay&&(n.flush=n.callback(e)),n=n.next;var r=Lt()-t;r>24?(isFinite(r)&&(clearTimeout(js),js=setTimeout(kt,r)),Bs=0):(Bs=1,Fs(kt))}function Lt(){var e=null,t=Hs,n=Infinity;while(t)t.flush?t=e?e.next=t.next:Hs=t.next:(n=Math.min(n,t.then+t.delay),t=(e=t).next);return n}function At(e,t){var n=e.ownerSVGElement||e;if(n.createSVGPoint){var r=n.createSVGPoint();if(Is<0&&(window.scrollX||window.scrollY)){n=d3.select(document.body).append("svg").style("position","absolute").style("top",0).style("left",0);var i=n[0][0].getScreenCTM();Is=!i.f&&!i.e,n.remove()}return Is?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(e.getScreenCTM().inverse()),[r.x,r.y]}var s=e.getBoundingClientRect();return[t.clientX-s.left-e.clientLeft,t.clientY-s.top-e.clientTop]}function Ot(){}function Mt(e){var t=e[0],n=e[e.length-1];return t2?Ut:Rt,a=r?q:I;return o=i(e,t,a,n),u=i(t,e,a,d3.interpolate),s}function s(e){return o(e)}var o,u;return s.invert=function(e){return u(e)},s.domain=function(t){return arguments.length?(e=t.map(Number),i()):e},s.range=function(e){return arguments.length?(t=e,i()):t},s.rangeRound=function(e){return s.range(e).interpolate(d3.interpolateRound)},s.clamp=function(e){return arguments.length?(r=e,i()):r},s.interpolate=function(e){return arguments.length?(n=e,i()):n},s.ticks=function(t){return It(e,t)},s.tickFormat=function(t){return qt(e,t)},s.nice=function(){return Dt(e,jt),i()},s.copy=function(){return Ht(e,t,n,r)},i()}function Bt(e,t){return d3.rebind(e,t,"range","rangeRound","interpolate","clamp")}function jt(e){return e=Math.pow(10,Math.round(Math.log(e)/Math.LN10)-1),e&&{floor:function(t){return Math.floor(t/e)*e},ceil:function(t){return Math.ceil(t/e)*e}}}function Ft(e,t){var n=Mt(e),r=n[1]-n[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),s=t/r*i;return s<=.15?i*=10:s<=.35?i*=5:s<=.75&&(i*=2),n[0]=Math.ceil(n[0]/i)*i,n[1]=Math.floor(n[1]/i)*i+i*.5,n[2]=i,n}function It(e,t){return d3.range.apply(d3,Ft(e,t))}function qt(e,t){return d3.format(",."+Math.max(0,-Math.floor(Math.log(Ft(e,t)[2])/Math.LN10+.01))+"f")}function Rt(e,t,n,r){var i=n(e[0],e[1]),s=r(t[0],t[1]);return function(e){return s(i(e))}}function Ut(e,t,n,r){var i=[],s=[],o=0,u=Math.min(e.length,t.length)-1;e[u]0;f--)i.push(r(s)*f)}else{for(;sa;o--);i=i.slice(s,o)}return i},n.tickFormat=function(e,i){arguments.length<2&&(i=qs);if(arguments.length<1)return i;var s=Math.max(.1,e/n.ticks().length),o=t===Xt?(u=-1e-12,Math.floor):(u=1e-12,Math.ceil),u;return function(e){return e/r(o(t(e)+u))<=s?i(e):""}},n.copy=function(){return zt(e.copy(),t)},Bt(n,e)}function Wt(e){return Math.log(e<0?0:e)/Math.LN10}function Xt(e){return-Math.log(e>0?0:-e)/Math.LN10}function Vt(e,t){function n(t){return e(r(t))}var r=$t(t),i=$t(1/t);return n.invert=function(t){return i(e.invert(t))},n.domain=function(t){return arguments.length?(e.domain(t.map(r)),n):e.domain().map(i)},n.ticks=function(e){return It(n.domain(),e)},n.tickFormat=function(e){return qt(n.domain(),e)},n.nice=function(){return n.domain(Dt(n.domain(),jt))},n.exponent=function(e){if(!arguments.length)return t;var s=n.domain();return r=$t(t=e),i=$t(1/t),n.domain(s)},n.copy=function(){return Vt(e.copy(),t)},Bt(n,e)}function $t(e){return function(t){return t<0?-Math.pow(-t,e):Math.pow(t,e)}}function Jt(e,t){function n(t){return o[((s.get(t)||s.set(t,e.push(t)))-1)%o.length]}function i(t,n){return d3.range(e.length).map(function(e){return t+n*e})}var s,o,u;return n.domain=function(i){if(!arguments.length)return e;e=[],s=new r;var o=-1,u=i.length,a;while(++o1){u=t[1],s=e[a],a++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(s[0]-u[0])+","+(s[1]-u[1])+","+s[0]+","+s[1];for(var f=2;f9&&(s=n*3/Math.sqrt(s),o[u]=s*r,o[u+1]=s*i));u=-1;while(++u<=a)s=(e[Math.min(a,u+1)][0]-e[Math.max(0,u-1)][0])/(6*(1+o[u]*o[u])),t.push([s||0,o[u]*s||0]);return t}function Nn(e){return e.length<3?un(e):e[0]+dn(e,Tn(e))}function Cn(e){var t,n=-1,r=e.length,i,s;while(++n1){var r=Mt(e.domain()),i,s=-1,o=t.length,u=(t[1]-t[0])/++n,a,f;while(++s0;)(f=+t[s]-a*u)>=r[0]&&i.push(f);for(--s,a=0;++ar&&(n=t,r=i);return n}function ir(e){return e.reduce(sr,0)}function sr(e,t){return e+t[1]}function or(e,t){return ur(e,Math.ceil(Math.log(t.length)/Math.LN2+1))}function ur(e,t){var n=-1,r=+e[0],i=(e[1]-r)/t,s=[];while(++n<=t)s[n]=i*n+r;return s}function ar(e){return[d3.min(e),d3.max(e)]}function fr(e,t){return d3.rebind(e,t,"sort","children","value"),e.links=pr,e.nodes=function(t){return uo=!0,(e.nodes=e)(t)},e}function lr(e){return e.children}function cr(e){return e.value}function hr(e,t){return t.value-e.value}function pr(e){return d3.merge(e.map(function(e){return(e.children||[]).map(function(t){return{source:e,target:t}})}))}function dr(e,t){return e.value-t.value}function vr(e,t){var n=e._pack_next;e._pack_next=t,t._pack_prev=e,t._pack_next=n,n._pack_prev=t}function mr(e,t){e._pack_next=t,t._pack_prev=e}function gr(e,t){var n=t.x-e.x,r=t.y-e.y,i=e.r+t.r;return i*i-n*n-r*r>.001}function yr(e){function t(e){r=Math.min(e.x-e.r,r),i=Math.max(e.x+e.r,i),s=Math.min(e.y-e.r,s),o=Math.max(e.y+e.r,o)}if(!(n=e.children)||!(p=n.length))return;var n,r=Infinity,i=-Infinity,s=Infinity,o=-Infinity,u,a,f,l,c,h,p;n.forEach(br),u=n[0],u.x=-u.r,u.y=0,t(u);if(p>1){a=n[1],a.x=a.r,a.y=0,t(a);if(p>2){f=n[2],Sr(u,a,f),t(f),vr(u,f),u._pack_prev=f,vr(f,a),a=u._pack_next;for(l=3;l0&&(e=r)}return e}function Mr(e,t){return e.x-t.x}function _r(e,t){return t.x-e.x}function Dr(e,t){return e.depth-t.depth}function Pr(e,t){function n(e,r){var i=e.children;if(i&&(a=i.length)){var s,o=null,u=-1,a;while(++u=0)s=r[i]._tree,s.prelim+=t,s.mod+=t,t+=s.shift+(n+=s.change)}function Br(e,t,n){e=e._tree,t=t._tree;var r=n/(t.number-e.number);e.change+=r,t.change-=r,t.shift+=n,t.prelim+=n,t.mod+=n}function jr(e,t,n){return e._tree.ancestor.parent==t.parent?e._tree.ancestor:n}function Fr(e){return{x:e.x,y:e.y,dx:e.dx,dy:e.dy}}function Ir(e,t){var n=e.x+t[3],r=e.y+t[0],i=e.dx-t[1]-t[3],s=e.dy-t[0]-t[2];return i<0&&(n+=i/2,i=0),s<0&&(r+=s/2,s=0),{x:n,y:r,dx:i,dy:s}}function qr(e,t){function n(e,r){d3.text(e,t,function(e){r(e&&n.parse(e))})}function r(t){return t.map(i).join(e)}function i(e){return o.test(e)?'"'+e.replace(/\"/g,'""')+'"':e}var s=new RegExp("\r\n|["+e+"\r\n]","g"),o=new RegExp('["'+e+"\n]"),u=e.charCodeAt(0);return n.parse=function(e){var t;return n.parseRows(e,function(e,n){if(n){var r={},i=-1,s=t.length;while(++i=e.length)return i;if(l)return l=!1,r;var t=s.lastIndex;if(e.charCodeAt(t)===34){var n=t;while(n++0}function ii(e,t,n){return(n[0]-t[0])*(e[1]-t[1])<(n[1]-t[1])*(e[0]-t[0])}function si(e,t,n,r){var i=e[0],s=t[0],o=n[0],u=r[0],a=e[1],f=t[1],l=n[1],c=r[1],h=i-o,p=s-i,d=u-o,v=a-l,m=f-a,g=c-l,y=(d*v-g*h)/(g*p-d*m);return[i+y*p,a+y*m]}function oi(e,t){var n={list:e.map(function(e,t){return{index:t,x:e[0],y:e[1]}}).sort(function(e,t){return e.yt.y?1:e.xt.x?1:0}),bottomSite:null},r={list:[],leftEnd:null,rightEnd:null,init:function(){r.leftEnd=r.createHalfEdge(null,"l"),r.rightEnd=r.createHalfEdge(null,"l"),r.leftEnd.r=r.rightEnd,r.rightEnd.l=r.leftEnd,r.list.unshift(r.leftEnd,r.rightEnd)},createHalfEdge:function(e,t){return{edge:e,side:t,vertex:null,l:null,r:null}},insert:function(e,t){t.l=e,t.r=e.r,e.r.l=t,e.r=t},leftBound:function(e){var t=r.leftEnd;do t=t.r;while(t!=r.rightEnd&&i.rightOf(t,e));return t=t.l,t},del:function(e){e.l.r=e.r,e.r.l=e.l,e.edge=null},right:function(e){return e.r},left:function(e){return e.l},leftRegion:function(e){return e.edge==null?n.bottomSite:e.edge.region[e.side]},rightRegion:function(e){return e.edge==null?n.bottomSite:e.edge.region[ho[e.side]]}},i={bisect:function(e,t){var n={region:{l:e,r:t},ep:{l:null,r:null}},r=t.x-e.x,i=t.y-e.y,s=r>0?r:-r,o=i>0?i:-i;return n.c=e.x*r+e.y*i+(r*r+i*i)*.5,s>o?(n.a=1,n.b=i/r,n.c/=r):(n.b=1,n.a=r/i,n.c/=i),n},intersect:function(e,t){var n=e.edge,r=t.edge;if(!n||!r||n.region.r==r.region.r)return null;var i=n.a*r.b-n.b*r.a;if(Math.abs(i)<1e-10)return null;var s=(n.c*r.b-r.c*n.b)/i,o=(r.c*n.a-n.c*r.a)/i,u=n.region.r,a=r.region.r,f,l;u.y=l.region.r.x;return c&&f.side==="l"||!c&&f.side==="r"?null:{x:s,y:o}},rightOf:function(e,t){var n=e.edge,r=n.region.r,i=t.x>r.x;if(i&&e.side==="l")return 1;if(!i&&e.side==="r")return 0;if(n.a===1){var s=t.y-r.y,o=t.x-r.x,u=0,a=0;!i&&n.b<0||i&&n.b>=0?a=u=s>=n.b*o:(a=t.x+t.y*n.b>n.c,n.b<0&&(a=!a),a||(u=1));if(!u){var f=r.x-n.region.l.x;a=n.b*(o*o-s*s)h*h+p*p}return e.side==="l"?a:!a},endPoint:function(e,n,r){e.ep[n]=r;if(!e.ep[ho[n]])return;t(e)},distance:function(e,t){var n=e.x-t.x,r=e.y-t.y;return Math.sqrt(n*n+r*r)}},s={list:[],insert:function(e,t,n){e.vertex=t,e.ystar=t.y+n;for(var r=0,i=s.list,o=i.length;ru.ystar||e.ystar==u.ystar&&t.x>u.vertex.x)continue;break}i.splice(r,0,e)},del:function(e){for(var t=0,n=s.list,r=n.length;td.y&&(v=p,p=d,d=v,b="r"),y=i.bisect(p,d),h=r.createHalfEdge(y,b),r.insert(l,h),i.endPoint(y,ho[b],g),m=i.intersect(l,h),m&&(s.del(l),s.insert(l,m,i.distance(m,p))),m=i.intersect(h,c),m&&s.insert(h,m,i.distance(m,p))}}for(a=r.right(r.leftEnd);a!=r.rightEnd;a=r.right(a))t(a.edge)}function ui(){return{leaf:!0,nodes:[],point:null}}function ai(e,t,n,r,i,s){if(!e(t,n,r,i,s)){var o=(n+i)*.5,u=(r+s)*.5,a=t.nodes;a[0]&&ai(e,a[0],n,r,o,u),a[1]&&ai(e,a[1],o,r,i,u),a[2]&&ai(e,a[2],n,u,o,s),a[3]&&ai(e,a[3],o,u,i,s)}}function fi(e){return{x:e[0],y:e[1]}}function li(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function ci(e){return e.substring(0,3)}function hi(e,t,n,r){var i,s,o=0,u=t.length,a=n.length;while(o=a)return-1;i=t.charCodeAt(o++);if(i==37){s=Ho[t.charAt(o++)];if(!s||(r=s(e,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}function pi(e){return new RegExp("^(?:"+e.map(d3.requote).join("|")+")","i")}function di(e){var t=new r,n=-1,i=e.length;while(++n68?1900:2e3)}function Ni(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.m=r[0]-1,n+=r[0].length):-1}function Ci(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.d=+r[0],n+=r[0].length):-1}function ki(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.H=+r[0],n+=r[0].length):-1}function Li(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.M=+r[0],n+=r[0].length):-1}function Ai(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.S=+r[0],n+=r[0].length):-1}function Oi(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+3));return r?(e.L=+r[0],n+=r[0].length):-1}function Mi(e,t,n){var r=jo.get(t.substring(n,n+=2).toLowerCase());return r==null?-1:(e.p=r,n)}function _i(e){var t=e.getTimezoneOffset(),n=t>0?"-":"+",r=~~(Math.abs(t)/60),i=Math.abs(t)%60;return n+To(r)+To(i)}function Di(e){return e.toISOString()}function Pi(e,t,n){function r(t){var n=e(t),r=s(n,1);return t-n1)while(ot?1:e>=t?0:NaN},d3.descending=function(e,t){return te?1:t>=e?0:NaN},d3.mean=function(e,t){var n=e.length,r,i=0,s=-1,o=0;if(arguments.length===1)while(++s1&&(e=e.map(t)),e=e.filter(f),e.length?d3.quantile(e.sort(d3.ascending),.5):undefined},d3.min=function(e,t){var n=-1,r=e.length,i,s;if(arguments.length===1){while(++ns&&(i=s)}else{while(++ns&&(i=s)}return i},d3.max=function(e,t){var n=-1,r=e.length,i,s;if(arguments.length===1){while(++ni&&(i=s)}else{while(++ni&&(i=s)}return i},d3.extent=function(e,t){var n=-1,r=e.length,i,s,o;if(arguments.length===1){while(++ns&&(i=s),os&&(i=s),o1);return e+t*n*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(e,t){var n=arguments.length;n<2&&(t=1),n<1&&(e=0);var r=d3.random.normal();return function(){return Math.exp(e+t*r())}},irwinHall:function(e){return function(){for(var t=0,n=0;n>>1;e.call(t,t[s],s)>>1;n0&&(i=s);return i},d3.last=function(e,t){var n=0,r=e.length,i=e[0],s;arguments.length===1&&(t=d3.ascending);while(++n=i.length)return u?u.call(n,t):o?t.sort(o):t;var a=-1,f=t.length,l=i[s++],c,h,p=new r,d,v={};while(++a=i.length)return e;var r=[],o=s[n++],u;for(u in e)r.push({key:u,values:t(e[u],n)});return o&&r.sort(function(e,t){return o(e.key,t.key)}),r}var n={},i=[],s=[],o,u;return n.map=function(t){return e(t,0)},n.entries=function(n){return t(e(n,0),0)},n.key=function(e){return i.push(e),n},n.sortKeys=function(e){return s[i.length-1]=e,n},n.sortValues=function(e){return o=e,n},n.rollup=function(e){return u=e,n},n},d3.keys=function(e){var t=[];for(var n in e)t.push(n);return t},d3.values=function(e){var t=[];for(var n in e)t.push(e[n]);return t},d3.entries=function(e){var t=[];for(var n in e)t.push({key:n,value:e[n]});return t},d3.permute=function(e,t){var n=[],r=-1,i=t.length;while(++rt)r.push(o/i);else while((o=e+n*++s)=200&&e<300||e===304?r:null)}},r.send(null)},d3.text=function(e,t,n){function r(e){n(e&&e.responseText)}arguments.length<3&&(n=t,t=null),d3.xhr(e,t,r)},d3.json=function(e,t){d3.text(e,"application/json",function(e){t(e?JSON.parse(e):null)})},d3.html=function(e,t){d3.text(e,"text/html",function(e){if(e!=null){var n=document.createRange();n.selectNode(document.body),e=n.createContextualFragment(e)}t(e)})},d3.xml=function(e,t,n){function r(e){n(e&&e.responseXML)}arguments.length<3&&(n=t,t=null),d3.xhr(e,t,r)};var es={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};d3.ns={prefix:es,qualify:function(e){var t=e.indexOf(":"),n=e;return t>=0&&(n=e.substring(0,t),e=e.substring(t+1)),es.hasOwnProperty(n)?{space:es[n],local:e}:e}},d3.dispatch=function(){var e=new d,t=-1,n=arguments.length;while(++t0&&(r=e.substring(n+1),e=e.substring(0,n)),arguments.length<2?this[e].on(r):this[e].on(r,t)},d3.format=function(e){var t=ts.exec(e),n=t[1]||" ",r=t[3]||"",i=t[5],s=+t[6],o=t[7],u=t[8],a=t[9],f=1,l="",c=!1;u&&(u=+u.substring(1)),i&&(n="0",o&&(s-=Math.floor((s-1)/4)));switch(a){case"n":o=!0,a="g";break;case"%":f=100,l="%",a="f";break;case"p":f=100,l="%",a="r";break;case"d":c=!0,u=0;break;case"s":f=-1,a="r"}return a=="r"&&!u&&(a="g"),a=ns.get(a)||g,function(e){if(c&&e%1)return"";var t=e<0&&(e=-e)?"-":r;if(f<0){var h=d3.formatPrefix(e,u);e=h.scale(e),l=h.symbol}else e*=f;e=a(e,u);if(i){var p=e.length+t.length;p=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,ns=d3.map({g:function(e,t){return e.toPrecision(t)},e:function(e,t){return e.toExponential(t)},f:function(e,t){return e.toFixed(t)},r:function(e,t){return d3.round(e,t=m(e,t)).toFixed(Math.max(0,Math.min(20,t)))}}),rs=["y","z","a","f","p","n","μ","m","","k","M","G","T","P","E","Z","Y"].map(b);d3.formatPrefix=function(e,t){var n=0;return e&&(e<0&&(e*=-1),t&&(e=d3.round(e,m(e,t))),n=1+Math.floor(1e-12+Math.log(e)/Math.LN10),n=Math.max(-24,Math.min(24,Math.floor((n<=0?n+1:n-1)/3)*3))),rs[8+n/3]};var is=T(2),ss=T(3),os=function(){return x},us=d3.map({linear:os,poly:T,quad:function(){return is},cubic:function(){return ss},sin:function(){return N},exp:function(){return C},circle:function(){return k},elastic:L,back:A,bounce:function(){return O}}),as=d3.map({"in":x,out:E,"in-out":S,"out-in":function(e){return S(E(e))}});d3.ease=function(e){var t=e.indexOf("-"),n=t>=0?e.substring(0,t):e,r=t>=0?e.substring(t+1):"in";return n=us.get(n)||os,r=as.get(r)||x,w(r(n.apply(null,Array.prototype.slice.call(arguments,1))))},d3.event=null,d3.transform=function(e){var t=document.createElementNS(d3.ns.prefix.svg,"g");return(d3.transform=function(e){t.setAttribute("transform",e);var n=t.transform.baseVal.consolidate();return new P(n?n.matrix:ls)})(e)},P.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var fs=180/Math.PI,ls={a:1,b:0,c:0,d:1,e:0,f:0};d3.interpolate=function(e,t){var n=d3.interpolators.length,r;while(--n>=0&&!(r=d3.interpolators[n](e,t)));return r},d3.interpolateNumber=function(e,t){return t-=e,function(n){return e+t*n}},d3.interpolateRound=function(e,t){return t-=e,function(n){return Math.round(e+t*n)}},d3.interpolateString=function(e,t){var n,r,i,s=0,o=0,u=[],a=[],f,l;cs.lastIndex=0;for(r=0;n=cs.exec(t);++r)n.index&&u.push(t.substring(s,o=n.index)),a.push({i:u.length,x:n[0]}),u.push(null),s=cs.lastIndex;s180?l+=360:l-f>180&&(f+=360),r.push({i:n.push(n.pop()+"rotate(",null,")")-2,x:d3.interpolateNumber(f,l)})):l&&n.push(n.pop()+"rotate("+l+")"),c!=h?r.push({i:n.push(n.pop()+"skewX(",null,")")-2,x:d3.interpolateNumber(c,h)}):h&&n.push(n.pop()+"skewX("+h+")"),p[0]!=d[0]||p[1]!=d[1]?(i=n.push(n.pop()+"scale(",null,",",null,")"),r.push({i:i-4,x:d3.interpolateNumber(p[0],d[0])},{i:i-2,x:d3.interpolateNumber(p[1],d[1])})):(d[0]!=1||d[1]!=1)&&n.push(n.pop()+"scale("+d+")"),i=r.length,function(e){var t=-1,s;while(++t180?s-=360:s<-180&&(s+=360),function(e){return G(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateLab=function(e,t){e=d3.lab(e),t=d3.lab(t);var n=e.l,r=e.a,i=e.b,s=t.l-n,o=t.a-r,u=t.b-i;return function(e){return rt(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateHcl=function(e,t){e=d3.hcl(e),t=d3.hcl(t);var n=e.h,r=e.c,i=e.l,s=t.h-n,o=t.c-r,u=t.l-i;return s>180?s-=360:s<-180&&(s+=360),function(e){return et(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateArray=function(e,t){var n=[],r=[],i=e.length,s=t.length,o=Math.min(e.length,t.length),u;for(u=0;u=0;)if(s=n[r])i&&i!==s.nextSibling&&i.parentNode.insertBefore(s,i),i=s;return this},Ss.sort=function(e){e=bt.apply(this,arguments);for(var t=-1,n=this.length;++t=Vs?e?"M0,"+s+"A"+s+","+s+" 0 1,1 0,"+ -s+"A"+s+","+s+" 0 1,1 0,"+s+"M0,"+e+"A"+e+","+e+" 0 1,0 0,"+ -e+"A"+e+","+e+" 0 1,0 0,"+e+"Z":"M0,"+s+"A"+s+","+s+" 0 1,1 0,"+ -s+"A"+s+","+s+" 0 1,1 0,"+s+"Z":e?"M"+s*l+","+s*c+"A"+s+","+s+" 0 "+f+",1 "+s*h+","+s*p+"L"+e*h+","+e*p+"A"+e+","+e+" 0 "+f+",0 "+e*l+","+e*c+"Z":"M"+s*l+","+s*c+"A"+s+","+s+" 0 "+f+",1 "+s*h+","+s*p+"L0,0"+"Z"}var t=Zt,n=en,r=tn,i=nn;return e.innerRadius=function(n){return arguments.length?(t=u(n),e):t},e.outerRadius=function(t){return arguments.length?(n=u(t),e):n},e.startAngle=function(t){return arguments.length?(r=u(t),e):r},e.endAngle=function(t){return arguments.length?(i=u(t),e):i},e.centroid=function(){var e=(t.apply(this,arguments)+n.apply(this,arguments))/2,s=(r.apply(this,arguments)+i.apply(this,arguments))/2+Xs;return[Math.cos(s)*e,Math.sin(s)*e]},e};var Xs=-Math.PI/2,Vs=2*Math.PI-1e-6;d3.svg.line=function(){return rn(i)};var $s=d3.map({linear:un,"linear-closed":an,"step-before":fn,"step-after":ln,basis:mn,"basis-open":gn,"basis-closed":yn,bundle:bn,cardinal:pn,"cardinal-open":cn,"cardinal-closed":hn,monotone:Nn});$s.forEach(function(e,t){t.key=e,t.closed=/-closed$/.test(e)});var Js=[0,2/3,1/3,0],Ks=[0,1/3,2/3,0],Qs=[0,1/6,2/3,1/6];d3.svg.line.radial=function(){var e=rn(Cn);return e.radius=e.x,delete e.x,e.angle=e.y,delete e.y,e},fn.reverse=ln,ln.reverse=fn,d3.svg.area=function(){return kn(i)},d3.svg.area.radial=function(){var e=kn(Cn);return e.radius=e.x,delete e.x,e.innerRadius=e.x0,delete e.x0,e.outerRadius=e.x1,delete e.x1,e.angle=e.y,delete e.y,e.startAngle=e.y0,delete e.y0,e.endAngle=e.y1,delete e.y1,e},d3.svg.chord=function(){function e(e,u){var a=t(this,s,e,u),f=t(this,o,e,u);return"M"+a.p0+r(a.r,a.p1,a.a1-a.a0)+(n(a,f)?i(a.r,a.p1,a.r,a.p0):i(a.r,a.p1,f.r,f.p0)+r(f.r,f.p1,f.a1-f.a0)+i(f.r,f.p1,a.r,a.p0))+"Z"}function t(e,t,n,r){var i=t.call(e,n,r),s=a.call(e,i,r),o=f.call(e,i,r)+Xs,u=l.call(e,i,r)+Xs;return{r:s,a0:o,a1:u,p0:[s*Math.cos(o),s*Math.sin(o)],p1:[s*Math.cos(u),s*Math.sin(u)]}}function n(e,t){return e.a0==t.a0&&e.a1==t.a1}function r(e,t,n){return"A"+e+","+e+" 0 "+ +(n>Math.PI)+",1 "+t}function i(e,t,n,r){return"Q 0,0 "+r}var s=Ln,o=An,a=On,f=tn,l=nn;return e.radius=function(t){return arguments.length?(a=u(t),e):a},e.source=function(t){return arguments.length?(s=u(t),e):s},e.target=function(t){return arguments.length?(o=u(t),e):o},e.startAngle=function(t){return arguments.length?(f=u(t),e):f},e.endAngle=function(t){return arguments.length?(l=u(t),e):l},e},d3.svg.diagonal=function(){function e(e,i){var s=t.call(this,e,i),o=n.call(this,e,i),u=(s.y+o.y)/2,a=[s,{x:s.x,y:u},{x:o.x,y:u},o];return a=a.map(r),"M"+a[0]+"C"+a[1]+" "+a[2]+" "+a[3]}var t=Ln,n=An,r=Dn;return e.source=function(n){return arguments.length?(t=u(n),e):t},e.target=function(t){return arguments.length?(n=u(t),e):n},e.projection=function(t){return arguments.length?(r=t,e):r},e},d3.svg.diagonal.radial=function(){var e=d3.svg.diagonal(),t=Dn,n=e.projection;return e.projection=function(e){return arguments.length?n(Pn(t=e)):t},e},d3.svg.mouse=d3.mouse,d3.svg.touches=d3.touches,d3.svg.symbol=function(){function e(e,r){return(Gs.get(t.call(this,e,r))||jn)(n.call(this,e,r))}var t=Bn,n=Hn;return e.type=function(n){return arguments.length?(t=u(n),e):t},e.size=function(t){return arguments.length?(n=u(t),e):n},e};var Gs=d3.map({circle:jn,cross:function(e){var t=Math.sqrt(e/5)/2;return"M"+ -3*t+","+ -t+"H"+ -t+"V"+ -3*t+"H"+t+"V"+ -t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+ -t+"V"+t+"H"+ -3*t+"Z"},diamond:function(e){var t=Math.sqrt(e/(2*Zs)),n=t*Zs;return"M0,"+ -t+"L"+n+",0"+" 0,"+t+" "+ -n+",0"+"Z"},square:function(e){var t=Math.sqrt(e)/2;return"M"+ -t+","+ -t+"L"+t+","+ -t+" "+t+","+t+" "+ -t+","+t+"Z"},"triangle-down":function(e){var t=Math.sqrt(e/Ys),n=t*Ys/2;return"M0,"+n+"L"+t+","+ -n+" "+ -t+","+ -n+"Z"},"triangle-up":function(e){var t=Math.sqrt(e/Ys),n=t*Ys/2;return"M0,"+ -n+"L"+t+","+n+" "+ -t+","+n+"Z"}});d3.svg.symbolTypes=Gs.keys();var Ys=Math.sqrt(3),Zs=Math.tan(30*Math.PI/180);d3.svg.axis=function(){function e(e){e.each(function(){var e=d3.select(this),c=a==null?t.ticks?t.ticks.apply(t,u):t.domain():a,h=f==null?t.tickFormat?t.tickFormat.apply(t,u):String:f,p=qn(t,c,l),d=e.selectAll(".minor").data(p,String),v=d.enter().insert("line","g").attr("class","tick minor").style("opacity",1e-6),m=d3.transition(d.exit()).style("opacity",1e-6).remove(),g=d3.transition(d).style("opacity",1),y=e.selectAll("g").data(c,String),b=y.enter().insert("g","path").style("opacity",1e-6),w=d3.transition(y.exit()).style("opacity",1e-6).remove(),E=d3.transition(y).style("opacity",1),S,x=_t(t),T=e.selectAll(".domain").data([0]),N=T.enter().append("path").attr("class","domain"),C=d3.transition(T),k=t.copy(),L=this.__chart__||k;this.__chart__=k,b.append("line").attr("class","tick"),b.append("text");var A=b.select("line"),O=E.select("line"),M=y.select("text").text(h),_=b.select("text"),D=E.select("text");switch(n){case"bottom":S=Fn,v.attr("y2",i),g.attr("x2",0).attr("y2",i),A.attr("y2",r),_.attr("y",Math.max(r,0)+o),O.attr("x2",0).attr("y2",r),D.attr("x",0).attr("y",Math.max(r,0)+o),M.attr("dy",".71em").attr("text-anchor","middle"),C.attr("d","M"+x[0]+","+s+"V0H"+x[1]+"V"+s);break;case"top":S=Fn,v.attr("y2",-i),g.attr("x2",0).attr("y2",-i),A.attr("y2",-r),_.attr("y",-(Math.max(r,0)+o)),O.attr("x2",0).attr("y2",-r),D.attr("x",0).attr("y",-(Math.max(r,0)+o)),M.attr("dy","0em").attr("text-anchor","middle"),C.attr("d","M"+x[0]+","+ -s+"V0H"+x[1]+"V"+ -s);break;case"left":S=In,v.attr("x2",-i),g.attr("x2",-i).attr("y2",0),A.attr("x2",-r),_.attr("x",-(Math.max(r,0)+o)),O.attr("x2",-r).attr("y2",0),D.attr("x",-(Math.max(r,0)+o)).attr("y",0),M.attr("dy",".32em").attr("text-anchor","end"),C.attr("d","M"+ -s+","+x[0]+"H0V"+x[1]+"H"+ -s);break;case"right":S=In,v.attr("x2",i),g.attr("x2",i).attr("y2",0),A.attr("x2",r),_.attr("x",Math.max(r,0)+o),O.attr("x2",r).attr("y2",0),D.attr("x",Math.max(r,0)+o).attr("y",0),M.attr("dy",".32em").attr("text-anchor","start"),C.attr("d","M"+s+","+x[0]+"H0V"+x[1]+"H"+s)}if(t.ticks)b.call(S,L),E.call(S,k),w.call(S,k),v.call(S,L),g.call(S,k),m.call(S,k);else{var P=k.rangeBand()/2,H=function(e){return k(e)+P};b.call(S,H),E.call(S,H)}})}var t=d3.scale.linear(),n="bottom",r=6,i=6,s=6,o=3,u=[10],a=null,f,l=0;return e.scale=function(n){return arguments.length?(t=n,e):t},e.orient=function(t){return arguments.length?(n=t,e):n},e.ticks=function(){return arguments.length?(u=arguments,e):u},e.tickValues=function(t){return arguments.length?(a=t,e):a},e.tickFormat=function(t){return arguments.length?(f=t,e):f},e.tickSize=function(t,n,o){if(!arguments.length)return r;var u=arguments.length-1;return r=+t,i=u>1?+n:r,s=u>0?+arguments[u]:r,e},e.tickPadding=function(t){return arguments.length?(o=+t,e):o},e.tickSubdivide=function(t){return arguments.length?(l=+t,e):l},e},d3.svg.brush=function(){function e(s){s.each(function(){var s=d3.select(this),f=s.selectAll(".background").data([0]),l=s.selectAll(".extent").data([0]),c=s.selectAll(".resize").data(a,String),h;s.style("pointer-events","all").on("mousedown.brush",i).on("touchstart.brush",i),f.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),l.enter().append("rect").attr("class","extent").style("cursor","move"),c.enter().append("g").attr("class",function(e){return"resize "+e}).style("cursor",function(e){return eo[e]}).append("rect").attr("x",function(e){return/[ew]$/.test(e)?-3:null}).attr("y",function(e){return/^[ns]/.test(e)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),c.style("display",e.empty()?"none":null),c.exit().remove(),o&&(h=_t(o),f.attr("x",h[0]).attr("width",h[1]-h[0]),n(s)),u&&(h=_t(u),f.attr("y",h[0]).attr("height",h[1]-h[0]),r(s)),t(s)})}function t(e){e.selectAll(".resize").attr("transform",function(e){return"translate("+f[+/e$/.test(e)][0]+","+f[+/^s/.test(e)][1]+")"})}function n(e){e.select(".extent").attr("x",f[0][0]),e.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1][0]-f[0][0])}function r(e){e.select(".extent").attr("y",f[0][1]),e.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1][1]-f[0][1])}function i(){function i(){var e=d3.event.changedTouches;return e?d3.touches(v,e)[0]:d3.mouse(v)}function a(){d3.event.keyCode==32&&(S||(x=null,T[0]-=f[1][0],T[1]-=f[1][1],S=2),M())}function c(){d3.event.keyCode==32&&S==2&&(T[0]+=f[1][0],T[1]+=f[1][1],S=0,M())}function h(){var e=i(),s=!1;N&&(e[0]+=N[0],e[1]+=N[1]),S||(d3.event.altKey?(x||(x=[(f[0][0]+f[1][0])/2,(f[0][1]+f[1][1])/2]),T[0]=f[+(e[0]0?a=e:a=0:e>0&&(r.start({type:"start",alpha:a=e}),d3.timer(n.tick)),n):a},n.start=function(){function e(e,n){var i=t(r),s=-1,o=i.length,u;while(++si&&(i=u),r.push(u)}for(o=0;o0){s=-1;while(++s=a[0]&&d<=a[1]&&(l=o[d3.bisect(f,d,1,h)-1],l.y+=p,l.push(e[s]))}return o}var t=!0,n=Number,r=ar,i=or;return e.value=function(t){return arguments.length?(n=t,e):n},e.range=function(t){return arguments.length?(r=u(t),e):r},e.bins=function(t){return arguments.length?(i=typeof t=="number"?function(e){return ur(e,t)}:u(t),e):i},e.frequency=function(n){return arguments.length?(t=!!n,e):t},e},d3.layout.hierarchy=function(){function e(t,o,u){var a=i.call(n,t,o),f=uo?t:{data:t};f.depth=o,u.push(f);if(a&&(c=a.length)){var l=-1,c,h=f.children=[],p=0,d=o+1,v;while(++l0){var l=n*f/2;Pr(o,function(e){e.r+=l}),Pr(o,yr),Pr(o,function(e){e.r-=l}),f=Math.max(2*o.r/u,2*o.r/a)}return Er(o,u/2,a/2,1/f),s}var t=d3.layout.hierarchy().sort(dr),n=0,r=[1,1];return e.size=function(t){return arguments.length?(r=t,e):r},e.padding=function(t){return arguments.length?(n=+t,e):n},fr(e,t)},d3.layout.cluster=function(){function e(e,i){var s=t.call(this,e,i),o=s[0],u,a=0,f,l;Pr(o,function(e){var t=e.children;t&&t.length?(e.x=Tr(t),e.y=xr(t)):(e.x=u?a+=n(e,u):0,e.y=0,u=e)});var c=Nr(o),h=Cr(o),p=c.x-n(c,h)/2,d=h.x+n(h,c)/2;return Pr(o,function(e){e.x=(e.x-p)/(d-p)*r[0],e.y=(1-(o.y?e.y/o.y:1))*r[1]}),s}var t=d3.layout.hierarchy().sort(null).value(null),n=kr,r=[1,1];return e.separation=function(t){return arguments.length?(n=t,e):n},e.size=function(t){return arguments.length?(r=t,e):r},fr(e,t)},d3.layout.tree=function(){function e(e,i){function s(e,t){var r=e.children,i=e._tree;if(r&&(o=r.length)){var o,a=r[0],f,l=a,c,h=-1;while(++h0&&(Br(jr(o,e,r),e,h),a+=h,f+=h),l+=o._tree.mod,a+=i._tree.mod,c+=u._tree.mod,f+=s._tree.mod;o&&!Ar(s)&&(s._tree.thread=o,s._tree.mod+=l-f),i&&!Lr(u)&&(u._tree.thread=i,u._tree.mod+=a-c,r=e)}return r}var a=t.call(this,e,i),f=a[0];Pr(f,function(e,t){e._tree={ancestor:e,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),s(f),o(f,-f._tree.prelim);var l=Or(f,_r),c=Or(f,Mr),h=Or(f,Dr),p=l.x-n(l,c)/2,d=c.x+n(c,l)/2,v=h.depth||1;return Pr(f,function(e){e.x=(e.x-p)/(d-p)*r[0],e.y=e.depth/v*r[1],delete e._tree}),a}var t=d3.layout.hierarchy().sort(null).value(null),n=kr,r=[1,1];return e.separation=function(t){return arguments.length?(n=t,e):n},e.size=function(t){return arguments.length?(r=t,e):r},fr(e,t)},d3.layout.treemap=function(){function e(e,t){var n=-1,r=e.length,i,s;while(++n0)u.push(f=a[d-1]),u.area+=f.area,(h=r(u,p))<=c?(a.pop(),c=h):(u.area-=u.pop().area,i(u,p,o,!1),p=Math.min(o.dx,o.dy),u.length=u.area=0,c=Infinity);u.length&&(i(u,p,o,!0),u.length=u.area=0),s.forEach(t)}}function n(t){var r=t.children;if(r&&r.length){var s=l(t),o=r.slice(),u,a=[];e(o,s.dx*s.dy/t.value),a.area=0;while(u=o.pop())a.push(u),a.area+=u.area,u.z!=null&&(i(a,u.z?s.dx:s.dy,s,!o.length),a.length=a.area=0);r.forEach(n)}}function r(e,t){var n=e.area,r,i=0,s=Infinity,o=-1,u=e.length;while(++oi&&(i=r)}return n*=n,t*=t,n?Math.max(t*i*p/n,n/(t*s*p)):Infinity}function i(e,t,n,r){var i=-1,s=e.length,o=n.x,a=n.y,f=t?u(e.area/t):0,l;if(t==n.dx){if(r||f>n.dy)f=n.dy;while(++in.dx)f=n.dx;while(++i50?n:s<-140?r:o<21?i:t)(e)}var t=d3.geo.albers(),n=d3.geo.albers().origin([-160,60]).parallels([55,65]),r=d3.geo.albers().origin([-160,20]).parallels([8,18]),i=d3.geo.albers().origin([-60,10]).parallels([8,18]);return e.scale=function(s){return arguments.length?(t.scale(s),n.scale(s*.6),r.scale(s),i.scale(s*1.5),e.translate(t.translate())):t.scale()},e.translate=function(s){if(!arguments.length)return t.translate();var o=t.scale()/1e3,u=s[0],a=s[1];return t.translate(s),n.translate([u-400*o,a+170*o]),r.translate([u-190*o,a+200*o]),i.translate([u+580*o,a+430*o]),e},e.scale(t.scale())},d3.geo.bonne=function(){function e(e){var u=e[0]*ao-r,a=e[1]*ao-i;if(s){var f=o+s-a,l=u*Math.cos(a)/f;u=f*Math.sin(l),a=f*Math.cos(l)-o}else u*=Math.cos(a),a*=-1;return[t*u+n[0],t*a+n[1]]}var t=200,n=[480,250],r,i,s,o;return e.invert=function(e){var i=(e[0]-n[0])/t,u=(e[1]-n[1])/t;if(s){var a=o+u,f=Math.sqrt(i*i+a*a);u=o+s-f,i=r+f*Math.atan2(i,a)/Math.cos(u)}else u*=-1,i/=Math.cos(u);return[i/ao,u/ao]},e.parallel=function(t){return arguments.length?(o=1/Math.tan 4 | (s=t*ao),e):s/ao},e.origin=function(t){return arguments.length?(r=t[0]*ao,i=t[1]*ao,e):[r/ao,i/ao]},e.scale=function(n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e.origin([0,0]).parallel(45)},d3.geo.equirectangular=function(){function e(e){var r=e[0]/360,i=-e[1]/360;return[t*r+n[0],t*i+n[1]]}var t=500,n=[480,250];return e.invert=function(e){var r=(e[0]-n[0])/t,i=(e[1]-n[1])/t;return[360*r,-360*i]},e.scale=function(n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e},d3.geo.mercator=function(){function e(e){var r=e[0]/360,i=-(Math.log(Math.tan(Math.PI/4+e[1]*ao/2))/ao)/360;return[t*r+n[0],t*Math.max(-0.5,Math.min(.5,i))+n[1]]}var t=500,n=[480,250];return e.invert=function(e){var r=(e[0]-n[0])/t,i=(e[1]-n[1])/t;return[360*r,2*Math.atan(Math.exp(-360*i*ao))/ao-90]},e.scale=function(n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e},d3.geo.path=function(){function e(e,t){typeof s=="function"&&(o=Ur(s.apply(this,arguments))),f(e);var n=a.length?a.join(""):null;return a=[],n}function t(e){return u(e).join(",")}function n(e){var t=i(e[0]),n=0,r=e.length;while(++n0){a.push("M");while(++o0){a.push("M");while(++lr&&(r=e),si&&(i=s)}),[[t,n],[r,i]]};var fo={Feature:Wr,FeatureCollection:Xr,GeometryCollection:Vr,LineString:$r,MultiLineString:Jr,MultiPoint:$r,MultiPolygon:Kr,Point:Qr,Polygon:Gr};d3.geo.circle=function(){function e(){}function t(e){return a.distance(e)=l*l+c*c?r[s].index=-1:(r[h].index=-1,d=r[s].angle,h=s,p=o)):(d=r[s].angle,h=s,p=o);i.push(u);for(s=0,o=0;s<2;++o)r[o].index!==-1&&(i.push(r[o].index),s++);v=i.length;for(;o=0?(n=e.ep.r,r=e.ep.l):(n=e.ep.l,r=e.ep.r),e.a===1?(o=n?n.y:-1e6,i=e.c-e.b*o,u=r?r.y:1e6,s=e.c-e.b*u):(i=n?n.x:-1e6,o=e.c-e.a*i,s=r?r.x:1e6,u=e.c-e.a*s);var a=[i,o],f=[s,u];t[e.region.l.index].push(a,f),t[e.region.r.index].push(a,f)}),t.map(function(t,n){var r=e[n][0],i=e[n][1];return t.forEach(function(e){e.angle=Math.atan2(e[0]-r,e[1]-i)}),t.sort(function(e,t){return e.angle-t.angle}).filter(function(e,n){return!n||e.angle-t[n-1].angle>1e-10})})};var ho={l:"r",r:"l"};d3.geom.delaunay=function(e){var t=e.map(function(){return[]}),n=[];return oi(e,function(n){t[n.region.l.index].push(e[n.region.r.index])}),t.forEach(function(t,r){var i=e[r],s=i[0],o=i[1];t.forEach(function(e){e.angle=Math.atan2(e[0]-s,e[1]-o)}),t.sort(function(e,t){return e.angle-t.angle});for(var u=0,a=t.length-1;u=u,l=t.y>=a,c=(l<<1)+f;e.leaf=!1,e=e.nodes[c]||(e.nodes[c]=ui()),f?n=u:i=u,l?r=a:o=a,s(e,t,n,r,i,o)}var u,a=-1,f=e.length;f&&isNaN(e[0].x)&&(e=e.map(fi));if(arguments.length<5)if(arguments.length===3)i=r=n,n=t;else{t=n=Infinity,r=i=-Infinity;while(++ar&&(r=u.x),u.y>i&&(i=u.y);var l=r-t,c=i-n;l>c?i=n+l:r=t+c}var h=ui();return h.add=function(e){s(h,e,t,n,r,i)},h.visit=function(e){ai(e,h,t,n,r,i)},e.forEach(h.add),h},d3.time={};var po=Date,vo=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];li.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){mo.setUTCDate.apply(this._,arguments)},setDay:function(){mo.setUTCDay.apply(this._,arguments)},setFullYear:function(){mo.setUTCFullYear.apply(this._,arguments)},setHours:function(){mo.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){mo.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){mo.setUTCMinutes.apply(this._,arguments)},setMonth:function(){mo.setUTCMonth.apply(this._,arguments)},setSeconds:function(){mo.setUTCSeconds.apply(this._,arguments)},setTime:function(){mo.setTime.apply(this._,arguments)}};var mo=Date.prototype,go="%a %b %e %H:%M:%S %Y",yo="%m/%d/%y",bo="%H:%M:%S",wo=vo,Eo=wo.map(ci),So=["January","February","March","April","May","June","July","August","September","October","November","December"],xo=So.map(ci);d3.time.format=function(e){function t(t){var r=[],i=-1,s=0,o,u;while(++i=12?"PM":"AM"},S:function(e){return To(e.getSeconds())},U:function(e){return To(d3.time.sundayOfYear(e))},w:function(e){return e.getDay()},W:function(e){return To(d3.time.mondayOfYear(e))},x:d3.time.format(yo),X:d3.time.format(bo),y:function(e){return To(e.getFullYear()%100)},Y:function(e){return Co(e.getFullYear()%1e4)},Z:_i,"%":function(e){return"%"}},Ho={a:vi,A:mi,b:gi,B:yi,c:bi,d:Ci,e:Ci,H:ki,I:ki,L:Oi,m:Ni,M:Li,p:Mi,S:Ai,x:wi,X:Ei,y:xi,Y:Si},Bo=/^\s*\d+/,jo=d3.map({am:0,pm:1});d3.time.format.utc=function(e){function t(e){try{po=li;var t=new po;return t._=e,n(t)}finally{po=Date}}var n=d3.time.format(e);return t.parse=function(e){try{po=li;var t=n.parse(e);return t&&t._}finally{po=Date}},t.toString=n.toString,t};var Fo=d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");d3.time.format.iso=Date.prototype.toISOString?Di:Fo,Di.parse=function(e){var t=new Date(e);return isNaN(t)?null:t},Di.toString=Fo.toString,d3.time.second=Pi(function(e){return new po(Math.floor(e/1e3)*1e3)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*1e3)},function(e){return e.getSeconds()}),d3.time.seconds=d3.time.second.range,d3.time.seconds.utc=d3.time.second.utc.range,d3.time.minute=Pi(function(e){return new po(Math.floor(e/6e4)*6e4)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*6e4)},function(e){return e.getMinutes()}),d3.time.minutes=d3.time.minute.range,d3.time.minutes.utc=d3.time.minute.utc.range,d3.time.hour=Pi(function(e){var t=e.getTimezoneOffset()/60;return new po((Math.floor(e/36e5-t)+t)*36e5)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*36e5)},function(e){return e.getHours()}),d3.time.hours=d3.time.hour.range,d3.time.hours.utc=d3.time.hour.utc.range,d3.time.day=Pi(function(e){var t=new po(1970,0);return t.setFullYear(e.getFullYear(),e.getMonth(),e.getDate()),t},function(e,t){e.setDate(e.getDate()+t)},function(e){return e.getDate()-1}),d3.time.days=d3.time.day.range,d3.time.days.utc=d3.time.day.utc.range,d3.time.dayOfYear=function(e){var t=d3.time.year(e);return Math.floor((e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5)},vo.forEach(function(e,t){e=e.toLowerCase(),t=7-t;var n=d3.time[e]=Pi(function(e){return(e=d3.time.day(e)).setDate(e.getDate()-(e.getDay()+t)%7),e},function(e,t){e.setDate(e.getDate()+Math.floor(t)*7)},function(e){var n=d3.time.year(e).getDay();return Math.floor((d3.time.dayOfYear(e)+(n+t)%7)/7)-(n!==t)});d3.time[e+"s"]=n.range,d3.time[e+"s"].utc=n.utc.range,d3.time[e+"OfYear"]=function(e){var n=d3.time.year(e).getDay();return Math.floor((d3.time.dayOfYear(e)+(n+t)%7)/7)}}),d3.time.week=d3.time.sunday,d3.time.weeks=d3.time.sunday.range,d3.time.weeks.utc=d3.time.sunday.utc.range,d3.time.weekOfYear=d3.time.sundayOfYear,d3.time.month=Pi(function(e){return e=d3.time.day(e),e.setDate(1),e},function(e,t){e.setMonth(e.getMonth()+t)},function(e){return e.getMonth()}),d3.time.months=d3.time.month.range,d3.time.months.utc=d3.time.month.utc.range,d3.time.year=Pi(function(e){return e=d3.time.day(e),e.setMonth(0,1),e},function(e,t){e.setFullYear(e.getFullYear()+t)},function(e){return e.getFullYear()}),d3.time.years=d3.time.year.range,d3.time.years.utc=d3.time.year.utc.range;var Io=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],qo=[[d3.time.second,1],[d3.time.second,5],[d3.time.second,15],[d3.time.second,30],[d3.time.minute,1],[d3.time.minute,5],[d3.time.minute,15],[d3.time.minute,30],[d3.time.hour,1],[d3.time.hour,3],[d3.time.hour,6],[d3.time.hour,12],[d3.time.day,1],[d3.time.day,2],[d3.time.week,1],[d3.time.month,1],[d3.time.month,3],[d3.time.year,1]],Ro=[[d3.time.format("%Y"),function(e){return!0}],[d3.time.format("%B"),function(e){return e.getMonth()}],[d3.time.format("%b %d"),function(e){return e.getDate()!=1}],[d3.time.format("%a %d"),function(e){return e.getDay()&&e.getDate()!=1}],[d3.time.format("%I %p"),function(e){return e.getHours()}],[d3.time.format("%I:%M"),function(e){return e.getMinutes()}],[d3.time.format(":%S"),function(e){return e.getSeconds()}],[d3.time.format(".%L"),function(e){return e.getMilliseconds()}]],Uo=d3.scale.linear(),zo=Ii(Ro);qo.year=function(e,t){return Uo.domain(e.map(Ri)).ticks(t).map(qi)},d3.time.scale=function(){return Bi(d3.scale.linear(),qo,zo)};var Wo=qo.map(function(e){return[e[0].utc,e[1]]}),Xo=[[d3.time.format.utc("%Y"),function(e){return!0}],[d3.time.format.utc("%B"),function(e){return e.getUTCMonth()}],[d3.time.format.utc("%b %d"),function(e){return e.getUTCDate()!=1}],[d3.time.format.utc("%a %d"),function(e){return e.getUTCDay()&&e.getUTCDate()!=1}],[d3.time.format.utc("%I %p"),function(e){return e.getUTCHours()}],[d3.time.format.utc("%I:%M"),function(e){return e.getUTCMinutes()}],[d3.time.format.utc(":%S"),function(e){return e.getUTCSeconds()}],[d3.time.format.utc(".%L"),function(e){return e.getUTCMilliseconds()}]],Vo=Ii(Xo);Wo.year=function(e,t){return Uo.domain(e.map(zi)).ticks(t).map(Ui)},d3.time.scale.utc=function(){return Bi(d3.scale.linear(),Wo,Vo)}})(); --------------------------------------------------------------------------------