Skip to content

Commit

Permalink
Fix "Xa*Zb*Zb*Zb works but Xa*Zb**3" (#6618)
Browse files Browse the repository at this point in the history
  • Loading branch information
rodolfocarobene committed Jun 3, 2024
1 parent 16de069 commit 543d9cd
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 0 deletions.
21 changes: 21 additions & 0 deletions cirq-core/cirq/ops/pauli_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,23 @@ def _validate_qubit_mapping(
)


def _try_interpret_as_pauli_string(op: Any):
"""Return a reprepresentation of an operation as a pauli string, if it is possible."""
if isinstance(op, gate_operation.GateOperation):
gates = {
common_gates.XPowGate: pauli_gates.X,
common_gates.YPowGate: pauli_gates.Y,
common_gates.ZPowGate: pauli_gates.Z,
}
if (pauli := gates.get(type(op.gate), None)) is not None:
exponent = op.gate.exponent # type: ignore
if exponent % 2 == 0:
return cirq.PauliString()
if exponent % 2 == 1:
return pauli.on(op.qubits[0])
return None


# Ignoring type because mypy believes `with_qubits` methods are incompatible.
class SingleQubitPauliStringGateOperation( # type: ignore
gate_operation.GateOperation, PauliString
Expand Down Expand Up @@ -1159,11 +1176,15 @@ def __mul__(self, other):
return self._as_pauli_string() * other._as_pauli_string()
if isinstance(other, (PauliString, complex, float, int)):
return self._as_pauli_string() * other
if (as_pauli_string := _try_interpret_as_pauli_string(other)) is not None:
return self * as_pauli_string
return NotImplemented

def __rmul__(self, other):
if isinstance(other, (PauliString, complex, float, int)):
return other * self._as_pauli_string()
if (as_pauli_string := _try_interpret_as_pauli_string(other)) is not None:
return as_pauli_string * self
return NotImplemented

def __neg__(self):
Expand Down
23 changes: 23 additions & 0 deletions cirq-core/cirq/ops/pauli_string_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,29 @@ def test_list_op_constructor_matches_mapping(pauli):
assert cirq.PauliString([op]) == cirq.PauliString({q0: pauli})


@pytest.mark.parametrize('pauli1', (cirq.X, cirq.Y, cirq.Z))
@pytest.mark.parametrize('pauli2', (cirq.X, cirq.Y, cirq.Z))
def test_exponent_mul_consistency(pauli1, pauli2):
a, b = cirq.LineQubit.range(2)
op_a, op_b = pauli1(a), pauli2(b)

assert op_a * op_a * op_a == op_a
assert op_a * op_a**2 == op_a
assert op_a**2 * op_a == op_a
assert op_b * op_a * op_a == op_b
assert op_b * op_a**2 == op_b
assert op_a**2 * op_b == op_b
assert op_a * op_a * op_a * op_a == cirq.PauliString()
assert op_a * op_a**3 == cirq.PauliString()
assert op_b * op_a * op_a * op_a == op_b * op_a
assert op_b * op_a**3 == op_b * op_a

op_a, op_b = pauli1(a), pauli2(a)

assert op_a * op_b**3 == op_a * op_b * op_b * op_b
assert op_b**3 * op_a == op_b * op_b * op_b * op_a


def test_constructor_flexibility():
a, b = cirq.LineQubit.range(2)
with pytest.raises(TypeError, match='cirq.PAULI_STRING_LIKE'):
Expand Down

0 comments on commit 543d9cd

Please sign in to comment.