Example #1
0
def test_product_layer_create_and_eval():
    # creating generic nodes
    node1 = Node()
    node2 = Node()
    node3 = Node()

    # whose values are
    val1 = 0.8
    val2 = 1.
    val3 = 0.
    node1.set_val(val1)
    node2.set_val(val2)
    node3.set_val(val3)

    # creating product nodes
    prod1 = ProductNode()
    prod2 = ProductNode()
    prod3 = ProductNode()

    # adding children
    prod1.add_child(node1)
    prod1.add_child(node2)

    prod2.add_child(node1)
    prod2.add_child(node3)

    prod3.add_child(node2)
    prod3.add_child(node3)

    # adding product nodes to layer
    product_layer = ProductLayer([prod1, prod2, prod3])

    # evaluating
    product_layer.eval()

    # getting log vals
    layer_evals = product_layer.node_values()
    print('layer eval nodes')
    print(layer_evals)

    # computing our values
    prodval1 = val1 * val2
    logval1 = log(prodval1) if prodval1 > 0. else LOG_ZERO
    prodval2 = val1 * val3
    logval2 = log(prodval2) if prodval2 > 0. else LOG_ZERO
    prodval3 = val2 * val3
    logval3 = log(prodval3) if prodval3 > 0. else LOG_ZERO
    logvals = [logval1, logval2, logval3]
    print('log vals')
    print(logvals)

    for logval, eval in zip(logvals, layer_evals):
        if logval == LOG_ZERO:
            # for zero log check this way for correctness
            assert IS_LOG_ZERO(eval) is True
        else:
            assert_almost_equal(logval, eval, PRECISION)
Example #2
0
def build_spn_layers_II(input_layer):

    # this is ugly... TODO try to beutify this process
    ind1 = input_layer._nodes[0]
    ind2 = input_layer._nodes[1]
    ind3 = input_layer._nodes[2]
    ind4 = input_layer._nodes[3]

    # creating product nodes
    prod_node1 = ProductNode()
    prod_node2 = ProductNode()
    prod_node3 = ProductNode()

    # linking them to sum nodes
    prod_node1.add_child(ind1)
    prod_node1.add_child(ind2)
    prod_node2.add_child(ind2)
    prod_node2.add_child(ind3)
    prod_node3.add_child(ind3)
    prod_node3.add_child(ind4)

    # creating a product layer
    prod_layer = ProductLayer([prod_node1,
                               prod_node2,
                               prod_node3])

    return prod_layer
Example #3
0
def test_spn_set_get_weights():
    # create a simple spn
    root_node = SumNode()
    root_layer = SumLayer([root_node])

    prod_node_1 = ProductNode()
    prod_node_2 = ProductNode()
    root_node.add_child(prod_node_1, 0.5)
    root_node.add_child(prod_node_2, 0.5)
    prod_layer = ProductLayer([prod_node_1, prod_node_2])

    sum_node_1 = SumNode()
    sum_node_2 = SumNode()
    sum_node_3 = SumNode()
    prod_node_1.add_child(sum_node_1)
    prod_node_1.add_child(sum_node_2)
    prod_node_2.add_child(sum_node_2)
    prod_node_2.add_child(sum_node_3)
    sum_layer = SumLayer([sum_node_1, sum_node_2, sum_node_3])

    ind_node_1 = CategoricalIndicatorNode(var=0, var_val=1)
    ind_node_2 = CategoricalIndicatorNode(var=0, var_val=1)
    ind_node_3 = CategoricalIndicatorNode(var=0, var_val=1)
    ind_node_4 = CategoricalIndicatorNode(var=0, var_val=1)
    ind_node_5 = CategoricalIndicatorNode(var=0, var_val=1)
    input_layer = CategoricalInputLayer(
        nodes=[ind_node_1, ind_node_2, ind_node_3, ind_node_4, ind_node_5])
    sum_node_1.add_child(ind_node_1, 0.2)
    sum_node_1.add_child(ind_node_2, 0.2)
    sum_node_2.add_child(ind_node_2, 0.2)
    sum_node_2.add_child(ind_node_3, 0.2)
    sum_node_2.add_child(ind_node_4, 0.2)
    sum_node_3.add_child(ind_node_4, 0.2)
    sum_node_3.add_child(ind_node_5, 0.2)

    spn = Spn(input_layer=input_layer,
              layers=[sum_layer, prod_layer, root_layer])

    print(spn)

    # storing these weights
    curr_weights = spn.get_weights()

    # setting the new weights
    spn.set_weights(weights_ds)

    # getting them again
    new_weights = spn.get_weights()

    # comparing them
    assert new_weights == weights_ds

    # now setting back the previous one
    spn.set_weights(curr_weights)

    # getting them back again
    old_weights = spn.get_weights()

    # and checking
    assert old_weights == curr_weights
Example #4
0
def build_spn_layers(input_layer):

    # this is ugly... TODO try to beutify this process
    ind1 = input_layer._nodes[0]
    ind2 = input_layer._nodes[1]
    ind3 = input_layer._nodes[2]
    ind4 = input_layer._nodes[3]
    ind5 = input_layer._nodes[4]
    ind6 = input_layer._nodes[5]
    ind7 = input_layer._nodes[6]
    ind8 = input_layer._nodes[7]

    # creating sum nodes
    sum_node1 = SumNode()
    sum_node2 = SumNode()
    sum_node3 = SumNode()
    sum_node4 = SumNode()

    # linking them with nodes
    sum_node1.add_child(ind1, 0.5)
    sum_node1.add_child(ind2, 0.5)
    sum_node2.add_child(ind3, 0.1)
    sum_node2.add_child(ind4, 0.9)
    sum_node3.add_child(ind5, 0.3)
    sum_node3.add_child(ind6, 0.7)
    sum_node4.add_child(ind7, 0.6)
    sum_node4.add_child(ind8, 0.4)

    # creating sumlayer
    sum_layer = SumLayer([sum_node1,
                          sum_node2,
                          sum_node3,
                          sum_node4])

    # creating product nodes
    prod_node1 = ProductNode()
    prod_node2 = ProductNode()
    prod_node3 = ProductNode()

    # linking them to sum nodes
    prod_node1.add_child(sum_node1)
    prod_node1.add_child(sum_node2)
    prod_node2.add_child(sum_node2)
    prod_node2.add_child(sum_node3)
    prod_node3.add_child(sum_node3)
    prod_node3.add_child(sum_node4)

    # creating a product layer
    prod_layer = ProductLayer([prod_node1,
                               prod_node2,
                               prod_node3])

    return sum_layer, prod_layer
Example #5
0
def test_product_layer_is_decomposable():
    # creating scopes and nodes
    scope1 = frozenset({0, 2, 3})
    scope2 = frozenset({10, 9})
    prod_node_1 = ProductNode(var_scope=scope1)
    prod_node_2 = ProductNode(var_scope=scope2)

    # creating children manually (argh=)
    for var in scope1:
        prod_node_1.add_child(SumNode(var_scope=frozenset({var})))
    for var in scope2:
        prod_node_2.add_child(CategoricalSmoothedNode(var=var,
                                                      var_values=2))

    # creating layer
    prod_layer = ProductLayer(nodes=[prod_node_1, prod_node_2])

    assert prod_layer.is_decomposable()

    # making it not decomposable anymore
    scope3 = frozenset({2})
    prod_node_1.add_child(SumNode(var_scope=scope3))

    assert not prod_layer.is_decomposable()
Example #6
0
def test_prod_layer_backprop():
    # input layer made of 5 generic nodes
    node1 = Node()
    node2 = Node()
    node3 = Node()
    node4 = Node()
    node5 = Node()

    input_layer = CategoricalInputLayer([node1, node2,
                                         node3, node4,
                                         node5])

    # top layer made by 3 prod nodes
    prod1 = ProductNode()
    prod2 = ProductNode()
    prod3 = ProductNode()

    # linking to input nodes
    prod1.add_child(node1)
    prod1.add_child(node2)
    prod1.add_child(node3)

    prod2.add_child(node2)
    prod2.add_child(node3)
    prod2.add_child(node4)

    prod3.add_child(node3)
    prod3.add_child(node4)
    prod3.add_child(node5)

    prod_layer = ProductLayer([prod1, prod2, prod3])

    # setting input values
    val1 = 0.0
    node1.set_val(val1)
    val2 = 0.5
    node2.set_val(val2)
    val3 = 0.3
    node3.set_val(val3)
    val4 = 1.0
    node4.set_val(val4)
    val5 = 0.0
    node5.set_val(val5)

    print('input', [node.log_val for node in input_layer.nodes()])
    # evaluating
    prod_layer.eval()
    print('eval\'d layer:', prod_layer.node_values())

    # set the parent derivatives
    prod_der1 = 1.0
    prod1.log_der = log(prod_der1)

    prod_der2 = 1.0
    prod2.log_der = log(prod_der2)

    prod_der3 = 0.0
    prod3.log_der = LOG_ZERO

    # back prop layer wise
    prod_layer.backprop()

    # check for correctness
    try:
        log_der1 = log(prod_der1 * val2 * val3)
    except:
        log_der1 = LOG_ZERO

    try:
        log_der2 = log(prod_der1 * val1 * val3 +
                       prod_der2 * val3 * val4)
    except:
        log_der2 = LOG_ZERO

    try:
        log_der3 = log(prod_der2 * val2 * val4 +
                       prod_der3 * val4 * val5 +
                       prod_der1 * val1 * val2)
    except:
        log_der3 = LOG_ZERO

    try:
        log_der4 = log(prod_der2 * val2 * val3 +
                       prod_der3 * val3 * val5)
    except:
        log_der4 = LOG_ZERO

    try:
        log_der5 = log(prod_der3 * val3 * val4)
    except:
        log_der5 = LOG_ZERO

    # printing, just in case
    print('child log der', node1.log_der, node2.log_der,
          node3.log_der, node4.log_der, node5.log_der)
    print('exact log der', log_der1, log_der2, log_der3,
          log_der4, log_der5)

    if IS_LOG_ZERO(log_der1):
        assert IS_LOG_ZERO(node1.log_der)
    else:
        assert_almost_equal(log_der1, node1.log_der, 15)
    if IS_LOG_ZERO(log_der2):
        assert IS_LOG_ZERO(node2.log_der)
    else:
        assert_almost_equal(log_der2, node2.log_der, 15)
    if IS_LOG_ZERO(log_der3):
        assert IS_LOG_ZERO(node3.log_der)
    else:
        assert_almost_equal(log_der3, node3.log_der, 15)
    if IS_LOG_ZERO(log_der4):
        assert IS_LOG_ZERO(node4.log_der)
    else:
        assert_almost_equal(log_der4, node4.log_der, 15)
    if IS_LOG_ZERO(log_der5):
        assert IS_LOG_ZERO(node5.log_der)
    else:
        assert_almost_equal(log_der5, node5.log_der, 15)

    # resetting derivatives
    node1.log_der = LOG_ZERO
    node2.log_der = LOG_ZERO
    node3.log_der = LOG_ZERO
    node4.log_der = LOG_ZERO
    node5.log_der = LOG_ZERO

    # setting new values as inputs
    val1 = 0.0
    node1.set_val(val1)
    val2 = 0.0
    node2.set_val(val2)
    val3 = 0.3
    node3.set_val(val3)
    val4 = 1.0
    node4.set_val(val4)
    val5 = 1.0
    node5.set_val(val5)

    # evaluating again
    prod_layer.eval()
    print('eval\'d layer:', prod_layer.node_values())

    # set the parent derivatives
    prod_der1 = 1.0
    prod1.log_der = log(prod_der1)

    prod_der2 = 1.0
    prod2.log_der = log(prod_der2)

    prod_der3 = 0.0
    prod3.log_der = LOG_ZERO

    # back prop layer wise
    prod_layer.backprop()

    # check for correctness
    try:
        log_der1 = log(prod_der1 * val2 * val3)
    except:
        log_der1 = LOG_ZERO

    try:
        log_der2 = log(prod_der1 * val1 * val3 +
                       prod_der2 * val3 * val4)
    except:
        log_der2 = LOG_ZERO

    try:
        log_der3 = log(prod_der2 * val2 * val4 +
                       prod_der3 * val4 * val5 +
                       prod_der1 * val1 * val2)
    except:
        log_der3 = LOG_ZERO

    try:
        log_der4 = log(prod_der2 * val2 * val3 +
                       prod_der3 * val3 * val5)
    except:
        log_der4 = LOG_ZERO

    try:
        log_der5 = log(prod_der3 * val3 * val4)
    except:
        log_der5 = LOG_ZERO

    # printing, just in case
    print('child log der', node1.log_der, node2.log_der,
          node3.log_der, node4.log_der, node5.log_der)
    print('exact log der', log_der1, log_der2, log_der3,
          log_der4, log_der5)

    if IS_LOG_ZERO(log_der1):
        assert IS_LOG_ZERO(node1.log_der)
    else:
        assert_almost_equal(log_der1, node1.log_der, 15)
    if IS_LOG_ZERO(log_der2):
        assert IS_LOG_ZERO(node2.log_der)
    else:
        assert_almost_equal(log_der2, node2.log_der, 15)
    if IS_LOG_ZERO(log_der3):
        assert IS_LOG_ZERO(node3.log_der)
    else:
        assert_almost_equal(log_der3, node3.log_der, 15)
    if IS_LOG_ZERO(log_der4):
        assert IS_LOG_ZERO(node4.log_der)
    else:
        assert_almost_equal(log_der4, node4.log_der, 15)
    if IS_LOG_ZERO(log_der5):
        assert IS_LOG_ZERO(node5.log_der)
    else:
        assert_almost_equal(log_der5, node5.log_der, 15)
Example #7
0
def create_valid_toy_spn():
    # root layer
    whole_scope = frozenset({0, 1, 2, 3})
    root_node = SumNode(var_scope=whole_scope)
    root_layer = SumLayer([root_node])

    # prod layer
    prod_node_1 = ProductNode(var_scope=whole_scope)
    prod_node_2 = ProductNode(var_scope=whole_scope)
    prod_layer_1 = ProductLayer([prod_node_1, prod_node_2])

    root_node.add_child(prod_node_1, 0.5)
    root_node.add_child(prod_node_2, 0.5)

    # sum layer
    scope_1 = frozenset({0, 1})
    scope_2 = frozenset({2})
    scope_3 = frozenset({3})
    scope_4 = frozenset({2, 3})

    sum_node_1 = SumNode(var_scope=scope_1)
    sum_node_2 = SumNode(var_scope=scope_2)
    sum_node_3 = SumNode(var_scope=scope_3)
    sum_node_4 = SumNode(var_scope=scope_4)

    prod_node_1.add_child(sum_node_1)
    prod_node_1.add_child(sum_node_2)
    prod_node_1.add_child(sum_node_3)

    prod_node_2.add_child(sum_node_1)
    prod_node_2.add_child(sum_node_4)

    sum_layer_1 = SumLayer([sum_node_1, sum_node_2,
                            sum_node_3, sum_node_4])

    # another product layer
    prod_node_3 = ProductNode(var_scope=scope_1)
    prod_node_4 = ProductNode(var_scope=scope_1)

    prod_node_5 = ProductNode(var_scope=scope_4)
    prod_node_6 = ProductNode(var_scope=scope_4)

    sum_node_1.add_child(prod_node_3, 0.5)
    sum_node_1.add_child(prod_node_4, 0.5)

    sum_node_4.add_child(prod_node_5, 0.5)
    sum_node_4.add_child(prod_node_6, 0.5)

    prod_layer_2 = ProductLayer([prod_node_3, prod_node_4,
                                 prod_node_5, prod_node_6])

    # last sum one
    scope_5 = frozenset({0})
    scope_6 = frozenset({1})

    sum_node_5 = SumNode(var_scope=scope_5)
    sum_node_6 = SumNode(var_scope=scope_6)
    sum_node_7 = SumNode(var_scope=scope_5)
    sum_node_8 = SumNode(var_scope=scope_6)

    sum_node_9 = SumNode(var_scope=scope_2)
    sum_node_10 = SumNode(var_scope=scope_3)
    sum_node_11 = SumNode(var_scope=scope_2)
    sum_node_12 = SumNode(var_scope=scope_3)

    prod_node_3.add_child(sum_node_5)
    prod_node_3.add_child(sum_node_6)
    prod_node_4.add_child(sum_node_7)
    prod_node_4.add_child(sum_node_8)

    prod_node_5.add_child(sum_node_9)
    prod_node_5.add_child(sum_node_10)
    prod_node_6.add_child(sum_node_11)
    prod_node_6.add_child(sum_node_12)

    sum_layer_2 = SumLayer([sum_node_5, sum_node_6,
                            sum_node_7, sum_node_8,
                            sum_node_9, sum_node_10,
                            sum_node_11, sum_node_12])

    # input layer
    vars = [2, 3, 2, 2]
    input_layer = CategoricalIndicatorLayer(vars=vars)
    last_sum_nodes = [sum_node_2, sum_node_3,
                      sum_node_5, sum_node_6,
                      sum_node_7, sum_node_8,
                      sum_node_9, sum_node_10,
                      sum_node_11, sum_node_12]
    for sum_node in last_sum_nodes:
        (var_scope,) = sum_node.var_scope
        for input_node in input_layer.nodes():
            if input_node.var == var_scope:
                sum_node.add_child(input_node, 1.0)

    spn = Spn(input_layer=input_layer,
              layers=[sum_layer_2, prod_layer_2,
                      sum_layer_1, prod_layer_1,
                      root_layer])

    # print(spn)
    return spn
Example #8
0
def test_spn_mpe_eval_and_traversal():
    # create initial layer
    node1 = Node()
    node2 = Node()
    node3 = Node()
    node4 = Node()
    node5 = Node()

    input_layer = CategoricalInputLayer([node1, node2,
                                         node3, node4,
                                         node5])

    # top layer made by 3 sum nodes
    sum1 = SumNode()
    sum2 = SumNode()
    sum3 = SumNode()

    # linking to input nodes
    weight11 = 0.3
    sum1.add_child(node1, weight11)
    weight12 = 0.3
    sum1.add_child(node2, weight12)
    weight13 = 0.4
    sum1.add_child(node3, weight13)

    weight22 = 0.15
    sum2.add_child(node2, weight22)
    weight23 = 0.15
    sum2.add_child(node3, weight23)
    weight24 = 0.7
    sum2.add_child(node4, weight24)

    weight33 = 0.4
    sum3.add_child(node3, weight33)
    weight34 = 0.25
    sum3.add_child(node4, weight34)
    weight35 = 0.35
    sum3.add_child(node5, weight35)

    sum_layer = SumLayer([sum1, sum2, sum3])

    # another layer with two product nodes
    prod1 = ProductNode()
    prod2 = ProductNode()

    prod1.add_child(sum1)
    prod1.add_child(sum2)
    prod2.add_child(sum2)
    prod2.add_child(sum3)

    prod_layer = ProductLayer([prod1, prod2])

    # root layer, double sum
    root1 = SumNode()
    root2 = SumNode()

    weightr11 = 0.5
    root1.add_child(prod1, weightr11)
    weightr12 = 0.5
    root1.add_child(prod2, weightr12)

    weightr21 = 0.9
    root2.add_child(prod1, weightr21)
    weightr22 = 0.1
    root2.add_child(prod2, weightr22)

    root_layer = SumLayer([root1, root2])

    # create the spn
    spn = Spn(input_layer=input_layer,
              layers=[sum_layer, prod_layer, root_layer])

    print('===================')
    print(spn)
    print('===================')

    # setting the input values
    val1 = 0.0
    node1.set_val(val1)
    val2 = 0.5
    node2.set_val(val2)
    val3 = 0.3
    node3.set_val(val3)
    val4 = 1.0
    node4.set_val(val4)
    val5 = 0.0
    node5.set_val(val5)

    # evaluating the spn with MPE inference
    res = spn.test_mpe_eval()
    print('spn eval\'d', res)

    # testing it
    #
    # testing the max layer
    max1 = max(val1 * weight11,
               val2 * weight12,
               val3 * weight13)
    max2 = max(val2 * weight22,
               val3 * weight23,
               val4 * weight24)
    max3 = max(val3 * weight33,
               val4 * weight34,
               val5 * weight35)
    log_max1 = log(max1) if not numpy.isclose(max1, 0) else LOG_ZERO
    log_max2 = log(max2) if not numpy.isclose(max2, 0) else LOG_ZERO
    log_max3 = log(max3) if not numpy.isclose(max3, 0) else LOG_ZERO

    print('expected max vals {0}, {1}, {2}'.format(log_max1,
                                                   log_max2,
                                                   log_max3))
    print('found    max vals {0}, {1}, {2}'.format(sum1.log_val,
                                                   sum2.log_val,
                                                   sum3.log_val))
    if IS_LOG_ZERO(log_max1):
        assert IS_LOG_ZERO(sum1.log_val)
    else:
        assert_almost_equal(log_max1, sum1.log_val)
    if IS_LOG_ZERO(log_max2):
        assert IS_LOG_ZERO(sum2.log_val)
    else:
        assert_almost_equal(log_max2, sum2.log_val)
    if IS_LOG_ZERO(log_max3):
        assert IS_LOG_ZERO(sum3.log_val)
    else:
        assert_almost_equal(log_max3, sum3.log_val)

    # product layer is assumed to be fine, but let's check
    # it anyways
    prod_val1 = max1 * max2
    prod_val2 = max2 * max3
    prod_log_val1 = log_max1 + log_max2
    prod_log_val2 = log_max2 + log_max3

    print('exp prod vals {0}, {1}'.format(prod_log_val1,
                                          prod_log_val2))
    print('rea prod vals {0}, {1}'.format(prod1.log_val,
                                          prod2.log_val))
    if IS_LOG_ZERO(prod_log_val1):
        assert IS_LOG_ZERO(prod1.log_val)
    else:
        assert_almost_equal(prod_log_val1, prod1.log_val)

    if IS_LOG_ZERO(prod_log_val2):
        assert IS_LOG_ZERO(prod2.log_val)
    else:
        assert_almost_equal(prod_log_val2, prod2.log_val)

    # root layer, again a sum layer
    root_val1 = max(prod_val1 * weightr11,
                    prod_val2 * weightr12)
    root_val2 = max(prod_val1 * weightr21,
                    prod_val2 * weightr22)
    root_log_val1 = log(root_val1) if not numpy.isclose(
        root_val1, 0) else LOG_ZERO
    root_log_val2 = log(root_val2) if not numpy.isclose(
        root_val2, 0) else LOG_ZERO

    print('exp root vals {0}, {1}'.format(root_log_val1,
                                          root_log_val2))
    print('found ro vals {0}, {1}'.format(root1.log_val,
                                          root2.log_val))

    if IS_LOG_ZERO(root_log_val1):
        assert IS_LOG_ZERO(root1.log_val)
    else:
        assert_almost_equal(root_log_val1, root1.log_val)
    if IS_LOG_ZERO(root_log_val2):
        assert IS_LOG_ZERO(root2.log_val)
    else:
        assert_almost_equal(root_log_val2, root2.log_val)

    # now we are traversing top down the net
    print('mpe traversing')
    for i, j, k in spn.mpe_traversal():
        print(i, j, k)
Example #9
0
def test_spn_backprop():
    # create initial layer
    node1 = Node()
    node2 = Node()
    node3 = Node()
    node4 = Node()
    node5 = Node()

    input_layer = CategoricalInputLayer([node1, node2,
                                         node3, node4,
                                         node5])

    # top layer made by 3 sum nodes
    sum1 = SumNode()
    sum2 = SumNode()
    sum3 = SumNode()

    # linking to input nodes
    weight11 = 0.3
    sum1.add_child(node1, weight11)
    weight12 = 0.3
    sum1.add_child(node2, weight12)
    weight13 = 0.4
    sum1.add_child(node3, weight13)

    weight22 = 0.15
    sum2.add_child(node2, weight22)
    weight23 = 0.15
    sum2.add_child(node3, weight23)
    weight24 = 0.7
    sum2.add_child(node4, weight24)

    weight33 = 0.4
    sum3.add_child(node3, weight33)
    weight34 = 0.25
    sum3.add_child(node4, weight34)
    weight35 = 0.35
    sum3.add_child(node5, weight35)

    sum_layer = SumLayer([sum1, sum2, sum3])

    # another layer with two product nodes
    prod1 = ProductNode()
    prod2 = ProductNode()

    prod1.add_child(sum1)
    prod1.add_child(sum2)
    prod2.add_child(sum2)
    prod2.add_child(sum3)

    prod_layer = ProductLayer([prod1, prod2])

    # root layer, double sum
    root1 = SumNode()
    root2 = SumNode()

    weightr11 = 0.5
    root1.add_child(prod1, weightr11)
    weightr12 = 0.5
    root1.add_child(prod2, weightr12)

    weightr21 = 0.9
    root2.add_child(prod1, weightr21)
    weightr22 = 0.1
    root2.add_child(prod2, weightr22)

    root_layer = SumLayer([root1, root2])
    # root_layer = SumLayer([root1])

    # create the spn
    spn = Spn(input_layer=input_layer,
              layers=[sum_layer, prod_layer, root_layer])

    # setting the input values
    val1 = 0.0
    node1.set_val(val1)
    val2 = 0.5
    node2.set_val(val2)
    val3 = 0.3
    node3.set_val(val3)
    val4 = 1.0
    node4.set_val(val4)
    val5 = 0.0
    node5.set_val(val5)

    # evaluating the spn
    res = spn.test_eval()
    print('spn eval\'d', res)

    # backprop
    spn.backprop()

    # computing derivatives by hand
    # topdown: root layer
    root_der = 1.0
    log_root_der = log(root_der)

    # print('root ders', root1.log_der, root2.log_der)
    print('root ders', root1.log_der)
    assert_almost_equal(log_root_der, root1.log_der)
    assert_almost_equal(log_root_der, root2.log_der)

    # product layer
    prod_der1 = (root_der * weightr11 +
                 root_der * weightr21)

    prod_der2 = (root_der * weightr12 +
                 root_der * weightr22)

    # prod_der1 = (root_der * weightr11)
    # prod_der2 = (root_der * weightr12)

    log_prod_der1 = log(prod_der1) if prod_der1 > 0.0 else LOG_ZERO
    log_prod_der2 = log(prod_der2) if prod_der2 > 0.0 else LOG_ZERO

    print('found  prod ders', prod1.log_der, prod2.log_der)
    print('expect prod ders', log_prod_der1, log_prod_der2)

    if IS_LOG_ZERO(log_prod_der1):
        assert IS_LOG_ZERO(prod1.log_der)
    else:
        assert_almost_equal(log_prod_der1, prod1.log_der)
    if IS_LOG_ZERO(log_prod_der2):
        assert IS_LOG_ZERO(prod2.log_der)
    else:
        assert_almost_equal(log_prod_der2, prod2.log_der)

    # sum layer
    sum_der1 = (
        prod_der1 * (weight22 * val2 +
                     weight23 * val3 +
                     weight24 * val4))

    log_sum_der1 = log(sum_der1) if sum_der1 > 0.0 else LOG_ZERO

    sum_der2 = (prod_der1 * (weight11 * val1 +
                             weight12 * val2 +
                             weight13 * val3) +
                prod_der2 * (weight33 * val3 +
                             weight34 * val4 +
                             weight35 * val5))

    log_sum_der2 = log(sum_der2) if sum_der2 > 0.0 else LOG_ZERO

    sum_der3 = (prod_der2 * (weight22 * val2 +
                             weight23 * val3 +
                             weight24 * val4))

    log_sum_der3 = log(sum_der3) if sum_der3 > 0.0 else LOG_ZERO

    print('expected sum ders', log_sum_der1,
          log_sum_der2,
          log_sum_der3)
    print('found    sum ders', sum1.log_der,
          sum2.log_der,
          sum3.log_der)

    if IS_LOG_ZERO(log_sum_der1):
        assert IS_LOG_ZERO(sum1.log_der)
    else:
        assert_almost_equal(log_sum_der1, sum1.log_der)
    if IS_LOG_ZERO(log_sum_der2):
        assert IS_LOG_ZERO(sum2.log_der)
    else:
        assert_almost_equal(log_sum_der2, sum2.log_der)
    if IS_LOG_ZERO(log_sum_der3):
        assert IS_LOG_ZERO(sum3.log_der)
    else:
        assert_almost_equal(log_sum_der3, sum3.log_der)

    # final level, the first one
    try:
        log_der1 = log(sum_der1 * weight11)
    except:
        log_der1 = LOG_ZERO

    try:
        log_der2 = log(sum_der1 * weight12 +
                       sum_der2 * weight22)
    except:
        log_der2 = LOG_ZERO

    try:
        log_der3 = log(sum_der1 * weight13 +
                       sum_der2 * weight23 +
                       sum_der3 * weight33)
    except:
        log_der3 = LOG_ZERO

    try:
        log_der4 = log(sum_der2 * weight24 +
                       sum_der3 * weight34)
    except:
        log_der4 = LOG_ZERO

    try:
        log_der5 = log(sum_der3 * weight35)
    except:
        log_der5 = LOG_ZERO

    # printing, just in case
    print('child log der', node1.log_der, node2.log_der,
          node3.log_der, node4.log_der, node5.log_der)
    print('exact log der', log_der1, log_der2, log_der3,
          log_der4, log_der5)

    if IS_LOG_ZERO(log_der1):
        assert IS_LOG_ZERO(node1.log_der)
    else:
        assert_almost_equal(log_der1, node1.log_der, 15)
    if IS_LOG_ZERO(log_der2):
        assert IS_LOG_ZERO(node2.log_der)
    else:
        assert_almost_equal(log_der2, node2.log_der, 15)
    if IS_LOG_ZERO(log_der3):
        assert IS_LOG_ZERO(node3.log_der)
    else:
        assert_almost_equal(log_der3, node3.log_der, 15)
    if IS_LOG_ZERO(log_der4):
        assert IS_LOG_ZERO(node4.log_der)
    else:
        assert_almost_equal(log_der4, node4.log_der, 15)
    if IS_LOG_ZERO(log_der5):
        assert IS_LOG_ZERO(node5.log_der)
    else:
        assert_almost_equal(log_der5, node5.log_der, 15)
Example #10
0
def test_spn_sampling():

    from collections import Counter

    from spn.factory import linked_categorical_input_to_indicators

    #
    # building a small mixture model
    features = [2, 2, 2, 2]
    n_features = len(features)

    #
    # different categorical vars groups as leaves
    input_nodes_1 = [
        CategoricalSmoothedNode(i, features[i], alpha=0.0, freqs=[0, 1])
        for i in range(n_features)
    ]

    input_nodes_2 = [
        CategoricalSmoothedNode(i, features[i], alpha=0.0, freqs=[1, 0])
        for i in range(n_features)
    ]

    input_nodes_3 = [CategoricalSmoothedNode(i, features[i], alpha=0.0,
                                             freqs=[1, 0]) for i in range(n_features // 2)] + \
        [CategoricalSmoothedNode(i, features[i], alpha=0.0,
                                 freqs=[0, 1]) for i in range(n_features // 2, n_features)]

    input_nodes_4 = [CategoricalSmoothedNode(i, features[i], alpha=0.0,
                                             freqs=[0, 1]) for i in range(n_features // 2)] + \
        [CategoricalSmoothedNode(i, features[i], alpha=0.0,
                                 freqs=[1, 0]) for i in range(n_features // 2, n_features)]

    input_layer = CategoricalSmoothedLayer(
        nodes=input_nodes_1 + input_nodes_2 + input_nodes_3 + input_nodes_4)
    #
    # one product node for each group
    prod_node_1 = ProductNode()
    for leaf in input_nodes_1:
        prod_node_1.add_child(leaf)

    prod_node_2 = ProductNode()
    for leaf in input_nodes_2:
        prod_node_2.add_child(leaf)

    prod_node_3 = ProductNode()
    for leaf in input_nodes_3:
        prod_node_3.add_child(leaf)

    prod_node_4 = ProductNode()
    for leaf in input_nodes_4:
        prod_node_4.add_child(leaf)

    prod_layer = ProductLayer(
        nodes=[prod_node_1, prod_node_2, prod_node_3, prod_node_4])

    #
    # one root as a mixture
    root = SumNode()
    root.add_child(prod_node_1, 0.5)
    root.add_child(prod_node_2, 0.1)
    root.add_child(prod_node_3, 0.2)
    root.add_child(prod_node_4, 0.2)

    root_layer = SumLayer(nodes=[root])

    spn = Spn(input_layer=input_layer, layers=[prod_layer, root_layer])

    print(spn)

    n_instances = 1000
    #
    # sampling some instances
    sample_start_t = perf_counter()
    samples = spn.sample(n_instances=n_instances, verbose=False)
    sample_end_t = perf_counter()
    print('Sampled in {} secs'.format(sample_end_t - sample_start_t))
    if n_instances < 20:
        print(samples)

    #
    # some statistics
    tuple_samples = [tuple(s) for s in samples]
    if n_instances < 20:
        print(tuple_samples)

    sample_counter = Counter(tuple_samples)
    print(sample_counter)

    #
    # transforming into an spn with indicator nodes
    print('Into indicator nodes')
    ind_start_t = perf_counter()
    spn = linked_categorical_input_to_indicators(spn)
    ind_end_t = perf_counter()
    print('Done in ', ind_end_t - ind_start_t)

    sample_start_t = perf_counter()
    samples = spn.sample(n_instances=n_instances,
                         verbose=False,
                         one_hot_encoding=True)
    sample_end_t = perf_counter()
    print('Sampled in {} secs'.format(sample_end_t - sample_start_t))
    if n_instances < 20:
        print(samples)

    #
    # some statistics
    tuple_samples = [tuple(s) for s in samples]
    if n_instances < 20:
        print(tuple_samples)

    sample_counter = Counter(tuple_samples)
    print(sample_counter)
Example #11
0
def test_toy_spn_numpy_linked():

    input_vec = numpy.array([[0., 0., 0.], [0., 0., 0.], [0., 1., 1.],
                             [MARG_IND, MARG_IND, MARG_IND]]).T

    ind_node_1 = CategoricalIndicatorNode(var=0, var_val=0)
    ind_node_2 = CategoricalIndicatorNode(var=0, var_val=1)
    ind_node_3 = CategoricalIndicatorNode(var=1, var_val=0)
    ind_node_4 = CategoricalIndicatorNode(var=1, var_val=1)
    ind_node_5 = CategoricalIndicatorNode(var=2, var_val=0)
    ind_node_6 = CategoricalIndicatorNode(var=2, var_val=1)

    input_layer = CategoricalInputLayer(nodes=[
        ind_node_1, ind_node_2, ind_node_3, ind_node_4, ind_node_5, ind_node_6
    ])

    n_nodes_layer_1 = 6
    layer_1_sum_nodes = [SumNode() for i in range(n_nodes_layer_1)]
    layer_1_sum_nodes[0].add_child(ind_node_1, 0.6)
    layer_1_sum_nodes[0].add_child(ind_node_2, 0.4)
    layer_1_sum_nodes[1].add_child(ind_node_1, 0.3)
    layer_1_sum_nodes[1].add_child(ind_node_2, 0.7)
    layer_1_sum_nodes[2].add_child(ind_node_3, 0.1)
    layer_1_sum_nodes[2].add_child(ind_node_4, 0.9)
    layer_1_sum_nodes[3].add_child(ind_node_3, 0.7)
    layer_1_sum_nodes[3].add_child(ind_node_4, 0.3)
    layer_1_sum_nodes[4].add_child(ind_node_5, 0.5)
    layer_1_sum_nodes[4].add_child(ind_node_6, 0.5)
    layer_1_sum_nodes[5].add_child(ind_node_5, 0.2)
    layer_1_sum_nodes[5].add_child(ind_node_6, 0.8)

    layer_1 = SumLayer(layer_1_sum_nodes)

    n_nodes_layer_2 = 4
    layer_2_prod_nodes = [ProductNode() for i in range(n_nodes_layer_2)]
    layer_2_prod_nodes[0].add_child(layer_1_sum_nodes[0])
    layer_2_prod_nodes[0].add_child(layer_1_sum_nodes[2])
    layer_2_prod_nodes[0].add_child(layer_1_sum_nodes[4])
    layer_2_prod_nodes[1].add_child(layer_1_sum_nodes[1])
    layer_2_prod_nodes[1].add_child(layer_1_sum_nodes[3])
    layer_2_prod_nodes[1].add_child(layer_1_sum_nodes[5])
    layer_2_prod_nodes[2].add_child(layer_1_sum_nodes[0])
    layer_2_prod_nodes[2].add_child(layer_1_sum_nodes[2])
    layer_2_prod_nodes[2].add_child(layer_1_sum_nodes[5])
    layer_2_prod_nodes[3].add_child(layer_1_sum_nodes[1])
    layer_2_prod_nodes[3].add_child(layer_1_sum_nodes[3])
    layer_2_prod_nodes[3].add_child(layer_1_sum_nodes[4])

    layer_2 = ProductLayer(layer_2_prod_nodes)

    root = SumNode()
    root.add_child(layer_2_prod_nodes[0], 0.2)
    root.add_child(layer_2_prod_nodes[1], 0.4)
    root.add_child(layer_2_prod_nodes[2], 0.15)
    root.add_child(layer_2_prod_nodes[3], 0.25)

    layer_3 = SumLayer([root])

    spn = Spn(input_layer=input_layer, layers=[layer_1, layer_2, layer_3])

    res = spn.eval(input_vec)
    print('First evaluation')
    print(res)
Example #12
0
def test_mini_spn_fit_em():
    vars = numpy.array([2, 2, 2, 2])
    input_layer = CategoricalIndicatorLayer(vars=vars)

    print(input_layer)
    ind1 = input_layer._nodes[0]
    ind2 = input_layer._nodes[1]
    ind3 = input_layer._nodes[2]
    ind4 = input_layer._nodes[3]
    ind5 = input_layer._nodes[4]
    ind6 = input_layer._nodes[5]
    ind7 = input_layer._nodes[6]
    ind8 = input_layer._nodes[7]

    # creating a sum layer of 4 nodes
    sum1 = SumNode()
    sum2 = SumNode()
    sum3 = SumNode()
    sum4 = SumNode()

    sum1.add_child(ind1, 0.6)
    sum1.add_child(ind2, 0.4)
    sum2.add_child(ind3, 0.5)
    sum2.add_child(ind4, 0.5)
    sum3.add_child(ind5, 0.7)
    sum3.add_child(ind6, 0.3)
    sum4.add_child(ind7, 0.4)
    sum4.add_child(ind8, 0.6)

    sum_layer = SumLayer(nodes=[sum1, sum2, sum3, sum4])

    # and a top layer of 3 products
    prod1 = ProductNode()
    prod2 = ProductNode()
    prod3 = ProductNode()

    prod1.add_child(sum1)
    prod1.add_child(sum2)
    prod2.add_child(sum2)
    prod2.add_child(sum3)
    prod3.add_child(sum3)
    prod3.add_child(sum4)

    prod_layer = ProductLayer(nodes=[prod1, prod2, prod3])

    # root layer
    root = SumNode()

    root.add_child(prod1, 0.4)
    root.add_child(prod2, 0.25)
    root.add_child(prod3, 0.35)

    root_layer = SumLayer(nodes=[root])

    spn = Spn(input_layer=input_layer,
              layers=[sum_layer, prod_layer, root_layer])

    print(spn)

    # training on obs
    spn.fit_em(train=syn_train_data, valid=syn_val_data, test=None, hard=True)
Example #13
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 #14
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)
Example #15
0
def test_build_linked_spn_from_scope_graph():

    #
    # creating a region graph as an input scope graph
    n_cols = 2
    n_rows = 2
    coarse = 2
    #
    # create initial region
    root_region = Region.create_whole_region(n_rows, n_cols)

    region_graph = create_poon_region_graph(root_region, coarse=coarse)

    # print(region_graph)
    print('# partitions', region_graph.n_partitions())
    print('# regions', region_graph.n_scopes())

    print(region_graph)

    #
    #
    k = 2
    spn = build_linked_spn_from_scope_graph(region_graph, k)

    print(spn)

    print(spn.stats())

    #
    # back to the scope graph
    root_layer = list(spn.root_layer().nodes())
    assert len(root_layer) == 1
    root = root_layer[0]

    scope_graph = get_scope_graph_from_linked_spn(root)
    print(scope_graph)

    assert scope_graph == region_graph

    #
    # building an spn from scratch
    #
    # building leaf nodes
    n_vars = 4
    vars = [0, 1, 2, 3]
    leaves = [
        CategoricalIndicatorNode(var, val) for var in range(n_vars)
        for val in [0, 1]
    ]
    input_layer = CategoricalIndicatorLayer(nodes=leaves, vars=vars)

    #
    # building root
    root_node = SumNode(var_scope=frozenset(vars))
    root_layer = SumLayer([root_node])

    #
    # building product nodes
    prod_list_1 = [ProductNode(var_scope=vars) for i in range(4)]
    prod_list_2 = [ProductNode(var_scope=vars) for i in range(4)]
    prod_nodes_1 = prod_list_1 + prod_list_2
    product_layer_1 = ProductLayer(prod_nodes_1)

    for p in prod_nodes_1:
        root_node.add_child(p, 1.0 / len(prod_nodes_1))

    #
    # build sum nodes
    sum_list_1 = [SumNode() for i in range(2)]
    sum_list_2 = [SumNode() for i in range(2)]
    sum_list_3 = [SumNode() for i in range(2)]
    sum_list_4 = [SumNode() for i in range(2)]

    sum_layer_2 = SumLayer(sum_list_1 + sum_list_2 + sum_list_3 + sum_list_4)

    sum_pairs = []
    for s_1 in sum_list_1:
        for s_2 in sum_list_2:
            sum_pairs.append((s_1, s_2))

    for p, (s_1, s_2) in zip(prod_list_1, sum_pairs):
        p.add_child(s_1)
        p.add_child(s_2)

    sum_pairs = []
    for s_3 in sum_list_3:
        for s_4 in sum_list_4:
            sum_pairs.append((s_3, s_4))

    for p, (s_3, s_4) in zip(prod_list_2, sum_pairs):
        p.add_child(s_3)
        p.add_child(s_4)

    #
    # again product nodes
    prod_list_3 = [ProductNode() for i in range(4)]
    prod_list_4 = [ProductNode() for i in range(4)]
    prod_list_5 = [ProductNode() for i in range(4)]
    prod_list_6 = [ProductNode() for i in range(4)]

    product_layer_3 = ProductLayer(prod_list_3 + prod_list_4 + prod_list_5 +
                                   prod_list_6)

    for s in sum_list_1:
        for p in prod_list_3:
            s.add_child(p, 1.0 / len(prod_list_3))

    for s in sum_list_2:
        for p in prod_list_4:
            s.add_child(p, 1.0 / len(prod_list_4))

    for s in sum_list_3:
        for p in prod_list_5:
            s.add_child(p, 1.0 / len(prod_list_5))

    for s in sum_list_4:
        for p in prod_list_6:
            s.add_child(p, 1.0 / len(prod_list_6))

    #
    # build sum nodes
    sum_list_5 = [SumNode() for i in range(2)]
    sum_list_6 = [SumNode() for i in range(2)]
    sum_list_7 = [SumNode() for i in range(2)]
    sum_list_8 = [SumNode() for i in range(2)]

    sum_layer_4 = SumLayer(sum_list_5 + sum_list_6 + sum_list_7 + sum_list_8)

    sum_pairs = []
    for s_5 in sum_list_5:
        for s_7 in sum_list_7:
            sum_pairs.append((s_5, s_7))

    for p, (s_5, s_7) in zip(prod_list_3, sum_pairs):
        p.add_child(s_5)
        p.add_child(s_7)

    sum_pairs = []
    for s_6 in sum_list_6:
        for s_8 in sum_list_8:
            sum_pairs.append((s_6, s_8))

    for p, (s_6, s_8) in zip(prod_list_4, sum_pairs):
        p.add_child(s_6)
        p.add_child(s_8)

    sum_pairs = []
    for s_5 in sum_list_5:
        for s_6 in sum_list_6:
            sum_pairs.append((s_5, s_6))

    for p, (s_5, s_6) in zip(prod_list_5, sum_pairs):
        p.add_child(s_5)
        p.add_child(s_6)

    sum_pairs = []
    for s_7 in sum_list_7:
        for s_8 in sum_list_8:
            sum_pairs.append((s_7, s_8))

    for p, (s_7, s_8) in zip(prod_list_6, sum_pairs):
        p.add_child(s_7)
        p.add_child(s_8)

    #
    # linking to input layer
    for s in sum_list_5:
        for i in leaves[0:2]:
            s.add_child(i, 0.5)

    for s in sum_list_6:
        for i in leaves[2:4]:
            s.add_child(i, 0.5)

    for s in sum_list_7:
        for i in leaves[4:6]:
            s.add_child(i, 0.5)

    for s in sum_list_8:
        for i in leaves[6:]:
            s.add_child(i, 0.5)

    lspn = LinkedSpn(input_layer=input_layer,
                     layers=[
                         sum_layer_4, product_layer_3, sum_layer_2,
                         product_layer_1, root_layer
                     ])
    print(lspn)
    print(lspn.stats())

    #
    # trying to evaluate them
    input_vec = numpy.array([[1., 1., 1., 0.], [0., 0., 0., 0.],
                             [0., 1., 1., 0.],
                             [MARG_IND, MARG_IND, MARG_IND, MARG_IND]]).T

    res = spn.eval(input_vec)
    print('First evaluation')
    print(res)

    res = lspn.eval(input_vec)
    print('Second evaluation')
    print(res)