Beispiel #1
0
 def test_manual_size(self):
     pgm = daft.PGM(shape=[1, 1], origin=[0, 0])
     pgm = daft.PGM()
     pgm.add_node("node1", x=0.0, y=0.0)
     pgm.add_node("node2", x=1.0, y=0.0)
     pgm.add_edge("node1", "node2")
     pgm.render()
def daft_pooled():

    # create the PGM
    pgm = daft.PGM(shape=[4, 2.5],
                   origin=[0, 0],
                   grid_unit=4,
                   label_params={'fontsize': 18})

    # priors
    pgm.add_node(daft.Node("beta", r"$\beta$", 1, 2, scale=2))

    # Latent variable.
    pgm.add_node(daft.Node("mu", r"$\beta X_{n}$", 1, 1, scale=2))

    # noise
    pgm.add_node(daft.Node("epsilon", r"$\epsilon$", 3, 1, scale=2))

    # observed data
    pgm.add_node(daft.Node("y", r"$y_n$", 2, 1, scale=2, observed=True))

    # edges
    pgm.add_edge("beta", "mu")
    pgm.add_edge("mu", "y")
    pgm.add_edge("epsilon", "y")

    # plate
    pgm.add_plate(
        daft.Plate([0.5, 0.6, 2, 0.9], label=r"$n \in 1:N$", shift=-0.1))

    pgm.render()
    plt.show()
Beispiel #3
0
def weakLensing():
    pgm = daft.PGM([4.7, 2.35], origin=[-1.35, 2.2])

    pgm.add_node(daft.Node("Omega", r"$\Omega$", -1, 4))
    pgm.add_node(daft.Node("rho", r"$\rho$", 0, 4))
    pgm.add_node(
        daft.Node("obs", r"$\epsilon^{\mathrm{obs}}_n$", 1, 4, observed=True))
    pgm.add_node(daft.Node("alpha", r"$\alpha$", 3, 4))
    pgm.add_node(daft.Node("true", r"$\epsilon^{\mathrm{true}}_n$", 2, 4))
    pgm.add_node(daft.Node("sigma", r"$\sigma_n$", 1, 3))
    pgm.add_node(daft.Node("Sigma", r"$\Sigma$", 0, 3))
    pgm.add_node(daft.Node("x", r"$x_n$", 2, 3, observed=True))

    pgm.add_plate(daft.Plate([0.5, 2.25, 2, 2.25], label=r"galaxies $n$"))

    pgm.add_edge("Omega", "rho")
    pgm.add_edge("rho", "obs")
    pgm.add_edge("alpha", "true")
    pgm.add_edge("true", "obs")
    pgm.add_edge("x", "obs")
    pgm.add_edge("Sigma", "sigma")
    pgm.add_edge("sigma", "obs")

    pgm.render()

    pgm.figure.savefig("weaklensing.pdf")
Beispiel #4
0
 def plot(self, layout=None, show_observed=False):
     layout = layout or Layout()
     layout.compute_dag(self.dag)
     from matplotlib import rc
     rc("font", family="serif", size=12)
     rc("text", usetex=True)
     import daft
     pgm = daft.PGM([layout.Lx, layout.Ly], origin=[0, 0])
     nodes = nx.topological_sort(self.dag)
     n_nodes = len(nodes)
     id_obs = 0
     for l_node, node in enumerate(nodes):
         x = layout.dag.node[node]["x"]
         y = layout.dag.node[node]["y"]
         fixed = isinstance(node, Factor) or isinstance(node, PlaceHolder)
         pgm.add_node(daft.Node(
             l_node, node.math(), x, y, fixed=fixed
         ))
         if isinstance(node, Likelihood):
             l_obs = n_nodes + id_obs
             pgm.add_node(daft.Node(
                 l_obs, node.y_name, x + layout.dx, y, observed=True
             ))
             pgm.add_edge(l_node, l_obs)
             id_obs += 1
     for source, target in self.dag.edges():
         l_source = nodes.index(source)
         l_target = nodes.index(target)
         pgm.add_edge(l_source, l_target)
     pgm.render()
Beispiel #5
0
def test_add_text():
    with daft.PGM() as pgm:
        pgm.add_text(x=0, y=0, label="text1")

        plate = pgm._plates[0]
        assert plate.rect == [0, 0, 0, 0]
        assert plate.label == "text1"
Beispiel #6
0
def make_normal_model_graph():

    arrow_params = {"linewidth": 2, "head_width": 0.25}

    mu_node = daft.Node("mu", r"$\mu$", 1, 2.5, scale=2)
    x_node = daft.Node("x", "x", 3.5, 2.5, scale=2, observed=True)

    x_plate = daft.Plate([2.5, 1.5, 2, 2],
                         label=r"$N$",
                         position="bottom right")

    normal_model_graph = daft.PGM([5, 5],
                                  line_width=2,
                                  label_params={"fontsize": 32})

    normal_model_graph.add_node(mu_node)
    normal_model_graph.add_node(x_node)

    normal_model_graph.add_edge("mu", "x", **arrow_params)

    normal_model_graph.add_plate(x_plate)

    # Avoid fill with blue in newer versions of matplotlib
    normal_model_graph._plates[0].bbox["fc"] = "white"

    normal_model_graph.render()
    return normal_model_graph
def pgm_cepheids():

    # Instantiate a PGM.
    pgm = daft.PGM([2.9, 2.7], origin=[0.3, 0.3], grid_unit=2.6, node_unit=1.3, observed_style="inner")

    # Model parameters:
    pgm.add_node(daft.Node("a", r"$a$", 1.0, 2.6))
    pgm.add_node(daft.Node("b", r"$b$", 2.0, 2.6))

    # Latent variable - intrinsic magnitude:
    pgm.add_node(daft.Node("m", r"$m_k$", 1.5, 1.4, fixed=True, offset=(0,-20)))

    # Data - observed magnitude:
    pgm.add_node(daft.Node("mobs", r"$m^{\rm obs}_k$", 2.5, 1.4, observed=True))

    # Constants - magnitude errors and log Periods:
    pgm.add_node(daft.Node("logP", r"$\log_{10} P_k$", 0.9, 1.4, fixed=True, offset=(-3,1)))
    pgm.add_node(daft.Node("merr", r"$\sigma_k$", 1.9, 0.9, fixed=True, offset=(-3,2)))

    # Add in the edges.
    pgm.add_edge("a", "m")
    pgm.add_edge("b", "m")
    pgm.add_edge("logP", "m")
    pgm.add_edge("merr", "mobs")
    pgm.add_edge("m", "mobs")

    # And a plate for the pixels
    pgm.add_plate(daft.Plate([0.5, 0.7, 2.5, 1.4], label=r"cepheids $k$", shift=-0.1))

    # Render and save.
    pgm.render()
    pgm.figure.savefig("pgms_cepheids.png", dpi=300)

    return
Beispiel #8
0
def test_huey_p_newton():
    pgm = daft.PGM()

    kx, ky = 1.5, 1.0
    nx, ny = kx + 3.0, ky + 0.0
    hx, hy, dhx = kx - 0.5, ky + 1.0, 1.0

    pgm.add_node("dyn", r"$\theta_{{dyn}}$", hx + 0.0 * dhx, hy + 0.0)
    pgm.add_node("ic", r"$\theta_{{I.C.}}$", hx + 1.0 * dhx, hy + 0.0)
    pgm.add_node("sun", r"$\theta_{\odot}$", hx + 2.0 * dhx, hy + 0.0)
    pgm.add_node("bg", r"$\theta_{{bg}}$", hx + 3.0 * dhx, hy + 0.0)
    pgm.add_node("Sigma", r"$\Sigma^2$", hx + 4.0 * dhx, hy + 0.0)

    pgm.add_plate([kx - 0.5, ky - 0.6, 2.0, 1.1], label=r"model points $k$")
    pgm.add_node("xk", r"$x_k$", kx + 0.0, ky + 0.0)
    pgm.add_edge("dyn", "xk")
    pgm.add_edge("ic", "xk")
    pgm.add_node("yk", r"$y_k$", kx + 1.0, ky + 0.0)
    pgm.add_edge("sun", "yk")
    pgm.add_edge("xk", "yk")

    pgm.add_plate([nx - 0.5, ny - 0.6, 2.0, 1.1], label=r"data points $n$")
    pgm.add_node("sigman", r"$\sigma^2_n$", nx + 1.0, ny + 0.0, observed=True)
    pgm.add_node("Yn", r"$Y_n$", nx + 0.0, ny + 0.0, observed=True)
    pgm.add_edge("bg", "Yn")
    pgm.add_edge("Sigma", "Yn")
    pgm.add_edge("Sigma", "Yn")
    pgm.add_edge("yk", "Yn")
    pgm.add_edge("sigman", "Yn")

    # Render and save.
    pgm.render()
Beispiel #9
0
    def draw_network(self):

        pgm = daft.PGM([2.3, 2.05], origin=[0.3, 0.3])

        for layer in self.layers:

            self.draw_layer(pgm, layer)
Beispiel #10
0
def test_overlap_nodes():
    with daft.PGM() as pgm:
        pgm.add_node("node1", x=0, y=0)
        pgm.add_node("node2", x=0, y=0)
        pgm.add_edge("node1", "node2")
        with pytest.raises(daft.SameLocationError):
            pgm.render()
Beispiel #11
0
def make_bivariate_graph(param, obs, observed=False, plate=False, scale=2):
    arrow_params = {"linewidth": 2, "head_width": 0.25}
    if param == "sigma":
        param_string = r"$\sigma$"
    elif param == "mu":
        param_string = r"$\mu$"
    else:
        param_string = param

    param_node = daft.Node("param", param_string, 1, 2.5, scale=scale)
    obs_node = daft.Node("obs", obs, 3.5, 2.5, scale=scale, observed=observed)

    obs_plate = daft.Plate([2.5, 1.5, 2, 2],
                           label=r"$N$",
                           position="bottom right")

    bivariate_model_graph = daft.PGM([5, 5],
                                     line_width=2,
                                     label_params={"fontsize": 32})

    bivariate_model_graph.add_node(param_node)
    bivariate_model_graph.add_node(obs_node)

    bivariate_model_graph.add_edge("param", "obs", **arrow_params)

    if plate:
        bivariate_model_graph.add_plate(obs_plate)

        # Avoid fill with blue in newer versions of matplotlib
        bivariate_model_graph._plates[0].bbox["fc"] = "white"

    bivariate_model_graph.render()
    return bivariate_model_graph
Beispiel #12
0
def make_parameters_graph(observed=True, plate=False):

    arrow_params = {"linewidth": 2, "head_width": 0.25}

    in_nodes = [
        daft.Node(f"theta_{ii}",
                  f"$\\beta_{ii}$",
                  1,
                  4 - 1.5 * ii,
                  scale=2,
                  observed=observed) for ii in range(3)
    ]
    x_node = daft.Node("x", "x", 3.5, 2.5, scale=2, observed=False)

    parameters_graph = daft.PGM([5, 5],
                                line_width=2,
                                label_params={"fontsize": 32})

    [parameters_graph.add_node(node) for node in in_nodes]
    parameters_graph.add_node(x_node)

    [
        parameters_graph.add_edge(f"theta_{ii}", "x", **arrow_params)
        for ii in range(3)
    ]

    parameters_graph.render()

    return parameters_graph
Beispiel #13
0
def display_mdma_model():
    s, asp = 3, 1.25

    dose_node = daft.Node("dose", "MDMA\nDose", 1, 3, scale=s, aspect=asp)
    social_node = daft.Node("social_setting",
                            "Social\nSetting",
                            1,
                            1,
                            scale=s,
                            aspect=asp)

    esteem_node = daft.Node("esteem",
                            "Self\nEsteem",
                            5,
                            3,
                            scale=s,
                            aspect=asp)
    mood_node = daft.Node("mood", "Mood", 5, 1, scale=s, aspect=asp)

    mdma_model = daft.PGM([7, 4],
                          line_width=4,
                          label_params={
                              "fontsize": 16,
                              "fontweight": "bold"
                          })

    nodes = [dose_node, social_node, esteem_node, mood_node]
    edges = [("dose", "esteem"), ("dose", "mood"),
             ("social_setting", "esteem"), ("social_setting", "mood")]

    [mdma_model.add_node(node) for node in nodes]
    [mdma_model.add_edge(*edge) for edge in edges]

    mdma_model.render()
Beispiel #14
0
    def test_add_text(self):
        pgm = daft.PGM()
        pgm.add_text(x=0, y=0, label="text1")

        plate = pgm._plates[0]
        assert plate.rect == [0, 0, 0, 0]
        assert plate.label == "text1"
Beispiel #15
0
def test_mrf():
    pgm = daft.PGM(node_unit=0.4, grid_unit=1, directed=False)

    for i, (xi, yi) in enumerate(itertools.product(range(1, 5), range(1, 5))):
        pgm.add_node(str(i), "", xi, yi)

    for e in [
        (4, 9),
        (6, 7),
        (3, 7),
        (10, 11),
        (10, 9),
        (10, 14),
        (10, 6),
        (10, 7),
        (1, 2),
        (1, 5),
        (1, 0),
        (1, 6),
        (8, 12),
        (12, 13),
        (13, 14),
        (15, 11),
    ]:
        pgm.add_edge(str(e[0]), str(e[1]))

    pgm.render()
Beispiel #16
0
    def show_model(self):
        """Render the model as a Bayes net using daft."""
        import daft
        gray = ".3"
        pgm = daft.PGM((3.5, 4), node_ec=gray)
        scale = 1.5

        pgm.add_node(daft.Node("k", r"$k$", 1.5, 3.5, scale))
        pgm.add_node(daft.Node("vim1", r"$v_{i-1}$", .5, 2.5, scale))
        pgm.add_node(daft.Node("vi", r"$v_i$", 2.5, 2.5, scale))
        pgm.add_node(daft.Node("pim1", r"$p_{i-1}$", 1, 1.5, scale))
        pgm.add_node(daft.Node("pi", r"$p_i$", 3, 1.5, scale))
        pgm.add_node(
            daft.Node("yim1", r"$y_{i-1}$", 1, .5, scale, observed=True))
        pgm.add_node(daft.Node("yi", r"$y_i$", 3, .5, scale, observed=True))

        pgm.add_edge("k", "vim1")
        pgm.add_edge("k", "vi")
        pgm.add_edge("vim1", "pim1")
        pgm.add_edge("vi", "pi")
        pgm.add_edge("vim1", "vi")
        pgm.add_edge("pim1", "pi")
        pgm.add_edge("pim1", "yim1")
        pgm.add_edge("pi", "yi")

        pgm.render()
        return pgm
Beispiel #17
0
def test_fixed():
    pgm = daft.PGM(aspect=1.5, node_unit=1.75)
    pgm.add_node("unobs", r"Unobserved!", 1, 4)
    pgm.add_node("obs", r"Observed!", 1, 3, observed=True)
    pgm.add_node("alt", r"Alternate!", 1, 2, alternate=True)
    pgm.add_node(
        "fixed", r"Fixed!", 1, 1, fixed=True, aspect=1.0, offset=[0, 5]
    )
    pgm.render()
Beispiel #18
0
def test_bca():
    pgm = daft.PGM()
    pgm.add_node("a", r"$a$", 1, 5)
    pgm.add_node("b", r"$b$", 1, 4)
    pgm.add_node("c", r"$c_n$", 1, 3, observed=True)
    pgm.add_plate([0.5, 2.25, 1, 1.25], label=r"data $n$")
    pgm.add_edge("a", "b")
    pgm.add_edge("b", "c")
    pgm.render()
Beispiel #19
0
def test_add_edge():
    with daft.PGM() as pgm:
        pgm.add_node("node1")
        pgm.add_node("node2")
        pgm.add_edge(name1="node1", name2="node2")

        edge = pgm._edges[0]
        assert edge.node1 == pgm._nodes["node1"]
        assert edge.node2 == pgm._nodes["node2"]
Beispiel #20
0
    def test_add_edge(self):
        pgm = daft.PGM()
        pgm.add_node("node1")
        pgm.add_node("node2")
        pgm.add_edge(name1="node1", name2="node2")

        edge = pgm._edges[0]
        assert edge.node1 == pgm._nodes["node1"]
        assert edge.node2 == pgm._nodes["node2"]
Beispiel #21
0
def display_huth_model():
    s, asp = 3, 1.25
    empty_params = {"linewidth": 0}

    semantic_node = daft.Node("semantic",
                              "Semantic\nContent",
                              1,
                              3,
                              scale=s,
                              aspect=asp)

    voxel1_node = daft.Node("voxel1", "voxel1", 5, 5, scale=s, aspect=asp)

    empty1_node = daft.Node("empty1",
                            "",
                            5,
                            4,
                            scale=s,
                            aspect=asp,
                            plot_params=empty_params)
    ellipsis = daft.Node("ellipsis",
                         "...",
                         5,
                         3,
                         scale=s,
                         aspect=asp,
                         plot_params=empty_params)
    emptyN_node = daft.Node("emptyN",
                            "",
                            5,
                            2,
                            scale=s,
                            aspect=asp,
                            plot_params=empty_params)

    voxelN_node = daft.Node("voxelN", "voxelN", 5, 1, scale=s, aspect=asp)

    huth_model = daft.PGM([7, 6],
                          line_width=4,
                          label_params={
                              "fontsize": 16,
                              "fontweight": "bold"
                          })

    nodes = [
        voxel1_node, voxelN_node, semantic_node, ellipsis, empty1_node,
        emptyN_node
    ]
    edges = [("semantic", "voxel1"), ("semantic", "empty1"),
             ("semantic", "ellipsis"), ("semantic", "emptyN"),
             ("semantic", "voxelN")]

    [huth_model.add_node(node) for node in nodes]
    [huth_model.add_edge(*edge) for edge in edges]

    huth_model.render()
Beispiel #22
0
def test_logo():
    pgm = daft.PGM()
    pgm.add_node("d", r"$D$", 0.5, 0.5)
    pgm.add_node("a", r"$a$", 1.5, 0.5, observed=True)
    pgm.add_node("f", r"$f$", 2.5, 0.5)
    pgm.add_node("t", r"$t$", 3.5, 0.5)
    pgm.add_edge("d", "a")
    pgm.add_edge("a", "f")
    pgm.add_edge("f", "t")
    pgm.render()
Beispiel #23
0
    def show(self):
        self.dag = self.create_dag()
        for node_key, node_items in self.nodes.items():
            self.dag.nodes.add(node_key)
            for e in node_items['edges']:
                self.dag.edges.add((node_key, e))
            self.dag.coordinates[node_key] = (node_items['coordinate'])

        self.dag.gm = CausalGraphicalModel(nodes=self.dag.nodes,
                                           edges=self.dag.edges)
        self.dag.pgm = daft.PGM()

        pgm = daft.PGM()
        for node in self.dag.gm.dag.nodes:
            pgm.add_node(node, node, *self.dag.coordinates[node])
        for edge in self.dag.gm.dag.edges:
            pgm.add_edge(*edge)
        pgm.render()
        plt.gca()  #.invert_yaxis()
def make_figure_8p13():
    """Create a graph like figure 8.13."""
    pgm = daft.PGM([3, 2], origin=[-0.3, -0.3])
    pgm.add_node(daft.Node("x1", r"$x_1$", 0.2, 1.3, observed=True))
    pgm.add_node(daft.Node("xm", r"$x_M$", 1.8, 1.3, observed=True))
    pgm.add_node(daft.Node("y", r"$y$", 1.0, 0.3, observed=True))
    pgm.add_edge("x1", "xm", directed=False, ls=":")
    pgm.add_edge("x1", "y")
    pgm.add_edge("xm", "y")
    pgm.render()
    pgm.figure.savefig("figures/fig_8p13.pdf")
def make_figure_8p1():
    """Make the intro graph."""
    pgm = daft.PGM([2.5, 2.5], origin=[-0.3, -0.3])
    pgm.add_node(daft.Node("a", r"a", 1., 1.8, observed=True))
    pgm.add_node(daft.Node("b", r"b", 0.2, 0.2, observed=True))
    pgm.add_node(daft.Node("c", r"c", 1.8, 0.2, observed=True))
    pgm.add_edge("a", "b")
    pgm.add_edge("b", "c")
    pgm.add_edge("a", "c")
    pgm.render()
    pgm.figure.savefig("figures/fig_8p1.pdf")
Beispiel #26
0
def test_no_circles():
    pgm = daft.PGM(node_ec="none")
    pgm.add_node("cloudy", r"cloudy", 3, 3)
    pgm.add_node("rain", r"rain", 2, 2)
    pgm.add_node("sprinkler", r"sprinkler", 4, 2)
    pgm.add_node("wet", r"grass wet", 3, 1)
    pgm.add_edge("cloudy", "rain")
    pgm.add_edge("cloudy", "sprinkler")
    pgm.add_edge("rain", "wet")
    pgm.add_edge("sprinkler", "wet")
    pgm.render()
def plot_gmm_plate(filename="gmm.png", dpi=100):
    pgm = daft.PGM([3.0, 2.5], origin=(0, 0))
    pgm.add_node(daft.Node("theta", r"$\mathbf{\theta}$", 1, 2, fixed=True))
    pgm.add_node(daft.Node("ti", r"$\mathbf{t}_i$", 1, 1))
    pgm.add_node(daft.Node("xi", r"$\mathbf{x}_i$", 2, 1, observed=True))
    pgm.add_edge("theta", "ti")
    pgm.add_edge("theta", "xi")
    pgm.add_edge("ti", "xi")
    pgm.add_plate(daft.Plate([0.4, 0.5, 2.2, 1.0], label=r"$N$"))
    ax = pgm.render()
    ax.text(0.8, 0.5, 'Gaussian mixture model')
    pgm.savefig(filename, dpi=dpi)
def daft_hier():

    # create the PGM
    pgm = daft.PGM(shape=[7, 2.5],
                   origin=[0, 0],
                   grid_unit=4,
                   label_params={'fontsize': 18})

    # priors
    pgm.add_node(daft.Node("beta_parent_mu", r"$\mu_{parent}$", 1, 1, scale=2))
    pgm.add_node(
        daft.Node("beta_parent_sd", r"$\sigma_{parent}$", 2, 2.2, scale=2))
    pgm.add_node(daft.Node("beta_mfr_mu", r"$\mu_{mfr}$", 2, 1, scale=2))
    pgm.add_node(daft.Node("beta_mfr_sd", r"$\sigma_{mfr}$", 3, 2.2, scale=2))
    pgm.add_node(daft.Node("beta_mfr", r"$\beta_{mfr}$", 3, 1, scale=2))
    pgm.add_node(daft.Node("beta", r"$\beta$", 4, 2.2, scale=2))

    # latent variable.
    pgm.add_node(daft.Node("mu", r"$\beta X_{n}$", 4, 1, scale=2))

    # noise
    pgm.add_node(daft.Node("epsilon", r"$\epsilon$", 6.2, 1, scale=2))

    # observed data
    pgm.add_node(daft.Node("y", r"$y_n$", 5, 1, scale=2, observed=True))

    # edges
    pgm.add_edge("beta_parent_mu", "beta_mfr_mu")
    pgm.add_edge("beta_parent_sd", "beta_mfr_mu")
    pgm.add_edge("beta_mfr_mu", "beta_mfr")
    pgm.add_edge("beta_mfr_sd", "beta_mfr")
    pgm.add_edge("beta_mfr", "mu")
    pgm.add_edge("beta", "mu")
    pgm.add_edge("mu", "y")
    pgm.add_edge("epsilon", "y")

    # plates
    pgm.add_plate(
        daft.Plate([3.5, 0.6, 2, 0.9], label=r"$n \in 1:N$", shift=-0.1))

    pgm.add_plate(
        daft.Plate([2.5, 0.5, 3.1, 1.1],
                   label=r"$mfr \in 1:N_{mfr}$",
                   shift=-0.1))

    pgm.add_plate(
        daft.Plate([1.5, 0.4, 4.2, 1.3],
                   label=r"$parent \in 1:N_{parent}$",
                   shift=-0.1))

    pgm.render()
    plt.show()
Beispiel #29
0
def test_recursive():
    def recurse(pgm, nodename, level, c):
        if level > 4:
            return nodename
        r = c // 2
        r1nodename = "r{0:02d}{1:04d}".format(level, r)
        if 2 * r == c:
            # print("adding {0}".format(r1nodename))
            pgm.add_node(
                r1nodename,
                r"reduce",
                2**level * (r + 0.5) - 0.5,
                3 - 0.7 * level,
                aspect=1.9,
            )
        pgm.add_edge(nodename, r1nodename)
        if 2 * r == c:
            return recurse(pgm, r1nodename, level + 1, r)

    pgm = daft.PGM()

    pgm.add_node(
        "query",
        r'"kittens?"',
        3,
        6.0,
        aspect=3.0,
        plot_params={"ec": "none"},
    )
    pgm.add_node("input", r"input", 7.5, 6.0, aspect=3.0)
    pgm.add_edge("query", "input")

    for c in range(16):
        nodename = "map {0:02d}".format(c)
        pgm.add_node(nodename, str(nodename), c, 3.0, aspect=1.9)
        pgm.add_edge("input", nodename)
        level = 1
        recurse(pgm, nodename, level, c)

    pgm.add_node("output", r"output", 7.5, -1.0, aspect=3.0)
    pgm.add_edge("r040000", "output")
    pgm.add_node(
        "answer",
        r'"http://dwh.gg/"',
        12.0,
        -1.0,
        aspect=4.5,
        plot_params={"ec": "none"},
    )
    pgm.add_edge("output", "answer")

    pgm.render()
def make_figure_8p9():
    """Create a pair of graphs like figure 8.9."""
    pgm = daft.PGM([3, 2], origin=[-0.3, -0.3])
    pgm.add_node(
        daft.Node("x11", r"$\boldsymbol{x}_1$", 0.2, 1.3, observed=True))
    pgm.add_node(
        daft.Node("x21", r"$\boldsymbol{x}_2$", 1.8, 1.3, observed=True))
    pgm.add_node(
        daft.Node("x12", r"$\boldsymbol{x}_1$", 0.2, 0.3, observed=True))
    pgm.add_node(
        daft.Node("x22", r"$\boldsymbol{x}_2$", 1.8, 0.3, observed=True))
    pgm.add_edge("x11", "x21")
    pgm.render()
    pgm.figure.savefig("figures/fig_8p9.pdf")