Skip to content

Commit 70d1173

Browse files
StrilancCirqBot
authored andcommitted
Make PauliString powers produce a PauliStringPhasor (#1508)
- move PauliStringPhasor out of contrib - refactor PauliStringPhasor to have separate phase exponents for both eigenspaces - Implement `__pow__` and `__rpow__` and numpy's exp method on PauliString - Single-qubit Pauli strings turn into X/Y/ZPowGate when raising/exponentiating ~~Comes after #1504 Part of #1037
1 parent 0e81e53 commit 70d1173

21 files changed

+1025
-696
lines changed

cirq/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@
141141
PauliInteractionGate,
142142
PauliString,
143143
PauliStringExpectation,
144+
PauliStringGateOperation,
145+
PauliStringPhasor,
144146
PauliTransform,
145147
phase_damp,
146148
phase_flip,

cirq/contrib/paulistring/__init__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from cirq.contrib.paulistring.pauli_string_raw_types import (
16-
PauliStringGateOperation,)
17-
18-
from cirq.contrib.paulistring.pauli_string_phasor import (
19-
PauliStringPhasor,)
20-
2115
from cirq.contrib.paulistring.convert_to_pauli_string_phasors import (
2216
ConvertToPauliStringPhasors,)
2317

cirq/contrib/paulistring/clifford_optimize.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
from typing import Tuple, cast
1616

1717
from cirq import ops, circuits
18-
from cirq.contrib.paulistring.pauli_string_phasor import (
19-
PauliStringPhasor)
2018
from cirq.contrib.paulistring.convert_gate_set import (
2119
converted_gate_set)
2220

@@ -31,15 +29,16 @@ def clifford_optimized_circuit(circuit: circuits.Circuit,
3129

3230
all_ops = list(c_cliff.all_operations())
3331

34-
def find_merge_point(start_i: int,
35-
string_op: PauliStringPhasor,
36-
stop_at_cz: bool,
37-
) -> Tuple[int, PauliStringPhasor, int]:
32+
def find_merge_point(
33+
start_i: int,
34+
string_op: ops.PauliStringPhasor,
35+
stop_at_cz: bool,
36+
) -> Tuple[int, ops.PauliStringPhasor, int]:
3837
STOP = 0
3938
CONTINUE = 1
4039
SKIP = 2
4140
def continue_condition(op: ops.Operation,
42-
current_string: PauliStringPhasor,
41+
current_string: ops.PauliStringPhasor,
4342
is_first: bool) -> int:
4443
if (isinstance(op, ops.GateOperation)
4544
and isinstance(op.gate, ops.SingleQubitCliffordGate)):
@@ -48,10 +47,10 @@ def continue_condition(op: ops.Operation,
4847
if (isinstance(op, ops.GateOperation)
4948
and isinstance(op.gate, ops.CZPowGate)):
5049
return STOP if stop_at_cz else CONTINUE
51-
if (isinstance(op, PauliStringPhasor)
52-
and len(op.qubits) == 1
53-
and (op.pauli_string[op.qubits[0]]
54-
== current_string.pauli_string[op.qubits[0]])):
50+
if (isinstance(op, ops.PauliStringPhasor) and
51+
len(op.qubits) == 1 and
52+
(op.pauli_string[op.qubits[0]] == current_string.pauli_string[
53+
op.qubits[0]])):
5554
return SKIP
5655
return STOP
5756

@@ -89,17 +88,17 @@ def try_merge_clifford(cliff_op: ops.GateOperation, start_i: int) -> bool:
8988
trans = remaining_cliff_gate.transform(pauli)
9089
pauli = trans.to
9190
quarter_turns *= -1 if trans.flip else 1
92-
string_op = PauliStringPhasor(
93-
ops.PauliString.from_single(cliff_op.qubits[0], pauli),
94-
half_turns=quarter_turns / 2)
91+
string_op = ops.PauliStringPhasor(ops.PauliString.from_single(
92+
cliff_op.qubits[0], pauli),
93+
exponent_neg=quarter_turns / 2)
9594

9695
merge_i, merge_op, num_passed = find_merge_point(start_i, string_op,
9796
quarter_turns == 2)
9897
assert merge_i > start_i
9998
assert len(merge_op.pauli_string) == 1, 'PauliString length != 1'
10099

101100
qubit, pauli = next(iter(merge_op.pauli_string.items()))
102-
quarter_turns = round(merge_op.half_turns * 2)
101+
quarter_turns = round(merge_op.exponent_relative * 2)
103102
if merge_op.pauli_string.coefficient not in [1, -1]:
104103
raise NotImplementedError("TODO: handle all coefficients.")
105104
quarter_turns *= int(merge_op.pauli_string.coefficient.real)
@@ -128,7 +127,7 @@ def try_merge_clifford(cliff_op: ops.GateOperation, start_i: int) -> bool:
128127
all_ops.insert(merge_i+1,
129128
ops.SingleQubitCliffordGate.Z(other_qubit))
130129
all_ops.insert(merge_i+1, part_cliff_gate(qubit))
131-
elif isinstance(other_op, PauliStringPhasor):
130+
elif isinstance(other_op, ops.PauliStringPhasor):
132131
# Pass over a non-Clifford gate
133132
mod_op = other_op.pass_operations_over(
134133
[part_cliff_gate(qubit)])

cirq/contrib/paulistring/convert_gate_set_test.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import pytest
1616

1717
import cirq
18-
from cirq.contrib.paulistring import PauliStringPhasor
1918

2019
from cirq.contrib.paulistring import converted_gate_set
2120

@@ -24,26 +23,21 @@
2423
(cirq.X(q0), cirq.SingleQubitCliffordGate.X(q0)),
2524
(cirq.Y(q0), cirq.SingleQubitCliffordGate.Y(q0)),
2625
(cirq.Z(q0), cirq.SingleQubitCliffordGate.Z(q0)),
27-
(cirq.X(q0) ** 0.5, cirq.SingleQubitCliffordGate.X_sqrt(q0)),
28-
(cirq.Y(q0) ** 0.5, cirq.SingleQubitCliffordGate.Y_sqrt(q0)),
29-
(cirq.Z(q0) ** 0.5, cirq.SingleQubitCliffordGate.Z_sqrt(q0)),
30-
(cirq.X(q0) ** -0.5, cirq.SingleQubitCliffordGate.X_nsqrt(q0)),
31-
(cirq.Y(q0) ** -0.5, cirq.SingleQubitCliffordGate.Y_nsqrt(q0)),
32-
(cirq.Z(q0) ** -0.5, cirq.SingleQubitCliffordGate.Z_nsqrt(q0)),
33-
34-
(cirq.X(q0) ** 0.25,
35-
PauliStringPhasor(cirq.PauliString.from_single(q0, cirq.X)) ** 0.25),
36-
(cirq.Y(q0) ** 0.25,
37-
PauliStringPhasor(cirq.PauliString.from_single(q0, cirq.Y)) ** 0.25),
38-
(cirq.Z(q0) ** 0.25,
39-
PauliStringPhasor(cirq.PauliString.from_single(q0, cirq.Z)) ** 0.25),
40-
41-
(cirq.X(q0) ** 0, ()),
42-
26+
(cirq.X(q0)**0.5, cirq.SingleQubitCliffordGate.X_sqrt(q0)),
27+
(cirq.Y(q0)**0.5, cirq.SingleQubitCliffordGate.Y_sqrt(q0)),
28+
(cirq.Z(q0)**0.5, cirq.SingleQubitCliffordGate.Z_sqrt(q0)),
29+
(cirq.X(q0)**-0.5, cirq.SingleQubitCliffordGate.X_nsqrt(q0)),
30+
(cirq.Y(q0)**-0.5, cirq.SingleQubitCliffordGate.Y_nsqrt(q0)),
31+
(cirq.Z(q0)**-0.5, cirq.SingleQubitCliffordGate.Z_nsqrt(q0)),
32+
(cirq.X(q0)**0.25,
33+
cirq.PauliStringPhasor(cirq.PauliString.from_single(q0, cirq.X))**0.25),
34+
(cirq.Y(q0)**0.25,
35+
cirq.PauliStringPhasor(cirq.PauliString.from_single(q0, cirq.Y))**0.25),
36+
(cirq.Z(q0)**0.25,
37+
cirq.PauliStringPhasor(cirq.PauliString.from_single(q0, cirq.Z))**0.25),
38+
(cirq.X(q0)**0, ()),
4339
(cirq.CZ(q0, q1), cirq.CZ(q0, q1)),
44-
45-
(cirq.measure(q0, q1, key='key'),
46-
cirq.measure(q0, q1, key='key')),
40+
(cirq.measure(q0, q1, key='key'), cirq.measure(q0, q1, key='key')),
4741
))(cirq.LineQubit(0), cirq.LineQubit(1)))
4842
def test_converts_various_ops(op, expected_ops):
4943
before = cirq.Circuit.from_ops(op)

cirq/contrib/paulistring/convert_to_pauli_string_phasors.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
PointOptimizationSummary,
2323
PointOptimizer,
2424
)
25-
from cirq.contrib.paulistring.pauli_string_phasor import PauliStringPhasor
2625

2726
if TYPE_CHECKING:
2827
# pylint: disable=unused-import
@@ -68,7 +67,8 @@ def _matrix_to_pauli_string_phasors(self,
6867
and linalg.all_near_zero_mod(half_turns, 0.5)):
6968
cliff_gate = ops.SingleQubitCliffordGate.from_quarter_turns(
7069
pauli, round(half_turns * 2))
71-
if out_ops and not isinstance(out_ops[-1], PauliStringPhasor):
70+
if out_ops and not isinstance(out_ops[-1],
71+
ops.PauliStringPhasor):
7272
op = cast(ops.GateOperation, out_ops[-1])
7373
gate = cast(ops.SingleQubitCliffordGate, op.gate)
7474
out_ops[-1] = gate.merged_with(cliff_gate)(qubit)
@@ -78,13 +78,13 @@ def _matrix_to_pauli_string_phasors(self,
7878
else:
7979
pauli_string = ops.PauliString.from_single(qubit, pauli)
8080
out_ops.append(
81-
PauliStringPhasor(pauli_string,
82-
half_turns=round(half_turns, 10)))
81+
ops.PauliStringPhasor(pauli_string,
82+
exponent_neg=round(half_turns, 10)))
8383
return out_ops
8484

8585
def _convert_one(self, op: ops.Operation) -> ops.OP_TREE:
86-
# Don't change if it's already a PauliStringPhasor
87-
if isinstance(op, PauliStringPhasor):
86+
# Don't change if it's already a ops.PauliStringPhasor
87+
if isinstance(op, ops.PauliStringPhasor):
8888
return op
8989

9090
if (self.keep_clifford

cirq/contrib/paulistring/convert_to_pauli_string_phasors_test.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
import pytest
1616

1717
import cirq
18-
from cirq.contrib.paulistring import (
19-
ConvertToPauliStringPhasors, PauliStringPhasor
20-
)
18+
from cirq.contrib.paulistring import ConvertToPauliStringPhasors
2119

2220

2321
def test_convert():
@@ -67,8 +65,7 @@ def test_convert_keep_clifford():
6765
def test_already_converted():
6866
q0 = cirq.LineQubit(0)
6967
circuit = cirq.Circuit.from_ops(
70-
PauliStringPhasor(cirq.PauliString.from_single(q0, cirq.X)),
71-
)
68+
cirq.PauliStringPhasor(cirq.PauliString.from_single(q0, cirq.X)),)
7269
c_orig = cirq.Circuit(circuit)
7370
ConvertToPauliStringPhasors().optimize_circuit(circuit)
7471

cirq/contrib/paulistring/pauli_string_dag.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@
1515
from typing import cast
1616

1717
from cirq import ops, circuits
18-
from cirq.contrib.paulistring.pauli_string_raw_types import (
19-
PauliStringGateOperation)
2018

2119

2220
def pauli_string_reorder_pred(op1: ops.Operation,
2321
op2: ops.Operation) -> bool:
24-
ps1 = cast(PauliStringGateOperation, op1).pauli_string
25-
ps2 = cast(PauliStringGateOperation, op2).pauli_string
22+
ps1 = cast(ops.PauliStringGateOperation, op1).pauli_string
23+
ps2 = cast(ops.PauliStringGateOperation, op2).pauli_string
2624
return ps1.commutes_with(ps2)
2725

2826

cirq/contrib/paulistring/pauli_string_optimize.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
from cirq import circuits, linalg
1818
from cirq.contrib.paulistring.pauli_string_dag import (
1919
pauli_string_dag_from_circuit)
20-
from cirq.contrib.paulistring.pauli_string_raw_types import (
21-
PauliStringGateOperation)
20+
from cirq.ops import PauliStringGateOperation
2221
from cirq.contrib.paulistring.recombine import (
2322
move_pauli_strings_into_circuit)
2423
from cirq.contrib.paulistring.separate import (
@@ -75,5 +74,5 @@ def merge_equal_strings(string_dag: circuits.CircuitDag) -> None:
7574
def remove_negligible_strings(string_dag: circuits.CircuitDag,
7675
atol=1e-8) -> None:
7776
for node in tuple(string_dag.nodes()):
78-
if linalg.all_near_zero_mod(node.val.half_turns, 2, atol=atol):
77+
if linalg.all_near_zero_mod(node.val.exponent_relative, 2, atol=atol):
7978
string_dag.remove_node(node)

cirq/contrib/paulistring/pauli_string_phasor.py

Lines changed: 0 additions & 144 deletions
This file was deleted.

0 commit comments

Comments
 (0)