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

Fix CommutationChecker for 2q Pauli rotations #13762

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
19 changes: 11 additions & 8 deletions crates/accelerate/src/commutation_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,13 +339,17 @@ impl CommutationChecker {

// For our cache to work correctly, we require the gate's definition to only depend on the
// ``params`` attribute. This cannot be guaranteed for custom gates, so we only check
// the cache for our standard gates, which we know are defined by the ``params`` AND
// that the ``params`` are float-only at this point.
let whitelist = get_standard_gate_names();
let check_cache = whitelist.contains(&first_op.name())
&& whitelist.contains(&second_op.name())
&& first_params.iter().all(|p| matches!(p, Param::Float(_)))
&& second_params.iter().all(|p| matches!(p, Param::Float(_)));
// the cache for
// * gates we know are in the cache (SUPPORTED_OPS), or
// * standard gates with float params (otherwise we cannot cache them)
let standard_gates = get_standard_gate_names();
let is_cachable = |name: &str, params: &[Param]| {
SUPPORTED_OP.contains(name)
|| (standard_gates.contains(&name)
&& params.iter().all(|p| matches!(p, Param::Float(_))))
};
let check_cache = is_cachable(first_op.name(), first_params)
&& is_cachable(second_op.name(), second_params);

if !check_cache {
return self.commute_matmul(
Expand Down Expand Up @@ -666,7 +670,6 @@ fn map_rotation<'a>(
if let Some(gate) = generator {
return (gate, &[], false);
};
return (op, &[], false);
}
(op, params, false)
}
Expand Down
16 changes: 16 additions & 0 deletions releasenotes/notes/fix-commchecker-2q-paulis-bcadef25247c7288.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
fixes:
- |
Fixed a bug in the :class:`.CommutationChecker` which could fail upon checking the commutation
relation of a two-qubit Pauli rotation with a gate that is not in the commutation cache.
For example::

import numpy as np
from qiskit.circuit.library import RXXGate, RGate
from qiskit.circuit.commutation_library import SessionCommutationChecker as scc

res = scc.commute(RGate(2, 2), [1], [], RXXGate(np.pi / 2), [0, 1], [])

This behavior is now resolved and the commutation relation correctly computed.
Fixed `#13742 <https://github.com/Qiskit/qiskit/issues/13742>`__.

14 changes: 14 additions & 0 deletions test/python/circuit/test_commutation_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
PauliGate,
PhaseGate,
Reset,
RGate,
RXGate,
RXXGate,
RYGate,
Expand All @@ -57,6 +58,7 @@
ZGate,
HGate,
UnitaryGate,
UGate,
)
from qiskit.dagcircuit import DAGOpNode

Expand Down Expand Up @@ -483,6 +485,18 @@ def test_nonfloat_param(self):
self.assertTrue(scc.commute(pauli_gate, [0, 1], [], rx_gate_theta, [0], []))
self.assertTrue(scc.commute(rx_gate_theta, [0], [], pauli_gate, [0, 1], []))

def test_2q_pauli_rot_with_non_cached(self):
"""Test the 2q-Pauli rotations with a gate that is not cached."""
x_equiv = UGate(np.pi, -np.pi / 2, np.pi / 2)
self.assertTrue(scc.commute(x_equiv, [0], [], RXXGate(np.pi / 2), [0, 1], []))
self.assertTrue(scc.commute(x_equiv, [1], [], RXXGate(np.pi / 2), [0, 1], []))

something_else = RGate(1, 2)
self.assertTrue(scc.commute(x_equiv, [1], [], RXXGate(np.pi / 2), [0, 1], []))

self.assertFalse(scc.commute(something_else, [1], [], RYYGate(np.pi), [1, 0], []))
self.assertFalse(scc.commute(something_else, [1], [], RYYGate(np.pi), [1, 0], []))


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from ddt import data, ddt

from qiskit.circuit import Parameter, QuantumCircuit
from qiskit.circuit.library import RZGate, UnitaryGate
from qiskit.circuit.library import RZGate, UnitaryGate, U2Gate
from qiskit.quantum_info import Operator
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import CommutativeInverseCancellation
Expand Down Expand Up @@ -890,6 +890,21 @@ def test_max_qubits(self):
new_circuit = passmanager.run(circuit)
self.assertEqual(circuit, new_circuit)

def test_2q_pauli_rot_with_non_cached(self):
"""Test a cached 2q-Pauli rotation with a non-cached gate.

Regression test of #13742.
"""
circuit = QuantumCircuit(2)
circuit.rxx(np.pi / 2, 1, 0)
circuit.append(U2Gate(np.pi / 2, -np.pi), [1])

pm = PassManager(CommutativeInverseCancellation())
tqc = pm.run(circuit)

self.assertEqual(tqc.count_ops().get("u2", 0), 1)
self.assertEqual(tqc.count_ops().get("rxx", 0), 1)


if __name__ == "__main__":
unittest.main()