cirq-core 1.7.0.dev20250924231107__py3-none-any.whl → 1.7.0.dev20251203004401__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 +1 -0
- cirq/_compat.py +3 -2
- cirq/_compat_test.py +16 -15
- cirq/_doc.py +4 -3
- cirq/_import.py +2 -1
- cirq/_version.py +1 -1
- cirq/_version_test.py +1 -1
- cirq/circuits/_bucket_priority_queue.py +2 -1
- cirq/circuits/circuit.py +7 -13
- cirq/circuits/circuit_operation.py +2 -1
- cirq/circuits/circuit_test.py +13 -12
- cirq/circuits/frozen_circuit.py +3 -2
- cirq/circuits/moment.py +3 -15
- cirq/circuits/optimization_pass.py +2 -1
- cirq/circuits/qasm_output.py +39 -10
- cirq/circuits/qasm_output_test.py +51 -2
- cirq/circuits/text_diagram_drawer.py +2 -1
- cirq/contrib/acquaintance/bipartite.py +2 -1
- cirq/contrib/acquaintance/devices.py +1 -1
- cirq/contrib/acquaintance/executor.py +4 -5
- cirq/contrib/acquaintance/executor_test.py +2 -1
- cirq/contrib/acquaintance/gates.py +2 -1
- cirq/contrib/acquaintance/gates_test.py +1 -1
- cirq/contrib/acquaintance/inspection_utils.py +2 -1
- cirq/contrib/acquaintance/mutation_utils.py +2 -1
- cirq/contrib/acquaintance/optimizers.py +2 -1
- cirq/contrib/acquaintance/permutation.py +2 -1
- cirq/contrib/acquaintance/permutation_test.py +1 -1
- cirq/contrib/acquaintance/shift.py +2 -1
- cirq/contrib/acquaintance/shift_swap_network.py +2 -1
- cirq/contrib/acquaintance/strategies/complete.py +3 -2
- cirq/contrib/acquaintance/strategies/cubic.py +2 -1
- cirq/contrib/acquaintance/strategies/quartic_paired.py +2 -1
- cirq/contrib/acquaintance/strategies/quartic_paired_test.py +1 -1
- cirq/contrib/acquaintance/testing.py +2 -1
- cirq/contrib/acquaintance/topological_sort.py +2 -1
- cirq/contrib/bayesian_network/bayesian_network_gate.py +3 -2
- cirq/contrib/circuitdag/circuit_dag.py +4 -2
- cirq/contrib/custom_simulators/custom_state_simulator.py +2 -1
- cirq/contrib/custom_simulators/custom_state_simulator_test.py +1 -1
- cirq/contrib/graph_device/graph_device.py +2 -1
- cirq/contrib/graph_device/graph_device_test.py +2 -1
- cirq/contrib/graph_device/hypergraph.py +2 -1
- cirq/contrib/graph_device/uniform_graph_device.py +2 -1
- cirq/contrib/json.py +14 -2
- cirq/contrib/json_test_data/BayesianNetworkGate.json +10 -0
- cirq/contrib/json_test_data/BayesianNetworkGate.repr +3 -0
- cirq/contrib/json_test_data/QuantumVolumeResult.json +169 -0
- cirq/contrib/json_test_data/QuantumVolumeResult.repr +22 -0
- cirq/contrib/json_test_data/SwapPermutationGate.json +3 -0
- cirq/contrib/json_test_data/SwapPermutationGate.repr +1 -0
- cirq/contrib/json_test_data/spec.py +0 -2
- cirq/contrib/noise_models/noise_models.py +2 -1
- cirq/contrib/paulistring/clifford_optimize.py +20 -2
- cirq/contrib/paulistring/optimize.py +1 -1
- cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation.py +146 -35
- cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation_test.py +74 -171
- cirq/contrib/paulistring/recombine.py +5 -2
- cirq/contrib/paulistring/separate.py +1 -1
- cirq/contrib/qasm_import/_parser.py +2 -1
- cirq/contrib/qasm_import/_parser_test.py +3 -3
- cirq/contrib/quantikz/__init__.py +21 -0
- cirq/contrib/quantikz/circuit_to_latex_quantikz.py +680 -0
- cirq/contrib/quantikz/circuit_to_latex_quantikz_test.py +253 -0
- cirq/contrib/quantikz/circuit_to_latex_render.py +424 -0
- cirq/contrib/quantikz/circuit_to_latex_render_test.py +44 -0
- cirq/contrib/quantum_volume/quantum_volume.py +2 -1
- cirq/contrib/quimb/density_matrix.py +1 -1
- cirq/contrib/quimb/grid_circuits.py +2 -1
- cirq/contrib/quimb/grid_circuits_test.py +1 -1
- cirq/contrib/quimb/mps_simulator.py +4 -3
- cirq/contrib/quimb/state_vector.py +2 -1
- cirq/contrib/quirk/export_to_quirk.py +2 -1
- cirq/contrib/quirk/linearize_circuit.py +1 -1
- cirq/contrib/quirk/quirk_gate.py +2 -1
- cirq/contrib/routing/device.py +1 -1
- cirq/contrib/routing/greedy.py +2 -1
- cirq/contrib/routing/initialization.py +2 -1
- cirq/contrib/routing/router.py +2 -1
- cirq/contrib/routing/swap_network.py +2 -1
- cirq/contrib/routing/utils.py +2 -1
- cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking.py +7 -5
- cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking_test.py +6 -6
- cirq/devices/device.py +2 -1
- cirq/devices/grid_device_metadata.py +2 -1
- cirq/devices/grid_qubit.py +7 -6
- cirq/devices/insertion_noise_model.py +2 -1
- cirq/devices/line_qubit.py +2 -1
- cirq/devices/named_topologies.py +2 -1
- cirq/devices/noise_model.py +2 -1
- cirq/devices/noise_model_test.py +1 -1
- cirq/devices/noise_properties.py +2 -1
- cirq/devices/superconducting_qubits_noise_properties_test.py +2 -1
- cirq/devices/thermal_noise_model.py +2 -1
- cirq/experiments/__init__.py +2 -0
- cirq/experiments/benchmarking/parallel_xeb.py +2 -1
- cirq/experiments/benchmarking/parallel_xeb_test.py +1 -1
- cirq/experiments/fidelity_estimation.py +2 -1
- cirq/experiments/fidelity_estimation_test.py +1 -1
- cirq/experiments/ghz_2d.py +150 -0
- cirq/experiments/ghz_2d_test.py +155 -0
- cirq/experiments/n_qubit_tomography.py +2 -1
- cirq/experiments/n_qubit_tomography_test.py +1 -1
- cirq/experiments/purity_estimation.py +1 -1
- cirq/experiments/qubit_characterizations.py +2 -1
- cirq/experiments/random_quantum_circuit_generation.py +2 -1
- cirq/experiments/random_quantum_circuit_generation_test.py +2 -1
- cirq/experiments/readout_confusion_matrix.py +2 -1
- cirq/experiments/readout_confusion_matrix_test.py +1 -1
- cirq/experiments/single_qubit_readout_calibration.py +2 -1
- cirq/experiments/single_qubit_readout_calibration_test.py +1 -1
- cirq/experiments/t1_decay_experiment.py +2 -1
- cirq/experiments/two_qubit_xeb.py +2 -1
- cirq/experiments/two_qubit_xeb_test.py +1 -1
- cirq/experiments/xeb_fitting.py +2 -1
- cirq/experiments/xeb_fitting_test.py +1 -1
- cirq/experiments/xeb_sampling.py +5 -3
- cirq/experiments/xeb_sampling_test.py +1 -1
- cirq/experiments/xeb_simulation.py +2 -1
- cirq/experiments/xeb_simulation_test.py +2 -1
- cirq/experiments/z_phase_calibration.py +2 -1
- cirq/experiments/z_phase_calibration_test.py +18 -3
- cirq/interop/quirk/cells/__init__.py +1 -2
- cirq/interop/quirk/cells/all_cells.py +2 -1
- cirq/interop/quirk/cells/arithmetic_cells.py +2 -1
- cirq/interop/quirk/cells/cell.py +2 -1
- cirq/interop/quirk/cells/composite_cell.py +2 -1
- cirq/interop/quirk/cells/composite_cell_test.py +1 -1
- cirq/interop/quirk/cells/control_cells.py +2 -1
- cirq/interop/quirk/cells/frequency_space_cells.py +1 -1
- cirq/interop/quirk/cells/ignored_cells.py +1 -1
- cirq/interop/quirk/cells/input_cells.py +2 -1
- cirq/interop/quirk/cells/input_rotation_cells.py +2 -1
- cirq/interop/quirk/cells/measurement_cells.py +2 -1
- cirq/interop/quirk/cells/parse.py +2 -11
- cirq/interop/quirk/cells/qubit_permutation_cells.py +2 -1
- cirq/interop/quirk/cells/scalar_cells.py +2 -1
- cirq/interop/quirk/cells/single_qubit_rotation_cells.py +2 -1
- cirq/interop/quirk/cells/swap_cell.py +2 -1
- cirq/interop/quirk/cells/unsupported_cells.py +1 -1
- cirq/interop/quirk/url_to_circuit.py +2 -1
- cirq/json_resolver_cache.py +0 -2
- cirq/linalg/decompositions.py +6 -2
- cirq/linalg/decompositions_test.py +1 -0
- cirq/linalg/diagonalize.py +1 -1
- cirq/linalg/predicates.py +2 -1
- cirq/linalg/tolerance.py +2 -1
- cirq/linalg/transformations.py +2 -1
- cirq/ops/arithmetic_operation.py +2 -1
- cirq/ops/arithmetic_operation_test.py +1 -1
- cirq/ops/boolean_hamiltonian.py +4 -3
- cirq/ops/classically_controlled_operation.py +3 -2
- cirq/ops/clifford_gate.py +2 -1
- cirq/ops/clifford_gate_test.py +1 -2
- cirq/ops/common_channels.py +2 -1
- cirq/ops/common_gates.py +2 -1
- cirq/ops/control_values.py +2 -1
- cirq/ops/controlled_gate.py +3 -2
- cirq/ops/controlled_gate_test.py +2 -1
- cirq/ops/controlled_operation.py +3 -2
- cirq/ops/controlled_operation_test.py +2 -1
- cirq/ops/dense_pauli_string.py +3 -13
- cirq/ops/diagonal_gate.py +3 -2
- cirq/ops/eigen_gate.py +9 -7
- cirq/ops/fourier_transform.py +3 -2
- cirq/ops/fourier_transform_test.py +2 -4
- cirq/ops/fsim_gate.py +3 -2
- cirq/ops/gate_operation.py +8 -12
- cirq/ops/gateset.py +22 -2
- cirq/ops/global_phase_op.py +3 -2
- cirq/ops/greedy_qubit_manager.py +2 -1
- cirq/ops/identity.py +2 -1
- cirq/ops/kraus_channel.py +2 -1
- cirq/ops/linear_combinations.py +8 -3
- cirq/ops/linear_combinations_test.py +23 -1
- cirq/ops/matrix_gates.py +2 -1
- cirq/ops/measure_util.py +2 -1
- cirq/ops/measurement_gate.py +2 -1
- cirq/ops/mixed_unitary_channel.py +2 -1
- cirq/ops/named_qubit.py +2 -2
- cirq/ops/op_tree.py +2 -1
- cirq/ops/parallel_gate.py +3 -2
- cirq/ops/parity_gates.py +2 -1
- cirq/ops/pauli_interaction_gate.py +2 -1
- cirq/ops/pauli_measurement_gate.py +2 -1
- cirq/ops/pauli_string.py +6 -12
- cirq/ops/pauli_string_phasor.py +3 -2
- cirq/ops/pauli_string_raw_types.py +2 -1
- cirq/ops/pauli_string_test.py +2 -2
- cirq/ops/pauli_sum_exponential.py +2 -1
- cirq/ops/permutation_gate.py +2 -1
- cirq/ops/phased_iswap_gate.py +3 -2
- cirq/ops/phased_x_gate.py +5 -4
- cirq/ops/phased_x_z_gate.py +12 -5
- cirq/ops/projector.py +2 -1
- cirq/ops/qubit_manager.py +2 -1
- cirq/ops/qubit_order.py +2 -1
- cirq/ops/qubit_order_or_list.py +1 -1
- cirq/ops/random_gate_channel.py +3 -2
- cirq/ops/raw_types.py +7 -15
- cirq/ops/raw_types_test.py +4 -3
- cirq/ops/state_preparation_channel.py +2 -1
- cirq/ops/three_qubit_gates.py +3 -2
- cirq/ops/two_qubit_diagonal_gate.py +3 -2
- cirq/ops/uniform_superposition_gate.py +2 -1
- cirq/ops/wait_gate.py +10 -4
- cirq/protocols/act_on_protocol.py +2 -1
- cirq/protocols/act_on_protocol_test.py +2 -1
- cirq/protocols/apply_channel_protocol.py +2 -1
- cirq/protocols/apply_mixture_protocol.py +2 -1
- cirq/protocols/apply_mixture_protocol_test.py +2 -1
- cirq/protocols/apply_unitary_protocol.py +2 -1
- cirq/protocols/apply_unitary_protocol_test.py +2 -0
- cirq/protocols/approximate_equality_protocol.py +2 -1
- cirq/protocols/circuit_diagram_info_protocol.py +2 -1
- cirq/protocols/decompose_protocol.py +2 -12
- cirq/protocols/has_stabilizer_effect_protocol.py +1 -1
- cirq/protocols/hash_from_pickle_test.py +2 -2
- cirq/protocols/inverse_protocol.py +2 -1
- cirq/protocols/json_serialization.py +2 -1
- cirq/protocols/kraus_protocol.py +4 -3
- cirq/protocols/kraus_protocol_test.py +2 -2
- cirq/protocols/measurement_key_protocol.py +2 -1
- cirq/protocols/mixture_protocol.py +2 -1
- cirq/protocols/pow_protocol.py +2 -1
- cirq/protocols/qasm.py +2 -1
- cirq/protocols/qid_shape_protocol.py +2 -1
- cirq/protocols/resolve_parameters.py +16 -14
- cirq/protocols/trace_distance_bound.py +2 -1
- cirq/protocols/unitary_protocol.py +21 -21
- cirq/qis/channels.py +1 -1
- cirq/qis/channels_test.py +1 -1
- cirq/qis/clifford_tableau.py +2 -1
- cirq/qis/entropy.py +2 -2
- cirq/qis/quantum_state_representation.py +2 -1
- cirq/qis/states.py +7 -2
- cirq/sim/classical_simulator.py +2 -1
- cirq/sim/clifford/clifford_simulator.py +2 -1
- cirq/sim/clifford/clifford_simulator_test.py +1 -1
- cirq/sim/clifford/clifford_tableau_simulation_state.py +2 -1
- cirq/sim/clifford/stabilizer_ch_form_simulation_state.py +2 -1
- cirq/sim/clifford/stabilizer_sampler.py +1 -1
- cirq/sim/clifford/stabilizer_simulation_state.py +2 -1
- cirq/sim/clifford/stabilizer_state_ch_form.py +3 -2
- cirq/sim/clifford/stabilizer_state_ch_form_test.py +0 -1
- cirq/sim/density_matrix_simulation_state.py +2 -1
- cirq/sim/density_matrix_simulator.py +2 -1
- cirq/sim/density_matrix_utils.py +2 -1
- cirq/sim/mux.py +2 -1
- cirq/sim/mux_test.py +0 -1
- cirq/sim/simulation_product_state.py +2 -1
- cirq/sim/simulation_product_state_test.py +2 -1
- cirq/sim/simulation_state.py +2 -1
- cirq/sim/simulation_state_base.py +2 -1
- cirq/sim/simulation_state_test.py +2 -1
- cirq/sim/simulation_utils.py +2 -1
- cirq/sim/simulator.py +2 -1
- cirq/sim/simulator_base.py +2 -1
- cirq/sim/simulator_base_test.py +2 -1
- cirq/sim/simulator_test.py +2 -1
- cirq/sim/sparse_simulator.py +2 -1
- cirq/sim/state_vector.py +2 -1
- cirq/sim/state_vector_simulation_state.py +2 -1
- cirq/sim/state_vector_simulator.py +2 -1
- cirq/sim/state_vector_test.py +1 -2
- cirq/study/__init__.py +1 -0
- cirq/study/flatten_expressions.py +2 -1
- cirq/study/resolver.py +31 -18
- cirq/study/resolver_test.py +1 -1
- cirq/study/result.py +2 -1
- cirq/study/sweepable.py +2 -1
- cirq/study/sweeps.py +26 -1
- cirq/study/sweeps_test.py +24 -0
- cirq/testing/_compat_test_data/__init__.py +3 -3
- cirq/testing/circuit_compare.py +2 -1
- cirq/testing/consistent_act_on_test.py +1 -1
- cirq/testing/consistent_controlled_gate_op.py +1 -1
- cirq/testing/consistent_controlled_gate_op_test.py +2 -1
- cirq/testing/consistent_protocols.py +2 -1
- cirq/testing/consistent_protocols_test.py +3 -3
- cirq/testing/consistent_qasm.py +2 -1
- cirq/testing/consistent_resolve_parameters.py +1 -1
- cirq/testing/consistent_unitary.py +1 -1
- cirq/testing/consistent_unitary_test.py +1 -1
- cirq/testing/deprecation.py +1 -1
- cirq/testing/devices.py +3 -2
- cirq/testing/equals_tester.py +4 -3
- cirq/testing/equivalent_basis_map.py +4 -2
- cirq/testing/json.py +3 -2
- cirq/testing/logs.py +1 -1
- cirq/testing/op_tree.py +1 -1
- cirq/testing/order_tester.py +2 -2
- cirq/testing/pytest_utils.py +2 -1
- cirq/testing/random_circuit.py +2 -1
- cirq/testing/random_circuit_test.py +2 -1
- cirq/testing/repr_pretty_tester.py +3 -3
- cirq/transformers/__init__.py +1 -0
- cirq/transformers/_connected_component.py +231 -0
- cirq/transformers/_connected_component_test.py +200 -0
- cirq/transformers/align_test.py +13 -13
- cirq/transformers/analytical_decompositions/clifford_decomposition.py +8 -7
- cirq/transformers/analytical_decompositions/clifford_decomposition_test.py +5 -5
- cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +11 -10
- cirq/transformers/analytical_decompositions/controlled_gate_decomposition_test.py +6 -6
- cirq/transformers/analytical_decompositions/cphase_to_fsim.py +3 -2
- cirq/transformers/analytical_decompositions/cphase_to_fsim_test.py +11 -10
- cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py +8 -7
- cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py +17 -20
- cirq/transformers/analytical_decompositions/single_qubit_decompositions_test.py +33 -27
- cirq/transformers/analytical_decompositions/single_to_two_qubit_isometry_test.py +1 -1
- cirq/transformers/analytical_decompositions/three_qubit_decomposition.py +1 -1
- cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py +12 -11
- cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +5 -2
- cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +3 -3
- cirq/transformers/analytical_decompositions/two_qubit_to_fsim.py +2 -1
- cirq/transformers/analytical_decompositions/two_qubit_to_ms.py +2 -1
- cirq/transformers/analytical_decompositions/two_qubit_to_ms_test.py +2 -2
- cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap.py +2 -1
- cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py +32 -30
- cirq/transformers/drop_negligible_operations_test.py +7 -7
- cirq/transformers/dynamical_decoupling.py +185 -112
- cirq/transformers/dynamical_decoupling_test.py +195 -201
- cirq/transformers/eject_phased_paulis.py +2 -1
- cirq/transformers/eject_phased_paulis_test.py +3 -2
- cirq/transformers/eject_z.py +4 -3
- cirq/transformers/eject_z_test.py +23 -25
- cirq/transformers/expand_composite.py +3 -2
- cirq/transformers/expand_composite_test.py +14 -14
- cirq/transformers/gauge_compiling/__init__.py +8 -0
- cirq/transformers/gauge_compiling/gauge_compiling.py +3 -2
- cirq/transformers/gauge_compiling/gauge_compiling_test.py +14 -12
- cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +3 -3
- cirq/transformers/gauge_compiling/idle_moments_gauge.py +5 -2
- cirq/transformers/gauge_compiling/multi_moment_cphase_gauge.py +242 -0
- cirq/transformers/gauge_compiling/multi_moment_cphase_gauge_test.py +243 -0
- cirq/transformers/gauge_compiling/multi_moment_gauge_compiling.py +151 -0
- cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +2 -1
- cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +1 -1
- cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py +6 -6
- cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +2 -1
- cirq/transformers/measurement_transformers.py +2 -1
- cirq/transformers/measurement_transformers_test.py +45 -39
- cirq/transformers/merge_k_qubit_gates.py +2 -1
- cirq/transformers/merge_k_qubit_gates_test.py +1 -1
- cirq/transformers/merge_single_qubit_gates.py +2 -1
- cirq/transformers/merge_single_qubit_gates_test.py +22 -22
- cirq/transformers/noise_adding_test.py +2 -2
- cirq/transformers/optimize_for_target_gateset.py +2 -1
- cirq/transformers/optimize_for_target_gateset_test.py +11 -9
- cirq/transformers/qubit_management_transformers_test.py +6 -2
- cirq/transformers/routing/mapping_manager.py +2 -1
- cirq/transformers/routing/route_circuit_cqc.py +2 -1
- cirq/transformers/stratify.py +2 -1
- cirq/transformers/symbolize.py +2 -1
- cirq/transformers/tag_transformers.py +2 -1
- cirq/transformers/target_gatesets/compilation_target_gateset.py +2 -1
- cirq/transformers/target_gatesets/cz_gateset.py +2 -1
- cirq/transformers/target_gatesets/cz_gateset_test.py +1 -1
- cirq/transformers/target_gatesets/sqrt_iswap_gateset.py +2 -1
- cirq/transformers/transformer_api.py +2 -1
- cirq/transformers/transformer_primitives.py +271 -145
- cirq/transformers/transformer_primitives_test.py +185 -1
- cirq/value/abc_alt.py +2 -1
- cirq/value/classical_data.py +2 -1
- cirq/value/condition.py +2 -1
- cirq/value/digits.py +9 -2
- cirq/value/duration.py +6 -5
- cirq/value/linear_dict.py +4 -9
- cirq/value/measurement_key.py +2 -1
- cirq/value/periodic_value.py +3 -2
- cirq/value/product_state.py +2 -1
- cirq/value/value_equality_attr.py +2 -1
- cirq/vis/density_matrix.py +1 -1
- cirq/vis/heatmap.py +2 -1
- cirq/vis/histogram.py +2 -1
- cirq/vis/state_histogram.py +2 -1
- cirq/work/collector.py +2 -1
- cirq/work/observable_grouping.py +2 -1
- cirq/work/observable_measurement.py +2 -1
- cirq/work/observable_measurement_data.py +2 -1
- cirq/work/observable_measurement_test.py +1 -1
- cirq/work/observable_readout_calibration.py +2 -1
- cirq/work/observable_readout_calibration_test.py +1 -1
- cirq/work/observable_settings.py +2 -1
- cirq/work/sampler.py +2 -1
- cirq/work/sampler_test.py +1 -1
- {cirq_core-1.7.0.dev20250924231107.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/METADATA +4 -4
- {cirq_core-1.7.0.dev20250924231107.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/RECORD +391 -374
- cirq/contrib/json_test.py +0 -33
- {cirq_core-1.7.0.dev20250924231107.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/WHEEL +0 -0
- {cirq_core-1.7.0.dev20250924231107.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/licenses/LICENSE +0 -0
- {cirq_core-1.7.0.dev20250924231107.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/top_level.txt +0 -0
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import itertools
|
|
18
|
+
from collections.abc import Iterator
|
|
18
19
|
|
|
19
20
|
import numpy as np
|
|
20
21
|
import pytest
|
|
@@ -23,11 +24,11 @@ import sympy
|
|
|
23
24
|
import cirq
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def random_unitary(seed):
|
|
27
|
+
def random_unitary(seed: cirq.RANDOM_STATE_OR_SEED_LIKE) -> np.ndarray:
|
|
27
28
|
return cirq.testing.random_unitary(4, random_state=seed)
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
def random_locals(x, y, z, seed=None):
|
|
31
|
+
def random_locals(x: float, y: float, z: float, seed=None) -> np.ndarray:
|
|
31
32
|
rng = np.random.RandomState(seed)
|
|
32
33
|
a0 = cirq.testing.random_unitary(2, random_state=rng)
|
|
33
34
|
a1 = cirq.testing.random_unitary(2, random_state=rng)
|
|
@@ -42,15 +43,14 @@ def random_locals(x, y, z, seed=None):
|
|
|
42
43
|
)
|
|
43
44
|
|
|
44
45
|
|
|
45
|
-
def perturbations_unitary(u, amount=1e-10):
|
|
46
|
+
def perturbations_unitary(u: np.ndarray, amount=1e-10) -> Iterator[np.ndarray]:
|
|
46
47
|
"""Returns several unitaries in the neighborhood of u to test for numerical
|
|
47
48
|
corner cases near critical values."""
|
|
48
49
|
kak = cirq.kak_decomposition(u)
|
|
49
50
|
yield u
|
|
50
51
|
for i in range(3):
|
|
51
52
|
for neg in (-1, 1):
|
|
52
|
-
perturb_xyz =
|
|
53
|
-
perturb_xyz[i] += neg * amount
|
|
53
|
+
perturb_xyz = tuple(c + neg * amount for c in kak.interaction_coefficients)
|
|
54
54
|
yield cirq.unitary(
|
|
55
55
|
cirq.KakDecomposition(
|
|
56
56
|
global_phase=kak.global_phase,
|
|
@@ -61,11 +61,11 @@ def perturbations_unitary(u, amount=1e-10):
|
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
def perturbations_gate(gate, amount=1e-10):
|
|
64
|
+
def perturbations_gate(gate: cirq.SupportsUnitary, amount=1e-10) -> Iterator[np.ndarray]:
|
|
65
65
|
return perturbations_unitary(cirq.unitary(gate), amount)
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def perturbations_weyl(x, y, z, amount=1e-10):
|
|
68
|
+
def perturbations_weyl(x: float, y: float, z: float, amount: float = 1e-10) -> Iterator[np.ndarray]:
|
|
69
69
|
return perturbations_gate(cirq.KakDecomposition(interaction_coefficients=(x, y, z)), amount)
|
|
70
70
|
|
|
71
71
|
|
|
@@ -211,7 +211,7 @@ def assert_valid_decomp(
|
|
|
211
211
|
atol=1e-6,
|
|
212
212
|
rtol=0,
|
|
213
213
|
qubit_order=cirq.LineQubit.range(2),
|
|
214
|
-
):
|
|
214
|
+
) -> None:
|
|
215
215
|
# Check expected gates
|
|
216
216
|
for op in operations:
|
|
217
217
|
if len(op.qubits) == 0 and isinstance(op.gate, cirq.GlobalPhaseGate):
|
|
@@ -242,7 +242,7 @@ def assert_valid_decomp(
|
|
|
242
242
|
)
|
|
243
243
|
|
|
244
244
|
|
|
245
|
-
def assert_specific_sqrt_iswap_count(operations, count):
|
|
245
|
+
def assert_specific_sqrt_iswap_count(operations, count) -> None:
|
|
246
246
|
actual = sum(len(op.qubits) == 2 for op in operations)
|
|
247
247
|
assert actual == count, f'Incorrect sqrt-iSWAP count. Expected {count} but got {actual}.'
|
|
248
248
|
|
|
@@ -255,7 +255,7 @@ def assert_specific_sqrt_iswap_count(operations, count):
|
|
|
255
255
|
cirq.CZPowGate(exponent=sympy.Symbol('t')),
|
|
256
256
|
],
|
|
257
257
|
)
|
|
258
|
-
def test_two_qubit_gates_with_symbols(gate: cirq.Gate):
|
|
258
|
+
def test_two_qubit_gates_with_symbols(gate: cirq.Gate) -> None:
|
|
259
259
|
op = gate(*cirq.LineQubit.range(2))
|
|
260
260
|
c_new_sqrt_iswap = cirq.Circuit(
|
|
261
261
|
cirq.parameterized_2q_op_to_sqrt_iswap_operations(op) # type: ignore
|
|
@@ -279,12 +279,14 @@ def test_two_qubit_gates_with_symbols(gate: cirq.Gate):
|
|
|
279
279
|
)
|
|
280
280
|
|
|
281
281
|
|
|
282
|
-
def test_fsim_gate_with_symbols():
|
|
282
|
+
def test_fsim_gate_with_symbols() -> None:
|
|
283
283
|
theta, phi = sympy.symbols(['theta', 'phi'])
|
|
284
284
|
op = cirq.FSimGate(theta=theta, phi=phi).on(*cirq.LineQubit.range(2))
|
|
285
|
-
c_new_sqrt_iswap = cirq.Circuit(
|
|
285
|
+
c_new_sqrt_iswap = cirq.Circuit(
|
|
286
|
+
cirq.parameterized_2q_op_to_sqrt_iswap_operations(op) # type: ignore[arg-type]
|
|
287
|
+
)
|
|
286
288
|
c_new_sqrt_iswap_inv = cirq.Circuit(
|
|
287
|
-
cirq.parameterized_2q_op_to_sqrt_iswap_operations(op, use_sqrt_iswap_inv=True)
|
|
289
|
+
cirq.parameterized_2q_op_to_sqrt_iswap_operations(op, use_sqrt_iswap_inv=True) # type: ignore[arg-type]
|
|
288
290
|
)
|
|
289
291
|
for theta_val in np.linspace(0, 2 * np.pi, 4):
|
|
290
292
|
for phi_val in np.linspace(0, 2 * np.pi, 6):
|
|
@@ -307,7 +309,7 @@ def test_fsim_gate_with_symbols():
|
|
|
307
309
|
|
|
308
310
|
|
|
309
311
|
@pytest.mark.parametrize('cnt', [-1, 4, 10])
|
|
310
|
-
def test_invalid_required_sqrt_iswap_count(cnt):
|
|
312
|
+
def test_invalid_required_sqrt_iswap_count(cnt) -> None:
|
|
311
313
|
u = TWO_SQRT_ISWAP_UNITARIES[0]
|
|
312
314
|
q0, q1 = cirq.LineQubit.range(2)
|
|
313
315
|
with pytest.raises(ValueError, match='required_sqrt_iswap_count'):
|
|
@@ -315,7 +317,7 @@ def test_invalid_required_sqrt_iswap_count(cnt):
|
|
|
315
317
|
|
|
316
318
|
|
|
317
319
|
@pytest.mark.parametrize('u', ZERO_UNITARIES)
|
|
318
|
-
def test_decomp0(u):
|
|
320
|
+
def test_decomp0(u) -> None:
|
|
319
321
|
# Decompose unitaries into zero sqrt-iSWAP gates
|
|
320
322
|
q0, q1 = cirq.LineQubit.range(2)
|
|
321
323
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=0)
|
|
@@ -326,7 +328,7 @@ def test_decomp0(u):
|
|
|
326
328
|
@pytest.mark.parametrize(
|
|
327
329
|
'u', ONE_SQRT_ISWAP_UNITARIES + TWO_SQRT_ISWAP_UNITARIES + THREE_SQRT_ISWAP_UNITARIES
|
|
328
330
|
)
|
|
329
|
-
def test_decomp0_invalid(u):
|
|
331
|
+
def test_decomp0_invalid(u) -> None:
|
|
330
332
|
# Attempt to decompose other unitaries into zero SQRT_ISWAP gates
|
|
331
333
|
q0, q1 = cirq.LineQubit.range(2)
|
|
332
334
|
with pytest.raises(ValueError, match='cannot be decomposed into exactly 0 sqrt-iSWAP gates'):
|
|
@@ -334,7 +336,7 @@ def test_decomp0_invalid(u):
|
|
|
334
336
|
|
|
335
337
|
|
|
336
338
|
@pytest.mark.parametrize('u', ONE_SQRT_ISWAP_UNITARIES)
|
|
337
|
-
def test_decomp1(u):
|
|
339
|
+
def test_decomp1(u) -> None:
|
|
338
340
|
q0, q1 = cirq.LineQubit.range(2)
|
|
339
341
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=1)
|
|
340
342
|
assert_valid_decomp(u, ops)
|
|
@@ -344,14 +346,14 @@ def test_decomp1(u):
|
|
|
344
346
|
@pytest.mark.parametrize(
|
|
345
347
|
'u', ZERO_UNITARIES + TWO_SQRT_ISWAP_UNITARIES + THREE_SQRT_ISWAP_UNITARIES
|
|
346
348
|
)
|
|
347
|
-
def test_decomp1_invalid(u):
|
|
349
|
+
def test_decomp1_invalid(u) -> None:
|
|
348
350
|
q0, q1 = cirq.LineQubit.range(2)
|
|
349
351
|
with pytest.raises(ValueError, match='cannot be decomposed into exactly 1 sqrt-iSWAP gates'):
|
|
350
352
|
cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=1)
|
|
351
353
|
|
|
352
354
|
|
|
353
355
|
@pytest.mark.parametrize('u', ZERO_UNITARIES + ONE_SQRT_ISWAP_UNITARIES + TWO_SQRT_ISWAP_UNITARIES)
|
|
354
|
-
def test_decomp2(u):
|
|
356
|
+
def test_decomp2(u) -> None:
|
|
355
357
|
q0, q1 = cirq.LineQubit.range(2)
|
|
356
358
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=2)
|
|
357
359
|
assert_valid_decomp(u, ops)
|
|
@@ -359,7 +361,7 @@ def test_decomp2(u):
|
|
|
359
361
|
|
|
360
362
|
|
|
361
363
|
@pytest.mark.parametrize('u', THREE_SQRT_ISWAP_UNITARIES)
|
|
362
|
-
def test_decomp2_invalid(u):
|
|
364
|
+
def test_decomp2_invalid(u) -> None:
|
|
363
365
|
q0, q1 = cirq.LineQubit.range(2)
|
|
364
366
|
with pytest.raises(ValueError, match='cannot be decomposed into exactly 2 sqrt-iSWAP gates'):
|
|
365
367
|
cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=2)
|
|
@@ -372,14 +374,14 @@ def test_decomp2_invalid(u):
|
|
|
372
374
|
+ TWO_SQRT_ISWAP_UNITARIES
|
|
373
375
|
+ THREE_SQRT_ISWAP_UNITARIES,
|
|
374
376
|
)
|
|
375
|
-
def test_decomp3(u):
|
|
377
|
+
def test_decomp3(u) -> None:
|
|
376
378
|
q0, q1 = cirq.LineQubit.range(2)
|
|
377
379
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=3)
|
|
378
380
|
assert_valid_decomp(u, ops)
|
|
379
381
|
assert_specific_sqrt_iswap_count(ops, 3)
|
|
380
382
|
|
|
381
383
|
|
|
382
|
-
def test_decomp3_invalid():
|
|
384
|
+
def test_decomp3_invalid() -> None:
|
|
383
385
|
# All two-qubit gates can be synthesized with three SQRT_ISWAP gates
|
|
384
386
|
u = cirq.unitary(cirq.X**0.2) # Pass an invalid size unitary
|
|
385
387
|
q0, q1 = cirq.LineQubit.range(2)
|
|
@@ -388,7 +390,7 @@ def test_decomp3_invalid():
|
|
|
388
390
|
|
|
389
391
|
|
|
390
392
|
@pytest.mark.parametrize('u', TWO_SQRT_ISWAP_UNITARIES[:1])
|
|
391
|
-
def test_qubit_order(u):
|
|
393
|
+
def test_qubit_order(u) -> None:
|
|
392
394
|
q0, q1 = cirq.LineQubit.range(2)
|
|
393
395
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q1, q0, u, required_sqrt_iswap_count=2)
|
|
394
396
|
assert_valid_decomp(u, ops, qubit_order=(q1, q0))
|
|
@@ -396,7 +398,7 @@ def test_qubit_order(u):
|
|
|
396
398
|
|
|
397
399
|
|
|
398
400
|
@pytest.mark.parametrize('u', ZERO_UNITARIES)
|
|
399
|
-
def test_decomp_optimal0(u):
|
|
401
|
+
def test_decomp_optimal0(u) -> None:
|
|
400
402
|
q0, q1 = cirq.LineQubit.range(2)
|
|
401
403
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u)
|
|
402
404
|
assert_valid_decomp(u, ops)
|
|
@@ -404,7 +406,7 @@ def test_decomp_optimal0(u):
|
|
|
404
406
|
|
|
405
407
|
|
|
406
408
|
@pytest.mark.parametrize('u', ONE_SQRT_ISWAP_UNITARIES)
|
|
407
|
-
def test_decomp_optimal1(u):
|
|
409
|
+
def test_decomp_optimal1(u) -> None:
|
|
408
410
|
q0, q1 = cirq.LineQubit.range(2)
|
|
409
411
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u)
|
|
410
412
|
assert_valid_decomp(u, ops)
|
|
@@ -412,7 +414,7 @@ def test_decomp_optimal1(u):
|
|
|
412
414
|
|
|
413
415
|
|
|
414
416
|
@pytest.mark.parametrize('u', TWO_SQRT_ISWAP_UNITARIES)
|
|
415
|
-
def test_decomp_optimal2(u):
|
|
417
|
+
def test_decomp_optimal2(u) -> None:
|
|
416
418
|
q0, q1 = cirq.LineQubit.range(2)
|
|
417
419
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u)
|
|
418
420
|
assert_valid_decomp(u, ops)
|
|
@@ -420,7 +422,7 @@ def test_decomp_optimal2(u):
|
|
|
420
422
|
|
|
421
423
|
|
|
422
424
|
@pytest.mark.parametrize('u', THREE_SQRT_ISWAP_UNITARIES)
|
|
423
|
-
def test_decomp_optimal3(u):
|
|
425
|
+
def test_decomp_optimal3(u) -> None:
|
|
424
426
|
q0, q1 = cirq.LineQubit.range(2)
|
|
425
427
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u)
|
|
426
428
|
assert_valid_decomp(u, ops)
|
|
@@ -428,7 +430,7 @@ def test_decomp_optimal3(u):
|
|
|
428
430
|
|
|
429
431
|
|
|
430
432
|
@pytest.mark.parametrize('u', ALL_REGION_UNITARIES)
|
|
431
|
-
def test_all_weyl_regions(u):
|
|
433
|
+
def test_all_weyl_regions(u) -> None:
|
|
432
434
|
q0, q1 = cirq.LineQubit.range(2)
|
|
433
435
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, clean_operations=True)
|
|
434
436
|
assert_valid_decomp(u, ops, single_qubit_gate_types=(cirq.PhasedXZGate,))
|
|
@@ -445,13 +447,13 @@ def test_all_weyl_regions(u):
|
|
|
445
447
|
THREE_SQRT_ISWAP_UNITARIES[0],
|
|
446
448
|
],
|
|
447
449
|
)
|
|
448
|
-
def test_decomp_sqrt_iswap_inv(u):
|
|
450
|
+
def test_decomp_sqrt_iswap_inv(u) -> None:
|
|
449
451
|
q0, q1 = cirq.LineQubit.range(2)
|
|
450
452
|
ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, use_sqrt_iswap_inv=True)
|
|
451
453
|
assert_valid_decomp(u, ops, two_qubit_gate=cirq.SQRT_ISWAP_INV)
|
|
452
454
|
|
|
453
455
|
|
|
454
|
-
def test_valid_check_raises():
|
|
456
|
+
def test_valid_check_raises() -> None:
|
|
455
457
|
q0 = cirq.LineQubit(0)
|
|
456
458
|
with pytest.raises(AssertionError, match='Unitaries are completely different'):
|
|
457
459
|
assert_valid_decomp(np.eye(4), [cirq.X(q0)], single_qubit_gate_types=(cirq.XPowGate,))
|
|
@@ -19,13 +19,13 @@ import cirq
|
|
|
19
19
|
NO_COMPILE_TAG = "no_compile_tag"
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def test_leaves_big():
|
|
22
|
+
def test_leaves_big() -> None:
|
|
23
23
|
a = cirq.NamedQubit('a')
|
|
24
24
|
circuit = cirq.Circuit(cirq.Moment(cirq.Z(a) ** 0.1))
|
|
25
25
|
cirq.testing.assert_same_circuits(cirq.drop_negligible_operations(circuit, atol=0.001), circuit)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def test_clears_small():
|
|
28
|
+
def test_clears_small() -> None:
|
|
29
29
|
a = cirq.NamedQubit('a')
|
|
30
30
|
circuit = cirq.Circuit(cirq.Moment(cirq.Z(a) ** 0.000001))
|
|
31
31
|
cirq.testing.assert_same_circuits(
|
|
@@ -33,7 +33,7 @@ def test_clears_small():
|
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def test_does_not_clear_small_no_compile():
|
|
36
|
+
def test_does_not_clear_small_no_compile() -> None:
|
|
37
37
|
a = cirq.NamedQubit('a')
|
|
38
38
|
circuit = cirq.Circuit(cirq.Moment((cirq.Z(a) ** 0.000001).with_tags(NO_COMPILE_TAG)))
|
|
39
39
|
cirq.testing.assert_same_circuits(
|
|
@@ -44,7 +44,7 @@ def test_does_not_clear_small_no_compile():
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def test_clears_known_empties_even_at_zero_tolerance():
|
|
47
|
+
def test_clears_known_empties_even_at_zero_tolerance() -> None:
|
|
48
48
|
a, b = cirq.LineQubit.range(2)
|
|
49
49
|
circuit = cirq.Circuit(
|
|
50
50
|
cirq.Z(a) ** 0, cirq.Y(a) ** 0.0000001, cirq.X(a) ** -0.0000001, cirq.CZ(a, b) ** 0
|
|
@@ -63,7 +63,7 @@ def test_clears_known_empties_even_at_zero_tolerance():
|
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
def test_recursively_runs_inside_circuit_ops_deep():
|
|
66
|
+
def test_recursively_runs_inside_circuit_ops_deep() -> None:
|
|
67
67
|
a = cirq.NamedQubit('a')
|
|
68
68
|
small_op = cirq.Z(a) ** 0.000001
|
|
69
69
|
nested_circuit = cirq.FrozenCircuit(
|
|
@@ -92,13 +92,13 @@ def test_recursively_runs_inside_circuit_ops_deep():
|
|
|
92
92
|
),
|
|
93
93
|
cirq.Moment(),
|
|
94
94
|
)
|
|
95
|
-
context = cirq.TransformerContext(tags_to_ignore=
|
|
95
|
+
context = cirq.TransformerContext(tags_to_ignore=(NO_COMPILE_TAG,), deep=True)
|
|
96
96
|
cirq.testing.assert_same_circuits(
|
|
97
97
|
cirq.drop_negligible_operations(c_orig, context=context, atol=0.001), c_expected
|
|
98
98
|
)
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
def test_ignores_large_ops():
|
|
101
|
+
def test_ignores_large_ops() -> None:
|
|
102
102
|
qnum = 20
|
|
103
103
|
qubits = cirq.LineQubit.range(qnum)
|
|
104
104
|
subcircuit = cirq.FrozenCircuit(cirq.X.on_each(*qubits))
|
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
+
from enum import Enum
|
|
19
20
|
from functools import reduce
|
|
20
21
|
from itertools import cycle
|
|
21
22
|
from typing import TYPE_CHECKING
|
|
22
23
|
|
|
23
24
|
import numpy as np
|
|
25
|
+
from attrs import frozen
|
|
24
26
|
|
|
25
27
|
from cirq import ops, protocols
|
|
26
28
|
from cirq.circuits import Circuit, FrozenCircuit, Moment
|
|
@@ -133,10 +135,6 @@ def _calc_busy_moment_range_of_each_qubit(circuit: FrozenCircuit) -> dict[ops.Qi
|
|
|
133
135
|
return busy_moment_range_by_qubit
|
|
134
136
|
|
|
135
137
|
|
|
136
|
-
def _is_insertable_moment(moment: Moment, single_qubit_gate_moments_only: bool) -> bool:
|
|
137
|
-
return not single_qubit_gate_moments_only or _is_single_qubit_gate_moment(moment)
|
|
138
|
-
|
|
139
|
-
|
|
140
138
|
def _merge_single_qubit_ops_to_phxz(
|
|
141
139
|
q: ops.Qid, operations: tuple[ops.Operation, ...]
|
|
142
140
|
) -> ops.Operation:
|
|
@@ -149,34 +147,162 @@ def _merge_single_qubit_ops_to_phxz(
|
|
|
149
147
|
return gate.on(q)
|
|
150
148
|
|
|
151
149
|
|
|
152
|
-
def
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
def _backward_set_stopping_slots(
|
|
151
|
+
q: ops.Qid,
|
|
152
|
+
from_mid: int,
|
|
153
|
+
mergable: dict[ops.Qid, dict[int, bool]],
|
|
154
|
+
need_to_stop: dict[ops.Qid, dict[int, bool]],
|
|
155
|
+
gate_types: dict[ops.Qid, dict[int, _CellType]],
|
|
156
|
+
circuit: FrozenCircuit,
|
|
157
|
+
):
|
|
158
|
+
"""Sets stopping slots for dynamical decoupling insertion.
|
|
159
|
+
|
|
160
|
+
This function traverses backward from a given moment `from_mid` for a specific qubit `q`.
|
|
161
|
+
It identifies moments where a dynamical decoupling sequence needs to be "stopped".
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
q: The qubit for which to set stopping slots.
|
|
165
|
+
from_mid: The moment ID to start the backward traversal from.
|
|
166
|
+
mergable: A dictionary indicating if a single-qubit Clifford gate at (qubit, moment_id)
|
|
167
|
+
can be merged with a Pauli gate.
|
|
168
|
+
need_to_stop: A dictionary to mark moments where a DD sequence must be stopped.
|
|
169
|
+
gate_types: A dictionary indicating the type of gate at each (qubit, moment_id).
|
|
170
|
+
circuit: The original frozen circuit.
|
|
155
171
|
"""
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
affected_qubits: set[ops.Qid] = {q}
|
|
173
|
+
for back_mid in range(from_mid, -1, -1):
|
|
174
|
+
for back_q in set(affected_qubits):
|
|
175
|
+
if gate_types[back_q][back_mid] == _CellType.WALL:
|
|
176
|
+
affected_qubits.remove(back_q)
|
|
177
|
+
continue
|
|
178
|
+
if mergable[back_q][back_mid]:
|
|
179
|
+
need_to_stop[back_q][back_mid] = True
|
|
180
|
+
affected_qubits.remove(back_q)
|
|
181
|
+
continue
|
|
182
|
+
op_at_q = circuit[back_mid].operation_at(back_q) or ops.I(q)
|
|
183
|
+
affected_qubits.update(op_at_q.qubits)
|
|
184
|
+
if not affected_qubits:
|
|
185
|
+
break
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class _CellType(Enum):
|
|
189
|
+
UNKNOWN = '?'
|
|
190
|
+
# Non-insertable gates that cannot be pulled through
|
|
191
|
+
WALL = 'w'
|
|
192
|
+
# Clifford gates where Pauli Gates can be pulled through
|
|
193
|
+
DOOR = 'd'
|
|
194
|
+
# An empty gate can be used to insert Pauli gates from the dd sequence
|
|
195
|
+
INSERTABLE = 'i'
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@frozen
|
|
199
|
+
class _Grid:
|
|
200
|
+
"""A grid representation of the circuit where each gate position is labeled for
|
|
201
|
+
dynamical decoupling.
|
|
202
|
+
|
|
203
|
+
With this representation, a DD sequence can be automatically navigated in a
|
|
204
|
+
forward-only process. This avoids issues where a partially inserted DD
|
|
205
|
+
sequence encounters a "wall" and a new moment must be inserted because the
|
|
206
|
+
remaining DD sequence cannot be absorbed by nearby gates.
|
|
207
|
+
|
|
208
|
+
This labeled representation pre-calculates where DD pulses can be inserted
|
|
209
|
+
and where leftover DD sequences must be merged, avoiding the need for
|
|
210
|
+
backtracking.
|
|
211
|
+
|
|
212
|
+
An example labeled circuit is shown below:
|
|
213
|
+
| 0 | 1 | 2 | 3 | 4 |
|
|
214
|
+
-----+-----+-----+-----+-----+-----+
|
|
215
|
+
q(0) | d | i | i,s | d | w |
|
|
216
|
+
q(1) | d | i | d,s | w | w |
|
|
217
|
+
q(2) | d | d | d,s | w | w |
|
|
218
|
+
where `w`=WALL, `d`=DOOR, `i`=INSERTABLE. `s` represents a stop gate,
|
|
219
|
+
meaning that any unfinished DD sequences must be merged at this gate.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
gate_types: dict[ops.Qid, dict[int, _CellType]]
|
|
223
|
+
need_to_stop: dict[ops.Qid, dict[int, bool]]
|
|
224
|
+
circuit: FrozenCircuit
|
|
225
|
+
|
|
226
|
+
@classmethod
|
|
227
|
+
def from_circuit(
|
|
228
|
+
cls, circuit: cirq.FrozenCircuit, single_qubit_gate_moments_only: bool
|
|
229
|
+
) -> _Grid:
|
|
230
|
+
gate_types: dict[ops.Qid, dict[int, _CellType]] = {
|
|
231
|
+
q: {mid: _CellType.UNKNOWN for mid in range(len(circuit))} for q in circuit.all_qubits()
|
|
232
|
+
}
|
|
233
|
+
mergable: dict[ops.Qid, dict[int, bool]] = {
|
|
234
|
+
q: {mid: False for mid in range(len(circuit))} for q in circuit.all_qubits()
|
|
235
|
+
}
|
|
236
|
+
busy_moment_range_by_qubit = _calc_busy_moment_range_of_each_qubit(circuit)
|
|
237
|
+
|
|
238
|
+
# Set gate types for each (q, mid)
|
|
239
|
+
for mid, moment in enumerate(circuit):
|
|
240
|
+
is_insertable_moment = (
|
|
241
|
+
not single_qubit_gate_moments_only or _is_single_qubit_gate_moment(moment)
|
|
242
|
+
)
|
|
243
|
+
for q in circuit.all_qubits():
|
|
244
|
+
if mid < busy_moment_range_by_qubit[q][0] or mid > busy_moment_range_by_qubit[q][1]:
|
|
245
|
+
gate_types[q][mid] = _CellType.WALL
|
|
246
|
+
continue
|
|
247
|
+
op_at_q = moment.operation_at(q)
|
|
248
|
+
if op_at_q is None:
|
|
249
|
+
if is_insertable_moment:
|
|
250
|
+
gate_types[q][mid] = _CellType.INSERTABLE
|
|
251
|
+
mergable[q][mid] = True
|
|
252
|
+
else:
|
|
253
|
+
gate_types[q][mid] = _CellType.DOOR
|
|
254
|
+
else:
|
|
255
|
+
if _is_clifford_op(op_at_q):
|
|
256
|
+
gate_types[q][mid] = _CellType.DOOR
|
|
257
|
+
mergable[q][mid] = _is_single_qubit_operation(op_at_q)
|
|
258
|
+
else:
|
|
259
|
+
gate_types[q][mid] = _CellType.WALL
|
|
260
|
+
|
|
261
|
+
need_to_stop: dict[ops.Qid, dict[int, bool]] = {
|
|
262
|
+
q: {mid: False for mid in range(len(circuit))} for q in circuit.all_qubits()
|
|
263
|
+
}
|
|
264
|
+
# Reversely find the last mergeable gate of each qubit, set them as need_to_stop.
|
|
265
|
+
for q in circuit.all_qubits():
|
|
266
|
+
_backward_set_stopping_slots(
|
|
267
|
+
q, len(circuit) - 1, mergable, need_to_stop, gate_types, circuit
|
|
268
|
+
)
|
|
269
|
+
# Reversely check for each wall gate, mark the closest mergeable gate as need_to_stop.
|
|
270
|
+
for mid in range(len(circuit)):
|
|
271
|
+
for q in circuit.all_qubits():
|
|
272
|
+
if gate_types[q][mid] == _CellType.WALL:
|
|
273
|
+
_backward_set_stopping_slots(
|
|
274
|
+
q, mid - 1, mergable, need_to_stop, gate_types, circuit
|
|
275
|
+
)
|
|
276
|
+
return cls(circuit=circuit, gate_types=gate_types, need_to_stop=need_to_stop)
|
|
160
277
|
|
|
278
|
+
def __str__(self) -> str:
|
|
279
|
+
if not self.gate_types:
|
|
280
|
+
return "Grid(empty)"
|
|
161
281
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
for op in moment:
|
|
165
|
-
if (not _is_clifford_op(op) and not _is_single_qubit_operation(op)) or not has_unitary(
|
|
166
|
-
op
|
|
167
|
-
): # multi-qubit clifford op or non-mergable op.
|
|
168
|
-
stop_pulling_through_qubits.update(op.qubits)
|
|
169
|
-
return stop_pulling_through_qubits
|
|
282
|
+
qubits = sorted(list(self.gate_types.keys()))
|
|
283
|
+
num_moments = len(self.gate_types[qubits[0]])
|
|
170
284
|
|
|
285
|
+
max_qubit_len = max(len(str(q)) for q in qubits) if qubits else 0
|
|
171
286
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
287
|
+
header = f"{'':>{max_qubit_len}} |"
|
|
288
|
+
for i in range(num_moments):
|
|
289
|
+
header += f" {i:^3} |"
|
|
290
|
+
|
|
291
|
+
separator = f"{'-' * max_qubit_len}-+"
|
|
292
|
+
separator += '-----+' * num_moments
|
|
293
|
+
|
|
294
|
+
lines = ["Grid Repr:", header, separator]
|
|
295
|
+
|
|
296
|
+
for q in qubits:
|
|
297
|
+
row_str = f"{str(q):>{max_qubit_len}} |"
|
|
298
|
+
for mid in range(num_moments):
|
|
299
|
+
gate_type = self.gate_types[q][mid].value
|
|
300
|
+
stop = self.need_to_stop[q][mid]
|
|
301
|
+
cell = f"{gate_type},s" if stop else f" {gate_type} "
|
|
302
|
+
row_str += f" {cell} |"
|
|
303
|
+
lines.append(row_str)
|
|
304
|
+
|
|
305
|
+
return "\n".join(lines)
|
|
180
306
|
|
|
181
307
|
|
|
182
308
|
@transformer_api.transformer
|
|
@@ -188,7 +314,7 @@ def add_dynamical_decoupling(
|
|
|
188
314
|
single_qubit_gate_moments_only: bool = True,
|
|
189
315
|
) -> cirq.Circuit:
|
|
190
316
|
"""Adds dynamical decoupling gate operations to a given circuit.
|
|
191
|
-
This transformer
|
|
317
|
+
This transformer preserves the structure of the original circuit.
|
|
192
318
|
|
|
193
319
|
Args:
|
|
194
320
|
circuit: Input circuit to transform.
|
|
@@ -202,11 +328,18 @@ def add_dynamical_decoupling(
|
|
|
202
328
|
Returns:
|
|
203
329
|
A copy of the input circuit with dynamical decoupling operations.
|
|
204
330
|
"""
|
|
205
|
-
|
|
331
|
+
|
|
332
|
+
if context is not None and context.deep:
|
|
333
|
+
raise ValueError("Deep transformation is not supported.")
|
|
334
|
+
|
|
206
335
|
orig_circuit = circuit.freeze()
|
|
207
336
|
|
|
208
|
-
|
|
337
|
+
grid = _Grid.from_circuit(orig_circuit, single_qubit_gate_moments_only)
|
|
338
|
+
|
|
339
|
+
if context is not None and context.logger is not None:
|
|
340
|
+
context.logger.log("Preprocessed input circuit grid repr:\n%s", str(grid))
|
|
209
341
|
|
|
342
|
+
base_dd_sequence, pauli_map = _parse_dd_sequence(schema)
|
|
210
343
|
# Stores all the moments of the output circuit chronologically.
|
|
211
344
|
transformed_moments: list[Moment] = []
|
|
212
345
|
# A PauliString stores the result of 'pulling' Pauli gates past each operations
|
|
@@ -215,90 +348,30 @@ def add_dynamical_decoupling(
|
|
|
215
348
|
# Iterator of gate to be used in dd sequence for each qubit.
|
|
216
349
|
dd_iter_by_qubits = {q: cycle(base_dd_sequence) for q in circuit.all_qubits()}
|
|
217
350
|
|
|
218
|
-
def _update_pulled_through(q: ops.Qid, insert_gate: ops.Gate) -> ops.Operation:
|
|
219
|
-
nonlocal pulled_through, pauli_map
|
|
220
|
-
pulled_through *= pauli_map[insert_gate].on(q)
|
|
221
|
-
return insert_gate.on(q)
|
|
222
|
-
|
|
223
|
-
# Insert and pull remaining Pauli ops through the whole circuit.
|
|
224
|
-
# General ideas are
|
|
225
|
-
# * Pull through Clifford gates.
|
|
226
|
-
# * Stop at multi-qubit non-Clifford ops (and other non-mergable ops).
|
|
227
|
-
# * Merge to single-qubit non-Clifford ops.
|
|
228
|
-
# * Insert a new moment if necessary.
|
|
229
|
-
# After pulling through pulled_through at `moment`, we expect a transformation of
|
|
230
|
-
# (pulled_through, moment) -> (updated_moment, updated_pulled_through) or
|
|
231
|
-
# (pulled_through, moment) -> (new_moment, updated_moment, updated_pulled_through)
|
|
232
|
-
# Moments structure changes are split into 3 steps:
|
|
233
|
-
# 1, (..., last_moment, pulled_through1, moment, ...)
|
|
234
|
-
# -> (..., last_moment, new_moment or None, pulled_through2, moment, ...)
|
|
235
|
-
# 2, (..., pulled_through2, moment, ...) -> (..., pulled_through3, updated_moment, ...)
|
|
236
|
-
# 3, (..., pulled_through3, updated_moment, ...)
|
|
237
|
-
# -> (..., updated_moment, pulled_through4, ...)
|
|
238
351
|
for moment_id, moment in enumerate(orig_circuit.moments):
|
|
239
|
-
# Step 1, insert new_moment if necessary.
|
|
240
|
-
# In detail: stop pulling through for multi-qubit non-Clifford ops or gates without
|
|
241
|
-
# unitary representation (e.g., measure gates). If there are remaining pulled through ops,
|
|
242
|
-
# insert into a new moment before current moment.
|
|
243
|
-
stop_pulling_through_qubits: set[ops.Qid] = _get_stop_qubits(moment)
|
|
244
|
-
new_moment_ops: list[ops.Operation] = []
|
|
245
|
-
for q in stop_pulling_through_qubits:
|
|
246
|
-
# Insert the remaining pulled_through
|
|
247
|
-
remaining_pulled_through_gate = pulled_through.get(q)
|
|
248
|
-
if remaining_pulled_through_gate is not None:
|
|
249
|
-
new_moment_ops.append(_update_pulled_through(q, remaining_pulled_through_gate))
|
|
250
|
-
# Reset dd sequence
|
|
251
|
-
dd_iter_by_qubits[q] = cycle(base_dd_sequence)
|
|
252
|
-
# Need to insert a new moment before current moment
|
|
253
|
-
if new_moment_ops:
|
|
254
|
-
# Fill insertable idle moments in the new moment using dd sequence
|
|
255
|
-
for q in orig_circuit.all_qubits() - stop_pulling_through_qubits:
|
|
256
|
-
if busy_moment_range_by_qubit[q][0] < moment_id <= busy_moment_range_by_qubit[q][1]:
|
|
257
|
-
new_moment_ops.append(_update_pulled_through(q, next(dd_iter_by_qubits[q])))
|
|
258
|
-
transformed_moments.append(Moment(new_moment_ops))
|
|
259
|
-
|
|
260
|
-
# Step 2, calc updated_moment with insertions / merges.
|
|
261
352
|
updated_moment_ops: set[cirq.Operation] = set()
|
|
262
353
|
for q in orig_circuit.all_qubits():
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
updated_op = _merge_single_qubit_ops_to_phxz(q, (remaining_op, op_at_q))
|
|
286
|
-
if updated_op is not None:
|
|
287
|
-
updated_moment_ops.add(updated_op)
|
|
288
|
-
|
|
289
|
-
if updated_moment_ops:
|
|
290
|
-
updated_moment = Moment(updated_moment_ops)
|
|
291
|
-
transformed_moments.append(updated_moment)
|
|
292
|
-
|
|
293
|
-
# Step 3, update pulled through.
|
|
294
|
-
# In detail: pulling current `pulled_through` through updated_moment.
|
|
295
|
-
pulled_through = _calc_pulled_through(updated_moment, pulled_through)
|
|
296
|
-
|
|
297
|
-
# Insert a new moment if there are remaining pulled-through operations.
|
|
298
|
-
ending_moment_ops = []
|
|
299
|
-
for affected_q, combined_op_in_pauli in pulled_through.items():
|
|
300
|
-
ending_moment_ops.append(combined_op_in_pauli.on(affected_q))
|
|
301
|
-
if ending_moment_ops:
|
|
302
|
-
transformed_moments.append(Moment(ending_moment_ops))
|
|
354
|
+
new_op_at_q = moment.operation_at(q)
|
|
355
|
+
if grid.gate_types[q][moment_id] == _CellType.INSERTABLE:
|
|
356
|
+
new_gate = next(dd_iter_by_qubits[q])
|
|
357
|
+
new_op_at_q = new_gate.on(q)
|
|
358
|
+
pulled_through *= pauli_map[new_gate].on(q)
|
|
359
|
+
if grid.need_to_stop[q][moment_id]:
|
|
360
|
+
to_be_merged = pulled_through.get(q)
|
|
361
|
+
if to_be_merged is not None:
|
|
362
|
+
new_op_at_q = _merge_single_qubit_ops_to_phxz(
|
|
363
|
+
q, (to_be_merged.on(q), new_op_at_q or ops.I(q))
|
|
364
|
+
)
|
|
365
|
+
pulled_through *= to_be_merged.on(q)
|
|
366
|
+
if new_op_at_q is not None:
|
|
367
|
+
updated_moment_ops.add(new_op_at_q)
|
|
368
|
+
|
|
369
|
+
updated_moment = Moment(updated_moment_ops)
|
|
370
|
+
clifford_ops = [op for op in updated_moment if _is_clifford_op(op)]
|
|
371
|
+
pulled_through = pulled_through.after(clifford_ops)
|
|
372
|
+
transformed_moments.append(updated_moment)
|
|
373
|
+
|
|
374
|
+
if len(pulled_through) > 0:
|
|
375
|
+
raise RuntimeError("Expect empty remaining Paulis after the dd insertion.")
|
|
303
376
|
|
|
304
377
|
return Circuit.from_moments(*transformed_moments)
|