예제 #1
0
def maxcut(graph):
    r"""Returns the QAOA cost Hamiltonian and the recommended mixer corresponding to the
    MaxCut problem, for a given graph.

    The goal of the MaxCut problem for a particular graph is to find a partition of nodes into two sets,
    such that the number of edges in the graph with endpoints in different sets is maximized. Formally,
    we wish to find the `cut of the graph <https://en.wikipedia.org/wiki/Cut_(graph_theory)>`__ such
    that the number of edges crossing the cut is maximized.

    The MaxCut Hamiltonian is defined as:

    .. math:: H_C \ = \ \frac{1}{2} \displaystyle\sum_{(i, j) \in E(G)} \big( Z_i Z_j \ - \ \mathbb{I} \big),

    where :math:`G` is a graph, :math:`\mathbb{I}` is the identity, and :math:`Z_i` and :math:`Z_j` are
    the Pauli-Z operators on the :math:`i`-th and :math:`j`-th wire respectively.

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

    .. note::

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

    Args:
        graph (nx.Graph): a graph defining the pairs of wires on which each term of the Hamiltonian acts

    Returns:
        (.Hamiltonian, .Hamiltonian):

    **Example**

    >>> graph = nx.Graph([(0, 1), (1, 2)])
    >>> cost_h, mixer_h = qml.qaoa.maxcut(graph)
    >>> print(cost_h)
    >>> print(mixer_h)
    (-0.5) [I0 I1] + (0.5) [Z0 Z1] + (-0.5) [I1 I2] + (0.5) [Z1 Z2]
    (1.0) [X0] + (1.0) [X1] + (1.0) [X2]
    """

    if not isinstance(graph, nx.Graph):
        raise ValueError(
            "Input graph must be a nx.Graph object, got {}".format(
                type(graph).__name__))

    edges = graph.edges

    coeffs = []
    obs = []

    for node1, node2 in edges:

        obs.append(qml.Identity(node1) @ qml.Identity(node2))
        coeffs.append(-0.5)

        obs.append(qml.PauliZ(node1) @ qml.PauliZ(node2))
        coeffs.append(0.5)

    return (qml.Hamiltonian(coeffs, obs), qaoa.x_mixer(graph.nodes))
예제 #2
0
def maxcut(graph):
    r"""Returns the QAOA cost Hamiltonian and the recommended mixer corresponding to the
    MaxCut problem, for a given graph.

    The goal of the MaxCut problem for a particular graph is to find a partition of nodes into two sets,
    such that the number of edges in the graph with endpoints in different sets is maximized. Formally,
    we wish to find the `cut of the graph <https://en.wikipedia.org/wiki/Cut_(graph_theory)>`__ such
    that the number of edges crossing the cut is maximized.

    The MaxCut cost Hamiltonian is defined as:

    .. math:: H_C \ = \ \frac{1}{2} \displaystyle\sum_{(i, j) \in E(G)} \big( Z_i Z_j \ - \ \mathbb{I} \big),

    where :math:`G` is a graph, :math:`\mathbb{I}` is the identity, and :math:`Z_i` and :math:`Z_j` are
    the Pauli-Z operators on the :math:`i`-th and :math:`j`-th wire respectively.

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

    .. note::

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

    Args:
        graph (nx.Graph): a graph defining the pairs of wires on which each term of the Hamiltonian acts

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

    **Example**

    >>> import networkx as nx
    >>> graph = nx.Graph([(0, 1), (1, 2)])
    >>> cost_h, mixer_h = qml.qaoa.maxcut(graph)
    >>> print(cost_h)
      (-1.0) [I0]
    + (0.5) [Z0 Z1]
    + (0.5) [Z1 Z2]
    >>> print(mixer_h)
      (1) [X0]
    + (1) [X1]
    + (1) [X2]
    """

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

    identity_h = qml.Hamiltonian(
        [-0.5 for e in graph.edges],
        [qml.Identity(e[0]) @ qml.Identity(e[1]) for e in graph.edges])
    H = edge_driver(graph, ["10", "01"]) + identity_h
    # store the valuable information that all observables are in one commuting group
    H.grouping_indices = [list(range(len(H.ops)))]
    return (H, qaoa.x_mixer(graph.nodes))
예제 #3
0
    def test_x_mixer_output(self):
        """Tests that the output of the Pauli-X mixer is correct"""

        wires = range(4)
        mixer_hamiltonian = qaoa.x_mixer(wires)

        mixer_coeffs = mixer_hamiltonian.coeffs
        mixer_ops = [i.name for i in mixer_hamiltonian.ops]
        mixer_wires = [i.wires[0] for i in mixer_hamiltonian.ops]

        assert mixer_coeffs == [1, 1, 1, 1]
        assert mixer_ops == ["PauliX", "PauliX", "PauliX", "PauliX"]
        assert mixer_wires == [Wires(0), Wires(1), Wires(2), Wires(3)]
예제 #4
0
def max_weight_cycle(graph, constrained=True):
    r"""Returns the QAOA cost Hamiltonian and the recommended mixer corresponding to the
    maximum-weighted cycle problem, for a given graph.

    The maximum-weighted cycle problem is defined in the following way (see
    `here <https://1qbit.com/whitepaper/arbitrage/>`__ for more details).
    The product of weights of a subset of edges in a graph is given by

    .. math:: P = \prod_{(i, j) \in E} [(c_{ij} - 1)x_{ij} + 1]

    where :math:`E` are the edges of the graph, :math:`x_{ij}` is a binary number that selects
    whether to include the edge :math:`(i, j)` and :math:`c_{ij}` is the corresponding edge weight.
    Our objective is to maximimize :math:`P`, subject to selecting the :math:`x_{ij}` so that
    our subset of edges composes a `cycle <https://en.wikipedia.org/wiki/Cycle_(graph_theory)>`__.

    Args:
        graph (nx.Graph): the directed graph on which the Hamiltonians are defined
        constrained (bool): specifies the variant of QAOA that is performed (constrained or unconstrained)

    Returns:
        (.Hamiltonian, .Hamiltonian, dict): The cost and mixer Hamiltonians, as well as a dictionary
        mapping from wires to the graph's edges

    .. 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 <https://arxiv.org/abs/1709.03489>`__.

        The maximum weighted cycle cost Hamiltonian for unconstrained QAOA is

        .. math:: H_C = H_{\rm loss}.

        Here, :math:`H_{\rm loss}` is a loss Hamiltonian:

        .. math:: H_{\rm loss} = \sum_{(i, j) \in E} Z_{ij}\log c_{ij}

        where :math:`E` are the edges of the graph and :math:`Z_{ij}` is a qubit Pauli-Z matrix
        acting upon the wire specified by the edge :math:`(i, j)` (see :func:`~.loss_hamiltonian`
        for more details).

        The returned mixer Hamiltonian is :func:`~.cycle_mixer` given by

        .. math:: H_M = \frac{1}{4}\sum_{(i, j)\in E}
                \left(\sum_{k \in V, k\neq i, k\neq j, (i, k) \in E, (k, j) \in E}
                \left[X_{ij}X_{ik}X_{kj} +Y_{ij}Y_{ik}X_{kj} + Y_{ij}X_{ik}Y_{kj} - X_{ij}Y_{ik}Y_{kj}\right]
                \right).

        This mixer provides transitions between collections of cycles, i.e., any subset of edges
        in :math:`E` such that all the graph's nodes :math:`V` have zero net flow
        (see the :func:`~.net_flow_constraint` function).

        .. note::

            **Recommended initialization circuit:**
                Your circuit must prepare a state that corresponds to a cycle (or a superposition
                of cycles). Follow the example code below to see how this is done.

        **Unconstrained**

        The maximum weighted cycle cost Hamiltonian for constrained QAOA is defined as:

        .. math:: H_C \ = H_{\rm loss} + 3 H_{\rm netflow} + 3 H_{\rm outflow}.

        The netflow constraint Hamiltonian :func:`~.net_flow_constraint` is given by

        .. math:: H_{\rm netflow} = \sum_{i \in V} \left((d_{i}^{\rm out} - d_{i}^{\rm in})\mathbb{I} -
                \sum_{j, (i, j) \in E} Z_{ij} + \sum_{j, (j, i) \in E} Z_{ji} \right)^{2},

        where :math:`d_{i}^{\rm out}` and :math:`d_{i}^{\rm in}` are
        the outdegree and indegree, respectively, of node :math:`i`. It is minimized whenever a
        subset of edges in :math:`E` results in zero net flow from each node in :math:`V`.

        The outflow constraint Hamiltonian :func:`~.out_flow_constraint` is given by

        .. math:: H_{\rm outflow} = \sum_{i\in V}\left(d_{i}^{out}(d_{i}^{out} - 2)\mathbb{I}
                - 2(d_{i}^{out}-1)\sum_{j,(i,j)\in E}\hat{Z}_{ij} +
                \left( \sum_{j,(i,j)\in E}\hat{Z}_{ij} \right)^{2}\right).

        It is minimized whenever a subset of edges in :math:`E` results in an outflow of at most one
        from each node in :math:`V`.

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

        .. note::

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

        **Example**

        First set up a simple graph:

        .. code-block:: python

            import pennylane as qml
            import numpy as np
            import networkx as nx

            a = np.random.random((4, 4))
            np.fill_diagonal(a, 0)
            g = nx.DiGraph(a)

        The cost and mixer Hamiltonian as well as the mapping from wires to edges can be loaded
        using:

        >>> cost, mixer, mapping = qml.qaoa.max_weight_cycle(g, constrained=True)

        Since we are using ``constrained=True``, we must ensure that the input state to the QAOA
        algorithm corresponds to a cycle. Consider the mapping:

        >>> mapping
        {0: (0, 1),
         1: (0, 2),
         2: (0, 3),
         3: (1, 0),
         4: (1, 2),
         5: (1, 3),
         6: (2, 0),
         7: (2, 1),
         8: (2, 3),
         9: (3, 0),
         10: (3, 1),
         11: (3, 2)}

        A simple cycle is given by the edges ``(0, 1)`` and ``(1, 0)`` and corresponding wires
        ``0`` and ``3``. Hence, the state :math:`|100100000000\rangle` corresponds to a cycle and
        can be prepared using :class:`~.BasisState` or simple :class:`~.PauliX` rotations on the
        ``0`` and ``3`` wires.
    """
    if not isinstance(graph, nx.Graph):
        raise ValueError("Input graph must be a nx.Graph, got {}".format(
            type(graph).__name__))

    mapping = qaoa.cycle.wires_to_edges(graph)

    if constrained:
        cost_h = qaoa.cycle.loss_hamiltonian(graph)
        cost_h.grouping_indices = [list(range(len(cost_h.ops)))]
        return (cost_h, qaoa.cycle.cycle_mixer(graph), mapping)

    cost_h = qaoa.cycle.loss_hamiltonian(
        graph) + 3 * (qaoa.cycle.net_flow_constraint(graph) +
                      qaoa.cycle.out_flow_constraint(graph))
    mixer_h = qaoa.x_mixer(mapping.keys())

    return (cost_h, mixer_h, mapping)
예제 #5
0
def max_clique(graph, constrained=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): 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):
        raise ValueError("Input graph must be a nx.Graph, got {}".format(
            type(graph).__name__))

    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(nx.complement(graph), 0))

    cost_h = 3 * edge_driver(nx.complement(graph),
                             ["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)
예제 #6
0
def min_vertex_cover(graph, constrained=True):
    r"""Returns the QAOA cost Hamiltonian and the recommended mixer corresponding to the Minimum Vertex Cover problem,
    for a given graph.

    To solve the Minimum Vertex Cover problem, we attempt to find the smallest
    `vertex cover <https://en.wikipedia.org/wiki/Vertex_cover>`__ of a graph --- a collection of vertices such that
    every edge in the graph has one of the vertices as an endpoint.

    Args:
        graph (nx.Graph): 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 Minimum Vertex Cover 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:`G`.

        .. note::

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

        **Unconstrained**

        The Minimum Vertex Cover cost Hamiltonian for unconstrained QAOA is defined as:

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

        where :math:`E(G)` is the set of edges of :math:`G`, :math:`V(G)` is the set of vertices,
        and :math:`Z_i` is the Pauli-Z operator acting on 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):
        raise ValueError("Input graph must be a nx.Graph, got {}".format(
            type(graph).__name__))

    if constrained:
        return (bit_driver(graph.nodes, 0), qaoa.bit_flip_mixer(graph, 1))

    cost_h = 3 * edge_driver(graph, ["11", "10", "01"]) + bit_driver(
        graph.nodes, 0)
    mixer_h = qaoa.x_mixer(graph.nodes)

    return (cost_h, mixer_h)
예제 #7
0
def max_independent_set(graph, constrained=True):
    r"""For a given graph, returns the QAOA cost Hamiltonian and the recommended mixer corresponding to the Maximum Independent Set problem.

    Given some graph :math:`G`, an independent set is a set of vertices such that no pair of vertices in the set
    share a common edge. The Maximum Independent Set problem, is the problem of finding the largest such set.

    Args:
        graph (nx.Graph): 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 Independent Set 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:`G`.

        .. note::

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

        **Unconstrained**

        The Maximum Independent Set cost Hamiltonian for unconstrained QAOA is defined as:

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

        where :math:`E(G)` is the set of edges of :math:`G`, :math:`V(G)` is the set of vertices,
        and :math:`Z_i` is the Pauli-Z operator acting on 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):
        raise ValueError("Input graph must be a nx.Graph, got {}".format(
            type(graph).__name__))

    if constrained:
        return (bit_driver(graph.nodes, 1), qaoa.bit_flip_mixer(graph, 0))

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

    return (cost_h, mixer_h)