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

QNGOptimizer_Hamiltonian #2524

Merged
merged 10 commits into from
Apr 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@

<h3>Breaking changes</h3>

<h3>Bug fixes</h3>

* Fixed a bug where `QNGOptimizer` did not work with operators
whose generator was a Hamiltonian.
[(#2524)](https://github.com/PennyLaneAI/pennylane/pull/2524)


<h3>Deprecations</h3>

<h3>Documentation</h3>
Expand All @@ -46,4 +53,5 @@

This release contains contributions from (in alphabetical order):

Utkarsh Azad, Christian Gogolin, Christina Lee
Guillermo Alonso-Linaje, Utkarsh Azad, Christian Gogolin, Christina Lee

16 changes: 16 additions & 0 deletions pennylane/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2478,3 +2478,19 @@ def defines_diagonalizing_gates(obj):
except DiagGatesUndefinedError:
return False
return True


@qml.BooleanFn
def gen_is_multi_term_hamiltonian(obj):
"""Returns ``True`` if an operator has a generator defined and it is a Hamiltonian
with more than one term."""

try:
o = obj.generator()
except (AttributeError, OperatorPropertyUndefined, GeneratorUndefinedError):
return False

if isinstance(o, qml.Hamiltonian):
if len(o.coeffs) > 1:
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
return True
return False
9 changes: 7 additions & 2 deletions pennylane/transforms/tape_expand.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import pennylane as qml
from pennylane.operation import (
has_gen,
gen_is_multi_term_hamiltonian,
has_grad_method,
has_nopar,
has_unitary_gen,
Expand Down Expand Up @@ -136,7 +137,7 @@ def expand_fn(tape, depth=depth, **kwargs):

expand_multipar = create_expand_fn(
depth=10,
stop_at=not_tape | is_measurement | has_nopar | has_gen,
stop_at=not_tape | is_measurement | has_nopar | (has_gen & ~gen_is_multi_term_hamiltonian),
docstring=_expand_multipar_doc,
)

Expand All @@ -159,7 +160,11 @@ def expand_fn(tape, depth=depth, **kwargs):

expand_trainable_multipar = create_expand_fn(
depth=10,
stop_at=not_tape | is_measurement | has_nopar | (~is_trainable) | has_gen,
stop_at=not_tape
| is_measurement
| has_nopar
| (~is_trainable)
| (has_gen & ~gen_is_multi_term_hamiltonian),
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
docstring=_expand_trainable_multipar_doc,
)

Expand Down
25 changes: 25 additions & 0 deletions tests/optimize/test_qng.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ def circuit(params):
assert np.allclose(step1, expected_step)
assert np.allclose(step2, expected_step)

def test_step_and_cost_autograd_with_gen_hamiltonian(self, tol):
"""Test that the correct cost and step is returned via the
step_and_cost method for the QNG optimizer when the generator
of an operator is a Hamiltonian"""
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

dev = qml.device("default.qubit", wires=4)

@qml.qnode(dev)
def circuit(params):
qml.DoubleExcitation(params[0], wires=[0, 1, 2, 3])
qml.RY(params[1], wires=0)
return qml.expval(qml.PauliZ(0))

var = np.array([0.011, 0.012])
opt = qml.QNGOptimizer(stepsize=0.01)

step1, res = opt.step_and_cost(circuit, var)
step2 = opt.step(circuit, var)

expected = circuit(var)
expected_step = var - opt.stepsize * 4 * qml.grad(circuit)(var)
assert np.all(res == expected)
assert np.allclose(step1, expected_step)
assert np.allclose(step2, expected_step)

def test_step_and_cost_with_grad_fn_grouped_input(self, tol):
"""Test that the correct cost and update is returned via the step_and_cost
method for the QNG optimizer when providing an explicit grad_fn.
Expand Down
8 changes: 8 additions & 0 deletions tests/test_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1531,6 +1531,7 @@ class DummyOp(qml.operation.CVOperation):


class TestCriteria:
doubleExcitation = qml.DoubleExcitation(0.1, wires=[0, 1, 2, 3])
rx = qml.RX(qml.numpy.array(0.3, requires_grad=True), wires=1)
stiff_rx = qml.RX(0.3, wires=1)
cnot = qml.CNOT(wires=[1, 0])
Expand All @@ -1555,6 +1556,13 @@ def test_has_grad_method(self):
assert qml.operation.has_grad_method(self.rot)
assert not qml.operation.has_grad_method(self.cnot)

def test_gen_is_multi_term_hamiltonian(self):
"""Test gen_is_multi_term_hamiltonian criterion."""
assert qml.operation.gen_is_multi_term_hamiltonian(self.doubleExcitation)
assert not qml.operation.gen_is_multi_term_hamiltonian(self.cnot)
assert not qml.operation.gen_is_multi_term_hamiltonian(self.rot)
assert not qml.operation.gen_is_multi_term_hamiltonian(self.exp)
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

def test_has_multipar(self):
"""Test has_multipar criterion."""
assert not qml.operation.has_multipar(self.rx)
Expand Down