diff --git a/doc/releases/changelog-0.30.0.md b/doc/releases/changelog-0.30.0.md
index 8b3521e54a2..675a135e80f 100644
--- a/doc/releases/changelog-0.30.0.md
+++ b/doc/releases/changelog-0.30.0.md
@@ -209,38 +209,31 @@
[(#3851)](https://github.com/PennyLaneAI/pennylane/pull/3851)
Three decompositions from [arXiv:2302.06377](https://arxiv.org/abs/2302.06377) are provided and
- compare favourably to the already-available ZYZ decomposition:
+ compare favourably to the already-available `qml.ops.ctrl_decomp_zyz`:
```python
wires = [0, 1, 2, 3, 4, 5]
control_wires = wires[1:]
- op = qml.RX(np.pi / 2, wires=0)
+ @qml.qnode(qml.device('default.qubit', wires=6))
+ def circuit():
+ with qml.QueuingManager.stop_recording():
+ # the decomposition does not un-queue the target
+ target = qml.RX(np.pi/2, wires=0)
+ qml.ops.ctrl_decomp_bisect(target, (1, 2, 3, 4, 5))
+ return qml.state()
- with qml.tape.QuantumTape() as tape:
- qml.ctrl(op, control=control_wires)
-
- with qml.tape.QuantumTape() as zyz_tape:
- qml.RZ(np.pi / 2, wires=0)
- qml.RY(np.pi / 4, wires=0)
- qml.MultiControlledX(wires=control_wires + [0], control_values="11111", work_wires=[6, 7, 8])
- qml.RY(-np.pi / 4, wires=0)
- qml.MultiControlledX(wires=control_wires + [0], control_values="11111", work_wires=[6, 7, 8])
- qml.RZ(-np.pi / 2, wires=0)
+ print(qml.draw(circuit, expansion_strategy="device")())
```
- Fewer CNOT gates are used:
-
- ```pycon
- >>> tape.expand(depth=5).specs["gate_types"]["CNOT"]
- 60
- >>> zyz_tape.expand(depth=5).specs["gate_types"]["CNOT"]
- 144
```
-
- The decompositions are applied automatically when expanding tapes or decomposing operations in
- PennyLane, but can also be accessed directly using
- [ctrl_decomp_bisect()](https://docs.pennylane.ai/en/stable/code/api/pennylane.ops.op_math.ctrl_decomp_bisect.html).
+ 0: ──H─╭X──U(M0)─╭X──U(M0)†─╭X──U(M0)─╭X──U(M0)†──H─┤ State
+ 1: ────├●────────│──────────├●────────│─────────────┤ State
+ 2: ────├●────────│──────────├●────────│─────────────┤ State
+ 3: ────╰●────────│──────────╰●────────│─────────────┤ State
+ 4: ──────────────├●───────────────────├●────────────┤ State
+ 5: ──────────────╰●───────────────────╰●────────────┤ State
+ ```
* A new decomposition to `qml.SingleExcitation` has been added that halves the number of
CNOTs required.
@@ -635,6 +628,10 @@ Nothing for this release!
Bug fixes 🐛
+* `ctrl_decomp_bisect` and `ctrl_decomp_zyz` are no longer used by default when decomposing
+ controlled operations due to the presence of a global phase difference in the zyz decomposition of some target operators.
+ [(#4065)](https://github.com/PennyLaneAI/pennylane/pull/4065)
+
* Fixed a bug where `qml.math.dot` returned a numpy array instead of an autograd array, breaking autograd derivatives
in certain circumstances.
[(#4019)](https://github.com/PennyLaneAI/pennylane/pull/4019)
diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py
index 7b8ae13b9ef..8c4a2a628ef 100644
--- a/pennylane/ops/op_math/controlled.py
+++ b/pennylane/ops/op_math/controlled.py
@@ -30,10 +30,6 @@
from pennylane.wires import Wires
from .symbolicop import SymbolicOp
-from .controlled_decompositions import (
- ctrl_decomp_zyz,
- ctrl_decomp_bisect,
-)
def ctrl(op, control, control_values=None, work_wires=None):
@@ -478,8 +474,8 @@ def has_decomposition(self):
return True
if isinstance(self.base, qml.PauliX):
return True
- if len(self.base.wires) == 1 and getattr(self.base, "has_matrix", False):
- return True
+ # if len(self.base.wires) == 1 and getattr(self.base, "has_matrix", False):
+ # return True
if self.base.has_decomposition:
return True
@@ -572,16 +568,16 @@ def _decompose_no_control_values(op: "operation.Operator") -> List["operation.Op
if isinstance(op.base, qml.PauliX):
# has some special case handling of its own for further decomposition
return [qml.MultiControlledX(wires=op.active_wires, work_wires=op.work_wires)]
- if (
- len(op.base.wires) == 1
- and len(op.control_wires) >= 2
- and getattr(op.base, "has_matrix", False)
- and qmlmath.get_interface(*op.data) == "numpy" # as implemented, not differentiable
- ):
- # Bisect algorithms use CNOTs and single qubit unitary
- return ctrl_decomp_bisect(op.base, op.control_wires)
- if len(op.base.wires) == 1 and getattr(op.base, "has_matrix", False):
- return ctrl_decomp_zyz(op.base, op.control_wires)
+ # if (
+ # len(op.base.wires) == 1
+ # and len(op.control_wires) >= 2
+ # and getattr(op.base, "has_matrix", False)
+ # and qmlmath.get_interface(*op.data) == "numpy" # as implemented, not differentiable
+ # ):
+ # Bisect algorithms use CNOTs and single qubit unitary
+ # return ctrl_decomp_bisect(op.base, op.control_wires)
+ # if len(op.base.wires) == 1 and getattr(op.base, "has_matrix", False):
+ # return ctrl_decomp_zyz(op.base, op.control_wires)
if not op.base.has_decomposition:
return None
diff --git a/tests/ops/op_math/test_controlled.py b/tests/ops/op_math/test_controlled.py
index 4a1c500c043..c6c69e15ed8 100644
--- a/tests/ops/op_math/test_controlled.py
+++ b/tests/ops/op_math/test_controlled.py
@@ -1520,6 +1520,7 @@ def test_qubit_unitary(M):
assert not equal_list(list(tape), expected)
+@pytest.mark.xfail
@pytest.mark.parametrize(
"M",
[
diff --git a/tests/ops/op_math/test_controlled_decompositions.py b/tests/ops/op_math/test_controlled_decompositions.py
index 4875ca1a483..53a5d2c8740 100644
--- a/tests/ops/op_math/test_controlled_decompositions.py
+++ b/tests/ops/op_math/test_controlled_decompositions.py
@@ -201,6 +201,7 @@ def test_trivial_ops_in_decomposition(self):
assert len(decomp) == 5
assert all(qml.equal(o, e) for o, e in zip(decomp, expected))
+ @pytest.mark.xfail
@pytest.mark.parametrize("test_expand", [False, True])
def test_zyz_decomp_no_control_values(self, test_expand):
"""Test that the ZYZ decomposition is used for single qubit target operations
@@ -225,6 +226,7 @@ def test_zyz_decomp_no_control_values(self, test_expand):
expected = qml.ops.ctrl_decomp_zyz(base, (0,))
assert equal_list(decomp, expected)
+ @pytest.mark.xfail
@pytest.mark.parametrize("test_expand", [False, True])
def test_zyz_decomp_control_values(self, test_expand):
"""Test that the ZYZ decomposition is used for single qubit target operations
@@ -627,6 +629,7 @@ def expected_circuit():
expected = expected_circuit()
assert np.allclose(res, expected, atol=tol, rtol=tol)
+ @pytest.mark.xfail
@pytest.mark.parametrize("op", zip(gen_ops, gen_ops_best))
@pytest.mark.parametrize("control_wires", cw5)
@pytest.mark.parametrize("all_the_way_from_ctrl", [False, True])