cirq-core 1.7.0.dev20250825174419__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 +19 -17
- cirq/circuits/circuit_operation.py +2 -1
- cirq/circuits/circuit_operation_test.py +19 -0
- cirq/circuits/circuit_test.py +31 -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 +81 -178
- cirq/contrib/paulistring/recombine.py +5 -2
- cirq/contrib/paulistring/separate.py +1 -1
- cirq/contrib/qasm_import/_lexer.py +6 -1
- cirq/contrib/qasm_import/_lexer_test.py +1 -1
- cirq/contrib/qasm_import/_parser.py +24 -8
- cirq/contrib/qasm_import/_parser_test.py +44 -6
- cirq/contrib/qcircuit/qcircuit_pdf_test.py +6 -9
- 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 +33 -4
- cirq/experiments/qubit_characterizations_test.py +16 -0
- 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 +3 -2
- cirq/ops/arithmetic_operation.py +4 -3
- cirq/ops/arithmetic_operation_test.py +1 -1
- cirq/ops/boolean_hamiltonian.py +4 -3
- cirq/ops/classically_controlled_operation.py +11 -11
- cirq/ops/classically_controlled_operation_test.py +26 -2
- cirq/ops/clifford_gate.py +3 -2
- cirq/ops/clifford_gate_test.py +1 -2
- cirq/ops/common_channels.py +2 -1
- cirq/ops/common_gates.py +3 -2
- 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 +44 -81
- cirq/ops/dense_pauli_string_test.py +21 -0
- 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 +23 -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 +12 -17
- cirq/ops/linear_combinations_test.py +23 -1
- cirq/ops/matrix_gates.py +2 -1
- cirq/ops/measure_util.py +8 -6
- 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/parity_gates_test.py +35 -0
- cirq/ops/pauli_interaction_gate.py +2 -1
- cirq/ops/pauli_measurement_gate.py +2 -1
- cirq/ops/pauli_string.py +37 -57
- cirq/ops/pauli_string_phasor.py +6 -5
- cirq/ops/pauli_string_raw_types.py +2 -1
- cirq/ops/pauli_string_test.py +49 -6
- 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 +33 -16
- 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/control_key_protocol.py +7 -0
- cirq/protocols/decompose_protocol.py +2 -12
- cirq/protocols/has_stabilizer_effect_protocol.py +1 -1
- cirq/protocols/has_stabilizer_effect_protocol_test.py +11 -9
- cirq/protocols/has_unitary_protocol_test.py +3 -3
- cirq/protocols/hash_from_pickle_test.py +2 -2
- cirq/protocols/inverse_protocol.py +2 -1
- cirq/protocols/json_serialization.py +5 -4
- cirq/protocols/json_serialization_test.py +31 -31
- cirq/protocols/kraus_protocol.py +4 -3
- cirq/protocols/kraus_protocol_test.py +7 -7
- cirq/protocols/measurement_key_protocol.py +32 -8
- cirq/protocols/mixture_protocol.py +3 -2
- cirq/protocols/mixture_protocol_test.py +7 -7
- cirq/protocols/mul_protocol_test.py +4 -4
- cirq/protocols/phase_protocol.py +13 -4
- cirq/protocols/pow_protocol.py +2 -1
- cirq/protocols/pow_protocol_test.py +5 -5
- cirq/protocols/qasm.py +2 -1
- cirq/protocols/qid_shape_protocol.py +2 -1
- cirq/protocols/resolve_parameters.py +17 -15
- cirq/protocols/trace_distance_bound.py +2 -1
- cirq/protocols/unitary_protocol.py +21 -21
- cirq/protocols/unitary_protocol_test.py +31 -19
- cirq/qis/channels.py +1 -1
- cirq/qis/channels_test.py +1 -1
- cirq/qis/clifford_tableau.py +16 -15
- cirq/qis/clifford_tableau_test.py +17 -17
- cirq/qis/entropy.py +3 -3
- cirq/qis/entropy_test.py +1 -1
- cirq/qis/quantum_state_representation.py +2 -1
- cirq/qis/states.py +7 -2
- cirq/qis/states_test.py +54 -54
- cirq/sim/classical_simulator.py +25 -14
- cirq/sim/classical_simulator_test.py +85 -30
- cirq/sim/clifford/clifford_simulator.py +7 -6
- cirq/sim/clifford/clifford_simulator_test.py +51 -50
- 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 +16 -15
- cirq/sim/clifford/stabilizer_state_ch_form_test.py +0 -1
- cirq/sim/density_matrix_simulation_state.py +7 -6
- cirq/sim/density_matrix_simulator.py +3 -2
- cirq/sim/density_matrix_simulator_test.py +94 -84
- cirq/sim/density_matrix_utils.py +2 -1
- cirq/sim/density_matrix_utils_test.py +1 -1
- cirq/sim/mux.py +35 -8
- cirq/sim/mux_test.py +39 -26
- cirq/sim/simulation_product_state.py +2 -1
- cirq/sim/simulation_product_state_test.py +8 -7
- cirq/sim/simulation_state.py +6 -5
- cirq/sim/simulation_state_base.py +3 -2
- cirq/sim/simulation_state_test.py +7 -6
- cirq/sim/simulation_utils.py +2 -1
- cirq/sim/simulator.py +4 -3
- cirq/sim/simulator_base.py +2 -1
- cirq/sim/simulator_base_test.py +51 -36
- cirq/sim/simulator_test.py +41 -36
- cirq/sim/sparse_simulator.py +3 -2
- cirq/sim/sparse_simulator_test.py +92 -82
- cirq/sim/state_vector.py +5 -6
- cirq/sim/state_vector_simulation_state.py +10 -9
- cirq/sim/state_vector_simulator.py +2 -1
- cirq/sim/state_vector_simulator_test.py +9 -9
- cirq/sim/state_vector_test.py +40 -39
- 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/result_test.py +20 -20
- cirq/study/sweepable.py +2 -1
- cirq/study/sweepable_test.py +20 -20
- cirq/study/sweeps.py +26 -1
- cirq/study/sweeps_test.py +67 -43
- cirq/testing/_compat_test_data/__init__.py +3 -3
- cirq/testing/circuit_compare.py +2 -1
- cirq/testing/circuit_compare_test.py +16 -14
- cirq/testing/consistent_act_on_test.py +1 -1
- cirq/testing/consistent_channels.py +2 -2
- cirq/testing/consistent_controlled_gate_op.py +2 -2
- cirq/testing/consistent_controlled_gate_op_test.py +2 -1
- cirq/testing/consistent_decomposition.py +4 -2
- cirq/testing/consistent_phase_by.py +1 -1
- cirq/testing/consistent_protocols.py +2 -1
- cirq/testing/consistent_protocols_test.py +3 -3
- cirq/testing/consistent_qasm.py +4 -3
- cirq/testing/consistent_qasm_test.py +3 -3
- 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/lin_alg_utils.py +1 -1
- 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 +5 -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 +13 -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 +225 -0
- cirq/transformers/gauge_compiling/idle_moments_gauge_test.py +193 -0
- 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 +3 -2
- 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 +9 -5
- 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.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/METADATA +5 -6
- {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/RECORD +425 -406
- cirq/contrib/json_test.py +0 -33
- {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/WHEEL +0 -0
- {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/licenses/LICENSE +0 -0
- {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/top_level.txt +0 -0
|
@@ -32,10 +32,10 @@ class Fixed(cirq.Operation):
|
|
|
32
32
|
return self.unitary
|
|
33
33
|
|
|
34
34
|
@property
|
|
35
|
-
def qubits(self):
|
|
36
|
-
return cirq.LineQubit.range(self.unitary.shape[0].bit_length() - 1)
|
|
35
|
+
def qubits(self) -> tuple[cirq.Qid, ...]:
|
|
36
|
+
return tuple(cirq.LineQubit.range(self.unitary.shape[0].bit_length() - 1))
|
|
37
37
|
|
|
38
|
-
def with_qubits(self, *new_qubits):
|
|
38
|
+
def with_qubits(self, *new_qubits) -> Fixed:
|
|
39
39
|
raise NotImplementedError()
|
|
40
40
|
|
|
41
41
|
def _qasm_(self, args: cirq.QasmArgs):
|
|
@@ -21,7 +21,7 @@ import numpy as np
|
|
|
21
21
|
import cirq
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def assert_unitary_is_consistent(val: Any, ignoring_global_phase: bool = False):
|
|
24
|
+
def assert_unitary_is_consistent(val: Any, ignoring_global_phase: bool = False) -> None:
|
|
25
25
|
if not isinstance(val, (cirq.Operation, cirq.Gate)):
|
|
26
26
|
return
|
|
27
27
|
|
|
@@ -95,4 +95,4 @@ def test_failed_decomposition() -> None:
|
|
|
95
95
|
with pytest.raises(ValueError):
|
|
96
96
|
cirq.testing.assert_unitary_is_consistent(FailsOnDecompostion())
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
cirq.testing.assert_unitary_is_consistent(cirq.Circuit())
|
cirq/testing/deprecation.py
CHANGED
cirq/testing/devices.py
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"""Provides test devices that can validate circuits."""
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
|
-
from
|
|
18
|
+
from collections.abc import Set
|
|
19
|
+
from typing import cast
|
|
19
20
|
|
|
20
21
|
from cirq import devices, ops
|
|
21
22
|
|
|
@@ -38,7 +39,7 @@ class ValidatingTestDevice(devices.Device):
|
|
|
38
39
|
|
|
39
40
|
def __init__(
|
|
40
41
|
self,
|
|
41
|
-
qubits:
|
|
42
|
+
qubits: Set[ops.Qid],
|
|
42
43
|
name: str = "ValidatingTestDevice",
|
|
43
44
|
allowed_gates: tuple[type, ...] = (ops.Gate,),
|
|
44
45
|
allowed_qubit_types: tuple[type, ...] = (devices.GridQubit,),
|
cirq/testing/equals_tester.py
CHANGED
|
@@ -24,7 +24,8 @@ from __future__ import annotations
|
|
|
24
24
|
|
|
25
25
|
import collections
|
|
26
26
|
import itertools
|
|
27
|
-
from
|
|
27
|
+
from collections.abc import Callable
|
|
28
|
+
from typing import Any
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class EqualsTester:
|
|
@@ -93,7 +94,7 @@ class EqualsTester:
|
|
|
93
94
|
"Common problem: returning NotImplementedError instead of NotImplemented. "
|
|
94
95
|
)
|
|
95
96
|
|
|
96
|
-
def add_equality_group(self, *group_items: Any):
|
|
97
|
+
def add_equality_group(self, *group_items: Any) -> None:
|
|
97
98
|
"""Tries to add a disjoint equivalence group to the equality tester.
|
|
98
99
|
|
|
99
100
|
This methods asserts that items within the group must all be equal to
|
|
@@ -114,7 +115,7 @@ class EqualsTester:
|
|
|
114
115
|
# Remember this group, to enable disjoint checks vs later groups.
|
|
115
116
|
self._groups.append(group_items)
|
|
116
117
|
|
|
117
|
-
def make_equality_group(self, *factories: Callable[[], Any]):
|
|
118
|
+
def make_equality_group(self, *factories: Callable[[], Any]) -> None:
|
|
118
119
|
"""Tries to add a disjoint equivalence group to the equality tester.
|
|
119
120
|
|
|
120
121
|
Uses the factory methods to produce two different objects with the same
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
from
|
|
17
|
+
from collections.abc import Sequence
|
|
18
18
|
|
|
19
19
|
import numpy as np
|
|
20
20
|
|
|
@@ -22,7 +22,9 @@ import cirq
|
|
|
22
22
|
from cirq import circuits
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def assert_equivalent_computational_basis_map(
|
|
25
|
+
def assert_equivalent_computational_basis_map(
|
|
26
|
+
maps: dict[int, int], circuit: circuits.Circuit
|
|
27
|
+
) -> None:
|
|
26
28
|
"""Ensure equivalence of basis state mapping.
|
|
27
29
|
|
|
28
30
|
Args:
|
cirq/testing/json.py
CHANGED
|
@@ -18,9 +18,10 @@ import dataclasses
|
|
|
18
18
|
import inspect
|
|
19
19
|
import io
|
|
20
20
|
import pathlib
|
|
21
|
+
from collections.abc import Iterator
|
|
21
22
|
from dataclasses import dataclass
|
|
22
23
|
from types import ModuleType
|
|
23
|
-
from typing import
|
|
24
|
+
from typing import TYPE_CHECKING
|
|
24
25
|
|
|
25
26
|
import numpy as np
|
|
26
27
|
import pandas as pd
|
|
@@ -149,7 +150,7 @@ def spec_for(module_name: str) -> ModuleJsonTestSpec:
|
|
|
149
150
|
return getattr(test_module, "TestSpec")
|
|
150
151
|
|
|
151
152
|
|
|
152
|
-
def assert_json_roundtrip_works(obj, text_should_be=None, resolvers=None):
|
|
153
|
+
def assert_json_roundtrip_works(obj, text_should_be=None, resolvers=None) -> None:
|
|
153
154
|
"""Tests that the given object can serialized and de-serialized
|
|
154
155
|
|
|
155
156
|
Args:
|
cirq/testing/lin_alg_utils.py
CHANGED
|
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
|
|
|
28
28
|
|
|
29
29
|
def random_superposition(
|
|
30
30
|
dim: int, *, random_state: cirq.RANDOM_STATE_OR_SEED_LIKE = None
|
|
31
|
-
) -> np.ndarray:
|
|
31
|
+
) -> np.ndarray[tuple[int], np.dtype[np.complex128]]:
|
|
32
32
|
"""Returns a random unit-length vector from the uniform distribution.
|
|
33
33
|
|
|
34
34
|
Args:
|
cirq/testing/logs.py
CHANGED
cirq/testing/op_tree.py
CHANGED
|
@@ -17,7 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
from cirq import ops
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def assert_equivalent_op_tree(x: ops.OP_TREE, y: ops.OP_TREE):
|
|
20
|
+
def assert_equivalent_op_tree(x: ops.OP_TREE, y: ops.OP_TREE) -> None:
|
|
21
21
|
"""Ensures that the two OP_TREEs are equivalent.
|
|
22
22
|
|
|
23
23
|
Args:
|
cirq/testing/order_tester.py
CHANGED
|
@@ -78,7 +78,7 @@ class OrderTester:
|
|
|
78
78
|
f"That rule is being violated by this value: {item!r}"
|
|
79
79
|
) from ex
|
|
80
80
|
|
|
81
|
-
def add_ascending(self, *items: Any):
|
|
81
|
+
def add_ascending(self, *items: Any) -> None:
|
|
82
82
|
"""Tries to add a sequence of ascending items to the order tester.
|
|
83
83
|
|
|
84
84
|
This methods asserts that items must all be ascending
|
|
@@ -98,7 +98,7 @@ class OrderTester:
|
|
|
98
98
|
for item in items:
|
|
99
99
|
self.add_ascending_equivalence_group(item)
|
|
100
100
|
|
|
101
|
-
def add_ascending_equivalence_group(self, *group_items: Any):
|
|
101
|
+
def add_ascending_equivalence_group(self, *group_items: Any) -> None:
|
|
102
102
|
"""Tries to add an ascending equivalence group to the order tester.
|
|
103
103
|
|
|
104
104
|
Asserts that the group items are equal to each other, but strictly
|
cirq/testing/pytest_utils.py
CHANGED
cirq/testing/random_circuit.py
CHANGED
|
@@ -34,11 +34,11 @@ class FakePrinter:
|
|
|
34
34
|
def __init__(self):
|
|
35
35
|
self.text_pretty = ""
|
|
36
36
|
|
|
37
|
-
def text(self, to_print):
|
|
37
|
+
def text(self, to_print) -> None:
|
|
38
38
|
self.text_pretty += to_print
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
def assert_repr_pretty(val: Any, text: str, cycle: bool = False):
|
|
41
|
+
def assert_repr_pretty(val: Any, text: str, cycle: bool = False) -> None:
|
|
42
42
|
"""Assert that the given object has a `_repr_pretty_` method that produces the given text.
|
|
43
43
|
|
|
44
44
|
Args:
|
|
@@ -57,7 +57,7 @@ def assert_repr_pretty(val: Any, text: str, cycle: bool = False):
|
|
|
57
57
|
assert p.text_pretty == text, f"{p.text_pretty} != {text}"
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def assert_repr_pretty_contains(val: Any, substr: str, cycle: bool = False):
|
|
60
|
+
def assert_repr_pretty_contains(val: Any, substr: str, cycle: bool = False) -> None:
|
|
61
61
|
"""Assert that the given object has a `_repr_pretty_` output that contains the given text.
|
|
62
62
|
|
|
63
63
|
Args:
|
cirq/transformers/__init__.py
CHANGED
|
@@ -150,6 +150,7 @@ from cirq.transformers.gauge_compiling import (
|
|
|
150
150
|
SpinInversionGaugeTransformer as SpinInversionGaugeTransformer,
|
|
151
151
|
SqrtCZGaugeTransformer as SqrtCZGaugeTransformer,
|
|
152
152
|
SqrtISWAPGaugeTransformer as SqrtISWAPGaugeTransformer,
|
|
153
|
+
CPhaseGaugeTransformerMM as CPhaseGaugeTransformerMM,
|
|
153
154
|
)
|
|
154
155
|
|
|
155
156
|
from cirq.transformers.randomized_measurements import (
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
"""Defines a connected component of operations, to be used in merge transformers."""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from collections.abc import Callable, Sequence
|
|
20
|
+
from typing import cast, TYPE_CHECKING
|
|
21
|
+
|
|
22
|
+
from scipy.cluster.hierarchy import DisjointSet
|
|
23
|
+
|
|
24
|
+
from cirq import ops, protocols
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
import cirq
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Component:
|
|
31
|
+
"""Internal representation for a connected component of operations."""
|
|
32
|
+
|
|
33
|
+
# Circuit moment containing the component
|
|
34
|
+
moment_id: int
|
|
35
|
+
# Union of all op qubits in the component
|
|
36
|
+
qubits: frozenset[cirq.Qid]
|
|
37
|
+
# Union of all measurement keys in the component
|
|
38
|
+
mkeys: frozenset[cirq.MeasurementKey]
|
|
39
|
+
# Union of all control keys in the component
|
|
40
|
+
ckeys: frozenset[cirq.MeasurementKey]
|
|
41
|
+
# Initial operation in the component
|
|
42
|
+
op: cirq.Operation
|
|
43
|
+
|
|
44
|
+
# True if the component can be merged with other components
|
|
45
|
+
is_mergeable: bool
|
|
46
|
+
|
|
47
|
+
def __init__(self, op: cirq.Operation, moment_id: int, is_mergeable=True):
|
|
48
|
+
"""Initializes a singleton component."""
|
|
49
|
+
self.op = op
|
|
50
|
+
self.is_mergeable = is_mergeable
|
|
51
|
+
self.moment_id = moment_id
|
|
52
|
+
self.qubits = frozenset(op.qubits)
|
|
53
|
+
self.mkeys = protocols.measurement_key_objs(op)
|
|
54
|
+
self.ckeys = protocols.control_keys(op)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ComponentWithOps(Component):
|
|
58
|
+
"""Component that keeps track of operations."""
|
|
59
|
+
|
|
60
|
+
# List of all operations in the component
|
|
61
|
+
ops: list[cirq.Operation]
|
|
62
|
+
|
|
63
|
+
def __init__(self, op: cirq.Operation, moment_id: int, is_mergeable=True):
|
|
64
|
+
super().__init__(op, moment_id, is_mergeable)
|
|
65
|
+
self.ops = [op]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ComponentWithCircuitOp(Component):
|
|
69
|
+
"""Component that keeps track of operations as a CircuitOperation."""
|
|
70
|
+
|
|
71
|
+
# CircuitOperation containing all the operations in the component,
|
|
72
|
+
# or a single Operation if the component is a singleton
|
|
73
|
+
circuit_op: cirq.Operation
|
|
74
|
+
|
|
75
|
+
def __init__(self, op: cirq.Operation, moment_id: int, is_mergeable=True):
|
|
76
|
+
super().__init__(op, moment_id, is_mergeable)
|
|
77
|
+
self.circuit_op = op
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ComponentSet:
|
|
81
|
+
"""Represents a set of mergeable components of operations."""
|
|
82
|
+
|
|
83
|
+
_comp_type: type[Component]
|
|
84
|
+
|
|
85
|
+
_disjoint_set: DisjointSet
|
|
86
|
+
|
|
87
|
+
# Callable to decide if a component is mergeable
|
|
88
|
+
_is_mergeable: Callable[[cirq.Operation], bool]
|
|
89
|
+
|
|
90
|
+
# List of components in creation order
|
|
91
|
+
_components: list[Component]
|
|
92
|
+
|
|
93
|
+
def __init__(self, is_mergeable: Callable[[cirq.Operation], bool]):
|
|
94
|
+
self._is_mergeable = is_mergeable
|
|
95
|
+
self._disjoint_set = DisjointSet()
|
|
96
|
+
self._components = []
|
|
97
|
+
self._comp_type = Component
|
|
98
|
+
|
|
99
|
+
def new_component(self, op: cirq.Operation, moment_id: int, is_mergeable=True) -> Component:
|
|
100
|
+
"""Creates a new component and adds it to the set."""
|
|
101
|
+
c = self._comp_type(op, moment_id, is_mergeable and self._is_mergeable(op))
|
|
102
|
+
self._disjoint_set.add(c)
|
|
103
|
+
self._components.append(c)
|
|
104
|
+
return c
|
|
105
|
+
|
|
106
|
+
def components(self) -> list[Component]:
|
|
107
|
+
"""Returns the initial components in creation order."""
|
|
108
|
+
return self._components
|
|
109
|
+
|
|
110
|
+
def find(self, x: Component) -> Component:
|
|
111
|
+
"""Finds the representative for a merged component."""
|
|
112
|
+
return self._disjoint_set[x]
|
|
113
|
+
|
|
114
|
+
def merge(self, x: Component, y: Component, merge_left=True) -> Component | None:
|
|
115
|
+
"""Attempts to merge two components.
|
|
116
|
+
|
|
117
|
+
If merge_left is True, y is merged into x, and the representative will keep
|
|
118
|
+
y's moment. If merge_left is False, x is merged into y, and the representative
|
|
119
|
+
will keep y's moment.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
x: First component to merge.
|
|
123
|
+
y: Second component to merge.
|
|
124
|
+
merge_left: True to keep x's moment for the merged component, False to
|
|
125
|
+
keep y's moment for the merged component.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
None, if the components can't be merged.
|
|
129
|
+
Otherwise the new component representative.
|
|
130
|
+
"""
|
|
131
|
+
x = self._disjoint_set[x]
|
|
132
|
+
y = self._disjoint_set[y]
|
|
133
|
+
|
|
134
|
+
if not x.is_mergeable or not y.is_mergeable:
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
if not self._disjoint_set.merge(x, y):
|
|
138
|
+
return x
|
|
139
|
+
|
|
140
|
+
root = self._disjoint_set[x]
|
|
141
|
+
root.moment_id = x.moment_id if merge_left else y.moment_id
|
|
142
|
+
root.qubits = x.qubits.union(y.qubits)
|
|
143
|
+
root.mkeys = x.mkeys.union(y.mkeys)
|
|
144
|
+
root.ckeys = x.ckeys.union(y.ckeys)
|
|
145
|
+
|
|
146
|
+
return root
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class ComponentWithOpsSet(ComponentSet):
|
|
150
|
+
"""Represents a set of mergeable components, where each component tracks operations."""
|
|
151
|
+
|
|
152
|
+
# Callable that returns if two components can be merged based on their operations
|
|
153
|
+
_can_merge: Callable[[Sequence[cirq.Operation], Sequence[cirq.Operation]], bool]
|
|
154
|
+
|
|
155
|
+
def __init__(
|
|
156
|
+
self,
|
|
157
|
+
is_mergeable: Callable[[cirq.Operation], bool],
|
|
158
|
+
can_merge: Callable[[Sequence[cirq.Operation], Sequence[cirq.Operation]], bool],
|
|
159
|
+
):
|
|
160
|
+
super().__init__(is_mergeable)
|
|
161
|
+
self._can_merge = can_merge
|
|
162
|
+
self._comp_type = ComponentWithOps
|
|
163
|
+
|
|
164
|
+
def merge(self, x: Component, y: Component, merge_left=True) -> Component | None:
|
|
165
|
+
"""Attempts to merge two components.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
None if can_merge is False or the merge doesn't succeed, otherwise the
|
|
169
|
+
new representative. The representative will have ops = x.ops + y.ops.
|
|
170
|
+
"""
|
|
171
|
+
x = cast(ComponentWithOps, self._disjoint_set[x])
|
|
172
|
+
y = cast(ComponentWithOps, self._disjoint_set[y])
|
|
173
|
+
|
|
174
|
+
if x is y:
|
|
175
|
+
return x
|
|
176
|
+
|
|
177
|
+
if not x.is_mergeable or not y.is_mergeable or not self._can_merge(x.ops, y.ops):
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
root = cast(ComponentWithOps, super().merge(x, y, merge_left))
|
|
181
|
+
root.ops = x.ops + y.ops
|
|
182
|
+
# Clear the ops list in the non-representative component to avoid duplication
|
|
183
|
+
other = y if x is root else x
|
|
184
|
+
other.ops = []
|
|
185
|
+
return root
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class ComponentWithCircuitOpSet(ComponentSet):
|
|
189
|
+
"""Represents a set of mergeable components, with operations as a CircuitOperation."""
|
|
190
|
+
|
|
191
|
+
# Callable that merges CircuitOperations from two components
|
|
192
|
+
_merge_func: Callable[[ops.Operation, ops.Operation], ops.Operation | None]
|
|
193
|
+
|
|
194
|
+
def __init__(
|
|
195
|
+
self,
|
|
196
|
+
is_mergeable: Callable[[cirq.Operation], bool],
|
|
197
|
+
merge_func: Callable[[ops.Operation, ops.Operation], ops.Operation | None],
|
|
198
|
+
):
|
|
199
|
+
super().__init__(is_mergeable)
|
|
200
|
+
self._merge_func = merge_func
|
|
201
|
+
self._comp_type = ComponentWithCircuitOp
|
|
202
|
+
|
|
203
|
+
def merge(self, x: Component, y: Component, merge_left=True) -> Component | None:
|
|
204
|
+
"""Attempts to merge two components.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
None if merge_func returns None or the merge doesn't succeed,
|
|
208
|
+
otherwise the new representative.
|
|
209
|
+
"""
|
|
210
|
+
x = cast(ComponentWithCircuitOp, self._disjoint_set[x])
|
|
211
|
+
y = cast(ComponentWithCircuitOp, self._disjoint_set[y])
|
|
212
|
+
|
|
213
|
+
if x is y:
|
|
214
|
+
return x
|
|
215
|
+
|
|
216
|
+
if not x.is_mergeable or not y.is_mergeable:
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
new_op = self._merge_func(x.circuit_op, y.circuit_op)
|
|
220
|
+
if not new_op:
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
root = cast(ComponentWithCircuitOp, super().merge(x, y, merge_left))
|
|
224
|
+
|
|
225
|
+
root.circuit_op = new_op
|
|
226
|
+
# The merge_func can be arbitrary, so we need to recompute the component properties
|
|
227
|
+
root.qubits = frozenset(new_op.qubits)
|
|
228
|
+
root.mkeys = protocols.measurement_key_objs(new_op)
|
|
229
|
+
root.ckeys = protocols.control_keys(new_op)
|
|
230
|
+
|
|
231
|
+
return root
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
import cirq
|
|
20
|
+
from cirq.transformers._connected_component import (
|
|
21
|
+
ComponentSet,
|
|
22
|
+
ComponentWithCircuitOpSet,
|
|
23
|
+
ComponentWithOpsSet,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _always_mergeable(_: cirq.Operation) -> bool:
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _never_mergeable(_: cirq.Operation) -> bool:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _always_can_merge(_ops1: list[cirq.Operation], _ops2: list[cirq.Operation]) -> bool:
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _never_can_merge(_ops1: list[cirq.Operation], _ops2: list[cirq.Operation]) -> bool:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _merge_as_first_operation(op1: cirq.Operation, _op2: cirq.Operation) -> cirq.Operation:
|
|
44
|
+
return op1
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _merge_never_successful(op1: cirq.Operation, _op2: cirq.Operation) -> None:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_find_returns_itself_for_singleton():
|
|
52
|
+
cset = ComponentSet(_always_mergeable)
|
|
53
|
+
|
|
54
|
+
q = cirq.NamedQubit('x')
|
|
55
|
+
c = cset.new_component(op=cirq.X(q), moment_id=0)
|
|
56
|
+
assert cset.find(c) is c
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_merge_components():
|
|
60
|
+
cset = ComponentSet(_always_mergeable)
|
|
61
|
+
|
|
62
|
+
q = cirq.NamedQubit('x')
|
|
63
|
+
c = [cset.new_component(op=cirq.X(q), moment_id=i) for i in range(5)]
|
|
64
|
+
cset.merge(c[1], c[0])
|
|
65
|
+
cset.merge(c[2], c[1])
|
|
66
|
+
cset.merge(c[4], c[3])
|
|
67
|
+
cset.merge(c[3], c[0])
|
|
68
|
+
|
|
69
|
+
for i in range(5):
|
|
70
|
+
assert cset.find(c[i]) is cset.find(c[0])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_merge_same_component():
|
|
74
|
+
cset = ComponentSet(_always_mergeable)
|
|
75
|
+
|
|
76
|
+
q = cirq.NamedQubit('x')
|
|
77
|
+
c = [cset.new_component(op=cirq.X(q), moment_id=i) for i in range(3)]
|
|
78
|
+
cset.merge(c[1], c[0])
|
|
79
|
+
cset.merge(c[2], c[1])
|
|
80
|
+
|
|
81
|
+
root = cset.find(c[0])
|
|
82
|
+
|
|
83
|
+
assert cset.merge(c[0], c[2]) is root
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_merge_returns_None_if_one_component_is_not_mergeable():
|
|
87
|
+
cset = ComponentSet(_always_mergeable)
|
|
88
|
+
|
|
89
|
+
q = cirq.NamedQubit('x')
|
|
90
|
+
c0 = cset.new_component(op=cirq.X(q), moment_id=0, is_mergeable=True)
|
|
91
|
+
c1 = cset.new_component(op=cirq.X(q), moment_id=1, is_mergeable=False)
|
|
92
|
+
assert cset.merge(c0, c1) is None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_cset_merge_returns_None_if_is_mergeable_is_false():
|
|
96
|
+
q = cirq.NamedQubit('x')
|
|
97
|
+
cset = ComponentSet(is_mergeable=_never_mergeable)
|
|
98
|
+
|
|
99
|
+
c0 = cset.new_component(op=cirq.X(q), moment_id=0, is_mergeable=True)
|
|
100
|
+
c1 = cset.new_component(op=cirq.X(q), moment_id=1, is_mergeable=True)
|
|
101
|
+
assert cset.merge(c0, c1) is None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@pytest.mark.parametrize("merge_left,expected_moment_id", [(True, 0), (False, 1)])
|
|
105
|
+
def test_merge_qubits_with_merge_left(merge_left: bool, expected_moment_id: int) -> None:
|
|
106
|
+
cset = ComponentSet(_always_mergeable)
|
|
107
|
+
|
|
108
|
+
q0 = cirq.NamedQubit('x')
|
|
109
|
+
q1 = cirq.NamedQubit('y')
|
|
110
|
+
c0 = cset.new_component(op=cirq.X(q0), moment_id=0)
|
|
111
|
+
c1 = cset.new_component(op=cirq.X(q1), moment_id=1)
|
|
112
|
+
c2 = cset.new_component(op=cirq.X(q1), moment_id=2)
|
|
113
|
+
cset.merge(c1, c2)
|
|
114
|
+
cset.merge(c0, c1, merge_left=merge_left)
|
|
115
|
+
assert cset.find(c1).qubits == frozenset([q0, q1])
|
|
116
|
+
assert cset.find(c1).moment_id == expected_moment_id
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_component_with_ops_merge():
|
|
120
|
+
cset = ComponentWithOpsSet(_always_mergeable, _always_can_merge)
|
|
121
|
+
|
|
122
|
+
q = cirq.LineQubit.range(3)
|
|
123
|
+
ops = [cirq.X(q[i]) for i in range(3)]
|
|
124
|
+
c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
|
|
125
|
+
cset.merge(c[0], c[1])
|
|
126
|
+
cset.merge(c[1], c[2])
|
|
127
|
+
assert cset.find(c[0]).ops == ops
|
|
128
|
+
# check merge of indirectly merged components does not make a difference
|
|
129
|
+
assert cset.merge(c[0], c[2]).ops == ops
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_component_with_ops_merge_when_merge_fails():
|
|
133
|
+
cset = ComponentWithOpsSet(_always_mergeable, _never_can_merge)
|
|
134
|
+
|
|
135
|
+
q = cirq.LineQubit.range(3)
|
|
136
|
+
ops = [cirq.X(q[i]) for i in range(3)]
|
|
137
|
+
c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
|
|
138
|
+
|
|
139
|
+
cset.merge(c[0], c[1])
|
|
140
|
+
cset.merge(c[1], c[2])
|
|
141
|
+
# No merge happened
|
|
142
|
+
for i in range(3):
|
|
143
|
+
assert cset.find(c[i]) is c[i]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_component_with_ops_merge_when_is_mergeable_is_false():
|
|
147
|
+
cset = ComponentWithOpsSet(_never_mergeable, _always_can_merge)
|
|
148
|
+
|
|
149
|
+
q = cirq.LineQubit.range(3)
|
|
150
|
+
ops = [cirq.X(q[i]) for i in range(3)]
|
|
151
|
+
c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
|
|
152
|
+
|
|
153
|
+
cset.merge(c[0], c[1])
|
|
154
|
+
cset.merge(c[1], c[2])
|
|
155
|
+
# No merge happened
|
|
156
|
+
for i in range(3):
|
|
157
|
+
assert cset.find(c[i]) is c[i]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_component_with_circuit_op_merge():
|
|
161
|
+
cset = ComponentWithCircuitOpSet(_always_mergeable, _merge_as_first_operation)
|
|
162
|
+
|
|
163
|
+
q = cirq.LineQubit.range(3)
|
|
164
|
+
ops = [cirq.X(q[i]) for i in range(3)]
|
|
165
|
+
c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
|
|
166
|
+
|
|
167
|
+
cset.merge(c[0], c[1])
|
|
168
|
+
cset.merge(c[1], c[2])
|
|
169
|
+
for i in range(3):
|
|
170
|
+
assert cset.find(c[i]).circuit_op == ops[0]
|
|
171
|
+
# check merge of indirectly merged components does not make a difference
|
|
172
|
+
assert cset.merge(c[0], c[2]) is cset.find(c[1])
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_component_with_circuit_op_merge_func_is_none():
|
|
176
|
+
cset = ComponentWithCircuitOpSet(_always_mergeable, _merge_never_successful)
|
|
177
|
+
|
|
178
|
+
q = cirq.LineQubit.range(3)
|
|
179
|
+
ops = [cirq.X(q[i]) for i in range(3)]
|
|
180
|
+
c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
|
|
181
|
+
|
|
182
|
+
cset.merge(c[0], c[1])
|
|
183
|
+
cset.merge(c[1], c[2])
|
|
184
|
+
# No merge happened
|
|
185
|
+
for i in range(3):
|
|
186
|
+
assert cset.find(c[i]) is c[i]
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def test_component_with_circuit_op_merge_when_is_mergeable_is_false():
|
|
190
|
+
cset = ComponentWithCircuitOpSet(_never_mergeable, _merge_as_first_operation)
|
|
191
|
+
|
|
192
|
+
q = cirq.LineQubit.range(3)
|
|
193
|
+
ops = [cirq.X(q[i]) for i in range(3)]
|
|
194
|
+
c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
|
|
195
|
+
|
|
196
|
+
cset.merge(c[0], c[1])
|
|
197
|
+
cset.merge(c[1], c[2])
|
|
198
|
+
# No merge happened
|
|
199
|
+
for i in range(3):
|
|
200
|
+
assert cset.find(c[i]) is c[i]
|