def test_bnet_sumproduct():
    """
    Testing: SUM-PRODUCT on BNET
    This example is based on the lawn sprinkler example, and the Bayesian
    network has the following structure, with all edges directed downwards:

                            Cloudy - 0
                             /  \
                            /    \
                           /      \
                   Sprinkler - 1  Rainy - 2
                           \      /
                            \    /
                             \  /
                           Wet Grass -3                
    """
    """Assign a unique numerical identifier to each node"""
    C = 0
    S = 1
    R = 2
    W = 3

    """Assign the number of nodes in the graph"""
    nodes = 4

    """
    The graph structure is represented as a adjacency matrix, dag.
    If dag[i, j] = 1, then there exists a directed edge from node
    i and node j.
    """
    dag = np.zeros((nodes, nodes))
    dag[C, [R, S]] = 1
    dag[R, W] = 1
    dag[S, W] = 1

    """
    Define the size of each node, which is the number of different values a
    node could observed at. For example, if a node is either True of False,
    it has only 2 possible values it could be, therefore its size is 2. All
    the nodes in this graph has a size 2.
    """
    node_sizes = 2 * np.ones(nodes)

    """
    We now need to assign a conditional probability distribution to each
    node.
    """
    node_cpds = [[], [], [], []]

    """Define the CPD for node 0"""
    CPT = np.array([0.5, 0.5])
    node_cpds[C] = cpds.TabularCPD(CPT)

    """Define the CPD for node 1"""
    CPT = np.array([[0.8, 0.2], [0.2, 0.8]])
    node_cpds[R] = cpds.TabularCPD(CPT)

    """Define the CPD for node 2"""
    CPT = np.array([[0.5, 0.5], [0.9, 0.1]])
    node_cpds[S] = cpds.TabularCPD(CPT)

    """Define the CPD for node 3"""
    CPT = np.array([[[1, 0], [0.1, 0.9]], [[0.1, 0.9], [0.01, 0.99]]])
    node_cpds[W] = cpds.TabularCPD(CPT)

    """Create the Bayesian network"""
    net = models.bnet(dag, node_sizes, node_cpds=node_cpds)

    """
    Intialize the BNET's inference engine to use EXACT inference
    by setting exact=True.
    """
    net.init_inference_engine(exact=True)

    """Create and enter evidence ([] means that node is unobserved)"""
    all_ev = sprinkler_evidence();
    all_prob = sprinkler_probs();

    count = 0;
    errors = 0;
    for evidence in all_ev:
        """Execute the max-sum algorithm"""
        net.sum_product(evidence)

        ans = [1, 1, 1, 1]
        marginal = net.marginal_nodes([C])

        if evidence[C] is None:
            ans[C] = marginal.T[1]
            
        marginal = net.marginal_nodes([S])
        if evidence[S] is None:
            ans[S] = marginal.T[1]
            
        marginal = net.marginal_nodes([R])
        if evidence[R] is None:
            ans[R] = marginal.T[1]
            
        marginal = net.marginal_nodes([W])
        if evidence[W] is None:
            ans[W] = marginal.T[1]

        errors = errors +  \
                 np.round(np.sum(np.array(ans) - np.array(all_prob[count])), 3)
        count = count + 1

    assert errors == 0
def test_mrf_maxsum():
    """
    Testing: MAX-SUM of MRF
    This example is based on the lawn sprinkler example, and the Markov
    random field has the following structure.

                            Cloudy - 0
                             /  \
                            /    \
                           /      \
                   Sprinkler - 1  Rainy - 2
                           \      /
                            \    /
                             \  /
                           Wet Grass -3                
    """
    """Assign a unique numerical identifier to each node"""
    C = 0
    S = 1
    R = 2
    W = 3
    """Assign the number of nodes in the graph"""
    nodes = 4
    """
    The graph structure is represented as a adjacency matrix, dag.
    If adj_mat[i, j] = 1, then there exists a undirected edge from node
    i and node j.
    """
    adj_mat = np.matrix(np.zeros((nodes, nodes)))
    adj_mat[C, [R, S]] = 1
    adj_mat[R, W] = 1
    adj_mat[S, W] = 1
    """
    Define the size of each node, which is the number of different values a
    node could observed at. For example, if a node is either True of False,
    it has only 2 possible values it could be, therefore its size is 2. All
    the nodes in this graph has a size 2.
    """
    ns = 2 * np.ones(nodes)
    """
    Define the clique domains. The domain of a clique, is the indices of the
    nodes in the clique. A clique is a fully connected set of nodes.
    Therefore, for a set of node to be a clique, every node in the set must
    be connected to every other node in the set.
    """
    clq_doms = [[0, 1, 2], [1, 2, 3]]
    """Define potentials for the cliques"""
    clqs = []
    T = np.zeros((2, 2, 2))
    T[:, :, 0] = np.array([[0.2, 0.2], [0.09, 0.01]])
    T[:, :, 1] = np.array([[0.05, 0.05], [0.36, 0.04]])
    clqs.append(cliques.clique(0, clq_doms[0], np.array([2, 2, 2]), T))
    T[:, :, 0] = np.array([[1, 0.1], [0.1, 0.01]])
    T[:, :, 1] = np.array([[0, 0.9], [0.9, 0.99]])
    clqs.append(cliques.clique(1, clq_doms[1], np.array([2, 2, 2]), T))
    """Create the MRF"""
    net = models.mrf(adj_mat, ns, clqs, lattice=False)
    """
    Intialize the MRF's inference engine to use EXACT inference, by
    setting exact=True.
    """
    net.init_inference_engine(exact=True)
    """Create and enter evidence ([] means that node is unobserved)"""
    all_ev = sprinkler_evidence()
    all_mpe = sprinkler_mpe()

    count = 0
    errors = 0
    for evidence in all_ev:
        """Execute the max-sum algorithm"""
        mlc = net.max_sum(evidence)

        mpe = all_mpe[count]
        errors = errors + np.sum(np.array(mpe) - np.array(mlc))

        count = count + 1

    assert errors == 0
def test_mrf_sumproduct():
    """
    Testing: SUM-PRODUCT on MRF
    This example is based on the lawn sprinkler example, and the Markov
    random field has the following structure.

                            Cloudy - 0
                             /  \
                            /    \
                           /      \
                   Sprinkler - 1  Rainy - 2
                           \      /
                            \    /
                             \  /
                           Wet Grass -3                
    """
    """Assign a unique numerical identifier to each node"""
    C = 0
    S = 1
    R = 2
    W = 3

    """Assign the number of nodes in the graph"""
    nodes = 4

    """
    The graph structure is represented as a adjacency matrix, dag.
    If adj_mat[i, j] = 1, then there exists a undirected edge from node
    i and node j.
    """
    adj_mat = np.matrix(np.zeros((nodes, nodes)))
    adj_mat[C, [R, S]] = 1
    adj_mat[R, W] = 1
    adj_mat[S, W] = 1

    """
    Define the size of each node, which is the number of different values a
    node could observed at. For example, if a node is either True of False,
    it has only 2 possible values it could be, therefore its size is 2. All
    the nodes in this graph has a size 2.
    """
    ns = 2 * np.ones(nodes)

    """
    Define the clique domains. The domain of a clique, is the indices of the
    nodes in the clique. A clique is a fully connected set of nodes.
    Therefore, for a set of node to be a clique, every node in the set must
    be connected to every other node in the set.
    """
    clq_doms = [[0, 1, 2], [1, 2, 3]]

    """Define potentials for the cliques"""
    clqs = []
    T = np.zeros((2, 2, 2))
    T[:, :, 0] = np.array([[0.2, 0.2], [0.09, 0.01]])
    T[:, :, 1] = np.array([[0.05, 0.05], [0.36, 0.04]])
    clqs.append(cliques.clique(0, clq_doms[0], np.array([2, 2, 2]), T))
    T[:, :, 0] = np.array([[1, 0.1], [0.1, 0.01]])
    T[:, :, 1] = np.array([[0, 0.9], [0.9, 0.99]])
    clqs.append(cliques.clique(1, clq_doms[1], np.array([2, 2, 2]), T))

    """Create the MRF"""
    net = models.mrf(adj_mat, ns, clqs, lattice=False)

    """
    Intialize the MRF's inference engine to use EXACT inference, by
    setting exact=True.
    """
    net.init_inference_engine(exact=True)

    """Create and enter evidence ([] means that node is unobserved)"""
    all_ev = sprinkler_evidence();
    all_prob = sprinkler_probs();

    count = 0;
    errors = 0;
    for evidence in all_ev:
        """Execute the max-sum algorithm"""
        net.sum_product(evidence)

        ans = [1, 1, 1, 1]
        marginal = net.marginal_nodes([C])

        if evidence[C] is None:
            ans[C] = marginal.T[1]
            
        marginal = net.marginal_nodes([S])
        if evidence[S] is None:
            ans[S] = marginal.T[1]
            
        marginal = net.marginal_nodes([R])
        if evidence[R] is None:
            ans[R] = marginal.T[1]
            
        marginal = net.marginal_nodes([W])
        if evidence[W] is None:
            ans[W] = marginal.T[1]

        errors = errors +  \
                 np.round(np.sum(np.array(ans) - np.array(all_prob[count])), 3)
        count = count + 1

    assert errors == 0
def test_bnet_sumproduct():
    """
    Testing: SUM-PRODUCT on BNET
    This example is based on the lawn sprinkler example, and the Bayesian
    network has the following structure, with all edges directed downwards:

                            Cloudy - 0
                             /  \
                            /    \
                           /      \
                   Sprinkler - 1  Rainy - 2
                           \      /
                            \    /
                             \  /
                           Wet Grass -3                
    """
    """Assign a unique numerical identifier to each node"""
    C = 0
    S = 1
    R = 2
    W = 3
    """Assign the number of nodes in the graph"""
    nodes = 4
    """
    The graph structure is represented as a adjacency matrix, dag.
    If dag[i, j] = 1, then there exists a directed edge from node
    i and node j.
    """
    dag = np.zeros((nodes, nodes))
    dag[C, [R, S]] = 1
    dag[R, W] = 1
    dag[S, W] = 1
    """
    Define the size of each node, which is the number of different values a
    node could observed at. For example, if a node is either True of False,
    it has only 2 possible values it could be, therefore its size is 2. All
    the nodes in this graph has a size 2.
    """
    node_sizes = 2 * np.ones(nodes)
    """
    We now need to assign a conditional probability distribution to each
    node.
    """
    node_cpds = [[], [], [], []]
    """Define the CPD for node 0"""
    CPT = np.array([0.5, 0.5])
    node_cpds[C] = cpds.TabularCPD(CPT)
    """Define the CPD for node 1"""
    CPT = np.array([[0.8, 0.2], [0.2, 0.8]])
    node_cpds[R] = cpds.TabularCPD(CPT)
    """Define the CPD for node 2"""
    CPT = np.array([[0.5, 0.5], [0.9, 0.1]])
    node_cpds[S] = cpds.TabularCPD(CPT)
    """Define the CPD for node 3"""
    CPT = np.array([[[1, 0], [0.1, 0.9]], [[0.1, 0.9], [0.01, 0.99]]])
    node_cpds[W] = cpds.TabularCPD(CPT)
    """Create the Bayesian network"""
    net = models.bnet(dag, node_sizes, node_cpds=node_cpds)
    """
    Intialize the BNET's inference engine to use EXACT inference
    by setting exact=True.
    """
    net.init_inference_engine(exact=True)
    """Create and enter evidence ([] means that node is unobserved)"""
    all_ev = sprinkler_evidence()
    all_prob = sprinkler_probs()

    count = 0
    errors = 0
    for evidence in all_ev:
        """Execute the max-sum algorithm"""
        net.sum_product(evidence)

        ans = [1, 1, 1, 1]
        marginal = net.marginal_nodes([C])

        if evidence[C] is None:
            ans[C] = marginal.T[1]

        marginal = net.marginal_nodes([S])
        if evidence[S] is None:
            ans[S] = marginal.T[1]

        marginal = net.marginal_nodes([R])
        if evidence[R] is None:
            ans[R] = marginal.T[1]

        marginal = net.marginal_nodes([W])
        if evidence[W] is None:
            ans[W] = marginal.T[1]

        errors = errors +  \
                 np.round(np.sum(np.array(ans) - np.array(all_prob[count])), 3)
        count = count + 1

    assert errors == 0