cirq-core 1.5.0.dev20250119140111__py3-none-any.whl → 1.5.0.dev20250121182620__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 CHANGED
@@ -28,4 +28,4 @@ if sys.version_info < (3, 10, 0): # pragma: no cover
28
28
  'of cirq (e.g. "python -m pip install cirq==1.1.*")'
29
29
  )
30
30
 
31
- __version__ = "1.5.0.dev20250119140111"
31
+ __version__ = "1.5.0.dev20250121182620"
cirq/_version_test.py CHANGED
@@ -3,4 +3,4 @@ import cirq
3
3
 
4
4
 
5
5
  def test_version():
6
- assert cirq.__version__ == "1.5.0.dev20250119140111"
6
+ assert cirq.__version__ == "1.5.0.dev20250121182620"
@@ -18,6 +18,7 @@ from cirq.transformers.gauge_compiling.gauge_compiling import (
18
18
  Gauge as Gauge,
19
19
  GaugeSelector as GaugeSelector,
20
20
  GaugeTransformer as GaugeTransformer,
21
+ TwoQubitGateSymbolizer as TwoQubitGateSymbolizer,
21
22
  )
22
23
 
23
24
  from cirq.transformers.gauge_compiling.sqrt_cz_gauge import (
@@ -24,134 +24,22 @@ from cirq import ops
24
24
 
25
25
  CZGaugeSelector = GaugeSelector(
26
26
  gauges=[
27
- ConstantGauge(
28
- two_qubit_gate=CZ,
29
- pre_q0=ops.I,
30
- pre_q1=ops.I,
31
- post_q0=ops.I,
32
- post_q1=ops.I,
33
- support_sweep=True,
34
- ),
35
- ConstantGauge(
36
- two_qubit_gate=CZ,
37
- pre_q0=ops.I,
38
- pre_q1=ops.X,
39
- post_q0=ops.Z,
40
- post_q1=ops.X,
41
- support_sweep=True,
42
- ),
43
- ConstantGauge(
44
- two_qubit_gate=CZ,
45
- pre_q0=ops.I,
46
- pre_q1=ops.Y,
47
- post_q0=ops.Z,
48
- post_q1=ops.Y,
49
- support_sweep=True,
50
- ),
51
- ConstantGauge(
52
- two_qubit_gate=CZ,
53
- pre_q0=ops.I,
54
- pre_q1=ops.Z,
55
- post_q0=ops.I,
56
- post_q1=ops.Z,
57
- support_sweep=True,
58
- ),
59
- ConstantGauge(
60
- two_qubit_gate=CZ,
61
- pre_q0=ops.X,
62
- pre_q1=ops.I,
63
- post_q0=ops.X,
64
- post_q1=ops.Z,
65
- support_sweep=True,
66
- ),
67
- ConstantGauge(
68
- two_qubit_gate=CZ,
69
- pre_q0=ops.X,
70
- pre_q1=ops.X,
71
- post_q0=ops.Y,
72
- post_q1=ops.Y,
73
- support_sweep=True,
74
- ),
75
- ConstantGauge(
76
- two_qubit_gate=CZ,
77
- pre_q0=ops.X,
78
- pre_q1=ops.Y,
79
- post_q0=ops.Y,
80
- post_q1=ops.X,
81
- support_sweep=True,
82
- ),
83
- ConstantGauge(
84
- two_qubit_gate=CZ,
85
- pre_q0=ops.X,
86
- pre_q1=ops.Z,
87
- post_q0=ops.X,
88
- post_q1=ops.I,
89
- support_sweep=True,
90
- ),
91
- ConstantGauge(
92
- two_qubit_gate=CZ,
93
- pre_q0=ops.Y,
94
- pre_q1=ops.I,
95
- post_q0=ops.Y,
96
- post_q1=ops.Z,
97
- support_sweep=True,
98
- ),
99
- ConstantGauge(
100
- two_qubit_gate=CZ,
101
- pre_q0=ops.Y,
102
- pre_q1=ops.X,
103
- post_q0=ops.X,
104
- post_q1=ops.Y,
105
- support_sweep=True,
106
- ),
107
- ConstantGauge(
108
- two_qubit_gate=CZ,
109
- pre_q0=ops.Y,
110
- pre_q1=ops.Y,
111
- post_q0=ops.X,
112
- post_q1=ops.X,
113
- support_sweep=True,
114
- ),
115
- ConstantGauge(
116
- two_qubit_gate=CZ,
117
- pre_q0=ops.Y,
118
- pre_q1=ops.Z,
119
- post_q0=ops.Y,
120
- post_q1=ops.I,
121
- support_sweep=True,
122
- ),
123
- ConstantGauge(
124
- two_qubit_gate=CZ,
125
- pre_q0=ops.Z,
126
- pre_q1=ops.I,
127
- post_q0=ops.Z,
128
- post_q1=ops.I,
129
- support_sweep=True,
130
- ),
131
- ConstantGauge(
132
- two_qubit_gate=CZ,
133
- pre_q0=ops.Z,
134
- pre_q1=ops.X,
135
- post_q0=ops.I,
136
- post_q1=ops.X,
137
- support_sweep=True,
138
- ),
139
- ConstantGauge(
140
- two_qubit_gate=CZ,
141
- pre_q0=ops.Z,
142
- pre_q1=ops.Y,
143
- post_q0=ops.I,
144
- post_q1=ops.Y,
145
- support_sweep=True,
146
- ),
147
- ConstantGauge(
148
- two_qubit_gate=CZ,
149
- pre_q0=ops.Z,
150
- pre_q1=ops.Z,
151
- post_q0=ops.Z,
152
- post_q1=ops.Z,
153
- support_sweep=True,
154
- ),
27
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.I, pre_q1=ops.I, post_q0=ops.I, post_q1=ops.I),
28
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.I, pre_q1=ops.X, post_q0=ops.Z, post_q1=ops.X),
29
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.I, pre_q1=ops.Y, post_q0=ops.Z, post_q1=ops.Y),
30
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.I, pre_q1=ops.Z, post_q0=ops.I, post_q1=ops.Z),
31
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.X, pre_q1=ops.I, post_q0=ops.X, post_q1=ops.Z),
32
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.X, pre_q1=ops.X, post_q0=ops.Y, post_q1=ops.Y),
33
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.X, pre_q1=ops.Y, post_q0=ops.Y, post_q1=ops.X),
34
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.X, pre_q1=ops.Z, post_q0=ops.X, post_q1=ops.I),
35
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.Y, pre_q1=ops.I, post_q0=ops.Y, post_q1=ops.Z),
36
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.Y, pre_q1=ops.X, post_q0=ops.X, post_q1=ops.Y),
37
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.Y, pre_q1=ops.Y, post_q0=ops.X, post_q1=ops.X),
38
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.Y, pre_q1=ops.Z, post_q0=ops.Y, post_q1=ops.I),
39
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.Z, pre_q1=ops.I, post_q0=ops.Z, post_q1=ops.I),
40
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.Z, pre_q1=ops.X, post_q0=ops.I, post_q1=ops.X),
41
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.Z, pre_q1=ops.Y, post_q0=ops.I, post_q1=ops.Y),
42
+ ConstantGauge(two_qubit_gate=CZ, pre_q0=ops.Z, pre_q1=ops.Z, post_q0=ops.Z, post_q1=ops.Z),
155
43
  ]
156
44
  )
157
45
 
@@ -15,8 +15,8 @@
15
15
  """Creates the abstraction for gauge compiling as a cirq transformer."""
16
16
 
17
17
  from typing import Callable, Dict, Tuple, Optional, Sequence, Union, List
18
- from itertools import count
19
18
  from dataclasses import dataclass
19
+ from numbers import Real
20
20
  import abc
21
21
  import itertools
22
22
  import functools
@@ -79,7 +79,6 @@ class ConstantGauge(Gauge):
79
79
  default=(), converter=lambda g: (g,) if isinstance(g, ops.Gate) else tuple(g)
80
80
  )
81
81
  swap_qubits: bool = False
82
- support_sweep: bool = False
83
82
 
84
83
  def sample(self, gate: ops.Gate, prng: np.random.Generator) -> "ConstantGauge":
85
84
  return self
@@ -118,7 +117,6 @@ class SameGateGauge(Gauge):
118
117
  default=(), converter=lambda g: (g,) if isinstance(g, ops.Gate) else tuple(g)
119
118
  )
120
119
  swap_qubits: bool = False
121
- support_sweep: bool = False
122
120
 
123
121
  def sample(self, gate: ops.Gate, prng: np.random.Generator) -> ConstantGauge:
124
122
  return ConstantGauge(
@@ -128,10 +126,46 @@ class SameGateGauge(Gauge):
128
126
  post_q0=self.post_q0,
129
127
  post_q1=self.post_q1,
130
128
  swap_qubits=self.swap_qubits,
131
- support_sweep=self.support_sweep,
132
129
  )
133
130
 
134
131
 
132
+ @frozen
133
+ class TwoQubitGateSymbolizer:
134
+ """Parameterizes two qubit gates with symbols.
135
+
136
+ Attributes:
137
+ symbolizer: A callable that takes a two-qubit gate and a sequence of symbols,
138
+ and returns a tuple containing the parameterized gate and a dictionary
139
+ mapping symbol names to their values.
140
+ n_symbols: The number of symbols to use for parameterization.
141
+ """
142
+
143
+ symbolizer_fn: Callable[[ops.Gate, Sequence[sympy.Symbol]], Tuple[ops.Gate, Dict[str, Real]]]
144
+ n_symbols: int
145
+
146
+ def __call__(
147
+ self, two_qubit_gate: ops.Gate, symbols: Sequence[sympy.Symbol]
148
+ ) -> Tuple[ops.Gate, Dict[str, Real]]:
149
+ """Symbolizes a two qubit gate to a parameterized gate.
150
+
151
+ Args:
152
+ two_qubit_gate: The 2 qubit gate to be symbolized.
153
+ symbols: A sequence of sympy symbols to use for parameterization.
154
+
155
+ Returns:
156
+ A tuple containing the parameterized gate and a dictionary mapping
157
+ symbol names to their values.
158
+
159
+ Raises:
160
+ ValueError: If the provided symbols do not match the expected number.
161
+ """
162
+ if len(symbols) != self.n_symbols:
163
+ raise ValueError(
164
+ f"Expect {self.n_symbols} symbols, but got {len(symbols)} symbols in {symbols}"
165
+ )
166
+ return self.symbolizer_fn(two_qubit_gate, symbols)
167
+
168
+
135
169
  def _select(choices: Sequence[Gauge], probabilites: np.ndarray, prng: np.random.Generator) -> Gauge:
136
170
  return choices[prng.choice(len(choices), p=probabilites)]
137
171
 
@@ -154,12 +188,14 @@ class GaugeSelector:
154
188
 
155
189
  @transformer_api.transformer
156
190
  class GaugeTransformer:
191
+
157
192
  def __init__(
158
193
  self,
159
194
  # target can be either a specific gate, gatefamily or gateset
160
195
  # which allows matching parametric gates.
161
196
  target: Union[ops.Gate, ops.Gateset, ops.GateFamily],
162
197
  gauge_selector: Callable[[np.random.Generator], Gauge],
198
+ two_qubit_gate_symbolizer: Optional[TwoQubitGateSymbolizer] = None,
163
199
  ) -> None:
164
200
  """Constructs a GaugeTransformer.
165
201
 
@@ -167,9 +203,11 @@ class GaugeTransformer:
167
203
  target: Target two-qubit gate, a gate-family or a gate-set of two-qubit gates.
168
204
  gauge_selector: A callable that takes a numpy random number generator
169
205
  as an argument and returns a Gauge.
206
+ two_qubit_gate_symbolizer: A symbolizer to symbolize 2 qubit gates.
170
207
  """
171
208
  self.target = ops.GateFamily(target) if isinstance(target, ops.Gate) else target
172
209
  self.gauge_selector = gauge_selector
210
+ self.two_qubit_gate_symbolizer = two_qubit_gate_symbolizer
173
211
 
174
212
  def __call__(
175
213
  self,
@@ -235,15 +273,24 @@ class GaugeTransformer:
235
273
  if context.deep:
236
274
  raise ValueError('GaugeTransformer cannot be used with deep=True')
237
275
  new_moments: List[List[ops.Operation]] = [] # Store parameterized circuits.
238
- values_by_params: Dict[str, List[float]] = {} # map from symbol name to N values.
239
- symbol_count = count()
276
+ phxz_sid = itertools.count()
277
+ two_qubit_gate_sid = itertools.count()
240
278
  # Map from "((pre|post),$qid,$moment_id)" to gate parameters.
241
- # E.g. {(post,q1,2): {"x_exponent": "x1", "z_exponent": "z1", "axis_phase": "a1"}}
242
- symbols_by_loc: Dict[Tuple[str, ops.Qid, int], Dict[str, sympy.Symbol]] = {}
279
+ # E.g., {(post,q1,2): {"x_exponent": "x1", "z_exponent": "z1", "axis_phase": "a1"}}
280
+ phxz_symbols_by_locs: Dict[Tuple[str, ops.Qid, int], Dict[str, sympy.Symbol]] = {}
281
+ # Map from "($q0,$q1,$moment_id)" to gate parameters.
282
+ # E.g., {(q0,q1,0): ["s0"]}.
283
+ two_qubit_gate_symbols_by_locs: Dict[Tuple[ops.Qid, ops.Qid, int], List[sympy.Symbol]] = {}
243
284
 
244
285
  def single_qubit_next_symbol() -> Dict[str, sympy.Symbol]:
245
- sid = next(symbol_count)
246
- return _parameterize(1, sid)
286
+ sid = next(phxz_sid)
287
+ return _parameterize_to_phxz(sid)
288
+
289
+ def two_qubit_gate_next_symbol_list(n: int) -> List[sympy.Symbol]:
290
+ """Returns symbols for 2 qubit gate parameterization."""
291
+ sid = next(two_qubit_gate_sid)
292
+ symbols: List[sympy.Symbol] = [sympy.Symbol(f"s{sid}_{sub}") for sub in range(n)]
293
+ return symbols
247
294
 
248
295
  # Build parameterized circuit.
249
296
  for moment_id, moment in enumerate(circuit):
@@ -257,17 +304,29 @@ class GaugeTransformer:
257
304
  center_moment.append(op)
258
305
  continue
259
306
  if op.gate is not None and op in self.target:
307
+ random_gauge = self.gauge_selector(rng).sample(op.gate, rng)
308
+ # Build symbols for 2-qubit-gates if the transformer might transform it,
309
+ # otherwise, keep it as it is.
310
+ if self.two_qubit_gate_symbolizer is not None:
311
+ symbols: list[sympy.Symbol] = two_qubit_gate_next_symbol_list(
312
+ self.two_qubit_gate_symbolizer.n_symbols
313
+ )
314
+ two_qubit_gate_symbols_by_locs[(op.qubits[0], op.qubits[1], moment_id)] = (
315
+ symbols
316
+ )
317
+ parameterized_2_qubit_gate, _ = self.two_qubit_gate_symbolizer(
318
+ random_gauge.two_qubit_gate, symbols
319
+ )
320
+ center_moment.append(parameterized_2_qubit_gate.on(*op.qubits))
321
+ else:
322
+ center_moment.append(op)
260
323
  # Build symbols for the gauge, for a 2-qubit gauge, symbols will be built for
261
324
  # pre/post q0/q1 and the new 2-qubit gate if the 2-qubit gate is updated in
262
325
  # the gauge compiling.
263
- center_moment.append(op)
264
326
  for prefix, q in itertools.product(["pre", "post"], op.qubits):
265
327
  xza_by_symbols = single_qubit_next_symbol() # xza in phased xz gate.
266
- loc = (prefix, q, moment_id)
267
- symbols_by_loc[loc] = xza_by_symbols
328
+ phxz_symbols_by_locs[(prefix, q, moment_id)] = xza_by_symbols
268
329
  new_op = ops.PhasedXZGate(**xza_by_symbols).on(q)
269
- for symbol in xza_by_symbols.values():
270
- values_by_params.update({str(symbol): []})
271
330
  if prefix == "pre":
272
331
  left_moment.append(new_op)
273
332
  else:
@@ -278,6 +337,20 @@ class GaugeTransformer:
278
337
  [moment for moment in [left_moment, center_moment, right_moment] if moment]
279
338
  )
280
339
 
340
+ # Initialize the map from symbol names to their N values.
341
+ values_by_params: Dict[str, List[float]] = {
342
+ **{
343
+ str(symbol): []
344
+ for symbols_by_names in phxz_symbols_by_locs.values()
345
+ for symbol in symbols_by_names.values()
346
+ },
347
+ **{
348
+ str(symbol): []
349
+ for symbols in two_qubit_gate_symbols_by_locs.values()
350
+ for symbol in symbols
351
+ },
352
+ }
353
+
281
354
  # Assign values for parameters via randomly chosen GaugeSelector.
282
355
  for _ in range(N):
283
356
  for moment_id, moment in enumerate(circuit):
@@ -288,17 +361,30 @@ class GaugeTransformer:
288
361
  continue
289
362
  if op.gate is not None and len(op.qubits) == 2 and op in self.target:
290
363
  gauge = self.gauge_selector(rng).sample(op.gate, rng)
291
- if not gauge.support_sweep:
292
- raise NotImplementedError(
293
- f"as_sweep isn't supported for {gauge.two_qubit_gate} gauge"
364
+
365
+ # Get the params for 2 qubit gates.
366
+ if self.two_qubit_gate_symbolizer is not None:
367
+ _, vals_by_symbols = self.two_qubit_gate_symbolizer(
368
+ gauge.two_qubit_gate,
369
+ [
370
+ *two_qubit_gate_symbols_by_locs[
371
+ (op.qubits[0], op.qubits[1], moment_id)
372
+ ]
373
+ ],
294
374
  )
375
+ for symbol_str, val in vals_by_symbols.items():
376
+ values_by_params[symbol_str].append(float(val))
377
+
295
378
  # Get the params of pre/post q0/q1 gates.
296
379
  for pre_or_post, idx in itertools.product(["pre", "post"], [0, 1]):
297
- symbols = symbols_by_loc[(pre_or_post, op.qubits[idx], moment_id)]
298
380
  gates = getattr(gauge, f"{pre_or_post}_q{idx}")
299
- phxz_params = _gate_sequence_to_phxz_params(gates, symbols)
381
+ phxz_params = _gate_sequence_to_phxz_params(
382
+ gates,
383
+ phxz_symbols_by_locs[(pre_or_post, op.qubits[idx], moment_id)],
384
+ )
300
385
  for key, value in phxz_params.items():
301
- values_by_params[key].append(value)
386
+ values_by_params[key].append(float(value))
387
+
302
388
  sweeps: List[Points] = [
303
389
  Points(key=key, points=values) for key, values in values_by_params.items()
304
390
  ]
@@ -318,17 +404,16 @@ def _build_moments(operation_by_qubits: List[List[ops.Operation]]) -> List[List[
318
404
  return moments
319
405
 
320
406
 
321
- def _parameterize(num_qubits: int, symbol_id: int) -> Dict[str, sympy.Symbol]:
407
+ def _parameterize_to_phxz(symbol_id: int) -> Dict[str, sympy.Symbol]:
322
408
  """Returns symbolized parameters for the gate."""
323
409
 
324
- if num_qubits == 1: # Convert single qubit gate to parameterized PhasedXZGate.
325
- phased_xz_params = {
326
- "x_exponent": sympy.Symbol(f"x{symbol_id}"),
327
- "z_exponent": sympy.Symbol(f"z{symbol_id}"),
328
- "axis_phase_exponent": sympy.Symbol(f"a{symbol_id}"),
329
- }
330
- return phased_xz_params
331
- raise NotImplementedError("parameterization for non single qubit gates is not supported yet")
410
+ # Parameterize single qubit gate to parameterized PhasedXZGate.
411
+ phased_xz_params = {
412
+ "x_exponent": sympy.Symbol(f"x{symbol_id}"),
413
+ "z_exponent": sympy.Symbol(f"z{symbol_id}"),
414
+ "axis_phase_exponent": sympy.Symbol(f"a{symbol_id}"),
415
+ }
416
+ return phased_xz_params
332
417
 
333
418
 
334
419
  def _gate_sequence_to_phxz_params(
@@ -16,12 +16,16 @@ import unittest.mock
16
16
  import pytest
17
17
  import numpy as np
18
18
  import cirq
19
+ import sympy
19
20
  from cirq.transformers.gauge_compiling import (
20
21
  GaugeTransformer,
21
22
  CZGaugeTransformer,
23
+ SqrtCZGaugeTransformer,
22
24
  ConstantGauge,
23
25
  GaugeSelector,
26
+ TwoQubitGateSymbolizer,
24
27
  )
28
+ from cirq.transformers.gauge_compiling.sqrt_cz_gauge import SqrtCZGauge
25
29
  from cirq.transformers.analytical_decompositions import single_qubit_decompositions
26
30
 
27
31
 
@@ -64,7 +68,6 @@ def test_as_sweep_multi_pre_or_multi_post():
64
68
  gauges=[
65
69
  ConstantGauge(
66
70
  two_qubit_gate=cirq.CZ,
67
- support_sweep=True,
68
71
  pre_q0=[cirq.X, cirq.X],
69
72
  post_q0=[cirq.Z],
70
73
  pre_q1=[cirq.Y],
@@ -91,7 +94,6 @@ def test_as_sweep_invalid_gauge_sequence():
91
94
  gauges=[
92
95
  ConstantGauge(
93
96
  two_qubit_gate=cirq.CZ,
94
- support_sweep=True,
95
97
  pre_q0=[cirq.measure],
96
98
  post_q0=[cirq.Z],
97
99
  pre_q1=[cirq.X],
@@ -110,16 +112,33 @@ def test_as_sweep_convert_to_phxz_failed():
110
112
  qs = cirq.LineQubit.range(2)
111
113
  c = cirq.Circuit(cirq.CZ(*qs))
112
114
 
113
- def mock_single_qubit_matrix_to_phxz(*args, **kwargs):
114
- # Return an non PhasedXZ gate, so we expect errors from as_sweep().
115
- return cirq.X
116
-
117
115
  with unittest.mock.patch.object(
118
116
  single_qubit_decompositions,
119
117
  "single_qubit_matrix_to_phxz",
120
- new=mock_single_qubit_matrix_to_phxz,
118
+ # Return an non PhasedXZ gate, so we expect errors from as_sweep().
119
+ return_value=cirq.X,
121
120
  ):
122
121
  with pytest.raises(
123
122
  ValueError, match="Failed to convert the gate sequence to a PhasedXZ gate."
124
123
  ):
125
124
  _ = CZGaugeTransformer.as_sweep(c, context=cirq.TransformerContext(), N=1)
125
+
126
+
127
+ def test_symbolize_2_qubits_gate_failed():
128
+ qs = cirq.LineQubit.range(2)
129
+ c = cirq.Circuit(cirq.CZPowGate(exponent=0.5).on(*qs))
130
+
131
+ with unittest.mock.patch.object(
132
+ SqrtCZGauge,
133
+ "sample",
134
+ # ISWAP gate is not a CZPowGate; errors are expected when symbolizing the 2-qubit gate.
135
+ return_value=ConstantGauge(two_qubit_gate=cirq.ISWAP),
136
+ ):
137
+ with pytest.raises(ValueError, match="Can't symbolize non-CZPowGate as CZ\\*\\*symbol."):
138
+ _ = SqrtCZGaugeTransformer.as_sweep(c, N=1)
139
+
140
+
141
+ def test_symbolize_2_qubits_gate_failed_unmatched_symbol_length():
142
+ symbolizer = TwoQubitGateSymbolizer(symbolizer_fn=lambda gate, _: (gate, {}), n_symbols=2)
143
+ with pytest.raises(ValueError, match="Expect 2 symbols, but got 1 symbols"):
144
+ symbolizer(cirq.CZ, [sympy.Symbol('x')])
@@ -27,7 +27,6 @@ class GaugeTester:
27
27
  two_qubit_gate: cirq.Gate
28
28
  gauge_transformer: GaugeTransformer
29
29
  must_fail: bool = False
30
- sweep_must_pass: bool = False
31
30
 
32
31
  @pytest.mark.parametrize(
33
32
  ['generation_seed', 'transformation_seed'],
@@ -77,13 +76,6 @@ class GaugeTester:
77
76
  def test_sweep(self):
78
77
  qubits = cirq.LineQubit.range(3)
79
78
 
80
- if not self.sweep_must_pass:
81
- with pytest.raises(NotImplementedError):
82
- self.gauge_transformer.as_sweep(
83
- cirq.Circuit(cirq.Moment(self.two_qubit_gate(*qubits[:2]))), N=1
84
- )
85
- return
86
-
87
79
  input_circuit = cirq.Circuit(
88
80
  cirq.Moment(cirq.H(qubits[0])),
89
81
  cirq.Moment(self.two_qubit_gate(*qubits[:2])),
@@ -104,6 +96,14 @@ class GaugeTester:
104
96
  # Check compilied circuits have the same unitary as the orig circuit.
105
97
  for params in sweeps:
106
98
  compiled_circuit = cirq.resolve_parameters(parameterized_circuit, params)
99
+ if self.must_fail:
100
+ with pytest.raises(AssertionError):
101
+ cirq.testing.assert_circuits_have_same_unitary_given_final_permutation(
102
+ input_circuit[:-1],
103
+ compiled_circuit[:-1],
104
+ qubit_map={q: q for q in input_circuit.all_qubits()},
105
+ )
106
+ break
107
107
  cirq.testing.assert_circuits_have_same_unitary_given_final_permutation(
108
108
  input_circuit[:-1],
109
109
  compiled_circuit[:-1],
@@ -48,22 +48,6 @@ _BAD_TRANSFORMER = GaugeTransformer(
48
48
  ),
49
49
  )
50
50
 
51
- _TRANSFORMER_WITH_SWEEP = GaugeTransformer(
52
- target=_EXAMPLE_SWEEP_TARGET,
53
- gauge_selector=GaugeSelector(
54
- gauges=[
55
- ConstantGauge(
56
- two_qubit_gate=_EXAMPLE_SWEEP_TARGET,
57
- pre_q0=cirq.Z,
58
- pre_q1=cirq.Z,
59
- post_q0=cirq.Z,
60
- post_q1=cirq.Z,
61
- support_sweep=True,
62
- )
63
- ]
64
- ),
65
- )
66
-
67
51
 
68
52
  class TestValidTransformer(GaugeTester):
69
53
  two_qubit_gate = _EXAMPLE_TARGET
@@ -74,9 +58,3 @@ class TestInvalidTransformer(GaugeTester):
74
58
  two_qubit_gate = _EXAMPLE_TARGET
75
59
  gauge_transformer = _BAD_TRANSFORMER
76
60
  must_fail = True
77
-
78
-
79
- class TestSweep(GaugeTester):
80
- two_qubit_gate = _EXAMPLE_SWEEP_TARGET
81
- gauge_transformer = _TRANSFORMER_WITH_SWEEP
82
- sweep_must_pass = True
@@ -56,7 +56,6 @@ class RZRotation(Gauge):
56
56
  pre_q1=n_rz if flip_diangonal else rz,
57
57
  post_q0=rz if flip_diangonal else n_rz,
58
58
  post_q1=n_rz,
59
- support_sweep=True,
60
59
  )
61
60
 
62
61
  def sample(self, gate: ops.Gate, prng: np.random.Generator) -> ConstantGauge:
@@ -89,12 +88,7 @@ class XYRotation(Gauge):
89
88
  xy_a = self._xy(a)
90
89
  xy_b = self._xy(b)
91
90
  return ConstantGauge(
92
- two_qubit_gate=ops.ISWAP,
93
- pre_q0=xy_a,
94
- pre_q1=xy_b,
95
- post_q0=xy_b,
96
- post_q1=xy_a,
97
- support_sweep=True,
91
+ two_qubit_gate=ops.ISWAP, pre_q0=xy_a, pre_q1=xy_b, post_q0=xy_b, post_q1=xy_a
98
92
  )
99
93
 
100
94
  def sample(self, gate: ops.Gate, prng: np.random.Generator) -> ConstantGauge:
@@ -21,4 +21,3 @@ from cirq.transformers.gauge_compiling.gauge_compiling_test_utils import GaugeTe
21
21
  class TestISWAPGauge(GaugeTester):
22
22
  two_qubit_gate = cirq.ISWAP
23
23
  gauge_transformer = ISWAPGaugeTransformer
24
- sweep_must_pass = True
@@ -23,8 +23,8 @@ from cirq import ops
23
23
 
24
24
  SpinInversionGaugeSelector = GaugeSelector(
25
25
  gauges=[
26
- SameGateGauge(pre_q0=ops.X, post_q0=ops.X, pre_q1=ops.X, post_q1=ops.X, support_sweep=True),
27
- SameGateGauge(support_sweep=True),
26
+ SameGateGauge(pre_q0=ops.X, post_q0=ops.X, pre_q1=ops.X, post_q1=ops.X),
27
+ SameGateGauge(),
28
28
  ]
29
29
  )
30
30
 
@@ -15,16 +15,20 @@
15
15
  """A Gauge transformer for CZ**0.5 and CZ**-0.5 gates."""
16
16
 
17
17
 
18
- from typing import TYPE_CHECKING
18
+ from typing import TYPE_CHECKING, Dict, Sequence, Tuple
19
+ from numbers import Real
19
20
  import numpy as np
21
+ import sympy
20
22
 
21
23
  from cirq.transformers.gauge_compiling.gauge_compiling import (
22
24
  GaugeTransformer,
23
25
  GaugeSelector,
24
26
  ConstantGauge,
25
27
  Gauge,
28
+ TwoQubitGateSymbolizer,
26
29
  )
27
- from cirq.ops import CZ, S, X, Gateset
30
+ from cirq.ops import CZ, S, X, Gateset, Gate, CZPowGate
31
+
28
32
 
29
33
  if TYPE_CHECKING:
30
34
  import cirq
@@ -59,6 +63,20 @@ class SqrtCZGauge(Gauge):
59
63
  )
60
64
 
61
65
 
66
+ def _symbolize_as_cz_pow(
67
+ two_qubit_gate: Gate, symbols: Sequence[sympy.Symbol]
68
+ ) -> Tuple[Gate, Dict[str, Real]]:
69
+ """Symbolizes a CZPowGate to a parameterized CZPowGate."""
70
+
71
+ if not isinstance(two_qubit_gate, CZPowGate) or not isinstance(two_qubit_gate.exponent, Real):
72
+ raise ValueError("Can't symbolize non-CZPowGate as CZ**symbol.")
73
+ return (CZ ** symbols[0], {str(symbols[0]): two_qubit_gate.exponent})
74
+
75
+
62
76
  SqrtCZGaugeTransformer = GaugeTransformer(
63
- target=Gateset(_SQRT_CZ, _SQRT_CZ**-1), gauge_selector=GaugeSelector(gauges=[SqrtCZGauge()])
77
+ target=Gateset(_SQRT_CZ, _SQRT_CZ**-1),
78
+ gauge_selector=GaugeSelector(gauges=[SqrtCZGauge()]),
79
+ two_qubit_gate_symbolizer=TwoQubitGateSymbolizer(
80
+ symbolizer_fn=_symbolize_as_cz_pow, n_symbols=1
81
+ ),
64
82
  )
@@ -20,8 +20,10 @@ from cirq.transformers.gauge_compiling.gauge_compiling_test_utils import GaugeTe
20
20
  class TestSqrtCZGauge(GaugeTester):
21
21
  two_qubit_gate = cirq.CZ**0.5
22
22
  gauge_transformer = SqrtCZGaugeTransformer
23
+ sweep_must_pass = True
23
24
 
24
25
 
25
26
  class TestAdjointSqrtCZGauge(GaugeTester):
26
27
  two_qubit_gate = cirq.CZ**-0.5
27
28
  gauge_transformer = SqrtCZGaugeTransformer
29
+ sweep_must_pass = True
@@ -49,12 +49,7 @@ class RZRotation(Gauge):
49
49
  rz = ops.rz(theta)
50
50
  n_rz = ops.rz(-theta)
51
51
  return ConstantGauge(
52
- two_qubit_gate=ops.SQRT_ISWAP,
53
- pre_q0=rz,
54
- pre_q1=rz,
55
- post_q0=n_rz,
56
- post_q1=n_rz,
57
- support_sweep=True,
52
+ two_qubit_gate=ops.SQRT_ISWAP, pre_q0=rz, pre_q1=rz, post_q0=n_rz, post_q1=n_rz
58
53
  )
59
54
 
60
55
  def sample(self, gate: ops.Gate, prng: np.random.Generator) -> ConstantGauge:
@@ -85,12 +80,7 @@ class XYRotation(Gauge):
85
80
  def _xy_gauge(self, theta: float) -> ConstantGauge:
86
81
  xy = self._xy(theta)
87
82
  return ConstantGauge(
88
- two_qubit_gate=ops.SQRT_ISWAP,
89
- pre_q0=xy,
90
- pre_q1=xy,
91
- post_q0=xy,
92
- post_q1=xy,
93
- support_sweep=True,
83
+ two_qubit_gate=ops.SQRT_ISWAP, pre_q0=xy, pre_q1=xy, post_q0=xy, post_q1=xy
94
84
  )
95
85
 
96
86
  def sample(self, gate: ops.Gate, prng: np.random.Generator) -> ConstantGauge:
@@ -20,4 +20,3 @@ from cirq.transformers.gauge_compiling.gauge_compiling_test_utils import GaugeTe
20
20
  class TestSqrtISWAPGauge(GaugeTester):
21
21
  two_qubit_gate = cirq.SQRT_ISWAP
22
22
  gauge_transformer = SqrtISWAPGaugeTransformer
23
- sweep_must_pass = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cirq-core
3
- Version: 1.5.0.dev20250119140111
3
+ Version: 1.5.0.dev20250121182620
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
@@ -4,8 +4,8 @@ cirq/_compat_test.py,sha256=Qq3ZcfgD-Nb81cEppQdJqhAyrVqXKtfXZYGXT0p-Wh0,34718
4
4
  cirq/_doc.py,sha256=yDyWUD_2JDS0gShfGRb-rdqRt9-WeL7DhkqX7np0Nko,2879
5
5
  cirq/_import.py,sha256=p9gMHJscbtDDkfHOaulvd3Aer0pwUF5AXpL89XR8dNw,8402
6
6
  cirq/_import_test.py,sha256=6K_v0riZJXOXUphHNkGA8MY-JcmGlezFaGmvrNhm3OQ,1015
7
- cirq/_version.py,sha256=bpeVZEmK8_z12uMzIE7OfSIJMu6eyRa8jQvuVY_oS80,1206
8
- cirq/_version_test.py,sha256=COeD9Sq5oxFb7da4qjWQyLEuCsKUMx-PmUXykpJIjdo,147
7
+ cirq/_version.py,sha256=XZdE7H_7w7Hiiirm6ued03JFp9knhCr9Q0kxSHi5gj4,1206
8
+ cirq/_version_test.py,sha256=zc0zE4BdzIXGggkuzj7jYcjDGNVrkasDxCUOXtwbiU8,147
9
9
  cirq/conftest.py,sha256=X7yLFL8GLhg2CjPw0hp5e_dGASfvHx1-QT03aUbhKJw,1168
10
10
  cirq/json_resolver_cache.py,sha256=p-vEOa-8GQ2cFIAdze-kd6C1un1uRvtujVPljVKaHBg,13557
11
11
  cirq/py.typed,sha256=VFSlmh_lNwnaXzwY-ZuW-C2Ws5PkuDoVgBdNCs0jXJE,63
@@ -1101,21 +1101,21 @@ cirq/transformers/analytical_decompositions/two_qubit_to_ms.py,sha256=Awr7WvaJe_
1101
1101
  cirq/transformers/analytical_decompositions/two_qubit_to_ms_test.py,sha256=85MbuIAos7o1371wXs_KH-Bk6jsPqSBKAx9GJ9c-wVo,4160
1102
1102
  cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap.py,sha256=F_XpM4ApYHxV6hbWnV3C7Ud9L1BnpvBHBXShPh2mP3k,25397
1103
1103
  cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py,sha256=eKOzjWkR7xs-CL2oPj__nWXR0LL9oO42wEHibnvWq-o,20618
1104
- cirq/transformers/gauge_compiling/__init__.py,sha256=ZF53ZtYRJeKsVJYjKc_QrAqE1pyd8FFmmb6Wo8JdgQs,1385
1105
- cirq/transformers/gauge_compiling/cz_gauge.py,sha256=pJ41uVaUltigKLIayxr0XMqTYEs0zUDnaWD-tp65pPk,4198
1104
+ cirq/transformers/gauge_compiling/__init__.py,sha256=eG8ghJrue39O5Ijla_VhKrxhgr2oWGIXHEcMKqSRkCM,1439
1105
+ cirq/transformers/gauge_compiling/cz_gauge.py,sha256=TNZviXFu4j-lCF87QMGYVdb2RC_ePHLdI6FRCqh9go4,2550
1106
1106
  cirq/transformers/gauge_compiling/cz_gauge_test.py,sha256=sHEgEEI_z9-Ka5ChN2JmtoYcEHhNYHysOjGJzaaKkoA,881
1107
- cirq/transformers/gauge_compiling/gauge_compiling.py,sha256=kNIJwyGchc1eVrsb1tzSkwm01_PVDYyLSL56mG8xlcA,15230
1108
- cirq/transformers/gauge_compiling/gauge_compiling_test.py,sha256=Nm0Uxqrq1Y5puQep9UpKXK2zg9a3Dx2NSFArIxeawUg,4444
1109
- cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py,sha256=7RNZ6-xQ1iKjoNWTokgok7xTCeAnrQUzbpdBhdJZEfY,4933
1110
- cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py,sha256=cWEAP1EWbpHNp7wQPXLyT413raoG3aIg8aFod_aXAtQ,2340
1111
- cirq/transformers/gauge_compiling/iswap_gauge.py,sha256=W2g2DXhN1OQbVL1UxBjvZa3YsRPMFAiaC-tvxKImSHU,3613
1112
- cirq/transformers/gauge_compiling/iswap_gauge_test.py,sha256=HoeJgSJSrr3ARXDPP9uOb5z4fgvR94snTWGfqG7-yuE,893
1113
- cirq/transformers/gauge_compiling/spin_inversion_gauge.py,sha256=wpZx2-FbBt2Eq9ORUzMYPqtuJcHkU_5PTcQFDRfwSBo,1124
1107
+ cirq/transformers/gauge_compiling/gauge_compiling.py,sha256=4g2IyjGqEHD7KkawXlprhNLfbeSyz3QdWIHPAjqeGFo,18846
1108
+ cirq/transformers/gauge_compiling/gauge_compiling_test.py,sha256=srpVkn1WlJh5SATk1UozZT2aLVwcykSNBpsJ8u4YCrk,5234
1109
+ cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py,sha256=K49nP6OOi2Fga50RA-qwjgjnOMvuT9hqhJdhaQnriTM,5025
1110
+ cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py,sha256=4Hc3oFInAtiZugWTL75jpGJTVTubKWWTk4uaTLvOEY0,1784
1111
+ cirq/transformers/gauge_compiling/iswap_gauge.py,sha256=UGJ_061h65Rfgb9LWREjxC8OSt01ZqP9TGnacL8VAuk,3500
1112
+ cirq/transformers/gauge_compiling/iswap_gauge_test.py,sha256=HEIIwKlX5ixau1e_etSUj5NvYOVTT-Gc3kuHcyKAeJ4,866
1113
+ cirq/transformers/gauge_compiling/spin_inversion_gauge.py,sha256=gfjSlQdo13GfBPlrkQoHPWWzouiV7yYr7JAaB85NSGY,1086
1114
1114
  cirq/transformers/gauge_compiling/spin_inversion_gauge_test.py,sha256=eQ65i1GovnL2-VmPumcFKMIjqE0Pee1PdmsO0XDGmW4,1400
1115
- cirq/transformers/gauge_compiling/sqrt_cz_gauge.py,sha256=32OGTcYT3tBFEQ1GQlrssc1wtwCcSvk4ZC0I1XD1QXg,1869
1116
- cirq/transformers/gauge_compiling/sqrt_cz_gauge_test.py,sha256=RwjadOOJfa2Qf7iryTIMJLPzeDMNqFkP6Tewjq68gJI,997
1117
- cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py,sha256=VnlO4eqy5Yx-cE_EO0JSV2iccyJnrp7WWgzPiF_cJGg,3288
1118
- cirq/transformers/gauge_compiling/sqrt_iswap_gauge_test.py,sha256=n0so4a7TLvpL5CelBUExpA0dgGvfAVxLsqLxRpKqFYE,909
1115
+ cirq/transformers/gauge_compiling/sqrt_cz_gauge.py,sha256=XwqVMeJu8zYpW7SAWZgLLsLGtZWdPfJLtCBdpEoROLQ,2529
1116
+ cirq/transformers/gauge_compiling/sqrt_cz_gauge_test.py,sha256=rNQ8O9d17xHHmV5Ss1167sZcKGMhGEQV26PMnvB-gHQ,1051
1117
+ cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py,sha256=dqQa-UWq31bE_jF3KMhU76sND5GuqTpEp9-wVuXdWVM,3126
1118
+ cirq/transformers/gauge_compiling/sqrt_iswap_gauge_test.py,sha256=0CLZoLw-WK3aKEIoaKBrQZ-qvaprOVLad-dVyWFmSiU,882
1119
1119
  cirq/transformers/heuristic_decompositions/__init__.py,sha256=_LEidXfFkmJicQapJVR1etyH1fLJ3ZwtBgq2M2_ECZI,926
1120
1120
  cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py,sha256=j9bbiIbC2rvwG830gTTf9zr9C7RVA5Ilhka_ZNF-N7w,10785
1121
1121
  cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py,sha256=N02nlz7tISYVArvWNILwg-hnDB5Y9PCHbwIxk42Afv8,1534
@@ -1199,8 +1199,8 @@ cirq/work/sampler.py,sha256=bE5tmVkcR6cZZMLETxDfHehdsYUMbx2RvBeIBetehI4,19187
1199
1199
  cirq/work/sampler_test.py,sha256=hL2UWx3dz2ukZVNxWftiKVvJcQoLplLZdQm-k1QcA40,13282
1200
1200
  cirq/work/zeros_sampler.py,sha256=x1C7cup66a43n-3tm8QjhiqJa07qcJW10FxNp9jJ59Q,2356
1201
1201
  cirq/work/zeros_sampler_test.py,sha256=JIkpBBFPJe5Ba4142vzogyWyboG1Q1ZAm0UVGgOoZn8,3279
1202
- cirq_core-1.5.0.dev20250119140111.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1203
- cirq_core-1.5.0.dev20250119140111.dist-info/METADATA,sha256=KdQcdFoXg5JUs1NNUsaIO_Aw18yo3L7midMDriHKX9s,2105
1204
- cirq_core-1.5.0.dev20250119140111.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
1205
- cirq_core-1.5.0.dev20250119140111.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
1206
- cirq_core-1.5.0.dev20250119140111.dist-info/RECORD,,
1202
+ cirq_core-1.5.0.dev20250121182620.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1203
+ cirq_core-1.5.0.dev20250121182620.dist-info/METADATA,sha256=La24Zt1W_AZjANNM5OI0aYbgAQ8aQMCGxi3_KCsagks,2105
1204
+ cirq_core-1.5.0.dev20250121182620.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
1205
+ cirq_core-1.5.0.dev20250121182620.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
1206
+ cirq_core-1.5.0.dev20250121182620.dist-info/RECORD,,