diff --git a/cirq/__init__.py b/cirq/__init__.py index 5029d83f2f4..f847f42c443 100644 --- a/cirq/__init__.py +++ b/cirq/__init__.py @@ -133,7 +133,6 @@ kak_decomposition, kak_vector, KakDecomposition, - subwavefunction, kron, kron_bases, kron_factor_4x4_to_2x2s, @@ -142,12 +141,15 @@ match_global_phase, matrix_from_basis_coefficients, partial_trace, + partial_trace_of_state_vector_as_mixture, PAULI_BASIS, scatter_plot_normalized_kak_interaction_coefficients, pow_pauli_combination, reflection_matrix_pow, slice_for_qubits_equal_to, so4_to_magic_su2s, + subwavefunction, + sub_state_vector, targeted_conjugate_about, targeted_left_multiply, unitary_eig, @@ -312,6 +314,8 @@ to_valid_density_matrix, to_valid_state_vector, validate_normalized_state, + validate_normalized_state_vector, + validate_qid_shape, von_neumann_entropy, ) @@ -331,6 +335,7 @@ measure_density_matrix, measure_state_vector, final_density_matrix, + final_state_vector, final_wavefunction, sample, sample_density_matrix, @@ -339,12 +344,16 @@ SimulatesAmplitudes, SimulatesFinalState, SimulatesIntermediateState, + SimulatesIntermediateStateVector, SimulatesIntermediateWaveFunction, SimulatesSamples, SimulationTrialResult, Simulator, SparseSimulatorStep, StateVectorMixin, + StateVectorSimulatorState, + StateVectorStepResult, + StateVectorTrialResult, StepResult, WaveFunctionSimulatorState, WaveFunctionStepResult, diff --git a/cirq/circuits/circuit.py b/cirq/circuits/circuit.py index e73826504b3..8c32611a15c 100644 --- a/cirq/circuits/circuit.py +++ b/cirq/circuits/circuit.py @@ -37,6 +37,7 @@ from cirq.circuits.qasm_output import QasmOutput from cirq.circuits.quil_output import QuilOutput from cirq.type_workarounds import NotImplementedType +from cirq._compat import deprecated import cirq._version if TYPE_CHECKING: @@ -64,7 +65,7 @@ class Circuit: are_all_matches_terminal are_all_measurements_terminal unitary - final_wavefunction + final_state_vector to_text_diagram to_text_diagram_drawer @@ -1521,7 +1522,7 @@ def unitary(self, result = _apply_unitary_circuit(self, state, qs, dtype) return result.reshape((side_len, side_len)) - def final_wavefunction( + def final_state_vector( self, initial_state: 'cirq.STATE_VECTOR_LIKE' = 0, qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT, @@ -1602,6 +1603,22 @@ def final_wavefunction( result = _apply_unitary_circuit(self, state, qs, dtype) return result.reshape((state_len,)) + @deprecated(deadline='v0.10.0', fix='Use final_state_vector instead.') + def final_wavefunction( + self, + 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) -> np.ndarray: + """Deprecated. Please use `final_state_vector`.""" + return self.final_state_vector( + initial_state=initial_state, + qubit_order=qubit_order, + qubits_that_should_be_present=qubits_that_should_be_present, + ignore_terminal_measurements=ignore_terminal_measurements, + dtype=dtype) + def to_text_diagram( self, *, diff --git a/cirq/circuits/circuit_test.py b/cirq/circuits/circuit_test.py index 854ba6fbd38..f39890a24d2 100644 --- a/cirq/circuits/circuit_test.py +++ b/cirq/circuits/circuit_test.py @@ -23,6 +23,7 @@ import cirq import cirq.google as cg +import cirq.testing from cirq import ops @@ -2373,83 +2374,83 @@ def test_apply_unitary_effect_to_state(): # State ordering. cirq.testing.assert_allclose_up_to_global_phase( - cirq.Circuit(cirq.X(a)**0.5).final_wavefunction(), + cirq.Circuit(cirq.X(a)**0.5).final_state_vector(), np.array([1j, 1]) * np.sqrt(0.5), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase( - cirq.Circuit(cirq.X(a)**0.5).final_wavefunction(initial_state=0), + cirq.Circuit(cirq.X(a)**0.5).final_state_vector(initial_state=0), np.array([1j, 1]) * np.sqrt(0.5), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase( - cirq.Circuit(cirq.X(a)**0.5).final_wavefunction(initial_state=1), + cirq.Circuit(cirq.X(a)**0.5).final_state_vector(initial_state=1), np.array([1, 1j]) * np.sqrt(0.5), atol=1e-8) # Vector state. cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit( - cirq.X(a)**0.5).final_wavefunction(initial_state=np.array([1j, 1]) * + cirq.X(a)**0.5).final_state_vector(initial_state=np.array([1j, 1]) * np.sqrt(0.5)), np.array([0, 1]), atol=1e-8) # Qubit ordering. cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit(cirq.CNOT( - a, b)).final_wavefunction(initial_state=0), + a, b)).final_state_vector(initial_state=0), np.array([1, 0, 0, 0]), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit(cirq.CNOT( - a, b)).final_wavefunction(initial_state=1), + a, b)).final_state_vector(initial_state=1), np.array([0, 1, 0, 0]), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit(cirq.CNOT( - a, b)).final_wavefunction(initial_state=2), + a, b)).final_state_vector(initial_state=2), np.array([0, 0, 0, 1]), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit(cirq.CNOT( - a, b)).final_wavefunction(initial_state=3), + a, b)).final_state_vector(initial_state=3), np.array([0, 0, 1, 0]), atol=1e-8) # Measurements. cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit( - cirq.measure(a)).final_wavefunction(), + cirq.measure(a)).final_state_vector(), np.array([1, 0]), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit( - cirq.X(a), cirq.measure(a)).final_wavefunction(), + cirq.X(a), cirq.measure(a)).final_state_vector(), np.array([0, 1]), atol=1e-8) with pytest.raises(ValueError): cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit( - cirq.measure(a), cirq.X(a)).final_wavefunction(), + cirq.measure(a), cirq.X(a)).final_state_vector(), np.array([1, 0]), atol=1e-8) with pytest.raises(ValueError): cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit( - cirq.measure(a)).final_wavefunction( + cirq.measure(a)).final_state_vector( ignore_terminal_measurements=False), np.array([1, 0]), atol=1e-8) # Extra qubits. cirq.testing.assert_allclose_up_to_global_phase( - cirq.Circuit().final_wavefunction(), np.array([1]), atol=1e-8) + cirq.Circuit().final_state_vector(), np.array([1]), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase( - cirq.Circuit().final_wavefunction(qubits_that_should_be_present=[a]), + cirq.Circuit().final_state_vector(qubits_that_should_be_present=[a]), np.array([1, 0]), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit( - cirq.X(b)).final_wavefunction(qubits_that_should_be_present=[a]), + 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(cirq.Circuit( - cirq.Z(a), cirq.X(b)).final_wavefunction(qubit_order=[a, b]), + cirq.Z(a), cirq.X(b)).final_state_vector(qubit_order=[a, b]), np.array([0, 1, 0, 0]), atol=1e-8) cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit( - cirq.Z(a), cirq.X(b)).final_wavefunction(qubit_order=[b, a]), + cirq.Z(a), cirq.X(b)).final_state_vector(qubit_order=[b, a]), np.array([0, 0, 1, 0]), atol=1e-8) @@ -2459,7 +2460,7 @@ def test_apply_unitary_effect_to_state(): dtypes.append(np.complex256) for dt in dtypes: cirq.testing.assert_allclose_up_to_global_phase(cirq.Circuit( - cirq.X(a)**0.5).final_wavefunction(initial_state=np.array([1j, 1]) * + cirq.X(a)**0.5).final_state_vector(initial_state=np.array([1j, 1]) * np.sqrt(0.5), dtype=dt), np.array([0, 1]), @@ -3756,3 +3757,10 @@ def _measurement_key_(self): cirq.measure(b, key='y'), cirq.measure(a, key='x'), ])).all_measurement_keys() == ('y', 'x') + + +def test_deprecated(): + q = cirq.NamedQubit('q') + circuit = cirq.Circuit([cirq.H(q)]) + with cirq.testing.assert_logs('final_state_vector', 'deprecated'): + _ = circuit.final_wavefunction() diff --git a/cirq/contrib/quantum_volume/quantum_volume.py b/cirq/contrib/quantum_volume/quantum_volume.py index 38a75b4277c..3442bf9c902 100644 --- a/cirq/contrib/quantum_volume/quantum_volume.py +++ b/cirq/contrib/quantum_volume/quantum_volume.py @@ -75,7 +75,7 @@ def compute_heavy_set(circuit: cirq.Circuit) -> List[int]: # Classically compute the probabilities of each output bit-string through # simulation. simulator = cirq.Simulator() - results = cast(cirq.WaveFunctionTrialResult, + results = cast(cirq.StateVectorTrialResult, simulator.simulate(program=circuit)) # Compute the median probability of the output bit-strings. Note that heavy diff --git a/cirq/experiments/cross_entropy_benchmarking.py b/cirq/experiments/cross_entropy_benchmarking.py index ddc5ed4c213..77f29ca4443 100644 --- a/cirq/experiments/cross_entropy_benchmarking.py +++ b/cirq/experiments/cross_entropy_benchmarking.py @@ -274,13 +274,13 @@ def cross_entropy_benchmarking( circuits_k) # Simulate each circuit with the Cirq simulator to obtain the - # wavefunction at the end of each circuit, from which the + # state vector at the end of each circuit, from which the # theoretically expected bit-string probabilities are obtained. probs_exp_k = [] # type: List[np.ndarray] for circ_k in circuits_k: res = simulator.simulate(circ_k, qubit_order=qubits) - state_probs = np.abs(np.asarray(res.final_state) # type: ignore - )**2 + state_probs = np.abs(np.asarray( + res.final_state_vector))**2 # type: ignore probs_exp_k.append(state_probs) for i, num_cycle in enumerate(cycle_range): diff --git a/cirq/experiments/fidelity_estimation.py b/cirq/experiments/fidelity_estimation.py index 90a721a8b1b..b7033865182 100644 --- a/cirq/experiments/fidelity_estimation.py +++ b/cirq/experiments/fidelity_estimation.py @@ -19,7 +19,7 @@ from cirq.circuits import Circuit from cirq.ops import QubitOrder, QubitOrderOrList -from cirq.sim import final_wavefunction +from cirq.sim import final_state_vector def linear_xeb_fidelity_from_probabilities( @@ -165,7 +165,7 @@ def xeb_fidelity( each circuit execution as integer array where each integer is formed from measured qubit values according to `qubit_order` from most to least significant qubit, i.e. in the order consistent with - `cirq.final_wavefunction`. + `cirq.final_state_vector`. qubit_order: Qubit order used to construct bitstrings enumerating qubits starting with the most sigificant qubit. amplitudes: Optional mapping from bitstring to output amplitude. @@ -192,7 +192,7 @@ def xeb_fidelity( f'on {len(circuit.qid_shape())} qubits.') if amplitudes is None: - output_state = final_wavefunction(circuit, qubit_order=qubit_order) + output_state = final_state_vector(circuit, qubit_order=qubit_order) output_probabilities = np.abs(output_state)**2 bitstring_probabilities = output_probabilities[bitstrings] else: diff --git a/cirq/experiments/fidelity_estimation_test.py b/cirq/experiments/fidelity_estimation_test.py index 6a858e918f6..584262e2247 100644 --- a/cirq/experiments/fidelity_estimation_test.py +++ b/cirq/experiments/fidelity_estimation_test.py @@ -78,7 +78,7 @@ def test_xeb_fidelity(depolarization, estimator): repetitions=5000) f = cirq.xeb_fidelity(circuit, bitstrings, qubits, estimator=estimator) - amplitudes = cirq.final_wavefunction(circuit) + amplitudes = cirq.final_state_vector(circuit) f2 = cirq.xeb_fidelity(circuit, bitstrings, qubits, diff --git a/cirq/interop/quirk/cells/arithmetic_cells_test.py b/cirq/interop/quirk/cells/arithmetic_cells_test.py index 5d954f9444b..566a195b1d4 100644 --- a/cirq/interop/quirk/cells/arithmetic_cells_test.py +++ b/cirq/interop/quirk/cells/arithmetic_cells_test.py @@ -495,7 +495,7 @@ def test_with_registers(): atol=1e-8) op2 = op.with_registers([*cirq.LineQubit.range(3)], 5, 5) - np.testing.assert_allclose(cirq.final_wavefunction(cirq.Circuit(op2), + np.testing.assert_allclose(cirq.final_state_vector(cirq.Circuit(op2), initial_state=0), cirq.one_hot(index=25 % 8, shape=8, diff --git a/cirq/interop/quirk/cells/testing.py b/cirq/interop/quirk/cells/testing.py index 4c981ff93e0..2afd10b8c94 100644 --- a/cirq/interop/quirk/cells/testing.py +++ b/cirq/interop/quirk/cells/testing.py @@ -67,7 +67,7 @@ def assert_url_to_circuit_returns( ]) np.testing.assert_allclose( - cirq.final_wavefunction( + cirq.final_state_vector( parsed, # Match Quirk's endian-ness for comparison purposes. qubit_order=sorted(parsed.all_qubits(), reverse=True), @@ -97,7 +97,7 @@ def _sparse_computational_basis_map(inputs: Sequence[int], input_state = np.zeros(1 << len(circuit.all_qubits()), dtype=np.complex128) for k, amp in zip(inputs, amps): input_state[k] = amp - output_state = cirq.final_wavefunction(circuit, initial_state=input_state) + output_state = cirq.final_state_vector(circuit, initial_state=input_state) # Find where each amplitude went. actual_map = {} diff --git a/cirq/linalg/__init__.py b/cirq/linalg/__init__.py index 3d40441027b..54c0cd31bcc 100644 --- a/cirq/linalg/__init__.py +++ b/cirq/linalg/__init__.py @@ -81,10 +81,12 @@ from cirq.linalg.transformations import ( apply_matrix_to_slices, - subwavefunction, match_global_phase, partial_trace, + partial_trace_of_state_vector_as_mixture, reflection_matrix_pow, + subwavefunction, + sub_state_vector, targeted_conjugate_about, targeted_left_multiply, wavefunction_partial_trace_as_mixture, diff --git a/cirq/linalg/transformations.py b/cirq/linalg/transformations.py index b3498dbbc8a..6c8b60d9b31 100644 --- a/cirq/linalg/transformations.py +++ b/cirq/linalg/transformations.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Utility methods for transforming matrices.""" +"""Utility methods for transforming matrices or vectors.""" from typing import Tuple, Optional, Sequence, List, Union, TypeVar @@ -20,8 +20,9 @@ from cirq import protocols from cirq.linalg import predicates +from cirq._compat import deprecated, deprecated_parameter -# This is a special indicator value used by the subwavefunction method to +# This is a special indicator value used by the `sub_state_vector` method to # determine whether or not the caller provided a 'default' argument. It must be # of type np.ndarray to ensure the method has the correct type signature in that # case. It is checked for using `is`, so it won't have a false positive if the @@ -317,53 +318,65 @@ def partial_trace(tensor: np.ndarray, return np.einsum(tensor, left_indices + right_indices) -def wavefunction_partial_trace_as_mixture( - wavefunction: np.ndarray, +@deprecated_parameter(deadline='v0.10.0', + fix='Use state_vector instead.', + parameter_desc='wavefunction', + match=lambda args, kwargs: 'wavefunction' in kwargs, + rewrite=lambda args, kwargs: + (args, {('state_vector' if k == 'wavefunction' else k): v + for k, v in kwargs.items()})) +def partial_trace_of_state_vector_as_mixture( + state_vector: np.ndarray, keep_indices: List[int], *, atol: Union[int, float] = 1e-8) -> Tuple[Tuple[float, np.ndarray], ...]: - """Returns a mixture representing a wavefunction with only some qubits kept. + """Returns a mixture representing a state vector with only some qubits kept. - The input wavefunction must have shape `(2,) * n` or `(2 ** n)` where - `wavefunction` is expressed over n qubits. States in the output mixture will - retain the same type of shape as the input wavefunction, either `(2 ** k)` + The input state vector must have shape `(2,) * n` or `(2 ** n)` where + `state_vector` is expressed over n qubits. States in the output mixture will + retain the same type of shape as the input state vector, either `(2 ** k)` or `(2,) * k` where k is the number of qubits kept. - If the wavefunction cannot be factored into a pure state over `keep_indices` + If the state vector cannot be factored into a pure state over `keep_indices` then eigendecomposition is used and the output mixture will not be unique. Args: - wavefunction: A wavefunction to express over a qubit subset. - keep_indices: Which indices to express the wavefunction on. + state_vector: The state vector to take the partial trace over. + keep_indices: Which indices to take the partial trace of the + state_vector on. atol: The tolerance for determining that a factored state is pure. Returns: - A single-component mixture in which the factored wavefunction has - probability '1' if the factored state is pure, or else a mixture of the - default eigendecomposition of the mixed state's partial trace. + A single-component mixture in which the factored state vector has + probability '1' if the partially traced state is pure, or else a + mixture of the default eigendecomposition of the mixed state's + partial trace. Raises: - ValueError: if the input wavefunction is not an array of length + ValueError: if the input `state_vector` is not an array of length `(2 ** n)` or a tensor with a shape of `(2,) * n` """ # Attempt to do efficient state factoring. - state = subwavefunction(wavefunction, keep_indices, default=None, atol=atol) + state = sub_state_vector(state_vector, + keep_indices, + default=None, + atol=atol) if state is not None: return ((1.0, state),) # Fall back to a (non-unique) mixture representation. keep_dims = 1 << len(keep_indices) ret_shape: Union[Tuple[int], Tuple[int, ...]] - if wavefunction.shape == (wavefunction.size,): + if state_vector.shape == (state_vector.size,): ret_shape = (keep_dims,) - elif all(e == 2 for e in wavefunction.shape): + elif all(e == 2 for e in state_vector.shape): ret_shape = tuple(2 for _ in range(len(keep_indices))) rho = np.kron( - np.conj(wavefunction.reshape(-1, 1)).T, - wavefunction.reshape(-1, 1)).reshape( - (2, 2) * int(np.log2(wavefunction.size))) + np.conj(state_vector.reshape(-1, 1)).T, + state_vector.reshape(-1, 1)).reshape( + (2, 2) * int(np.log2(state_vector.size))) keep_rho = partial_trace(rho, keep_indices).reshape((keep_dims,) * 2) eigvals, eigvecs = np.linalg.eigh(keep_rho) mixture = tuple(zip(eigvals, [vec.reshape(ret_shape) for vec in eigvecs.T])) @@ -372,41 +385,52 @@ def wavefunction_partial_trace_as_mixture( if not protocols.approx_eq(p[0], 0.0)]) -def subwavefunction(wavefunction: np.ndarray, - keep_indices: List[int], - *, - default: TDefault = RaiseValueErrorIfNotProvided, - atol: Union[int, float] = 1e-8) -> np.ndarray: - r"""Attempts to factor a wavefunction into two parts and return one of them. - - The input wavefunction must have shape ``(2,) * n`` or ``(2 ** n)`` where - `wavefunction` is expressed over n qubits. The returned array will retain - the same type of shape as the input wavefunction, either ``(2 ** k)`` or +wavefunction_partial_trace_as_mixture = deprecated( + deadline='v0.10.0', + fix='Use `cirq.partial_trace_of_state_vector_as_mixture` instead.')( + partial_trace_of_state_vector_as_mixture) + + +@deprecated_parameter(deadline='v0.10.0', + fix='Use state_vector instead.', + parameter_desc='wavefunction', + match=lambda args, kwargs: 'wavefunction' in kwargs, + rewrite=lambda args, kwargs: + (args, {('state_vector' if k == 'wavefunction' else k): v + for k, v in kwargs.items()})) +def sub_state_vector(state_vector: np.ndarray, + keep_indices: List[int], + *, + default: TDefault = RaiseValueErrorIfNotProvided, + atol: Union[int, float] = 1e-8) -> np.ndarray: + r"""Attempts to factor a state vector into two parts and return one of them. + + The input `state_vector` must have shape ``(2,) * n`` or ``(2 ** n)`` where + `state_vector` is expressed over n qubits. The returned array will retain + the same type of shape as the input state vector, either ``(2 ** k)`` or ``(2,) * k`` where k is the number of qubits kept. - If a wavefunction $|\psi\rangle$ defined on n qubits is an outer product + If a state vector $|\psi\rangle$ defined on n qubits is an outer product of kets like $|\psi\rangle$ = $|x\rangle \otimes |y\rangle$, and $|x\rangle$ is defined over the subset ``keep_indices`` of k qubits, then this method will factor $|\psi\rangle$ into $|x\rangle$ and $|y\rangle$ and return $|x\rangle$. Note that $|x\rangle$ is not unique, because scalar - multiplication may be absorbed by any factor of a tensor product: - - $$ - e^{i \theta} |y\rangle \otimes |x\rangle - = |y\rangle \otimes e^{i \theta} |x\rangle - $$ + multiplication may be absorbed by any factor of a tensor product, + $e^{i \theta} |y\rangle \otimes |x\rangle = + |y\rangle \otimes e^{i \theta} |x\rangle$ This method randomizes the global phase of $|x\rangle$ in order to avoid accidental reliance on the global phase being some specific value. - If the provided wavefunction cannot be factored into a pure state over + If the provided `state_vector` cannot be factored into a pure state over `keep_indices`, the method will fall back to return `default`. If `default` is not provided, the method will fail and raise `ValueError`. Args: - wavefunction: A wavefunction to express over a qubit subset. - keep_indices: Which indices to express the wavefunction on. - default: Determines the fallback behavior when `wavefunction` doesn't + state_vector: The target state_vector. + keep_indices: Which indices to attempt to get the separable part of the + `state_vector` on. + default: Determines the fallback behavior when `state_vector` doesn't have a pure state factorization. If the factored state is not pure and `default` is not set, a ValueError is raised. If default is set to a value, that value is returned. @@ -414,43 +438,43 @@ def subwavefunction(wavefunction: np.ndarray, measure to 1. Returns: - The wavefunction expressed over the desired subset of qubits. + The state vector expressed over the desired subset of qubits. Raises: - ValueError: if the wavefunction is not of the correct shape or the - indices are not a valid subset of the input wavefunction's indices, or + ValueError: if the `state_vector` is not of the correct shape or the + indices are not a valid subset of the input `state_vector`'s indices, or the result of factoring is not a pure state. """ - if not np.log2(wavefunction.size).is_integer(): - raise ValueError("Input wavefunction of size {} does not represent a " - "state over qubits.".format(wavefunction.size)) + if not np.log2(state_vector.size).is_integer(): + raise ValueError("Input state_vector of size {} does not represent a " + "state over qubits.".format(state_vector.size)) - n_qubits = int(np.log2(wavefunction.size)) + n_qubits = int(np.log2(state_vector.size)) keep_dims = 1 << len(keep_indices) ret_shape: Union[Tuple[int], Tuple[int, ...]] - if wavefunction.shape == (wavefunction.size,): + if state_vector.shape == (state_vector.size,): ret_shape = (keep_dims,) - wavefunction = wavefunction.reshape((2,) * n_qubits) - elif wavefunction.shape == (2,) * n_qubits: + state_vector = state_vector.reshape((2,) * n_qubits) + elif state_vector.shape == (2,) * n_qubits: ret_shape = tuple(2 for _ in range(len(keep_indices))) else: raise ValueError( - "Input wavefunction must be shaped like (2 ** n,) or (2,) * n") + "Input state_vector must be shaped like (2 ** n,) or (2,) * n") keep_dims = 1 << len(keep_indices) - if not np.isclose(np.linalg.norm(wavefunction), 1): + if not np.isclose(np.linalg.norm(state_vector), 1): raise ValueError("Input state must be normalized.") if len(set(keep_indices)) != len(keep_indices): raise ValueError( "keep_indices were {} but must be unique.".format(keep_indices)) if any([ind >= n_qubits for ind in keep_indices]): raise ValueError( - "keep_indices {} are an invalid subset of the input wavefunction.") + "keep_indices {} are an invalid subset of the input state vector.") other_qubits = sorted(set(range(n_qubits)) - set(keep_indices)) candidates = [ - wavefunction[predicates.slice_for_qubits_equal_to(other_qubits, + state_vector[predicates.slice_for_qubits_equal_to(other_qubits, k)].reshape(keep_dims) for k in range(1 << len(other_qubits)) ] @@ -470,5 +494,10 @@ def subwavefunction(wavefunction: np.ndarray, return default raise ValueError( - "Input wavefunction could not be factored into pure state over " + "Input state vector could not be factored into pure state over " "indices {}".format(keep_indices)) + + +subwavefunction = deprecated( + deadline='v0.10.0', + fix='Use `cirq.sub_state_vector` instead.')(sub_state_vector) diff --git a/cirq/linalg/transformations_test.py b/cirq/linalg/transformations_test.py index 2172780cce6..c4469c860fb 100644 --- a/cirq/linalg/transformations_test.py +++ b/cirq/linalg/transformations_test.py @@ -16,6 +16,7 @@ import pytest import cirq +import cirq.testing def test_reflection_matrix_pow_consistent_results(): @@ -396,142 +397,146 @@ def test_partial_trace_invalid_inputs(): np.reshape(np.arange(2 * 2 * 2 * 2), (2,) * 4), [2]) -def test_subwavefunction(): +def test_sub_state_vector(): a = np.arange(4) / np.linalg.norm(np.arange(4)) b = (np.arange(8) + 3) / np.linalg.norm(np.arange(8) + 3) c = (np.arange(16) + 1) / np.linalg.norm(np.arange(16) + 1) state = np.kron(np.kron(a, b), c).reshape(2, 2, 2, 2, 2, 2, 2, 2, 2) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(a, [0, 1], atol=1e-8), a) + cirq.sub_state_vector(a, [0, 1], atol=1e-8), a) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(b, [0, 1, 2], atol=1e-8), b) + cirq.sub_state_vector(b, [0, 1, 2], atol=1e-8), b) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(c, [0, 1, 2, 3], atol=1e-8), c) + cirq.sub_state_vector(c, [0, 1, 2, 3], atol=1e-8), c) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(state, [0, 1], atol=1e-15), a.reshape(2, 2)) + cirq.sub_state_vector(state, [0, 1], atol=1e-15), a.reshape(2, 2)) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(state, [2, 3, 4], atol=1e-15), b.reshape(2, 2, 2)) + cirq.sub_state_vector(state, [2, 3, 4], atol=1e-15), b.reshape(2, 2, 2)) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(state, [5, 6, 7, 8], atol=1e-15), + cirq.sub_state_vector(state, [5, 6, 7, 8], atol=1e-15), c.reshape(2, 2, 2, 2)) - # Output wavefunction conforms to the shape of the input wavefunction. + # Output state vector conforms to the shape of the input state vector. reshaped_state = state.reshape(-1) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(reshaped_state, [0, 1], atol=1e-15), a) + cirq.sub_state_vector(reshaped_state, [0, 1], atol=1e-15), a) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(reshaped_state, [2, 3, 4], atol=1e-15), b) + cirq.sub_state_vector(reshaped_state, [2, 3, 4], atol=1e-15), b) assert cirq.equal_up_to_global_phase( - cirq.subwavefunction(reshaped_state, [5, 6, 7, 8], atol=1e-15), c) + cirq.sub_state_vector(reshaped_state, [5, 6, 7, 8], atol=1e-15), c) # Reject factoring for very tight tolerance. - assert cirq.subwavefunction(state, [0, 1], default=None, atol=1e-16) is None - assert cirq.subwavefunction(state, [2, 3, 4], default=None, - atol=1e-16) is None - assert cirq.subwavefunction(state, [5, 6, 7, 8], default=None, - atol=1e-16) is None + assert cirq.sub_state_vector(state, [0, 1], default=None, + atol=1e-16) is None + assert cirq.sub_state_vector(state, [2, 3, 4], default=None, + atol=1e-16) is None + assert cirq.sub_state_vector(state, [5, 6, 7, 8], default=None, + atol=1e-16) is None # Permit invalid factoring for loose tolerance. for q1 in range(9): - assert cirq.subwavefunction(state, [q1], default=None, - atol=1) is not None + assert cirq.sub_state_vector(state, [q1], default=None, + atol=1) is not None -def test_subwavefunction_bad_subset(): +def test_sub_state_vector_bad_subset(): a = cirq.testing.random_superposition(4) b = cirq.testing.random_superposition(8) state = np.kron(a, b).reshape((2, 2, 2, 2, 2)) for q1 in range(5): - assert cirq.subwavefunction(state, [q1], default=None, - atol=1e-8) is None + assert cirq.sub_state_vector(state, [q1], default=None, + atol=1e-8) is None for q1 in range(2): for q2 in range(2, 5): - assert cirq.subwavefunction( + assert cirq.sub_state_vector( state, [q1, q2], default=None, atol=1e-8) is None for q3 in range(2, 5): - assert cirq.subwavefunction(state, [0, 1, q3], default=None, - atol=1e-8) is None + assert cirq.sub_state_vector(state, [0, 1, q3], default=None, + atol=1e-8) is None for q4 in range(2): - assert cirq.subwavefunction( + assert cirq.sub_state_vector( state, [2, 3, 4, q4], default=None, atol=1e-8) is None -def test_subwavefunction_non_kron(): +def test_sub_state_vector_non_kron(): a = np.array([1, 0, 0, 0, 0, 0, 0, 1]) / np.sqrt(2) b = np.array([1, 1]) / np.sqrt(2) state = np.kron(a, b).reshape((2, 2, 2, 2)) for q1 in [0, 1, 2]: - assert cirq.subwavefunction(state, [q1], default=None, - atol=1e-8) is None + assert cirq.sub_state_vector(state, [q1], default=None, + atol=1e-8) is None for q1 in [0, 1, 2]: - assert cirq.subwavefunction(state, [q1, 3], default=None, - atol=1e-8) is None + assert cirq.sub_state_vector(state, [q1, 3], default=None, + atol=1e-8) is None with pytest.raises(ValueError, match='factored'): - _ = cirq.subwavefunction(a, [0], atol=1e-8) + _ = cirq.sub_state_vector(a, [0], atol=1e-8) - assert cirq.equal_up_to_global_phase(cirq.subwavefunction(state, [3]), + assert cirq.equal_up_to_global_phase(cirq.sub_state_vector(state, [3]), b, atol=1e-8) -def test_subwavefunction_invalid_inputs(): +def test_sub_state_vector_invalid_inputs(): - # State cannot be expressed as a qubit wavefunction. + # State cannot be expressed as a seperable pure state. with pytest.raises(ValueError, match='7'): - cirq.subwavefunction(np.arange(7), [1, 2], atol=1e-8) + cirq.sub_state_vector(np.arange(7), [1, 2], atol=1e-8) # State shape does not conform to input requirements. with pytest.raises(ValueError, match='shaped'): - cirq.subwavefunction(np.arange(16).reshape((2, 4, 2)), [1, 2], - atol=1e-8) + cirq.sub_state_vector(np.arange(16).reshape((2, 4, 2)), [1, 2], + atol=1e-8) with pytest.raises(ValueError, match='shaped'): - cirq.subwavefunction(np.arange(16).reshape((16, 1)), [1, 2], atol=1e-8) + cirq.sub_state_vector(np.arange(16).reshape((16, 1)), [1, 2], atol=1e-8) with pytest.raises(ValueError, match='normalized'): - cirq.subwavefunction(np.arange(16), [1, 2], atol=1e-8) + cirq.sub_state_vector(np.arange(16), [1, 2], atol=1e-8) # Bad choice of input indices. state = np.arange(8) / np.linalg.norm(np.arange(8)) with pytest.raises(ValueError, match='2, 2'): - cirq.subwavefunction(state, [1, 2, 2], atol=1e-8) + cirq.sub_state_vector(state, [1, 2, 2], atol=1e-8) state = np.array([1, 0, 0, 0]).reshape((2, 2)) with pytest.raises(ValueError, match='invalid'): - cirq.subwavefunction(state, [5], atol=1e-8) + cirq.sub_state_vector(state, [5], atol=1e-8) with pytest.raises(ValueError, match='invalid'): - cirq.subwavefunction(state, [0, 1, 2], atol=1e-8) + cirq.sub_state_vector(state, [0, 1, 2], atol=1e-8) -def test_wavefunction_partial_trace_as_mixture_invalid_input(): +def test_partial_trace_of_state_vector_as_mixture_invalid_input(): with pytest.raises(ValueError, match='7'): - cirq.wavefunction_partial_trace_as_mixture(np.arange(7), [1, 2], - atol=1e-8) + cirq.partial_trace_of_state_vector_as_mixture(np.arange(7), [1, 2], + atol=1e-8) bad_shape = np.arange(16).reshape((2, 4, 2)) with pytest.raises(ValueError, match='shaped'): - cirq.wavefunction_partial_trace_as_mixture(bad_shape, [1], atol=1e-8) + cirq.partial_trace_of_state_vector_as_mixture(bad_shape, [1], atol=1e-8) bad_shape = np.arange(16).reshape((16, 1)) with pytest.raises(ValueError, match='shaped'): - cirq.wavefunction_partial_trace_as_mixture(bad_shape, [1], atol=1e-8) + cirq.partial_trace_of_state_vector_as_mixture(bad_shape, [1], atol=1e-8) with pytest.raises(ValueError, match='normalized'): - cirq.wavefunction_partial_trace_as_mixture(np.arange(8), [1], atol=1e-8) + cirq.partial_trace_of_state_vector_as_mixture(np.arange(8), [1], + atol=1e-8) state = np.arange(8) / np.linalg.norm(np.arange(8)) with pytest.raises(ValueError, match='2, 2'): - cirq.wavefunction_partial_trace_as_mixture(state, [1, 2, 2], atol=1e-8) + cirq.partial_trace_of_state_vector_as_mixture(state, [1, 2, 2], + atol=1e-8) state = np.array([1, 0, 0, 0]).reshape((2, 2)) with pytest.raises(ValueError, match='invalid'): - cirq.wavefunction_partial_trace_as_mixture(state, [5], atol=1e-8) + cirq.partial_trace_of_state_vector_as_mixture(state, [5], atol=1e-8) with pytest.raises(ValueError, match='invalid'): - cirq.wavefunction_partial_trace_as_mixture(state, [0, 1, 2], atol=1e-8) + cirq.partial_trace_of_state_vector_as_mixture(state, [0, 1, 2], + atol=1e-8) def mixtures_equal(m1, m2, atol=1e-7): @@ -542,85 +547,110 @@ def mixtures_equal(m1, m2, atol=1e-7): return True -def test_wavefunction_partial_trace_as_mixture_pure_result(): +def test_partial_trace_of_state_vector_as_mixture_pure_result(): a = cirq.testing.random_superposition(4) b = cirq.testing.random_superposition(8) c = cirq.testing.random_superposition(16) state = np.kron(np.kron(a, b), c).reshape((2,) * 9) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [0, 1], atol=1e-8), + cirq.partial_trace_of_state_vector_as_mixture(state, [0, 1], atol=1e-8), ((1.0, a.reshape(2, 2)),)) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [2, 3, 4], atol=1e-8), + cirq.partial_trace_of_state_vector_as_mixture(state, [2, 3, 4], + atol=1e-8), ((1.0, b.reshape(2, 2, 2)),)) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [5, 6, 7, 8], - atol=1e-8), + cirq.partial_trace_of_state_vector_as_mixture(state, [5, 6, 7, 8], + atol=1e-8), ((1.0, c.reshape(2, 2, 2, 2)),)) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [0, 1, 2, 3, 4], - atol=1e-8), + cirq.partial_trace_of_state_vector_as_mixture(state, [0, 1, 2, 3, 4], + atol=1e-8), ((1.0, np.kron(a, b).reshape((2, 2, 2, 2, 2))),)) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [0, 1, 5, 6, 7, 8], - atol=1e-8), + cirq.partial_trace_of_state_vector_as_mixture(state, [0, 1, 5, 6, 7, 8], + atol=1e-8), ((1.0, np.kron(a, c).reshape((2, 2, 2, 2, 2, 2))),)) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [2, 3, 4, 5, 6, 7, 8], - atol=1e-8), + cirq.partial_trace_of_state_vector_as_mixture(state, + [2, 3, 4, 5, 6, 7, 8], + atol=1e-8), ((1.0, np.kron(b, c).reshape((2, 2, 2, 2, 2, 2, 2))),)) # Shapes of states in the output mixture conform to the input's shape. state = state.reshape(2**9) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [0, 1], atol=1e-8), + cirq.partial_trace_of_state_vector_as_mixture(state, [0, 1], atol=1e-8), ((1.0, a),)) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [2, 3, 4], atol=1e-8), - ((1.0, b),)) + cirq.partial_trace_of_state_vector_as_mixture(state, [2, 3, 4], + atol=1e-8), ((1.0, b),)) assert mixtures_equal( - cirq.wavefunction_partial_trace_as_mixture(state, [5, 6, 7, 8], - atol=1e-8), ((1.0, c),)) + cirq.partial_trace_of_state_vector_as_mixture(state, [5, 6, 7, 8], + atol=1e-8), ((1.0, c),)) # Return mixture will defer to numpy.linalg.eigh's builtin tolerance. state = np.array([1, 0, 0, 1]) / np.sqrt(2) truth = ((0.5, np.array([1, 0])), (0.5, np.array([0, 1]))) - assert mixtures_equal(cirq.wavefunction_partial_trace_as_mixture( + assert mixtures_equal(cirq.partial_trace_of_state_vector_as_mixture( state, [1], atol=1e-20), truth, atol=1e-15) - assert not mixtures_equal(cirq.wavefunction_partial_trace_as_mixture( + assert not mixtures_equal(cirq.partial_trace_of_state_vector_as_mixture( state, [1], atol=1e-20), truth, atol=1e-16) -def test_wavefunction_partial_trace_as_mixture_mixed_result(): +def test_partial_trace_of_state_vector_as_mixture_mixed_result(): state = np.array([1, 0, 0, 1]) / np.sqrt(2) truth = ((0.5, np.array([1, 0])), (0.5, np.array([0, 1]))) for q1 in [0, 1]: - mixture = cirq.wavefunction_partial_trace_as_mixture(state, [q1], - atol=1e-8) + mixture = cirq.partial_trace_of_state_vector_as_mixture(state, [q1], + atol=1e-8) assert mixtures_equal(mixture, truth) state = np.array([0, 1, 1, 0, 1, 0, 0, 0]).reshape((2, 2, 2)) / np.sqrt(3) truth = ((1 / 3, np.array([0.0, 1.0])), (2 / 3, np.array([1.0, 0.0]))) for q1 in [0, 1, 2]: - mixture = cirq.wavefunction_partial_trace_as_mixture(state, [q1], - atol=1e-8) + mixture = cirq.partial_trace_of_state_vector_as_mixture(state, [q1], + atol=1e-8) assert mixtures_equal(mixture, truth) state = np.array([1, 0, 0, 0, 0, 0, 0, 1]).reshape((2, 2, 2)) / np.sqrt(2) truth = ((0.5, np.array([1, 0])), (0.5, np.array([0, 1]))) for q1 in [0, 1, 2]: - mixture = cirq.wavefunction_partial_trace_as_mixture(state, [q1], - atol=1e-8) + mixture = cirq.partial_trace_of_state_vector_as_mixture(state, [q1], + atol=1e-8) assert mixtures_equal(mixture, truth) truth = ((0.5, np.array([1, 0, 0, 0]).reshape( (2, 2))), (0.5, np.array([0, 0, 0, 1]).reshape((2, 2)))) for (q1, q2) in [(0, 1), (0, 2), (1, 2)]: - mixture = cirq.wavefunction_partial_trace_as_mixture(state, [q1, q2], - atol=1e-8) + mixture = cirq.partial_trace_of_state_vector_as_mixture(state, [q1, q2], + atol=1e-8) assert mixtures_equal(mixture, truth) + + +def test_deprecated(): + a = np.arange(4) / np.linalg.norm(np.arange(4)) + with cirq.testing.assert_logs('subwavefunction', 'sub_state_vector', + 'deprecated'): + _ = cirq.subwavefunction(a, [0, 1], atol=1e-8) + + with cirq.testing.assert_logs('wavefunction', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.sub_state_vector(wavefunction=a, + keep_indices=[0, 1], + atol=1e-8) + + with cirq.testing.assert_logs('wavefunction_partial_trace_as_mixture', + 'partial_trace_of_state_vector_as_mixture', + 'deprecated'): + _ = cirq.wavefunction_partial_trace_as_mixture(a, [0]) + + with cirq.testing.assert_logs('wavefunction', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.partial_trace_of_state_vector_as_mixture(wavefunction=a, + keep_indices=[0]) diff --git a/cirq/ops/arithmetic_operation_test.py b/cirq/ops/arithmetic_operation_test.py index 6824b77581c..e84ffeda058 100644 --- a/cirq/ops/arithmetic_operation_test.py +++ b/cirq/ops/arithmetic_operation_test.py @@ -168,5 +168,5 @@ def with_registers(self, *new_registers): raise NotImplementedError() state = np.ones(4, dtype=np.complex64) / 2 - output = cirq.final_wavefunction(cirq.Circuit(Op1()), initial_state=state) + output = cirq.final_state_vector(cirq.Circuit(Op1()), initial_state=state) np.testing.assert_allclose(state, output) diff --git a/cirq/ops/linear_combinations.py b/cirq/ops/linear_combinations.py index 5d502beb60d..36d777cccb7 100644 --- a/cirq/ops/linear_combinations.py +++ b/cirq/ops/linear_combinations.py @@ -24,6 +24,7 @@ from cirq.ops import identity, raw_types, pauli_gates, pauli_string from cirq.ops.pauli_string import PauliString, _validate_qubit_mapping from cirq.value.linear_dict import _format_terms +from cirq._compat import deprecated, deprecated_parameter if TYPE_CHECKING: import cirq @@ -350,6 +351,8 @@ def copy(self) -> 'PauliSum': factory = type(self) return factory(self._linear_dict.copy()) + @deprecated(deadline='v0.10.0', + fix='Use expectation_from_state_vector instead.') def expectation_from_wavefunction(self, state: np.ndarray, qubit_map: Mapping[raw_types.Qid, int], @@ -357,17 +360,37 @@ def expectation_from_wavefunction(self, atol: float = 1e-7, check_preconditions: bool = True ) -> float: - """Evaluate the expectation of this PauliSum given a wavefunction. + return self.expectation_from_state_vector( + state_vector=state, + qubit_map=qubit_map, + atol=atol, + check_preconditions=check_preconditions) + + @deprecated_parameter(deadline='v0.10.0', + fix='Use state_vector instead', + parameter_desc='state', + match=lambda args, kwargs: 'state' in kwargs, + rewrite=lambda args, kwargs: ( + args, {('state_vector' if k == 'state' else k): v + for k, v in kwargs.items()})) + def expectation_from_state_vector(self, + state_vector: np.ndarray, + qubit_map: Mapping[raw_types.Qid, int], + *, + atol: float = 1e-7, + check_preconditions: bool = True + ) -> float: + """Evaluate the expectation of this PauliSum given a state vector. - See `PauliString.expectation_from_wavefunction`. + See `PauliString.expectation_from_state_vector`. Args: - state: An array representing a valid wavefunction. + state: An array representing a valid state vector. qubit_map: A map from all qubits used in this PauliSum to the - indices of the qubits that `state` is defined over. + indices of the qubits that `state_vector` is defined over. atol: Absolute numerical tolerance. - check_preconditions: Whether to check that `state` represents a - valid wavefunction. + check_preconditions: Whether to check that `state_vector` represents + a valid state vector. Returns: The expectation value of the input state. @@ -377,28 +400,30 @@ def expectation_from_wavefunction(self, "Cannot compute expectation value of a non-Hermitian " "PauliString <{}>. Coefficient must be real.".format(self)) - # FIXME: Avoid enforce specific complex type. This is necessary to - # prevent an `apply_unitary` bug (Issue #2041). - if state.dtype.kind != 'c': + # TODO: Avoid enforce specific complex type. This is necessary to + # prevent an `apply_unitary` bug. + # Github issue: https://github.com/quantumlib/Cirq/issues/2041 + if state_vector.dtype.kind != 'c': raise TypeError("Input state dtype must be np.complex64 or " "np.complex128") - size = state.size + size = state_vector.size num_qubits = size.bit_length() - 1 _validate_qubit_mapping(qubit_map, self.qubits, num_qubits) - if len(state.shape) != 1 and state.shape != (2,) * num_qubits: - raise ValueError("Input array does not represent a wavefunction " + if len(state_vector.shape) != 1 and state_vector.shape != ( + 2,) * num_qubits: + raise ValueError("Input array does not represent a state vector " "with shape `(2 ** n,)` or `(2, ..., 2)`.") if check_preconditions: - qis.validate_normalized_state(state=state, - qid_shape=(2,) * num_qubits, - dtype=state.dtype, - atol=atol) + qis.validate_normalized_state_vector(state_vector=state_vector, + qid_shape=(2,) * num_qubits, + dtype=state_vector.dtype, + atol=atol) return sum( - p._expectation_from_wavefunction_no_validation(state, qubit_map) - for p in self) + p._expectation_from_state_vector_no_validation( + state_vector, qubit_map) for p in self) def expectation_from_density_matrix(self, state: np.ndarray, diff --git a/cirq/ops/linear_combinations_test.py b/cirq/ops/linear_combinations_test.py index 8f4ed3032c6..d25fdd9ecf7 100644 --- a/cirq/ops/linear_combinations_test.py +++ b/cirq/ops/linear_combinations_test.py @@ -20,6 +20,7 @@ import sympy import cirq +import cirq.testing _ = 0.0 # Make matrices readable by visually hiding off-diagonal elements. q0, q1, q2, q3 = cirq.LineQubit.range(4) @@ -1058,7 +1059,7 @@ def test_imul_aliasing(): assert psum1 == psum2 -def test_expectation_from_wavefunction_invalid_input(): +def test_expectation_from_state_vector_invalid_input(): q0, q1, q2, q3 = cirq.LineQubit.range(4) psum = cirq.X(q0) + 2 * cirq.Y(q1) + 3 * cirq.Z(q3) q_map = {q0: 0, q1: 1, q3: 2} @@ -1066,88 +1067,88 @@ def test_expectation_from_wavefunction_invalid_input(): im_psum = (1j + 1) * psum with pytest.raises(NotImplementedError, match='non-Hermitian'): - im_psum.expectation_from_wavefunction(wf, q_map) + im_psum.expectation_from_state_vector(wf, q_map) with pytest.raises(TypeError, match='dtype'): - psum.expectation_from_wavefunction(np.array([1, 0], dtype=np.int), + psum.expectation_from_state_vector(np.array([1, 0], dtype=np.int), q_map) with pytest.raises(TypeError, match='mapping'): - psum.expectation_from_wavefunction(wf, "bad type") + psum.expectation_from_state_vector(wf, "bad type") with pytest.raises(TypeError, match='mapping'): - psum.expectation_from_wavefunction(wf, {"bad key": 1}) + psum.expectation_from_state_vector(wf, {"bad key": 1}) with pytest.raises(TypeError, match='mapping'): - psum.expectation_from_wavefunction(wf, {q0: "bad value"}) + psum.expectation_from_state_vector(wf, {q0: "bad value"}) with pytest.raises(ValueError, match='complete'): - psum.expectation_from_wavefunction(wf, {q0: 0}) + psum.expectation_from_state_vector(wf, {q0: 0}) with pytest.raises(ValueError, match='complete'): - psum.expectation_from_wavefunction(wf, {q0: 0, q2: 2}) + psum.expectation_from_state_vector(wf, {q0: 0, q2: 2}) with pytest.raises(ValueError, match='indices'): - psum.expectation_from_wavefunction(wf, {q0: -1, q1: 1, q3: 2}) + psum.expectation_from_state_vector(wf, {q0: -1, q1: 1, q3: 2}) with pytest.raises(ValueError, match='indices'): - psum.expectation_from_wavefunction(wf, {q0: 0, q1: 3, q3: 2}) + psum.expectation_from_state_vector(wf, {q0: 0, q1: 3, q3: 2}) with pytest.raises(ValueError, match='indices'): - psum.expectation_from_wavefunction(wf, {q0: 0, q1: 0, q3: 2}) + psum.expectation_from_state_vector(wf, {q0: 0, q1: 0, q3: 2}) with pytest.raises(ValueError, match='9'): - psum.expectation_from_wavefunction(np.arange(9, dtype=np.complex64), + psum.expectation_from_state_vector(np.arange(9, dtype=np.complex64), q_map) q_map_2 = {q0: 0, q1: 1, q2: 2, q3: 3} with pytest.raises(ValueError, match='normalized'): - psum.expectation_from_wavefunction(np.arange(16, dtype=np.complex64), + psum.expectation_from_state_vector(np.arange(16, dtype=np.complex64), q_map_2) wf = np.arange(16, dtype=np.complex64) / np.linalg.norm(np.arange(16)) with pytest.raises(ValueError, match='shape'): - psum.expectation_from_wavefunction(wf.reshape((16, 1)), q_map_2) + psum.expectation_from_state_vector(wf.reshape((16, 1)), q_map_2) with pytest.raises(ValueError, match='shape'): - psum.expectation_from_wavefunction(wf.reshape((4, 4, 1)), q_map_2) + psum.expectation_from_state_vector(wf.reshape((4, 4, 1)), q_map_2) -def test_expectation_from_wavefunction_check_preconditions(): +def test_expectation_from_state_vector_check_preconditions(): q0, q1, q2, q3 = cirq.LineQubit.range(4) psum = cirq.X(q0) + 2 * cirq.Y(q1) + 3 * cirq.Z(q3) q_map = {q0: 0, q1: 1, q2: 2, q3: 3} with pytest.raises(ValueError, match='normalized'): - psum.expectation_from_wavefunction(np.arange(16, dtype=np.complex64), + psum.expectation_from_state_vector(np.arange(16, dtype=np.complex64), q_map) - _ = psum.expectation_from_wavefunction(np.arange(16, dtype=np.complex64), + _ = psum.expectation_from_state_vector(np.arange(16, dtype=np.complex64), q_map, check_preconditions=False) -def test_expectation_from_wavefunction_basis_states(): +def test_expectation_from_state_vector_basis_states(): q = cirq.LineQubit.range(2) psum = cirq.X(q[0]) + 2 * cirq.Y(q[0]) + 3 * cirq.Z(q[0]) q_map = {x: i for i, x in enumerate(q)} np.testing.assert_allclose( - psum.expectation_from_wavefunction(np.array([1, 1], dtype=np.complex) / + psum.expectation_from_state_vector(np.array([1, 1], dtype=np.complex) / np.sqrt(2), qubit_map=q_map), 1) np.testing.assert_allclose( - psum.expectation_from_wavefunction(np.array([1, -1], dtype=np.complex) / + psum.expectation_from_state_vector(np.array([1, -1], dtype=np.complex) / np.sqrt(2), qubit_map=q_map), -1) np.testing.assert_allclose( - psum.expectation_from_wavefunction(np.array([1, 1j], dtype=np.complex) / + psum.expectation_from_state_vector(np.array([1, 1j], dtype=np.complex) / np.sqrt(2), qubit_map=q_map), 2) np.testing.assert_allclose( - psum.expectation_from_wavefunction( + psum.expectation_from_state_vector( np.array([1, -1j], dtype=np.complex) / np.sqrt(2), qubit_map=q_map), -2) np.testing.assert_allclose( - psum.expectation_from_wavefunction(np.array([1, 0], dtype=np.complex), + psum.expectation_from_state_vector(np.array([1, 0], dtype=np.complex), qubit_map=q_map), 3) np.testing.assert_allclose( - psum.expectation_from_wavefunction(np.array([0, 1], dtype=np.complex), + psum.expectation_from_state_vector(np.array([0, 1], dtype=np.complex), qubit_map=q_map), -3) -def test_expectation_from_wavefunction_two_qubit_states(): +def test_expectation_from_state_vector_two_qubit_states(): q = cirq.LineQubit.range(2) q_map = {x: i for i, x in enumerate(q)} @@ -1155,22 +1156,22 @@ def test_expectation_from_wavefunction_two_qubit_states(): psum2 = -1 * cirq.X(q[0]) + 2 * cirq.X(q[1]) wf1 = np.array([0, 1, 0, 0], dtype=np.complex) for state in [wf1, wf1.reshape(2, 2)]: - np.testing.assert_allclose(psum1.expectation_from_wavefunction( + np.testing.assert_allclose(psum1.expectation_from_state_vector( state, qubit_map=q_map), -2.2, atol=1e-7) - np.testing.assert_allclose(psum2.expectation_from_wavefunction( + np.testing.assert_allclose(psum2.expectation_from_state_vector( state, qubit_map=q_map), 0, atol=1e-7) wf2 = np.array([1, 1, 1, 1], dtype=np.complex) / 2 for state in [wf2, wf2.reshape(2, 2)]: - np.testing.assert_allclose(psum1.expectation_from_wavefunction( + np.testing.assert_allclose(psum1.expectation_from_state_vector( state, qubit_map=q_map), 0, atol=1e-7) - np.testing.assert_allclose(psum2.expectation_from_wavefunction( + np.testing.assert_allclose(psum2.expectation_from_state_vector( state, qubit_map=q_map), 1, atol=1e-7) @@ -1179,11 +1180,11 @@ def test_expectation_from_wavefunction_two_qubit_states(): wf3 = np.array([1, 1, 0, 0], dtype=np.complex) / np.sqrt(2) q_map_2 = {q0: 1, q1: 0} for state in [wf3, wf3.reshape(2, 2)]: - np.testing.assert_allclose(psum3.expectation_from_wavefunction( + np.testing.assert_allclose(psum3.expectation_from_state_vector( state, qubit_map=q_map), 2, atol=1e-7) - np.testing.assert_allclose(psum3.expectation_from_wavefunction( + np.testing.assert_allclose(psum3.expectation_from_state_vector( state, qubit_map=q_map_2), 0, atol=1e-7) @@ -1321,3 +1322,17 @@ def test_expectation_from_density_matrix_two_qubit_states(): psum3.expectation_from_density_matrix(state, qubit_map=q_map), 2) np.testing.assert_allclose( psum3.expectation_from_density_matrix(state, qubit_map=q_map_2), 0) + + +def test_deprecated(): + q = cirq.LineQubit(0) + pauli_sum = cirq.X(q) + 0.2 * cirq.Z(q) + state_vector = np.array([1, 1], dtype=np.complex64) / np.sqrt(2) + with cirq.testing.assert_logs('expectation_from_wavefunction', + 'expectation_from_state_vector', + 'deprecated'): + _ = pauli_sum.expectation_from_wavefunction(state_vector, {q: 0}) + + with cirq.testing.assert_logs('state', 'state_vector', 'deprecated'): + _ = pauli_sum.expectation_from_state_vector(state=state_vector, + qubit_map={q: 0}) diff --git a/cirq/ops/pauli_string.py b/cirq/ops/pauli_string.py index b4fd3a8d937..c25c95d71e8 100644 --- a/cirq/ops/pauli_string.py +++ b/cirq/ops/pauli_string.py @@ -37,6 +37,7 @@ raw_types, ) from cirq.type_workarounds import NotImplementedType +from cirq._compat import deprecated, deprecated_parameter if TYPE_CHECKING: import cirq @@ -340,6 +341,8 @@ def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs'): return protocols.apply_unitaries([self[q].on(q) for q in self.qubits], self.qubits, args) + @deprecated(deadline='v0.10.0', + fix='Use expectation_from_state_vector instead') def expectation_from_wavefunction(self, state: np.ndarray, qubit_map: Mapping[raw_types.Qid, int], @@ -347,14 +350,34 @@ def expectation_from_wavefunction(self, atol: float = 1e-7, check_preconditions: bool = True ) -> float: - r"""Evaluate the expectation of this PauliString given a wavefunction. + return self.expectation_from_state_vector( + state_vector=state, + qubit_map=qubit_map, + atol=atol, + check_preconditions=check_preconditions) + + @deprecated_parameter(deadline='v0.10.0', + fix='Use state_vector instead', + parameter_desc='state', + match=lambda args, kwargs: 'state' in kwargs, + rewrite=lambda args, kwargs: ( + args, {('state_vector' if k == 'state' else k): v + for k, v in kwargs.items()})) + def expectation_from_state_vector(self, + state_vector: np.ndarray, + qubit_map: Mapping[raw_types.Qid, int], + *, + atol: float = 1e-7, + check_preconditions: bool = True + ) -> float: + r"""Evaluate the expectation of this PauliString given a state vector. Compute the expectation value of this PauliString with respect to a - wavefunction. By convention expectation values are defined for Hermitian + state vector. By convention expectation values are defined for Hermitian operators, and so this method will fail if this PauliString is non-Hermitian. - `state` must be an array representation of a wavefunction and have + `state` must be an array representation of a state vector and have shape `(2 ** n, )` or `(2, 2, ..., 2)` (n entries) where `state` is expressed over n qubits. @@ -367,12 +390,12 @@ def expectation_from_wavefunction(self, cirq.X(q0).expectation(state, qubit_map={q0: 1, q1: 0}) = 1 Args: - state: An array representing a valid wavefunction. + state_vector: An array representing a valid state vector. qubit_map: A map from all qubits used in this PauliString to the - indices of the qubits that `state` is defined over. + indices of the qubits that `state_vector` is defined over. atol: Absolute numerical tolerance. - check_preconditions: Whether to check that `state` represents a - valid wavefunction. + check_preconditions: Whether to check that `state_vector` represents + a valid state vector. Returns: The expectation value of the input state. @@ -387,55 +410,56 @@ def expectation_from_wavefunction(self, # FIXME: Avoid enforce specific complex type. This is necessary to # prevent an `apply_unitary` bug (Issue #2041). - if state.dtype.kind != 'c': + if state_vector.dtype.kind != 'c': raise TypeError("Input state dtype must be np.complex64 or " "np.complex128") - size = state.size + size = state_vector.size num_qubits = size.bit_length() - 1 - if len(state.shape) != 1 and state.shape != (2,) * num_qubits: - raise ValueError("Input array does not represent a wavefunction " + if len(state_vector.shape) != 1 and state_vector.shape != ( + 2,) * num_qubits: + raise ValueError("Input array does not represent a state vector " "with shape `(2 ** n,)` or `(2, ..., 2)`.") _validate_qubit_mapping(qubit_map, self.qubits, num_qubits) if check_preconditions: - qis.validate_normalized_state(state=state, - qid_shape=(2,) * num_qubits, - dtype=state.dtype, - atol=atol) - return self._expectation_from_wavefunction_no_validation( - state, qubit_map) - - def _expectation_from_wavefunction_no_validation( - self, state: np.ndarray, + qis.validate_normalized_state_vector(state_vector=state_vector, + qid_shape=(2,) * num_qubits, + dtype=state_vector.dtype, + atol=atol) + return self._expectation_from_state_vector_no_validation( + state_vector, qubit_map) + + def _expectation_from_state_vector_no_validation( + self, state_vector: np.ndarray, qubit_map: Mapping[raw_types.Qid, int]) -> float: - """Evaluate the expectation of this PauliString given a wavefunction. + """Evaluate the expectation of this PauliString given a state vector. This method does not provide input validation. See - `PauliString.expectation_from_wavefunction` for function description. + `PauliString.expectation_from_state_vector` for function description. Args: - state: An array representing a valid wavefunction. + state_vector: An array representing a valid state vector. qubit_map: A map from all qubits used in this PauliString to the indices of the qubits that `state` is defined over. Returns: The expectation value of the input state. """ - if len(state.shape) == 1: - num_qubits = state.shape[0].bit_length() - 1 - state = np.reshape(state, (2,) * num_qubits) + if len(state_vector.shape) == 1: + num_qubits = state_vector.shape[0].bit_length() - 1 + state_vector = np.reshape(state_vector, (2,) * num_qubits) - ket = np.copy(state) + ket = np.copy(state_vector) for qubit, pauli in self.items(): - buffer = np.empty(ket.shape, dtype=state.dtype) + buffer = np.empty(ket.shape, dtype=state_vector.dtype) args = protocols.ApplyUnitaryArgs(target_tensor=ket, available_buffer=buffer, axes=(qubit_map[qubit],)) ket = protocols.apply_unitary(pauli, args) return self.coefficient * (np.tensordot( - state.conj(), ket, axes=len(ket.shape)).item()) + state_vector.conj(), ket, axes=len(ket.shape)).item()) def expectation_from_density_matrix(self, state: np.ndarray, diff --git a/cirq/ops/pauli_string_test.py b/cirq/ops/pauli_string_test.py index 1abc039a474..33747589b26 100644 --- a/cirq/ops/pauli_string_test.py +++ b/cirq/ops/pauli_string_test.py @@ -21,6 +21,7 @@ import sympy import cirq +import cirq.testing def _make_qubits(n): @@ -563,7 +564,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_wavefunction(initial_state) + z_basis_state = circuit.final_state_vector(initial_state) expected_state = np.zeros(2 ** 6) expected_state[0b010101] = 1 @@ -767,7 +768,7 @@ def test_filters_identities(): cirq.PauliString({q2: cirq.X}) -def test_expectation_from_wavefunction_invalid_input(): +def test_expectation_from_state_vector_invalid_input(): q0, q1, q2, q3 = _make_qubits(4) ps = cirq.PauliString({q0: cirq.X, q1: cirq.Y}) wf = np.array([1, 0, 0, 0], dtype=np.complex64) @@ -775,106 +776,106 @@ def test_expectation_from_wavefunction_invalid_input(): im_ps = (1j + 1) * ps with pytest.raises(NotImplementedError, match='non-Hermitian'): - im_ps.expectation_from_wavefunction(wf, q_map) + im_ps.expectation_from_state_vector(wf, q_map) with pytest.raises(TypeError, match='dtype'): - ps.expectation_from_wavefunction(np.array([1, 0], dtype=np.int), q_map) + ps.expectation_from_state_vector(np.array([1, 0], dtype=np.int), q_map) with pytest.raises(TypeError, match='mapping'): - ps.expectation_from_wavefunction(wf, "bad type") + ps.expectation_from_state_vector(wf, "bad type") with pytest.raises(TypeError, match='mapping'): - ps.expectation_from_wavefunction(wf, {"bad key": 1}) + ps.expectation_from_state_vector(wf, {"bad key": 1}) with pytest.raises(TypeError, match='mapping'): - ps.expectation_from_wavefunction(wf, {q0: "bad value"}) + ps.expectation_from_state_vector(wf, {q0: "bad value"}) with pytest.raises(ValueError, match='complete'): - ps.expectation_from_wavefunction(wf, {q0: 0}) + ps.expectation_from_state_vector(wf, {q0: 0}) with pytest.raises(ValueError, match='complete'): - ps.expectation_from_wavefunction(wf, {q0: 0, q2: 2}) + ps.expectation_from_state_vector(wf, {q0: 0, q2: 2}) with pytest.raises(ValueError, match='indices'): - ps.expectation_from_wavefunction(wf, {q0: -1, q1: 1}) + ps.expectation_from_state_vector(wf, {q0: -1, q1: 1}) with pytest.raises(ValueError, match='indices'): - ps.expectation_from_wavefunction(wf, {q0: 0, q1: 3}) + ps.expectation_from_state_vector(wf, {q0: 0, q1: 3}) with pytest.raises(ValueError, match='indices'): - ps.expectation_from_wavefunction(wf, {q0: 0, q1: 0}) + ps.expectation_from_state_vector(wf, {q0: 0, q1: 0}) # Excess keys are ignored. - _ = ps.expectation_from_wavefunction(wf, {q0: 0, q1: 1, q2: 0}) + _ = ps.expectation_from_state_vector(wf, {q0: 0, q1: 1, q2: 0}) - # Incorrectly shaped wavefunction input. + # Incorrectly shaped state_vector input. with pytest.raises(ValueError, match='7'): - ps.expectation_from_wavefunction(np.arange(7, dtype=np.complex64), + ps.expectation_from_state_vector(np.arange(7, dtype=np.complex64), q_map) q_map_2 = {q0: 0, q1: 1, q2: 2, q3: 3} with pytest.raises(ValueError, match='normalized'): - ps.expectation_from_wavefunction(np.arange(16, dtype=np.complex64), + ps.expectation_from_state_vector(np.arange(16, dtype=np.complex64), q_map_2) # The ambiguous case: Density matrices satisfying L2 normalization. rho_or_wf = 0.5 * np.ones((2, 2), dtype=np.complex64) - _ = ps.expectation_from_wavefunction(rho_or_wf, q_map) + _ = ps.expectation_from_state_vector(rho_or_wf, q_map) wf = np.arange(16, dtype=np.complex64) / np.linalg.norm(np.arange(16)) with pytest.raises(ValueError, match='shape'): - ps.expectation_from_wavefunction(wf.reshape((16, 1)), q_map_2) + ps.expectation_from_state_vector(wf.reshape((16, 1)), q_map_2) with pytest.raises(ValueError, match='shape'): - ps.expectation_from_wavefunction(wf.reshape((4, 4, 1)), q_map_2) + ps.expectation_from_state_vector(wf.reshape((4, 4, 1)), q_map_2) -def test_expectation_from_wavefunction_check_preconditions(): +def test_expectation_from_state_vector_check_preconditions(): q0, q1, q2, q3 = _make_qubits(4) ps = cirq.PauliString({q0: cirq.X, q1: cirq.Y}) q_map = {q0: 0, q1: 1, q2: 2, q3: 3} with pytest.raises(ValueError, match='normalized'): - ps.expectation_from_wavefunction(np.arange(16, dtype=np.complex64), + ps.expectation_from_state_vector(np.arange(16, dtype=np.complex64), q_map) - _ = ps.expectation_from_wavefunction(np.arange(16, dtype=np.complex64), + _ = ps.expectation_from_state_vector(np.arange(16, dtype=np.complex64), q_map, check_preconditions=False) -def test_expectation_from_wavefunction_basis_states(): +def test_expectation_from_state_vector_basis_states(): q0 = cirq.LineQubit(0) x0 = cirq.PauliString({q0: cirq.X}) q_map = {q0: 0} - np.testing.assert_allclose(x0.expectation_from_wavefunction( + np.testing.assert_allclose(x0.expectation_from_state_vector( np.array([1, 0], dtype=np.complex), q_map), 0, atol=1e-7) - np.testing.assert_allclose(x0.expectation_from_wavefunction( + np.testing.assert_allclose(x0.expectation_from_state_vector( np.array([0, 1], dtype=np.complex), q_map), 0, atol=1e-7) - np.testing.assert_allclose(x0.expectation_from_wavefunction( + np.testing.assert_allclose(x0.expectation_from_state_vector( np.array([1, 1], dtype=np.complex) / np.sqrt(2), q_map), 1, atol=1e-7) - np.testing.assert_allclose(x0.expectation_from_wavefunction( + np.testing.assert_allclose(x0.expectation_from_state_vector( np.array([1, -1], dtype=np.complex) / np.sqrt(2), q_map), -1, atol=1e-7) y0 = cirq.PauliString({q0: cirq.Y}) - np.testing.assert_allclose(y0.expectation_from_wavefunction( + np.testing.assert_allclose(y0.expectation_from_state_vector( np.array([1, 1j], dtype=np.complex) / np.sqrt(2), q_map), 1, atol=1e-7) - np.testing.assert_allclose(y0.expectation_from_wavefunction( + np.testing.assert_allclose(y0.expectation_from_state_vector( np.array([1, -1j], dtype=np.complex) / np.sqrt(2), q_map), -1, atol=1e-7) - np.testing.assert_allclose(y0.expectation_from_wavefunction( + np.testing.assert_allclose(y0.expectation_from_state_vector( np.array([1, 1], dtype=np.complex) / np.sqrt(2), q_map), 0, atol=1e-7) - np.testing.assert_allclose(y0.expectation_from_wavefunction( + np.testing.assert_allclose(y0.expectation_from_state_vector( np.array([1, -1], dtype=np.complex) / np.sqrt(2), q_map), 0, atol=1e-7) -def test_expectation_from_wavefunction_entangled_states(): +def test_expectation_from_state_vector_entangled_states(): q0, q1 = _make_qubits(2) z0z1_pauli_map = {q0: cirq.Z, q1: cirq.Z} z0z1 = cirq.PauliString(z0z1_pauli_map) @@ -884,69 +885,69 @@ def test_expectation_from_wavefunction_entangled_states(): wf1 = np.array([0, 1, 1, 0], dtype=np.complex) / np.sqrt(2) for state in [wf1, wf1.reshape(2, 2)]: np.testing.assert_allclose( - z0z1.expectation_from_wavefunction(state, q_map), -1) + z0z1.expectation_from_state_vector(state, q_map), -1) np.testing.assert_allclose( - x0x1.expectation_from_wavefunction(state, q_map), 1) + x0x1.expectation_from_state_vector(state, q_map), 1) wf2 = np.array([1, 0, 0, 1], dtype=np.complex) / np.sqrt(2) for state in [wf2, wf2.reshape(2, 2)]: np.testing.assert_allclose( - z0z1.expectation_from_wavefunction(state, q_map), 1) + z0z1.expectation_from_state_vector(state, q_map), 1) np.testing.assert_allclose( - x0x1.expectation_from_wavefunction(state, q_map), 1) + x0x1.expectation_from_state_vector(state, q_map), 1) wf3 = np.array([1, 1, 1, 1], dtype=np.complex) / 2 for state in [wf3, wf3.reshape(2, 2)]: np.testing.assert_allclose( - z0z1.expectation_from_wavefunction(state, q_map), 0) + z0z1.expectation_from_state_vector(state, q_map), 0) np.testing.assert_allclose( - x0x1.expectation_from_wavefunction(state, q_map), 1) + x0x1.expectation_from_state_vector(state, q_map), 1) -def test_expectation_from_wavefunction_qubit_map(): +def test_expectation_from_state_vector_qubit_map(): q0, q1, q2 = _make_qubits(3) z = cirq.PauliString({q0: cirq.Z}) wf = np.array([0, 1, 0, 1, 0, 0, 0, 0], dtype=np.complex) / np.sqrt(2) for state in [wf, wf.reshape(2, 2, 2)]: np.testing.assert_allclose( - z.expectation_from_wavefunction(state, { + z.expectation_from_state_vector(state, { q0: 0, q1: 1, q2: 2 }), 1) np.testing.assert_allclose( - z.expectation_from_wavefunction(state, { + z.expectation_from_state_vector(state, { q0: 0, q1: 2, q2: 1 }), 1) np.testing.assert_allclose( - z.expectation_from_wavefunction(state, { + z.expectation_from_state_vector(state, { q0: 1, q1: 0, q2: 2 }), 0) np.testing.assert_allclose( - z.expectation_from_wavefunction(state, { + z.expectation_from_state_vector(state, { q0: 1, q1: 2, q2: 0 }), 0) np.testing.assert_allclose( - z.expectation_from_wavefunction(state, { + z.expectation_from_state_vector(state, { q0: 2, q1: 0, q2: 1 }), -1) np.testing.assert_allclose( - z.expectation_from_wavefunction(state, { + z.expectation_from_state_vector(state, { q0: 2, q1: 1, q2: 0 }), -1) -def test_pauli_string_expectation_from_wavefunction_pure_state(): +def test_pauli_string_expectation_from_state_vector_pure_state(): qubits = cirq.LineQubit.range(4) q_map = {q: i for i, q in enumerate(qubits)} @@ -956,7 +957,7 @@ def test_pauli_string_expectation_from_wavefunction_pure_state(): cirq.X(qubits[3]), cirq.H(qubits[3]), ) - wf = circuit.final_wavefunction(qubit_order=qubits) + wf = circuit.final_state_vector(qubit_order=qubits) z0z1 = cirq.PauliString({qubits[0]: cirq.Z, qubits[1]: cirq.Z}) z0z2 = cirq.PauliString({qubits[0]: cirq.Z, qubits[2]: cirq.Z}) @@ -968,22 +969,22 @@ def test_pauli_string_expectation_from_wavefunction_pure_state(): for state in [wf, wf.reshape((2, 2, 2, 2))]: np.testing.assert_allclose( - z0z1.expectation_from_wavefunction(state, q_map), -1) + z0z1.expectation_from_state_vector(state, q_map), -1) np.testing.assert_allclose( - z0z2.expectation_from_wavefunction(state, q_map), 0) + z0z2.expectation_from_state_vector(state, q_map), 0) np.testing.assert_allclose( - z0z3.expectation_from_wavefunction(state, q_map), 0) + z0z3.expectation_from_state_vector(state, q_map), 0) np.testing.assert_allclose( - z0x1.expectation_from_wavefunction(state, q_map), 0) + z0x1.expectation_from_state_vector(state, q_map), 0) np.testing.assert_allclose( - z1x2.expectation_from_wavefunction(state, q_map), -1) + z1x2.expectation_from_state_vector(state, q_map), -1) np.testing.assert_allclose( - x0z1.expectation_from_wavefunction(state, q_map), 0) + x0z1.expectation_from_state_vector(state, q_map), 0) np.testing.assert_allclose( - x3.expectation_from_wavefunction(state, q_map), -1) + x3.expectation_from_state_vector(state, q_map), -1) -def test_pauli_string_expectation_from_wavefunction_pure_state_with_coef(): +def test_pauli_string_expectation_from_state_vector_pure_state_with_coef(): qs = cirq.LineQubit.range(4) q_map = {q: i for i, q in enumerate(qs)} @@ -993,7 +994,7 @@ def test_pauli_string_expectation_from_wavefunction_pure_state_with_coef(): cirq.X(qs[3]), cirq.H(qs[3]), ) - wf = circuit.final_wavefunction(qubit_order=qs) + wf = circuit.final_state_vector(qubit_order=qs) z0z1 = cirq.Z(qs[0]) * cirq.Z(qs[1]) * .123 z0z2 = cirq.Z(qs[0]) * cirq.Z(qs[2]) * -1 @@ -1001,11 +1002,11 @@ def test_pauli_string_expectation_from_wavefunction_pure_state_with_coef(): for state in [wf, wf.reshape(2, 2, 2, 2)]: np.testing.assert_allclose( - z0z1.expectation_from_wavefunction(state, q_map), -0.123) + z0z1.expectation_from_state_vector(state, q_map), -0.123) np.testing.assert_allclose( - z0z2.expectation_from_wavefunction(state, q_map), 0) + z0z2.expectation_from_state_vector(state, q_map), 0) np.testing.assert_allclose( - z1x2.expectation_from_wavefunction(state, q_map), 1) + z1x2.expectation_from_state_vector(state, q_map), 1) def test_expectation_from_density_matrix_invalid_input(): @@ -1061,14 +1062,14 @@ def test_expectation_from_density_matrix_invalid_input(): with pytest.raises(ValueError, match='shape'): ps.expectation_from_density_matrix(rho.reshape((-1)), q_map_2) - # Correctly shaped wavefunctions. + # Correctly shaped state_vectors. with pytest.raises(ValueError, match='shape'): ps.expectation_from_density_matrix(np.array([1, 0], dtype=np.complex64), q_map) with pytest.raises(ValueError, match='shape'): ps.expectation_from_density_matrix(wf, q_map) - # The ambiguous cases: Wavefunctions satisfying trace normalization. + # The ambiguous cases: state_vectors satisfying trace normalization. # This also throws an unrelated warning, which is a bug. See #2041. rho_or_wf = 0.25 * np.ones((4, 4), dtype=np.complex64) _ = ps.expectation_from_density_matrix(rho_or_wf, q_map) @@ -1199,8 +1200,8 @@ def test_pauli_string_expectation_from_density_matrix_pure_state(): cirq.X(qubits[3]), cirq.H(qubits[3]), ) - wavefunction = circuit.final_wavefunction(qubit_order=qubits) - rho = np.outer(wavefunction, np.conj(wavefunction)) + state_vector = circuit.final_state_vector(qubit_order=qubits) + rho = np.outer(state_vector, np.conj(state_vector)) z0z1 = cirq.PauliString({qubits[0]: cirq.Z, qubits[1]: cirq.Z}) z0z2 = cirq.PauliString({qubits[0]: cirq.Z, qubits[2]: cirq.Z}) @@ -1237,8 +1238,8 @@ def test_pauli_string_expectation_from_density_matrix_pure_state_with_coef(): cirq.X(qs[3]), cirq.H(qs[3]), ) - wavefunction = circuit.final_wavefunction(qubit_order=qs) - rho = np.outer(wavefunction, np.conj(wavefunction)) + state_vector = circuit.final_state_vector(qubit_order=qs) + rho = np.outer(state_vector, np.conj(state_vector)) z0z1 = cirq.Z(qs[0]) * cirq.Z(qs[1]) * .123 z0z2 = cirq.Z(qs[0]) * cirq.Z(qs[2]) * -1 @@ -1253,13 +1254,13 @@ def test_pauli_string_expectation_from_density_matrix_pure_state_with_coef(): z1x2.expectation_from_density_matrix(state, q_map), 1) -def test_pauli_string_expectation_from_wavefunction_mixed_state_linearity(): +def test_pauli_string_expectation_from_state_vector_mixed_state_linearity(): n_qubits = 6 - wavefunction1 = cirq.testing.random_superposition(2**n_qubits) - wavefunction2 = cirq.testing.random_superposition(2**n_qubits) - rho1 = np.outer(wavefunction1, np.conj(wavefunction1)) - rho2 = np.outer(wavefunction2, np.conj(wavefunction2)) + state_vector1 = cirq.testing.random_superposition(2**n_qubits) + state_vector2 = cirq.testing.random_superposition(2**n_qubits) + rho1 = np.outer(state_vector1, np.conj(state_vector1)) + rho2 = np.outer(state_vector2, np.conj(state_vector2)) density_matrix = rho1 / 2 + rho2 / 2 qubits = cirq.LineQubit.range(n_qubits) @@ -1268,8 +1269,8 @@ def test_pauli_string_expectation_from_wavefunction_mixed_state_linearity(): pauli_string = cirq.PauliString( {q: np.random.choice(paulis) for q in qubits}) - a = pauli_string.expectation_from_wavefunction(wavefunction1, q_map) - b = pauli_string.expectation_from_wavefunction(wavefunction2, q_map) + a = pauli_string.expectation_from_state_vector(state_vector1, q_map) + b = pauli_string.expectation_from_state_vector(state_vector2, q_map) c = pauli_string.expectation_from_density_matrix(density_matrix, q_map) np.testing.assert_allclose(0.5 * (a + b), c) @@ -1540,3 +1541,20 @@ def text(self, to_print): p = FakePrinter() result._repr_pretty_(p, True) assert p.text_pretty == 'cirq.PauliString(...)' + + +def test_deprecated(): + a = cirq.LineQubit(0) + state_vector = np.array([1, 1], dtype=np.complex64) / np.sqrt(2) + with cirq.testing.assert_logs('expectation_from_wavefunction', + 'expectation_from_state_vector', + 'deprecated'): + _ = cirq.PauliString({ + a: 'x' + }).expectation_from_wavefunction(state_vector, {a: 0}) + + with cirq.testing.assert_logs('state', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.PauliString({ + a: 'x' + }).expectation_from_state_vector(state=state_vector, qubit_map={a: 0}) diff --git a/cirq/protocols/apply_mixture_protocol.py b/cirq/protocols/apply_mixture_protocol.py index bdbb901bf05..5d7746ac7ca 100644 --- a/cirq/protocols/apply_mixture_protocol.py +++ b/cirq/protocols/apply_mixture_protocol.py @@ -43,7 +43,7 @@ class ApplyMixtureArgs: """Arguments for performing a mixture of unitaries. The receiving object is expected to mutate `target_tensor` so that it - contains the state (wavefunction or density matrix) after applying the + contains the state (state vector or density matrix) after applying the mixture then return `target_tensor`. Alternatively, if workspace is required, the receiving object can overwrite `out_buffer` with the results and return `out_buffer`. Or, if the receiving object is attempting to @@ -54,7 +54,7 @@ class ApplyMixtureArgs: target_tensor: The input tensor that needs to be left (and potentially right) multiplied and summed, representing the effect of the mixture. The tensor will have the shape (2, 2, 2, ..., 2). It can - correspond to a wavefunction or a density matrix. + correspond to a state vector or a density matrix. out_buffer: Pre-allocated workspace with the same shape and dtype as the target tensor. If buffers are used, the result should end up in this buffer. It is the responsibility of calling code @@ -66,7 +66,7 @@ class ApplyMixtureArgs: left_axes: Which axes to multiply the left action of the mixture upon. right_axes: Which axes to multiply the right action of the mixture upon. If provided we will assume `target_tensor` is a density matrix, - otherwise it will be assuemd `target_tensor` is a wavefunction. + otherwise it will be assumed `target_tensor` is a state vector. """ def __init__(self, @@ -82,7 +82,7 @@ def __init__(self, target_tensor: The input tensor that needs to be left (and potentially right) multiplied and summed, representing the effect of the mixture. The tensor will have the shape - (2, 2, 2, ..., 2). It can correspond to a wavefunction or a + (2, 2, 2, ..., 2). It can correspond to a state vector or a density matrix. out_buffer: Pre-allocated workspace with the same shape and dtype as the target tensor. If buffers are used, the result @@ -97,7 +97,7 @@ def __init__(self, right_axes: Which axes to multiply the right action of the mixture upon. If provided we will assume `target_tensor` is a density matrix, otherwise it will be assuemd `target_tensor` is a - wavefunction. + state vector. """ self.target_tensor = target_tensor self.out_buffer = out_buffer @@ -275,7 +275,7 @@ def err_str(buf_num_str): def _validate_input(val: Any, args: 'ApplyMixtureArgs' ) -> Tuple[Any, 'ApplyMixtureArgs', bool]: """Validate args input and determine if we are operating on a - density matrix or a wavefunction. + density matrix or a state vector. """ is_density_matrix = False diff --git a/cirq/protocols/apply_mixture_protocol_test.py b/cirq/protocols/apply_mixture_protocol_test.py index 81e9b46b4a8..2b9d0fb9c6c 100644 --- a/cirq/protocols/apply_mixture_protocol_test.py +++ b/cirq/protocols/apply_mixture_protocol_test.py @@ -141,7 +141,7 @@ def _apply_mixture_(self, args: cirq.ApplyMixtureArgs): assert_apply_mixture_returns(ReturnsAuxBuffer1(), rho, [0], [1]) -def test_apply_mixture_simple_wavefunction(): +def test_apply_mixture_simple_state_vector(): for _ in range(25): state = cirq.testing.random_superposition(2) u1 = cirq.testing.random_unitary(2) diff --git a/cirq/protocols/json_serialization_test.py b/cirq/protocols/json_serialization_test.py index cd112192880..3320f6ed605 100644 --- a/cirq/protocols/json_serialization_test.py +++ b/cirq/protocols/json_serialization_test.py @@ -323,6 +323,8 @@ def test_mutually_exclusive_blacklist(): 'Timestamp', 'TwoQubitDiagonalGate', 'UnitSweep', + 'StateVectorSimulatorState', + 'StateVectorTrialResult', 'WaveFunctionSimulatorState', 'WaveFunctionTrialResult', 'XmonDevice', diff --git a/cirq/qis/__init__.py b/cirq/qis/__init__.py index 7f338c9a133..9d0d1d60f8c 100644 --- a/cirq/qis/__init__.py +++ b/cirq/qis/__init__.py @@ -15,4 +15,5 @@ validate_indices, validate_qid_shape, validate_normalized_state, + validate_normalized_state_vector, ) diff --git a/cirq/qis/states.py b/cirq/qis/states.py index f343492b801..7c736777031 100644 --- a/cirq/qis/states.py +++ b/cirq/qis/states.py @@ -20,6 +20,8 @@ import numpy as np +from cirq._compat import deprecated, deprecated_parameter + if TYPE_CHECKING: import cirq @@ -32,33 +34,46 @@ np.ndarray, Sequence[Union[int, float, complex]]] -def bloch_vector_from_state_vector(state: Sequence, +@deprecated_parameter( + deadline='v0.10.0', + fix='Use state_vector instead.', + parameter_desc='state', + match=lambda args, kwargs: 'state' in kwargs, + rewrite=lambda args, kwargs: (args, {('state_vector' if k == 'state' else k + ): v for k, v in kwargs.items()})) +def bloch_vector_from_state_vector(state_vector: Sequence, index: int, qid_shape: Optional[Tuple[int, ...]] = None ) -> np.ndarray: """Returns the bloch vector of a qubit. - Calculates the bloch vector of the qubit at index - in the wavefunction given by state, assuming state follows - the standard Kronecker convention of numpy.kron. + Calculates the bloch vector of the qubit at index in the state vector, + assuming state vector follows the standard Kronecker convention of + numpy.kron. Args: - state: A sequence representing a wave function in which + state_vector: A sequence representing a state vector in which the ordering mapping to qubits follows the standard Kronecker - convention of numpy.kron. + convention of numpy.kron (big-endian). index: index of qubit who's bloch vector we want to find. follows the standard Kronecker convention of numpy.kron. + qid_shape: specifies the dimensions of the qudits for the input + `state_vector`. If not specified, qubits are assumed and the + `state_vector` must have a dimension a power of two. + The qudit at `index` must be a qubit. Returns: A length 3 numpy array representing the qubit's bloch vector. Raises: - ValueError: if the size of state is not a power of 2. - ValueError: if the size of the state represents more than 25 qubits. - IndexError: if index is out of range for the number of qubits - corresponding to the state. + ValueError: if the size of `state_vector `is not a power of 2 and the + shape is not given or if the shape is given and `state_vector` has + a size that contradicts this shape. + IndexError: if index is out of range for the number of qubits or qudits + corresponding to `state_vector`. """ - rho = density_matrix_from_state_vector(state, [index], qid_shape=qid_shape) + rho = density_matrix_from_state_vector(state_vector, [index], + qid_shape=qid_shape) v = np.zeros(3, dtype=np.float32) v[0] = 2 * np.real(rho[0][1]) v[1] = 2 * np.imag(rho[1][0]) @@ -67,21 +82,28 @@ def bloch_vector_from_state_vector(state: Sequence, return v +@deprecated_parameter( + deadline='v0.10.0', + fix='Use state_vector instead.', + parameter_desc='state', + match=lambda args, kwargs: 'state' in kwargs, + rewrite=lambda args, kwargs: (args, {('state_vector' if k == 'state' else k + ): v for k, v in kwargs.items()})) def density_matrix_from_state_vector( - state: Sequence, + state_vector: Sequence, indices: Optional[Iterable[int]] = None, qid_shape: Optional[Tuple[int, ...]] = None, ) -> np.ndarray: - r"""Returns the density matrix of the wavefunction. + r"""Returns the density matrix of the state vector. - Calculate the density matrix for the system on the given qubit - indices, with the qubits not in indices that are present in state - traced out. If indices is None the full density matrix for state - is returned. We assume state follows the standard Kronecker - convention of numpy.kron. + Calculate the density matrix for the system on the given qubit indices, + with the qubits not in indices that are present in state vector traced out. + If indices is None the full density matrix for `state_vector` is returned. + We assume `state_vector` follows the standard Kronecker convention of + numpy.kron (big-endian). For example: - state = np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=np.complex64) + state_vector = np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=np.complex64) indices = None gives us @@ -93,66 +115,81 @@ def density_matrix_from_state_vector( $$ Args: - state: A sequence representing a wave function in which + state_vector: A sequence representing a state vector in which the ordering mapping to qubits follows the standard Kronecker - convention of numpy.kron. + convention of numpy.kron (big-endian). indices: list containing indices for qubits that you would like to include in the density matrix (i.e.) qubits that WON'T be traced out. follows the standard Kronecker convention of numpy.kron. + qid_shape: specifies the dimensions of the qudits for the input + `state_vector`. If not specified, qubits are assumed and the + `state_vector` must have a dimension a power of two. Returns: A numpy array representing the density matrix. Raises: - ValueError: if the size of state is not a power of 2. - ValueError: if the size of the state represents more than 25 qubits. + ValueError: if the size of `state_vector` is not a power of 2 and the + shape is not given or if the shape is given and `state_vector` + has a size that contradicts this shape. IndexError: if the indices are out of range for the number of qubits - corresponding to the state. + corresponding to `state_vector`. """ - qid_shape = validate_qid_shape(state, qid_shape) - n_qubits = len(qid_shape) + shape = validate_qid_shape(state_vector, qid_shape) + n_qubits = len(shape) if indices is None: - return np.outer(state, np.conj(state)) + return np.outer(state_vector, np.conj(state_vector)) indices = list(indices) validate_indices(n_qubits, indices) - state = np.asarray(state).reshape(qid_shape) + state_vector = np.asarray(state_vector).reshape(shape) sum_inds = np.array(range(n_qubits)) sum_inds[indices] += n_qubits - rho = np.einsum(state, list(range(n_qubits)), np.conj(state), + rho = np.einsum(state_vector, list(range(n_qubits)), np.conj(state_vector), sum_inds.tolist(), indices + sum_inds[indices].tolist()) - new_shape = np.prod([qid_shape[i] for i in indices], dtype=int) + new_shape = np.prod([shape[i] for i in indices], dtype=int) return rho.reshape((new_shape, new_shape)) -def dirac_notation(state: Sequence, +@deprecated_parameter( + deadline='v0.10.0', + fix='Use state_vector instead.', + parameter_desc='state', + match=lambda args, kwargs: 'state' in kwargs, + rewrite=lambda args, kwargs: (args, {('state_vector' if k == 'state' else k + ): v for k, v in kwargs.items()})) +def dirac_notation(state_vector: Sequence, decimals: int = 2, qid_shape: Optional[Tuple[int, ...]] = None) -> str: - """Returns the wavefunction as a string in Dirac notation. + """Returns the state vector as a string in Dirac notation. For example: - state = np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=np.complex64) - print(dirac_notation(state)) -> 0.71|0⟩ + 0.71|1⟩ + state_vector = np.array([1/np.sqrt(2), 1/np.sqrt(2)], + dtype=np.complex64) + print(dirac_notation(state_vector)) -> 0.71|0⟩ + 0.71|1⟩ Args: - state: A sequence representing a wave function in which the ordering - mapping to qubits follows the standard Kronecker convention of - numpy.kron. + state_vector: A sequence representing a state vector in which + the ordering mapping to qubits follows the standard Kronecker + convention of numpy.kron (big-endian). decimals: How many decimals to include in the pretty print. + qid_shape: specifies the dimensions of the qudits for the input + `state_vector`. If not specified, qubits are assumed and the + `state_vector` must have a dimension a power of two. Returns: A pretty string consisting of a sum of computational basis kets and non-zero floats of the specified accuracy. """ if qid_shape is None: - qid_shape = (2,) * (len(state).bit_length() - 1) + qid_shape = (2,) * (len(state_vector).bit_length() - 1) digit_separator = '' if max(qid_shape, default=0) < 10 else ',' perm_list = [ @@ -163,8 +200,8 @@ def dirac_notation(state: Sequence, ket = "|{}⟩" for x in range(len(perm_list)): format_str = "({:." + str(decimals) + "g})" - val = (round(state[x].real, decimals) + - 1j * round(state[x].imag, decimals)) + val = (round(state_vector[x].real, decimals) + + 1j * round(state_vector[x].imag, decimals)) if round(val.real, decimals) == 0 and round(val.imag, decimals) != 0: val = val.imag @@ -173,8 +210,8 @@ def dirac_notation(state: Sequence, val = val.real format_str = "{:." + str(decimals) + "g}" if val != 0: - if round(state[x].real, decimals) == 1 and \ - round(state[x].imag, decimals) == 0: + if round(state_vector[x].real, decimals) == 1 and \ + round(state_vector[x].imag, decimals) == 0: components.append(ket.format(perm_list[x])) else: components.append((format_str + ket).format(val, perm_list[x])) @@ -194,31 +231,32 @@ def to_valid_state_vector( """Verifies the state_rep is valid and converts it to ndarray form. This method is used to support passing in an integer representing a - computational basis state or a full wave function as a representation of - a state. + computational basis state or a full state vector as a representation of + a pure state. Args: - state_rep: If an int, the state returned is the state corresponding to - a computational basis state. If an numpy array this is the full - wave function. Both of these are validated for the given number - of qubits, and the state must be properly normalized and of the - appropriate dtype. - num_qubits: The number of qubits for the state. The state_rep must be - valid for this number of qubits. - qid_shape: The expected qid shape of the state vector. Specify this + state_rep: If an int, the state vector returned is the state vector + corresponding to a computational basis state. If an numpy array + this is the full state vector. Both of these are validated for + the given number of qubits, and the state must be properly + normalized and of the appropriate dtype. + num_qubits: The number of qubits for the state vector. The state_rep + must be valid for this number of qubits. + qid_shape: The expected qid shape of the state vector. Specify this argument when using qudits. - dtype: The numpy dtype of the state, will be used when creating the - state for a computational basis state, or validated against if + dtype: The numpy dtype of the state vector, will be used when creating + the state for a computational basis state, or validated against if state_rep is a numpy array. atol: Numerical tolerance for verifying that the norm of the state - is close to 1. + vector is close to 1. Returns: - A numpy ndarray corresponding to the state on the given number of + A numpy ndarray corresponding to the state vector on the given number of qubits. Raises: - ValueError if the state is not valid or num_qubits != len(qid_shape). + ValueError: if `state_vector` is not valid or + num_qubits != len(qid_shape). """ # Check shape. @@ -247,7 +285,7 @@ def _state_like_to_state_tensor(*, state_like: 'cirq.STATE_VECTOR_LIKE', atol: float) -> np.ndarray: if isinstance(state_like, int): - return _computational_basis_state_to_state_tensor(state=state_like, + return _computational_basis_state_to_state_tensor(state_rep=state_like, qid_shape=qid_shape, dtype=dtype) @@ -270,13 +308,14 @@ def _state_like_to_state_tensor(*, state_like: 'cirq.STATE_VECTOR_LIKE', 'dtype.') if state_like.shape == (prod,) or state_like.shape == qid_shape: - return _amplitudes_to_validated_state_tensor(state=state_like, - qid_shape=qid_shape, - dtype=dtype, - atol=atol) + return _amplitudes_to_validated_state_tensor( + state_vector=state_like, + qid_shape=qid_shape, + dtype=dtype, + atol=atol) if state_like.shape == (len(qid_shape),): - return _qudit_values_to_state_tensor(state=state_like, + return _qudit_values_to_state_tensor(state_vector=state_like, qid_shape=qid_shape, dtype=dtype) @@ -299,24 +338,24 @@ def _state_like_to_state_tensor(*, state_like: 'cirq.STATE_VECTOR_LIKE', f'qid_shape={qid_shape!r}') -def _amplitudes_to_validated_state_tensor(*, state: np.ndarray, +def _amplitudes_to_validated_state_tensor(*, state_vector: np.ndarray, qid_shape: Tuple[int, ...], dtype: Type[np.number], atol: float) -> np.ndarray: - result = np.array(state, dtype=dtype).reshape(qid_shape) - validate_normalized_state(result, - qid_shape=qid_shape, - dtype=dtype, - atol=atol) + result = np.array(state_vector, dtype=dtype).reshape(qid_shape) + validate_normalized_state_vector(result, + qid_shape=qid_shape, + dtype=dtype, + atol=atol) return result -def _qudit_values_to_state_tensor(*, state: np.ndarray, +def _qudit_values_to_state_tensor(*, state_vector: np.ndarray, qid_shape: Tuple[int, ...], dtype: Type[np.number]) -> np.ndarray: for i in range(len(qid_shape)): - s = state[i] + s = state_vector[i] q = qid_shape[i] if not 0 <= s < q: raise ValueError( @@ -324,71 +363,89 @@ def _qudit_values_to_state_tensor(*, state: np.ndarray, f'qudit dimension {q}.\n' f'\n' f'qid_shape={qid_shape!r}\n' - f'state={state!r}\n') + f'state={state_vector!r}\n') - if state.dtype.kind[0] not in '?bBiu': + if state_vector.dtype.kind[0] not in '?bBiu': raise ValueError(f'Expected a bool or int entry for each qudit in ' f'`state`, because len(state) == len(qid_shape), ' - f'but got dtype {state.dtype}.' + f'but got dtype {state_vector.dtype}.' f'\n' f'qid_shape={qid_shape!r}\n' - f'state={state!r}\n') + f'state={state_vector!r}\n') - return one_hot(index=tuple(int(e) for e in state), + return one_hot(index=tuple(int(e) for e in state_vector), shape=qid_shape, dtype=dtype) -def _computational_basis_state_to_state_tensor(*, state: int, +def _computational_basis_state_to_state_tensor(*, state_rep: int, qid_shape: Tuple[int, ...], dtype: Type[np.number] ) -> np.ndarray: n = np.prod(qid_shape, dtype=int) - if not 0 <= state <= n: + if not 0 <= state_rep <= n: raise ValueError(f'Computational basis state is out of range.\n' f'\n' - f'state={state!r}\n' + f'state={state_rep!r}\n' f'MIN_STATE=0\n' f'MAX_STATE=product(qid_shape)-1={n-1}\n' f'qid_shape={qid_shape!r}\n') - return one_hot(index=state, shape=n, dtype=dtype).reshape(qid_shape) + return one_hot(index=state_rep, shape=n, dtype=dtype).reshape(qid_shape) -def validate_normalized_state( - state: np.ndarray, +def validate_normalized_state_vector( + state_vector: np.ndarray, *, # Force keyword arguments qid_shape: Tuple[int, ...], dtype: Type[np.number] = np.complex64, atol: float = 1e-7) -> None: - """Validates that the given state is a valid wave function.""" - if state.size != np.prod(qid_shape, dtype=int): + """Validates that the given state vector is a valid.""" + if state_vector.size != np.prod(qid_shape, dtype=int): raise ValueError( - 'State has incorrect size. Expected {} but was {}.'.format( - np.prod(qid_shape, dtype=int), state.size)) - if state.dtype != dtype: + 'state_vector has incorrect size. Expected {} but was {}.'.format( + np.prod(qid_shape, dtype=int), state_vector.size)) + if state_vector.dtype != dtype: raise ValueError( - 'State has invalid dtype. Expected {} but was {}'.format( - dtype, state.dtype)) - norm = np.sum(np.abs(state)**2) + 'state_vector has invalid dtype. Expected {} but was {}'.format( + dtype, state_vector.dtype)) + norm = np.sum(np.abs(state_vector)**2) if not np.isclose(norm, 1, atol=atol): - raise ValueError('State is not normalized instead had norm %s' % norm) + raise ValueError( + 'State_vector is not normalized instead had norm {}'.format(norm)) -def validate_qid_shape(state: np.ndarray, +validate_normalized_state = deprecated( + deadline='v0.10.0', + fix='Use `cirq.validate_normalized_state_vector` instead.')( + validate_normalized_state_vector) + + +@deprecated_parameter( + deadline='v0.10.0', + fix='Use state_vector instead.', + parameter_desc='state', + match=lambda args, kwargs: 'state' in kwargs, + rewrite=lambda args, kwargs: (args, {('state_vector' if k == 'state' else k + ): v for k, v in kwargs.items()})) +def validate_qid_shape(state_vector: np.ndarray, qid_shape: Optional[Tuple[int, ...]]) -> Tuple[int, ...]: - """Validates that state's size is either a power of 2 or the product of the - qid shape. + """Validates the size of the given `state_vector` against the given shape. Returns: The qid shape. + + Raises: + ValueError: if the size of `state_vector` does not match that given in + `qid_shape` or if `qid_state` is not given if `state_vector` does + not have a dimension that is a power of two. """ - size = state.size + size = state_vector.size if qid_shape is None: qid_shape = (2,) * (size.bit_length() - 1) if size != np.prod(qid_shape, dtype=int): raise ValueError( - 'state.size ({}) is not a power of two or is not a product of the ' - 'qid shape {!r}.'.format(size, qid_shape)) + 'state_vector.size ({}) is not a power of two or is not a product ' + 'of the qid shape {!r}.'.format(size, qid_shape)) return qid_shape @@ -410,17 +467,17 @@ def to_valid_density_matrix( atol: float = 1e-7) -> np.ndarray: """Verifies the density_matrix_rep is valid and converts it to ndarray form. - This method is used to support passing a matrix, a vector (wave function), + This method is used to support passing a matrix, a state vector, or a computational basis state as a representation of a state. Args: density_matrix_rep: If an numpy array, if it is of rank 2 (a matrix), then this is the density matrix. If it is a numpy array of rank 1 - (a vector) then this is a wave function. If this is an int, + (a vector) then this is a state vector. If this is an int, then this is the computation basis state. num_qubits: The number of qubits for the density matrix. The density_matrix_rep must be valid for this number of qubits. - qid_shape: The qid shape of the state vector. Specify this argument + qid_shape: The qid shape of the state vector. Specify this argument when using qudits. dtype: The numpy dtype of the density matrix, will be used when creating the state for a computational basis state (int), or validated @@ -524,6 +581,6 @@ def eye_tensor( Returns: The created numpy array with shape `half_shape + half_shape`. """ - state = np.eye(np.prod(half_shape, dtype=int), dtype=dtype) - state.shape = half_shape * 2 - return state + identity = np.eye(np.prod(half_shape, dtype=int), dtype=dtype) + identity.shape = half_shape * 2 + return identity diff --git a/cirq/qis/states_test.py b/cirq/qis/states_test.py index 526406ed9f0..e101064bd13 100644 --- a/cirq/qis/states_test.py +++ b/cirq/qis/states_test.py @@ -16,6 +16,7 @@ import pytest import cirq +import cirq.testing def assert_dirac_notation_numpy(vec, expected, decimals=2): @@ -36,46 +37,69 @@ def assert_valid_density_matrix(matrix, num_qubits=None, qid_shape=None): dtype=matrix.dtype), matrix) -def test_bloch_vector_simple_H_zero(): - sqrt = np.sqrt(0.5) - H_state = np.array([sqrt, sqrt]) +@pytest.mark.parametrize('global_phase', (1, 1j, np.exp(1j))) +def test_bloch_vector_zero_state(global_phase): + zero_state = global_phase * np.array([1, 0]) - bloch = cirq.bloch_vector_from_state_vector(H_state, 0) - desired_simple = np.array([1, 0, 0]) + bloch = cirq.bloch_vector_from_state_vector(zero_state, 0) + desired_simple = np.array([0, 0, 1]) np.testing.assert_array_almost_equal(bloch, desired_simple) -def test_bloch_vector_simple_XH_zero(): +@pytest.mark.parametrize('global_phase', (1, 1j, np.exp(1j))) +def test_bloch_vector_one_state(global_phase): + one_state = global_phase * np.array([0, 1]) + + bloch = cirq.bloch_vector_from_state_vector(one_state, 0) + desired_simple = np.array([0, 0, -1]) + np.testing.assert_array_almost_equal(bloch, desired_simple) + + +@pytest.mark.parametrize('global_phase', (1, 1j, np.exp(1j))) +def test_bloch_vector_plus_state(global_phase): sqrt = np.sqrt(0.5) - XH_state = np.array([sqrt, sqrt]) - bloch = cirq.bloch_vector_from_state_vector(XH_state, 0) + plus_state = global_phase * np.array([sqrt, sqrt]) + bloch = cirq.bloch_vector_from_state_vector(plus_state, 0) desired_simple = np.array([1, 0, 0]) np.testing.assert_array_almost_equal(bloch, desired_simple) -def test_bloch_vector_simple_YH_zero(): +@pytest.mark.parametrize('global_phase', (1, 1j, np.exp(1j))) +def test_bloch_vector_minus_state(global_phase): sqrt = np.sqrt(0.5) - YH_state = np.array([-1.0j * sqrt, 1.0j * sqrt]) - bloch = cirq.bloch_vector_from_state_vector(YH_state, 0) + minus_state = np.array([-1.0j * sqrt, 1.0j * sqrt]) + bloch = cirq.bloch_vector_from_state_vector(minus_state, 0) desired_simple = np.array([-1, 0, 0]) np.testing.assert_array_almost_equal(bloch, desired_simple) -def test_bloch_vector_simple_ZH_zero(): +@pytest.mark.parametrize('global_phase', (1, 1j, np.exp(1j))) +def test_bloch_vector_iplus_state(global_phase): sqrt = np.sqrt(0.5) - ZH_state = np.array([sqrt, -sqrt]) - bloch = cirq.bloch_vector_from_state_vector(ZH_state, 0) + iplus_state = global_phase * np.array([sqrt, 1j * sqrt]) - desired_simple = np.array([-1, 0, 0]) + bloch = cirq.bloch_vector_from_state_vector(iplus_state, 0) + desired_simple = np.array([0, 1, 0]) + np.testing.assert_array_almost_equal(bloch, desired_simple) + + +@pytest.mark.parametrize('global_phase', (1, 1j, np.exp(1j))) +def test_bloch_vector_iminus_state(global_phase): + sqrt = np.sqrt(0.5) + iminus_state = global_phase * np.array([sqrt, -1j * sqrt]) + + bloch = cirq.bloch_vector_from_state_vector(iminus_state, 0) + desired_simple = np.array([0, -1, 0]) np.testing.assert_array_almost_equal(bloch, desired_simple) -def test_bloch_vector_simple_TH_zero(): +def test_bloch_vector_simple_th_zero(): sqrt = np.sqrt(0.5) - TH_state = np.array([sqrt, 0.5 + 0.5j]) - bloch = cirq.bloch_vector_from_state_vector(TH_state, 0) + # State TH|0>. + th_state = np.array([sqrt, 0.5 + 0.5j]) + bloch = cirq.bloch_vector_from_state_vector(th_state, 0) desired_simple = np.array([sqrt, sqrt, 0]) np.testing.assert_array_almost_equal(bloch, desired_simple) @@ -91,10 +115,10 @@ def test_bloch_vector_equal_sqrt3(): def test_bloch_vector_multi_pure(): - HH_state = np.array([0.5, 0.5, 0.5, 0.5]) + plus_plus_state = np.array([0.5, 0.5, 0.5, 0.5]) - bloch_0 = cirq.bloch_vector_from_state_vector(HH_state, 0) - bloch_1 = cirq.bloch_vector_from_state_vector(HH_state, 1) + bloch_0 = cirq.bloch_vector_from_state_vector(plus_plus_state, 0) + bloch_1 = cirq.bloch_vector_from_state_vector(plus_plus_state, 1) desired_simple = np.array([1, 0, 0]) np.testing.assert_array_almost_equal(bloch_1, desired_simple) @@ -103,18 +127,19 @@ def test_bloch_vector_multi_pure(): def test_bloch_vector_multi_mixed(): sqrt = np.sqrt(0.5) - HCNOT_state = np.array([sqrt, 0., 0., sqrt]) + # Bell state 1/sqrt(2)(|00>+|11>) + phi_plus = np.array([sqrt, 0., 0., sqrt]) - bloch_0 = cirq.bloch_vector_from_state_vector(HCNOT_state, 0) - bloch_1 = cirq.bloch_vector_from_state_vector(HCNOT_state, 1) + bloch_0 = cirq.bloch_vector_from_state_vector(phi_plus, 0) + bloch_1 = cirq.bloch_vector_from_state_vector(phi_plus, 1) zero = np.zeros(3) np.testing.assert_array_almost_equal(bloch_0, zero) np.testing.assert_array_almost_equal(bloch_1, zero) - RCNOT_state = np.array([0.90612745, -0.07465783j, -0.37533028j, 0.18023996]) - bloch_mixed_0 = cirq.bloch_vector_from_state_vector(RCNOT_state, 0) - bloch_mixed_1 = cirq.bloch_vector_from_state_vector(RCNOT_state, 1) + rcnot_state = np.array([0.90612745, -0.07465783j, -0.37533028j, 0.18023996]) + bloch_mixed_0 = cirq.bloch_vector_from_state_vector(rcnot_state, 0) + bloch_mixed_1 = cirq.bloch_vector_from_state_vector(rcnot_state, 1) true_mixed_0 = np.array([0., -0.6532815, 0.6532815]) true_mixed_1 = np.array([0., 0., 0.9238795]) @@ -124,10 +149,11 @@ def test_bloch_vector_multi_mixed(): def test_bloch_vector_multi_big(): - big_H_state = np.array([0.1767767] * 32) + five_qubit_plus_state = np.array([0.1767767] * 32) desired_simple = np.array([1, 0, 0]) for qubit in range(0, 5): - bloch_i = cirq.bloch_vector_from_state_vector(big_H_state, qubit) + bloch_i = cirq.bloch_vector_from_state_vector(five_qubit_plus_state, + qubit) np.testing.assert_array_almost_equal(bloch_i, desired_simple) @@ -297,21 +323,22 @@ def test_invalid_to_valid_state_vector(): qid_shape=(2, 1)) -def test_check_state(): - cirq.validate_normalized_state(np.array([0.5, 0.5, 0.5, 0.5], - dtype=np.complex64), - qid_shape=(2, 2)) +def test_validate_normalized_state(): + cirq.validate_normalized_state_vector(np.array([0.5, 0.5, 0.5, 0.5], + dtype=np.complex64), + qid_shape=(2, 2)) with pytest.raises(ValueError): - cirq.validate_normalized_state(np.array([1, 1], dtype=np.complex64), - qid_shape=(2, 2)) + cirq.validate_normalized_state_vector(np.array([1, 1], + dtype=np.complex64), + qid_shape=(2, 2)) with pytest.raises(ValueError): - cirq.validate_normalized_state(np.array([1.0, 0.2, 0.0, 0.0], - dtype=np.complex64), - qid_shape=(2, 2)) + cirq.validate_normalized_state_vector(np.array([1.0, 0.2, 0.0, 0.0], + dtype=np.complex64), + qid_shape=(2, 2)) with pytest.raises(ValueError): - cirq.validate_normalized_state(np.array([1.0, 0.0, 0.0, 0.0], - dtype=np.float64), - qid_shape=(2, 2)) + cirq.validate_normalized_state_vector(np.array([1.0, 0.0, 0.0, 0.0], + dtype=np.float64), + qid_shape=(2, 2)) def test_to_valid_density_matrix_from_density_matrix(): @@ -411,7 +438,7 @@ def test_to_valid_density_matrix_wrong_dtype(): dtype=np.complex128) -def test_to_valid_density_matrix_from_state(): +def test_to_valid_density_matrix_from_state_vector(): np.testing.assert_almost_equal( cirq.to_valid_density_matrix(density_matrix_rep=np.array( [1, 0], dtype=np.complex64), @@ -498,3 +525,27 @@ def test_eye_tensor(): [[0, 0], [0, 1], [0, 0]]], [[[0, 0], [0, 0], [1, 0]], [[0, 0], [0, 0], [0, 1]]]])) # yapf: disable + + +def test_deprecated(): + state_vector = np.array([1, 1], dtype=np.complex64) / np.sqrt(2) + with cirq.testing.assert_logs('state', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.bloch_vector_from_state_vector(state=state_vector, index=0) + + with cirq.testing.assert_logs('state', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.density_matrix_from_state_vector(state=state_vector) + + with cirq.testing.assert_logs('state', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.dirac_notation(state=state_vector) + + with cirq.testing.assert_logs('validate_normalized_state', + 'validate_normalized_state_vector', + 'deprecated'): + _ = cirq.validate_normalized_state(state_vector, qid_shape=(2,)) + + with cirq.testing.assert_logs('state', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.validate_qid_shape(state=state_vector, qid_shape=(2,)) diff --git a/cirq/sim/__init__.py b/cirq/sim/__init__.py index bdf79d4a14d..b527cf726e9 100644 --- a/cirq/sim/__init__.py +++ b/cirq/sim/__init__.py @@ -34,6 +34,7 @@ from cirq.sim.mux import ( CIRCUIT_LIKE, final_density_matrix, + final_state_vector, final_wavefunction, sample, sample_sweep, @@ -53,14 +54,18 @@ SparseSimulatorStep, ) -from cirq.sim.wave_function_simulator import ( +from cirq.sim.state_vector_simulator import ( + SimulatesIntermediateStateVector, SimulatesIntermediateWaveFunction, + StateVectorSimulatorState, + StateVectorStepResult, + StateVectorTrialResult, WaveFunctionSimulatorState, WaveFunctionStepResult, WaveFunctionTrialResult, ) -from cirq.sim.wave_function import ( +from cirq.sim.state_vector import ( bloch_vector_from_state_vector, density_matrix_from_state_vector, dirac_notation, diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 4a43f5786aa..56ef51348f2 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -26,7 +26,7 @@ 2. In the CH-form defined by Bravyi et al, 2018 (arXiv:1808.00128). This representation keeps track of overall phase and enables access - to wavefunction amplitudes. + to state vector amplitudes. """ import collections @@ -43,6 +43,7 @@ from cirq.protocols import unitary from cirq.sim import simulator from cirq.sim.clifford import clifford_tableau, stabilizer_state_ch_form +from cirq._compat import deprecated, deprecated_parameter class CliffordSimulator(simulator.SimulatesSamples, @@ -254,7 +255,7 @@ def sample(self, measurements.append( self.state.perform_measurement(qubits, value.parse_random_state(seed), - collapse_wavefunction=False)) + collapse_state_vector=False)) return np.array(measurements, dtype=bool) @@ -266,7 +267,7 @@ class CliffordState(): The state is stored using two complementary representations: Anderson's tableaux form and Bravyi's CH-form. The tableaux keeps track of the stabilizer operations, while the - CH-form allows access to the full wavefunction (including phase). + CH-form allows access to the full state vector (including phase). Gates and measurements are applied to each representation in O(n^2) time. """ @@ -309,7 +310,7 @@ def __repr__(self) -> str: return repr(self.ch_form) def __str__(self) -> str: - """Return the wavefunction string representation of the state.""" + """Return the state vector string representation of the state.""" return str(self.ch_form) def to_numpy(self) -> np.ndarray: @@ -326,8 +327,12 @@ def destabilizers(self) -> List[DensePauliString]: generators above generate the full Pauli group on n qubits.""" return self.tableau.destabilizers() + def state_vector(self): + return self.ch_form.state_vector() + + @deprecated(deadline='v0.10.0', fix='use state_vector instead') def wave_function(self): - return self.ch_form.wave_function() + return self.state_vector() def apply_unitary(self, op: 'cirq.Operation'): if len(op.qubits) == 1: @@ -419,13 +424,21 @@ def _apply_Y(self, qubit: int): self.tableau._Y(qubit) self.ch_form._Y(qubit) + @deprecated_parameter( + deadline='v0.10.0', + fix='Use collapse_state_vector instead.', + parameter_desc='collapse_wavefunction', + match=lambda args, kwargs: 'collapse_wave_function' in kwargs, + rewrite=lambda args, kwargs: (args, {( + 'collapse_state_vector' if k == 'collapse_wave_function' else k): v + for k, v in kwargs.items()})) def perform_measurement(self, qubits: Sequence[ops.Qid], prng: np.random.RandomState, - collapse_wavefunction=True): + collapse_state_vector=True): results = [] - if collapse_wavefunction: + if collapse_state_vector: state = self else: state = self.copy() diff --git a/cirq/sim/clifford/clifford_simulator_test.py b/cirq/sim/clifford/clifford_simulator_test.py index 7d18dbc6649..555da501f6f 100644 --- a/cirq/sim/clifford/clifford_simulator_test.py +++ b/cirq/sim/clifford/clifford_simulator_test.py @@ -3,6 +3,7 @@ import sympy import cirq +import cirq.testing def test_simulate_no_circuit(): @@ -240,11 +241,11 @@ def test_clifford_state_stabilizers(): assert (state.destabilizers() == [f('ZII'), f('IZI'), f('IIX')]) -def test_clifford_state_wave_function(): +def test_clifford_state_state_vector(): (q0, q1) = (cirq.LineQubit(0), cirq.LineQubit(1)) state = cirq.CliffordState(qubit_map={q0: 0, q1: 1}) - np.testing.assert_equal(state.wave_function(), + np.testing.assert_equal(state.state_vector(), [1. + 0.j, 0. + 0.j, 0. + 0.j, 0. + 0.j]) @@ -297,11 +298,11 @@ def test_clifforf_circuit_SHSYSHS(): cirq.S(q0), cirq.H(q0), cirq.S(q0), cirq.measure(q0)) clifford_simulator = cirq.CliffordSimulator() - wave_function_simulator = cirq.Simulator() + state_vector_simulator = cirq.Simulator() np.testing.assert_almost_equal( - clifford_simulator.simulate(circuit).final_state.wave_function(), - wave_function_simulator.simulate(circuit).final_state) + clifford_simulator.simulate(circuit).final_state.state_vector(), + state_vector_simulator.simulate(circuit).final_state_vector) def test_clifford_circuit(): @@ -329,11 +330,11 @@ def test_clifford_circuit(): circuit.append(cirq.CZ(q0, q1)) clifford_simulator = cirq.CliffordSimulator() - wave_function_simulator = cirq.Simulator() + state_vector_simulator = cirq.Simulator() np.testing.assert_almost_equal( - clifford_simulator.simulate(circuit).final_state.wave_function(), - wave_function_simulator.simulate(circuit).final_state) + clifford_simulator.simulate(circuit).final_state.state_vector(), + state_vector_simulator.simulate(circuit).final_state_vector) @pytest.mark.parametrize( @@ -390,11 +391,11 @@ def random_clifford_gate(): circuit.append(random_clifford_gate()(np.random.choice((q0, q1)))) clifford_simulator = cirq.CliffordSimulator() - wave_function_simulator = cirq.Simulator() + state_vector_simulator = cirq.Simulator() np.testing.assert_almost_equal( - clifford_simulator.simulate(circuit).final_state.wave_function(), - wave_function_simulator.simulate(circuit).final_state) + clifford_simulator.simulate(circuit).final_state.state_vector(), + state_vector_simulator.simulate(circuit).final_state_vector) def test_non_clifford_circuit(): @@ -446,7 +447,7 @@ def test_simulate_pauli_string(): cirq.PauliString({q: 'Z'})]) simulator = cirq.CliffordSimulator() - result = simulator.simulate(circuit).final_state.wave_function() + result = simulator.simulate(circuit).final_state.state_vector() assert np.allclose(result, [0, -1]) @@ -458,7 +459,7 @@ def test_simulate_global_phase_operation(): cirq.GlobalPhaseOperation(-1j)]) simulator = cirq.CliffordSimulator() - result = simulator.simulate(circuit).final_state.wave_function() + result = simulator.simulate(circuit).final_state.state_vector() assert np.allclose(result, [-1j, 0, 0, 0]) @@ -487,5 +488,20 @@ def test_json_roundtrip(): assert (state.tableau._str_full_() == state_roundtrip.tableau._str_full_()) # And the CH form isn't changed either. - assert np.allclose(state.ch_form.wave_function(), - state_roundtrip.ch_form.wave_function()) + assert np.allclose(state.ch_form.state_vector(), + state_roundtrip.ch_form.state_vector()) + + +def test_deprecated(): + q = cirq.LineQubit(0) + clifford_state = cirq.CliffordState({q: 0}) + with cirq.testing.assert_logs('wave_function', 'state_vector', + 'deprecated'): + _ = clifford_state.wave_function() + + with cirq.testing.assert_logs('collapse_wave_function', + 'collapse_state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = clifford_state.perform_measurement([q], + prng=0, + collapse_wave_function=True) diff --git a/cirq/sim/clifford/clifford_tableau.py b/cirq/sim/clifford/clifford_tableau.py index 3c6826df151..ec54a917451 100644 --- a/cirq/sim/clifford/clifford_tableau.py +++ b/cirq/sim/clifford/clifford_tableau.py @@ -28,7 +28,7 @@ class CliffordTableau(): the state using three binary arrays: xs, zs, and rs. Each row of the arrays represents a Pauli string, P, that is - an eigenoperator of the wavefunction with eigenvalue one: P|psi> = |psi>. + an eigenoperator of the state vector with eigenvalue one: P|psi> = |psi>. """ def __init__(self, num_qubits, initial_state=0): diff --git a/cirq/sim/clifford/stabilizer_state_ch_form.py b/cirq/sim/clifford/stabilizer_state_ch_form.py index 7e6cdea1440..f5341d7dbf3 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form.py @@ -17,6 +17,7 @@ import cirq from cirq import protocols, value +from cirq._compat import deprecated @value.value_equality @@ -101,7 +102,7 @@ def copy(self) -> 'cirq.StabilizerStateChForm': return copy def __str__(self) -> str: - """Return the wavefunction string representation of the state.""" + """Return the state vector string representation of the state.""" return cirq.dirac_notation(self.to_state_vector()) def __repr__(self) -> str: @@ -110,7 +111,7 @@ def __repr__(self) -> str: def inner_product_of_state_and_x(self, x: int) -> Union[float, complex]: """ Returns the amplitude of x'th element of - the wavefunction, i.e. """ + the state vector, i.e. """ if type(x) == int: y = cirq.big_endian_int_to_bits(x, bit_count=self.n) @@ -124,7 +125,7 @@ def inner_product_of_state_and_x(self, x: int) -> Union[float, complex]: return (self.omega * 2**(-sum(self.v) / 2) * 1j**mu * (-1)**sum(self.v & u & self.s) * np.all(self.v | (u == self.s))) - def wave_function(self) -> np.ndarray: + def state_vector(self) -> np.ndarray: wf = np.zeros(2**self.n, dtype=complex) for x in range(2**self.n): @@ -132,6 +133,10 @@ def wave_function(self) -> np.ndarray: return wf + @deprecated(deadline='v0.10.0', fix='Use state_vector instead.') + def wave_function(self) -> np.ndarray: + return self.state_vector() + def _S(self, q, right=False): if right: self.M[:, q] ^= self.F[:, q] diff --git a/cirq/sim/clifford/stabilizer_state_ch_form_test.py b/cirq/sim/clifford/stabilizer_state_ch_form_test.py new file mode 100644 index 00000000000..620435a0ec7 --- /dev/null +++ b/cirq/sim/clifford/stabilizer_state_ch_form_test.py @@ -0,0 +1,26 @@ +# Copyright 2020 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import cirq +import cirq.testing + +# TODO: This and clifford tableau need tests. +# Github issue: https://github.com/quantumlib/Cirq/issues/3021 + + +def test_deprecated(): + with cirq.testing.assert_logs('wave_function', 'state_vector', + 'deprecated'): + _ = cirq.StabilizerStateChForm(initial_state=0, + num_qubits=1).wave_function() diff --git a/cirq/sim/density_matrix_simulator_test.py b/cirq/sim/density_matrix_simulator_test.py index a29e5f3d2be..afdbcc953e0 100644 --- a/cirq/sim/density_matrix_simulator_test.py +++ b/cirq/sim/density_matrix_simulator_test.py @@ -466,7 +466,7 @@ def test_simulate_qudits(dtype): cirq.testing.random_circuit(cirq.LineQubit.range(4), 5, 0.9) for _ in range(20) ])) -def test_simulate_compare_to_wave_function_simulator(dtype, circuit): +def test_simulate_compare_to_state_vector_simulator(dtype, circuit): qubits = cirq.LineQubit.range(4) pure_result = (cirq.Simulator(dtype=dtype).simulate( circuit, qubit_order=qubits).density_matrix_of()) diff --git a/cirq/sim/mux.py b/cirq/sim/mux.py index ad7bb764862..d90bbeb970a 100644 --- a/cirq/sim/mux.py +++ b/cirq/sim/mux.py @@ -24,8 +24,9 @@ from cirq import circuits, protocols, study, devices, ops, value from cirq._doc import document from cirq.sim import (sparse_simulator, density_matrix_simulator, - wave_function_simulator) + state_vector_simulator) from cirq.sim.clifford import clifford_simulator +from cirq._compat import deprecated if TYPE_CHECKING: import cirq @@ -99,7 +100,7 @@ def _to_circuit(program: 'cirq.CIRCUIT_LIKE') -> 'cirq.Circuit': return cast('cirq.Circuit', result) -def final_wavefunction( +def final_state_vector( program: 'cirq.CIRCUIT_LIKE', *, initial_state: Union[int, Sequence[Union[int, float, complex]], np. @@ -130,7 +131,7 @@ def final_wavefunction( seed: The random seed to use for this simulator. Returns: - The wavefunction resulting from applying the given unitary operations to + 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 @@ -145,7 +146,7 @@ def final_wavefunction( if not protocols.has_unitary( protocols.resolve_parameters(circuit_like, param_resolver)): raise ValueError( - "Program doesn't have a single well defined final wavefunction " + "Program doesn't have a single well defined final state vector " "because it is not unitary. " "Maybe you wanted `cirq.final_density_matrix`?\n" "\n" @@ -160,6 +161,11 @@ def final_wavefunction( return cast(sparse_simulator.SparseSimulatorStep, result).state_vector() +final_wavefunction = deprecated( + deadline='v.0.10.0', + fix='Use `cirq.final_state_vector` instead.')(final_state_vector) + + def sample_sweep(program: 'cirq.Circuit', params: study.Sweepable, *, @@ -215,7 +221,7 @@ def final_density_matrix( ignore_measurement_results: bool = True) -> 'np.ndarray': """Returns the density matrix resulting from simulating the circuit. - Note that, unlike `cirq.final_wavefunction`, terminal measurements + Note that, unlike `cirq.final_state_vector`, terminal measurements are not omitted. Instead, all measurements are treated as sources of decoherence (i.e. measurements do not collapse, they dephase). See ignore_measurement_results for details. @@ -275,7 +281,7 @@ def final_density_matrix( initial_state=initial_state_like, qubit_order=qubit_order, param_resolver=param_resolver) - return cast(wave_function_simulator.WaveFunctionTrialResult, + return cast(state_vector_simulator.StateVectorTrialResult, result).density_matrix_of() else: # noisy case: use DensityMatrixSimulator with dephasing diff --git a/cirq/sim/mux_test.py b/cirq/sim/mux_test.py index 324728074fb..323987c4367 100644 --- a/cirq/sim/mux_test.py +++ b/cirq/sim/mux_test.py @@ -20,6 +20,7 @@ import sympy import cirq +import cirq.testing def test_sample(): @@ -124,100 +125,100 @@ def test_sample_sweep_seed(): assert np.all(results[2].measurements['q'] == [[True], [False]]) -def test_final_wavefunction_different_program_types(): +def test_final_state_vector_different_program_types(): a, b = cirq.LineQubit.range(2) - np.testing.assert_allclose(cirq.final_wavefunction(cirq.X), [0, 1], + np.testing.assert_allclose(cirq.final_state_vector(cirq.X), [0, 1], atol=1e-8) ops = [cirq.H(a), cirq.CNOT(a, b)] np.testing.assert_allclose( - cirq.final_wavefunction(ops), + cirq.final_state_vector(ops), [np.sqrt(0.5), 0, 0, np.sqrt(0.5)], atol=1e-8) np.testing.assert_allclose( - cirq.final_wavefunction(cirq.Circuit(ops)), + cirq.final_state_vector(cirq.Circuit(ops)), [np.sqrt(0.5), 0, 0, np.sqrt(0.5)], atol=1e-8) -def test_final_wavefunction_initial_state(): - np.testing.assert_allclose(cirq.final_wavefunction(cirq.X, initial_state=0), +def test_final_state_vector_initial_state(): + np.testing.assert_allclose(cirq.final_state_vector(cirq.X, initial_state=0), [0, 1], atol=1e-8) - np.testing.assert_allclose(cirq.final_wavefunction(cirq.X, initial_state=1), + np.testing.assert_allclose(cirq.final_state_vector(cirq.X, initial_state=1), [1, 0], atol=1e-8) np.testing.assert_allclose( - cirq.final_wavefunction(cirq.X, + cirq.final_state_vector(cirq.X, initial_state=[np.sqrt(0.5), 1j * np.sqrt(0.5)]), [1j * np.sqrt(0.5), np.sqrt(0.5)], atol=1e-8) -def test_final_wavefunction_dtype_insensitive_to_initial_state(): - assert cirq.final_wavefunction(cirq.X,).dtype == np.complex64 +def test_final_state_vector_dtype_insensitive_to_initial_state(): + assert cirq.final_state_vector(cirq.X,).dtype == np.complex64 - assert cirq.final_wavefunction(cirq.X, + assert cirq.final_state_vector(cirq.X, initial_state=0).dtype == np.complex64 - assert cirq.final_wavefunction(cirq.X, + assert cirq.final_state_vector(cirq.X, initial_state=[np.sqrt(0.5), np.sqrt(0.5) ]).dtype == np.complex64 - assert cirq.final_wavefunction(cirq.X, + assert cirq.final_state_vector(cirq.X, initial_state=np.array( [np.sqrt(0.5), np.sqrt(0.5)])).dtype == np.complex64 for t in [np.int32, np.float32, np.float64, np.complex64]: - assert cirq.final_wavefunction( + assert cirq.final_state_vector( cirq.X, initial_state=np.array([1, 0], dtype=t)).dtype == np.complex64 - assert cirq.final_wavefunction( + assert cirq.final_state_vector( cirq.X, initial_state=np.array([1, 0], dtype=t), dtype=np.complex128).dtype == np.complex128 -def test_final_wavefunction_param_resolver(): +def test_final_state_vector_param_resolver(): s = sympy.Symbol('s') with pytest.raises(ValueError, match='not unitary'): - _ = cirq.final_wavefunction(cirq.X**s) + _ = cirq.final_state_vector(cirq.X**s) np.testing.assert_allclose( - cirq.final_wavefunction(cirq.X**s, param_resolver={s: 0.5}), + cirq.final_state_vector(cirq.X**s, param_resolver={s: 0.5}), [0.5 + 0.5j, 0.5 - 0.5j]) -def test_final_wavefunction_qubit_order(): +def test_final_state_vector_qubit_order(): a, b = cirq.LineQubit.range(2) np.testing.assert_allclose( - cirq.final_wavefunction([cirq.X(a), cirq.X(b)**0.5], qubit_order=[a, + cirq.final_state_vector([cirq.X(a), cirq.X(b)**0.5], qubit_order=[a, b]), [0, 0, 0.5 + 0.5j, 0.5 - 0.5j]) np.testing.assert_allclose( - cirq.final_wavefunction([cirq.X(a), cirq.X(b)**0.5], qubit_order=[b, + cirq.final_state_vector([cirq.X(a), cirq.X(b)**0.5], qubit_order=[b, a]), [0, 0.5 + 0.5j, 0, 0.5 - 0.5j]) -def test_final_wavefunction_seed(): +def test_final_state_vector_seed(): a = cirq.LineQubit(0) - np.testing.assert_allclose(cirq.final_wavefunction( + 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_wavefunction( + 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) @@ -361,3 +362,10 @@ def test_final_density_matrix_noise(): noise=cirq.ConstantQubitNoiseModel(cirq.amplitude_damp(1.0))), [[1, 0], [0, 0]], atol=1e-4) + + +def test_deprecated(): + a = cirq.LineQubit(0) + with cirq.testing.assert_logs('final_wavefunction', 'final_state_vector', + 'deprecated'): + _ = cirq.final_wavefunction([cirq.H(a)]) diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index c56e608fcbc..e62fa0ddd6b 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -183,12 +183,12 @@ def compute_amplitudes_sweep( class SimulatesFinalState(metaclass=abc.ABCMeta): - """Simulator that allows access to a quantum computer's final state. + """Simulator that allows access to the simulator's final state. Implementors of this interface should implement the simulate_sweep method. This simulator only returns the state of the quantum system - for the final step of a simulation. This simulator state may be a wave - function, the density matrix, or another representation, depending on the + for the final step of a simulation. This simulator state may be a state + vector, the density matrix, or another representation, depending on the implementation. For simulators that also allow stepping through a circuit see `SimulatesIntermediateState`. """ @@ -203,7 +203,7 @@ def simulate( """Simulates the supplied Circuit. This method returns a result which allows access to the entire - wave function. + simulator's final state. Args: program: The circuit to simulate. @@ -234,8 +234,8 @@ def simulate_sweep( ) -> List['SimulationTrialResult']: """Simulates the supplied Circuit. - This method returns a result which allows access to the entire - wave function. In contrast to simulate, this allows for sweeping + This method returns a result which allows access to the entire final + simulator state. In contrast to simulate, this allows for sweeping over different parameter values. Args: @@ -258,12 +258,15 @@ def simulate_sweep( class SimulatesIntermediateState(SimulatesFinalState, metaclass=abc.ABCMeta): """A SimulatesFinalState that simulates a circuit by moments. - Whereas a general SimulatesFinalState may return the entire wave - function at the end of a circuit, a SimulatesIntermediateState can + Whereas a general SimulatesFinalState may return the entire simulator + state at the end of a circuit, a SimulatesIntermediateState can simulate stepping through the moments of a circuit. Implementors of this interface should implement the _simulator_iterator method. + + Note that state here refers to simulator state, which is not necessarily + a state vector. """ def simulate_sweep( @@ -276,7 +279,7 @@ def simulate_sweep( """Simulates the supplied Circuit. This method returns a result which allows access to the entire - wave function. In contrast to simulate, this allows for sweeping + state vector. In contrast to simulate, this allows for sweeping over different parameter values. Args: @@ -420,7 +423,7 @@ def sample(self, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None) -> np.ndarray: """Samples from the system at this point in the computation. - Note that this does not collapse the wave function. + Note that this does not collapse the state vector. Args: qubits: The qubits to be sampled in an order that influence the @@ -443,7 +446,7 @@ def sample_measurement_ops(self, ) -> Dict[str, np.ndarray]: """Samples from the system at this point in the computation. - Note that this does not collapse the wave function. + Note that this does not collapse the state vector. In contrast to `sample` which samples qubits, this takes a list of `cirq.GateOperation` instances whose gates are `cirq.MeasurementGate` @@ -500,8 +503,8 @@ class SimulationTrialResult: Unlike TrialResult these results contain the final simulator_state of the system. This simulator_state is dependent on the simulation implementation - and may be, for example, the wave function of the system or the density - matrix of the system. + and may be, for example, the state vector or the density matrix of the + system. Attributes: params: A ParamResolver of settings used for this result. diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 6996c4461e0..1c1d968c6c0 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""A simulator that uses numpy's einsum or sparse matrix operations.""" +"""A simulator that uses numpy's einsum for sparse matrix operations.""" import collections from typing import Dict, Iterator, List, Type, TYPE_CHECKING, DefaultDict @@ -22,8 +22,8 @@ from cirq import circuits, ops, protocols, qis, study, value from cirq.sim import ( simulator, - wave_function, - wave_function_simulator, + state_vector, + state_vector_simulator, act_on_state_vector_args, ) @@ -31,17 +31,9 @@ import cirq -# Mutable named tuple to hold state and a buffer. -class _StateAndBuffer: - - def __init__(self, state: np.ndarray, buffer: np.ndarray): - self.state = state - self.buffer = buffer - - class Simulator(simulator.SimulatesSamples, - wave_function_simulator.SimulatesIntermediateWaveFunction): - """A sparse matrix wave function simulator that uses numpy. + state_vector_simulator.SimulatesIntermediateStateVector): + """A sparse matrix state vector simulator that uses numpy. This simulator can be applied on circuits that are made up of operations that have a `_unitary_` method, or `_has_unitary_` and @@ -57,7 +49,7 @@ class Simulator(simulator.SimulatesSamples, This simulator supports three types of simulation. Run simulations which mimic running on actual quantum hardware. These - simulations do not give access to the wave function (like actual hardware). + simulations do not give access to the state vector (like actual hardware). There are two variations of run methods, one which takes in a single (optional) way to resolve parameterized circuits, and a second which takes in a list or sweep of parameter resolver: @@ -74,12 +66,12 @@ class Simulator(simulator.SimulatesSamples, in the computational basis. By contrast the simulate methods of the simulator give access to the - wave function of the simulation at the end of the simulation of the circuit. + state vector of the simulation at the end of the simulation of the circuit. These methods take in two parameters that the run methods do not: a qubit order and an initial state. The qubit order is necessary because an ordering must be chosen for the kronecker product (see `SparseSimulationTrialResult` for details of this ordering). The initial - state can be either the full wave function, or an integer which represents + state can be either the full state vector, or an integer which represents the initial state of being in a computational basis state for the binary representation of that integer. Similar to run methods, there are two simulate methods that run for single runs or for sweeps across different @@ -97,9 +89,9 @@ class Simulator(simulator.SimulatesSamples, methods. If one wishes to perform simulations that have access to the - wave function as one steps through running the circuit there is a generator + state vector as one steps through running the circuit there is a generator which can be iterated over and each step is an object that gives access - to the wave function. This stepping through a `Circuit` is done on a + to the state vector. This stepping through a `Circuit` is done on a `Moment` by `Moment` manner. simulate_moment_steps(circuit, param_resolver, qubit_order, @@ -108,7 +100,7 @@ class Simulator(simulator.SimulatesSamples, One can iterate over the moments via for step_result in simulate_moments(circuit): - # do something with the wave function via step_result.state + # do something with the state vector via step_result.state_vector Note also that simulations can be stochastic, i.e. return different results for different runs. The first version of this occurs for measurements, @@ -265,8 +257,8 @@ def _check_all_resolved(self, circuit): 'parameter sweep. Ops: {}'.format(unresolved)) -class SparseSimulatorStep(wave_function.StateVectorMixin, - wave_function_simulator.WaveFunctionStepResult): +class SparseSimulatorStep(state_vector.StateVectorMixin, + state_vector_simulator.StateVectorStepResult): """A `StepResult` that includes `StateVectorMixin` methods.""" def __init__(self, state_vector, measurements, qubit_map, dtype): @@ -286,13 +278,12 @@ def __init__(self, state_vector, measurements, qubit_map, dtype): self._state_vector = np.reshape(state_vector, size) def _simulator_state(self - ) -> wave_function_simulator.WaveFunctionSimulatorState: - return wave_function_simulator.WaveFunctionSimulatorState( - qubit_map=self.qubit_map, - state_vector=self._state_vector) + ) -> state_vector_simulator.StateVectorSimulatorState: + return state_vector_simulator.StateVectorSimulatorState( + qubit_map=self.qubit_map, state_vector=self._state_vector) def state_vector(self): - """Return the wave function at this point in the computation. + """Return the state vector at this point in the computation. The state is returned in the computational basis with these basis states defined by the qubit_map. In particular the value in the @@ -332,9 +323,9 @@ def sample(self, repetitions: int = 1, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None) -> np.ndarray: indices = [self.qubit_map[qubit] for qubit in qubits] - return wave_function.sample_state_vector(self._state_vector, - indices, - qid_shape=protocols.qid_shape( - self, None), - repetitions=repetitions, - seed=seed) + return state_vector.sample_state_vector(self._state_vector, + indices, + qid_shape=protocols.qid_shape( + self, None), + repetitions=repetitions, + seed=seed) diff --git a/cirq/sim/sparse_simulator_test.py b/cirq/sim/sparse_simulator_test.py index a6bdf91241d..a2a3d2945b8 100644 --- a/cirq/sim/sparse_simulator_test.py +++ b/cirq/sim/sparse_simulator_test.py @@ -307,7 +307,7 @@ def test_simulate_random_unitary(dtype): for x in range(4): result = simulator.simulate(random_circuit, qubit_order=[q0, q1], initial_state=x) - circuit_unitary.append(result.final_state) + circuit_unitary.append(result.final_state_vector) np.testing.assert_almost_equal( np.transpose(circuit_unitary), random_circuit.unitary(qubit_order=[q0, q1]), @@ -320,7 +320,7 @@ def test_simulate_no_circuit(dtype,): simulator = cirq.Simulator(dtype=dtype) circuit = cirq.Circuit() result = simulator.simulate(circuit, qubit_order=[q0, q1]) - np.testing.assert_almost_equal(result.final_state, + np.testing.assert_almost_equal(result.final_state_vector, np.array([1, 0, 0, 0])) assert len(result.measurements) == 0 @@ -331,7 +331,7 @@ def test_simulate(dtype,): simulator = cirq.Simulator(dtype=dtype) circuit = cirq.Circuit(cirq.H(q0), cirq.H(q1)) result = simulator.simulate(circuit, qubit_order=[q0, q1]) - np.testing.assert_almost_equal(result.final_state, + np.testing.assert_almost_equal(result.final_state_vector, np.array([0.5, 0.5, 0.5, 0.5])) assert len(result.measurements) == 0 @@ -378,7 +378,7 @@ def test_simulate_qudits(dtype,): result = simulator.simulate(circuit, qubit_order=[q0, q1]) expected = np.zeros(12) expected[4 * 1 + 3] = 1 - np.testing.assert_almost_equal(result.final_state, expected) + np.testing.assert_almost_equal(result.final_state_vector, expected) assert len(result.measurements) == 0 @@ -391,11 +391,11 @@ def test_simulate_mixtures(dtype,): for _ in range(100): result = simulator.simulate(circuit, qubit_order=[q0]) if result.measurements['0']: - np.testing.assert_almost_equal(result.final_state, - np.array([0, 1])) + np.testing.assert_almost_equal(result.final_state_vector, + np.array([0, 1])) count += 1 else: - np.testing.assert_almost_equal(result.final_state, + np.testing.assert_almost_equal(result.final_state_vector, np.array([1, 0])) assert count < 80 and count > 20 @@ -412,7 +412,8 @@ def test_simulate_qudit_mixtures(dtype,): meas = result.measurements['0 (d=3)'][0] counts[meas] += 1 np.testing.assert_almost_equal( - result.final_state, np.array([meas == 0, meas == 1, meas == 2])) + result.final_state_vector, + np.array([meas == 0, meas == 1, meas == 2])) assert counts[0] < 160 and counts[0] > 40 assert counts[1] < 160 and counts[1] > 40 assert counts[2] < 160 and counts[2] > 40 @@ -430,7 +431,7 @@ def test_simulate_bit_flips(dtype): np.testing.assert_equal(result.measurements, {'0': [b0], '1': [b1]}) expected_state = np.zeros(shape=(2, 2)) expected_state[b0][b1] = 1.0 - np.testing.assert_equal(result.final_state, + np.testing.assert_equal(result.final_state_vector, np.reshape(expected_state, 4)) @@ -444,7 +445,7 @@ def test_simulate_initial_state(dtype): result = simulator.simulate(circuit, initial_state=1) expected_state = np.zeros(shape=(2, 2)) expected_state[b0][1 - b1] = 1.0 - np.testing.assert_equal(result.final_state, + np.testing.assert_equal(result.final_state_vector, np.reshape(expected_state, 4)) @@ -458,7 +459,7 @@ def test_simulate_qubit_order(dtype): result = simulator.simulate(circuit, qubit_order=[q1, q0]) expected_state = np.zeros(shape=(2, 2)) expected_state[b1][b0] = 1.0 - np.testing.assert_equal(result.final_state, + np.testing.assert_equal(result.final_state_vector, np.reshape(expected_state, 4)) @@ -474,7 +475,7 @@ def test_simulate_param_resolver(dtype): result = simulator.simulate(circuit, param_resolver=resolver) expected_state = np.zeros(shape=(2, 2)) expected_state[b0][b1] = 1.0 - np.testing.assert_equal(result.final_state, + np.testing.assert_equal(result.final_state_vector, np.reshape(expected_state, 4)) assert result.params == cirq.ParamResolver(resolver) assert len(result.measurements) == 0 @@ -506,12 +507,12 @@ def test_simulate_sweeps_param_resolver(dtype): results = simulator.simulate_sweep(circuit, params=params) expected_state = np.zeros(shape=(2, 2)) expected_state[b0][b1] = 1.0 - np.testing.assert_equal(results[0].final_state, + np.testing.assert_equal(results[0].final_state_vector, np.reshape(expected_state, 4)) expected_state = np.zeros(shape=(2, 2)) expected_state[b1][b0] = 1.0 - np.testing.assert_equal(results[1].final_state, + np.testing.assert_equal(results[1].final_state_vector, np.reshape(expected_state, 4)) assert results[0].params == params[0] @@ -539,7 +540,7 @@ def test_simulate_moment_steps_empty_circuit(dtype): step = None for step in simulator.simulate_moment_steps(circuit): pass - assert step._simulator_state() == cirq.WaveFunctionSimulatorState( + assert step._simulator_state() == cirq.StateVectorSimulatorState( state_vector=np.array([1]), qubit_map={}) @@ -671,7 +672,7 @@ 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_wavefunction(), expected) + np.testing.assert_allclose(c.final_state_vector(), expected) np.testing.assert_allclose(cirq.Simulator().simulate(c).state_vector(), expected) diff --git a/cirq/sim/wave_function.py b/cirq/sim/state_vector.py similarity index 75% rename from cirq/sim/wave_function.py rename to cirq/sim/state_vector.py index ec0b7dbb065..bddd4b020a1 100644 --- a/cirq/sim/wave_function.py +++ b/cirq/sim/state_vector.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Helpers for handling quantum wavefunctions.""" +"""Helpers for handling quantum state vectors.""" from typing import (Dict, List, Optional, Tuple, TYPE_CHECKING, Sequence) @@ -20,7 +20,7 @@ from cirq import linalg, ops, qis, value from cirq.sim import simulator -from cirq._compat import deprecated +from cirq._compat import deprecated, deprecated_parameter if TYPE_CHECKING: import cirq @@ -38,8 +38,8 @@ deadline='v0.9', fix='Use cirq.to_valid_state_vector instead.')(qis.to_valid_state_vector) validate_normalized_state = deprecated( - deadline='v0.9', fix='Use cirq.validate_normalized_state instead.')( - qis.validate_normalized_state) + deadline='v0.10', fix='Use cirq.validate_normalized_state_vector instead.')( + qis.validate_normalized_state_vector) STATE_VECTOR_LIKE = qis.STATE_VECTOR_LIKE @@ -48,8 +48,10 @@ class StateVectorMixin(): """ # Reason for 'type: ignore': https://github.com/python/mypy/issues/5887 - def __init__(self, qubit_map: Optional[Dict[ops.Qid, int]] = None, - *args, **kwargs): + def __init__(self, + qubit_map: Optional[Dict[ops.Qid, int]] = None, + *args, + **kwargs): """ Args: qubit_map: A map from the Qubits in the Circuit to the the index @@ -176,8 +178,15 @@ def bloch_vector_of(self, qubit: 'cirq.Qid') -> np.ndarray: qid_shape=self._qid_shape) +@deprecated_parameter( + deadline='v0.10.0', + fix='Use state_vector instead.', + parameter_desc='state', + match=lambda args, kwargs: 'state' in kwargs, + rewrite=lambda args, kwargs: (args, {('state_vector' if k == 'state' else k + ): v for k, v in kwargs.items()})) def sample_state_vector( - state: np.ndarray, + state_vector: np.ndarray, indices: List[int], *, # Force keyword args qid_shape: Optional[Tuple[int, ...]] = None, @@ -188,16 +197,17 @@ def sample_state_vector( Note that this does not modify the passed in state. Args: - state: The multi-qubit wavefunction to be sampled. This is an array of - 2 to the power of the number of qubit complex numbers, and so - state must be of size ``2**integer``. The state can be a vector of - size ``2**integer`` or a tensor of shape ``(2, 2, ..., 2)``. - indices: Which qubits are measured. The state is assumed to be supplied - in big endian order. That is the xth index of v, when expressed as - a bitstring, has its largest values in the 0th index. - qid_shape: The qid shape of the state vector. Specify this argument + state_vector: The multi-qubit state vector to be sampled. This is an + array of 2 to the power of the number of qubit complex numbers, and + so state must be of size ``2**integer``. The `state_vector` can be + a vector of size ``2**integer`` or a tensor of shape + ``(2, 2, ..., 2)``. + indices: Which qubits are measured. The `state_vector` is assumed to be + supplied in big endian order. That is the xth index of v, when + expressed as a bitstring, has its largest values in the 0th index. + qid_shape: The qid shape of the `state_vector`. Specify this argument when using qudits. - repetitions: The number of times to sample the state. + repetitions: The number of times to sample. seed: A seed for the pseudorandom number generator. Returns: @@ -207,16 +217,17 @@ def sample_state_vector( are wrapped as an numpy ndarray. Raises: - ValueError: ``repetitions`` is less than one or size of ``state`` is not - a power of 2. + ValueError: ``repetitions`` is less than one or size of `state_vector` + is not a power of 2. IndexError: An index from ``indices`` is out of range, given the number of qubits corresponding to the state. """ if repetitions < 0: - raise ValueError('Number of repetitions cannot be negative. Was {}' - .format(repetitions)) - qid_shape = qis.validate_qid_shape(state, qid_shape) - num_qubits = len(qid_shape) + raise ValueError( + 'Number of repetitions cannot be negative. Was {}'.format( + repetitions)) + shape = qis.validate_qid_shape(state_vector, qid_shape) + num_qubits = len(shape) qis.validate_indices(num_qubits, indices) if repetitions == 0 or len(indices) == 0: @@ -225,14 +236,14 @@ def sample_state_vector( prng = value.parse_random_state(seed) # Calculate the measurement probabilities. - probs = _probs(state, indices, qid_shape) + probs = _probs(state_vector, indices, shape) # We now have the probability vector, correctly ordered, so sample over # it. Note that we us ints here, since numpy's choice does not allow for # choosing from a list of tuples or list of lists. result = prng.choice(len(probs), size=repetitions, p=probs) # Convert to individual qudit measurements. - meas_shape = tuple(qid_shape[i] for i in indices) + meas_shape = tuple(shape[i] for i in indices) return np.array([ value.big_endian_int_to_digits(result[i], base=meas_shape) for i in range(len(result)) @@ -240,8 +251,15 @@ def sample_state_vector( dtype=np.uint8) +@deprecated_parameter( + deadline='v0.10.0', + fix='Use state_vector instead.', + parameter_desc='state', + match=lambda args, kwargs: 'state' in kwargs, + rewrite=lambda args, kwargs: (args, {('state_vector' if k == 'state' else k + ): v for k, v in kwargs.items()})) def measure_state_vector( - state: np.ndarray, + state_vector: np.ndarray, indices: Sequence[int], *, # Force keyword args qid_shape: Optional[Tuple[int, ...]] = None, @@ -253,75 +271,75 @@ def measure_state_vector( This does not modify `state` unless the optional `out` is `state`. Args: - state: The state to be measured. This state is assumed to be normalized. - The state must be of size 2 ** integer. The state can be of shape - (2 ** integer) or (2, 2, ..., 2). - indices: Which qubits are measured. The state is assumed to be supplied - in big endian order. That is the xth index of v, when expressed as - a bitstring, has the largest values in the 0th index. - qid_shape: The qid shape of the state vector. Specify this argument + state_vector: The state to be measured. This state vector is assumed to + be normalized. The state vector must be of size 2 ** integer. The + state vector can be of shape (2 ** integer) or (2, 2, ..., 2). + indices: Which qubits are measured. The `state_vector` is assumed to be + supplied in big endian order. That is the xth index of v, when + expressed as a bitstring, has the largest values in the 0th index. + qid_shape: The qid shape of the `state_vector`. Specify this argument when using qudits. out: An optional place to store the result. If `out` is the same as - the `state` parameter, then state will be modified inline. If `out` - is not None, then the result is put into `out`. If `out` is None - a new value will be allocated. In all of these case out will be the - same as the returned ndarray of the method. The shape and dtype of - `out` will match that of state if `out` is None, otherwise it will - match the shape and dtype of `out`. + the `state_vector` parameter, then `state_vector` will be modified + inline. If `out` is not None, then the result is put into `out`. + If `out` is None a new value will be allocated. In all of these + case out will be the same as the returned ndarray of the method. + The shape and dtype of `out` will match that of `state_vector` if + `out` is None, otherwise it will match the shape and dtype of `out`. seed: A seed for the pseudorandom number generator. Returns: A tuple of a list and an numpy array. The list is an array of booleans corresponding to the measurement values (ordered by the indices). The - numpy array is the post measurement state. This state has the same - shape and dtype as the input state. + numpy array is the post measurement state vector. This state vector has + the same shape and dtype as the input `state_vector`. Raises: ValueError if the size of state is not a power of 2. IndexError if the indices are out of range for the number of qubits corresponding to the state. """ - qid_shape = qis.validate_qid_shape(state, qid_shape) - num_qubits = len(qid_shape) + shape = qis.validate_qid_shape(state_vector, qid_shape) + num_qubits = len(shape) qis.validate_indices(num_qubits, indices) if len(indices) == 0: if out is None: - out = np.copy(state) - elif out is not state: - np.copyto(dst=out, src=state) + out = np.copy(state_vector) + elif out is not state_vector: + np.copyto(dst=out, src=state_vector) # Final else: if out is state then state will be modified in place. return ([], out) prng = value.parse_random_state(seed) # Cache initial shape. - initial_shape = state.shape + initial_shape = state_vector.shape # Calculate the measurement probabilities and then make the measurement. - probs = _probs(state, indices, qid_shape) + probs = _probs(state_vector, indices, shape) result = prng.choice(len(probs), p=probs) ###measurement_bits = [(1 & (result >> i)) for i in range(len(indices))] # Convert to individual qudit measurements. - meas_shape = tuple(qid_shape[i] for i in indices) + meas_shape = tuple(shape[i] for i in indices) measurement_bits = value.big_endian_int_to_digits(result, base=meas_shape) # Calculate the slice for the measurement result. result_slice = linalg.slice_for_qubits_equal_to( - indices, big_endian_qureg_value=result, qid_shape=qid_shape) + indices, big_endian_qureg_value=result, qid_shape=shape) # Create a mask which is False for only the slice. - mask = np.ones(qid_shape, dtype=bool) + mask = np.ones(shape, dtype=bool) mask[result_slice] = False if out is None: - out = np.copy(state) - elif out is not state: - np.copyto(dst=out, src=state) + out = np.copy(state_vector) + elif out is not state_vector: + np.copyto(dst=out, src=state_vector) # Final else: if out is state then state will be modified in place. # Potentially reshape to tensor, and then set masked values to 0. - out.shape = qid_shape + out.shape = shape out[mask] = 0 # Restore original shape (if necessary) and renormalize. diff --git a/cirq/sim/wave_function_simulator.py b/cirq/sim/state_vector_simulator.py similarity index 60% rename from cirq/sim/wave_function_simulator.py rename to cirq/sim/state_vector_simulator.py index 8e3790b2139..252082a1698 100644 --- a/cirq/sim/wave_function_simulator.py +++ b/cirq/sim/state_vector_simulator.py @@ -11,8 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -"""Abstract classes for simulations which keep track of wave functions.""" +"""Abstract classes for simulations which keep track of state vector.""" import abc @@ -21,29 +20,30 @@ import numpy as np from cirq import circuits, ops, study, value -from cirq.sim import simulator, wave_function +from cirq.sim import simulator, state_vector +from cirq._compat import deprecated if TYPE_CHECKING: import cirq -class SimulatesIntermediateWaveFunction(simulator.SimulatesAmplitudes, - simulator.SimulatesIntermediateState, - metaclass=abc.ABCMeta): - """A simulator that accesses its wave function as it does its simulation. +class SimulatesIntermediateStateVector(simulator.SimulatesAmplitudes, + simulator.SimulatesIntermediateState, + metaclass=abc.ABCMeta): + """A simulator that accesses its state vector as it does its simulation. Implementors of this interface should implement the _simulator_iterator method.""" @abc.abstractmethod def _simulator_iterator( - self, - circuit: circuits.Circuit, - param_resolver: study.ParamResolver, - qubit_order: ops.QubitOrderOrList, - initial_state: np.ndarray, + self, + circuit: circuits.Circuit, + param_resolver: study.ParamResolver, + qubit_order: ops.QubitOrderOrList, + initial_state: np.ndarray, ) -> Iterator: - """Iterator over WaveFunctionStepResult from Moments of a Circuit. + """Iterator over StateVectorStepResult from Moments of a Circuit. Args: circuit: The circuit to simulate. @@ -57,16 +57,16 @@ def _simulator_iterator( documentation of the implementing class for details. Yields: - WaveFunctionStepResult from simulating a Moment of the Circuit. + StateVectorStepResult from simulating a Moment of the Circuit. """ raise NotImplementedError() def _create_simulator_trial_result(self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: 'WaveFunctionSimulatorState') \ - -> 'WaveFunctionTrialResult': - return WaveFunctionTrialResult( + final_simulator_state: 'StateVectorSimulatorState') \ + -> 'StateVectorTrialResult': + return StateVectorTrialResult( params=params, measurements=measurements, final_simulator_state=final_simulator_state) @@ -92,17 +92,27 @@ def compute_amplitudes_sweep( all_amplitudes = [] for trial_result in trial_results: - trial_result = cast(WaveFunctionTrialResult, trial_result) - amplitudes = trial_result.final_state[bitstrings] + trial_result = cast(StateVectorTrialResult, trial_result) + amplitudes = trial_result.final_state_vector[bitstrings] all_amplitudes.append(amplitudes) return all_amplitudes -class WaveFunctionStepResult(simulator.StepResult, metaclass=abc.ABCMeta): +class SimulatesIntermediateWaveFunction(SimulatesIntermediateStateVector): + """Deprecated. Please use `SimulatesIntermediateStateVector` instead.""" + + @deprecated(deadline='v0.10.0', + fix='Use SimulatesIntermediateStateVector instead.', + name='The class SimulatesIntermediateWaveFunction') + def __new__(cls, *args, **kwargs): + return SimulatesIntermediateStateVector.__new__(cls) + + +class StateVectorStepResult(simulator.StepResult, metaclass=abc.ABCMeta): @abc.abstractmethod - def _simulator_state(self) -> 'WaveFunctionSimulatorState': + def _simulator_state(self) -> 'StateVectorSimulatorState': """Returns the simulator_state of the simulator after this step. The form of the simulator_state depends on the implementation of the @@ -112,8 +122,18 @@ def _simulator_state(self) -> 'WaveFunctionSimulatorState': raise NotImplementedError() +class WaveFunctionStepResult(StateVectorStepResult): + """Deprecated. Please use `StateVectorStepResult` instead.""" + + @deprecated(deadline='v0.10.0', + fix='Use StateVectorStepResult instead.', + name='The class WaveFunctionStepResult') + def __new__(cls, *args, **kwargs): + return StateVectorStepResult.__new__(cls) + + @value.value_equality(unhashable=True) -class WaveFunctionSimulatorState: +class StateVectorSimulatorState: def __init__(self, state_vector: np.ndarray, qubit_map: Dict[ops.Qid, int]) -> None: @@ -125,7 +145,7 @@ def _qid_shape_(self) -> Tuple[int, ...]: return self._qid_shape def __repr__(self) -> str: - return ('cirq.WaveFunctionSimulatorState(' + return ('cirq.StateVectorSimulatorState(' f'state_vector=np.{self.state_vector!r}, ' f'qubit_map={self.qubit_map!r})') @@ -133,27 +153,41 @@ def _value_equality_values_(self) -> Any: return (self.state_vector.tolist(), self.qubit_map) +class WaveFunctionSimulatorState(StateVectorSimulatorState): + """Deprecated. Please use `StateVectorSimulatorState` instead.""" + + @deprecated(deadline='v0.10.0', + fix='Use StateVectorSimulatorState instead.', + name='The class WaveFunctionSimulatorState') + def __new__(cls, *args, **kwargs): + return StateVectorSimulatorState.__new__(cls) + + @value.value_equality(unhashable=True) -class WaveFunctionTrialResult(wave_function.StateVectorMixin, - simulator.SimulationTrialResult): +class StateVectorTrialResult(state_vector.StateVectorMixin, + simulator.SimulationTrialResult): """A `SimulationTrialResult` that includes the `StateVectorMixin` methods. Attributes: - final_state: The final wave function of the system. + final_state_vector: The final state vector for the system. """ - def __init__(self, - params: study.ParamResolver, - measurements: Dict[str, np.ndarray], - final_simulator_state: WaveFunctionSimulatorState) -> None: + def __init__(self, params: study.ParamResolver, + measurements: Dict[str, np.ndarray], + final_simulator_state: StateVectorSimulatorState) -> None: super().__init__(params=params, measurements=measurements, final_simulator_state=final_simulator_state, qubit_map=final_simulator_state.qubit_map) - self.final_state = final_simulator_state.state_vector + self.final_state_vector = final_simulator_state.state_vector + + @property # type: ignore + @deprecated(deadline='v0.10.0', fix='Use final_state_vector instead.') + def final_state(self): + return self.final_state_vector def state_vector(self): - """Return the wave function at the end of the computation. + """Return the state vector at the end of the computation. The state is returned in the computational basis with these basis states defined by the qubit_map. In particular the value in the @@ -181,28 +215,39 @@ def state_vector(self): return self._final_simulator_state.state_vector def _value_equality_values_(self): - measurements = {k: v.tolist() for k, v in - sorted(self.measurements.items())} + measurements = { + k: v.tolist() for k, v in sorted(self.measurements.items()) + } return (self.params, measurements, self._final_simulator_state) def __str__(self) -> str: samples = super().__str__() final = self.state_vector() if len([1 for e in final if abs(e) > 0.001]) < 16: - wave = self.dirac_notation(3) + state_vector = self.dirac_notation(3) else: - wave = str(final) - return f'measurements: {samples}\noutput vector: {wave}' + state_vector = str(final) + return f'measurements: {samples}\noutput vector: {state_vector}' def _repr_pretty_(self, p: Any, cycle: bool) -> None: """Text output in Jupyter.""" if cycle: # There should never be a cycle. This is just in case. - p.text('WaveFunctionTrialResult(...)') + p.text('StateVectorTrialResult(...)') else: p.text(str(self)) def __repr__(self) -> str: - return (f'cirq.WaveFunctionTrialResult(params={self.params!r}, ' + return (f'cirq.StateVectorTrialResult(params={self.params!r}, ' f'measurements={self.measurements!r}, ' f'final_simulator_state={self._final_simulator_state!r})') + + +class WaveFunctionTrialResult(StateVectorTrialResult): + """Deprecated. Please use `StateVectorTrialResult` instead.""" + + @deprecated(deadline='v0.10.0', + fix='Use StateVectorTrialResult instead.', + name='The class WaveFunctionTrialResult') + def __new__(cls, *args, **kwargs): + return StateVectorTrialResult.__new__(cls) diff --git a/cirq/sim/state_vector_simulator_test.py b/cirq/sim/state_vector_simulator_test.py new file mode 100644 index 00000000000..43c39a8fcd4 --- /dev/null +++ b/cirq/sim/state_vector_simulator_test.py @@ -0,0 +1,191 @@ +# Copyright 2019 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +import cirq +import cirq.testing + + +def test_state_vector_trial_result_repr(): + final_simulator_state = cirq.StateVectorSimulatorState( + qubit_map={cirq.NamedQubit('a'): 0}, state_vector=np.array([0, 1])) + trial_result = cirq.StateVectorTrialResult( + params=cirq.ParamResolver({'s': 1}), + measurements={'m': np.array([[1]])}, + final_simulator_state=final_simulator_state) + assert repr(trial_result) == ( + "cirq.StateVectorTrialResult(" + "params=cirq.ParamResolver({'s': 1}), " + "measurements={'m': array([[1]])}, " + "final_simulator_state=cirq.StateVectorSimulatorState(" + "state_vector=np.array([0, 1]), " + "qubit_map={cirq.NamedQubit('a'): 0}))") + + +def test_state_vector_simulator_state_repr(): + final_simulator_state = cirq.StateVectorSimulatorState( + qubit_map={cirq.NamedQubit('a'): 0}, state_vector=np.array([0, 1])) + cirq.testing.assert_equivalent_repr(final_simulator_state) + + +def test_state_vector_trial_result_equality(): + eq = cirq.testing.EqualsTester() + eq.add_equality_group( + cirq.StateVectorTrialResult( + params=cirq.ParamResolver({}), + measurements={}, + final_simulator_state=cirq.StateVectorSimulatorState( + np.array([]), {})), + cirq.StateVectorTrialResult( + params=cirq.ParamResolver({}), + measurements={}, + final_simulator_state=cirq.StateVectorSimulatorState( + np.array([]), {}))) + eq.add_equality_group( + cirq.StateVectorTrialResult( + params=cirq.ParamResolver({'s': 1}), + measurements={}, + final_simulator_state=cirq.StateVectorSimulatorState( + np.array([]), {}))) + eq.add_equality_group( + cirq.StateVectorTrialResult( + params=cirq.ParamResolver({'s': 1}), + measurements={'m': np.array([[1]])}, + final_simulator_state=cirq.StateVectorSimulatorState( + np.array([]), {}))) + eq.add_equality_group( + cirq.StateVectorTrialResult( + params=cirq.ParamResolver({'s': 1}), + measurements={'m': np.array([[1]])}, + final_simulator_state=cirq.StateVectorSimulatorState( + np.array([1]), {}))) + + +def test_state_vector_trial_result_state_mixin(): + qubits = cirq.LineQubit.range(2) + qubit_map = {qubits[i]: i for i in range(2)} + result = cirq.StateVectorTrialResult( + params=cirq.ParamResolver({'a': 2}), + measurements={'m': np.array([1, 2])}, + final_simulator_state=cirq.StateVectorSimulatorState( + qubit_map=qubit_map, state_vector=np.array([0, 1, 0, 0]))) + rho = np.array([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) + np.testing.assert_array_almost_equal(rho, result.density_matrix_of(qubits)) + bloch = np.array([0, 0, -1]) + np.testing.assert_array_almost_equal(bloch, + result.bloch_vector_of(qubits[1])) + assert result.dirac_notation() == '|01⟩' + + +def test_state_vector_trial_result_qid_shape(): + final_simulator_state = cirq.StateVectorSimulatorState( + qubit_map={cirq.NamedQubit('a'): 0}, state_vector=np.array([0, 1])) + trial_result = cirq.StateVectorTrialResult( + params=cirq.ParamResolver({'s': 1}), + measurements={'m': np.array([[1]])}, + final_simulator_state=final_simulator_state) + assert cirq.qid_shape(final_simulator_state) == (2,) + assert cirq.qid_shape(trial_result) == (2,) + + q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) + final_simulator_state = cirq.StateVectorSimulatorState( + qubit_map={ + q0: 1, + q1: 0 + }, state_vector=np.array([0, 0, 0, 0, 1, 0])) + trial_result = cirq.StateVectorTrialResult( + params=cirq.ParamResolver({'s': 1}), + measurements={'m': np.array([[2, 0]])}, + final_simulator_state=final_simulator_state) + assert cirq.qid_shape(final_simulator_state) == (3, 2) + assert cirq.qid_shape(trial_result) == (3, 2) + + +def test_str_big(): + qs = cirq.LineQubit.range(20) + result = cirq.StateVectorTrialResult( + cirq.ParamResolver(), {}, + cirq.StateVectorSimulatorState(np.array([1] * 2**10), + {q: q.x for q in qs})) + assert str(result).startswith('measurements: (no measurements)\n' + 'output vector: [1 1 1 ..') + + +def test_pretty_print(): + result = cirq.StateVectorTrialResult( + cirq.ParamResolver(), {}, + cirq.StateVectorSimulatorState(np.array([1]), {})) + + # Test Jupyter console output from + class FakePrinter: + + def __init__(self): + self.text_pretty = '' + + def text(self, to_print): + self.text_pretty += to_print + + p = FakePrinter() + result._repr_pretty_(p, False) + assert p.text_pretty == 'measurements: (no measurements)\noutput vector: |⟩' + + # Test cycle handling + p = FakePrinter() + result._repr_pretty_(p, True) + assert p.text_pretty == 'StateVectorTrialResult(...)' + + +def test_deprecated(): + with cirq.testing.assert_logs('WaveFunctionTrialResult', + 'StateVectorTrialResult', 'deprecated'): + _ = cirq.sim.WaveFunctionTrialResult( + cirq.ParamResolver(), {}, + cirq.StateVectorSimulatorState(np.array([1]), {})) + + with cirq.testing.assert_logs('final_state', 'final_state_vector', + 'deprecated'): + _ = cirq.sim.StateVectorTrialResult( + cirq.ParamResolver(), {}, + cirq.StateVectorSimulatorState(np.array([1]), {})).final_state + + with cirq.testing.assert_logs('WaveFunctionSimulatorState', + 'StateVectorSimulatorState', 'deprecated'): + _ = cirq.sim.WaveFunctionSimulatorState(np.array([1]), {}) + + class TestStepResult(cirq.sim.WaveFunctionStepResult): + + def _simulator_state(self): + pass + + def _simulator_state(self): + pass + + def sample(self, qubits, repetitions, seed): + pass + + with cirq.testing.assert_logs('WaveFunctionStepResult', + 'StateVectorStepResult', 'deprecated'): + _ = TestStepResult() + + class TestSimulatesClass(cirq.sim.SimulatesIntermediateWaveFunction): + + def _simulator_iterator(self, circuit, param_resolver, qubit_order, + initial_state): + pass + + with cirq.testing.assert_logs('SimulatesIntermediateWaveFunction', + 'SimulatesIntermediateStateVector', + 'deprecated'): + _ = TestSimulatesClass() diff --git a/cirq/sim/wave_function_test.py b/cirq/sim/state_vector_test.py similarity index 88% rename from cirq/sim/wave_function_test.py rename to cirq/sim/state_vector_test.py index 204cc8095e7..4f1bce09804 100644 --- a/cirq/sim/wave_function_test.py +++ b/cirq/sim/state_vector_test.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Tests for wave_function.py""" +"""Tests for state_vector.py""" import itertools import pytest @@ -22,36 +22,13 @@ import cirq.testing -def test_deprecated(): - with cirq.testing.assert_logs('cirq.bloch_vector_from_state_vector', - 'deprecated'): - _ = cirq.sim.bloch_vector_from_state_vector(np.array([1, 0]), 0) - - with cirq.testing.assert_logs('cirq.density_matrix_from_state_vector', - 'deprecated'): - _ = cirq.sim.density_matrix_from_state_vector(np.array([1, 0])) - - with cirq.testing.assert_logs('cirq.dirac_notation', 'deprecated'): - _ = cirq.sim.dirac_notation(np.array([1, 0])) - - with cirq.testing.assert_logs('cirq.to_valid_state_vector', 'deprecated'): - _ = cirq.sim.to_valid_state_vector(0, 1) - - with cirq.testing.assert_logs('irq.validate_normalized_state', - 'deprecated'): - _ = cirq.sim.validate_normalized_state(np.array([1, 0], - dtype=np.complex64), - qid_shape=(2,)) - - with cirq.testing.assert_logs('cirq.STATE_VECTOR_LIKE', 'deprecated'): - # Reason for type: ignore: https://github.com/python/mypy/issues/5354 - _ = cirq.sim.STATE_VECTOR_LIKE # type: ignore - - def test_state_mixin(): + class TestClass(cirq.StateVectorMixin): + def state_vector(self) -> np.ndarray: return np.array([0, 0, 1, 0]) + qubits = cirq.LineQubit.range(2) test = TestClass(qubit_map={qubits[i]: i for i in range(2)}) assert test.dirac_notation() == '|10⟩' @@ -83,8 +60,8 @@ def test_sample_state_big_endian(): state = cirq.to_valid_state_vector(x, 3) sample = cirq.sample_state_vector(state, [2, 1, 0]) results.append(sample) - expecteds = [[list(reversed(x))] for x in - list(itertools.product([False, True], repeat=3))] + expecteds = [[list(reversed(x))] + for x in list(itertools.product([False, True], repeat=3))] for result, expected in zip(results, expecteds): np.testing.assert_equal(result, expected) @@ -96,6 +73,7 @@ def test_sample_state_partial_indices(): np.testing.assert_equal(cirq.sample_state_vector(state, [index]), [[bool(1 & (x >> (2 - index)))]]) + def test_sample_state_partial_indices_oder(): for x in range(8): state = cirq.to_valid_state_vector(x, 3) @@ -119,8 +97,8 @@ def test_sample_state(): state[2] = 1 / np.sqrt(2) for _ in range(10): sample = cirq.sample_state_vector(state, [2, 1, 0]) - assert (np.array_equal(sample, [[False, False, False]]) - or np.array_equal(sample, [[False, True, False]])) + assert (np.array_equal(sample, [[False, False, False]]) or + np.array_equal(sample, [[False, True, False]])) # Partial sample is correct. for _ in range(10): np.testing.assert_equal(cirq.sample_state_vector(state, [2]), [[False]]) @@ -130,7 +108,7 @@ def test_sample_state(): def test_sample_empty_state(): state = np.array([1.0]) np.testing.assert_almost_equal(cirq.sample_state_vector(state, []), - np.zeros(shape=(1,0))) + np.zeros(shape=(1, 0))) def test_sample_no_repetitions(): @@ -190,8 +168,8 @@ def test_sample_state_index_out_of_range(): def test_sample_no_indices(): state = cirq.to_valid_state_vector(0, 3) - np.testing.assert_almost_equal( - cirq.sample_state_vector(state, []), np.zeros(shape=(1, 0))) + np.testing.assert_almost_equal(cirq.sample_state_vector(state, []), + np.zeros(shape=(1, 0))) def test_sample_no_indices_repetitions(): @@ -208,8 +186,10 @@ def test_measure_state_computational_basis(): bits, state = cirq.measure_state_vector(initial_state, [2, 1, 0]) results.append(bits) np.testing.assert_almost_equal(state, initial_state) - expected = [list(reversed(x)) for x in - list(itertools.product([False, True], repeat=3))] + expected = [ + list(reversed(x)) + for x in list(itertools.product([False, True], repeat=3)) + ] assert results == expected @@ -220,8 +200,10 @@ def test_measure_state_reshape(): bits, state = cirq.measure_state_vector(initial_state, [2, 1, 0]) results.append(bits) np.testing.assert_almost_equal(state, initial_state) - expected = [list(reversed(x)) for x in - list(itertools.product([False, True], repeat=3))] + expected = [ + list(reversed(x)) + for x in list(itertools.product([False, True], repeat=3)) + ] assert results == expected @@ -364,6 +346,7 @@ def test_measure_state_empty_state(): class BasicStateVector(cirq.StateVectorMixin): + def state_vector(self) -> np.ndarray: return np.array([0, 1, 0, 0]) @@ -377,27 +360,20 @@ def test_step_result_density_matrix(): q0, q1 = cirq.LineQubit.range(2) step_result = BasicStateVector({q0: 0, q1: 1}) - rho = np.array([[0, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 0, 0], - [0, 0, 0, 0]]) - np.testing.assert_array_almost_equal(rho, - step_result.density_matrix_of([q0, q1])) - - np.testing.assert_array_almost_equal(rho, - step_result.density_matrix_of()) - - rho_ind_rev = np.array([[0, 0, 0, 0], - [0, 0, 0, 0], - [0, 0, 1, 0], + rho = np.array([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) + np.testing.assert_array_almost_equal( + rho, step_result.density_matrix_of([q0, q1])) + + np.testing.assert_array_almost_equal(rho, step_result.density_matrix_of()) + + rho_ind_rev = np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) - np.testing.assert_array_almost_equal(rho_ind_rev, - step_result.density_matrix_of([q1, q0])) + np.testing.assert_array_almost_equal( + rho_ind_rev, step_result.density_matrix_of([q1, q0])) - single_rho = np.array([[0, 0], - [0, 1]]) + single_rho = np.array([[0, 0], [0, 1]]) np.testing.assert_array_almost_equal(single_rho, - step_result.density_matrix_of([q1])) + step_result.density_matrix_of([q1])) def test_step_result_density_matrix_invalid(): @@ -419,6 +395,41 @@ def test_step_result_bloch_vector(): bloch1 = np.array([0, 0, -1]) bloch0 = np.array([0, 0, 1]) np.testing.assert_array_almost_equal(bloch1, - step_result.bloch_vector_of(q1)) + step_result.bloch_vector_of(q1)) np.testing.assert_array_almost_equal(bloch0, - step_result.bloch_vector_of(q0)) + step_result.bloch_vector_of(q0)) + + +def test_deprecated(): + with cirq.testing.assert_logs('cirq.bloch_vector_from_state_vector', + 'deprecated'): + _ = cirq.sim.bloch_vector_from_state_vector(np.array([1, 0]), 0) + + with cirq.testing.assert_logs('cirq.density_matrix_from_state_vector', + 'deprecated'): + _ = cirq.sim.density_matrix_from_state_vector(np.array([1, 0])) + + with cirq.testing.assert_logs('cirq.dirac_notation', 'deprecated'): + _ = cirq.sim.dirac_notation(np.array([1, 0])) + + with cirq.testing.assert_logs('cirq.to_valid_state_vector', 'deprecated'): + _ = cirq.sim.to_valid_state_vector(0, 1) + + with cirq.testing.assert_logs('irq.validate_normalized_state', + 'deprecated'): + _ = cirq.sim.validate_normalized_state(np.array([1, 0], + dtype=np.complex64), + qid_shape=(2,)) + + with cirq.testing.assert_logs('cirq.STATE_VECTOR_LIKE', 'deprecated'): + # Reason for type: ignore: https://github.com/python/mypy/issues/5354 + _ = cirq.sim.STATE_VECTOR_LIKE # type: ignore + + state_vector = np.array([1, 1]) / np.sqrt(2) + with cirq.testing.assert_logs('state', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.sample_state_vector(state=state_vector, indices=[0]) + + with cirq.testing.assert_logs('state', 'state_vector', 'deprecated'): + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + _ = cirq.measure_state_vector(state=state_vector, indices=[0]) diff --git a/cirq/sim/wave_function_simulator_test.py b/cirq/sim/wave_function_simulator_test.py deleted file mode 100644 index 58d07103c4b..00000000000 --- a/cirq/sim/wave_function_simulator_test.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright 2019 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np - -import cirq - - -def test_wave_function_trial_result_repr(): - final_simulator_state = cirq.WaveFunctionSimulatorState( - qubit_map={cirq.NamedQubit('a'): 0}, state_vector=np.array([0, 1])) - trial_result = cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({'s': 1}), - measurements={'m': np.array([[1]])}, - final_simulator_state=final_simulator_state) - assert repr(trial_result) == ( - "cirq.WaveFunctionTrialResult(" - "params=cirq.ParamResolver({'s': 1}), " - "measurements={'m': array([[1]])}, " - "final_simulator_state=cirq.WaveFunctionSimulatorState(" - "state_vector=np.array([0, 1]), " - "qubit_map={cirq.NamedQubit('a'): 0}))") - - -def test_wave_function_simulator_state_repr(): - final_simulator_state = cirq.WaveFunctionSimulatorState( - qubit_map={cirq.NamedQubit('a'): 0}, state_vector=np.array([0, 1])) - cirq.testing.assert_equivalent_repr(final_simulator_state) - - -def test_wave_function_trial_result_equality(): - eq = cirq.testing.EqualsTester() - eq.add_equality_group( - cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({}), - measurements={}, - final_simulator_state=cirq.WaveFunctionSimulatorState(np.array([]), - {})), - cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({}), - measurements={}, - final_simulator_state=cirq.WaveFunctionSimulatorState(np.array([]), - {}))) - eq.add_equality_group( - cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({'s': 1}), - measurements={}, - final_simulator_state=cirq.WaveFunctionSimulatorState(np.array([]), - {}))) - eq.add_equality_group( - cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({'s': 1}), - measurements={'m': np.array([[1]])}, - final_simulator_state=cirq.WaveFunctionSimulatorState(np.array([]), - {}))) - eq.add_equality_group( - cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({'s': 1}), - measurements={'m': np.array([[1]])}, - final_simulator_state=cirq.WaveFunctionSimulatorState(np.array([1]), - {}))) - - -def test_wave_function_trial_result_state_mixin(): - qubits = cirq.LineQubit.range(2) - qubit_map = {qubits[i]: i for i in range(2)} - result = cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({'a': 2}), - measurements={'m': np.array([1, 2])}, - final_simulator_state=cirq.WaveFunctionSimulatorState( - qubit_map=qubit_map, state_vector=np.array([0, 1, 0, 0]))) - rho = np.array([[0, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 0, 0], - [0, 0, 0, 0]]) - np.testing.assert_array_almost_equal(rho, - result.density_matrix_of(qubits)) - bloch = np.array([0,0,-1]) - np.testing.assert_array_almost_equal(bloch, - result.bloch_vector_of(qubits[1])) - assert result.dirac_notation() == '|01⟩' - - -def test_wave_function_trial_result_qid_shape(): - final_simulator_state = cirq.WaveFunctionSimulatorState( - qubit_map={cirq.NamedQubit('a'): 0}, state_vector=np.array([0, 1])) - trial_result = cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({'s': 1}), - measurements={'m': np.array([[1]])}, - final_simulator_state=final_simulator_state) - assert cirq.qid_shape(final_simulator_state) == (2,) - assert cirq.qid_shape(trial_result) == (2,) - - q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) - final_simulator_state = cirq.WaveFunctionSimulatorState( - qubit_map={ - q0: 1, - q1: 0 - }, state_vector=np.array([0, 0, 0, 0, 1, 0])) - trial_result = cirq.WaveFunctionTrialResult( - params=cirq.ParamResolver({'s': 1}), - measurements={'m': np.array([[2, 0]])}, - final_simulator_state=final_simulator_state) - assert cirq.qid_shape(final_simulator_state) == (3, 2) - assert cirq.qid_shape(trial_result) == (3, 2) - - -def test_str_big(): - qs = cirq.LineQubit.range(20) - result = cirq.WaveFunctionTrialResult( - cirq.ParamResolver(), {}, - cirq.WaveFunctionSimulatorState(np.array([1] * 2**10), - {q: q.x for q in qs})) - assert str(result).startswith('measurements: (no measurements)\n' - 'output vector: [1 1 1 ..') - - -def test_pretty_print(): - result = cirq.WaveFunctionTrialResult( - cirq.ParamResolver(), {}, - cirq.WaveFunctionSimulatorState(np.array([1]), {})) - - # Test Jupyter console output from - class FakePrinter: - - def __init__(self): - self.text_pretty = '' - - def text(self, to_print): - self.text_pretty += to_print - - p = FakePrinter() - result._repr_pretty_(p, False) - assert p.text_pretty == 'measurements: (no measurements)\noutput vector: |⟩' - - # Test cycle handling - p = FakePrinter() - result._repr_pretty_(p, True) - assert p.text_pretty == 'WaveFunctionTrialResult(...)' diff --git a/dev_tools/profiling/benchmark_simulators.py b/dev_tools/profiling/benchmark_simulators.py index b64fb71ff97..ef7c7dc947a 100644 --- a/dev_tools/profiling/benchmark_simulators.py +++ b/dev_tools/profiling/benchmark_simulators.py @@ -60,7 +60,7 @@ def simulate(sim_type: str, num_qubits: int, num_gates: int) -> None: ]) if sim_type == _UNITARY: - circuit.final_wavefunction(initial_state=0) + circuit.final_state_vector(initial_state=0) elif sim_type == _DENSITY: cirq.DensityMatrixSimulator().run(circuit) diff --git a/docs/api.rst b/docs/api.rst index 3614c71a71a..295fd97dc20 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -223,7 +223,7 @@ results. cirq.big_endian_int_to_bits cirq.big_endian_int_to_digits cirq.final_density_matrix - cirq.final_wavefunction + cirq.final_state_vector cirq.flatten cirq.flatten_to_ops cirq.flatten_to_ops_or_moments @@ -264,20 +264,20 @@ results. cirq.SimulatesAmplitudes cirq.SimulatesFinalState cirq.SimulatesIntermediateState - cirq.SimulatesIntermediateWaveFunction + cirq.SimulatesIntermediateStateVector cirq.SimulatesSamples cirq.SimulationTrialResult cirq.Simulator cirq.SparseSimulatorStep cirq.StateVectorMixin + cirq.StateVectorSimulatorState + cirq.StateVectorStepResult + cirq.StateVectorTrialResult cirq.StepResult cirq.Sweep cirq.Sweepable cirq.TrialResult cirq.UnitSweep - cirq.WaveFunctionSimulatorState - cirq.WaveFunctionStepResult - cirq.WaveFunctionTrialResult cirq.Zip @@ -644,14 +644,14 @@ Algebra and Representation cirq.match_global_phase cirq.matrix_from_basis_coefficients cirq.partial_trace + cirq.partial_trace_of_state_vector_as_mixture cirq.reflection_matrix_pow cirq.slice_for_qubits_equal_to cirq.so4_to_magic_su2s - cirq.subwavefunction + cirq.sub_state_vector cirq.targeted_conjugate_about cirq.targeted_left_multiply cirq.unitary_eig - cirq.wavefunction_partial_trace_as_mixture cirq.AxisAngleDecomposition cirq.Duration cirq.KakDecomposition @@ -673,7 +673,8 @@ Quantum Information Science cirq.one_hot cirq.to_valid_density_matrix cirq.to_valid_state_vector - cirq.validate_normalized_state + cirq.validate_normalized_state_vector + cirq.validate_qid_shape cirq.von_neumann_entropy @@ -691,3 +692,21 @@ important roles in the internal machinery of the library. cirq.LinearCombinationOfOperations cirq.SingleQubitPauliStringGateOperation cirq.TParamVal + + +Deprecated +'''''''''' + +These objects and methods will be removed in a future version of the library. + +.. autosummary:: + :toctree: generated/ + + cirq.final_wavefunction + cirq.subwavefunction + cirq.validate_normalized_state + cirq.wavefunction_partial_trace_as_mixture + cirq.SimulatesIntermediateWaveFunction + cirq.WaveFunctionSimulatorState + cirq.WaveFunctionStepResult + cirq.WaveFunctionTrialResult diff --git a/docs/dev/index.rst b/docs/dev/index.rst index c2ce57b59e0..52cfb13450b 100644 --- a/docs/dev/index.rst +++ b/docs/dev/index.rst @@ -5,5 +5,6 @@ Internal Documentation for Cirq Developers :maxdepth: 1 gates + nomenclature plotting serialization diff --git a/docs/dev/nomenclature.md b/docs/dev/nomenclature.md new file mode 100644 index 00000000000..a33b7393c29 --- /dev/null +++ b/docs/dev/nomenclature.md @@ -0,0 +1,22 @@ +## Nomenclature + +This page describes naming rules and conventions that exist throughout Cirq. +These rules are important in order to maintain a consistent interface that is +easy to use. By using consistent naming we can reduce cognitive load on +users and developers. Please try to use these terms when writing code. + +If you have suggestions for improvements or changes, please create a PR +to modify this list or open an issue. + +### Quantum computing terms + +Use `state_vector` to describe a pure state that is passed in as a numpy +array, numpy tensor, or list of amplitudes. **Do not** use `wavefunction`, +`wave_function`, or `state` for this object (`state` is too overloaded). +If the object is an array or possibly a computational basis state +(given by an `int`), use `state_rep` or, if it is the initial state of +a system `initial_state`. + +Use `density_matrix` to describe a mixed state that is passed in as a numpy +matrix or numpy tensor. **Do not** used `mixed_state`, `density_operator`, or +`state`. \ No newline at end of file diff --git a/docs/tutorials/Rabi_Demo.ipynb b/docs/tutorials/Rabi_Demo.ipynb index 026b763b438..ac80cacec31 100644 --- a/docs/tutorials/Rabi_Demo.ipynb +++ b/docs/tutorials/Rabi_Demo.ipynb @@ -327,7 +327,7 @@ "colab_type": "text" }, "source": [ - "You can also get properties of the circuit, such as the density matrix of the circuit's output or the wavefunction just before the terminal measurement." + "You can also get properties of the circuit, such as the density matrix of the circuit's output or the state vector just before the terminal measurement." ] }, { @@ -342,13 +342,13 @@ "outputId": "feea8b8b-e0ac-4860-d5c8-8cac46b087fe" }, "source": [ - "wavefuntion_before_measurement = sim.simulate(my_circuit[:-1])\n", - "sampled_wavefunction_after_measurement = sim.simulate(my_circuit)\n", + "state_vector_before_measurement = sim.simulate(my_circuit[:-1])\n", + "sampled_state_vector_after_measurement = sim.simulate(my_circuit)\n", "\n", "print(f'State before measurement:')\n", - "print(wavefuntion_before_measurement)\n", + "print(state_vector_before_measurement)\n", "print(f'State after measurement:')\n", - "print(sampled_wavefunction_after_measurement)\n" + "print(sampled_state_vector_after_measurement)\n" ], "execution_count": 6, "outputs": [ diff --git a/examples/bcs_mean_field.py b/examples/bcs_mean_field.py index d00a3882683..47f09ae86a4 100644 --- a/examples/bcs_mean_field.py +++ b/examples/bcs_mean_field.py @@ -317,7 +317,7 @@ def bcs_parameters(n_site, n_fermi, u, t) : hop_erg = hop_erg - fermi_erg def _bcs_gap(x): - """Defines the self-consistent equation for the BCS wavefunction. + """Defines the self-consistent equation for the BCS state vector. Args: x: the superconducting gap diff --git a/examples/direct_fidelity_estimation.py b/examples/direct_fidelity_estimation.py index f73c15da21f..05f50fd1b7e 100644 --- a/examples/direct_fidelity_estimation.py +++ b/examples/direct_fidelity_estimation.py @@ -209,7 +209,7 @@ def _estimate_pauli_traces_clifford(n_qubits: int, pauli_traces: List[PauliTrace] = [] for dense_pauli_string in dense_pauli_strings: # The code below is equivalent to calling - # clifford_state.wave_function() and then calling + # clifford_state.state_vector() and then calling # compute_characteristic_function() on the results (albeit with a # wave function instead of a density matrix). It is, however, # unncessary to do so. Instead we directly obtain the scalar rho_i. diff --git a/examples/quantum_fourier_transform.py b/examples/quantum_fourier_transform.py index f12420356b8..d4d7d764952 100644 --- a/examples/quantum_fourier_transform.py +++ b/examples/quantum_fourier_transform.py @@ -37,7 +37,8 @@ def main(): result = simulator.simulate(qft_circuit) print() print('FinalState') - print(np.around(result.final_state, 3)) + print(np.around(result.final_state_vector, 3)) + def _cz_and_swap(q0, q1, rot): yield cirq.CZ(q0, q1)**rot diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 4df3c179857..843fcb93b25 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -80,7 +80,8 @@ def main(seed=None): print("\nBloch Sphere of Message After Random X and Y Gates:") # Prints the Bloch Sphere of the Message after the X and Y gates. - expected = cirq.bloch_vector_from_state_vector(message.final_state, 0) + expected = cirq.bloch_vector_from_state_vector(message.final_state_vector, + 0) print("x: ", np.around(expected[0], 4), "y: ", np.around(expected[1], 4), "z: ", np.around(expected[2], 4)) @@ -89,8 +90,8 @@ def main(seed=None): print("\nBloch Sphere of Qubit 2 at Final State:") # Prints the Bloch Sphere of Bob's entangled qubit at the final state. - teleported = cirq.bloch_vector_from_state_vector(final_results.final_state, - 2) + teleported = cirq.bloch_vector_from_state_vector( + final_results.final_state_vector, 2) print("x: ", np.around(teleported[0], 4), "y: ", np.around(teleported[1], 4), "z: ", np.around(teleported[2], 4)) diff --git a/style.md b/style.md index 79e432eb54a..d6d9c9bfdac 100644 --- a/style.md +++ b/style.md @@ -69,3 +69,9 @@ Note that if you do this you will need to use the string version of the type, def my_func() -> 'module.that.causes.cycle.MyClass': pass ``` + +#### Nomenclature + +Using consistent wording across Cirq is important for lowering users +cognitive load. For rule governing naming, see the +[nomenclature guidelines](docs/dev/nomenclature.md).