Skip to content

Consistent final_state_vector #5267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 9, 2022
1 change: 1 addition & 0 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
dephase_measurements,
drop_empty_moments,
drop_negligible_operations,
drop_terminal_measurements,
eject_phased_paulis,
eject_z,
expand_composite,
Expand Down
127 changes: 67 additions & 60 deletions cirq-core/cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,89 +1041,96 @@ def _superoperator_(self) -> np.ndarray:
circuit_superoperator = moment_superoperator @ circuit_superoperator
return circuit_superoperator

@_compat.deprecated_parameter(
deadline='v0.16',
fix='Inject identity operators to include untouched qubits.',
parameter_desc='qubits_that_should_be_present',
match=lambda args, kwargs: 'qubits_that_should_be_present' in kwargs,
)
@_compat.deprecated_parameter(
deadline='v0.16',
fix='Only use keyword arguments.',
parameter_desc='positional args',
match=lambda args, kwargs: len(args) > 1,
)
def final_state_vector(
self,
# TODO(v0.16): Force kwargs and match order found in:
# cirq-core/cirq/sim/mux.py:final_state_vector
initial_state: 'cirq.STATE_VECTOR_LIKE' = 0,
qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT,
qubits_that_should_be_present: Iterable['cirq.Qid'] = (),
ignore_terminal_measurements: bool = True,
dtype: Type[np.number] = np.complex128,
ignore_terminal_measurements: Optional[bool] = None,
dtype: Optional[Type[np.number]] = None,
param_resolver: 'cirq.ParamResolverOrSimilarType' = None,
seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
) -> np.ndarray:
"""Left-multiplies a state vector by the circuit's unitary effect.

A circuit's "unitary effect" is the unitary matrix produced by
multiplying together all of its gates' unitary matrices. A circuit
with non-unitary gates (such as measurement or parameterized gates) does
not have a well-defined unitary effect, and the method will fail if such
operations are present.
"""Returns the state vector resulting from acting operations on a state.

For convenience, terminal measurements are automatically ignored
instead of causing a failure. Set the `ignore_terminal_measurements`
argument to False to disable this behavior.

This method is equivalent to left-multiplying the input state by
`cirq.unitary(circuit)` but it's computed in a more efficient
way.
This is equivalent to calling cirq.final_state_vector with the same
arguments and this circuit as the "program".

Args:
initial_state: The input state for the circuit. This can be a list
of qudit values, a big endian int encoding the qudit values,
a vector of amplitudes, or a tensor of amplitudes.

When this is an int, it refers to a computational
basis state (e.g. 5 means initialize to ``|5⟩ = |...000101⟩``).

If this is a vector of amplitudes (a flat numpy array of the
correct length for the system) or a tensor of amplitudes (a
numpy array whose shape equals this circuit's `qid_shape`), it
directly specifies the initial state's amplitudes. The vector
type must be convertible to the given `dtype` argument.
qubit_order: Determines how qubits are ordered when passing matrices
into np.kron.
initial_state: If an int, the state is set to the computational
basis state corresponding to this state. Otherwise if this
is a np.ndarray it is the full initial state. In this case it
must be the correct size, be normalized (an L2 norm of 1), and
be safely castable to an appropriate dtype for the simulator.
qubit_order: Determines the canonical ordering of the qubits. This
is often used in specifying the initial state, i.e. the
ordering of the computational basis states.
qubits_that_should_be_present: Qubits that may or may not appear
in operations within the circuit, but that should be included
regardless when generating the matrix.
ignore_terminal_measurements: When set, measurements at the end of
the circuit are ignored instead of causing the method to
fail.
dtype: The numpy dtype for the returned unitary. Defaults to
np.complex128. Specifying np.complex64 will run faster at the
cost of precision. `dtype` must be a complex np.dtype, unless
all operations in the circuit have unitary matrices with
exclusively real coefficients (e.g. an H + TOFFOLI circuit).
dtype: The `numpy.dtype` used by the simulation. Typically one of
`numpy.complex64` or `numpy.complex128`.
param_resolver: Parameters to run with the program.
seed: The random seed to use for this simulator.

Returns:
A (possibly gigantic) numpy array storing the superposition that
came out of the circuit for the given input state.
The state vector resulting from applying the given unitary
operations to the desired initial state. Specifically, a numpy
array containing the amplitudes in np.kron order, where the
order of arguments to kron is determined by the qubit order
argument (which defaults to just sorting the qubits that are
present into an ascending order).

Raises:
ValueError: The circuit contains measurement gates that are not
ignored.
TypeError: The circuit contains gates that don't have a known
unitary matrix, e.g. gates parameterized by a Symbol.
ValueError: If the program doesn't have a well defined final state
because it has non-unitary gates.
"""
if ignore_terminal_measurements is None:
if self.has_measurements():
_compat._warn_or_error(
'`ignore_terminal_measurements` will default to False in v0.16. '
'To drop terminal measurements, please explicitly include '
'`ignore_terminal_measurements=True` when calling this method.'
)
ignore_terminal_measurements = True

if dtype is None:
_compat._warn_or_error(
'`dtype` will default to np.complex64 in v0.16. '
'To use the previous default, please explicitly include '
'`dtype=np.complex128` when calling this method.'
)
dtype = np.complex128

if not ignore_terminal_measurements and any(
protocols.is_measurement(op) for op in self.all_operations()
):
raise ValueError('Circuit contains a measurement.')

if not self.are_all_measurements_terminal():
raise ValueError('Circuit contains a non-terminal measurement.')

qs = ops.QubitOrder.as_qubit_order(qubit_order).order_for(
self.all_qubits().union(qubits_that_should_be_present)
)

# Force qubits to have dimension at least 2 for backwards compatibility.
qid_shape = self.qid_shape(qubit_order=qs)
state_len = np.prod(qid_shape, dtype=np.int64)
from cirq.sim.mux import final_state_vector

state = qis.to_valid_state_vector(initial_state, qid_shape=qid_shape, dtype=dtype).reshape(
qid_shape
program = Circuit(cirq.I(q) for q in qubits_that_should_be_present) + self
return final_state_vector(
program,
initial_state=initial_state,
param_resolver=param_resolver,
qubit_order=qubit_order,
ignore_terminal_measurements=ignore_terminal_measurements,
dtype=dtype,
seed=seed,
)
result = _apply_unitary_circuit(self, state, qs, dtype)
return result.reshape((state_len,))

def to_text_diagram(
self,
Expand Down
Loading