cirq-core 1.6.0.dev20250625011740__py3-none-any.whl → 1.6.0.dev20250625152617__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cirq-core might be problematic. Click here for more details.
- cirq/_version.py +1 -1
- cirq/_version_test.py +1 -1
- cirq/ops/common_gates.py +50 -1
- cirq/ops/common_gates_test.py +34 -0
- cirq/ops/controlled_gate.py +23 -53
- cirq/ops/controlled_gate_test.py +28 -0
- cirq/ops/global_phase_op.py +27 -0
- cirq/ops/global_phase_op_test.py +20 -0
- cirq/ops/three_qubit_gates.py +4 -12
- cirq/protocols/decompose_protocol.py +7 -0
- cirq/protocols/decompose_protocol_test.py +9 -0
- cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py +131 -36
- cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py +33 -1
- cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +64 -5
- cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +18 -0
- {cirq_core-1.6.0.dev20250625011740.dist-info → cirq_core-1.6.0.dev20250625152617.dist-info}/METADATA +1 -1
- {cirq_core-1.6.0.dev20250625011740.dist-info → cirq_core-1.6.0.dev20250625152617.dist-info}/RECORD +20 -20
- {cirq_core-1.6.0.dev20250625011740.dist-info → cirq_core-1.6.0.dev20250625152617.dist-info}/WHEEL +0 -0
- {cirq_core-1.6.0.dev20250625011740.dist-info → cirq_core-1.6.0.dev20250625152617.dist-info}/licenses/LICENSE +0 -0
- {cirq_core-1.6.0.dev20250625011740.dist-info → cirq_core-1.6.0.dev20250625152617.dist-info}/top_level.txt +0 -0
cirq/_version.py
CHANGED
cirq/_version_test.py
CHANGED
cirq/ops/common_gates.py
CHANGED
|
@@ -37,7 +37,14 @@ import cirq
|
|
|
37
37
|
from cirq import protocols, value
|
|
38
38
|
from cirq._compat import proper_repr
|
|
39
39
|
from cirq._doc import document
|
|
40
|
-
from cirq.ops import
|
|
40
|
+
from cirq.ops import (
|
|
41
|
+
control_values as cv,
|
|
42
|
+
controlled_gate,
|
|
43
|
+
eigen_gate,
|
|
44
|
+
gate_features,
|
|
45
|
+
global_phase_op,
|
|
46
|
+
raw_types,
|
|
47
|
+
)
|
|
41
48
|
from cirq.ops.measurement_gate import MeasurementGate
|
|
42
49
|
from cirq.ops.swap_gates import ISWAP, ISwapPowGate, SWAP, SwapPowGate
|
|
43
50
|
|
|
@@ -235,6 +242,11 @@ class XPowGate(eigen_gate.EigenGate):
|
|
|
235
242
|
return cirq.CCXPowGate(exponent=self._exponent)
|
|
236
243
|
return result
|
|
237
244
|
|
|
245
|
+
def _decompose_with_context_(
|
|
246
|
+
self, qubits: tuple[cirq.Qid, ...], context: cirq.DecompositionContext
|
|
247
|
+
) -> list[cirq.Operation] | NotImplementedType:
|
|
248
|
+
return _extract_phase(self, XPowGate, qubits, context)
|
|
249
|
+
|
|
238
250
|
def _pauli_expansion_(self) -> value.LinearDict[str]:
|
|
239
251
|
if self._dimension != 2:
|
|
240
252
|
return NotImplemented # pragma: no cover
|
|
@@ -487,6 +499,11 @@ class YPowGate(eigen_gate.EigenGate):
|
|
|
487
499
|
f'global_shift={self._global_shift!r})'
|
|
488
500
|
)
|
|
489
501
|
|
|
502
|
+
def _decompose_with_context_(
|
|
503
|
+
self, qubits: tuple[cirq.Qid, ...], context: cirq.DecompositionContext
|
|
504
|
+
) -> list[cirq.Operation] | NotImplementedType:
|
|
505
|
+
return _extract_phase(self, YPowGate, qubits, context)
|
|
506
|
+
|
|
490
507
|
|
|
491
508
|
class Ry(YPowGate):
|
|
492
509
|
r"""A gate with matrix $e^{-i Y t/2}$ that rotates around the Y axis of the Bloch sphere by $t$.
|
|
@@ -699,6 +716,11 @@ class ZPowGate(eigen_gate.EigenGate):
|
|
|
699
716
|
return cirq.CCZPowGate(exponent=self._exponent)
|
|
700
717
|
return result
|
|
701
718
|
|
|
719
|
+
def _decompose_with_context_(
|
|
720
|
+
self, qubits: tuple[cirq.Qid, ...], context: cirq.DecompositionContext
|
|
721
|
+
) -> list[cirq.Operation] | NotImplementedType:
|
|
722
|
+
return _extract_phase(self, ZPowGate, qubits, context)
|
|
723
|
+
|
|
702
724
|
def _qid_shape_(self) -> tuple[int, ...]:
|
|
703
725
|
return (self._dimension,)
|
|
704
726
|
|
|
@@ -1131,6 +1153,11 @@ class CZPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
|
|
|
1131
1153
|
control_qid_shape=result.control_qid_shape + (2,),
|
|
1132
1154
|
)
|
|
1133
1155
|
|
|
1156
|
+
def _decompose_with_context_(
|
|
1157
|
+
self, qubits: tuple[cirq.Qid, ...], context: cirq.DecompositionContext
|
|
1158
|
+
) -> list[cirq.Operation] | NotImplementedType:
|
|
1159
|
+
return _extract_phase(self, CZPowGate, qubits, context)
|
|
1160
|
+
|
|
1134
1161
|
def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.CircuitDiagramInfo:
|
|
1135
1162
|
return protocols.CircuitDiagramInfo(
|
|
1136
1163
|
wire_symbols=('@', '@'), exponent=self._diagram_exponent(args)
|
|
@@ -1486,3 +1513,25 @@ def _phased_x_or_pauli_gate(
|
|
|
1486
1513
|
case 0.5:
|
|
1487
1514
|
return YPowGate(exponent=exponent)
|
|
1488
1515
|
return cirq.ops.PhasedXPowGate(exponent=exponent, phase_exponent=phase_exponent)
|
|
1516
|
+
|
|
1517
|
+
|
|
1518
|
+
def _extract_phase(
|
|
1519
|
+
gate: cirq.EigenGate,
|
|
1520
|
+
gate_class: type,
|
|
1521
|
+
qubits: tuple[cirq.Qid, ...],
|
|
1522
|
+
context: cirq.DecompositionContext,
|
|
1523
|
+
) -> list[cirq.Operation] | NotImplementedType:
|
|
1524
|
+
"""Extracts the global phase field to its own gate, or absorbs it if it has no effect.
|
|
1525
|
+
|
|
1526
|
+
This is for use within the decompose handlers, and will return `NotImplemented` if there is no
|
|
1527
|
+
global phase, implying it is already in its simplest form. It will return a list, with the
|
|
1528
|
+
original op minus any global phase first, and the global phase op second. If the resulting
|
|
1529
|
+
global phase is empty (can happen for example in `XPowGate(global_phase=2/3)**3`), then it is
|
|
1530
|
+
excluded from the return value."""
|
|
1531
|
+
if not context.extract_global_phases or gate.global_shift == 0:
|
|
1532
|
+
return NotImplemented
|
|
1533
|
+
result = [gate_class(exponent=gate.exponent).on(*qubits)]
|
|
1534
|
+
phase_gate = global_phase_op.from_phase_and_exponent(gate.global_shift, gate.exponent)
|
|
1535
|
+
if not phase_gate.is_identity():
|
|
1536
|
+
result.append(phase_gate())
|
|
1537
|
+
return result
|
cirq/ops/common_gates_test.py
CHANGED
|
@@ -1322,3 +1322,37 @@ def test_parameterized_pauli_expansion(gate_type, exponent) -> None:
|
|
|
1322
1322
|
gate_resolved = cirq.resolve_parameters(gate, {'s': 0.5})
|
|
1323
1323
|
pauli_resolved = cirq.resolve_parameters(pauli, {'s': 0.5})
|
|
1324
1324
|
assert cirq.approx_eq(pauli_resolved, cirq.pauli_expansion(gate_resolved))
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
@pytest.mark.parametrize('gate_type', [cirq.XPowGate, cirq.YPowGate, cirq.ZPowGate, cirq.CZPowGate])
|
|
1328
|
+
@pytest.mark.parametrize('exponent', [0, 0.5, 2, 3, -0.5, -2, -3, sympy.Symbol('s')])
|
|
1329
|
+
def test_decompose_with_extracted_phases(gate_type: type, exponent: cirq.TParamVal) -> None:
|
|
1330
|
+
context = cirq.DecompositionContext(cirq.SimpleQubitManager(), extract_global_phases=True)
|
|
1331
|
+
test_shift = 2 / 3 # Interesting because e.g. X(shift=2/3) ** 3 == X with no phase
|
|
1332
|
+
gate = gate_type(exponent=exponent, global_shift=test_shift)
|
|
1333
|
+
op = gate.on(*cirq.LineQubit.range(cirq.num_qubits(gate)))
|
|
1334
|
+
decomposed = cirq.decompose(op, context=context)
|
|
1335
|
+
|
|
1336
|
+
# The first gate should be the original gate, but with shift removed.
|
|
1337
|
+
gate0 = decomposed[0].gate
|
|
1338
|
+
assert isinstance(gate0, gate_type)
|
|
1339
|
+
assert isinstance(gate0, cirq.EigenGate)
|
|
1340
|
+
assert gate0.global_shift == 0
|
|
1341
|
+
assert gate0.exponent == exponent
|
|
1342
|
+
if exponent % 3 == 0:
|
|
1343
|
+
# Since test_shift == 2/3, gate**3 nullifies the phase, leaving only the unphased gate.
|
|
1344
|
+
assert len(decomposed) == 1
|
|
1345
|
+
else:
|
|
1346
|
+
# Other exponents emit a global phase gate to compensate.
|
|
1347
|
+
assert len(decomposed) == 2
|
|
1348
|
+
gate1 = decomposed[1].gate
|
|
1349
|
+
assert isinstance(gate1, cirq.GlobalPhaseGate)
|
|
1350
|
+
assert gate1.coefficient == 1j ** (2 * exponent * test_shift)
|
|
1351
|
+
|
|
1352
|
+
# Sanity check that the decomposition is equivalent to the original.
|
|
1353
|
+
decomposed_circuit = cirq.Circuit(decomposed)
|
|
1354
|
+
if cirq.is_parameterized(exponent):
|
|
1355
|
+
resolver = {'s': -1.234} # arbitrary
|
|
1356
|
+
op = cirq.resolve_parameters(op, resolver)
|
|
1357
|
+
decomposed_circuit = cirq.resolve_parameters(decomposed_circuit, resolver)
|
|
1358
|
+
np.testing.assert_allclose(cirq.unitary(op), cirq.unitary(decomposed_circuit), atol=1e-10)
|
cirq/ops/controlled_gate.py
CHANGED
|
@@ -24,7 +24,6 @@ from cirq.ops import (
|
|
|
24
24
|
control_values as cv,
|
|
25
25
|
controlled_operation as cop,
|
|
26
26
|
diagonal_gate as dg,
|
|
27
|
-
global_phase_op as gp,
|
|
28
27
|
op_tree,
|
|
29
28
|
raw_types,
|
|
30
29
|
)
|
|
@@ -139,12 +138,9 @@ class ControlledGate(raw_types.Gate):
|
|
|
139
138
|
def _qid_shape_(self) -> tuple[int, ...]:
|
|
140
139
|
return self.control_qid_shape + protocols.qid_shape(self.sub_gate)
|
|
141
140
|
|
|
142
|
-
def _decompose_(self, qubits: tuple[cirq.Qid, ...]) -> None | NotImplementedType | cirq.OP_TREE:
|
|
143
|
-
return self._decompose_with_context_(qubits)
|
|
144
|
-
|
|
145
141
|
def _decompose_with_context_(
|
|
146
|
-
self, qubits: tuple[cirq.Qid, ...], context: cirq.DecompositionContext
|
|
147
|
-
) ->
|
|
142
|
+
self, qubits: tuple[cirq.Qid, ...], context: cirq.DecompositionContext
|
|
143
|
+
) -> NotImplementedType | cirq.OP_TREE:
|
|
148
144
|
control_qubits = list(qubits[: self.num_controls()])
|
|
149
145
|
controlled_sub_gate = self.sub_gate.controlled(
|
|
150
146
|
self.num_controls(), self.control_values, self.control_qid_shape
|
|
@@ -152,6 +148,25 @@ class ControlledGate(raw_types.Gate):
|
|
|
152
148
|
# Prefer the subgate controlled version if available
|
|
153
149
|
if self != controlled_sub_gate:
|
|
154
150
|
return controlled_sub_gate.on(*qubits)
|
|
151
|
+
|
|
152
|
+
# Try decomposing the subgate next.
|
|
153
|
+
result = protocols.decompose_once_with_qubits(
|
|
154
|
+
self.sub_gate,
|
|
155
|
+
qubits[self.num_controls() :],
|
|
156
|
+
NotImplemented,
|
|
157
|
+
flatten=False,
|
|
158
|
+
# Extract global phases from decomposition, as controlled phases decompose easily.
|
|
159
|
+
context=context.extracting_global_phases(),
|
|
160
|
+
)
|
|
161
|
+
if result is not NotImplemented:
|
|
162
|
+
return op_tree.transform_op_tree(
|
|
163
|
+
result,
|
|
164
|
+
lambda op: op.controlled_by(
|
|
165
|
+
*qubits[: self.num_controls()], control_values=self.control_values
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Finally try brute-force on the unitary.
|
|
155
170
|
if protocols.has_unitary(self.sub_gate) and all(q.dimension == 2 for q in qubits):
|
|
156
171
|
n_qubits = protocols.num_qubits(self.sub_gate)
|
|
157
172
|
# Case 1: Global Phase (1x1 Matrix)
|
|
@@ -173,54 +188,9 @@ class ControlledGate(raw_types.Gate):
|
|
|
173
188
|
protocols.unitary(self.sub_gate), control_qubits, qubits[-1]
|
|
174
189
|
)
|
|
175
190
|
return invert_ops + decomposed_ops + invert_ops
|
|
176
|
-
if isinstance(self.sub_gate, common_gates.CZPowGate):
|
|
177
|
-
z_sub_gate = common_gates.ZPowGate(exponent=self.sub_gate.exponent)
|
|
178
|
-
num_controls = self.num_controls() + 1
|
|
179
|
-
control_values = self.control_values & cv.ProductOfSums(((1,),))
|
|
180
|
-
control_qid_shape = self.control_qid_shape + (2,)
|
|
181
|
-
controlled_z = (
|
|
182
|
-
z_sub_gate.controlled(
|
|
183
|
-
num_controls=num_controls,
|
|
184
|
-
control_values=control_values,
|
|
185
|
-
control_qid_shape=control_qid_shape,
|
|
186
|
-
)
|
|
187
|
-
if protocols.is_parameterized(self)
|
|
188
|
-
else ControlledGate(
|
|
189
|
-
z_sub_gate,
|
|
190
|
-
num_controls=num_controls,
|
|
191
|
-
control_values=control_values,
|
|
192
|
-
control_qid_shape=control_qid_shape,
|
|
193
|
-
)
|
|
194
|
-
)
|
|
195
|
-
if self != controlled_z:
|
|
196
|
-
result = controlled_z.on(*qubits)
|
|
197
|
-
if self.sub_gate.global_shift == 0:
|
|
198
|
-
return result
|
|
199
|
-
# Reconstruct the controlled global shift of the subgate.
|
|
200
|
-
total_shift = self.sub_gate.exponent * self.sub_gate.global_shift
|
|
201
|
-
phase_gate = gp.GlobalPhaseGate(1j ** (2 * total_shift))
|
|
202
|
-
controlled_phase_op = phase_gate.controlled(
|
|
203
|
-
num_controls=self.num_controls(),
|
|
204
|
-
control_values=self.control_values,
|
|
205
|
-
control_qid_shape=self.control_qid_shape,
|
|
206
|
-
).on(*control_qubits)
|
|
207
|
-
return [result, controlled_phase_op]
|
|
208
|
-
result = protocols.decompose_once_with_qubits(
|
|
209
|
-
self.sub_gate,
|
|
210
|
-
qubits[self.num_controls() :],
|
|
211
|
-
NotImplemented,
|
|
212
|
-
flatten=False,
|
|
213
|
-
context=context,
|
|
214
|
-
)
|
|
215
|
-
if result is NotImplemented:
|
|
216
|
-
return NotImplemented
|
|
217
191
|
|
|
218
|
-
return
|
|
219
|
-
|
|
220
|
-
lambda op: op.controlled_by(
|
|
221
|
-
*qubits[: self.num_controls()], control_values=self.control_values
|
|
222
|
-
),
|
|
223
|
-
)
|
|
192
|
+
# If nothing works, return `NotImplemented`.
|
|
193
|
+
return NotImplemented
|
|
224
194
|
|
|
225
195
|
def on(self, *qubits: cirq.Qid) -> cop.ControlledOperation:
|
|
226
196
|
if len(qubits) == 0:
|
cirq/ops/controlled_gate_test.py
CHANGED
|
@@ -804,3 +804,31 @@ def test_controlled_global_phase_matrix_gate_decomposes(
|
|
|
804
804
|
decomposed = cirq.decompose(cg_matrix(*all_qubits))
|
|
805
805
|
assert not any(isinstance(op.gate, cirq.MatrixGate) for op in decomposed)
|
|
806
806
|
np.testing.assert_allclose(cirq.unitary(cirq.Circuit(decomposed)), cirq.unitary(cg_matrix))
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
@pytest.mark.parametrize('gate_type', [cirq.XPowGate, cirq.YPowGate, cirq.ZPowGate, cirq.CZPowGate])
|
|
810
|
+
@pytest.mark.parametrize('test_shift', np.pi * (np.random.default_rng(324).random(10) * 2 - 1))
|
|
811
|
+
def test_controlled_phase_extracted_before_decomposition(gate_type, test_shift) -> None:
|
|
812
|
+
test_shift = 0.123 # arbitrary
|
|
813
|
+
|
|
814
|
+
shifted_gate = gate_type(global_shift=test_shift).controlled()
|
|
815
|
+
unshifted_gate = gate_type().controlled()
|
|
816
|
+
qs = cirq.LineQubit.range(cirq.num_qubits(shifted_gate))
|
|
817
|
+
shifted_op = shifted_gate.on(*qs)
|
|
818
|
+
unshifted_op = unshifted_gate.on(*qs)
|
|
819
|
+
shifted_decomposition = cirq.decompose(shifted_op)
|
|
820
|
+
unshifted_decomposition = cirq.decompose(unshifted_op)
|
|
821
|
+
|
|
822
|
+
# No brute-force calculation. It's the standard decomposition plus Z for the controlled shift.
|
|
823
|
+
assert shifted_decomposition[:-1] == unshifted_decomposition
|
|
824
|
+
z_op = shifted_decomposition[-1]
|
|
825
|
+
assert z_op.qubits == (qs[0],)
|
|
826
|
+
z = z_op.gate
|
|
827
|
+
assert isinstance(z, cirq.ZPowGate)
|
|
828
|
+
np.testing.assert_approx_equal(z.exponent, test_shift)
|
|
829
|
+
assert z.global_shift == 0
|
|
830
|
+
|
|
831
|
+
# Sanity check that the decomposition is equivalent
|
|
832
|
+
np.testing.assert_allclose(
|
|
833
|
+
cirq.unitary(cirq.Circuit(shifted_decomposition)), cirq.unitary(shifted_op), atol=1e-10
|
|
834
|
+
)
|
cirq/ops/global_phase_op.py
CHANGED
|
@@ -92,6 +92,13 @@ class GlobalPhaseGate(raw_types.Gate):
|
|
|
92
92
|
coefficient = protocols.resolve_parameters(self.coefficient, resolver, recursive)
|
|
93
93
|
return GlobalPhaseGate(coefficient=coefficient)
|
|
94
94
|
|
|
95
|
+
def is_identity(self) -> bool:
|
|
96
|
+
"""Checks if gate is equivalent to an identity.
|
|
97
|
+
|
|
98
|
+
Returns: True if the coefficient is within rounding error of 1.
|
|
99
|
+
"""
|
|
100
|
+
return not protocols.is_parameterized(self._coefficient) and np.isclose(self.coefficient, 1)
|
|
101
|
+
|
|
95
102
|
def controlled(
|
|
96
103
|
self,
|
|
97
104
|
num_controls: int | None = None,
|
|
@@ -122,3 +129,23 @@ def global_phase_operation(
|
|
|
122
129
|
) -> cirq.GateOperation:
|
|
123
130
|
"""Creates an operation that represents a global phase on the state."""
|
|
124
131
|
return GlobalPhaseGate(coefficient, atol)()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def from_phase_and_exponent(
|
|
135
|
+
half_turns: cirq.TParamVal, exponent: cirq.TParamVal
|
|
136
|
+
) -> cirq.GlobalPhaseGate:
|
|
137
|
+
"""Creates a GlobalPhaseGate from the global phase and exponent.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
half_turns: The number of half turns to rotate by.
|
|
141
|
+
exponent: The power to raise the phase to.
|
|
142
|
+
|
|
143
|
+
Returns: A `GlobalPhaseGate` with the corresponding coefficient.
|
|
144
|
+
"""
|
|
145
|
+
coefficient = 1j ** (2 * half_turns * exponent)
|
|
146
|
+
coefficient = (
|
|
147
|
+
complex(coefficient)
|
|
148
|
+
if isinstance(coefficient, sympy.Expr) and coefficient.is_complex
|
|
149
|
+
else coefficient
|
|
150
|
+
)
|
|
151
|
+
return GlobalPhaseGate(coefficient)
|
cirq/ops/global_phase_op_test.py
CHANGED
|
@@ -19,6 +19,7 @@ import pytest
|
|
|
19
19
|
import sympy
|
|
20
20
|
|
|
21
21
|
import cirq
|
|
22
|
+
from cirq.ops import global_phase_op
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def test_init() -> None:
|
|
@@ -304,3 +305,22 @@ def test_global_phase_gate_controlled(coeff, exp) -> None:
|
|
|
304
305
|
assert g.controlled(control_values=xor_control_values) == cirq.ControlledGate(
|
|
305
306
|
g, control_values=xor_control_values
|
|
306
307
|
)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def test_is_identity() -> None:
|
|
311
|
+
g = cirq.GlobalPhaseGate(1)
|
|
312
|
+
assert g.is_identity()
|
|
313
|
+
g = cirq.GlobalPhaseGate(1j)
|
|
314
|
+
assert not g.is_identity()
|
|
315
|
+
g = cirq.GlobalPhaseGate(-1)
|
|
316
|
+
assert not g.is_identity()
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def test_from_phase_and_exponent() -> None:
|
|
320
|
+
g = global_phase_op.from_phase_and_exponent(2.5, 0.5)
|
|
321
|
+
assert g.coefficient == np.exp(1.25j * np.pi)
|
|
322
|
+
a, b = sympy.symbols('a, b')
|
|
323
|
+
g = global_phase_op.from_phase_and_exponent(a, b)
|
|
324
|
+
assert g.coefficient == 1j ** (2 * a * b)
|
|
325
|
+
g = global_phase_op.from_phase_and_exponent(1 / a, a)
|
|
326
|
+
assert g.coefficient == -1
|
cirq/ops/three_qubit_gates.py
CHANGED
|
@@ -106,19 +106,11 @@ class CCZPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
|
|
|
106
106
|
elif not b.is_adjacent(c):
|
|
107
107
|
a, b = b, a
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
exp = self._exponent
|
|
110
|
+
p = common_gates.T**exp
|
|
110
111
|
sweep_abc = [common_gates.CNOT(a, b), common_gates.CNOT(b, c)]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
complex(global_phase)
|
|
114
|
-
if protocols.is_parameterized(global_phase) and global_phase.is_complex
|
|
115
|
-
else global_phase
|
|
116
|
-
)
|
|
117
|
-
global_phase_operation = (
|
|
118
|
-
[global_phase_op.global_phase_operation(global_phase)]
|
|
119
|
-
if protocols.is_parameterized(global_phase) or abs(global_phase - 1.0) > 0
|
|
120
|
-
else []
|
|
121
|
-
)
|
|
112
|
+
global_phase_gate = global_phase_op.from_phase_and_exponent(self.global_shift, exp)
|
|
113
|
+
global_phase_operation = [] if global_phase_gate.is_identity() else [global_phase_gate()]
|
|
122
114
|
return global_phase_operation + [
|
|
123
115
|
p(a),
|
|
124
116
|
p(b),
|
|
@@ -81,9 +81,16 @@ class DecompositionContext:
|
|
|
81
81
|
Args:
|
|
82
82
|
qubit_manager: A `cirq.QubitManager` instance to allocate clean / dirty ancilla qubits as
|
|
83
83
|
part of the decompose protocol.
|
|
84
|
+
extract_global_phases: If set, will extract the global phases from
|
|
85
|
+
`DECOMPOSE_TARGET_GATESET` into independent global phase operations.
|
|
84
86
|
"""
|
|
85
87
|
|
|
86
88
|
qubit_manager: cirq.QubitManager
|
|
89
|
+
extract_global_phases: bool = False
|
|
90
|
+
|
|
91
|
+
def extracting_global_phases(self) -> DecompositionContext:
|
|
92
|
+
"""Returns a copy with the `extract_global_phases` field set."""
|
|
93
|
+
return dataclasses.replace(self, extract_global_phases=True)
|
|
87
94
|
|
|
88
95
|
|
|
89
96
|
class SupportsDecompose(Protocol):
|
|
@@ -445,3 +445,12 @@ def test_decompose_without_context_succeed() -> None:
|
|
|
445
445
|
cirq.ops.CleanQubit(1, prefix='_decompose_protocol'),
|
|
446
446
|
)
|
|
447
447
|
]
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def test_extracting_global_phases() -> None:
|
|
451
|
+
qm = cirq.SimpleQubitManager()
|
|
452
|
+
context = cirq.DecompositionContext(qm)
|
|
453
|
+
new_context = context.extracting_global_phases()
|
|
454
|
+
assert not context.extract_global_phases
|
|
455
|
+
assert new_context.extract_global_phases
|
|
456
|
+
assert new_context.qubit_manager is qm
|
|
@@ -21,26 +21,31 @@ https://arxiv.org/abs/quant-ph/0406176
|
|
|
21
21
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
|
-
from typing import Callable, Iterable, TYPE_CHECKING
|
|
24
|
+
from typing import Callable, cast, Iterable, TYPE_CHECKING
|
|
25
25
|
|
|
26
26
|
import numpy as np
|
|
27
|
+
from attr import define
|
|
27
28
|
from scipy.linalg import cossin
|
|
28
29
|
|
|
29
30
|
from cirq import ops
|
|
30
31
|
from cirq.circuits.frozen_circuit import FrozenCircuit
|
|
31
32
|
from cirq.linalg import decompositions, predicates
|
|
32
33
|
from cirq.protocols import unitary_protocol
|
|
33
|
-
from cirq.transformers.analytical_decompositions.three_qubit_decomposition import (
|
|
34
|
-
three_qubit_matrix_to_operations,
|
|
35
|
-
)
|
|
36
34
|
from cirq.transformers.analytical_decompositions.two_qubit_to_cz import (
|
|
37
35
|
two_qubit_matrix_to_cz_operations,
|
|
36
|
+
two_qubit_matrix_to_diagonal_and_cz_operations,
|
|
38
37
|
)
|
|
39
38
|
|
|
40
39
|
if TYPE_CHECKING:
|
|
41
40
|
import cirq
|
|
42
41
|
|
|
43
42
|
|
|
43
|
+
@define
|
|
44
|
+
class _TwoQubitGate:
|
|
45
|
+
location: int
|
|
46
|
+
matrix: np.ndarray
|
|
47
|
+
|
|
48
|
+
|
|
44
49
|
def quantum_shannon_decomposition(
|
|
45
50
|
qubits: list[cirq.Qid], u: np.ndarray, atol: float = 1e-8
|
|
46
51
|
) -> Iterable[cirq.Operation]:
|
|
@@ -67,14 +72,12 @@ def quantum_shannon_decomposition(
|
|
|
67
72
|
1. _single_qubit_decomposition
|
|
68
73
|
OR
|
|
69
74
|
(Recursive Case)
|
|
70
|
-
1.
|
|
71
|
-
2. _multiplexed_cossin
|
|
72
|
-
3. _msb_demuxer
|
|
75
|
+
1. _recursive_decomposition
|
|
73
76
|
|
|
74
77
|
Yields:
|
|
75
78
|
A single 2-qubit or 1-qubit operations from OP TREE
|
|
76
79
|
composed from the set
|
|
77
|
-
{ CNOT, rz, ry, ZPowGate }
|
|
80
|
+
{ CNOT, CZ, rz, ry, ZPowGate }
|
|
78
81
|
|
|
79
82
|
Raises:
|
|
80
83
|
ValueError: If the u matrix is non-unitary
|
|
@@ -98,30 +101,92 @@ def quantum_shannon_decomposition(
|
|
|
98
101
|
yield from _single_qubit_decomposition(qubits[0], u)
|
|
99
102
|
return
|
|
100
103
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
# Collect all operations from the recursive decomposition
|
|
105
|
+
shannon_decomp: list[cirq.Operation | list[cirq.Operation]] = [
|
|
106
|
+
*_recursive_decomposition(qubits, u)
|
|
107
|
+
]
|
|
108
|
+
# Separate all 2-qubit generic gates while keeping track of location
|
|
109
|
+
two_qubit_gates = [
|
|
110
|
+
_TwoQubitGate(location=loc, matrix=unitary_protocol.unitary(o))
|
|
111
|
+
for loc, o in enumerate(cast(list[ops.Operation], shannon_decomp))
|
|
112
|
+
if isinstance(o.gate, ops.MatrixGate)
|
|
113
|
+
]
|
|
114
|
+
# Apply case A.2 from Shende et al.
|
|
115
|
+
q0 = qubits[-2]
|
|
116
|
+
q1 = qubits[-1]
|
|
117
|
+
for idx in range(len(two_qubit_gates) - 1, 0, -1):
|
|
118
|
+
diagonal, operations = two_qubit_matrix_to_diagonal_and_cz_operations(
|
|
119
|
+
q0,
|
|
120
|
+
q1,
|
|
121
|
+
two_qubit_gates[idx].matrix,
|
|
122
|
+
allow_partial_czs=True,
|
|
123
|
+
clean_operations=True,
|
|
124
|
+
atol=atol,
|
|
106
125
|
)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
global_phase = _global_phase_difference(
|
|
127
|
+
two_qubit_gates[idx].matrix, [ops.MatrixGate(diagonal)(q0, q1), *operations]
|
|
128
|
+
)
|
|
129
|
+
if not np.isclose(global_phase, 0, atol=atol):
|
|
130
|
+
operations.append(ops.global_phase_operation(np.exp(1j * global_phase)))
|
|
131
|
+
# Replace the generic gate with ops from OP TREE
|
|
132
|
+
shannon_decomp[two_qubit_gates[idx].location] = operations
|
|
133
|
+
# Join the diagonal with the unitary to be decomposed in the next step
|
|
134
|
+
two_qubit_gates[idx - 1].matrix = diagonal @ two_qubit_gates[idx - 1].matrix
|
|
135
|
+
if len(two_qubit_gates) > 0:
|
|
136
|
+
operations = two_qubit_matrix_to_cz_operations(
|
|
137
|
+
q0,
|
|
138
|
+
q1,
|
|
139
|
+
two_qubit_gates[0].matrix,
|
|
140
|
+
allow_partial_czs=True,
|
|
141
|
+
clean_operations=True,
|
|
142
|
+
atol=atol,
|
|
143
|
+
)
|
|
144
|
+
global_phase = _global_phase_difference(two_qubit_gates[0].matrix, operations)
|
|
145
|
+
if not np.isclose(global_phase, 0, atol=atol):
|
|
146
|
+
operations.append(ops.global_phase_operation(np.exp(1j * global_phase)))
|
|
147
|
+
shannon_decomp[two_qubit_gates[0].location] = operations
|
|
148
|
+
# Yield the final operations in order
|
|
149
|
+
yield from cast(Iterable[ops.Operation], ops.flatten_op_tree(shannon_decomp))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _recursive_decomposition(qubits: list[cirq.Qid], u: np.ndarray) -> Iterable[cirq.Operation]:
|
|
153
|
+
"""Recursive step in the quantum shannon decomposition.
|
|
154
|
+
|
|
155
|
+
Decomposes n-qubit unitary into generic 2-qubit gates, CNOT, CZ and 1-qubit gates.
|
|
156
|
+
All generic 2-qubit gates are applied to the two least significant qubits and
|
|
157
|
+
are not decomposed further here.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
qubits: List of qubits in order of significance
|
|
161
|
+
u: Numpy array for unitary matrix representing gate to be decomposed
|
|
162
|
+
|
|
163
|
+
Calls:
|
|
164
|
+
1. _msb_demuxer
|
|
165
|
+
2. _multiplexed_cossin
|
|
166
|
+
3. _msb_demuxer
|
|
167
|
+
|
|
168
|
+
Yields:
|
|
169
|
+
Generic 2-qubit gates or operations from {ry,rz,CNOT,CZ}.
|
|
114
170
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
171
|
+
Raises:
|
|
172
|
+
ValueError: If the u matrix is not of shape (2^n,2^n)
|
|
173
|
+
ValueError: If the u matrix is not of size at least 4
|
|
174
|
+
"""
|
|
175
|
+
n = u.shape[0]
|
|
176
|
+
if n & (n - 1):
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"Expected input matrix u to be a (2^n x 2^n) shaped numpy array, \
|
|
179
|
+
but instead got shape {u.shape}"
|
|
118
180
|
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
181
|
+
|
|
182
|
+
if n <= 2:
|
|
183
|
+
raise ValueError(
|
|
184
|
+
f"Expected input matrix u for recursive step to have size at least 4, \
|
|
185
|
+
but it has size {n}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if n == 4:
|
|
189
|
+
yield ops.MatrixGate(u).on(*qubits)
|
|
125
190
|
return
|
|
126
191
|
|
|
127
192
|
# Perform a cosine-sine (linalg) decomposition on u
|
|
@@ -137,10 +202,30 @@ def quantum_shannon_decomposition(
|
|
|
137
202
|
# Yield ops from multiplexed Ry part
|
|
138
203
|
yield from _multiplexed_cossin(qubits, theta, ops.ry)
|
|
139
204
|
|
|
205
|
+
# Optimization A.1 in Shende et al. - the last CZ gate in the multiplexed Ry part
|
|
206
|
+
# is merged into the generic multiplexor (u1, u2)
|
|
207
|
+
# This gate is CZ(qubits[1], qubits[0]) = CZ(qubits[0], qubits[1])
|
|
208
|
+
# as CZ is symmetric.
|
|
209
|
+
# For the u1⊕u2 multiplexor operator:
|
|
210
|
+
# as u1 is the operator in case qubits[0] = |0>,
|
|
211
|
+
# and u2 is the operator in case qubits[0] = |1>
|
|
212
|
+
# we can represent the merge by phasing u2 with Z ⊗ I
|
|
213
|
+
cz_diag = np.concatenate((np.ones(n >> 2), np.full(n >> 2, -1)))
|
|
214
|
+
u2 = u2 @ np.diag(cz_diag)
|
|
215
|
+
|
|
140
216
|
# Yield ops from decomposition of multiplexed u1/u2 part
|
|
141
217
|
yield from _msb_demuxer(qubits, u1, u2)
|
|
142
218
|
|
|
143
219
|
|
|
220
|
+
def _global_phase_difference(u: np.ndarray, ops: list[cirq.Operation]) -> float:
|
|
221
|
+
"""Returns the difference in global phase between unitary u and
|
|
222
|
+
a list of operations computing u.
|
|
223
|
+
"""
|
|
224
|
+
i, j = np.unravel_index(np.argmax(np.abs(u)), u.shape)
|
|
225
|
+
new_unitary = unitary_protocol.unitary(FrozenCircuit.from_moments(*ops))
|
|
226
|
+
return np.angle(u[i, j]) - np.angle(new_unitary[i, j])
|
|
227
|
+
|
|
228
|
+
|
|
144
229
|
def _single_qubit_decomposition(qubit: cirq.Qid, u: np.ndarray) -> Iterable[cirq.Operation]:
|
|
145
230
|
"""Decomposes single-qubit gate, and returns list of operations, keeping phase invariant.
|
|
146
231
|
|
|
@@ -202,11 +287,14 @@ def _msb_demuxer(
|
|
|
202
287
|
u2: Lower-right quadrant of total unitary to be decomposed (see diagram)
|
|
203
288
|
|
|
204
289
|
Calls:
|
|
205
|
-
1.
|
|
290
|
+
1. _recursive_decomposition
|
|
206
291
|
2. _multiplexed_cossin
|
|
207
|
-
3.
|
|
292
|
+
3. _recursive_decomposition
|
|
208
293
|
|
|
209
|
-
Yields:
|
|
294
|
+
Yields:
|
|
295
|
+
Generic 2-qubit gates on the two least significant qubits,
|
|
296
|
+
CNOT gates with the target not on the two least significant qubits,
|
|
297
|
+
ry or rz
|
|
210
298
|
"""
|
|
211
299
|
# Perform a diagonalization to find values
|
|
212
300
|
u1 = u1.astype(np.complex128)
|
|
@@ -231,7 +319,7 @@ def _msb_demuxer(
|
|
|
231
319
|
# Last term is given by ( I ⊗ W ), demultiplexed
|
|
232
320
|
# Remove most-significant (demuxed) control-qubit
|
|
233
321
|
# Yield operations for QSD on W
|
|
234
|
-
yield from
|
|
322
|
+
yield from _recursive_decomposition(demux_qubits[1:], W)
|
|
235
323
|
|
|
236
324
|
# Use complex phase of d_i to give theta_i (so d_i* gives -theta_i)
|
|
237
325
|
# Observe that middle part looks like Σ_i( Rz(theta_i)⊗|i><i| )
|
|
@@ -239,7 +327,7 @@ def _msb_demuxer(
|
|
|
239
327
|
yield from _multiplexed_cossin(demux_qubits, -np.angle(d), ops.rz)
|
|
240
328
|
|
|
241
329
|
# Yield operations for QSD on V
|
|
242
|
-
yield from
|
|
330
|
+
yield from _recursive_decomposition(demux_qubits[1:], V)
|
|
243
331
|
|
|
244
332
|
|
|
245
333
|
def _nth_gray(n: int) -> int:
|
|
@@ -263,7 +351,7 @@ def _multiplexed_cossin(
|
|
|
263
351
|
Calls:
|
|
264
352
|
No major calls
|
|
265
353
|
|
|
266
|
-
Yields: Single operation from OP TREE from set 1- and 2-qubit gates: {ry,rz,CNOT}
|
|
354
|
+
Yields: Single operation from OP TREE from set 1- and 2-qubit gates: {ry,rz,CNOT,CZ}
|
|
267
355
|
"""
|
|
268
356
|
# Most significant qubit is main qubit with rotation function applied
|
|
269
357
|
main_qubit = cossin_qubits[0]
|
|
@@ -304,4 +392,11 @@ def _multiplexed_cossin(
|
|
|
304
392
|
yield rot_func(rotation).on(main_qubit)
|
|
305
393
|
|
|
306
394
|
# Add a CNOT from the select qubit to the main qubit
|
|
307
|
-
|
|
395
|
+
# Optimization A.1 in Shende et al. - use CZ instead of CNOT for ry rotations
|
|
396
|
+
if rot_func == ops.ry:
|
|
397
|
+
# Don't emit the last gate, as it will be merged into the generic multiplexor
|
|
398
|
+
# in the cosine-sine decomposition
|
|
399
|
+
if j < len(angles) - 1:
|
|
400
|
+
yield ops.CZ(control_qubits[select_qubit], main_qubit)
|
|
401
|
+
else:
|
|
402
|
+
yield ops.CNOT(control_qubits[select_qubit], main_qubit)
|
|
@@ -20,10 +20,12 @@ from scipy.stats import unitary_group
|
|
|
20
20
|
|
|
21
21
|
import cirq
|
|
22
22
|
from cirq.ops import common_gates
|
|
23
|
+
from cirq.testing import random_two_qubit_circuit_with_czs
|
|
23
24
|
from cirq.transformers.analytical_decompositions.quantum_shannon_decomposition import (
|
|
24
25
|
_msb_demuxer,
|
|
25
26
|
_multiplexed_cossin,
|
|
26
27
|
_nth_gray,
|
|
28
|
+
_recursive_decomposition,
|
|
27
29
|
_single_qubit_decomposition,
|
|
28
30
|
quantum_shannon_decomposition,
|
|
29
31
|
)
|
|
@@ -49,6 +51,14 @@ def test_qsd_n_qubit_errors():
|
|
|
49
51
|
cirq.Circuit(quantum_shannon_decomposition(qubits, np.ones((8, 8))))
|
|
50
52
|
|
|
51
53
|
|
|
54
|
+
def test_recursive_decomposition_n_qubit_errors():
|
|
55
|
+
qubits = [cirq.NamedQubit(f'q{i}') for i in range(3)]
|
|
56
|
+
with pytest.raises(ValueError, match="shaped numpy array"):
|
|
57
|
+
cirq.Circuit(_recursive_decomposition(qubits, np.eye(9)))
|
|
58
|
+
with pytest.raises(ValueError, match="size at least 4"):
|
|
59
|
+
cirq.Circuit(_recursive_decomposition(qubits, np.eye(2)))
|
|
60
|
+
|
|
61
|
+
|
|
52
62
|
def test_random_single_qubit_decomposition():
|
|
53
63
|
U = unitary_group.rvs(2)
|
|
54
64
|
qubit = cirq.NamedQubit('q0')
|
|
@@ -80,10 +90,18 @@ def test_multiplexed_cossin():
|
|
|
80
90
|
multiplexed_ry = np.array(multiplexed_ry)
|
|
81
91
|
qubits = [cirq.NamedQubit(f'q{i}') for i in range(2)]
|
|
82
92
|
circuit = cirq.Circuit(_multiplexed_cossin(qubits, [angle_1, angle_2]))
|
|
93
|
+
# Add back the CZ gate removed by the A.1 optimization
|
|
94
|
+
circuit += cirq.CZ(qubits[1], qubits[0])
|
|
83
95
|
# Test return is equal to inital unitary
|
|
84
96
|
assert cirq.approx_eq(multiplexed_ry, circuit.unitary(), atol=1e-9)
|
|
85
97
|
# Test all operations in gate set
|
|
86
|
-
gates = (
|
|
98
|
+
gates = (
|
|
99
|
+
common_gates.Rz,
|
|
100
|
+
common_gates.Ry,
|
|
101
|
+
common_gates.ZPowGate,
|
|
102
|
+
common_gates.CXPowGate,
|
|
103
|
+
common_gates.CZPowGate,
|
|
104
|
+
)
|
|
87
105
|
assert all(isinstance(op.gate, gates) for op in circuit.all_operations())
|
|
88
106
|
|
|
89
107
|
|
|
@@ -203,3 +221,17 @@ def test_qft5():
|
|
|
203
221
|
)
|
|
204
222
|
new_unitary = cirq.unitary(shannon_circuit)
|
|
205
223
|
np.testing.assert_allclose(new_unitary, desired_unitary, atol=1e-6)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_random_circuit_decomposition():
|
|
227
|
+
qubits = cirq.LineQubit.range(3)
|
|
228
|
+
test_circuit = (
|
|
229
|
+
random_two_qubit_circuit_with_czs(3, qubits[0], qubits[1])
|
|
230
|
+
+ random_two_qubit_circuit_with_czs(3, qubits[1], qubits[2])
|
|
231
|
+
+ random_two_qubit_circuit_with_czs(3, qubits[0], qubits[2])
|
|
232
|
+
)
|
|
233
|
+
circuit = cirq.Circuit(quantum_shannon_decomposition(qubits, test_circuit.unitary()))
|
|
234
|
+
# Test return is equal to initial unitary
|
|
235
|
+
assert cirq.approx_eq(test_circuit.unitary(), circuit.unitary(), atol=1e-9)
|
|
236
|
+
# Test all operations have at most 2 qubits.
|
|
237
|
+
assert all(cirq.num_qubits(op) <= 2 for op in circuit.all_operations())
|
|
@@ -26,7 +26,6 @@ from cirq.linalg.decompositions import extract_right_diag, num_cnots_required
|
|
|
26
26
|
from cirq.transformers.analytical_decompositions import single_qubit_decompositions
|
|
27
27
|
from cirq.transformers.eject_phased_paulis import eject_phased_paulis
|
|
28
28
|
from cirq.transformers.eject_z import eject_z
|
|
29
|
-
from cirq.transformers.merge_single_qubit_gates import merge_single_qubit_gates_to_phased_x_and_z
|
|
30
29
|
|
|
31
30
|
if TYPE_CHECKING:
|
|
32
31
|
import cirq
|
|
@@ -81,8 +80,8 @@ def two_qubit_matrix_to_cz_operations(
|
|
|
81
80
|
if clean_operations:
|
|
82
81
|
if not allow_partial_czs:
|
|
83
82
|
# CZ^t is not allowed for any $t$ except $t=1$.
|
|
84
|
-
return _remove_partial_czs_or_fail(cleanup_operations(operations), atol=atol)
|
|
85
|
-
return cleanup_operations(operations)
|
|
83
|
+
return _remove_partial_czs_or_fail(cleanup_operations(operations, atol=atol), atol=atol)
|
|
84
|
+
return cleanup_operations(operations, atol=atol)
|
|
86
85
|
return operations
|
|
87
86
|
|
|
88
87
|
|
|
@@ -182,15 +181,75 @@ def _xx_yy_zz_interaction_via_full_czs(q0: cirq.Qid, q1: cirq.Qid, x: float, y:
|
|
|
182
181
|
yield ops.H(q1)
|
|
183
182
|
|
|
184
183
|
|
|
185
|
-
def cleanup_operations(operations: Sequence[ops.Operation]):
|
|
184
|
+
def cleanup_operations(operations: Sequence[ops.Operation], atol: float = 1e-8):
|
|
185
|
+
operations = _merge_single_qubit_gates(operations, atol=atol)
|
|
186
186
|
circuit = circuits.Circuit(operations)
|
|
187
|
-
circuit = merge_single_qubit_gates_to_phased_x_and_z(circuit)
|
|
188
187
|
circuit = eject_phased_paulis(circuit)
|
|
189
188
|
circuit = eject_z(circuit)
|
|
190
189
|
circuit = circuits.Circuit(circuit.all_operations(), strategy=circuits.InsertStrategy.EARLIEST)
|
|
191
190
|
return list(circuit.all_operations())
|
|
192
191
|
|
|
193
192
|
|
|
193
|
+
def _transform_single_qubit_operations_to_phased_x_and_z(
|
|
194
|
+
operations: Sequence[ops.Operation], atol: float
|
|
195
|
+
) -> Sequence[ops.Operation]:
|
|
196
|
+
"""Transforms operations on the same qubit to a PhasedXPowGate followed by a Z gate.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
operations: sequence of operations on the same qubit
|
|
200
|
+
atol: a limit on the amount of absolute error introduced by the
|
|
201
|
+
transformation.
|
|
202
|
+
Returns:
|
|
203
|
+
A PhasedXPowGate followed by a Z gate. If one the gates is not needed, it will be omitted.
|
|
204
|
+
"""
|
|
205
|
+
u = np.eye(2)
|
|
206
|
+
for op in operations:
|
|
207
|
+
u = protocols.unitary(op) @ u
|
|
208
|
+
return [
|
|
209
|
+
g(op.qubits[0])
|
|
210
|
+
for g in single_qubit_decompositions.single_qubit_matrix_to_phased_x_z(u, atol=atol)
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _merge_single_qubit_gates(
|
|
215
|
+
operations: Sequence[ops.Operation], atol: float
|
|
216
|
+
) -> Sequence[ops.Operation]:
|
|
217
|
+
"""Merge consecutive single qubit gates.
|
|
218
|
+
|
|
219
|
+
Traverses the sequence of operations maintaining a list of consecutive single qubit
|
|
220
|
+
operations for each qubit. When a 2-qubit gate is encountered, it transforms pending
|
|
221
|
+
operations to a PhasedXPowGate followed by a Z gate.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
operations: sequence of operations
|
|
225
|
+
atol: a limit on the amount of absolute error introduced by the
|
|
226
|
+
transformation.
|
|
227
|
+
Returns:
|
|
228
|
+
new sequence of operations after merging gates
|
|
229
|
+
"""
|
|
230
|
+
merged_ops: list[ops.Operation] = []
|
|
231
|
+
pending_ops: dict[tuple[cirq.Qid, ...], list[ops.Operation]] = dict()
|
|
232
|
+
for op in operations:
|
|
233
|
+
if protocols.num_qubits(op) == 2:
|
|
234
|
+
for _, qubit_ops in pending_ops.items():
|
|
235
|
+
merged_ops.extend(
|
|
236
|
+
_transform_single_qubit_operations_to_phased_x_and_z(qubit_ops, atol=atol)
|
|
237
|
+
)
|
|
238
|
+
pending_ops.clear()
|
|
239
|
+
# Add the 2-qubit gate
|
|
240
|
+
merged_ops.append(op)
|
|
241
|
+
elif protocols.num_qubits(op) == 1:
|
|
242
|
+
if op.qubits not in pending_ops:
|
|
243
|
+
pending_ops[op.qubits] = []
|
|
244
|
+
pending_ops[op.qubits].append(op)
|
|
245
|
+
# Merge remaining pending operations
|
|
246
|
+
for _, qubit_ops in pending_ops.items():
|
|
247
|
+
merged_ops.extend(
|
|
248
|
+
_transform_single_qubit_operations_to_phased_x_and_z(qubit_ops, atol=atol)
|
|
249
|
+
)
|
|
250
|
+
return merged_ops
|
|
251
|
+
|
|
252
|
+
|
|
194
253
|
def _kak_decomposition_to_operations(
|
|
195
254
|
q0: cirq.Qid,
|
|
196
255
|
q1: cirq.Qid,
|
|
@@ -261,6 +261,24 @@ def test_decompose_to_diagonal_and_circuit(v) -> None:
|
|
|
261
261
|
cirq.testing.assert_allclose_up_to_global_phase(circuit_unitary, v, atol=2e-6)
|
|
262
262
|
|
|
263
263
|
|
|
264
|
+
@pytest.mark.parametrize(
|
|
265
|
+
"mat, num_czs",
|
|
266
|
+
[
|
|
267
|
+
(cirq.unitary(random_two_qubit_circuit_with_czs(3)), 2),
|
|
268
|
+
(cirq.unitary(random_two_qubit_circuit_with_czs(2)), 2),
|
|
269
|
+
(cirq.unitary(random_two_qubit_circuit_with_czs(1)), 1),
|
|
270
|
+
(cirq.unitary(random_two_qubit_circuit_with_czs(0)), 0),
|
|
271
|
+
],
|
|
272
|
+
)
|
|
273
|
+
def test_decompose_to_diagonal_and_circuit_returns_circuit_with_expected_number_of_czs(
|
|
274
|
+
mat, num_czs
|
|
275
|
+
):
|
|
276
|
+
b, c = cirq.LineQubit.range(2)
|
|
277
|
+
_, ops = two_qubit_matrix_to_diagonal_and_cz_operations(b, c, mat, atol=1e-8)
|
|
278
|
+
circuit = cirq.Circuit(ops)
|
|
279
|
+
assert len(list(circuit.findall_operations_with_gate_type(cirq.CZPowGate))) == num_czs
|
|
280
|
+
|
|
281
|
+
|
|
264
282
|
def test_remove_partial_czs_or_fail() -> None:
|
|
265
283
|
CZ = cirq.CZ(*cirq.LineQubit.range(2))
|
|
266
284
|
assert (
|
{cirq_core-1.6.0.dev20250625011740.dist-info → cirq_core-1.6.0.dev20250625152617.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cirq-core
|
|
3
|
-
Version: 1.6.0.
|
|
3
|
+
Version: 1.6.0.dev20250625152617
|
|
4
4
|
Summary: A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
|
|
5
5
|
Home-page: http://github.com/quantumlib/cirq
|
|
6
6
|
Author: The Cirq Developers
|
{cirq_core-1.6.0.dev20250625011740.dist-info → cirq_core-1.6.0.dev20250625152617.dist-info}/RECORD
RENAMED
|
@@ -4,8 +4,8 @@ cirq/_compat_test.py,sha256=emXpdD5ZvwLRlFAoQB8YatmZyU3b4e9jg6FppMTUhkU,33900
|
|
|
4
4
|
cirq/_doc.py,sha256=BrnoABo1hk5RgB3Cgww4zLHUfiyFny0F1V-tOMCbdaU,2909
|
|
5
5
|
cirq/_import.py,sha256=ixBu4EyGl46Ram2cP3p5eZVEFDW5L2DS-VyTjz4N9iw,8429
|
|
6
6
|
cirq/_import_test.py,sha256=oF4izzOVZLc7NZ0aZHFcGv-r01eiFFt_JORx_x7_D4s,1089
|
|
7
|
-
cirq/_version.py,sha256=
|
|
8
|
-
cirq/_version_test.py,sha256=
|
|
7
|
+
cirq/_version.py,sha256=WaS6yXNBrQkWyNLtZioFkSEnew96llAnYDIuKKhPb-4,1206
|
|
8
|
+
cirq/_version_test.py,sha256=LhXcrh_kn28Wi96pK1rtGPJU3q_VGDIJsTcF1OWhgYg,155
|
|
9
9
|
cirq/conftest.py,sha256=X7yLFL8GLhg2CjPw0hp5e_dGASfvHx1-QT03aUbhKJw,1168
|
|
10
10
|
cirq/json_resolver_cache.py,sha256=hYyG53VJeV61X0oukK5ndZYega8lkL2FyaL1m0j6h5M,13556
|
|
11
11
|
cirq/py.typed,sha256=VFSlmh_lNwnaXzwY-ZuW-C2Ws5PkuDoVgBdNCs0jXJE,63
|
|
@@ -283,12 +283,12 @@ cirq/ops/common_channels.py,sha256=BD8-qJDGITOSP0bJVcWGgjbI5dj2dwFkSzzWb-qgfcc,3
|
|
|
283
283
|
cirq/ops/common_channels_test.py,sha256=Qzw7nDrWgO1GDB7qkN2YUZi3NDswvvBJ9TEnSNakJX4,30755
|
|
284
284
|
cirq/ops/common_gate_families.py,sha256=trK4ZXCKqYahZkyuwaAn-TcjUu7gmI9n9geO8PYiRGE,8606
|
|
285
285
|
cirq/ops/common_gate_families_test.py,sha256=SfIKolQhVIof0uOHljY1QKT9Tu_4WzUsoeCNR2jIOPg,5441
|
|
286
|
-
cirq/ops/common_gates.py,sha256=
|
|
287
|
-
cirq/ops/common_gates_test.py,sha256=
|
|
286
|
+
cirq/ops/common_gates.py,sha256=pnyLK2FurjE6idejtAzgRaawiW-AqiM22w7xMkEpxVs,58221
|
|
287
|
+
cirq/ops/common_gates_test.py,sha256=IcUWxDTTTbis0efXz2Kn0zAdclp9R31fZAfCx3E-BOo,49758
|
|
288
288
|
cirq/ops/control_values.py,sha256=GrNi8YJZSZDCl8Su6Ocimvd1R1SejFJjVu2thcJ8VLI,13346
|
|
289
289
|
cirq/ops/control_values_test.py,sha256=Wyn0nwtcpnJvcPVRHmFGb3PtYxvsbpluA5UbPrG7tIo,13067
|
|
290
|
-
cirq/ops/controlled_gate.py,sha256=
|
|
291
|
-
cirq/ops/controlled_gate_test.py,sha256=
|
|
290
|
+
cirq/ops/controlled_gate.py,sha256=3Hex9AdY6c_DedKoCqqpS4gx9rAgm9KZITbwUBXsoYg,13562
|
|
291
|
+
cirq/ops/controlled_gate_test.py,sha256=jmIOlCx8dC3VId4NynX1ZYy7s7tkLav_d-fjiIZyVh0,29308
|
|
292
292
|
cirq/ops/controlled_operation.py,sha256=l0pjUfru39HBuAbBkRCqJmrJDxah0JOFxXXILcUt0v8,13978
|
|
293
293
|
cirq/ops/controlled_operation_test.py,sha256=qXpnUoeWmOQaTMZPAuuICtX9Idf-JWtdARTsTENv54g,16546
|
|
294
294
|
cirq/ops/dense_pauli_string.py,sha256=1TijNu1D2HIbbnwLbT_f546R2L4OCQtm1bKjqhno1Kg,24234
|
|
@@ -307,8 +307,8 @@ cirq/ops/gate_operation.py,sha256=MF8JIYEM6bQu6ft9Eb19hSOillzu8MmaIoXSlmwbm5U,13
|
|
|
307
307
|
cirq/ops/gate_operation_test.py,sha256=4QwWxCjGXNM__6QGw1kYSbBMh_4783jBZVBJD1ERGPk,18020
|
|
308
308
|
cirq/ops/gateset.py,sha256=9HFW5Sg_jPpao8SEd7XgLdc_6xgUwZfvb6f7pfVPIF8,21492
|
|
309
309
|
cirq/ops/gateset_test.py,sha256=_dpKTEwXuOY-jpev9rBPEcMy4KZ7zd6GmJ5JkvZAeog,16580
|
|
310
|
-
cirq/ops/global_phase_op.py,sha256=
|
|
311
|
-
cirq/ops/global_phase_op_test.py,sha256=
|
|
310
|
+
cirq/ops/global_phase_op.py,sha256=GcERs4X5h5_at6tvJc8-AcM0cVsLCRPlpPkAWMINm54,5711
|
|
311
|
+
cirq/ops/global_phase_op_test.py,sha256=9BBnPZLLmBzHsMoRPMFTAShx87TJnhTLvvgLpHJF4wc,10721
|
|
312
312
|
cirq/ops/greedy_qubit_manager.py,sha256=UTd9cTRbl4GQmf6ai6zqVBn5TR3-Vg84jJu4AN-0cxc,4050
|
|
313
313
|
cirq/ops/greedy_qubit_manager_test.py,sha256=5vSKWsO0q8LwaKJSnOqr0bVQooKt0cwcO7zmPArHRRs,3464
|
|
314
314
|
cirq/ops/identity.py,sha256=jWPE3jWLduXF5JgA0qol2blHXRvFUndClukG6nwGvUE,5862
|
|
@@ -374,7 +374,7 @@ cirq/ops/swap_gates.py,sha256=mEDVB4pdBsbenaOahrNtAcE2B1ZPW-4vGq079rECxf4,11743
|
|
|
374
374
|
cirq/ops/swap_gates_test.py,sha256=8Yee6RgkQahsnB92ZD-rTb9dNqMLXdBKlgWC8qWd2uo,7624
|
|
375
375
|
cirq/ops/tags.py,sha256=nBKqDnPHunxABIOqSAHsVb2hByRAJSfGCJjTC6-AbTY,2307
|
|
376
376
|
cirq/ops/tags_test.py,sha256=EUCAjFYsQ98r6eo8XUNJoQCKMRCc4KvcqXUZAzfPqiE,1210
|
|
377
|
-
cirq/ops/three_qubit_gates.py,sha256=
|
|
377
|
+
cirq/ops/three_qubit_gates.py,sha256=yPzu3xYz9_bKQCzuRyH3YnB8KHEI1qtdH8whBgHhMkU,27911
|
|
378
378
|
cirq/ops/three_qubit_gates_test.py,sha256=r9bO7noLCUIGhSprE-KUTlnrf16IEoAhy93Obi6oL18,11951
|
|
379
379
|
cirq/ops/two_qubit_diagonal_gate.py,sha256=m2wnFpi0sHA9cxMa2cqwRYGQIlbyTGohMTLdVVQZ2-U,5377
|
|
380
380
|
cirq/ops/two_qubit_diagonal_gate_test.py,sha256=pv24VMf2yQ-2gaHg_EUwWx5tAFvXo47_sKzdbdCPhRY,4100
|
|
@@ -399,8 +399,8 @@ cirq/protocols/commutes_protocol.py,sha256=6cJNba3aEsCh_XHIeNTHb0LRzws6ZbxOrKL_r
|
|
|
399
399
|
cirq/protocols/commutes_protocol_test.py,sha256=9YhBFYAwc-XpU7HrQp-GarKwmwmbgyadUYqlkiG10A8,5885
|
|
400
400
|
cirq/protocols/control_key_protocol.py,sha256=uGgfahCHzsFpUGq6flgTMuqPh20zUSB2AOkSrhyoqwQ,2621
|
|
401
401
|
cirq/protocols/control_key_protocol_test.py,sha256=fNDDkf4mQpA_tKuhX1e2BJN72v9HdGftgd79sOqREJE,1014
|
|
402
|
-
cirq/protocols/decompose_protocol.py,sha256=
|
|
403
|
-
cirq/protocols/decompose_protocol_test.py,sha256=
|
|
402
|
+
cirq/protocols/decompose_protocol.py,sha256=zwmrlFmBggInx1u1xZEeBDxaLGRWMGHCp87F7RRQPH4,19129
|
|
403
|
+
cirq/protocols/decompose_protocol_test.py,sha256=s5ELgra5UKExKCjDHUtMOwTZrGmYwxAphCRryRLMM_Q,16456
|
|
404
404
|
cirq/protocols/equal_up_to_global_phase_protocol.py,sha256=y-GPOImHgdjVqXF-qE3SUmlekF6-zI0tgi0E2nTdW1M,4106
|
|
405
405
|
cirq/protocols/equal_up_to_global_phase_protocol_test.py,sha256=EDfWnCuYAVfcvBXHYoZ0lDukNEGG2c53vzP7s8jHLKA,6050
|
|
406
406
|
cirq/protocols/has_stabilizer_effect_protocol.py,sha256=T_CVVpvckp3ZTsWi089mPqbmwOPLlF6GalEKrVK7Hvs,4309
|
|
@@ -1102,8 +1102,8 @@ cirq/transformers/analytical_decompositions/cphase_to_fsim.py,sha256=jFWTFjCQ76c
|
|
|
1102
1102
|
cirq/transformers/analytical_decompositions/cphase_to_fsim_test.py,sha256=6ZDfSTL7aWSXXfM5DToeX8VxMalWirrCGKKg7QKBGFc,5591
|
|
1103
1103
|
cirq/transformers/analytical_decompositions/pauli_string_decomposition.py,sha256=7GKdVnN5jvpjABudB0zE1kUn0BPZmuvRZcM2yLMUoWs,4540
|
|
1104
1104
|
cirq/transformers/analytical_decompositions/pauli_string_decomposition_test.py,sha256=yWHkXXErOxLmnp5t65WUsY9e9iN6L950MZRw5OY3QW0,2172
|
|
1105
|
-
cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py,sha256=
|
|
1106
|
-
cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py,sha256=
|
|
1105
|
+
cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py,sha256=EIrK1PbHRMMhT5wei6rYk8diPEaqEZ0B-9M1chhCfCo,15450
|
|
1106
|
+
cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py,sha256=Bk94HEm9a-cywM_Q2TBF6eUYkSJB1GWwJ5QO1J-rMoA,9006
|
|
1107
1107
|
cirq/transformers/analytical_decompositions/single_qubit_decompositions.py,sha256=6kaQfcXY7je42o-pVprSwgPXCDJXraDXQ9mHyR_BaBk,8428
|
|
1108
1108
|
cirq/transformers/analytical_decompositions/single_qubit_decompositions_test.py,sha256=oh96QXddCm1v8CrYA5mEnnx_ENXwZi89W6paVZU273I,12344
|
|
1109
1109
|
cirq/transformers/analytical_decompositions/single_to_two_qubit_isometry.py,sha256=CRZylmI8nA_aq6vXj0WmMce8PIe8OFCjq4_bqYfkenk,2464
|
|
@@ -1112,8 +1112,8 @@ cirq/transformers/analytical_decompositions/three_qubit_decomposition.py,sha256=
|
|
|
1112
1112
|
cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py,sha256=a21HU4BE82AG2UJU4hUVqymUU7Lw4CLw4-NChX4OpLI,6808
|
|
1113
1113
|
cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py,sha256=2bIpZOHBHxH2mdbJfDpo6nQgpitOI0ZmoH_5l_nA1nU,6167
|
|
1114
1114
|
cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py,sha256=c-vFBZnybd-Ez4rcs13THGGthzEZ0qw9Iw9sAKbE6yc,4494
|
|
1115
|
-
cirq/transformers/analytical_decompositions/two_qubit_to_cz.py,sha256=
|
|
1116
|
-
cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py,sha256=
|
|
1115
|
+
cirq/transformers/analytical_decompositions/two_qubit_to_cz.py,sha256=L2ScuiYW5tbNSc9_DJ4S6Lr_0mpmurVKUzlwLmGdJmw,11513
|
|
1116
|
+
cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py,sha256=PLPQRsBCVwGCgtL7nBY6xdI_CG1zxTdIj7fFGpxjU2U,10704
|
|
1117
1117
|
cirq/transformers/analytical_decompositions/two_qubit_to_fsim.py,sha256=8tYGV41gQBRzJSNUB3bsTMI8vil0TNE4TgqWlEw8BhE,9656
|
|
1118
1118
|
cirq/transformers/analytical_decompositions/two_qubit_to_fsim_test.py,sha256=bX8ap8VskN-gKxn3F4A8_vhgWO0AErOpP1QVVEtBjmY,7073
|
|
1119
1119
|
cirq/transformers/analytical_decompositions/two_qubit_to_ms.py,sha256=dP9umZJBgNJ8FbDFFZVgMSyO-NjWJqXmoWyQuTo--0g,3751
|
|
@@ -1220,8 +1220,8 @@ cirq/work/sampler.py,sha256=rxbMWvrhu3gfNSBjZKozw28lLKVvBAS_1EGyPdYe8Xg,19041
|
|
|
1220
1220
|
cirq/work/sampler_test.py,sha256=SsMrRvLDYELyOAWLKISjkdEfrBwLYWRsT6D8WrsLM3Q,13533
|
|
1221
1221
|
cirq/work/zeros_sampler.py,sha256=Fs2JWwq0n9zv7_G5Rm-9vPeHUag7uctcMOHg0JTkZpc,2371
|
|
1222
1222
|
cirq/work/zeros_sampler_test.py,sha256=lQLgQDGBLtfImryys2HzQ2jOSGxHgc7-koVBUhv8qYk,3345
|
|
1223
|
-
cirq_core-1.6.0.
|
|
1224
|
-
cirq_core-1.6.0.
|
|
1225
|
-
cirq_core-1.6.0.
|
|
1226
|
-
cirq_core-1.6.0.
|
|
1227
|
-
cirq_core-1.6.0.
|
|
1223
|
+
cirq_core-1.6.0.dev20250625152617.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
|
1224
|
+
cirq_core-1.6.0.dev20250625152617.dist-info/METADATA,sha256=fnk4EUEjwRjeZxmyGn_W7MpZMYIIs5bO1ox35RYjOZ0,4857
|
|
1225
|
+
cirq_core-1.6.0.dev20250625152617.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
1226
|
+
cirq_core-1.6.0.dev20250625152617.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
|
|
1227
|
+
cirq_core-1.6.0.dev20250625152617.dist-info/RECORD,,
|
{cirq_core-1.6.0.dev20250625011740.dist-info → cirq_core-1.6.0.dev20250625152617.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|