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

Bugfix: ctrl_decomp_zyz - isclose => allclose #5198

Merged
merged 8 commits into from
Feb 16, 2024
5 changes: 4 additions & 1 deletion doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,11 @@

<h3>Bug fixes 🐛</h3>

* `ctrl_decomp_zyz` is now differentiable.
[(#5198)](https://github.com/PennyLaneAI/pennylane/pull/5198)

* `qml.ops.Pow.matrix()` is now differentiable with TensorFlow with integer exponents.
[(#5178)](https://github.com/PennyLaneAI/pennylane/pull/5178)
[(#5178)](https://github.com/PennyLaneAI/pennylane/pull/5178)

* The `qml.MottonenStatePreparation` template is updated to include a global phase operation.
[(#5166)](https://github.com/PennyLaneAI/pennylane/pull/5166)
Expand Down
1 change: 0 additions & 1 deletion pennylane/drawer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ def unwrap_controls(op):
next_ctrl = op

while hasattr(next_ctrl, "base"):

if _is_controlled(next_ctrl.base):
base_control_wires = getattr(next_ctrl.base, "control_wires", [])
control_wires += base_control_wires
Expand Down
8 changes: 4 additions & 4 deletions pennylane/ops/op_math/controlled_decompositions.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ def decomp_circuit(op):

decomp = []

if not qml.math.isclose(0.0, phi, atol=1e-8, rtol=0):
if not qml.math.allclose(0.0, phi, atol=1e-8, rtol=0):
decomp.append(qml.RZ(phi, wires=target_wire))
if not qml.math.isclose(0.0, theta / 2, atol=1e-8, rtol=0):
if not qml.math.allclose(0.0, theta / 2, atol=1e-8, rtol=0):
decomp.extend(
[
qml.RY(theta / 2, wires=target_wire),
Expand All @@ -205,10 +205,10 @@ def decomp_circuit(op):
)
else:
decomp.append(qml.ctrl(qml.PauliX(wires=target_wire), control=control_wires))
if not qml.math.isclose(0.0, -(phi + omega) / 2, atol=1e-6, rtol=0):
if not qml.math.allclose(0.0, -(phi + omega) / 2, atol=1e-6, rtol=0):
decomp.append(qml.RZ(-(phi + omega) / 2, wires=target_wire))
decomp.append(qml.ctrl(qml.PauliX(wires=target_wire), control=control_wires))
if not qml.math.isclose(0.0, (omega - phi) / 2, atol=1e-8, rtol=0):
if not qml.math.allclose(0.0, (omega - phi) / 2, atol=1e-8, rtol=0):
decomp.append(qml.RZ((omega - phi) / 2, wires=target_wire))

return decomp
Expand Down
1 change: 0 additions & 1 deletion pennylane/ops/op_math/controlled_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,6 @@ def decomposition(self):


def _check_and_convert_control_values(control_values, control_wires):

if isinstance(control_values, str):
# Make sure all values are either 0 or 1
if not set(control_values).issubset({"1", "0"}):
Expand Down
35 changes: 35 additions & 0 deletions tests/ops/op_math/test_controlled_decompositions.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,41 @@ def expected_circuit():

assert np.allclose(res, expected, atol=tol, rtol=0)

@pytest.mark.parametrize("control_wires", ([1], [1, 2], [1, 2, 3]))
def test_decomposition_circuit_gradient(self, control_wires, tol):
"""Tests that the controlled decomposition of a single-qubit operation
behaves as expected in a quantum circuit"""
n_qubits = 4
np.random.seed(1337)

dev = qml.device("default.qubit", wires=n_qubits)
init_state = np.random.rand(2**n_qubits) + 1.0j * np.random.rand(2**n_qubits)
init_state /= np.sqrt(np.dot(np.conj(init_state), init_state))
init_state = np.array(init_state)
target_wires = [0]
control_values = [True] * len(control_wires)

def circuit(p):
qml.StatePrep(init_state, wires=range(n_qubits))
qml.ctrl(
qml.Rot(*p, wires=target_wires),
control_wires,
control_values=control_values,
)
return qml.probs(wires=target_wires)

circ_ad = qml.QNode(circuit, dev, diff_method="adjoint")
circ_ps = qml.QNode(circuit, dev, diff_method="finite-diff")
vincentmr marked this conversation as resolved.
Show resolved Hide resolved
par = qml.numpy.array([0.1234, 0.235, 0.5678])
jac_ad = qml.jacobian(circ_ad)(par)
jac_ps = qml.jacobian(circ_ps)(par)

# different methods must agree
assert jac_ad.size == 2 * 3
assert np.allclose(jac_ad.shape, [2, 3])
assert np.allclose(jac_ad.shape, jac_ps.shape)
assert np.allclose(jac_ad, jac_ps, atol=tol, rtol=0)

@pytest.mark.parametrize("op", su2_ops)
@pytest.mark.parametrize("control_wires", ([1], [1, 2], [1, 2, 3]))
def test_decomposition_matrix(self, op, control_wires, tol):
Expand Down
Loading