Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QAOA] Adds Built-In MaxCut Cost Hamiltonian #718

Merged
merged 19 commits into from
Jul 31, 2020

Conversation

Lucaman99
Copy link
Contributor

@Lucaman99 Lucaman99 commented Jul 22, 2020

Context:

Part of the new PennyLane QAOA functionality.

Description of the Change:

Adds a built-in MaxCut cost Hamiltonian and tests.

Benefits:

Possible Drawbacks:

Related GitHub Issues:

@codecov
Copy link

codecov bot commented Jul 22, 2020

Codecov Report

Merging #718 into master will increase coverage by 0.01%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #718      +/-   ##
==========================================
+ Coverage   95.47%   95.48%   +0.01%     
==========================================
  Files         109      110       +1     
  Lines        6813     6829      +16     
==========================================
+ Hits         6505     6521      +16     
  Misses        308      308              
Impacted Files Coverage Δ
pennylane/qaoa/__init__.py 100.00% <100.00%> (ø)
pennylane/qaoa/cost.py 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 485677c...e05b762. Read the comment docs.

@ixfoduap ixfoduap self-requested a review July 23, 2020 21:34
Copy link
Contributor

@ixfoduap ixfoduap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just missing usage details

find the cut of the graph such that the number of edges crossing the cut is maximized
(see `Cut (graph theory) <https://en.wikipedia.org/wiki/Cut_(graph_theory)>`__).

Recommended mixer Hamiltonian: ~.qaoa.x_mixer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this! Like a wine pairing in a restaurant menu 🍷 🍝

pennylane/qaoa/cost.py Outdated Show resolved Hide resolved
pennylane/qaoa/cost.py Show resolved Hide resolved
pennylane/qaoa/cost.py Outdated Show resolved Hide resolved
@Lucaman99 Lucaman99 marked this pull request as ready for review July 30, 2020 20:02
Copy link
Member

@josh146 josh146 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @Lucaman99. My main suggestion is with regard to docstring improvements, but the code and tests look great!

Comment on lines 24 to 70
r"""A method that builds a QAOA cost Hamiltonian 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 such that the number of edges crossing the cut is maximized
(see `Cut (graph theory) <https://en.wikipedia.org/wiki/Cut_(graph_theory)>`__).

The MaxCut Hamiltonian is defined as:

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

where :math:`G` is some graph and :math:`Z_i` and :math:`Z_j` are the Pauli-Z operators on the :math:`i`-th and
:math:`j`-th wire respectively.

As one can check, the states :math:`|01\rangle` and
:math:`|10\rangle` (representing a cut) both have eigenvalues with respect to :math:`H_C` of :math:`-1`. One can
also see that :math:`|00\rangle` and :math:`|11\rangle`` (no cut) have eigenvalues of :math:`0`.
Thus, for a given basis state, with each entry of the state vector representing a node of the graph, and :math:`0` and
:math:`1` being the labels of the two partitioned sets, the MaxCut cost Hamiltonian effectively counts the number
of edges crossing the cut and multiplies it by :math:`-1`. Upon minimization, we are left with the basis
state that yields the maximum cut.

Recommended mixer Hamiltonian: ~.qaoa.x_mixer

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:

.. UsageDetails::

The MaxCut cost Hamiltonian can be called as follows:

.. code-block:: python

from pennylane import qaoa
from networkx import Graph

graph = Graph([(0, 1), (1, 2)])
cost_h = qaoa.MaxCut(graph)

>>> print(cost_h)
(-0.5) [I0 I1] + (0.5) [Z0 Z1] + (-0.5) [I1 I2] + (0.5) [Z1 Z2]
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Lucaman99, the docstring might need some work, so that there is a better flow between essential details, examples, and technical details.

I suggest something similar to the following:

Returns the QAOA cost Hamiltonian 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)} Z_i Z_j \ - \ \mathbb{I}

where :math:`G` is some graph and :math:`Z_i` and :math:`Z_j` are the Pauli-Z operators on the
:math:`i`-th and :math:`j`-th wire respectively.

*Recommended mixer Hamiltonian:* :func:`~.qaoa.x_mixer`

*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:

**Example**

>>> graph = nx.Graph([(0, 1), (1, 2)])
>>> cost_h = qml.qaoa.MaxCut(graph) print(cost_h)
(-0.5) [I0 I1] + (0.5) [Z0 Z1] + (-0.5) [I1 I2] + (0.5) [Z1 Z2]

.. UsageDetails::

    <For more advanced examples, advanced usage details, and additional technical details>

I based this on the documentation guidelines. Breaking this down part by part:

  • The docstring summary: 'A method that builds' is redundant, and likely to be confused with class methods. Better to just start with 'Returns ...' or 'Creates...'.

  • Rather than using '(see )', it flows a bit better to have the actual term ('cut of the graph') link to the resource. Think of how Wikipedia articles do it 🙂 Aside from this, the introduction and latex descpription are great.

  • The paragraph that begins 'As one can check, ..' is more of an aside, and not essential for the top level docstring. I would suggest moving this down into 'Usage details'.

  • To create links to code objects in general text, you need to use Sphinx domains, e.g.,

    :class:`~.BaseQNode`, :func:`~.StronglyEntanglingLayers`

    The only exception is on the left hand side of the colon in the Args: and Returns: sections.

  • ~ is a formatting specifier for links (it instructs Sphinx to display only the name of the object in the link, not the full absolute path. E.g., .Hamiltonian would display as pennylane.Hamiltonian, whereas ~.Hamiltonian will display just as Hamiltonian. Since it is only for formatting, it shouldn't be used when specifying types on the left hand side of the colon in the Args: and Returns: sections.

  • All docstrings should include a top-level minimal example below Returns:. This example can assume standard imports (so no need to write import networkx as nx and import pennylane as qml), and should show a basic working use-case of the function. This example should always use Python console syntax, and not a .. code-block::.

  • Finally, the 'Usage Details' section is optional, but the place to put any more advanced technical details, usage details, and bigger examples. For example, a lot of the templates use the UsageDetails section to add more usage examples. This section is collapsed by default, leaving only the top-level example visible, to avoid overwhelming the user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josh146 I see! In that case, I think it should be Ok to just have an example and omit the "Usage Details"!

tests/test_qaoa.py Outdated Show resolved Hide resolved
tests/test_qaoa.py Outdated Show resolved Hide resolved

for node1, node2 in edges:

obs.append(qml.Identity(node1) @ qml.Identity(node2))
Copy link
Member

@josh146 josh146 Jul 31, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something that just crossed my mind, is that if/when the Hamiltonian is being used as an observable (not for time evolution), the expectation value of these terms is simply <I>=1, and we don't even need to create a QNode for this term.

On the other hand, it will be needed for doing time evolution...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right, we thought it was worthwhile since the Hamiltonian may be used in both regards.

Lucaman99 and others added 4 commits July 31, 2020 01:08
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Copy link
Member

@josh146 josh146 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 🏅

Copy link
Contributor

@trbromley trbromley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Lucaman99, looks good and have left some comments.

pennylane/qaoa/cost.py Outdated Show resolved Hide resolved
pennylane/qaoa/cost.py Outdated Show resolved Hide resolved
pennylane/qaoa/cost.py Outdated Show resolved Hide resolved
pennylane/qaoa/cost.py Outdated Show resolved Hide resolved
tests/test_qaoa.py Outdated Show resolved Hide resolved
Comment on lines 152 to 192
[
(
Graph([(0, 1), (1, 2)]),
qml.Hamiltonian(
[-0.5, 0.5, -0.5, 0.5],
[
qml.Identity(0) @ qml.Identity(1),
qml.PauliZ(0) @ qml.PauliZ(1),
qml.Identity(1) @ qml.Identity(2),
qml.PauliZ(1) @ qml.PauliZ(2),
],
),
),
(
Graph((np.array([0, 1]), np.array([1, 2]), np.array([0, 2]))),
qml.Hamiltonian(
[-0.5, 0.5, -0.5, 0.5, -0.5, 0.5],
[
qml.Identity(0) @ qml.Identity(1),
qml.PauliZ(0) @ qml.PauliZ(1),
qml.Identity(0) @ qml.Identity(2),
qml.PauliZ(0) @ qml.PauliZ(2),
qml.Identity(1) @ qml.Identity(2),
qml.PauliZ(1) @ qml.PauliZ(2),
],
),
),
(
graph,
qml.Hamiltonian(
[-0.5, 0.5, -0.5, 0.5],
[
qml.Identity(0) @ qml.Identity(1),
qml.PauliZ(0) @ qml.PauliZ(1),
qml.Identity(1) @ qml.Identity(2),
qml.PauliZ(1) @ qml.PauliZ(2),
],
),
),
],
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comment:
It might be nice to build this up earlier in TestCostHamiltonians and then pass to parametrize(), e.g.,

  • Create graphs
  • Create hamiltonians
  • Create list of pairs

^ Just might help with readability.

pennylane/qaoa/cost.py Outdated Show resolved Hide resolved
tests/test_qaoa.py Outdated Show resolved Hide resolved
tests/test_qaoa.py Show resolved Hide resolved
pennylane/qaoa/cost.py Outdated Show resolved Hide resolved
@ixfoduap ixfoduap merged commit 51dd8b4 into PennyLaneAI:master Jul 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants