Example #1
0
def build_linked_spn_from_scope_graph(scope_graph,
                                      k,
                                      root_scope=None,
                                      feature_values=None):
    """
    Turning a ScopeGraph into an SPN by puttin k sum nodes for each scope
    and a combinatorial number of product nodes to wire the partition nodes

    This is the algorithm used in Poon2011 and is shown (and used) as BuildSPN in Dennis2012
    """

    if not root_scope:
        root_scope = scope_graph.root

    n_vars = len(root_scope.vars)
    if not feature_values:
        #
        # assuming binary r.v.s
        feature_values = [2 for _i in range(n_vars)]

    #
    # adding leaves
    leaves_dict = defaultdict(list)
    leaves_list = []
    for var in sorted(root_scope.vars):
        for var_val in range(feature_values[var]):
            leaf = CategoricalIndicatorNode(var, var_val)
            leaves_list.append(leaf)
            leaves_dict[var].append(leaf)

    input_layer = CategoricalIndicatorLayer(nodes=leaves_list,
                                            vars=list(sorted(root_scope.vars)))

    #
    # in a first pass we need to assign each scope/region k sum nodes
    sum_nodes_assoc = {}
    for r in scope_graph.traverse_scopes(root_scope=root_scope):

        num_sum_nodes = k

        if r == root_scope:
            num_sum_nodes = 1

        added_sum_nodes = [
            SumNode(var_scope=r.vars) for i in range(num_sum_nodes)
        ]
        #
        # creating a sum layer
        sum_layer = SumLayer(added_sum_nodes)
        sum_nodes_assoc[r] = sum_layer

        #
        # if this is a univariate scope, we link it to leaves corresponding to its r.v.
        if r.is_atomic():
            single_rv = set(r.vars).pop()
            rv_leaves = leaves_dict[single_rv]
            uniform_weight = 1.0 / len(rv_leaves)
            for s in added_sum_nodes:
                for leaf in rv_leaves:
                    s.add_child(leaf, uniform_weight)
            #
            # linking to input layer
            sum_layer.add_input_layer(input_layer)
            input_layer.add_output_layer(sum_layer)

    layers = []
    #
    # looping again to add and wire product nodes
    for r in scope_graph.traverse_scopes(root_scope=root_scope):

        sum_layer = sum_nodes_assoc[r]
        layers.append(sum_layer)

        for p in r.partitions:

            sum_layer_descs = [sum_nodes_assoc[r_p] for r_p in p.scopes]
            sum_nodes_lists = [
                list(layer.nodes()) for layer in sum_layer_descs
            ]
            num_prod_nodes = numpy.prod([len(r_p) for r_p in sum_nodes_lists])

            #
            # adding product nodes
            added_prod_nodes = [
                ProductNode(var_scope=r.vars) for i in range(num_prod_nodes)
            ]
            #
            # adding product layer and linking
            prod_layer = ProductLayer(added_prod_nodes)
            sum_layer.add_input_layer(prod_layer)
            prod_layer.add_output_layer(sum_layer)
            for desc in sum_layer_descs:
                prod_layer.add_input_layer(desc)
                desc.add_output_layer(prod_layer)
            layers.append(prod_layer)

            #
            # linking to parents
            sum_nodes_parents = sum_layer.nodes()
            for sum_node in sum_nodes_parents:
                uniform_weight = 1.0 / (len(added_prod_nodes) *
                                        len(r.partitions))
                for prod_node in added_prod_nodes:
                    sum_node.add_child(prod_node, uniform_weight)
            #
            # linking to children
            sum_nodes_to_wire = list(itertools.product(*sum_nodes_lists))

            assert len(added_prod_nodes) == len(sum_nodes_to_wire)

            for prod_node, sum_nodes in zip(added_prod_nodes,
                                            sum_nodes_to_wire):
                for sum_node in sum_nodes:
                    prod_node.add_child(sum_node)

    #
    # toposort
    layers = topological_layer_sort(layers)

    spn = LinkedSpn(layers=layers, input_layer=input_layer)

    return spn
Example #2
0
def test_compute_block_layer_depths_II():

    input_layer = CategoricalIndicatorLayer([])

    sum_layer_1 = SumLayer([])

    prod_layer_21 = ProductLayer([])
    prod_layer_22 = ProductLayer([])

    sum_layer_3 = SumLayer([])

    prod_layer_41 = ProductLayer([])
    prod_layer_42 = ProductLayer([])

    sum_layer_5 = SumLayer([])

    #
    # linking them
    sum_layer_1.add_input_layer(input_layer)
    input_layer.add_output_layer(sum_layer_1)

    prod_layer_21.add_input_layer(sum_layer_1)
    prod_layer_22.add_input_layer(sum_layer_1)
    sum_layer_1.add_output_layer(prod_layer_21)
    sum_layer_1.add_output_layer(prod_layer_22)

    sum_layer_3.add_input_layer(prod_layer_21)
    sum_layer_3.add_input_layer(prod_layer_22)
    sum_layer_3.add_input_layer(input_layer)
    prod_layer_21.add_output_layer(sum_layer_3)
    prod_layer_22.add_output_layer(sum_layer_3)
    input_layer.add_output_layer(sum_layer_3)

    prod_layer_41.add_input_layer(sum_layer_3)
    prod_layer_41.add_input_layer(sum_layer_1)
    prod_layer_42.add_input_layer(sum_layer_3)
    prod_layer_42.add_input_layer(input_layer)
    sum_layer_3.add_output_layer(prod_layer_41)
    sum_layer_3.add_output_layer(prod_layer_42)
    sum_layer_1.add_output_layer(prod_layer_41)
    input_layer.add_output_layer(prod_layer_42)

    sum_layer_5.add_input_layer(prod_layer_41)
    sum_layer_5.add_input_layer(prod_layer_42)
    prod_layer_41.add_output_layer(sum_layer_5)
    prod_layer_42.add_output_layer(sum_layer_5)

    #
    # creating an SPN with unordered layer list
    spn = LinkedSpn(input_layer=input_layer,
                    layers=[
                        sum_layer_1, sum_layer_3, sum_layer_5, prod_layer_21,
                        prod_layer_22, prod_layer_41, prod_layer_42
                    ])

    depth_dict = compute_block_layer_depths(spn)

    print(spn)

    for layer, depth in depth_dict.items():
        print(layer.id, depth)