Exemple #1
0
    def test_clique(self):
        N = 5
        graph = retworkx.PyGraph()
        graph.extend_from_edge_list([(i, j) for i in range(N) for j in range(N)
                                     if i < j])

        complement_graph = retworkx.complement(graph)
        self.assertEqual(graph.nodes(), complement_graph.nodes())
        self.assertEqual(0, len(complement_graph.edges()))
Exemple #2
0
    def test_multigraph(self):
        graph = retworkx.PyGraph(multigraph=True)
        graph.extend_from_edge_list([(0, 0), (0, 1), (1, 1), (2, 2), (1, 0)])

        expected_graph = retworkx.PyGraph(multigraph=True)
        expected_graph.extend_from_edge_list([(0, 2), (1, 2)])

        complement_graph = retworkx.complement(graph)
        self.assertTrue(
            retworkx.is_isomorphic(
                expected_graph,
                complement_graph,
            ))
Exemple #3
0
    def test_empty(self):
        N = 5
        graph = retworkx.PyGraph()
        graph.add_nodes_from([i for i in range(N)])

        expected_graph = retworkx.PyGraph()
        expected_graph.extend_from_edge_list([(i, j) for i in range(N)
                                              for j in range(N) if i < j])

        complement_graph = retworkx.complement(graph)
        self.assertTrue(
            retworkx.is_isomorphic(
                expected_graph,
                complement_graph,
            ))
Exemple #4
0
    def test_complement(self):
        N = 8
        graph = retworkx.PyGraph()
        graph.extend_from_edge_list([(j, i) for i in range(N) for j in range(N)
                                     if i < j and (i + j) % 3 == 0])

        expected_graph = retworkx.PyGraph()
        expected_graph.extend_from_edge_list([(i, j) for i in range(N)
                                              for j in range(N)
                                              if i < j and (i + j) % 3 != 0])

        complement_graph = retworkx.complement(graph)
        self.assertTrue(
            retworkx.is_isomorphic(
                expected_graph,
                complement_graph,
            ))
Exemple #5
0
 def test_null_graph(self):
     graph = retworkx.PyGraph()
     complement_graph = retworkx.complement(graph)
     self.assertEqual(0, len(complement_graph.nodes()))
     self.assertEqual(0, len(complement_graph.edges()))
Exemple #6
0
def max_clique(graph: Union[nx.Graph, rx.PyGraph], constrained: bool = True):
    r"""Returns the QAOA cost Hamiltonian and the recommended mixer corresponding to the Maximum Clique problem,
    for a given graph.

    The goal of Maximum Clique is to find the largest `clique <https://en.wikipedia.org/wiki/Clique_(graph_theory)>`__ of a
    graph --- the largest subgraph such that all vertices are connected by an edge.

    Args:
        graph (nx.Graph or rx.PyGraph): a graph whose edges define the pairs of vertices on which each term of the Hamiltonian acts
        constrained (bool): specifies the variant of QAOA that is performed (constrained or unconstrained)

    Returns:
        (.Hamiltonian, .Hamiltonian): The cost and mixer Hamiltonians

    .. UsageDetails::

        There are two variations of QAOA for this problem, constrained and unconstrained:

        **Constrained**

        .. note::

            This method of constrained QAOA was introduced by Hadfield, Wang, Gorman, Rieffel, Venturelli, and Biswas
            in arXiv:1709.03489.

        The Maximum Clique cost Hamiltonian for constrained QAOA is defined as:

        .. math:: H_C \ = \ \displaystyle\sum_{v \in V(G)} Z_{v},

        where :math:`V(G)` is the set of vertices of the input graph, and :math:`Z_i` is the Pauli-Z operator
        applied to the :math:`i`-th
        vertex.

        The returned mixer Hamiltonian is :func:`~qaoa.bit_flip_mixer` applied to :math:`\bar{G}`,
        the complement of the graph.

        .. note::

            **Recommended initialization circuit:**
                Each wire in the :math:`|0\rangle` state.

        **Unconstrained**

        The Maximum Clique cost Hamiltonian for unconstrained QAOA is defined as:

        .. math:: H_C \ = \ 3 \sum_{(i, j) \in E(\bar{G})}
                  (Z_i Z_j \ - \ Z_i \ - \ Z_j) \ + \ \displaystyle\sum_{i \in V(G)} Z_i

        where :math:`V(G)` is the set of vertices of the input graph :math:`G`, :math:`E(\bar{G})` is the set of
        edges of the complement of :math:`G`, and :math:`Z_i` is the Pauli-Z operator applied to the
        :math:`i`-th vertex.

        The returned mixer Hamiltonian is :func:`~qaoa.x_mixer` applied to all wires.

        .. note::

            **Recommended initialization circuit:**
                Even superposition over all basis states.

    """

    if not isinstance(graph, (nx.Graph, rx.PyGraph)):
        raise ValueError(
            f"Input graph must be a nx.Graph or rx.PyGraph, got {type(graph).__name__}"
        )

    graph_nodes = graph.nodes()
    graph_complement = (rx.complement(graph) if isinstance(graph, rx.PyGraph)
                        else nx.complement(graph))

    if constrained:
        cost_h = bit_driver(graph_nodes, 1)
        cost_h.grouping_indices = [list(range(len(cost_h.ops)))]
        return (cost_h, qaoa.bit_flip_mixer(graph_complement, 0))

    cost_h = 3 * edge_driver(graph_complement,
                             ["10", "01", "00"]) + bit_driver(graph_nodes, 1)
    mixer_h = qaoa.x_mixer(graph_nodes)

    # store the valuable information that all observables are in one commuting group
    cost_h.grouping_indices = [list(range(len(cost_h.ops)))]

    return (cost_h, mixer_h)