qml.qaoa.cycle.loss_hamiltonian

loss_hamiltonian(graph)[source]

Calculates the loss Hamiltonian for the maximum-weighted cycle problem.

We consider the problem of selecting a cycle from a graph that has the greatest product of edge weights, as outlined here. The product of weights of a subset of edges in a graph is given by

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

where \(E\) are the edges of the graph, \(x_{ij}\) is a binary number that selects whether to include the edge \((i, j)\) and \(c_{ij}\) is the corresponding edge weight. Our objective is to maximize \(P\), subject to selecting the \(x_{ij}\) so that our subset of edges composes a cycle.

The product of edge weights is maximized by equivalently considering

\[\sum_{(i, j) \in E} x_{ij}\log c_{ij},\]

assuming \(c_{ij} > 0\).

This can be restated as a minimization of the expectation value of the following qubit Hamiltonian:

\[H = \sum_{(i, j) \in E} Z_{ij}\log c_{ij}.\]

where \(Z_{ij}\) is a qubit Pauli-Z matrix acting upon the wire specified by the edge \((i, j)\). Mapping from edges to wires can be achieved using edges_to_wires().

Note

The expectation value of the returned Hamiltonian \(H\) is not equal to \(P\), but minimizing the expectation value of \(H\) is equivalent to maximizing \(P\).

Also note that the returned Hamiltonian does not impose that the selected set of edges is a cycle. This constraint can be enforced using a penalty term or by selecting a QAOA mixer Hamiltonian that only transitions between states that correspond to cycles.

Example

>>> import networkx as nx
>>> g = nx.complete_graph(3).to_directed()
>>> edge_weight_data = {edge: (i + 1) * 0.5 for i, edge in enumerate(g.edges)}
>>> for k, v in edge_weight_data.items():
        g[k[0]][k[1]]["weight"] = v
>>> h = loss_hamiltonian(g)
>>> h
(
    -0.6931471805599453 * Z(0)
  + 0.0 * Z(1)
  + 0.4054651081081644 * Z(2)
  + 0.6931471805599453 * Z(3)
  + 0.9162907318741551 * Z(4)
  + 1.0986122886681098 * Z(5)
)
>>> import rustworkx as rx
>>> g = rx.generators.directed_mesh_graph(3, [0, 1, 2])
>>> edge_weight_data = {edge: (i + 1) * 0.5 for i, edge in enumerate(sorted(g.edge_list()))}
>>> for k, v in edge_weight_data.items():
        g.update_edge(k[0], k[1], {"weight": v})
>>> h = loss_hamiltonian(g)
>>> print(h)
(
    -0.6931471805599453 * Z(0)
  + 0.0 * Z(1)
  + 0.4054651081081644 * Z(2)
  + 0.6931471805599453 * Z(3)
  + 0.9162907318741551 * Z(4)
  + 1.0986122886681098 * Z(5)
)
Parameters

graph (nx.Graph or rx.PyGraph or rx.PyDiGraph) – the graph specifying possible edges

Returns

the loss Hamiltonian

Return type

qml.Hamiltonian

Raises
  • ValueError – if the graph contains self-loops

  • KeyError – if one or more edges do not contain weight data