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))
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))
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)]
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)
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)
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)
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)