Example #1
0
def filter_edges_inputs_outputs_by_nodes_negative(
        nodes: List[Node], containing_graph: Graph) -> Graph:
    """

    Since the node list is split in two, many edges and wires don't need to be considered for the next iteration of
    each half. Thus, the edges, inputs and outputs are filtered so they are not passed to the next iteration if they are
    in relation with the given nodes from the other half

    Args:
        nodes (List[Node]): nodes to apply the filter from
        containing_graph (Graph): graph to be filtered

    Returns:
        Graph: graph containing the edges, inputs and outputs without the members in relation with the given nodes
    """
    new_edges = containing_graph.edges[:]
    new_inputs = containing_graph.inputs[:]
    new_outputs = containing_graph.outputs[:]
    for edge in containing_graph.edges:
        if set(edge).intersection(
                nodes):  # edge doesn't contain any node from the list
            new_edges.remove(edge)
            if set(edge).intersection(containing_graph.inputs):
                new_inputs.remove(
                    list(set(edge).intersection(containing_graph.inputs))[0])
            if Wire(edge.name) in containing_graph.inputs:
                new_inputs.remove(Wire(edge.name))
            if set(edge).intersection(containing_graph.outputs):
                new_outputs.remove(
                    list(set(edge).intersection(containing_graph.outputs))[0])
            if Wire(edge.name) in containing_graph.outputs:
                new_outputs.remove(Wire(edge.name))
    return Graph(edges=new_edges, inputs=new_inputs, outputs=new_outputs)
Example #2
0
def test_between_graphs_edges():
    b0 = Wire("b0")
    b1 = Wire("b1")
    b2 = Wire("b2")
    b3 = Wire("b3")
    v0 = Node("v0", 0.0, "X", 3)
    v1 = Node("v1", 0.0, "X", 3)
    v2 = Node("v2", 0.0, arity=3)
    v3 = Node("v3", 0.0, arity=3)
    e0 = Edge("e0", v0, v3)
    e1 = Edge("e1", v1, v2)
    e2 = Edge("e2", v2, v0)
    e3 = Edge("e3", v1, v3)
    e4 = Edge("e4", v0, b1)
    e5 = Edge("e5", v2, b0)
    e7 = Edge("e7", v1, b2)
    e8 = Edge("e8", v3, b3)
    inputs = [b1, b2]
    outputs = [b0, b3]
    nodes = [v3, v1, v0, v2]
    edges = [e3, e5, e0, e4, e2, e8, e1, e7]
    graph = Graph(nodes, edges, inputs, outputs)
    g1 = Graph([v0], [e4], [b1])
    g2 = Graph([v3, v1, v2], [e2, e0, e1, e3, e7, e8, e5], [b2], [b0, b3])
    result = qf.between_graphs_edges(g1, g2, graph)
    expected_result = [e2, e0]
    assert set(result) - set(expected_result) == set()
Example #3
0
def test_split_and_reunite():
    b0 = Wire("b0")
    b1 = Wire("b1")
    b2 = Wire("b2")
    b3 = Wire("b3")
    v0 = Node("v0", 0.0, "X", 3)
    v1 = Node("v1", 0.0, "X", 3)
    v2 = Node("v2", 0.0, arity=3)
    v3 = Node("v3", 0.0, arity=3)
    e0 = Edge("e0", v0, v3)
    e1 = Edge("e1", v1, v2)
    e2 = Edge("e2", v2, v0)
    e3 = Edge("e3", v1, v3)
    e4 = Edge("e4", v0, b1)
    e5 = Edge("e5", v2, b0)
    e7 = Edge("e7", v1, b2)
    e8 = Edge("e8", v3, b3)
    inputs = [b1, b2]
    outputs = [b0, b3]
    nodes = [v3, v1, v0, v2]
    edges = [e3, e5, e0, e4, e2, e8, e1, e7]
    graph = Graph(nodes, edges, inputs, outputs)
    m1 = [[32, 0, 0, 0], [0, 0, 0, 32], [0, 0, 0, 32], [32, 0, 0, 0]]
    m2 = np.zeros((4, 4))
    m3 = np.zeros((4, 4))
    m4 = np.zeros((4, 4))
    expected_result = Pi4Matrix(m1, m2, m3, m4, 6)
    assert not (qf.split_and_reunite(graph) - expected_result).any()
Example #4
0
def test_connected_graphs_split():

    #  a  b  c
    #  |  |  |
    # n0 n1 n2    g
    #  |\/|\/|    |
    #  |/\|/\|    h
    # n3 n4 n5
    #  |  |  |
    #  d  e  f
    #
    # problem if an edge has the same name as a node

    a, b, c, d, e, f = Wire('a'), Wire('b'), Wire('c'), Wire('d'), Wire(
        'e'), Wire('f')
    g, h = Wire('g'), Wire('h')
    n0, n1, n2, n3, n4, n5 = Node('n0'), Node('n1'), Node('n2'), Node(
        'n3'), Node('n4'), Node('n5')
    e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 = \
        Edge('e0', a, n0), Edge('e1', b, n1), Edge('e2', c, n2), \
        Edge('e3', n0, n3), Edge('e4', n0, n4), \
        Edge('e5', n1, n3), Edge('e6', n1, n4), Edge('e7', n1, n5), \
        Edge('e8', n2, n4), Edge('e9', n2, n5), \
        Edge('e10', d, n3), Edge('e11', e, n4), Edge('e12', f, n5), \
        Edge('e13', n0, n0)
    e14 = Edge('e14', g, h)
    inputs = [a, b, c, g]
    outputs = [d, e, f, h]
    nodes = [n0, n1, n2, n3, n4, n5]
    edges = [e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14]
    graph = Graph(nodes, edges, inputs, outputs)
    small_graph = Graph(inputs=[g], outputs=[h], edges=[e14])
    g1, g2 = qf.connected_graphs_split(graph)
    assert g1 == small_graph or g2 == small_graph
Example #5
0
    def _wires_to_connection_point_node_shortcut(_wires, _edges, _nodes_group,
                                                 _is_output, _is_matrix_2,
                                                 _cp_dict):
        index = 0
        for node in _nodes_group:
            # we are iterating over the nodes to ensure an order, indeed since the two halves are done depending on the
            # node order in this V2.0 of the algorithm, we can simply ensure the order by giving the I/O indexes
            # depending on node position
            for edge in edges:
                if node in edge:
                    wire_index = None
                    if set(edge).intersection(wires):
                        # in this case, the edge links a node to an original I/O
                        wire_index = wires.index(
                            list(set(edge).intersection(wires))[0])
                    if edge.name in [wire.name for wire in wires]:
                        # when a diagram is split, the edges between two diagrams are considered as connected to a new
                        # virtual output, with the name of the edge as wire name
                        wire_index = wires.index(Wire(edge.name))

                    if wire_index is not None:
                        point = ConnectionPoint(is_matrix_2=_is_matrix_2,
                                                is_out=is_output,
                                                index=index)
                        _cp_dict[wire_index] = point
                        index += 1

        return _cp_dict
Example #6
0
def test_wires_to_connection_point_edge_sorted():
    b0 = Wire("b0")
    b1 = Wire("b1")
    b2 = Wire("b2")
    b3 = Wire("b3")
    e2 = Edge("e2", b3, b1)
    e1 = Edge("e1", b0, b2)
    wires = [b0, b1]
    edges1 = [e2]
    edges2 = [e1]
    result = qf.wires_to_connection_point_edge_sorted(wires, edges1, edges2,
                                                      False)
    c0 = ConnectionPoint(is_matrix_2=True, is_out=False)
    c1 = ConnectionPoint(index=1, is_matrix_2=False, is_out=False)
    expected_result = [c0, c1]
    assert result == expected_result
Example #7
0
def test_matrix_linker():
    v0 = Node("v0", arity=2, node_type="hadamard")
    v3 = Node("v3", arity=2, node_type="hadamard")
    v5 = Node("v5", angle=1.0, arity=3, node_type="X")
    we5 = Wire("e5")
    we6 = Wire("e6")
    e5 = Edge("e5", v5, v0)
    e6 = Edge("e6", v0, v3)
    graph1 = Graph([v0], [e5, e6], [we5], [we6])
    we8 = Wire("e8")
    v8 = Node("v8", 0.5, arity=4)
    e8 = Edge("e8", v8, v3)
    graph2 = Graph([v3], [e8, e6], [we8, we6], [])
    result = qf.matrix_linker(graph1, graph2)
    p1 = ConnectionPoint(False, True)
    p2 = ConnectionPoint(True, False, 1)
    expected_result = [InterMatrixLink(p1, p2)]
    assert result == expected_result
Example #8
0
def dictionary_to_data(wire_vertices_dictionary: dict, node_vertices_dictionary: dict,
                       undir_edges_dictionary: dict) -> (List[Wire], List[Node], List[Edge]):
    """dictionary_to_data(wire_vertices_dictionary: dict, node_vertices_dictionary: dict, undir_edges_dictionary: dict) -> List[Wire], List[Node], List[Edge]

    Converts the three dictionaries (wires, nodes and edges) to lists of classes defined in :ref:`data`

    Args:
        wire_vertices_dictionary (dict): dictionary containing the data about the wires
        node_vertices_dictionary (dict): dictionary containing the data about the nodes
        undir_edges_dictionary (dict): dictionary containing the data about the edges

    Returns:
        List[Wire], List[Node], List[Edge]: lists of wires, nodes and edges
    """
    wires = []  # type: List[Wire]
    for wire_name in wire_vertices_dictionary:
        wires.append(Wire(wire_name))

    nodes = []  # type: List[Node]
    for node_name in node_vertices_dictionary:
        angle = 0.
        node_type = 'Z'
        if 'data' in node_vertices_dictionary[node_name]:
            node_type = node_vertices_dictionary[node_name]['data']['type']
            angle = node_vertices_dictionary[node_name]['data']['value']
            if angle == '':
                angle = 0.
            else:
                angle = angle.replace('\\pi', '1')
                angle = angle.replace('pi', '1')
                angle = angle.replace('Pi', '1')
                angle = float(eval(angle))
        nodes.append(Node(node_name, angle, node_type))

    edges = []  # type: List[Edge]
    for edge_name in undir_edges_dictionary:
        label = ""
        if 'data' in undir_edges_dictionary[edge_name]:
            label = undir_edges_dictionary[edge_name]['data']['label']
        n1 = None
        n2 = None
        for node in nodes:
            if node.name == undir_edges_dictionary[edge_name]['src']:
                node.arity += 1
                n1 = node
            if node.name == undir_edges_dictionary[edge_name]['tgt']:
                node.arity += 1
                n2 = node
        for wire in wires:
            if wire.name == undir_edges_dictionary[edge_name]['src']:
                n1 = wire
            if wire.name == undir_edges_dictionary[edge_name]['tgt']:
                n2 = wire
        edges.append(Edge(edge_name, n1, n2, label))

    return wires, nodes, edges
Example #9
0
def test_wires_to_connection_point_node_sorted():
    we5 = Wire("e5")
    we8 = Wire("e8")
    v5 = Node("v5")
    v8 = Node("v8")
    v0 = Node("v0", node_type='hadamard', arity=2)
    v3 = Node("v3", node_type='hadamard', arity=2)
    e5 = Edge("e5", v5, v0)
    e6 = Edge("e6", v0, v3)
    e8 = Edge("e8", v8, v3)
    wires = [we5, we8]
    edges = [e8, e6, e5]
    nodes1 = [v0]
    nodes2 = [v3]
    result = qf.wires_to_connection_point_node_sorted(wires, edges, nodes1,
                                                      nodes2, False)
    c0 = ConnectionPoint(is_matrix_2=False, is_out=False)
    c1 = ConnectionPoint(is_matrix_2=True, is_out=False)
    expected_result = [c0, c1]
    assert result == expected_result
Example #10
0
def test_no_node_matrix():
    #
    #   b4  b0  b5
    #     \  |  /
    #      --|-
    #        |
    #       b2
    #
    b0 = Wire("b0")
    b2 = Wire("b2")
    b4 = Wire("b4")
    b5 = Wire("b5")
    e0 = Edge("e0", b4, b5)
    e1 = Edge("e1", b0, b2)
    edges = [e0, e1]
    inputs = [b4, b0, b5]
    outputs = [b2]
    result = qf.no_node_matrix(edges, inputs, outputs)
    expected_result = Pi4Matrix([[1, 0, 0, 0, 0, 1, 0, 0],
                                 [0, 0, 1, 0, 0, 0, 0, 1]])
    assert not (result - expected_result).any()
Example #11
0
def test_graph_augmentation():

    #  a  b  c
    #  |  |  |
    # n0 n1 n2
    #  |\/|\/|
    #  |/\|/\|
    # n3 n4 n5
    #  |  |  |
    #  d  e  f

    a, b, c, d, e, f = Wire('a'), Wire('b'), Wire('c'), Wire('d'), Wire(
        'e'), Wire('f')
    n0, n1, n2, n3, n4, n5 = Node('0'), Node('1'), Node('2'), Node('3'), Node(
        '4'), Node('5')
    e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 = \
        Edge('0', a, n0), Edge('1', b, n1), Edge('2', c, n2), \
        Edge('3', n0, n3), Edge('4', n0, n4), \
        Edge('5', n1, n3), Edge('6', n1, n4), Edge('7', n1, n5), \
        Edge('8', n2, n4), Edge('9', n2, n5), \
        Edge('10', d, n3), Edge('11', e, n4), Edge('12', f, n5), \
        Edge('13', n0, n0)
    inputs = [a, b, c]
    outputs = [d, e, f]
    nodes = [n0, n1, n2, n3, n4, n5]
    edges = [e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13]
    graph = Graph(nodes, edges, inputs, outputs)
    inputs_graph = Graph(inputs=inputs, nodes=[n0], edges=[e0, e13])
    inputs_graph.augment(graph)
    expected_result = Graph([n0, n1, n2, n3, n4],
                            [e0, e1, e2, e3, e4, e5, e6, e8, e13], inputs, [])
    assert inputs_graph == expected_result
Example #12
0
def test_graph_subtraction():

    #  a  b  c
    #  |  |  |
    # n0 n1 n2
    #  |\/|\/|
    #  |/\|/\|
    # n3 n4 n5
    #  |  |  |
    #  d  e  f

    a, b, c, d, e, f = Wire('a'), Wire('b'), Wire('c'), Wire('d'), Wire(
        'e'), Wire('f')
    n0, n1, n2, n3, n4, n5 = Node('0'), Node('1'), Node('2'), Node('3'), Node(
        '4'), Node('5')
    e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 = \
        Edge('0', a, n0), Edge('1', b, n1), Edge('2', c, n2), \
        Edge('3', n0, n3), Edge('4', n0, n4), \
        Edge('5', n1, n3), Edge('6', n1, n4), Edge('7', n1, n5), \
        Edge('8', n2, n4), Edge('9', n2, n5), \
        Edge('10', d, n3), Edge('11', e, n4), Edge('12', f, n5)
    inputs = [a, b, c]
    outputs = [d, e, f]
    nodes = [n0, n1, n2, n3, n4, n5]
    edges = [e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12]
    graph = Graph(nodes, edges, inputs, outputs)
    inputs_graph = Graph(inputs=inputs)
    assert graph - inputs_graph == Graph(nodes, edges, [], outputs)
Example #13
0
def test_filter_inputs_outputs_by_edges():
    b0 = Wire("b0")
    b1 = Wire("b1")
    b2 = Wire("b2")
    b3 = Wire("b3")
    b4 = Wire("b4")
    b5 = Wire("b5")
    e1 = Edge("e1", b0, b2)
    edges = [e1]
    inputs = [b4, b0, b5, b1]
    outputs = [b3, b2]
    result = qf.filter_inputs_outputs_by_edges(edges, inputs, outputs)
    expected_result = [b0], [b2]
    assert result == expected_result
Example #14
0
def matrix_linker(graph1: Graph, graph2: Graph) -> List[InterMatrixLink]:
    """Creates a list of *InterMatrixLink* from the **m1** to **m2**.

    Args:
        graph1 (Graph): graph corresponding to **m1**
        graph2 (Graph): graph corresponding to **m2**

    Returns:
        List[InterMatrixLink]: links between **m1** and **m2** as a *InterMatrixLink* list
    """
    inter_matrix_links = []
    for edge in set(graph1.edges).intersection(graph2.edges):
        wire = Wire(edge.name)

        is_out = wire in graph1.outputs
        is_in = wire in graph1.inputs
        if is_out:
            index = graph1.outputs.index(wire)
        elif is_in:
            index = graph1.inputs.index(wire)
        else:
            continue
        connection_point1 = ConnectionPoint(is_matrix_2=False,
                                            is_out=is_out,
                                            index=index)

        is_out = wire in graph2.outputs
        is_in = wire in graph2.inputs
        if is_out:
            index = graph2.outputs.index(wire)
        elif is_in:
            index = graph2.inputs.index(wire)
        else:
            continue
        connection_point2 = ConnectionPoint(is_matrix_2=True,
                                            is_out=is_out,
                                            index=index)

        link = InterMatrixLink(connection_point1, connection_point2)
        inter_matrix_links.append(link)
    return inter_matrix_links
Example #15
0
def test_no_node_edges_detection_true():
    assert qf.no_node_edges_detection([Edge("e0", Wire("b0"), Wire("b1"))])
Example #16
0
def test_no_node_edges_detection_false():
    assert not qf.no_node_edges_detection([Edge("e0", Node("n0"), Wire("b1"))])
Example #17
0
def split_and_reunite(graph: Graph) -> GenericMatrix:
    """Recursive function taking in a graph and returning the corresponding matrix.

    To do so, split the graph in two, passes the two halves to it's next iteration and reunite the two matrix obtained
    using the :ref:`fusion_matrices <fusion_matrices>` method from :ref:`divide_conquer`.

    The main part of this function is converting the graph format to the matrix format. tmp

    Args:
        graph (Graph): diagram considered

    Returns:
        GenericMatrix: matrix corresponding to the given diagram
    """
    if len(graph.nodes) == 0:
        return no_node_matrix(graph.edges, graph.inputs, graph.outputs)
    elif len(graph.nodes) == 1 and not no_node_edges_detection(graph.edges):
        try:
            return UsedFragment.node_to_matrix(graph.nodes[0],
                                               len(graph.inputs),
                                               len(graph.outputs))
        except AttributeError:
            return fallback_node_to_matrix(graph.nodes[0], len(graph.inputs),
                                           len(graph.outputs))
    else:
        graph1, graph2 = connected_graphs_split(graph)
        if not graph2:
            # we rewrite graph1 and graph2 so they contain two parts of the current graph1
            if no_node_edges_detection(graph.edges):
                # probably dead code since if a graph has such an edge (containing no node, only I/O), and has nodes,
                # the connected_graphs_split function would return two distinct graphs
                #
                # degenerate cases, when a graph contains only wires
                # in this case, graph1 will contain the I/O connected to another I/O and graph2 will contain the rest
                graph2 = Graph(nodes=graph.nodes)

                graph2 += filter_edges_inputs_outputs_by_nodes(
                    graph2.nodes, graph)
                graph1 = graph - graph2
            else:
                if graph.inputs:
                    graph1 = Graph(inputs=[graph.inputs[0]])
                    graph1.augment(graph)
                elif graph.nodes:
                    graph1 = Graph(nodes=[graph.nodes[0]])
                else:
                    raise RuntimeError(
                        'A graph with no node shouldn\'t enter in this branch')

                graph1 += graph1.neighbouring_i_o(graph)
                graph2 = graph - graph1

                in_between_edges = between_graphs_edges(graph1, graph2, graph)

                graph1.edges += in_between_edges

                in_between_wires = []
                for edge in in_between_edges:
                    in_between_wires.append(Wire(edge.name))

                graph1.outputs += in_between_wires
                graph2.inputs += in_between_wires

            first_half_matrix = split_and_reunite(graph1)
            second_half_matrix = split_and_reunite(graph2)

            inter_matrix_link = matrix_linker(graph1, graph2)
        else:
            first_half_matrix = split_and_reunite(graph1)
            second_half_matrix = split_and_reunite(graph2)
            inter_matrix_link = []

        input_connections = wires_to_connection_point_node_sorted(
            graph.inputs, graph.edges, graph1.nodes, graph2.nodes, False)
        output_connections = wires_to_connection_point_node_sorted(
            graph.outputs, graph.edges, graph1.nodes, graph2.nodes, True)

        return divide_conquer.fusion_matrices(first_half_matrix,
                                              second_half_matrix,
                                              input_connections,
                                              output_connections,
                                              inter_matrix_link)