From 331c99d71b9755d2e19179fd3c9d3388a77847b7 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:01:16 -0700 Subject: [PATCH 01/12] Consistent final_state_vector --- cirq-core/cirq/__init__.py | 1 + cirq-core/cirq/circuits/circuit.py | 117 +++++++------- cirq-core/cirq/circuits/circuit_test.py | 151 +++++++++++------- cirq-core/cirq/ops/pauli_string_test.py | 4 +- cirq-core/cirq/sim/mux.py | 20 ++- cirq-core/cirq/sim/mux_test.py | 14 ++ cirq-core/cirq/transformers/__init__.py | 1 + .../transformers/measurement_transformers.py | 37 +++++ .../measurement_transformers_test.py | 33 ++++ docs/tutorials/educators/chemistry.ipynb | 6 +- docs/tutorials/fourier_checking.ipynb | 2 +- 11 files changed, 260 insertions(+), 126 deletions(-) diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py index 19c9c3009be..f2ecb67cecd 100644 --- a/cirq-core/cirq/__init__.py +++ b/cirq-core/cirq/__init__.py @@ -370,6 +370,7 @@ dephase_measurements, drop_empty_moments, drop_negligible_operations, + drop_terminal_measurements, eject_phased_paulis, eject_z, expand_composite, diff --git a/cirq-core/cirq/circuits/circuit.py b/cirq-core/cirq/circuits/circuit.py index 9f4251ebdb7..312cae3ef8d 100644 --- a/cirq-core/cirq/circuits/circuit.py +++ b/cirq-core/cirq/circuits/circuit.py @@ -1036,96 +1036,93 @@ def _superoperator_(self) -> np.ndarray: if n > 10: raise ValueError(f"{n} > 10 qubits is too many to compute superoperator") - circuit_superoperator = np.eye(4**n) + circuit_superoperator = np.eye(4 ** n) for moment in self: full_moment = moment.expand_to(all_qubits) moment_superoperator = full_moment._superoperator_() circuit_superoperator = moment_superoperator @ circuit_superoperator return circuit_superoperator + @_compat.deprecated_parameter( + deadline='v0.16', + fix='Inject identity operators to include untouched qubits.', + parameter_desc='qubits_that_should_be_present', + match=lambda args, kwargs: 'qubits_that_should_be_present' in kwargs, + ) + @_compat.deprecated_parameter( + deadline='v0.16', + fix='Only use keyword arguments.', + parameter_desc='positional args', + match=lambda args, kwargs: len(args) > 1, + ) def final_state_vector( self, + # TODO(v0.16): Force kwargs and match order found in: + # cirq-core/cirq/sim/mux.py:final_state_vector initial_state: 'cirq.STATE_VECTOR_LIKE' = 0, qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT, qubits_that_should_be_present: Iterable['cirq.Qid'] = (), ignore_terminal_measurements: bool = True, - dtype: Type[np.number] = np.complex128, + dtype: Type[np.number] = np.complex64, + param_resolver: 'cirq.ParamResolverOrSimilarType' = None, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> np.ndarray: - """Left-multiplies a state vector by the circuit's unitary effect. - - A circuit's "unitary effect" is the unitary matrix produced by - multiplying together all of its gates' unitary matrices. A circuit - with non-unitary gates (such as measurement or parameterized gates) does - not have a well-defined unitary effect, and the method will fail if such - operations are present. - - For convenience, terminal measurements are automatically ignored - instead of causing a failure. Set the `ignore_terminal_measurements` - argument to False to disable this behavior. + """Returns the state vector resulting from acting operations on a state. - This method is equivalent to left-multiplying the input state by - `cirq.unitary(circuit)` but it's computed in a more efficient - way. + This is equivalent to calling cirq.final_state_vector with the same + arguments and this circuit as the "program". Args: - initial_state: The input state for the circuit. This can be a list - of qudit values, a big endian int encoding the qudit values, - a vector of amplitudes, or a tensor of amplitudes. - - When this is an int, it refers to a computational - basis state (e.g. 5 means initialize to ``|5⟩ = |...000101⟩``). - - If this is a vector of amplitudes (a flat numpy array of the - correct length for the system) or a tensor of amplitudes (a - numpy array whose shape equals this circuit's `qid_shape`), it - directly specifies the initial state's amplitudes. The vector - type must be convertible to the given `dtype` argument. - qubit_order: Determines how qubits are ordered when passing matrices - into np.kron. + initial_state: If an int, the state is set to the computational + basis state corresponding to this state. Otherwise if this + is a np.ndarray it is the full initial state. In this case it + must be the correct size, be normalized (an L2 norm of 1), and + be safely castable to an appropriate dtype for the simulator. + qubit_order: Determines the canonical ordering of the qubits. This + is often used in specifying the initial state, i.e. the + ordering of the computational basis states. qubits_that_should_be_present: Qubits that may or may not appear in operations within the circuit, but that should be included regardless when generating the matrix. ignore_terminal_measurements: When set, measurements at the end of the circuit are ignored instead of causing the method to fail. - dtype: The numpy dtype for the returned unitary. Defaults to - np.complex128. Specifying np.complex64 will run faster at the - cost of precision. `dtype` must be a complex np.dtype, unless - all operations in the circuit have unitary matrices with - exclusively real coefficients (e.g. an H + TOFFOLI circuit). + dtype: The `numpy.dtype` used by the simulation. Typically one of + `numpy.complex64` or `numpy.complex128`. + param_resolver: Parameters to run with the program. + seed: The random seed to use for this simulator. Returns: - A (possibly gigantic) numpy array storing the superposition that - came out of the circuit for the given input state. + The state vector resulting from applying the given unitary + operations to the desired initial state. Specifically, a numpy + array containing the the amplitudes in np.kron order, where the + order of arguments to kron is determined by the qubit order + argument (which defaults to just sorting the qubits that are + present into an ascending order). Raises: - ValueError: The circuit contains measurement gates that are not - ignored. - TypeError: The circuit contains gates that don't have a known - unitary matrix, e.g. gates parameterized by a Symbol. + ValueError: If the program doesn't have a well defined final state + because it has non-unitary gates. """ + if ignore_terminal_measurements and self.has_measurements(): + _compat._warn_or_error( + '`ignore_terminal_measurements` will default to False in v0.16. ' + 'To drop terminal measurements, please explicitly include ' + '`ignore_terminal_measurements=True` when calling this method.' + ) - if not ignore_terminal_measurements and any( - protocols.is_measurement(op) for op in self.all_operations() - ): - raise ValueError('Circuit contains a measurement.') - - if not self.are_all_measurements_terminal(): - raise ValueError('Circuit contains a non-terminal measurement.') - - qs = ops.QubitOrder.as_qubit_order(qubit_order).order_for( - self.all_qubits().union(qubits_that_should_be_present) - ) - - # Force qubits to have dimension at least 2 for backwards compatibility. - qid_shape = self.qid_shape(qubit_order=qs) - state_len = np.prod(qid_shape, dtype=np.int64) + from cirq.sim.mux import final_state_vector - state = qis.to_valid_state_vector(initial_state, qid_shape=qid_shape, dtype=dtype).reshape( - qid_shape + program = [cirq.I(q) for q in qubits_that_should_be_present] + self + return final_state_vector( + program, + initial_state=initial_state, + param_resolver=param_resolver, + qubit_order=qubit_order, + ignore_terminal_measurements=ignore_terminal_measurements, + dtype=dtype, + seed=seed, ) - result = _apply_unitary_circuit(self, state, qs, dtype) - return result.reshape((state_len,)) def to_text_diagram( self, diff --git a/cirq-core/cirq/circuits/circuit_test.py b/cirq-core/cirq/circuits/circuit_test.py index f1d7c131a0f..956a55519cb 100644 --- a/cirq-core/cirq/circuits/circuit_test.py +++ b/cirq-core/cirq/circuits/circuit_test.py @@ -1139,7 +1139,7 @@ def test_next_moment_operating_on_distance(circuit_cls): assert c.next_moment_operating_on([a], 1, max_distance=500) == 4 # Huge max distances should be handled quickly due to capping. - assert c.next_moment_operating_on([a], 5, max_distance=10**100) is None + assert c.next_moment_operating_on([a], 5, max_distance=10 ** 100) is None with pytest.raises(ValueError, match='Negative max_distance'): c.next_moment_operating_on([a], 0, max_distance=-1) @@ -1228,7 +1228,7 @@ def test_prev_moment_operating_on_distance(circuit_cls): assert c.prev_moment_operating_on([a], 13, max_distance=500) == 1 # Huge max distances should be handled quickly due to capping. - assert c.prev_moment_operating_on([a], 1, max_distance=10**100) is None + assert c.prev_moment_operating_on([a], 1, max_distance=10 ** 100) is None with pytest.raises(ValueError, match='Negative max_distance'): c.prev_moment_operating_on([a], 6, max_distance=-1) @@ -1535,7 +1535,7 @@ def test_findall_operations_until_blocked(circuit_cls): ) -@pytest.mark.parametrize('seed', [randint(0, 2**31)]) +@pytest.mark.parametrize('seed', [randint(0, 2 ** 31)]) @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_findall_operations_until_blocked_docstring_examples(seed, circuit_cls): prng = np.random.RandomState(seed) @@ -2693,7 +2693,7 @@ def pauli_error_probability(r: float, n_qubits: int) -> float: makes it simple to compute the serial composition of depolarizing channels. It is multiplicative under channel composition. """ - d2 = 4**n_qubits + d2 = 4 ** n_qubits return (1 - r) * (d2 - 1) / d2 def depolarize(r: float, n_qubits: int) -> cirq.DepolarizingChannel: @@ -2879,7 +2879,7 @@ def test_insert_moments(): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) -def test_apply_unitary_effect_to_state(circuit_cls): +def test_final_state_vector(circuit_cls): a = cirq.NamedQubit('a') b = cirq.NamedQubit('b') @@ -2962,12 +2962,24 @@ def test_apply_unitary_effect_to_state(circuit_cls): ) # Measurements. - cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.measure(a)).final_state_vector(), np.array([1, 0]), atol=1e-8 - ) - cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(a), cirq.measure(a)).final_state_vector(), np.array([0, 1]), atol=1e-8 - ) + # TODO: remove deprecation assertions after v0.16, but keep tests. + # Warnings are unavoidable due to how they are triggered. + with cirq.testing.assert_deprecated("To drop terminal measurements", deadline="v0.16"): + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls(cirq.measure(a)).final_state_vector( + ignore_terminal_measurements=True + ), + np.array([1, 0]), + atol=1e-8 + ) + with cirq.testing.assert_deprecated("To drop terminal measurements", deadline="v0.16"): + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls(cirq.X(a), cirq.measure(a)).final_state_vector( + ignore_terminal_measurements=True + ), + np.array([0, 1]), + atol=1e-8 + ) with pytest.raises(ValueError): cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.measure(a), cirq.X(a)).final_state_vector(), @@ -2981,21 +2993,6 @@ def test_apply_unitary_effect_to_state(circuit_cls): atol=1e-8, ) - # Extra qubits. - cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls().final_state_vector(), np.array([1]), atol=1e-8 - ) - cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls().final_state_vector(qubits_that_should_be_present=[a]), - np.array([1, 0]), - atol=1e-8, - ) - cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(b)).final_state_vector(qubits_that_should_be_present=[a]), - np.array([0, 1, 0, 0]), - atol=1e-8, - ) - # Qubit order. cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.Z(a), cirq.X(b)).final_state_vector(qubit_order=[a, b]), @@ -3022,6 +3019,43 @@ def test_apply_unitary_effect_to_state(circuit_cls): ) +@pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) +def test_final_state_vector_deprecated_params(circuit_cls): + a = cirq.NamedQubit('a') + b = cirq.NamedQubit('b') + # Extra qubits. + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls().final_state_vector(), np.array([1]), atol=1e-8 + ) + with cirq.testing.assert_deprecated("Inject identity operators", deadline="v0.16"): + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls().final_state_vector(qubits_that_should_be_present=[a]), + np.array([1, 0]), + atol=1e-8, + ) + with cirq.testing.assert_deprecated("Inject identity operators", deadline="v0.16"): + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls(cirq.X(b)).final_state_vector(qubits_that_should_be_present=[a]), + np.array([0, 1, 0, 0]), + atol=1e-8, + ) + + with cirq.testing.assert_deprecated("To drop terminal measurements", deadline="v0.16"): + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls(cirq.X(a), cirq.measure(a)).final_state_vector(), + np.array([0, 1]), + atol=1e-8 + ) + + # Non-keyword args. + with cirq.testing.assert_deprecated("Only use keyword arguments", deadline="v0.16"): + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls(cirq.X(a) ** 0.5).final_state_vector(1), + np.array([1, 1j]) * np.sqrt(0.5), + atol=1e-8, + ) + + @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) @pytest.mark.parametrize('resolve_fn', [cirq.resolve_parameters, cirq.resolve_parameters_once]) def test_is_parameterized(circuit_cls, resolve_fn): @@ -3817,9 +3851,9 @@ def test_submoments(circuit_cls): cirq.H.on(d), cirq.CZ.on(a, d), cirq.CZ.on(b, c), - (cirq.CNOT**0.5).on(a, d), - (cirq.CNOT**0.5).on(b, e), - (cirq.CNOT**0.5).on(c, f), + (cirq.CNOT ** 0.5).on(a, d), + (cirq.CNOT ** 0.5).on(b, e), + (cirq.CNOT ** 0.5).on(c, f), cirq.H.on(c), cirq.H.on(e), ) @@ -3946,12 +3980,15 @@ def test_measurement_key_mapping(circuit_cls): assert simulator.run(c).measurements == {'m1': 1, 'm2': 0} assert simulator.run(c_swapped).measurements == {'m1': 0, 'm2': 1} - assert cirq.with_measurement_key_mapping( - c, - { - 'x': 'z', - }, - ).all_measurement_key_names() == {'m1', 'm2'} + assert ( + cirq.with_measurement_key_mapping( + c, + { + 'x': 'z', + }, + ).all_measurement_key_names() + == {'m1', 'm2'} + ) @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) @@ -3974,7 +4011,7 @@ def test_measurement_key_mapping_preserves_moments(circuit_cls): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_inverse(circuit_cls): a, b = cirq.LineQubit.range(2) - forward = circuit_cls((cirq.X**0.5)(a), (cirq.Y**-0.2)(b), cirq.CZ(a, b)) + forward = circuit_cls((cirq.X ** 0.5)(a), (cirq.Y ** -0.2)(b), cirq.CZ(a, b)) backward = circuit_cls((cirq.CZ ** (-1.0))(a, b), (cirq.X ** (-0.5))(a), (cirq.Y ** (0.2))(b)) cirq.testing.assert_same_circuits(cirq.inverse(forward), backward) @@ -3985,7 +4022,7 @@ def test_inverse(circuit_cls): cirq.inverse(no_inverse) # Default when there is no inverse for an op. - default = circuit_cls((cirq.X**0.5)(a), (cirq.Y**-0.2)(b)) + default = circuit_cls((cirq.X ** 0.5)(a), (cirq.Y ** -0.2)(b)) cirq.testing.assert_same_circuits(cirq.inverse(no_inverse, default), default) assert cirq.inverse(no_inverse, None) is None @@ -3993,7 +4030,7 @@ def test_inverse(circuit_cls): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_pow_valid_only_for_minus_1(circuit_cls): a, b = cirq.LineQubit.range(2) - forward = circuit_cls((cirq.X**0.5)(a), (cirq.Y**-0.2)(b), cirq.CZ(a, b)) + forward = circuit_cls((cirq.X ** 0.5)(a), (cirq.Y ** -0.2)(b), cirq.CZ(a, b)) backward = circuit_cls((cirq.CZ ** (-1.0))(a, b), (cirq.X ** (-0.5))(a), (cirq.Y ** (0.2))(b)) cirq.testing.assert_same_circuits(cirq.pow(forward, -1), backward) @@ -4334,22 +4371,28 @@ def _measurement_key_name_(self): assert circuit_cls().all_measurement_key_names() == set() # Order does not matter. - assert circuit_cls( - cirq.Moment( - [ - cirq.measure(a, key='x'), - cirq.measure(b, key='y'), - ] - ) - ).all_measurement_key_names() == {'x', 'y'} - assert circuit_cls( - cirq.Moment( - [ - cirq.measure(b, key='y'), - cirq.measure(a, key='x'), - ] - ) - ).all_measurement_key_names() == {'x', 'y'} + assert ( + circuit_cls( + cirq.Moment( + [ + cirq.measure(a, key='x'), + cirq.measure(b, key='y'), + ] + ) + ).all_measurement_key_names() + == {'x', 'y'} + ) + assert ( + circuit_cls( + cirq.Moment( + [ + cirq.measure(b, key='y'), + cirq.measure(a, key='x'), + ] + ) + ).all_measurement_key_names() + == {'x', 'y'} + ) def test_zip(): diff --git a/cirq-core/cirq/ops/pauli_string_test.py b/cirq-core/cirq/ops/pauli_string_test.py index c164d573b38..39c3d6585c3 100644 --- a/cirq-core/cirq/ops/pauli_string_test.py +++ b/cirq-core/cirq/ops/pauli_string_test.py @@ -591,7 +591,7 @@ def test_to_z_basis_ops(): circuit = cirq.Circuit(pauli_string.to_z_basis_ops()) initial_state = cirq.kron(x0, x1, y0, y1, z0, z1, shape_len=1) - z_basis_state = circuit.final_state_vector(initial_state) + z_basis_state = circuit.final_state_vector(initial_state=initial_state) expected_state = np.zeros(2**6) expected_state[0b010101] = 1 @@ -616,7 +616,7 @@ def test_to_z_basis_ops_product_state(): * cirq.KET_ZERO(q4) * cirq.KET_ONE(q5) ) - z_basis_state = circuit.final_state_vector(initial_state) + z_basis_state = circuit.final_state_vector(initial_state=initial_state) expected_state = np.zeros(2**6) expected_state[0b010101] = 1 diff --git a/cirq-core/cirq/sim/mux.py b/cirq-core/cirq/sim/mux.py index 2095abe3113..39ef89e9c34 100644 --- a/cirq-core/cirq/sim/mux.py +++ b/cirq-core/cirq/sim/mux.py @@ -21,7 +21,7 @@ import numpy as np -from cirq import circuits, protocols, study, devices, ops, value +from cirq import _compat, circuits, protocols, study, devices, ops, value from cirq._doc import document from cirq.sim import sparse_simulator, density_matrix_simulator from cirq.sim.clifford import clifford_simulator @@ -99,13 +99,13 @@ def _to_circuit(program: 'cirq.CIRCUIT_LIKE') -> 'cirq.Circuit': result = circuits.Circuit(program) return cast('cirq.Circuit', result) - def final_state_vector( program: 'cirq.CIRCUIT_LIKE', *, initial_state: 'cirq.STATE_VECTOR_LIKE' = 0, param_resolver: 'cirq.ParamResolverOrSimilarType' = None, qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT, + ignore_terminal_measurements: bool = False, dtype: Type[np.number] = np.complex64, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> 'np.ndarray': @@ -117,15 +117,18 @@ def final_state_vector( Args: program: The circuit, gate, operation, or tree of operations to apply to the initial state in order to produce the result. - param_resolver: Parameters to run with the program. - qubit_order: Determines the canonical ordering of the qubits. This - is often used in specifying the initial state, i.e. the - ordering of the computational basis states. initial_state: If an int, the state is set to the computational basis state corresponding to this state. Otherwise if this is a np.ndarray it is the full initial state. In this case it must be the correct size, be normalized (an L2 norm of 1), and be safely castable to an appropriate dtype for the simulator. + param_resolver: Parameters to run with the program. + qubit_order: Determines the canonical ordering of the qubits. This + is often used in specifying the initial state, i.e. the + ordering of the computational basis states. + ignore_terminal_measurements: When set, measurements at the end of + the circuit are ignored instead of causing the method to + fail. dtype: The `numpy.dtype` used by the simulation. Typically one of `numpy.complex64` or `numpy.complex128`. seed: The random seed to use for this simulator. @@ -142,6 +145,11 @@ def final_state_vector( it has non-unitary gates. """ circuit_like = _to_circuit(program) + if not ignore_terminal_measurements: + if any(protocols.is_measurement(op) for op in circuit_like.all_operations()): + raise ValueError('Circuit contains a measurement.') + else: + circuit_like = measurement_transformers.drop_terminal_measurements(circuit_like) if not protocols.has_unitary(protocols.resolve_parameters(circuit_like, param_resolver)): raise ValueError( diff --git a/cirq-core/cirq/sim/mux_test.py b/cirq-core/cirq/sim/mux_test.py index 2b47840cacb..e269961fe17 100644 --- a/cirq-core/cirq/sim/mux_test.py +++ b/cirq-core/cirq/sim/mux_test.py @@ -218,6 +218,20 @@ def test_final_state_vector_qubit_order(): ) +def test_final_state_vector_ignore_terminal_measurement(): + a, b = cirq.LineQubit.range(2) + + np.testing.assert_allclose( + cirq.final_state_vector( + [cirq.X(a), cirq.X(b) ** 0.5, cirq.measure(a, b, key='m')], + ignore_terminal_measurements=True, + ), + [0, 0, 0.5 + 0.5j, 0.5 - 0.5j], + ) + with pytest.raises(ValueError, match='is not unitary'): + cirq.final_state_vector([cirq.X(a), cirq.X(b), cirq.measure(a, b, key='m')]), + + def test_final_state_vector_seed(): a = cirq.LineQubit(0) np.testing.assert_allclose( diff --git a/cirq-core/cirq/transformers/__init__.py b/cirq-core/cirq/transformers/__init__.py index 33ad13c2e30..d6cbb0639ff 100644 --- a/cirq-core/cirq/transformers/__init__.py +++ b/cirq-core/cirq/transformers/__init__.py @@ -68,6 +68,7 @@ from cirq.transformers.measurement_transformers import ( defer_measurements, dephase_measurements, + drop_terminal_measurements, ) from cirq.transformers.merge_k_qubit_gates import merge_k_qubit_unitaries diff --git a/cirq-core/cirq/transformers/measurement_transformers.py b/cirq-core/cirq/transformers/measurement_transformers.py index 1c1323e0242..1ab6070df69 100644 --- a/cirq-core/cirq/transformers/measurement_transformers.py +++ b/cirq-core/cirq/transformers/measurement_transformers.py @@ -175,3 +175,40 @@ def dephase(op: 'cirq.Operation', _) -> 'cirq.OP_TREE': return transformer_primitives.map_operations( circuit, dephase, deep=True, tags_to_ignore=ignored ).unfreeze() + + +@transformer_api.transformer +def drop_terminal_measurements( + circuit: 'cirq.AbstractCircuit', *, context: Optional['cirq.TransformerContext'] = None +) -> 'cirq.Circuit': + """Removes terminal measurements from a circuit. + + This transformer is helpful when trying to capture the final state vector + of a circuit with many terminal measurements, as simulating the circuit + with those measurements in place would otherwise collapse the final state. + + Args: + circuit: The circuit to transform. It will not be modified. + context: `cirq.TransformerContext` storing common configurable options + for transformers. + Returns: + A copy of the circuit, with identity or X gates in place of terminal + measurements. + Raises: + ValueError: if the circuit contains non-terminal measurements. + """ + + def flip_inversion(op: 'cirq.Operation', _) -> 'cirq.OP_TREE': + if isinstance(op.gate, ops.MeasurementGate): + return [ + ops.X(q) if b else ops.I(q) for q, b in zip(op.qubits, op.gate.full_invert_mask()) + ] + return op + + if not circuit.are_all_measurements_terminal(): + raise ValueError('Circuit contains a non-terminal measurement.') + + ignored = () if context is None else context.tags_to_ignore + return transformer_primitives.map_operations( + circuit, flip_inversion, deep=True, tags_to_ignore=ignored + ).unfreeze() diff --git a/cirq-core/cirq/transformers/measurement_transformers_test.py b/cirq-core/cirq/transformers/measurement_transformers_test.py index 462665278ab..49111264599 100644 --- a/cirq-core/cirq/transformers/measurement_transformers_test.py +++ b/cirq-core/cirq/transformers/measurement_transformers_test.py @@ -357,3 +357,36 @@ def test_dephase_nocompile_context(): ) ), ) + + +def test_drop_terminal(): + q0, q1 = cirq.LineQubit.range(2) + circuit = cirq.Circuit( + cirq.CircuitOperation( + cirq.FrozenCircuit( + cirq.CX(q0, q1), + cirq.measure(q0, q1, key='a~b', invert_mask=[0, 1]), + ) + ) + ) + dropped = cirq.drop_terminal_measurements(circuit) + cirq.testing.assert_same_circuits( + dropped, + cirq.Circuit( + cirq.CircuitOperation(cirq.FrozenCircuit(cirq.CX(q0, q1), cirq.I(q0), cirq.X(q1))) + ), + ) + + +def test_drop_terminal_nonterminal_error(): + q0, q1 = cirq.LineQubit.range(2) + circuit = cirq.Circuit( + cirq.CircuitOperation( + cirq.FrozenCircuit( + cirq.measure(q0, q1, key='a~b', invert_mask=[0, 1]), + cirq.CX(q0, q1), + ) + ) + ) + with pytest.raises(ValueError, match='Circuit contains a non-terminal measurement'): + _ = cirq.drop_terminal_measurements(circuit) diff --git a/docs/tutorials/educators/chemistry.ipynb b/docs/tutorials/educators/chemistry.ipynb index 8901033813c..347adc502f1 100644 --- a/docs/tutorials/educators/chemistry.ipynb +++ b/docs/tutorials/educators/chemistry.ipynb @@ -910,7 +910,7 @@ ")\n", "\n", "# Apply the circuit to the initial state\n", - "result = circuit.final_state_vector(initial_state)\n", + "result = circuit.final_state_vector(initial_state=initial_state)\n", "\n", "# Compute the fidelity with the final state from exact evolution\n", "fidelity = abs(np.dot(exact_state, result.conj()))**2\n", @@ -2178,7 +2178,7 @@ "# ---------------------------------------------\n", "\n", "# Apply the circuit to the initial state\n", - "result = circuit.final_state_vector(initial_state)\n", + "result = circuit.final_state_vector(initial_state=initial_state)\n", "\n", "# Compute the fidelity with the correct final state\n", "fidelity = abs(np.dot(final_state, result.conj()))**2\n", @@ -2232,7 +2232,7 @@ "# ---------------------------------------------\n", "\n", "# Apply the circuit to the initial state\n", - "result = circuit.final_state_vector(initial_state)\n", + "result = circuit.final_state_vector(initial_state=initial_state)\n", "\n", "# Compute the fidelity with the correct final state\n", "fidelity = abs(np.dot(final_state, result.conj()))**2\n", diff --git a/docs/tutorials/fourier_checking.ipynb b/docs/tutorials/fourier_checking.ipynb index 35e30098377..4f4f4fe2774 100644 --- a/docs/tutorials/fourier_checking.ipynb +++ b/docs/tutorials/fourier_checking.ipynb @@ -754,7 +754,7 @@ }, "outputs": [], "source": [ - "final_state = circuit.final_state_vector()\n", + "final_state = circuit.final_state_vector(ignore_terminal_measurements=True)\n", "plt.fill_between(np.arange(len(final_state)),\n", " np.abs(final_state)**2)\n", "plt.xlabel(\"State of qubits\")\n", From 365c3cd6a1c8cb41c5cf3b1744c0b9bab2c0f071 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:56:37 -0700 Subject: [PATCH 02/12] Repair tests --- cirq-core/cirq/ops/pauli_string_test.py | 8 ++++---- cirq-core/cirq/sim/mux_test.py | 19 ++++--------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/cirq-core/cirq/ops/pauli_string_test.py b/cirq-core/cirq/ops/pauli_string_test.py index 99676a12d2c..7e727c4f9e2 100644 --- a/cirq-core/cirq/ops/pauli_string_test.py +++ b/cirq-core/cirq/ops/pauli_string_test.py @@ -1047,7 +1047,7 @@ def test_pauli_string_expectation_from_state_vector_pure_state(): circuit = cirq.Circuit( cirq.X(qubits[1]), cirq.H(qubits[2]), cirq.X(qubits[3]), cirq.H(qubits[3]) ) - wf = circuit.final_state_vector(qubit_order=qubits) + wf = circuit.final_state_vector(qubit_order=qubits, dtype=np.complex128) z0z1 = cirq.PauliString({qubits[0]: cirq.Z, qubits[1]: cirq.Z}) z0z2 = cirq.PauliString({qubits[0]: cirq.Z, qubits[2]: cirq.Z}) @@ -1072,7 +1072,7 @@ def test_pauli_string_expectation_from_state_vector_pure_state_with_coef(): q_map = {q: i for i, q in enumerate(qs)} circuit = cirq.Circuit(cirq.X(qs[1]), cirq.H(qs[2]), cirq.X(qs[3]), cirq.H(qs[3])) - wf = circuit.final_state_vector(qubit_order=qs) + wf = circuit.final_state_vector(qubit_order=qs, dtype=np.complex128) z0z1 = cirq.Z(qs[0]) * cirq.Z(qs[1]) * 0.123 z0z2 = cirq.Z(qs[0]) * cirq.Z(qs[2]) * -1 @@ -1256,7 +1256,7 @@ def test_pauli_string_expectation_from_density_matrix_pure_state(): circuit = cirq.Circuit( cirq.X(qubits[1]), cirq.H(qubits[2]), cirq.X(qubits[3]), cirq.H(qubits[3]) ) - state_vector = circuit.final_state_vector(qubit_order=qubits) + state_vector = circuit.final_state_vector(qubit_order=qubits, dtype=np.complex128) rho = np.outer(state_vector, np.conj(state_vector)) z0z1 = cirq.PauliString({qubits[0]: cirq.Z, qubits[1]: cirq.Z}) @@ -1282,7 +1282,7 @@ def test_pauli_string_expectation_from_density_matrix_pure_state_with_coef(): q_map = {q: i for i, q in enumerate(qs)} circuit = cirq.Circuit(cirq.X(qs[1]), cirq.H(qs[2]), cirq.X(qs[3]), cirq.H(qs[3])) - state_vector = circuit.final_state_vector(qubit_order=qs) + state_vector = circuit.final_state_vector(qubit_order=qs, dtype=np.complex128) rho = np.outer(state_vector, np.conj(state_vector)) z0z1 = cirq.Z(qs[0]) * cirq.Z(qs[1]) * 0.123 diff --git a/cirq-core/cirq/sim/mux_test.py b/cirq-core/cirq/sim/mux_test.py index 1ab72f5505c..a96ba807375 100644 --- a/cirq-core/cirq/sim/mux_test.py +++ b/cirq-core/cirq/sim/mux_test.py @@ -214,21 +214,10 @@ def test_final_state_vector_ignore_terminal_measurement(): [0, 0, 0.5 + 0.5j, 0.5 - 0.5j], ) with pytest.raises(ValueError, match='is not unitary'): - cirq.final_state_vector([cirq.X(a), cirq.X(b), cirq.measure(a, b, key='m')]), - - -def test_final_state_vector_seed(): - a = cirq.LineQubit(0) - np.testing.assert_allclose( - cirq.final_state_vector([cirq.X(a) ** 0.5, cirq.measure(a)], seed=123), - [0, 0.707107 - 0.707107j], - atol=1e-4, - ) - np.testing.assert_allclose( - cirq.final_state_vector([cirq.X(a) ** 0.5, cirq.measure(a)], seed=124), - [0.707107 + 0.707107j, 0], - atol=1e-4, - ) + cirq.final_state_vector( + [cirq.X(a), cirq.amplitude_damp(0.1).on(b), cirq.measure(a, b, key='m')], + ignore_terminal_measurements=True + ), @pytest.mark.parametrize('repetitions', (0, 1, 100)) From 9026c83ae184115a19bbb71ac903eb2cf1b5ade1 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Wed, 20 Apr 2022 13:00:26 -0700 Subject: [PATCH 03/12] Format, type, avoid deprecations --- cirq-core/cirq/circuits/circuit.py | 4 +-- cirq-core/cirq/circuits/circuit_test.py | 30 +++++++++---------- cirq-core/cirq/sim/mux.py | 3 +- cirq-core/cirq/sim/mux_test.py | 4 +-- .../measurement_transformers_test.py | 10 ++----- dev_tools/profiling/benchmark_simulators.py | 3 +- 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/cirq-core/cirq/circuits/circuit.py b/cirq-core/cirq/circuits/circuit.py index b93ff677031..c56c5b5ce86 100644 --- a/cirq-core/cirq/circuits/circuit.py +++ b/cirq-core/cirq/circuits/circuit.py @@ -1034,7 +1034,7 @@ def _superoperator_(self) -> np.ndarray: if n > 10: raise ValueError(f"{n} > 10 qubits is too many to compute superoperator") - circuit_superoperator = np.eye(4 ** n) + circuit_superoperator = np.eye(4**n) for moment in self: full_moment = moment.expand_to(all_qubits) moment_superoperator = full_moment._superoperator_() @@ -1111,7 +1111,7 @@ def final_state_vector( from cirq.sim.mux import final_state_vector - program = [cirq.I(q) for q in qubits_that_should_be_present] + self + program = Circuit(cirq.I(q) for q in qubits_that_should_be_present) + self return final_state_vector( program, initial_state=initial_state, diff --git a/cirq-core/cirq/circuits/circuit_test.py b/cirq-core/cirq/circuits/circuit_test.py index 1f4864c4304..b1a9de7ba47 100644 --- a/cirq-core/cirq/circuits/circuit_test.py +++ b/cirq-core/cirq/circuits/circuit_test.py @@ -1078,7 +1078,7 @@ def test_next_moment_operating_on_distance(circuit_cls): assert c.next_moment_operating_on([a], 1, max_distance=500) == 4 # Huge max distances should be handled quickly due to capping. - assert c.next_moment_operating_on([a], 5, max_distance=10 ** 100) is None + assert c.next_moment_operating_on([a], 5, max_distance=10**100) is None with pytest.raises(ValueError, match='Negative max_distance'): c.next_moment_operating_on([a], 0, max_distance=-1) @@ -1162,7 +1162,7 @@ def test_prev_moment_operating_on_distance(circuit_cls): assert c.prev_moment_operating_on([a], 13, max_distance=500) == 1 # Huge max distances should be handled quickly due to capping. - assert c.prev_moment_operating_on([a], 1, max_distance=10 ** 100) is None + assert c.prev_moment_operating_on([a], 1, max_distance=10**100) is None with pytest.raises(ValueError, match='Negative max_distance'): c.prev_moment_operating_on([a], 6, max_distance=-1) @@ -1469,7 +1469,7 @@ def test_findall_operations_until_blocked(circuit_cls): ) -@pytest.mark.parametrize('seed', [randint(0, 2 ** 31)]) +@pytest.mark.parametrize('seed', [randint(0, 2**31)]) @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_findall_operations_until_blocked_docstring_examples(seed, circuit_cls): prng = np.random.RandomState(seed) @@ -2591,7 +2591,7 @@ def pauli_error_probability(r: float, n_qubits: int) -> float: makes it simple to compute the serial composition of depolarizing channels. It is multiplicative under channel composition. """ - d2 = 4 ** n_qubits + d2 = 4**n_qubits return (1 - r) * (d2 - 1) / d2 def depolarize(r: float, n_qubits: int) -> cirq.DepolarizingChannel: @@ -2860,11 +2860,9 @@ def test_final_state_vector(circuit_cls): # Warnings are unavoidable due to how they are triggered. with cirq.testing.assert_deprecated("To drop terminal measurements", deadline="v0.16"): cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.measure(a)).final_state_vector( - ignore_terminal_measurements=True - ), + circuit_cls(cirq.measure(a)).final_state_vector(ignore_terminal_measurements=True), np.array([1, 0]), - atol=1e-8 + atol=1e-8, ) with cirq.testing.assert_deprecated("To drop terminal measurements", deadline="v0.16"): cirq.testing.assert_allclose_up_to_global_phase( @@ -2872,7 +2870,7 @@ def test_final_state_vector(circuit_cls): ignore_terminal_measurements=True ), np.array([0, 1]), - atol=1e-8 + atol=1e-8, ) with pytest.raises(ValueError): cirq.testing.assert_allclose_up_to_global_phase( @@ -2938,7 +2936,7 @@ def test_final_state_vector_deprecated_params(circuit_cls): cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.X(a), cirq.measure(a)).final_state_vector(), np.array([0, 1]), - atol=1e-8 + atol=1e-8, ) # Non-keyword args. @@ -3700,9 +3698,9 @@ def test_submoments(circuit_cls): cirq.H.on(d), cirq.CZ.on(a, d), cirq.CZ.on(b, c), - (cirq.CNOT ** 0.5).on(a, d), - (cirq.CNOT ** 0.5).on(b, e), - (cirq.CNOT ** 0.5).on(c, f), + (cirq.CNOT**0.5).on(a, d), + (cirq.CNOT**0.5).on(b, e), + (cirq.CNOT**0.5).on(c, f), cirq.H.on(c), cirq.H.on(e), ) @@ -3851,7 +3849,7 @@ def test_measurement_key_mapping_preserves_moments(circuit_cls): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_inverse(circuit_cls): a, b = cirq.LineQubit.range(2) - forward = circuit_cls((cirq.X ** 0.5)(a), (cirq.Y ** -0.2)(b), cirq.CZ(a, b)) + forward = circuit_cls((cirq.X**0.5)(a), (cirq.Y**-0.2)(b), cirq.CZ(a, b)) backward = circuit_cls((cirq.CZ ** (-1.0))(a, b), (cirq.X ** (-0.5))(a), (cirq.Y ** (0.2))(b)) cirq.testing.assert_same_circuits(cirq.inverse(forward), backward) @@ -3862,7 +3860,7 @@ def test_inverse(circuit_cls): cirq.inverse(no_inverse) # Default when there is no inverse for an op. - default = circuit_cls((cirq.X ** 0.5)(a), (cirq.Y ** -0.2)(b)) + default = circuit_cls((cirq.X**0.5)(a), (cirq.Y**-0.2)(b)) cirq.testing.assert_same_circuits(cirq.inverse(no_inverse, default), default) assert cirq.inverse(no_inverse, None) is None @@ -3870,7 +3868,7 @@ def test_inverse(circuit_cls): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_pow_valid_only_for_minus_1(circuit_cls): a, b = cirq.LineQubit.range(2) - forward = circuit_cls((cirq.X ** 0.5)(a), (cirq.Y ** -0.2)(b), cirq.CZ(a, b)) + forward = circuit_cls((cirq.X**0.5)(a), (cirq.Y**-0.2)(b), cirq.CZ(a, b)) backward = circuit_cls((cirq.CZ ** (-1.0))(a, b), (cirq.X ** (-0.5))(a), (cirq.Y ** (0.2))(b)) cirq.testing.assert_same_circuits(cirq.pow(forward, -1), backward) diff --git a/cirq-core/cirq/sim/mux.py b/cirq-core/cirq/sim/mux.py index 5319b882a78..f375dba580d 100644 --- a/cirq-core/cirq/sim/mux.py +++ b/cirq-core/cirq/sim/mux.py @@ -21,7 +21,7 @@ import numpy as np -from cirq import _compat, circuits, protocols, study, devices, ops, value +from cirq import circuits, protocols, study, devices, ops, value from cirq._doc import document from cirq.sim import sparse_simulator, density_matrix_simulator from cirq.sim.clifford import clifford_simulator @@ -99,6 +99,7 @@ def _to_circuit(program: 'cirq.CIRCUIT_LIKE') -> 'cirq.Circuit': result = circuits.Circuit(program) return cast('cirq.Circuit', result) + def final_state_vector( program: 'cirq.CIRCUIT_LIKE', *, diff --git a/cirq-core/cirq/sim/mux_test.py b/cirq-core/cirq/sim/mux_test.py index a96ba807375..f393602ed3f 100644 --- a/cirq-core/cirq/sim/mux_test.py +++ b/cirq-core/cirq/sim/mux_test.py @@ -214,9 +214,9 @@ def test_final_state_vector_ignore_terminal_measurement(): [0, 0, 0.5 + 0.5j, 0.5 - 0.5j], ) with pytest.raises(ValueError, match='is not unitary'): - cirq.final_state_vector( + _ = cirq.final_state_vector( [cirq.X(a), cirq.amplitude_damp(0.1).on(b), cirq.measure(a, b, key='m')], - ignore_terminal_measurements=True + ignore_terminal_measurements=True, ), diff --git a/cirq-core/cirq/transformers/measurement_transformers_test.py b/cirq-core/cirq/transformers/measurement_transformers_test.py index 9701c4da804..d95aa6dd8d8 100644 --- a/cirq-core/cirq/transformers/measurement_transformers_test.py +++ b/cirq-core/cirq/transformers/measurement_transformers_test.py @@ -359,10 +359,7 @@ def test_drop_terminal(): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit( cirq.CircuitOperation( - cirq.FrozenCircuit( - cirq.CX(q0, q1), - cirq.measure(q0, q1, key='a~b', invert_mask=[0, 1]), - ) + cirq.FrozenCircuit(cirq.CX(q0, q1), cirq.measure(q0, q1, key='a~b', invert_mask=[0, 1])) ) ) dropped = cirq.drop_terminal_measurements(circuit) @@ -378,10 +375,7 @@ def test_drop_terminal_nonterminal_error(): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit( cirq.CircuitOperation( - cirq.FrozenCircuit( - cirq.measure(q0, q1, key='a~b', invert_mask=[0, 1]), - cirq.CX(q0, q1), - ) + cirq.FrozenCircuit(cirq.measure(q0, q1, key='a~b', invert_mask=[0, 1]), cirq.CX(q0, q1)) ) ) with pytest.raises(ValueError, match='Circuit contains a non-terminal measurement'): diff --git a/dev_tools/profiling/benchmark_simulators.py b/dev_tools/profiling/benchmark_simulators.py index 7286d667351..4f58366f717 100644 --- a/dev_tools/profiling/benchmark_simulators.py +++ b/dev_tools/profiling/benchmark_simulators.py @@ -54,7 +54,8 @@ def simulate(sim_type: str, num_qubits: int, num_gates: int, run_repetitions: in circuit.append(cirq.measure(cirq.GridQubit(0, i), key=f"meas{i}.")) if sim_type == _UNITARY: - circuit.final_state_vector(initial_state=0) + # TODO: remove ignore_terminal_measurements in v0.16 + circuit.final_state_vector(initial_state=0, ignore_terminal_measurements=True) elif sim_type == _DENSITY: cirq.DensityMatrixSimulator().run(circuit, repetitions=run_repetitions) From 84c1720aaac95667f8234fc8511288e576532b96 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Wed, 20 Apr 2022 13:18:10 -0700 Subject: [PATCH 04/12] one day I will get these tests to work --- cirq-core/cirq/sim/mux_test.py | 10 ++++++---- dev_tools/profiling/benchmark_simulators.py | 3 +-- .../profiling/benchmark_simulators_test.py | 17 ++++++++++++----- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/cirq-core/cirq/sim/mux_test.py b/cirq-core/cirq/sim/mux_test.py index f393602ed3f..7509fc4e3f4 100644 --- a/cirq-core/cirq/sim/mux_test.py +++ b/cirq-core/cirq/sim/mux_test.py @@ -214,10 +214,12 @@ def test_final_state_vector_ignore_terminal_measurement(): [0, 0, 0.5 + 0.5j, 0.5 - 0.5j], ) with pytest.raises(ValueError, match='is not unitary'): - _ = cirq.final_state_vector( - [cirq.X(a), cirq.amplitude_damp(0.1).on(b), cirq.measure(a, b, key='m')], - ignore_terminal_measurements=True, - ), + _ = ( + cirq.final_state_vector( + [cirq.X(a), cirq.amplitude_damp(0.1).on(b), cirq.measure(a, b, key='m')], + ignore_terminal_measurements=True, + ), + ) @pytest.mark.parametrize('repetitions', (0, 1, 100)) diff --git a/dev_tools/profiling/benchmark_simulators.py b/dev_tools/profiling/benchmark_simulators.py index 4f58366f717..7286d667351 100644 --- a/dev_tools/profiling/benchmark_simulators.py +++ b/dev_tools/profiling/benchmark_simulators.py @@ -54,8 +54,7 @@ def simulate(sim_type: str, num_qubits: int, num_gates: int, run_repetitions: in circuit.append(cirq.measure(cirq.GridQubit(0, i), key=f"meas{i}.")) if sim_type == _UNITARY: - # TODO: remove ignore_terminal_measurements in v0.16 - circuit.final_state_vector(initial_state=0, ignore_terminal_measurements=True) + circuit.final_state_vector(initial_state=0) elif sim_type == _DENSITY: cirq.DensityMatrixSimulator().run(circuit, repetitions=run_repetitions) diff --git a/dev_tools/profiling/benchmark_simulators_test.py b/dev_tools/profiling/benchmark_simulators_test.py index c9710ecab85..1a68914bb53 100644 --- a/dev_tools/profiling/benchmark_simulators_test.py +++ b/dev_tools/profiling/benchmark_simulators_test.py @@ -13,13 +13,17 @@ # limitations under the License. """Tests for the simulator benchmarker.""" +import cirq from dev_tools.profiling import benchmark_simulators def test_unitary_simulator(): for num_qubits in (4, 10): for num_gates in (10, 20): - benchmark_simulators.simulate('unitary', num_qubits, num_gates) + with cirq.testing.assert_deprecated( + '`ignore_terminal_measurements` will default to False', deadline='v0.16' + ): + benchmark_simulators.simulate('unitary', num_qubits, num_gates) def test_density_matrix_simulator(): @@ -37,10 +41,13 @@ def test_args_have_defaults(): def test_main_loop(): # Keep test from taking a long time by lowering max qubits. args = '--max_num_qubits 5'.split() - benchmark_simulators.main( - **benchmark_simulators.parse_arguments(args), - setup='from dev_tools.profiling.benchmark_simulators import simulate', - ) + with cirq.testing.assert_deprecated( + '`ignore_terminal_measurements` will default to False', deadline='v0.16', count=20 + ): + benchmark_simulators.main( + **benchmark_simulators.parse_arguments(args), + setup='from dev_tools.profiling.benchmark_simulators import simulate', + ) def test_parse_args(): From c1d213712a3ceea97a89c51c5bb15a9ca0a10ff1 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 25 Apr 2022 09:44:28 -0700 Subject: [PATCH 05/12] default deep=True --- .../transformers/measurement_transformers.py | 33 +++++++++++++------ .../measurement_transformers_test.py | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/cirq-core/cirq/transformers/measurement_transformers.py b/cirq-core/cirq/transformers/measurement_transformers.py index 20d9fcef8db..58315018a5b 100644 --- a/cirq-core/cirq/transformers/measurement_transformers.py +++ b/cirq-core/cirq/transformers/measurement_transformers.py @@ -136,7 +136,9 @@ def defer(op: 'cirq.Operation', _) -> 'cirq.OP_TREE': @transformer_api.transformer def dephase_measurements( - circuit: 'cirq.AbstractCircuit', *, context: Optional['cirq.TransformerContext'] = None + circuit: 'cirq.AbstractCircuit', + *, + context: Optional['cirq.TransformerContext'] = transformer_api.TransformerContext(deep=True), ) -> 'cirq.Circuit': """Changes all measurements to a dephase operation. @@ -147,7 +149,8 @@ def dephase_measurements( Args: circuit: The circuit to transform. It will not be modified. context: `cirq.TransformerContext` storing common configurable options - for transformers. + for transformers. The default has `deep=True` to ensure + measurements at all levels are dephased. Returns: A copy of the circuit, with dephase operations in place of all measurements. @@ -170,13 +173,15 @@ def dephase(op: 'cirq.Operation', _) -> 'cirq.OP_TREE': ignored = () if context is None else context.tags_to_ignore return transformer_primitives.map_operations( - circuit, dephase, deep=True, tags_to_ignore=ignored + circuit, dephase, deep=context.deep if context else True, tags_to_ignore=ignored ).unfreeze() @transformer_api.transformer def drop_terminal_measurements( - circuit: 'cirq.AbstractCircuit', *, context: Optional['cirq.TransformerContext'] = None + circuit: 'cirq.AbstractCircuit', + *, + context: Optional['cirq.TransformerContext'] = transformer_api.TransformerContext(deep=True), ) -> 'cirq.Circuit': """Removes terminal measurements from a circuit. @@ -187,14 +192,25 @@ def drop_terminal_measurements( Args: circuit: The circuit to transform. It will not be modified. context: `cirq.TransformerContext` storing common configurable options - for transformers. + for transformers. The default has `deep=True`, as "terminal + measurements" is ill-defined without inspecting subcircuits; + passing a context with `deep=False` will return an error. Returns: A copy of the circuit, with identity or X gates in place of terminal measurements. Raises: - ValueError: if the circuit contains non-terminal measurements. + ValueError: if the circuit contains non-terminal measurements, or if + the provided context has`deep=False`. """ + if not context.deep: + raise ValueError( + 'Context has `deep=False`, but `deep=True` is required to drop terminal measurements.' + ) + + if not circuit.are_all_measurements_terminal(): + raise ValueError('Circuit contains a non-terminal measurement.') + def flip_inversion(op: 'cirq.Operation', _) -> 'cirq.OP_TREE': if isinstance(op.gate, ops.MeasurementGate): return [ @@ -202,10 +218,7 @@ def flip_inversion(op: 'cirq.Operation', _) -> 'cirq.OP_TREE': ] return op - if not circuit.are_all_measurements_terminal(): - raise ValueError('Circuit contains a non-terminal measurement.') - ignored = () if context is None else context.tags_to_ignore return transformer_primitives.map_operations( - circuit, flip_inversion, deep=True, tags_to_ignore=ignored + circuit, flip_inversion, deep=context.deep if context else True, tags_to_ignore=ignored ).unfreeze() diff --git a/cirq-core/cirq/transformers/measurement_transformers_test.py b/cirq-core/cirq/transformers/measurement_transformers_test.py index d95aa6dd8d8..ec346b9964f 100644 --- a/cirq-core/cirq/transformers/measurement_transformers_test.py +++ b/cirq-core/cirq/transformers/measurement_transformers_test.py @@ -338,7 +338,7 @@ def test_dephase_nocompile_context(): ) ) dephased = cirq.dephase_measurements( - circuit, context=cirq.TransformerContext(tags_to_ignore=('nocompile',)) + circuit, context=cirq.TransformerContext(deep=True, tags_to_ignore=('nocompile',)) ) cirq.testing.assert_same_circuits( dephased, From 2ceb8124d24cded97278f93165104680d72bc599 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 25 Apr 2022 10:10:47 -0700 Subject: [PATCH 06/12] None-checking --- cirq-core/cirq/transformers/measurement_transformers.py | 2 +- .../cirq/transformers/measurement_transformers_test.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/transformers/measurement_transformers.py b/cirq-core/cirq/transformers/measurement_transformers.py index 58315018a5b..144d8488f0c 100644 --- a/cirq-core/cirq/transformers/measurement_transformers.py +++ b/cirq-core/cirq/transformers/measurement_transformers.py @@ -203,7 +203,7 @@ def drop_terminal_measurements( the provided context has`deep=False`. """ - if not context.deep: + if context is None or not context.deep: raise ValueError( 'Context has `deep=False`, but `deep=True` is required to drop terminal measurements.' ) diff --git a/cirq-core/cirq/transformers/measurement_transformers_test.py b/cirq-core/cirq/transformers/measurement_transformers_test.py index ec346b9964f..4e85c1c71e7 100644 --- a/cirq-core/cirq/transformers/measurement_transformers_test.py +++ b/cirq-core/cirq/transformers/measurement_transformers_test.py @@ -380,3 +380,11 @@ def test_drop_terminal_nonterminal_error(): ) with pytest.raises(ValueError, match='Circuit contains a non-terminal measurement'): _ = cirq.drop_terminal_measurements(circuit) + + with pytest.raises(ValueError, match='Context has `deep=False`'): + _ = cirq.drop_terminal_measurements( + circuit, context=cirq.TransformerContext(deep=False) + ) + + with pytest.raises(ValueError, match='Context has `deep=False`'): + _ = cirq.drop_terminal_measurements(circuit, context=None) From d949760ab05e028931b3751215545f4c4a38fbb9 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 25 Apr 2022 11:09:46 -0700 Subject: [PATCH 07/12] Format --- cirq-core/cirq/transformers/measurement_transformers_test.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cirq-core/cirq/transformers/measurement_transformers_test.py b/cirq-core/cirq/transformers/measurement_transformers_test.py index 4e85c1c71e7..6b8dc1e4d08 100644 --- a/cirq-core/cirq/transformers/measurement_transformers_test.py +++ b/cirq-core/cirq/transformers/measurement_transformers_test.py @@ -382,9 +382,7 @@ def test_drop_terminal_nonterminal_error(): _ = cirq.drop_terminal_measurements(circuit) with pytest.raises(ValueError, match='Context has `deep=False`'): - _ = cirq.drop_terminal_measurements( - circuit, context=cirq.TransformerContext(deep=False) - ) + _ = cirq.drop_terminal_measurements(circuit, context=cirq.TransformerContext(deep=False)) with pytest.raises(ValueError, match='Context has `deep=False`'): _ = cirq.drop_terminal_measurements(circuit, context=None) From daf4fb5a22445bd8ae161e0ec2a780817b8ece7e Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Tue, 26 Apr 2022 09:04:54 -0700 Subject: [PATCH 08/12] Deprecate dtype=np.complex128 default --- cirq-core/cirq/circuits/circuit.py | 15 ++- cirq-core/cirq/circuits/circuit_test.py | 127 ++++++++++++------ cirq-core/cirq/ops/pauli_string_test.py | 24 +++- dev_tools/profiling/benchmark_simulators.py | 4 +- .../profiling/benchmark_simulators_test.py | 16 +-- docs/tutorials/educators/chemistry.ipynb | 8 +- docs/tutorials/fourier_checking.ipynb | 4 +- 7 files changed, 133 insertions(+), 65 deletions(-) diff --git a/cirq-core/cirq/circuits/circuit.py b/cirq-core/cirq/circuits/circuit.py index c56c5b5ce86..472c454a9f5 100644 --- a/cirq-core/cirq/circuits/circuit.py +++ b/cirq-core/cirq/circuits/circuit.py @@ -1060,8 +1060,8 @@ def final_state_vector( initial_state: 'cirq.STATE_VECTOR_LIKE' = 0, qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT, qubits_that_should_be_present: Iterable['cirq.Qid'] = (), - ignore_terminal_measurements: bool = True, - dtype: Type[np.number] = np.complex64, + ignore_terminal_measurements: Optional[bool] = None, + dtype: Optional[Type[np.number]] = None, param_resolver: 'cirq.ParamResolverOrSimilarType' = None, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> np.ndarray: @@ -1102,12 +1102,21 @@ def final_state_vector( ValueError: If the program doesn't have a well defined final state because it has non-unitary gates. """ - if ignore_terminal_measurements and self.has_measurements(): + if ignore_terminal_measurements is None and self.has_measurements(): _compat._warn_or_error( '`ignore_terminal_measurements` will default to False in v0.16. ' 'To drop terminal measurements, please explicitly include ' '`ignore_terminal_measurements=True` when calling this method.' ) + ignore_terminal_measurements = True + + if dtype is None: + _compat._warn_or_error( + '`dtype` will default to np.complex64 in v0.16. ' + 'To use the previous default, please explicitly include ' + '`dtype=np.complex128` when calling this method.' + ) + dtype = np.complex128 from cirq.sim.mux import final_state_vector diff --git a/cirq-core/cirq/circuits/circuit_test.py b/cirq-core/cirq/circuits/circuit_test.py index b1a9de7ba47..e987d8a7fe1 100644 --- a/cirq-core/cirq/circuits/circuit_test.py +++ b/cirq-core/cirq/circuits/circuit_test.py @@ -2779,17 +2779,23 @@ def test_final_state_vector(circuit_cls): # State ordering. cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(a) ** 0.5).final_state_vector(), + circuit_cls(cirq.X(a) ** 0.5).final_state_vector( + ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([1j, 1]) * np.sqrt(0.5), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(a) ** 0.5).final_state_vector(initial_state=0), + circuit_cls(cirq.X(a) ** 0.5).final_state_vector( + initial_state=0, ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([1j, 1]) * np.sqrt(0.5), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(a) ** 0.5).final_state_vector(initial_state=1), + circuit_cls(cirq.X(a) ** 0.5).final_state_vector( + initial_state=1, ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([1, 1j]) * np.sqrt(0.5), atol=1e-8, ) @@ -2797,7 +2803,9 @@ def test_final_state_vector(circuit_cls): # Vector state. cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.X(a) ** 0.5).final_state_vector( - initial_state=np.array([1j, 1]) * np.sqrt(0.5) + initial_state=np.array([1j, 1]) * np.sqrt(0.5), + ignore_terminal_measurements=False, + dtype=np.complex64, ), np.array([0, 1]), atol=1e-8, @@ -2805,22 +2813,30 @@ def test_final_state_vector(circuit_cls): # Qubit ordering. cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.CNOT(a, b)).final_state_vector(initial_state=0), + circuit_cls(cirq.CNOT(a, b)).final_state_vector( + initial_state=0, ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([1, 0, 0, 0]), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.CNOT(a, b)).final_state_vector(initial_state=1), + circuit_cls(cirq.CNOT(a, b)).final_state_vector( + initial_state=1, ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([0, 1, 0, 0]), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.CNOT(a, b)).final_state_vector(initial_state=2), + circuit_cls(cirq.CNOT(a, b)).final_state_vector( + initial_state=2, ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([0, 0, 0, 1]), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.CNOT(a, b)).final_state_vector(initial_state=3), + circuit_cls(cirq.CNOT(a, b)).final_state_vector( + initial_state=3, ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([0, 0, 1, 0]), atol=1e-8, ) @@ -2828,71 +2844,85 @@ def test_final_state_vector(circuit_cls): # Product state cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.CNOT(a, b)).final_state_vector( - initial_state=cirq.KET_ZERO(a) * cirq.KET_ZERO(b) + initial_state=cirq.KET_ZERO(a) * cirq.KET_ZERO(b), + ignore_terminal_measurements=False, + dtype=np.complex64, ), np.array([1, 0, 0, 0]), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.CNOT(a, b)).final_state_vector( - initial_state=cirq.KET_ZERO(a) * cirq.KET_ONE(b) + initial_state=cirq.KET_ZERO(a) * cirq.KET_ONE(b), + ignore_terminal_measurements=False, + dtype=np.complex64, ), np.array([0, 1, 0, 0]), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.CNOT(a, b)).final_state_vector( - initial_state=cirq.KET_ONE(a) * cirq.KET_ZERO(b) + initial_state=cirq.KET_ONE(a) * cirq.KET_ZERO(b), + ignore_terminal_measurements=False, + dtype=np.complex64, ), np.array([0, 0, 0, 1]), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.CNOT(a, b)).final_state_vector( - initial_state=cirq.KET_ONE(a) * cirq.KET_ONE(b) + initial_state=cirq.KET_ONE(a) * cirq.KET_ONE(b), + ignore_terminal_measurements=False, + dtype=np.complex64, ), np.array([0, 0, 1, 0]), atol=1e-8, ) # Measurements. - # TODO: remove deprecation assertions after v0.16, but keep tests. - # Warnings are unavoidable due to how they are triggered. - with cirq.testing.assert_deprecated("To drop terminal measurements", deadline="v0.16"): - cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.measure(a)).final_state_vector(ignore_terminal_measurements=True), - np.array([1, 0]), - atol=1e-8, - ) - with cirq.testing.assert_deprecated("To drop terminal measurements", deadline="v0.16"): - cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(a), cirq.measure(a)).final_state_vector( - ignore_terminal_measurements=True - ), - np.array([0, 1]), - atol=1e-8, - ) + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls(cirq.measure(a)).final_state_vector( + ignore_terminal_measurements=True, dtype=np.complex64 + ), + np.array([1, 0]), + atol=1e-8, + ) + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls(cirq.X(a), cirq.measure(a)).final_state_vector( + ignore_terminal_measurements=True, dtype=np.complex64 + ), + np.array([0, 1]), + atol=1e-8, + ) with pytest.raises(ValueError): cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.measure(a), cirq.X(a)).final_state_vector(), + circuit_cls(cirq.measure(a), cirq.X(a)).final_state_vector( + ignore_terminal_measurements=True, dtype=np.complex64 + ), np.array([1, 0]), atol=1e-8, ) with pytest.raises(ValueError): cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.measure(a)).final_state_vector(ignore_terminal_measurements=False), + circuit_cls(cirq.measure(a)).final_state_vector( + ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([1, 0]), atol=1e-8, ) # Qubit order. cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.Z(a), cirq.X(b)).final_state_vector(qubit_order=[a, b]), + circuit_cls(cirq.Z(a), cirq.X(b)).final_state_vector( + qubit_order=[a, b], ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([0, 1, 0, 0]), atol=1e-8, ) cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.Z(a), cirq.X(b)).final_state_vector(qubit_order=[b, a]), + circuit_cls(cirq.Z(a), cirq.X(b)).final_state_vector( + qubit_order=[b, a], ignore_terminal_measurements=False, dtype=np.complex64 + ), np.array([0, 0, 1, 0]), atol=1e-8, ) @@ -2904,7 +2934,9 @@ def test_final_state_vector(circuit_cls): for dt in dtypes: cirq.testing.assert_allclose_up_to_global_phase( circuit_cls(cirq.X(a) ** 0.5).final_state_vector( - initial_state=np.array([1j, 1]) * np.sqrt(0.5), dtype=dt + initial_state=np.array([1j, 1]) * np.sqrt(0.5), + ignore_terminal_measurements=False, + dtype=dt, ), np.array([0, 1]), atol=1e-8, @@ -2917,24 +2949,41 @@ def test_final_state_vector_deprecated_params(circuit_cls): b = cirq.NamedQubit('b') # Extra qubits. cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls().final_state_vector(), np.array([1]), atol=1e-8 + circuit_cls().final_state_vector(ignore_terminal_measurements=False, dtype=np.complex128), + np.array([1]), + atol=1e-8, ) with cirq.testing.assert_deprecated("Inject identity operators", deadline="v0.16"): cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls().final_state_vector(qubits_that_should_be_present=[a]), + circuit_cls().final_state_vector( + qubits_that_should_be_present=[a], + ignore_terminal_measurements=False, + dtype=np.complex128, + ), np.array([1, 0]), atol=1e-8, ) with cirq.testing.assert_deprecated("Inject identity operators", deadline="v0.16"): cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(b)).final_state_vector(qubits_that_should_be_present=[a]), + circuit_cls(cirq.X(b)).final_state_vector( + qubits_that_should_be_present=[a], + ignore_terminal_measurements=False, + dtype=np.complex128, + ), np.array([0, 1, 0, 0]), atol=1e-8, ) with cirq.testing.assert_deprecated("To drop terminal measurements", deadline="v0.16"): cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(a), cirq.measure(a)).final_state_vector(), + circuit_cls(cirq.X(a), cirq.measure(a)).final_state_vector(dtype=np.complex128), + np.array([0, 1]), + atol=1e-8, + ) + + with cirq.testing.assert_deprecated("`dtype` will default to np.complex64", deadline="v0.16"): + cirq.testing.assert_allclose_up_to_global_phase( + circuit_cls(cirq.X(a)).final_state_vector(ignore_terminal_measurements=False), np.array([0, 1]), atol=1e-8, ) @@ -2942,7 +2991,9 @@ def test_final_state_vector_deprecated_params(circuit_cls): # Non-keyword args. with cirq.testing.assert_deprecated("Only use keyword arguments", deadline="v0.16"): cirq.testing.assert_allclose_up_to_global_phase( - circuit_cls(cirq.X(a) ** 0.5).final_state_vector(1), + circuit_cls(cirq.X(a) ** 0.5).final_state_vector( + 1, ignore_terminal_measurements=False, dtype=np.complex128 + ), np.array([1, 1j]) * np.sqrt(0.5), atol=1e-8, ) diff --git a/cirq-core/cirq/ops/pauli_string_test.py b/cirq-core/cirq/ops/pauli_string_test.py index 7e727c4f9e2..d9001afbf00 100644 --- a/cirq-core/cirq/ops/pauli_string_test.py +++ b/cirq-core/cirq/ops/pauli_string_test.py @@ -584,7 +584,9 @@ def test_to_z_basis_ops(): circuit = cirq.Circuit(pauli_string.to_z_basis_ops()) initial_state = cirq.kron(x0, x1, y0, y1, z0, z1, shape_len=1) - z_basis_state = circuit.final_state_vector(initial_state=initial_state) + z_basis_state = circuit.final_state_vector( + initial_state=initial_state, ignore_terminal_measurements=False, dtype=np.complex64 + ) expected_state = np.zeros(2**6) expected_state[0b010101] = 1 @@ -609,7 +611,9 @@ def test_to_z_basis_ops_product_state(): * cirq.KET_ZERO(q4) * cirq.KET_ONE(q5) ) - z_basis_state = circuit.final_state_vector(initial_state=initial_state) + z_basis_state = circuit.final_state_vector( + initial_state=initial_state, ignore_terminal_measurements=False, dtype=np.complex64 + ) expected_state = np.zeros(2**6) expected_state[0b010101] = 1 @@ -1047,7 +1051,9 @@ def test_pauli_string_expectation_from_state_vector_pure_state(): circuit = cirq.Circuit( cirq.X(qubits[1]), cirq.H(qubits[2]), cirq.X(qubits[3]), cirq.H(qubits[3]) ) - wf = circuit.final_state_vector(qubit_order=qubits, dtype=np.complex128) + wf = circuit.final_state_vector( + qubit_order=qubits, ignore_terminal_measurements=False, dtype=np.complex128 + ) z0z1 = cirq.PauliString({qubits[0]: cirq.Z, qubits[1]: cirq.Z}) z0z2 = cirq.PauliString({qubits[0]: cirq.Z, qubits[2]: cirq.Z}) @@ -1072,7 +1078,9 @@ def test_pauli_string_expectation_from_state_vector_pure_state_with_coef(): q_map = {q: i for i, q in enumerate(qs)} circuit = cirq.Circuit(cirq.X(qs[1]), cirq.H(qs[2]), cirq.X(qs[3]), cirq.H(qs[3])) - wf = circuit.final_state_vector(qubit_order=qs, dtype=np.complex128) + wf = circuit.final_state_vector( + qubit_order=qs, ignore_terminal_measurements=False, dtype=np.complex128 + ) z0z1 = cirq.Z(qs[0]) * cirq.Z(qs[1]) * 0.123 z0z2 = cirq.Z(qs[0]) * cirq.Z(qs[2]) * -1 @@ -1256,7 +1264,9 @@ def test_pauli_string_expectation_from_density_matrix_pure_state(): circuit = cirq.Circuit( cirq.X(qubits[1]), cirq.H(qubits[2]), cirq.X(qubits[3]), cirq.H(qubits[3]) ) - state_vector = circuit.final_state_vector(qubit_order=qubits, dtype=np.complex128) + state_vector = circuit.final_state_vector( + qubit_order=qubits, ignore_terminal_measurements=False, dtype=np.complex128 + ) rho = np.outer(state_vector, np.conj(state_vector)) z0z1 = cirq.PauliString({qubits[0]: cirq.Z, qubits[1]: cirq.Z}) @@ -1282,7 +1292,9 @@ def test_pauli_string_expectation_from_density_matrix_pure_state_with_coef(): q_map = {q: i for i, q in enumerate(qs)} circuit = cirq.Circuit(cirq.X(qs[1]), cirq.H(qs[2]), cirq.X(qs[3]), cirq.H(qs[3])) - state_vector = circuit.final_state_vector(qubit_order=qs, dtype=np.complex128) + state_vector = circuit.final_state_vector( + qubit_order=qs, ignore_terminal_measurements=False, dtype=np.complex128 + ) rho = np.outer(state_vector, np.conj(state_vector)) z0z1 = cirq.Z(qs[0]) * cirq.Z(qs[1]) * 0.123 diff --git a/dev_tools/profiling/benchmark_simulators.py b/dev_tools/profiling/benchmark_simulators.py index 7286d667351..953c9af3590 100644 --- a/dev_tools/profiling/benchmark_simulators.py +++ b/dev_tools/profiling/benchmark_simulators.py @@ -54,7 +54,9 @@ def simulate(sim_type: str, num_qubits: int, num_gates: int, run_repetitions: in circuit.append(cirq.measure(cirq.GridQubit(0, i), key=f"meas{i}.")) if sim_type == _UNITARY: - circuit.final_state_vector(initial_state=0) + circuit.final_state_vector( + initial_state=0, ignore_terminal_measurements=True, dtype=np.complex64 + ) elif sim_type == _DENSITY: cirq.DensityMatrixSimulator().run(circuit, repetitions=run_repetitions) diff --git a/dev_tools/profiling/benchmark_simulators_test.py b/dev_tools/profiling/benchmark_simulators_test.py index 1a68914bb53..f4f0146ace1 100644 --- a/dev_tools/profiling/benchmark_simulators_test.py +++ b/dev_tools/profiling/benchmark_simulators_test.py @@ -20,10 +20,7 @@ def test_unitary_simulator(): for num_qubits in (4, 10): for num_gates in (10, 20): - with cirq.testing.assert_deprecated( - '`ignore_terminal_measurements` will default to False', deadline='v0.16' - ): - benchmark_simulators.simulate('unitary', num_qubits, num_gates) + benchmark_simulators.simulate('unitary', num_qubits, num_gates) def test_density_matrix_simulator(): @@ -41,13 +38,10 @@ def test_args_have_defaults(): def test_main_loop(): # Keep test from taking a long time by lowering max qubits. args = '--max_num_qubits 5'.split() - with cirq.testing.assert_deprecated( - '`ignore_terminal_measurements` will default to False', deadline='v0.16', count=20 - ): - benchmark_simulators.main( - **benchmark_simulators.parse_arguments(args), - setup='from dev_tools.profiling.benchmark_simulators import simulate', - ) + benchmark_simulators.main( + **benchmark_simulators.parse_arguments(args), + setup='from dev_tools.profiling.benchmark_simulators import simulate', + ) def test_parse_args(): diff --git a/docs/tutorials/educators/chemistry.ipynb b/docs/tutorials/educators/chemistry.ipynb index 1f080df43ca..fe6607b0d69 100644 --- a/docs/tutorials/educators/chemistry.ipynb +++ b/docs/tutorials/educators/chemistry.ipynb @@ -901,7 +901,7 @@ ")\n", "\n", "# Apply the circuit to the initial state\n", - "result = circuit.final_state_vector(initial_state=initial_state)\n", + "result = circuit.final_state_vector(initial_state=initial_state, ignore_terminal_measurements=False, dtype=np.complex64)\n", "\n", "# Compute the fidelity with the final state from exact evolution\n", "fidelity = abs(np.dot(exact_state, result.conj()))**2\n", @@ -2084,7 +2084,7 @@ ], "source": [ "# Apply the circuit with initial state having the first two modes occupied.\n", - "result = circuit.final_state_vector(initial_state=0b11000)\n", + "result = circuit.final_state_vector(initial_state=0b11000, ignore_terminal_measurements=False, dtype=np.complex64)\n", "\n", "# Compute the expectation value of the final state with the Hamiltonian\n", "quad_ham_sparse = of.get_sparse_operator(quad_ham)\n", @@ -2169,7 +2169,7 @@ "# ---------------------------------------------\n", "\n", "# Apply the circuit to the initial state\n", - "result = circuit.final_state_vector(initial_state=initial_state)\n", + "result = circuit.final_state_vector(initial_state=initial_state, ignore_terminal_measurements=False, dtype=np.complex64)\n", "\n", "# Compute the fidelity with the correct final state\n", "fidelity = abs(np.dot(final_state, result.conj()))**2\n", @@ -2223,7 +2223,7 @@ "# ---------------------------------------------\n", "\n", "# Apply the circuit to the initial state\n", - "result = circuit.final_state_vector(initial_state=initial_state)\n", + "result = circuit.final_state_vector(initial_state=initial_state, ignore_terminal_measurements=False, dtype=np.complex64)\n", "\n", "# Compute the fidelity with the correct final state\n", "fidelity = abs(np.dot(final_state, result.conj()))**2\n", diff --git a/docs/tutorials/fourier_checking.ipynb b/docs/tutorials/fourier_checking.ipynb index 4f4f4fe2774..07da65669c6 100644 --- a/docs/tutorials/fourier_checking.ipynb +++ b/docs/tutorials/fourier_checking.ipynb @@ -737,7 +737,7 @@ }, "outputs": [], "source": [ - "assert np.isclose(circuit.final_state_vector()[0], forrelation)\n", + "assert np.isclose(circuit.final_state_vector(ignore_terminal_measurements=False, dtype=np.complex64)[0], forrelation)\n", "\n", "s = cirq.Simulator()\n", "for step in s.simulate_moment_steps(circuit):\n", @@ -754,7 +754,7 @@ }, "outputs": [], "source": [ - "final_state = circuit.final_state_vector(ignore_terminal_measurements=True)\n", + "final_state = circuit.final_state_vector(ignore_terminal_measurements=True, dtype=np.complex64)\n", "plt.fill_between(np.arange(len(final_state)),\n", " np.abs(final_state)**2)\n", "plt.xlabel(\"State of qubits\")\n", From 9c2ec3f1d8c57e9c890880fb9a984ed9a907f150 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Tue, 26 Apr 2022 09:12:05 -0700 Subject: [PATCH 09/12] preformat --- cirq-core/cirq/circuits/circuit.py | 13 +++++++------ dev_tools/profiling/benchmark_simulators_test.py | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cirq-core/cirq/circuits/circuit.py b/cirq-core/cirq/circuits/circuit.py index 472c454a9f5..6bf455ee2ae 100644 --- a/cirq-core/cirq/circuits/circuit.py +++ b/cirq-core/cirq/circuits/circuit.py @@ -1102,12 +1102,13 @@ def final_state_vector( ValueError: If the program doesn't have a well defined final state because it has non-unitary gates. """ - if ignore_terminal_measurements is None and self.has_measurements(): - _compat._warn_or_error( - '`ignore_terminal_measurements` will default to False in v0.16. ' - 'To drop terminal measurements, please explicitly include ' - '`ignore_terminal_measurements=True` when calling this method.' - ) + if ignore_terminal_measurements is None: + if self.has_measurements(): + _compat._warn_or_error( + '`ignore_terminal_measurements` will default to False in v0.16. ' + 'To drop terminal measurements, please explicitly include ' + '`ignore_terminal_measurements=True` when calling this method.' + ) ignore_terminal_measurements = True if dtype is None: diff --git a/dev_tools/profiling/benchmark_simulators_test.py b/dev_tools/profiling/benchmark_simulators_test.py index f4f0146ace1..c9710ecab85 100644 --- a/dev_tools/profiling/benchmark_simulators_test.py +++ b/dev_tools/profiling/benchmark_simulators_test.py @@ -13,7 +13,6 @@ # limitations under the License. """Tests for the simulator benchmarker.""" -import cirq from dev_tools.profiling import benchmark_simulators From e241f1c8a60e2fb44e873b85ba68ac6c165e310e Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Tue, 26 Apr 2022 09:43:35 -0700 Subject: [PATCH 10/12] More fsv calls --- cirq-core/cirq/sim/sparse_simulator_test.py | 4 +++- .../two_qubit_state_preparation.py | 8 ++++++-- .../two_qubit_state_preparation_test.py | 8 ++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/sim/sparse_simulator_test.py b/cirq-core/cirq/sim/sparse_simulator_test.py index 0a27e775f30..b25892e5b5a 100644 --- a/cirq-core/cirq/sim/sparse_simulator_test.py +++ b/cirq-core/cirq/sim/sparse_simulator_test.py @@ -774,7 +774,9 @@ def _decompose_(self, qubits): def test_simulates_composite(): c = cirq.Circuit(MultiHTestGate().on(*cirq.LineQubit.range(2))) expected = np.array([0.5] * 4) - np.testing.assert_allclose(c.final_state_vector(), expected) + np.testing.assert_allclose( + c.final_state_vector(ignore_terminal_measurements=False, dtype=np.complex64), expected + ) np.testing.assert_allclose(cirq.Simulator().simulate(c).state_vector(), expected) diff --git a/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py b/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py index 3e3ded680e8..03fba8f860e 100644 --- a/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py +++ b/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py @@ -66,7 +66,9 @@ def prepare_two_qubit_state_using_sqrt_iswap( alpha = np.arccos(np.sqrt(np.clip(1 - s[0] * 2 * s[1], 0, 1))) sqrt_iswap_gate = ops.SQRT_ISWAP_INV if use_sqrt_iswap_inv else ops.SQRT_ISWAP op_list = [ops.ry(2 * alpha).on(q0), sqrt_iswap_gate.on(q0, q1)] - intermediate_state = circuits.Circuit(op_list).final_state_vector() + intermediate_state = circuits.Circuit(op_list).final_state_vector( + ignore_terminal_measurements=False, dtype=np.complex64 + ) u_iSWAP, _, vh_iSWAP = np.linalg.svd(intermediate_state.reshape(2, 2)) return op_list + _1q_matrices_to_ops( np.dot(u, np.linalg.inv(u_iSWAP)), np.dot(vh.T, np.linalg.inv(vh_iSWAP.T)), q0, q1 @@ -97,7 +99,9 @@ def prepare_two_qubit_state_using_cz( return _1q_matrices_to_ops(u, vh.T, q0, q1, True) alpha = np.arccos(np.clip(s[0], 0, 1)) op_list = [ops.ry(2 * alpha).on(q0), ops.H.on(q1), ops.CZ.on(q0, q1)] - intermediate_state = circuits.Circuit(op_list).final_state_vector() + intermediate_state = circuits.Circuit(op_list).final_state_vector( + ignore_terminal_measurements=False, dtype=np.complex64 + ) u_CZ, _, vh_CZ = np.linalg.svd(intermediate_state.reshape(2, 2)) return op_list + _1q_matrices_to_ops( np.dot(u, np.linalg.inv(u_CZ)), np.dot(vh.T, np.linalg.inv(vh_CZ.T)), q0, q1 diff --git a/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py b/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py index b4b59f2383c..a0f8fb54837 100644 --- a/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py +++ b/cirq-core/cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py @@ -66,7 +66,9 @@ def test_prepare_two_qubit_state_using_cz(state): ops_2q = [*circuit.findall_operations(lambda op: cirq.num_qubits(op) > 1)] assert ops_cz == ops_2q assert len(ops_cz) <= 1 - assert cirq.allclose_up_to_global_phase(circuit.final_state_vector(), state) + assert cirq.allclose_up_to_global_phase( + circuit.final_state_vector(ignore_terminal_measurements=False, dtype=np.complex64), state + ) @pytest.mark.parametrize("state", STATES_TO_PREPARE) @@ -84,4 +86,6 @@ def test_prepare_two_qubit_state_using_sqrt_iswap(state, use_sqrt_iswap_inv): ops_2q = [*circuit.findall_operations(lambda op: cirq.num_qubits(op) > 1)] assert ops_iswap == ops_2q assert len(ops_iswap) <= 1 - assert cirq.allclose_up_to_global_phase(circuit.final_state_vector(), state) + assert cirq.allclose_up_to_global_phase( + circuit.final_state_vector(ignore_terminal_measurements=False, dtype=np.complex64), state + ) From ddfe34e31674a43a12a03d02fee78e2568466625 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Tue, 26 Apr 2022 14:15:30 -0700 Subject: [PATCH 11/12] Fourier ignore measure --- docs/tutorials/fourier_checking.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/fourier_checking.ipynb b/docs/tutorials/fourier_checking.ipynb index 07da65669c6..e82cfdf229b 100644 --- a/docs/tutorials/fourier_checking.ipynb +++ b/docs/tutorials/fourier_checking.ipynb @@ -737,7 +737,7 @@ }, "outputs": [], "source": [ - "assert np.isclose(circuit.final_state_vector(ignore_terminal_measurements=False, dtype=np.complex64)[0], forrelation)\n", + "assert np.isclose(circuit.final_state_vector(ignore_terminal_measurements=True, dtype=np.complex64)[0], forrelation)\n", "\n", "s = cirq.Simulator()\n", "for step in s.simulate_moment_steps(circuit):\n", From 4e0305932e52a0b137ebd8439443bbf90daf14fa Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Wed, 27 Apr 2022 14:51:03 -0700 Subject: [PATCH 12/12] the the the --- cirq-core/cirq/circuits/circuit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/circuits/circuit.py b/cirq-core/cirq/circuits/circuit.py index 6bf455ee2ae..b0fb9b3f7cc 100644 --- a/cirq-core/cirq/circuits/circuit.py +++ b/cirq-core/cirq/circuits/circuit.py @@ -1093,7 +1093,7 @@ def final_state_vector( Returns: The state vector resulting from applying the given unitary operations to the desired initial state. Specifically, a numpy - array containing the the amplitudes in np.kron order, where the + array containing the amplitudes in np.kron order, where the order of arguments to kron is determined by the qubit order argument (which defaults to just sorting the qubits that are present into an ascending order).