Beispiel #1
0
    def _mpl(graph: PyGraph, self_loop: bool, **kwargs):
        """
        Auxiliary function for drawing the lattice using matplotlib.

        Args:
            graph : graph to be drawn.
            self_loop : Draw self-loops, which are edges connecting a node to itself.
            **kwargs : Kwargs for drawing the lattice.

        Raises:
            MissingOptionalLibraryError: Requires matplotlib.
        """
        # pylint: disable=unused-import
        from matplotlib import pyplot as plt

        if not self_loop:
            self_loops = [(i, i) for i in range(graph.num_nodes())
                          if graph.has_edge(i, i)]
            graph.remove_edges_from(self_loops)

        mpl_draw(
            graph=graph,
            **kwargs,
        )
        plt.draw()
Beispiel #2
0
    def _mpl(graph: PyGraph, self_loop: bool, **kwargs):
        """
        Auxiliary function for drawing the lattice using matplotlib.

        Args:
            graph : graph to be drawn.
            self_loop : Draw self-loops, which are edges connecting a node to itself.
            **kwargs : Kwargs for drawing the lattice.

        Raises:
            MissingOptionalLibraryError: Requires matplotlib.
        """
        if not HAS_MATPLOTLIB:
            raise MissingOptionalLibraryError(
                libname="Matplotlib", name="_mpl", pip_install="pip install matplotlib"
            )
        from matplotlib import pyplot as plt

        if not self_loop:
            self_loops = [(i, i) for i in range(graph.num_nodes()) if graph.has_edge(i, i)]
            graph.remove_edges_from(self_loops)

        mpl_draw(
            graph=graph,
            **kwargs,
        )
        plt.draw()
    def run(self, dag):
        """run the layout method"""
        qubits = dag.qubits
        qubit_indices = {qubit: index for index, qubit in enumerate(qubits)}

        interactions = []
        for node in dag.op_nodes(include_directives=False):
            len_args = len(node.qargs)
            if len_args == 2:
                interactions.append((qubit_indices[node.qargs[0]],
                                     qubit_indices[node.qargs[1]]))
            if len_args >= 3:
                raise TranspilerError(
                    "VF2Layout only can handle 2-qubit gates or less. Node "
                    f"{node.name} ({node}) is {len_args}-qubit")

        if self.strict_direction:
            cm_graph = self.coupling_map.graph
            im_graph = PyDiGraph(multigraph=False)
        else:
            cm_graph = self.coupling_map.graph.to_undirected()
            im_graph = PyGraph(multigraph=False)

        cm_nodes = list(cm_graph.node_indexes())
        if self.seed != -1:
            random.Random(self.seed).shuffle(cm_nodes)
            shuffled_cm_graph = type(cm_graph)()
            shuffled_cm_graph.add_nodes_from(cm_nodes)
            new_edges = [(cm_nodes[edge[0]], cm_nodes[edge[1]])
                         for edge in cm_graph.edge_list()]
            shuffled_cm_graph.add_edges_from_no_data(new_edges)
            cm_nodes = [
                k for k, v in sorted(enumerate(cm_nodes),
                                     key=lambda item: item[1])
            ]
            cm_graph = shuffled_cm_graph
        im_graph.add_nodes_from(range(len(qubits)))
        im_graph.add_edges_from_no_data(interactions)

        mappings = vf2_mapping(cm_graph,
                               im_graph,
                               subgraph=True,
                               id_order=False,
                               induced=False)
        try:
            mapping = next(mappings)
            stop_reason = "solution found"
            layout = Layout({
                qubits[im_i]: cm_nodes[cm_i]
                for cm_i, im_i in mapping.items()
            })
            self.property_set["layout"] = layout
            for reg in dag.qregs.values():
                self.property_set["layout"].add_register(reg)
        except StopIteration:
            stop_reason = "nonexistent solution"

        self.property_set["VF2Layout_stop_reason"] = stop_reason
Beispiel #4
0
    def __init__(self, graph: PyGraph) -> None:
        """
        Args:
            graph: Input graph for Lattice. `graph.multigraph` must be False.

        Raises:
            ValueError: If `graph.multigraph` is True for a given graph, it is invalid.
        """
        if graph.multigraph:
            raise ValueError(
                f"Invalid `graph.multigraph` {graph.multigraph} is given. "
                "`graph.multigraph` must be `False`."
            )
        if graph.edges() == [None] * graph.num_edges():
            weighted_edges = [edge + (1.0,) for edge in graph.edge_list()]
            for start, end, weight in weighted_edges:
                graph.update_edge(start, end, weight)
        self._graph = graph

        self.pos: Optional[dict] = None
Beispiel #5
0
    def test_from_nodes_and_edges(self):
        """Test from_nodes_edges."""
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(6))
        weighted_edge_list = [
            (0, 1, 1.0 + 1.0j),
            (0, 2, -1.0),
            (2, 3, 2.0),
            (4, 2, -1.0),
            (4, 4, 3.0),
            (2, 5, -1.0),
        ]
        graph.add_edges_from(weighted_edge_list)
        lattice = Lattice(graph)
        target_num_nodes = 6
        target_weighted_edge_list = [
            (2, 5, -1.0),
            (4, 4, 3),
            (4, 2, -1.0),
            (2, 3, 2.0),
            (0, 2, -1.0),
            (0, 1, 1.0 + 1.0j),
        ]
        target_lattice = Lattice.from_nodes_and_edges(
            target_num_nodes, target_weighted_edge_list)

        self.assertTrue(
            is_isomorphic(lattice.graph,
                          target_lattice.graph,
                          edge_matcher=lambda x, y: x == y))
Beispiel #6
0
    def test_from_parameters(self):
        """Test from_parameters."""
        coupling_matrix = np.array([[1.0, 1.0 + 1.0j, 2.0 - 2.0j],
                                    [1.0 - 1.0j, 0.0, 0.0],
                                    [2.0 + 2.0j, 0.0, 1.0]])

        ism = cast(IsingModel, IsingModel.from_parameters(coupling_matrix))
        with self.subTest("Check the graph."):
            target_graph = PyGraph(multigraph=False)
            target_graph.add_nodes_from(range(3))
            target_weight = [(0, 0, 1.0), (0, 1, 1.0 + 1.0j),
                             (0, 2, 2.0 - 2.0j), (2, 2, 1.0)]
            target_graph.add_edges_from(target_weight)
            self.assertTrue(
                is_isomorphic(ism.lattice.graph,
                              target_graph,
                              edge_matcher=lambda x, y: x == y))

        with self.subTest("Check the coupling matrix."):
            assert_array_equal(ism.coupling_matrix(), coupling_matrix)

        with self.subTest("Check the second q op representation."):
            coupling = [
                ("Z_0 Z_1", 1.0 + 1.0j),
                ("Z_0 Z_2", 2.0 - 2.0j),
                ("X_0", 1.0),
                ("X_2", 1.0),
            ]

            ham = coupling

            self.assertSetEqual(set(ham), set(ism.second_q_ops().to_list()))
Beispiel #7
0
    def test_nonnumeric_weight_raises(self):
        """Test the initialization with a graph with non-numeric edge weights raises."""
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(3))
        graph.add_edges_from([(0, 1, 1), (1, 2, "banana")])

        with self.assertRaises(ValueError):
            _ = Lattice(graph)
Beispiel #8
0
    def test_init(self):
        """Test init."""
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(3))
        weighted_edge_list = [
            (0, 1, 1.0 + 1.0j),
            (0, 2, -1.0),
            (1, 1, 2.0),
        ]
        graph.add_edges_from(weighted_edge_list)
        lattice = Lattice(graph)
        fhm = FermiHubbardModel(lattice, onsite_interaction=10.0)

        with self.subTest("Check the graph."):
            self.assertTrue(
                is_isomorphic(fhm.lattice.graph,
                              lattice.graph,
                              edge_matcher=lambda x, y: x == y))

        with self.subTest("Check the hopping matrix"):
            hopping_matrix = fhm.hopping_matrix()
            target_matrix = np.array([[0.0, 1.0 + 1.0j, -1.0],
                                      [1.0 - 1.0j, 2.0, 0.0], [-1.0, 0.0,
                                                               0.0]])
            assert_array_equal(hopping_matrix, target_matrix)

        with self.subTest("Check the second q op representation."):
            hopping = [
                ("+_0 -_2", 1.0 + 1.0j),
                ("-_0 +_2", -(1.0 - 1.0j)),
                ("+_0 -_4", -1.0),
                ("-_0 +_4", 1.0),
                ("+_1 -_3", 1.0 + 1.0j),
                ("-_1 +_3", -(1.0 - 1.0j)),
                ("+_1 -_5", -1.0),
                ("-_1 +_5", 1.0),
                ("+_2 -_2", 2.0),
                ("+_3 -_3", 2.0),
            ]

            interaction = [
                ("+_0 -_0 +_1 -_1", 10.0),
                ("+_2 -_2 +_3 -_3", 10.0),
                ("+_4 -_4 +_5 -_5", 10.0),
            ]

            ham = hopping + interaction

            self.assertSetEqual(
                set(ham),
                set(fhm.second_q_ops(display_format="sparse").to_list()))
Beispiel #9
0
    def test_from_parameters(self):
        """Test from_parameters."""
        hopping_matrix = np.array([[1.0, 1.0 + 1.0j, 2.0 + 2.0j],
                                   [1.0 - 1.0j, 0.0, 0.0],
                                   [2.0 - 2.0j, 0.0, 1.0]])

        onsite_interaction = 10.0
        fhm = FermiHubbardModel.from_parameters(hopping_matrix,
                                                onsite_interaction)
        with self.subTest("Check the graph."):
            target_graph = PyGraph(multigraph=False)
            target_graph.add_nodes_from(range(3))
            target_weight = [(0, 0, 1.0), (0, 1, 1.0 + 1.0j),
                             (0, 2, 2.0 + 2.0j), (2, 2, 1.0)]
            target_graph.add_edges_from(target_weight)
            self.assertTrue(
                is_isomorphic(fhm.lattice.graph,
                              target_graph,
                              edge_matcher=lambda x, y: x == y))

        with self.subTest("Check the hopping matrix."):
            assert_array_equal(fhm.hopping_matrix(), hopping_matrix)

        with self.subTest("Check the second q op representation."):
            hopping = [
                ("+_0 -_2", 1.0 + 1.0j),
                ("-_0 +_2", -(1.0 - 1.0j)),
                ("+_0 -_4", 2.0 + 2.0j),
                ("-_0 +_4", -(2.0 - 2.0j)),
                ("+_1 -_3", 1.0 + 1.0j),
                ("-_1 +_3", -(1.0 - 1.0j)),
                ("+_1 -_5", 2.0 + 2.0j),
                ("-_1 +_5", -(2.0 - 2.0j)),
                ("+_0 -_0", 1.0),
                ("+_1 -_1", 1.0),
                ("+_4 -_4", 1.0),
                ("+_5 -_5", 1.0),
            ]

            interaction = [
                ("+_0 -_0 +_1 -_1", onsite_interaction),
                ("+_2 -_2 +_3 -_3", onsite_interaction),
                ("+_4 -_4 +_5 -_5", onsite_interaction),
            ]

            ham = hopping + interaction

            self.assertSetEqual(
                set(ham),
                set(fhm.second_q_ops(display_format="sparse").to_list()))
Beispiel #10
0
def build_interaction_graph(dag, strict_direction=True):
    """Build an interaction graph from a dag."""
    if strict_direction:
        im_graph = PyDiGraph(multigraph=False)
    else:
        im_graph = PyGraph(multigraph=False)
    im_graph_node_map = {}
    reverse_im_graph_node_map = {}

    for node in dag.op_nodes(include_directives=False):
        len_args = len(node.qargs)
        if len_args == 1:
            if node.qargs[0] not in im_graph_node_map:
                weight = defaultdict(int)
                weight[node.name] += 1
                im_graph_node_map[node.qargs[0]] = im_graph.add_node(weight)
                reverse_im_graph_node_map[im_graph_node_map[
                    node.qargs[0]]] = node.qargs[0]
            else:
                im_graph[im_graph_node_map[node.qargs[0]]][node.op.name] += 1
        if len_args == 2:
            if node.qargs[0] not in im_graph_node_map:
                im_graph_node_map[node.qargs[0]] = im_graph.add_node(
                    defaultdict(int))
                reverse_im_graph_node_map[im_graph_node_map[
                    node.qargs[0]]] = node.qargs[0]
            if node.qargs[1] not in im_graph_node_map:
                im_graph_node_map[node.qargs[1]] = im_graph.add_node(
                    defaultdict(int))
                reverse_im_graph_node_map[im_graph_node_map[
                    node.qargs[1]]] = node.qargs[1]
            edge = (im_graph_node_map[node.qargs[0]],
                    im_graph_node_map[node.qargs[1]])
            if im_graph.has_edge(*edge):
                im_graph.get_edge_data(*edge)[node.name] += 1
            else:
                weight = defaultdict(int)
                weight[node.name] += 1
                im_graph.add_edge(*edge, weight)
        if len_args > 2:
            return None
    return im_graph, im_graph_node_map, reverse_im_graph_node_map
Beispiel #11
0
    def test_to_adjacency_matrix(self):
        """Test to_adjacency_matrix."""
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(3))
        weighted_edge_list = [(0, 1, 1.0 + 1.0j), (0, 2, -1.0), (2, 2, 3)]
        graph.add_edges_from(weighted_edge_list)
        lattice = Lattice(graph)

        target_matrix = np.array([[0, 1 + 1j, -1.0], [1 - 1j, 0, 0],
                                  [-1.0, 0, 3.0]])
        assert_array_equal(lattice.to_adjacency_matrix(weighted=True),
                           target_matrix)

        target_matrix = np.array([[0, 1, 1], [1, 0, 0], [1, 0, 1]])
        assert_array_equal(lattice.to_adjacency_matrix(), target_matrix)
Beispiel #12
0
    def from_nodes_and_edges(
        cls, num_nodes: int, weighted_edges: List[Tuple[int, int, complex]]
    ) -> "Lattice":
        """Return an instance of Lattice from the number of nodes and the list of edges.

        Args:
            num_nodes: The number of nodes.
            weighted_edges: A list of tuples consisting of two nodes and the weight between them.
        Returns:
            Lattice generated from lists of nodes and edges.
        """
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(num_nodes))
        graph.add_edges_from(weighted_edges)
        return cls(graph)
Beispiel #13
0
    def test_from_networkx(self):
        """Test initialization from a networkx graph."""
        graph = nx.Graph()
        graph.add_nodes_from(range(5))
        graph.add_edges_from([(i, i + 1) for i in range(4)])
        lattice = Lattice(graph)

        target_graph = PyGraph()
        target_graph.add_nodes_from(range(5))
        target_graph.add_edges_from([(i, i + 1, 1) for i in range(4)])

        self.assertTrue(
            is_isomorphic(lattice.graph,
                          target_graph,
                          edge_matcher=lambda x, y: x == y))
Beispiel #14
0
    def _generate_lattice_from_parameters(interaction_matrix: np.ndarray):
        # make a graph from the interaction matrix.
        # This should be replaced by from_adjacency_matrix of retworkx.
        shape = interaction_matrix.shape
        if len(shape) != 2 or shape[0] != shape[1]:
            raise ValueError(
                f"Invalid shape of `interaction_matrix`, {shape},  is given."
                "It must be a square matrix.")

        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(shape[0]))
        for source_index in range(shape[0]):
            for target_index in range(source_index, shape[0]):
                weight = interaction_matrix[source_index, target_index]
                if not weight == 0.0:
                    graph.add_edge(source_index, target_index, weight)
        return Lattice(graph)
Beispiel #15
0
 def test_copy(self):
     """Test test_copy."""
     graph = PyGraph(multigraph=False)
     graph.add_nodes_from(range(6))
     weighted_edge_list = [
         (0, 1, 1.0 + 1.0j),
         (0, 2, -1.0),
         (2, 3, 2.0),
         (2, 4, -1.0),
         (4, 4, 3.0),
         (2, 5, -1.0),
     ]
     graph.add_edges_from(weighted_edge_list)
     lattice = Lattice(graph)
     lattice_copy = lattice.copy()
     self.assertTrue(
         is_isomorphic(lattice_copy.graph,
                       graph,
                       edge_matcher=lambda x, y: x == y))
Beispiel #16
0
    def test_init(self):
        """Test init."""
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(3))
        weighted_edge_list = [
            (0, 1, 1.0 + 1.0j),
            (0, 2, -1.0),
            (1, 1, 2.0),
        ]
        graph.add_edges_from(weighted_edge_list)
        lattice = Lattice(graph)
        ism = IsingModel(lattice)

        with self.subTest("Check the graph."):
            self.assertTrue(
                is_isomorphic(ism.lattice.graph,
                              lattice.graph,
                              edge_matcher=lambda x, y: x == y))

        with self.subTest("Check the coupling matrix"):
            coupling_matrix = ism.coupling_matrix()
            target_matrix = np.array([[0.0, 1.0 + 1.0j, -1.0],
                                      [1.0 - 1.0j, 2.0, 0.0], [-1.0, 0.0,
                                                               0.0]])
            assert_array_equal(coupling_matrix, target_matrix)

        with self.subTest("Check the second q op representation."):
            coupling = [
                ("Z_0 Z_1", 1.0 + 1.0j),
                ("Z_0 Z_2", -1.0),
                ("X_1", 2.0),
            ]

            ham = coupling

            self.assertSetEqual(set(ham), set(ism.second_q_ops().to_list()))
Beispiel #17
0
    def from_json(cls, s: str) -> 'Topology':
        """Initializes from a JSON string."""
        # load dict from JSON string
        data = orjson.loads(s)

        # validate type
        _type = data.pop("type")
        if _type != cls.__name__:
            raise TypeError(f"cannot deserialize from type `{_type}`")

        # initialize an empty graph
        graph = PyGraph()

        # process atoms
        for atom in data["atoms"]:
            graph.add_node(Atom.from_json(orjson.dumps(atom)))

        # process bonds
        for bond in data["bonds"]:
            bond = Bond.from_json(orjson.dumps(bond))
            graph.add_edge(bond.indices[0], bond.indices[1], edge=bond)

        # return instance
        return cls(graph)
Beispiel #18
0
    def test_init(self):
        """Test init."""
        rows = 3
        cols = 2
        edge_parameter = (1.0 + 1.0j, 2.0 + 2.0j)
        onsite_parameter = 1.0
        boundary_condition = (BoundaryCondition.PERIODIC,
                              BoundaryCondition.OPEN)
        square = SquareLattice(rows, cols, edge_parameter, onsite_parameter,
                               boundary_condition)

        with self.subTest("Check the graph."):
            target_graph = PyGraph(multigraph=False)
            target_graph.add_nodes_from(range(6))
            weighted_edge_list = [
                (0, 1, 1.0 + 1.0j),
                (1, 2, 1.0 + 1.0j),
                (0, 2, 1.0 - 1.0j),
                (3, 4, 1.0 + 1.0j),
                (4, 5, 1.0 + 1.0j),
                (3, 5, 1.0 - 1.0j),
                (0, 3, 2.0 + 2.0j),
                (1, 4, 2.0 + 2.0j),
                (2, 5, 2.0 + 2.0j),
                (0, 0, 1.0),
                (1, 1, 1.0),
                (2, 2, 1.0),
                (3, 3, 1.0),
                (4, 4, 1.0),
                (5, 5, 1.0),
            ]
            target_graph.add_edges_from(weighted_edge_list)
            self.assertTrue(
                is_isomorphic(square.graph,
                              target_graph,
                              edge_matcher=lambda x, y: x == y))

        with self.subTest("Check the number of nodes."):
            self.assertEqual(square.num_nodes, 6)

        with self.subTest("Check the set of nodes."):
            self.assertSetEqual(set(square.node_indexes), set(range(6)))

        with self.subTest("Check the set of weights."):
            target_set = {
                (0, 1, 1.0 + 1.0j),
                (1, 2, 1.0 + 1.0j),
                (0, 2, 1.0 - 1.0j),
                (3, 4, 1.0 + 1.0j),
                (4, 5, 1.0 + 1.0j),
                (3, 5, 1.0 - 1.0j),
                (0, 3, 2.0 + 2.0j),
                (1, 4, 2.0 + 2.0j),
                (2, 5, 2.0 + 2.0j),
                (0, 0, 1.0),
                (1, 1, 1.0),
                (2, 2, 1.0),
                (3, 3, 1.0),
                (4, 4, 1.0),
                (5, 5, 1.0),
            }
            self.assertSetEqual(set(square.weighted_edge_list), target_set)

        with self.subTest("Check the adjacency matrix."):
            target_matrix = np.array([
                [1.0, 1.0 + 1.0j, 1.0 - 1.0j, 2.0 + 2.0j, 0.0, 0.0],
                [1.0 - 1.0j, 1.0, 1.0 + 1.0j, 0.0, 2.0 + 2.0j, 0.0],
                [1.0 + 1.0j, 1.0 - 1.0j, 1.0, 0.0, 0.0, 2.0 + 2.0j],
                [2.0 - 2.0j, 0.0, 0.0, 1.0, 1.0 + 1.0j, 1.0 - 1.0j],
                [0.0, 2.0 - 2.0j, 0.0, 1.0 - 1.0j, 1.0, 1.0 + 1.0j],
                [0.0, 0.0, 2.0 - 2.0j, 1.0 + 1.0j, 1.0 - 1.0j, 1.0],
            ])

            assert_array_equal(square.to_adjacency_matrix(weighted=True),
                               target_matrix)
Beispiel #19
0
    def test_uniform_parameters(self):
        """Test uniform_parameters."""
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(3))
        weighted_edge_list = [
            (0, 1, 1.0 + 1.0j),
            (0, 2, -1.0),
            (1, 1, 2.0),
        ]
        graph.add_edges_from(weighted_edge_list)
        lattice = Lattice(graph)
        uniform_ism = cast(
            IsingModel,
            IsingModel.uniform_parameters(
                lattice,
                uniform_interaction=1.0 + 1.0j,
                uniform_onsite_potential=0.0,
            ),
        )
        with self.subTest("Check the graph."):
            target_graph = PyGraph(multigraph=False)
            target_graph.add_nodes_from(range(3))
            target_weight = [
                (0, 1, 1.0 + 1.0j),
                (0, 2, 1.0 + 1.0j),
                (0, 0, 0.0),
                (1, 1, 0.0),
                (2, 2, 0.0),
            ]
            target_graph.add_edges_from(target_weight)
            self.assertTrue(
                is_isomorphic(uniform_ism.lattice.graph,
                              target_graph,
                              edge_matcher=lambda x, y: x == y))
        with self.subTest("Check the coupling matrix."):
            coupling_matrix = uniform_ism.coupling_matrix()
            target_matrix = np.array([[0.0, 1.0 + 1.0j, 1.0 + 1.0j],
                                      [1.0 - 1.0j, 0.0, 0.0],
                                      [1.0 - 1.0j, 0.0, 0.0]])
            assert_array_equal(coupling_matrix, target_matrix)

        with self.subTest("Check the second q op representation."):
            coupling = [
                ("Z_0 Z_1", 1.0 + 1.0j),
                ("Z_0 Z_2", 1.0 + 1.0j),
                ("X_0", 0.0),
                ("X_1", 0.0),
                ("X_2", 0.0),
            ]

            ham = coupling

            self.assertSetEqual(set(ham),
                                set(uniform_ism.second_q_ops().to_list()))
    def __init__(
        self,
        size: Tuple[int, ...],
        edge_parameter: Union[complex, Tuple[complex, ...]] = 1.0,
        onsite_parameter: complex = 0.0,
        boundary_condition: Union[
            BoundaryCondition, Tuple[BoundaryCondition, ...]
        ] = BoundaryCondition.OPEN,
    ) -> None:
        """
        Args:
            size: Lengths of each dimension.
            edge_parameter: Weights on the edges in each direction.
                When it is a single value, it is interpreted as a tuple of the same length as `size`
                consisting of the same values.
                Defaults to 1.0.
            onsite_parameter: Weight on the self-loops, which are edges connecting a node to itself.
                This is uniform over the lattice points.
                Defaults to 0.0.
            boundary_condition: Boundary condition for each dimension.
                The available boundary conditions are:
                BoundaryCondition.OPEN, BoundaryCondition.PERIODIC.
                When it is a single value, it is interpreted as a tuple of the same length as `size`
                consisting of the same values.
                Defaults to BoundaryCondition.OPEN.

        Raises:
            ValueError: When edge parameter or boundary condition is a tuple,
                the length of that is not the same as that of size.
        """

        self._dim = len(size)
        self._size = size

        # edge parameter
        if isinstance(edge_parameter, (int, float, complex)):
            edge_parameter = (edge_parameter,) * self._dim
        elif isinstance(edge_parameter, tuple):
            if len(edge_parameter) != self._dim:
                raise ValueError(
                    "size mismatch, "
                    f"`edge_parameter`: {len(edge_parameter)}, `size`: {self._dim}."
                    "The length of `edge_parameter` must be the same as that of size."
                )

        self._edge_parameter = edge_parameter

        self._onsite_parameter = onsite_parameter

        # boundary condition
        if isinstance(boundary_condition, BoundaryCondition):
            boundary_condition = (boundary_condition,) * self._dim
        elif isinstance(boundary_condition, tuple):
            if len(boundary_condition) != self._dim:
                raise ValueError(
                    "size mismatch, "
                    f"`boundary_condition`: {len(boundary_condition)}, `size`: {self._dim}."
                    "The length of `boundary_condition` must be the same as that of size."
                )

        self._boundary_condition = boundary_condition

        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(np.prod(size)))

        # add edges excluding the boundary edges
        bulk_edge_list = self._bulk_edges()
        graph.add_edges_from(bulk_edge_list)

        # add self-loops.
        self_loop_list = self._self_loops()
        graph.add_edges_from(self_loop_list)

        # add edges that cross the boundaries
        boundary_edge_list = self._create_boundary_edges()
        graph.add_edges_from(boundary_edge_list)

        # a list of edges that depend on the boundary condition
        self._boundary_edges = [(edge[0], edge[1]) for edge in boundary_edge_list]

        super().__init__(graph)

        # default position for one and two-dimensional cases.
        self.pos = self._default_position()
Beispiel #21
0
    def __init__(
        self,
        rows: int,
        cols: int,
        edge_parameter: Union[complex, Tuple[complex, complex, complex]] = 1.0,
        onsite_parameter: complex = 0.0,
        boundary_condition: BoundaryCondition = BoundaryCondition.OPEN,
    ) -> None:
        """
        Args:
            rows: Length of the x direction.
            cols: Length of the y direction.
            edge_parameter: Weights on the edges in x, y and diagonal directions.
                This is specified as a tuple of length 3 or a single value.
                When it is a single value, it is interpreted as a tuple of length 3
                consisting of the same values.
                Defaults to 1.0,
            onsite_parameter: Weight on the self-loops, which are edges connecting a node to itself.
                Defaults to 0.0.
            boundary_condition: Boundary condition for the lattice.
                The available boundary conditions are:
                BoundaryCondition.OPEN, BoundaryCondition.PERIODIC.
                Defaults to BoundaryCondition.OPEN.

        Raises:
            ValueError: Given size, edge parameter or boundary condition are invalid values.
        """
        self.rows = rows
        self.cols = cols
        self.size = (rows, cols)
        self.dim = 2
        self.boundary_condition = boundary_condition

        if rows < 2 or cols < 2 or (rows, cols) == (2, 2):
            # If it's True, triangular lattice can't be well defined.
            raise ValueError("Both of `rows` and `cols` must not be (2, 2)"
                             "and must be greater than or equal to 2.")

        if isinstance(edge_parameter, (int, float, complex)):
            edge_parameter = (edge_parameter, edge_parameter, edge_parameter)
        elif isinstance(edge_parameter, tuple):
            if len(edge_parameter) != 3:
                raise ValueError(
                    f"The length of `edge_parameter` must be 3, not {len(edge_parameter)}."
                )

        self.edge_parameter = edge_parameter
        self.onsite_parameter = onsite_parameter

        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(np.prod(self.size)))

        # add edges excluding the boundary edges
        bulk_edges = self._bulk_edges()
        graph.add_edges_from(bulk_edges)

        # add self-loops
        self_loop_list = self._self_loops()
        graph.add_edges_from(self_loop_list)

        # add edges that cross the boundaries
        boundary_edge_list = self._boundary_edges()
        graph.add_edges_from(boundary_edge_list)

        # a list of edges that depend on the boundary condition
        self.boundary_edges = [(edge[0], edge[1])
                               for edge in boundary_edge_list]
        super().__init__(graph)
        # default position
        self.pos = self._default_position()
Beispiel #22
0
    def test_init(self):
        """Test init."""
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(6))
        weighted_edge_list = [
            (0, 1, 1.0 + 1.0j),
            (0, 2, -1.0),
            (2, 3, 2.0),
            (2, 4, -1.0),
            (4, 4, 3.0),
            (2, 5, -1.0),
        ]
        graph.add_edges_from(weighted_edge_list)
        lattice = Lattice(graph)

        with self.subTest("Check the type of lattice."):
            self.assertIsInstance(lattice, Lattice)

        with self.subTest("Check graph."):
            target_graph = PyGraph(multigraph=False)
            target_graph.add_nodes_from(range(6))
            target_weighted_edge_list = [
                (4, 4, 3.0),
                (0, 1, 1 + 1j),
                (2, 3, 2.0),
                (2, 4, -1.0),
                (2, 5, -1.0),
                (0, 2, -1),
            ]
            target_graph.add_edges_from(target_weighted_edge_list)
            self.assertTrue(
                is_isomorphic(lattice.graph,
                              target_graph,
                              edge_matcher=lambda x, y: x == y))

        with self.subTest("Check the number of nodes."):
            self.assertEqual(lattice.num_nodes, 6)

        with self.subTest("Check the set of nodes."):
            self.assertSetEqual(set(lattice.node_indexes), set(range(6)))

        with self.subTest("Check the set of weights."):
            target_set = {
                (0, 1, 1 + 1j),
                (4, 4, 3),
                (2, 5, -1.0),
                (0, 2, -1.0),
                (2, 3, 2.0),
                (2, 4, -1.0),
            }
            self.assertEqual(set(lattice.weighted_edge_list), target_set)
Beispiel #23
0
    def test_edges_removed(self):
        """Test the initialization with a graph where edges have been removed."""
        graph = PyGraph(multigraph=False)
        graph.add_nodes_from(range(3))
        graph.add_edges_from([(0, 1, 1), (1, 2, 1)])
        graph.remove_edge_from_index(0)

        lattice = Lattice(graph)

        target_graph = PyGraph(multigraph=False)
        target_graph.add_nodes_from(range(3))
        target_graph.add_edges_from([(1, 2, 1)])

        self.assertTrue(
            is_isomorphic(lattice.graph,
                          target_graph,
                          edge_matcher=lambda x, y: x == y))
Beispiel #24
0
    def run(self, dag):
        """run the layout method"""
        if self.coupling_map is None:
            raise TranspilerError("coupling_map or target must be specified.")

        qubits = dag.qubits
        qubit_indices = {qubit: index for index, qubit in enumerate(qubits)}

        interactions = []
        for node in dag.op_nodes(include_directives=False):
            len_args = len(node.qargs)
            if len_args == 2:
                interactions.append((qubit_indices[node.qargs[0]],
                                     qubit_indices[node.qargs[1]]))
            if len_args >= 3:
                self.property_set[
                    "VF2Layout_stop_reason"] = VF2LayoutStopReason.MORE_THAN_2Q
                return

        if self.strict_direction:
            cm_graph = self.coupling_map.graph
            im_graph = PyDiGraph(multigraph=False)
        else:
            cm_graph = self.coupling_map.graph.to_undirected()
            im_graph = PyGraph(multigraph=False)

        cm_nodes = list(cm_graph.node_indexes())
        if self.seed != -1:
            random.Random(self.seed).shuffle(cm_nodes)
            shuffled_cm_graph = type(cm_graph)()
            shuffled_cm_graph.add_nodes_from(cm_nodes)
            new_edges = [(cm_nodes[edge[0]], cm_nodes[edge[1]])
                         for edge in cm_graph.edge_list()]
            shuffled_cm_graph.add_edges_from_no_data(new_edges)
            cm_nodes = [
                k for k, v in sorted(enumerate(cm_nodes),
                                     key=lambda item: item[1])
            ]
            cm_graph = shuffled_cm_graph

        im_graph.add_nodes_from(range(len(qubits)))
        im_graph.add_edges_from_no_data(interactions)
        # To avoid trying to over optimize the result by default limit the number
        # of trials based on the size of the graphs. For circuits with simple layouts
        # like an all 1q circuit we don't want to sit forever trying every possible
        # mapping in the search space
        if self.max_trials is None:
            im_graph_edge_count = len(im_graph.edge_list())
            cm_graph_edge_count = len(cm_graph.edge_list())
            self.max_trials = max(im_graph_edge_count,
                                  cm_graph_edge_count) + 15

        logger.debug("Running VF2 to find mappings")
        mappings = vf2_mapping(
            cm_graph,
            im_graph,
            subgraph=True,
            id_order=False,
            induced=False,
            call_limit=self.call_limit,
        )
        chosen_layout = None
        chosen_layout_score = None
        start_time = time.time()
        trials = 0
        for mapping in mappings:
            trials += 1
            logger.debug("Running trial: %s", trials)
            stop_reason = VF2LayoutStopReason.SOLUTION_FOUND
            layout = Layout({
                qubits[im_i]: cm_nodes[cm_i]
                for cm_i, im_i in mapping.items()
            })
            # If the graphs have the same number of nodes we don't need to score or do multiple
            # trials as the score heuristic currently doesn't weigh nodes based on gates on a
            # qubit so the scores will always all be the same
            if len(cm_graph) == len(im_graph):
                chosen_layout = layout
                break
            layout_score = self._score_layout(layout)
            logger.debug("Trial %s has score %s", trials, layout_score)
            if chosen_layout is None:
                chosen_layout = layout
                chosen_layout_score = layout_score
            elif layout_score < chosen_layout_score:
                logger.debug(
                    "Found layout %s has a lower score (%s) than previous best %s (%s)",
                    layout,
                    layout_score,
                    chosen_layout,
                    chosen_layout_score,
                )
                chosen_layout = layout
                chosen_layout_score = layout_score
            if self.max_trials > 0 and trials >= self.max_trials:
                logger.debug("Trial %s is >= configured max trials %s", trials,
                             self.max_trials)
                break
            elapsed_time = time.time() - start_time
            if self.time_limit is not None and elapsed_time >= self.time_limit:
                logger.debug(
                    "VF2Layout has taken %s which exceeds configured max time: %s",
                    elapsed_time,
                    self.time_limit,
                )
                break
        if chosen_layout is None:
            stop_reason = VF2LayoutStopReason.NO_SOLUTION_FOUND
        else:
            self.property_set["layout"] = chosen_layout
            for reg in dag.qregs.values():
                self.property_set["layout"].add_register(reg)

        self.property_set["VF2Layout_stop_reason"] = stop_reason
Beispiel #25
0
 def __init__(self, graph: Optional[PyGraph] = None) -> None:
     if graph is None:
         graph = PyGraph()
     self._graph = graph
    def test_init(self):
        """Test init."""
        size = (2, 2, 2)
        edge_parameter = (1.0 + 1.0j, 0.0, -2.0 - 2.0j)
        onsite_parameter = 5.0
        boundary_condition = (
            BoundaryCondition.OPEN,
            BoundaryCondition.PERIODIC,
            BoundaryCondition.OPEN,
        )
        hyper_cubic = HyperCubicLattice(size, edge_parameter, onsite_parameter,
                                        boundary_condition)

        with self.subTest("Check the graph."):
            target_graph = PyGraph(multigraph=False)
            target_graph.add_nodes_from(range(8))
            weighted_edge_list = [
                (0, 1, 1.0 + 1.0j),
                (2, 3, 1.0 + 1.0j),
                (4, 5, 1.0 + 1.0j),
                (6, 7, 1.0 + 1.0j),
                (0, 2, 0.0),
                (1, 3, 0.0),
                (4, 6, 0.0),
                (5, 7, 0.0),
                (0, 4, -2.0 - 2.0j),
                (1, 5, -2.0 - 2.0j),
                (2, 6, -2.0 - 2.0j),
                (3, 7, -2.0 - 2.0j),
                (0, 0, 5.0),
                (1, 1, 5.0),
                (2, 2, 5.0),
                (3, 3, 5.0),
                (4, 4, 5.0),
                (5, 5, 5.0),
                (6, 6, 5.0),
                (7, 7, 5.0),
            ]
            target_graph.add_edges_from(weighted_edge_list)
            self.assertTrue(
                is_isomorphic(hyper_cubic.graph,
                              target_graph,
                              edge_matcher=lambda x, y: x == y))

        with self.subTest("Check the number of nodes."):
            self.assertEqual(hyper_cubic.num_nodes, 8)

        with self.subTest("Check the set of nodes."):
            self.assertSetEqual(set(hyper_cubic.node_indexes), set(range(8)))

        with self.subTest("Check the set of weights."):
            target_set = {
                (0, 1, 1.0 + 1.0j),
                (2, 3, 1.0 + 1.0j),
                (4, 5, 1.0 + 1.0j),
                (6, 7, 1.0 + 1.0j),
                (0, 2, 0.0),
                (1, 3, 0.0),
                (4, 6, 0.0),
                (5, 7, 0.0),
                (0, 4, -2.0 - 2.0j),
                (1, 5, -2.0 - 2.0j),
                (2, 6, -2.0 - 2.0j),
                (3, 7, -2.0 - 2.0j),
                (0, 0, 5.0),
                (1, 1, 5.0),
                (2, 2, 5.0),
                (3, 3, 5.0),
                (4, 4, 5.0),
                (5, 5, 5.0),
                (6, 6, 5.0),
                (7, 7, 5.0),
            }
            self.assertSetEqual(set(hyper_cubic.weighted_edge_list),
                                target_set)

        with self.subTest("Check the adjacency matrix."):
            target_matrix = np.array([
                [5.0, 1.0 + 1.0j, 0.0, 0.0, -2.0 - 2.0j, 0.0, 0.0, 0.0],
                [1.0 - 1.0j, 5.0, 0.0, 0.0, 0.0, -2.0 - 2.0j, 0.0, 0.0],
                [0.0, 0.0, 5.0, 1.0 + 1.0j, 0.0, 0.0, -2.0 - 2.0j, 0.0],
                [0.0, 0.0, 1.0 - 1.0j, 5.0, 0.0, 0.0, 0.0, -2.0 - 2.0j],
                [-2.0 + 2.0j, 0.0, 0.0, 0.0, 5.0, 1.0 + 1.0j, 0.0, 0.0],
                [0.0, -2.0 + 2.0j, 0.0, 0.0, 1.0 - 1.0j, 5.0, 0.0, 0.0],
                [0.0, 0.0, -2.0 + 2.0j, 0.0, 0.0, 0.0, 5.0, 1.0 + 1.0j],
                [0.0, 0.0, 0.0, -2.0 + 2.0j, 0.0, 0.0, 1.0 - 1.0j, 5.0],
            ])

            assert_array_equal(hyper_cubic.to_adjacency_matrix(weighted=True),
                               target_matrix)
    def run(self, dag):
        """run the layout method"""
        if self.target is None and (self.coupling_map is None or self.properties is None):
            raise TranspilerError(
                "A target must be specified or a coupling map and properties must be provided"
            )
        if not self.strict_direction and self.avg_error_map is None:
            self.avg_error_map = vf2_utils.build_average_error_map(
                self.target, self.properties, self.coupling_map
            )
        result = vf2_utils.build_interaction_graph(dag, self.strict_direction)
        if result is None:
            self.property_set["VF2PostLayout_stop_reason"] = VF2PostLayoutStopReason.MORE_THAN_2Q
            return
        im_graph, im_graph_node_map, reverse_im_graph_node_map = result

        if self.target is not None:
            if self.strict_direction:
                cm_graph = PyDiGraph(multigraph=False)
            else:
                cm_graph = PyGraph(multigraph=False)
            cm_graph.add_nodes_from(
                [self.target.operation_names_for_qargs((i,)) for i in range(self.target.num_qubits)]
            )
            for qargs in self.target.qargs:
                len_args = len(qargs)
                # If qargs == 1 we already populated it and if qargs > 2 there are no instructions
                # using those in the circuit because we'd have already returned by this point
                if len_args == 2:
                    cm_graph.add_edge(
                        qargs[0], qargs[1], self.target.operation_names_for_qargs(qargs)
                    )
            cm_nodes = list(cm_graph.node_indexes())
        else:
            cm_graph, cm_nodes = vf2_utils.shuffle_coupling_graph(
                self.coupling_map, self.seed, self.strict_direction
            )

        logger.debug("Running VF2 to find post transpile mappings")
        if self.target and self.strict_direction:
            mappings = vf2_mapping(
                cm_graph,
                im_graph,
                node_matcher=_target_match,
                edge_matcher=_target_match,
                subgraph=True,
                id_order=False,
                induced=False,
                call_limit=self.call_limit,
            )
        else:
            mappings = vf2_mapping(
                cm_graph,
                im_graph,
                subgraph=True,
                id_order=False,
                induced=False,
                call_limit=self.call_limit,
            )
        chosen_layout = None
        initial_layout = Layout(dict(enumerate(dag.qubits)))
        try:
            if self.strict_direction:
                chosen_layout_score = self._score_layout(
                    initial_layout, im_graph_node_map, reverse_im_graph_node_map, im_graph
                )
            else:
                chosen_layout_score = vf2_utils.score_layout(
                    self.avg_error_map,
                    initial_layout,
                    im_graph_node_map,
                    reverse_im_graph_node_map,
                    im_graph,
                    self.strict_direction,
                )
        # Circuit not in basis so we have nothing to compare against return here
        except KeyError:
            self.property_set[
                "VF2PostLayout_stop_reason"
            ] = VF2PostLayoutStopReason.NO_SOLUTION_FOUND
            return

        logger.debug("Initial layout has score %s", chosen_layout_score)

        start_time = time.time()
        trials = 0
        for mapping in mappings:
            trials += 1
            logger.debug("Running trial: %s", trials)
            stop_reason = VF2PostLayoutStopReason.SOLUTION_FOUND
            layout = Layout(
                {reverse_im_graph_node_map[im_i]: cm_nodes[cm_i] for cm_i, im_i in mapping.items()}
            )
            if self.strict_direction:
                layout_score = self._score_layout(
                    layout, im_graph_node_map, reverse_im_graph_node_map, im_graph
                )
            else:
                layout_score = vf2_utils.score_layout(
                    self.avg_error_map,
                    layout,
                    im_graph_node_map,
                    reverse_im_graph_node_map,
                    im_graph,
                    self.strict_direction,
                )
            logger.debug("Trial %s has score %s", trials, layout_score)
            if layout_score < chosen_layout_score:
                logger.debug(
                    "Found layout %s has a lower score (%s) than previous best %s (%s)",
                    layout,
                    layout_score,
                    chosen_layout,
                    chosen_layout_score,
                )
                chosen_layout = layout
                chosen_layout_score = layout_score
            elapsed_time = time.time() - start_time
            if self.time_limit is not None and elapsed_time >= self.time_limit:
                logger.debug(
                    "VFPostLayout has taken %s which exceeds configured max time: %s",
                    elapsed_time,
                    self.time_limit,
                )
                break
        if chosen_layout is None:
            stop_reason = VF2PostLayoutStopReason.NO_SOLUTION_FOUND
        else:
            existing_layout = self.property_set["layout"]
            # If any ancillas in initial layout map them back to the final layout output
            if existing_layout is not None and len(existing_layout) > len(chosen_layout):
                virtual_bits = chosen_layout.get_virtual_bits()
                used_bits = set(virtual_bits.values())
                num_qubits = len(cm_graph)
                for bit in dag.qubits:
                    if len(chosen_layout) == len(existing_layout):
                        break
                    if bit not in virtual_bits:
                        for i in range(num_qubits):
                            if i not in used_bits:
                                used_bits.add(i)
                                chosen_layout.add(bit, i)
                                break
            self.property_set["post_layout"] = chosen_layout

        self.property_set["VF2PostLayout_stop_reason"] = stop_reason