cirq-core 1.2.0.dev20230717232332__py3-none-any.whl → 1.3.0__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.
- cirq/__init__.py +5 -0
- cirq/_compat.py +26 -11
- cirq/_compat_test.py +37 -3
- cirq/_version.py +31 -1
- cirq/_version_test.py +1 -1
- cirq/circuits/circuit.py +106 -32
- cirq/circuits/circuit_operation.py +2 -2
- cirq/circuits/circuit_operation_test.py +1 -1
- cirq/circuits/circuit_test.py +109 -3
- cirq/circuits/frozen_circuit.py +80 -5
- cirq/circuits/frozen_circuit_test.py +47 -2
- cirq/circuits/qasm_output_test.py +9 -9
- cirq/conftest.py +1 -2
- cirq/contrib/acquaintance/devices.py +1 -1
- cirq/contrib/hacks/disable_validation_test.py +1 -1
- cirq/contrib/noise_models/noise_models.py +1 -2
- cirq/contrib/paulistring/clifford_optimize.py +1 -1
- cirq/contrib/paulistring/clifford_target_gateset_test.py +4 -4
- cirq/contrib/qcircuit/qcircuit_pdf.py +1 -1
- cirq/contrib/quimb/density_matrix.py +2 -3
- cirq/contrib/quimb/grid_circuits.py +3 -3
- cirq/contrib/quimb/state_vector.py +3 -5
- cirq/contrib/routing/utils.py +1 -2
- cirq/contrib/svg/svg.py +4 -6
- cirq/devices/grid_qubit.py +49 -38
- cirq/devices/grid_qubit_test.py +1 -3
- cirq/devices/insertion_noise_model.py +21 -1
- cirq/devices/insertion_noise_model_test.py +6 -0
- cirq/devices/line_qubit.py +67 -40
- cirq/devices/named_topologies.py +8 -14
- cirq/devices/noise_properties.py +1 -1
- cirq/devices/noise_utils.py +7 -5
- cirq/devices/noise_utils_test.py +7 -0
- cirq/experiments/fidelity_estimation_test.py +1 -1
- cirq/experiments/qubit_characterizations.py +6 -5
- cirq/experiments/random_quantum_circuit_generation.py +1 -1
- cirq/experiments/random_quantum_circuit_generation_test.py +28 -1
- cirq/experiments/readout_confusion_matrix.py +6 -6
- cirq/experiments/xeb_fitting.py +3 -5
- cirq/experiments/xeb_fitting_test.py +2 -2
- cirq/experiments/xeb_sampling.py +1 -1
- cirq/interop/quirk/url_to_circuit.py +40 -38
- cirq/json_resolver_cache.py +2 -0
- cirq/linalg/decompositions.py +6 -5
- cirq/ops/__init__.py +2 -0
- cirq/ops/classically_controlled_operation.py +1 -1
- cirq/ops/clifford_gate.py +9 -9
- cirq/ops/clifford_gate_test.py +3 -4
- cirq/ops/common_channels.py +2 -5
- cirq/ops/common_channels_test.py +3 -5
- cirq/ops/common_gates_test.py +7 -7
- cirq/ops/controlled_operation_test.py +2 -2
- cirq/ops/dense_pauli_string.py +3 -0
- cirq/ops/eigen_gate_test.py +1 -3
- cirq/ops/fourier_transform.py +1 -2
- cirq/ops/fsim_gate.py +1 -1
- cirq/ops/gate_features_test.py +2 -2
- cirq/ops/gate_operation_test.py +1 -2
- cirq/ops/greedy_qubit_manager.py +86 -0
- cirq/ops/greedy_qubit_manager_test.py +98 -0
- cirq/ops/linear_combinations.py +1 -1
- cirq/ops/named_qubit.py +55 -18
- cirq/ops/parity_gates.py +65 -18
- cirq/ops/parity_gates_test.py +41 -2
- cirq/ops/pauli_gates.py +2 -2
- cirq/ops/pauli_string.py +3 -4
- cirq/ops/pauli_string_raw_types_test.py +3 -3
- cirq/ops/pauli_string_test.py +3 -4
- cirq/ops/random_gate_channel_test.py +3 -3
- cirq/ops/raw_types.py +1 -1
- cirq/ops/raw_types_test.py +5 -5
- cirq/ops/three_qubit_gates.py +12 -8
- cirq/protocols/act_on_protocol_test.py +9 -9
- cirq/protocols/apply_channel_protocol.py +9 -6
- cirq/protocols/apply_unitary_protocol_test.py +1 -1
- cirq/protocols/equal_up_to_global_phase_protocol_test.py +2 -2
- cirq/protocols/has_stabilizer_effect_protocol.py +52 -6
- cirq/protocols/has_stabilizer_effect_protocol_test.py +21 -8
- cirq/protocols/has_unitary_protocol_test.py +1 -3
- cirq/protocols/json_serialization.py +6 -6
- cirq/protocols/json_serialization_test.py +7 -14
- cirq/protocols/json_test_data/InsertionNoiseModel.json +91 -0
- cirq/protocols/json_test_data/InsertionNoiseModel.repr +4 -0
- cirq/protocols/json_test_data/OpIdentifier.json +45 -10
- cirq/protocols/json_test_data/OpIdentifier.repr +7 -1
- cirq/protocols/json_test_data/spec.py +4 -0
- cirq/protocols/measurement_key_protocol_test.py +1 -1
- cirq/protocols/unitary_protocol_test.py +13 -16
- cirq/qis/clifford_tableau.py +7 -8
- cirq/qis/measures.py +1 -1
- cirq/qis/states.py +2 -3
- cirq/sim/__init__.py +2 -0
- cirq/sim/classical_simulator.py +107 -0
- cirq/sim/classical_simulator_test.py +207 -0
- cirq/sim/clifford/clifford_simulator_test.py +7 -7
- cirq/sim/clifford/stabilizer_simulation_state.py +2 -2
- cirq/sim/clifford/stabilizer_state_ch_form.py +7 -7
- cirq/sim/density_matrix_simulation_state.py +19 -4
- cirq/sim/density_matrix_simulator_test.py +5 -13
- cirq/sim/simulation_state_test.py +13 -14
- cirq/sim/simulator_test.py +6 -9
- cirq/sim/state_vector_simulation_state.py +1 -1
- cirq/study/resolver.py +41 -41
- cirq/study/resolver_test.py +13 -12
- cirq/testing/__init__.py +4 -1
- cirq/testing/circuit_compare.py +1 -1
- cirq/testing/circuit_compare_test.py +11 -11
- cirq/testing/consistent_controlled_gate_op.py +15 -1
- cirq/testing/consistent_controlled_gate_op_test.py +12 -3
- cirq/testing/consistent_decomposition.py +0 -1
- cirq/testing/consistent_protocols.py +6 -1
- cirq/testing/consistent_protocols_test.py +5 -10
- cirq/testing/consistent_qasm.py +2 -4
- cirq/testing/consistent_qasm_test.py +2 -3
- cirq/testing/consistent_specified_has_unitary_test.py +1 -3
- cirq/testing/equals_tester.py +1 -1
- cirq/testing/equals_tester_test.py +5 -5
- cirq/testing/equivalent_repr_eval_test.py +1 -3
- cirq/testing/gate_features_test.py +6 -6
- cirq/testing/order_tester_test.py +1 -3
- cirq/testing/random_circuit_test.py +1 -3
- cirq/transformers/__init__.py +3 -0
- cirq/transformers/analytical_decompositions/__init__.py +1 -0
- cirq/transformers/analytical_decompositions/three_qubit_decomposition.py +1 -2
- cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py +2 -5
- cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py +38 -0
- cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py +18 -0
- cirq/transformers/expand_composite_test.py +4 -4
- cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +1 -1
- cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +1 -2
- cirq/transformers/merge_k_qubit_gates_test.py +2 -2
- cirq/transformers/qubit_management_transformers.py +177 -0
- cirq/transformers/qubit_management_transformers_test.py +250 -0
- cirq/transformers/routing/route_circuit_cqc.py +23 -4
- cirq/transformers/routing/route_circuit_cqc_test.py +42 -0
- cirq/transformers/stratify.py +10 -11
- cirq/transformers/target_gatesets/compilation_target_gateset_test.py +10 -10
- cirq/transformers/target_gatesets/cz_gateset_test.py +8 -10
- cirq/transformers/transformer_primitives.py +138 -28
- cirq/value/abc_alt_test.py +4 -4
- cirq/value/duration.py +68 -37
- cirq/value/duration_test.py +2 -0
- cirq/value/measurement_key_test.py +1 -1
- cirq/value/product_state.py +4 -8
- cirq/value/value_equality_attr.py +12 -5
- cirq/vis/heatmap.py +7 -4
- cirq/vis/heatmap_test.py +14 -4
- cirq/vis/histogram.py +4 -4
- cirq/vis/state_histogram.py +10 -6
- cirq/vis/state_histogram_test.py +2 -0
- cirq/work/observable_measurement_data_test.py +1 -1
- cirq/work/observable_measurement_test.py +2 -2
- cirq/work/zeros_sampler.py +1 -1
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/METADATA +11 -19
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/RECORD +158 -150
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/WHEEL +1 -1
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/LICENSE +0 -0
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/top_level.txt +0 -0
cirq/__init__.py
CHANGED
|
@@ -224,6 +224,7 @@ from cirq.ops import (
|
|
|
224
224
|
givens,
|
|
225
225
|
GlobalPhaseGate,
|
|
226
226
|
global_phase_operation,
|
|
227
|
+
GreedyQubitManager,
|
|
227
228
|
H,
|
|
228
229
|
HPowGate,
|
|
229
230
|
I,
|
|
@@ -301,6 +302,7 @@ from cirq.ops import (
|
|
|
301
302
|
ry,
|
|
302
303
|
rz,
|
|
303
304
|
S,
|
|
305
|
+
SimpleQubitManager,
|
|
304
306
|
SingleQubitCliffordGate,
|
|
305
307
|
SingleQubitPauliStringGateOperation,
|
|
306
308
|
SQRT_ISWAP,
|
|
@@ -356,6 +358,7 @@ from cirq.transformers import (
|
|
|
356
358
|
is_negligible_turn,
|
|
357
359
|
LineInitialMapper,
|
|
358
360
|
MappingManager,
|
|
361
|
+
map_clean_and_borrowable_qubits,
|
|
359
362
|
map_moments,
|
|
360
363
|
map_operations,
|
|
361
364
|
map_operations_and_unroll,
|
|
@@ -370,6 +373,7 @@ from cirq.transformers import (
|
|
|
370
373
|
optimize_for_target_gateset,
|
|
371
374
|
parameterized_2q_op_to_sqrt_iswap_operations,
|
|
372
375
|
prepare_two_qubit_state_using_cz,
|
|
376
|
+
prepare_two_qubit_state_using_iswap,
|
|
373
377
|
prepare_two_qubit_state_using_sqrt_iswap,
|
|
374
378
|
quantum_shannon_decomposition,
|
|
375
379
|
RouteCQC,
|
|
@@ -437,6 +441,7 @@ from cirq.qis import (
|
|
|
437
441
|
|
|
438
442
|
from cirq.sim import (
|
|
439
443
|
CIRCUIT_LIKE,
|
|
444
|
+
ClassicalStateSimulator,
|
|
440
445
|
CliffordSimulator,
|
|
441
446
|
CliffordState,
|
|
442
447
|
CliffordSimulatorStepResult,
|
cirq/_compat.py
CHANGED
|
@@ -191,6 +191,9 @@ def proper_repr(value: Any) -> str:
|
|
|
191
191
|
if isinstance(value, Dict):
|
|
192
192
|
return '{' + ','.join(f"{proper_repr(k)}: {proper_repr(v)}" for k, v in value.items()) + '}'
|
|
193
193
|
|
|
194
|
+
if hasattr(value, "__qualname__"):
|
|
195
|
+
return f"{value.__module__}.{value.__qualname__}"
|
|
196
|
+
|
|
194
197
|
return repr(value)
|
|
195
198
|
|
|
196
199
|
|
|
@@ -400,23 +403,36 @@ def deprecated_parameter(
|
|
|
400
403
|
_validate_deadline(deadline)
|
|
401
404
|
|
|
402
405
|
def decorator(func: Callable) -> Callable:
|
|
406
|
+
def deprecation_warning():
|
|
407
|
+
qualname = func.__qualname__ if func_name is None else func_name
|
|
408
|
+
_warn_or_error(
|
|
409
|
+
f'The {parameter_desc} parameter of {qualname} was '
|
|
410
|
+
f'used but is deprecated.\n'
|
|
411
|
+
f'It will be removed in cirq {deadline}.\n'
|
|
412
|
+
f'{fix}\n'
|
|
413
|
+
)
|
|
414
|
+
|
|
403
415
|
@functools.wraps(func)
|
|
404
416
|
def decorated_func(*args, **kwargs) -> Any:
|
|
405
417
|
if match(args, kwargs):
|
|
406
418
|
if rewrite is not None:
|
|
407
419
|
args, kwargs = rewrite(args, kwargs)
|
|
420
|
+
deprecation_warning()
|
|
421
|
+
return func(*args, **kwargs)
|
|
408
422
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
)
|
|
423
|
+
@functools.wraps(func)
|
|
424
|
+
async def async_decorated_func(*args, **kwargs) -> Any:
|
|
425
|
+
if match(args, kwargs):
|
|
426
|
+
if rewrite is not None:
|
|
427
|
+
args, kwargs = rewrite(args, kwargs)
|
|
428
|
+
deprecation_warning()
|
|
416
429
|
|
|
417
|
-
return func(*args, **kwargs)
|
|
430
|
+
return await func(*args, **kwargs)
|
|
418
431
|
|
|
419
|
-
|
|
432
|
+
if inspect.iscoroutinefunction(func):
|
|
433
|
+
return async_decorated_func
|
|
434
|
+
else:
|
|
435
|
+
return decorated_func
|
|
420
436
|
|
|
421
437
|
return decorator
|
|
422
438
|
|
|
@@ -436,13 +452,12 @@ def deprecate_attributes(module_name: str, deprecated_attributes: Dict[str, Tupl
|
|
|
436
452
|
will cause a warning for these deprecated attributes.
|
|
437
453
|
"""
|
|
438
454
|
|
|
439
|
-
for
|
|
455
|
+
for deadline, _ in deprecated_attributes.values():
|
|
440
456
|
_validate_deadline(deadline)
|
|
441
457
|
|
|
442
458
|
module = sys.modules[module_name]
|
|
443
459
|
|
|
444
460
|
class Wrapped(ModuleType):
|
|
445
|
-
|
|
446
461
|
__dict__ = module.__dict__
|
|
447
462
|
|
|
448
463
|
# Workaround for: https://github.com/python/mypy/issues/8083
|
cirq/_compat_test.py
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import collections
|
|
15
15
|
import dataclasses
|
|
16
16
|
import importlib.metadata
|
|
17
|
+
import inspect
|
|
17
18
|
import logging
|
|
18
19
|
import multiprocessing
|
|
19
20
|
import os
|
|
@@ -26,7 +27,7 @@ from typing import Any, Callable, Dict, Optional, Tuple
|
|
|
26
27
|
from importlib.machinery import ModuleSpec
|
|
27
28
|
from unittest import mock
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
import duet
|
|
30
31
|
import numpy as np
|
|
31
32
|
import pandas as pd
|
|
32
33
|
import pytest
|
|
@@ -263,6 +264,40 @@ def test_deprecated_parameter():
|
|
|
263
264
|
# pylint: enable=unused-variable
|
|
264
265
|
|
|
265
266
|
|
|
267
|
+
@duet.sync
|
|
268
|
+
async def test_deprecated_parameter_async_function():
|
|
269
|
+
@deprecated_parameter(
|
|
270
|
+
deadline='v1.2',
|
|
271
|
+
fix='Double it yourself.',
|
|
272
|
+
func_name='test_func',
|
|
273
|
+
parameter_desc='double_count',
|
|
274
|
+
match=lambda args, kwargs: 'double_count' in kwargs,
|
|
275
|
+
rewrite=lambda args, kwargs: (args, {'new_count': kwargs['double_count'] * 2}),
|
|
276
|
+
)
|
|
277
|
+
async def f(new_count):
|
|
278
|
+
return new_count
|
|
279
|
+
|
|
280
|
+
assert inspect.iscoroutinefunction(f)
|
|
281
|
+
|
|
282
|
+
# Does not warn on usual use.
|
|
283
|
+
with cirq.testing.assert_logs(count=0):
|
|
284
|
+
assert await f(1) == 1
|
|
285
|
+
assert await f(new_count=1) == 1
|
|
286
|
+
|
|
287
|
+
with cirq.testing.assert_deprecated(
|
|
288
|
+
'_compat_test.py:',
|
|
289
|
+
'double_count parameter of test_func was used',
|
|
290
|
+
'will be removed in cirq v1.2',
|
|
291
|
+
'Double it yourself.',
|
|
292
|
+
deadline='v1.2',
|
|
293
|
+
):
|
|
294
|
+
# pylint: disable=unexpected-keyword-arg
|
|
295
|
+
# pylint: disable=no-value-for-parameter
|
|
296
|
+
assert await f(double_count=1) == 2
|
|
297
|
+
# pylint: enable=no-value-for-parameter
|
|
298
|
+
# pylint: enable=unexpected-keyword-arg
|
|
299
|
+
|
|
300
|
+
|
|
266
301
|
def test_wrap_module():
|
|
267
302
|
my_module = types.ModuleType('my_module', 'my doc string')
|
|
268
303
|
my_module.foo = 'foo'
|
|
@@ -781,8 +816,7 @@ def test_deprecated_module_deadline_validation():
|
|
|
781
816
|
|
|
782
817
|
def _test_broken_module_1_inner():
|
|
783
818
|
with pytest.raises(
|
|
784
|
-
DeprecatedModuleImportError,
|
|
785
|
-
match="missing_module cannot be imported. " "The typical reasons",
|
|
819
|
+
DeprecatedModuleImportError, match="missing_module cannot be imported. The typical reasons"
|
|
786
820
|
):
|
|
787
821
|
# pylint: disable=unused-import
|
|
788
822
|
import cirq.testing._compat_test_data.broken_ref as br # type: ignore
|
cirq/_version.py
CHANGED
|
@@ -1 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
# Copyright 2018 The Cirq Developers
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Define version number here, read it from setup.py automatically,
|
|
16
|
+
and warn users that the latest version of cirq uses python 3.9+"""
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
|
|
20
|
+
if sys.version_info < (3, 9, 0): # pragma: no cover
|
|
21
|
+
raise SystemError(
|
|
22
|
+
"You installed the latest version of cirq but aren't on python 3.9+.\n"
|
|
23
|
+
'To fix this error, you need to either:\n'
|
|
24
|
+
'\n'
|
|
25
|
+
'A) Update to python 3.9 or later.\n'
|
|
26
|
+
'- OR -\n'
|
|
27
|
+
'B) Explicitly install an older deprecated-but-compatible version '
|
|
28
|
+
'of cirq (e.g. "python -m pip install cirq==1.1.*")'
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__version__ = "1.3.0"
|
cirq/_version_test.py
CHANGED
cirq/circuits/circuit.py
CHANGED
|
@@ -149,11 +149,26 @@ class AbstractCircuit(abc.ABC):
|
|
|
149
149
|
"""Create a circuit from moment op trees.
|
|
150
150
|
|
|
151
151
|
Args:
|
|
152
|
-
*moments: Op tree for each moment.
|
|
152
|
+
*moments: Op tree for each moment. If an op tree is a moment, it
|
|
153
|
+
will be included directly in the new circuit. If an op tree is
|
|
154
|
+
a circuit, it will be frozen, wrapped in a CircuitOperation, and
|
|
155
|
+
included in its own moment in the new circuit. Otherwise, the
|
|
156
|
+
op tree will be passed to `cirq.Moment` to create a new moment
|
|
157
|
+
which is then included in the new circuit. Note that in the
|
|
158
|
+
latter case we have the normal restriction that operations in a
|
|
159
|
+
moment must be applied to disjoint sets of qubits.
|
|
153
160
|
"""
|
|
154
|
-
return cls._from_moments(
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
return cls._from_moments(cls._make_moments(moments))
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def _make_moments(moments: Iterable['cirq.OP_TREE']) -> Iterator['cirq.Moment']:
|
|
165
|
+
for m in moments:
|
|
166
|
+
if isinstance(m, Moment):
|
|
167
|
+
yield m
|
|
168
|
+
elif isinstance(m, AbstractCircuit):
|
|
169
|
+
yield Moment(m.freeze().to_op())
|
|
170
|
+
else:
|
|
171
|
+
yield Moment(m)
|
|
157
172
|
|
|
158
173
|
@classmethod
|
|
159
174
|
@abc.abstractmethod
|
|
@@ -173,28 +188,20 @@ class AbstractCircuit(abc.ABC):
|
|
|
173
188
|
def moments(self) -> Sequence['cirq.Moment']:
|
|
174
189
|
pass
|
|
175
190
|
|
|
191
|
+
@abc.abstractmethod
|
|
176
192
|
def freeze(self) -> 'cirq.FrozenCircuit':
|
|
177
193
|
"""Creates a FrozenCircuit from this circuit.
|
|
178
194
|
|
|
179
195
|
If 'self' is a FrozenCircuit, the original object is returned.
|
|
180
196
|
"""
|
|
181
|
-
from cirq.circuits import FrozenCircuit
|
|
182
|
-
|
|
183
|
-
if isinstance(self, FrozenCircuit):
|
|
184
|
-
return self
|
|
185
|
-
|
|
186
|
-
return FrozenCircuit(self, strategy=InsertStrategy.EARLIEST)
|
|
187
197
|
|
|
198
|
+
@abc.abstractmethod
|
|
188
199
|
def unfreeze(self, copy: bool = True) -> 'cirq.Circuit':
|
|
189
200
|
"""Creates a Circuit from this circuit.
|
|
190
201
|
|
|
191
202
|
Args:
|
|
192
203
|
copy: If True and 'self' is a Circuit, returns a copy that circuit.
|
|
193
204
|
"""
|
|
194
|
-
if isinstance(self, Circuit):
|
|
195
|
-
return Circuit.copy(self) if copy else self
|
|
196
|
-
|
|
197
|
-
return Circuit(self, strategy=InsertStrategy.EARLIEST)
|
|
198
205
|
|
|
199
206
|
def __bool__(self):
|
|
200
207
|
return bool(self.moments)
|
|
@@ -272,12 +279,15 @@ class AbstractCircuit(abc.ABC):
|
|
|
272
279
|
def __str__(self) -> str:
|
|
273
280
|
return self.to_text_diagram()
|
|
274
281
|
|
|
275
|
-
def
|
|
276
|
-
cls_name = self.__class__.__name__
|
|
282
|
+
def _repr_args(self) -> str:
|
|
277
283
|
args = []
|
|
278
284
|
if self.moments:
|
|
279
285
|
args.append(_list_repr_with_indented_item_lines(self.moments))
|
|
280
|
-
return f'
|
|
286
|
+
return f'{", ".join(args)}'
|
|
287
|
+
|
|
288
|
+
def __repr__(self) -> str:
|
|
289
|
+
cls_name = self.__class__.__name__
|
|
290
|
+
return f'cirq.{cls_name}({self._repr_args()})'
|
|
281
291
|
|
|
282
292
|
def _repr_pretty_(self, p: Any, cycle: bool) -> None:
|
|
283
293
|
"""Print ASCII diagram in Jupyter."""
|
|
@@ -804,6 +814,9 @@ class AbstractCircuit(abc.ABC):
|
|
|
804
814
|
"""
|
|
805
815
|
return protocols.is_measurement(self)
|
|
806
816
|
|
|
817
|
+
def _is_measurement_(self) -> bool:
|
|
818
|
+
return any(protocols.is_measurement(op) for op in self.all_operations())
|
|
819
|
+
|
|
807
820
|
def are_all_measurements_terminal(self) -> bool:
|
|
808
821
|
"""Whether all measurement gates are at the end of the circuit.
|
|
809
822
|
|
|
@@ -1365,8 +1378,7 @@ class AbstractCircuit(abc.ABC):
|
|
|
1365
1378
|
self._to_qasm_output(header, precision, qubit_order).save(file_path)
|
|
1366
1379
|
|
|
1367
1380
|
def _json_dict_(self):
|
|
1368
|
-
|
|
1369
|
-
return ret
|
|
1381
|
+
return protocols.obj_to_dict_helper(self, ['moments'])
|
|
1370
1382
|
|
|
1371
1383
|
@classmethod
|
|
1372
1384
|
def _from_json_dict_(cls, moments, **kwargs):
|
|
@@ -1462,14 +1474,14 @@ class AbstractCircuit(abc.ABC):
|
|
|
1462
1474
|
|
|
1463
1475
|
Beware that this method is *not* associative. For example:
|
|
1464
1476
|
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1477
|
+
>>> a, b = cirq.LineQubit.range(2)
|
|
1478
|
+
>>> A = cirq.Circuit(cirq.H(a))
|
|
1479
|
+
>>> B = cirq.Circuit(cirq.H(b))
|
|
1480
|
+
>>> f = cirq.Circuit.concat_ragged
|
|
1481
|
+
>>> f(f(A, B), A) == f(A, f(B, A))
|
|
1482
|
+
False
|
|
1483
|
+
>>> len(f(f(f(A, B), A), B)) == len(f(f(A, f(B, A)), B))
|
|
1484
|
+
False
|
|
1473
1485
|
|
|
1474
1486
|
Args:
|
|
1475
1487
|
*circuits: The circuits to concatenate.
|
|
@@ -1741,6 +1753,16 @@ class Circuit(AbstractCircuit):
|
|
|
1741
1753
|
circuit.
|
|
1742
1754
|
"""
|
|
1743
1755
|
self._moments: List['cirq.Moment'] = []
|
|
1756
|
+
|
|
1757
|
+
# Implementation note: the following cached properties are set lazily and then
|
|
1758
|
+
# invalidated and reset to None in `self._mutated()`, which is called any time
|
|
1759
|
+
# `self._moments` is changed.
|
|
1760
|
+
self._all_qubits: Optional[FrozenSet['cirq.Qid']] = None
|
|
1761
|
+
self._frozen: Optional['cirq.FrozenCircuit'] = None
|
|
1762
|
+
self._is_measurement: Optional[bool] = None
|
|
1763
|
+
self._is_parameterized: Optional[bool] = None
|
|
1764
|
+
self._parameter_names: Optional[AbstractSet[str]] = None
|
|
1765
|
+
|
|
1744
1766
|
flattened_contents = tuple(ops.flatten_to_ops_or_moments(contents))
|
|
1745
1767
|
if all(isinstance(c, Moment) for c in flattened_contents):
|
|
1746
1768
|
self._moments[:] = cast(Iterable[Moment], flattened_contents)
|
|
@@ -1751,6 +1773,14 @@ class Circuit(AbstractCircuit):
|
|
|
1751
1773
|
else:
|
|
1752
1774
|
self.append(flattened_contents, strategy=strategy)
|
|
1753
1775
|
|
|
1776
|
+
def _mutated(self) -> None:
|
|
1777
|
+
"""Clear cached properties in response to this circuit being mutated."""
|
|
1778
|
+
self._all_qubits = None
|
|
1779
|
+
self._frozen = None
|
|
1780
|
+
self._is_measurement = None
|
|
1781
|
+
self._is_parameterized = None
|
|
1782
|
+
self._parameter_names = None
|
|
1783
|
+
|
|
1754
1784
|
@classmethod
|
|
1755
1785
|
def _from_moments(cls, moments: Iterable['cirq.Moment']) -> 'Circuit':
|
|
1756
1786
|
new_circuit = Circuit()
|
|
@@ -1791,7 +1821,6 @@ class Circuit(AbstractCircuit):
|
|
|
1791
1821
|
|
|
1792
1822
|
# "mop" means current moment-or-operation
|
|
1793
1823
|
for mop in ops.flatten_to_ops_or_moments(contents):
|
|
1794
|
-
|
|
1795
1824
|
# Identify the index of the moment to place this `mop` into.
|
|
1796
1825
|
placement_index = get_earliest_accommodating_moment_index(
|
|
1797
1826
|
mop, qubit_indices, mkey_indices, ckey_indices, length
|
|
@@ -1814,6 +1843,41 @@ class Circuit(AbstractCircuit):
|
|
|
1814
1843
|
def __copy__(self) -> 'cirq.Circuit':
|
|
1815
1844
|
return self.copy()
|
|
1816
1845
|
|
|
1846
|
+
def freeze(self) -> 'cirq.FrozenCircuit':
|
|
1847
|
+
"""Gets a frozen version of this circuit.
|
|
1848
|
+
|
|
1849
|
+
Repeated calls to `.freeze()` will return the same FrozenCircuit
|
|
1850
|
+
instance as long as this circuit is not mutated.
|
|
1851
|
+
"""
|
|
1852
|
+
from cirq.circuits.frozen_circuit import FrozenCircuit
|
|
1853
|
+
|
|
1854
|
+
if self._frozen is None:
|
|
1855
|
+
self._frozen = FrozenCircuit.from_moments(*self._moments)
|
|
1856
|
+
return self._frozen
|
|
1857
|
+
|
|
1858
|
+
def unfreeze(self, copy: bool = True) -> 'cirq.Circuit':
|
|
1859
|
+
return self.copy() if copy else self
|
|
1860
|
+
|
|
1861
|
+
def all_qubits(self) -> FrozenSet['cirq.Qid']:
|
|
1862
|
+
if self._all_qubits is None:
|
|
1863
|
+
self._all_qubits = super().all_qubits()
|
|
1864
|
+
return self._all_qubits
|
|
1865
|
+
|
|
1866
|
+
def _is_measurement_(self) -> bool:
|
|
1867
|
+
if self._is_measurement is None:
|
|
1868
|
+
self._is_measurement = super()._is_measurement_()
|
|
1869
|
+
return self._is_measurement
|
|
1870
|
+
|
|
1871
|
+
def _is_parameterized_(self) -> bool:
|
|
1872
|
+
if self._is_parameterized is None:
|
|
1873
|
+
self._is_parameterized = super()._is_parameterized_()
|
|
1874
|
+
return self._is_parameterized
|
|
1875
|
+
|
|
1876
|
+
def _parameter_names_(self) -> AbstractSet[str]:
|
|
1877
|
+
if self._parameter_names is None:
|
|
1878
|
+
self._parameter_names = super()._parameter_names_()
|
|
1879
|
+
return self._parameter_names
|
|
1880
|
+
|
|
1817
1881
|
def copy(self) -> 'Circuit':
|
|
1818
1882
|
"""Return a copy of this circuit."""
|
|
1819
1883
|
copied_circuit = Circuit()
|
|
@@ -1839,11 +1903,13 @@ class Circuit(AbstractCircuit):
|
|
|
1839
1903
|
raise TypeError('Can only assign Moments into Circuits.')
|
|
1840
1904
|
|
|
1841
1905
|
self._moments[key] = value
|
|
1906
|
+
self._mutated()
|
|
1842
1907
|
|
|
1843
1908
|
# pylint: enable=function-redefined
|
|
1844
1909
|
|
|
1845
1910
|
def __delitem__(self, key: Union[int, slice]):
|
|
1846
1911
|
del self._moments[key]
|
|
1912
|
+
self._mutated()
|
|
1847
1913
|
|
|
1848
1914
|
def __iadd__(self, other):
|
|
1849
1915
|
self.append(other)
|
|
@@ -1872,6 +1938,7 @@ class Circuit(AbstractCircuit):
|
|
|
1872
1938
|
if not isinstance(repetitions, (int, np.integer)):
|
|
1873
1939
|
return NotImplemented
|
|
1874
1940
|
self._moments *= int(repetitions)
|
|
1941
|
+
self._mutated()
|
|
1875
1942
|
return self
|
|
1876
1943
|
|
|
1877
1944
|
def __mul__(self, repetitions: _INT_TYPE):
|
|
@@ -2015,6 +2082,7 @@ class Circuit(AbstractCircuit):
|
|
|
2015
2082
|
|
|
2016
2083
|
if strategy is InsertStrategy.NEW or strategy is InsertStrategy.NEW_THEN_INLINE:
|
|
2017
2084
|
self._moments.insert(splitter_index, Moment())
|
|
2085
|
+
self._mutated()
|
|
2018
2086
|
return splitter_index
|
|
2019
2087
|
|
|
2020
2088
|
if strategy is InsertStrategy.INLINE:
|
|
@@ -2082,6 +2150,7 @@ class Circuit(AbstractCircuit):
|
|
|
2082
2150
|
k = max(k, p + 1)
|
|
2083
2151
|
if strategy is InsertStrategy.NEW_THEN_INLINE:
|
|
2084
2152
|
strategy = InsertStrategy.INLINE
|
|
2153
|
+
self._mutated()
|
|
2085
2154
|
return k
|
|
2086
2155
|
|
|
2087
2156
|
def insert_into_range(self, operations: 'cirq.OP_TREE', start: int, end: int) -> int:
|
|
@@ -2118,6 +2187,7 @@ class Circuit(AbstractCircuit):
|
|
|
2118
2187
|
|
|
2119
2188
|
self._moments[i] = self._moments[i].with_operation(op)
|
|
2120
2189
|
op_index += 1
|
|
2190
|
+
self._mutated()
|
|
2121
2191
|
|
|
2122
2192
|
if op_index >= len(flat_ops):
|
|
2123
2193
|
return end
|
|
@@ -2163,6 +2233,7 @@ class Circuit(AbstractCircuit):
|
|
|
2163
2233
|
if n_new_moments > 0:
|
|
2164
2234
|
insert_index = min(late_frontier.values())
|
|
2165
2235
|
self._moments[insert_index:insert_index] = [Moment()] * n_new_moments
|
|
2236
|
+
self._mutated()
|
|
2166
2237
|
for q in update_qubits:
|
|
2167
2238
|
if early_frontier.get(q, 0) > insert_index:
|
|
2168
2239
|
early_frontier[q] += n_new_moments
|
|
@@ -2189,13 +2260,12 @@ class Circuit(AbstractCircuit):
|
|
|
2189
2260
|
if len(operations) != len(insertion_indices):
|
|
2190
2261
|
raise ValueError('operations and insertion_indices must have the same length.')
|
|
2191
2262
|
self._moments += [Moment() for _ in range(1 + max(insertion_indices) - len(self))]
|
|
2263
|
+
self._mutated()
|
|
2192
2264
|
moment_to_ops: Dict[int, List['cirq.Operation']] = defaultdict(list)
|
|
2193
2265
|
for op_index, moment_index in enumerate(insertion_indices):
|
|
2194
2266
|
moment_to_ops[moment_index].append(operations[op_index])
|
|
2195
2267
|
for moment_index, new_ops in moment_to_ops.items():
|
|
2196
|
-
self._moments[moment_index] =
|
|
2197
|
-
self._moments[moment_index].operations + tuple(new_ops)
|
|
2198
|
-
)
|
|
2268
|
+
self._moments[moment_index] = self._moments[moment_index].with_operations(*new_ops)
|
|
2199
2269
|
|
|
2200
2270
|
def insert_at_frontier(
|
|
2201
2271
|
self,
|
|
@@ -2257,6 +2327,7 @@ class Circuit(AbstractCircuit):
|
|
|
2257
2327
|
old_op for old_op in copy._moments[i].operations if op != old_op
|
|
2258
2328
|
)
|
|
2259
2329
|
self._moments = copy._moments
|
|
2330
|
+
self._mutated()
|
|
2260
2331
|
|
|
2261
2332
|
def batch_replace(
|
|
2262
2333
|
self, replacements: Iterable[Tuple[int, 'cirq.Operation', 'cirq.Operation']]
|
|
@@ -2281,6 +2352,7 @@ class Circuit(AbstractCircuit):
|
|
|
2281
2352
|
old_op if old_op != op else new_op for old_op in copy._moments[i].operations
|
|
2282
2353
|
)
|
|
2283
2354
|
self._moments = copy._moments
|
|
2355
|
+
self._mutated()
|
|
2284
2356
|
|
|
2285
2357
|
def batch_insert_into(self, insert_intos: Iterable[Tuple[int, 'cirq.OP_TREE']]) -> None:
|
|
2286
2358
|
"""Inserts operations into empty spaces in existing moments.
|
|
@@ -2301,6 +2373,7 @@ class Circuit(AbstractCircuit):
|
|
|
2301
2373
|
for i, insertions in insert_intos:
|
|
2302
2374
|
copy._moments[i] = copy._moments[i].with_operations(insertions)
|
|
2303
2375
|
self._moments = copy._moments
|
|
2376
|
+
self._mutated()
|
|
2304
2377
|
|
|
2305
2378
|
def batch_insert(self, insertions: Iterable[Tuple[int, 'cirq.OP_TREE']]) -> None:
|
|
2306
2379
|
"""Applies a batched insert operation to the circuit.
|
|
@@ -2335,6 +2408,7 @@ class Circuit(AbstractCircuit):
|
|
|
2335
2408
|
if next_index > insert_index:
|
|
2336
2409
|
shift += next_index - insert_index
|
|
2337
2410
|
self._moments = copy._moments
|
|
2411
|
+
self._mutated()
|
|
2338
2412
|
|
|
2339
2413
|
def append(
|
|
2340
2414
|
self,
|
|
@@ -2365,6 +2439,7 @@ class Circuit(AbstractCircuit):
|
|
|
2365
2439
|
for k in moment_indices:
|
|
2366
2440
|
if 0 <= k < len(self._moments):
|
|
2367
2441
|
self._moments[k] = self._moments[k].without_operations_touching(qubits)
|
|
2442
|
+
self._mutated()
|
|
2368
2443
|
|
|
2369
2444
|
@property
|
|
2370
2445
|
def moments(self) -> Sequence['cirq.Moment']:
|
|
@@ -2450,7 +2525,6 @@ def _draw_moment_annotations(
|
|
|
2450
2525
|
first_annotation_row: int,
|
|
2451
2526
|
transpose: bool,
|
|
2452
2527
|
):
|
|
2453
|
-
|
|
2454
2528
|
for k, annotation in enumerate(_get_moment_annotations(moment)):
|
|
2455
2529
|
args = protocols.CircuitDiagramInfoArgs(
|
|
2456
2530
|
known_qubits=(),
|
|
@@ -61,9 +61,9 @@ def _full_join_string_lists(
|
|
|
61
61
|
list1: Optional[Sequence[str]], list2: Optional[Sequence[str]]
|
|
62
62
|
) -> Optional[Sequence[str]]:
|
|
63
63
|
if list1 is None and list2 is None:
|
|
64
|
-
return None #
|
|
64
|
+
return None # pragma: no cover
|
|
65
65
|
if list1 is None:
|
|
66
|
-
return list2 #
|
|
66
|
+
return list2 # pragma: no cover
|
|
67
67
|
if list2 is None:
|
|
68
68
|
return list1
|
|
69
69
|
return [f'{first}{REPETITION_ID_SEPARATOR}{second}' for first in list1 for second in list2]
|
|
@@ -351,7 +351,7 @@ def test_repeat_zero_times(add_measurements, use_repetition_ids, initial_reps):
|
|
|
351
351
|
|
|
352
352
|
|
|
353
353
|
def test_no_repetition_ids():
|
|
354
|
-
def default_repetition_ids(self):
|
|
354
|
+
def default_repetition_ids(self): # pragma: no cover
|
|
355
355
|
assert False, "Should not call default_repetition_ids"
|
|
356
356
|
|
|
357
357
|
with mock.patch.object(circuit_operation, 'default_repetition_ids', new=default_repetition_ids):
|