-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Allow simulator quantum state representations to be used in qis #5265
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
Changes from all commits
65bc838
fdddbf9
15c4413
2ffa3ae
4dc7d3a
468eeb5
cb07017
abbbbfe
2be0f13
e4e1924
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,8 +13,9 @@ | |
# limitations under the License. | ||
"""Classes and methods for quantum states.""" | ||
|
||
from typing import Any, cast, Iterable, List, Optional, Sequence, Set, TYPE_CHECKING, Tuple, Union | ||
import abc | ||
import itertools | ||
from typing import Any, cast, Iterable, List, Optional, Sequence, Set, TYPE_CHECKING, Tuple, Union | ||
|
||
import numpy as np | ||
|
||
|
@@ -46,14 +47,70 @@ | |
# density matrix | ||
np.ndarray, | ||
# quantum state object | ||
'cirq.QuantumState', | ||
'HasQuantumState', | ||
] | ||
document(QUANTUM_STATE_LIKE, """An object representing a quantum state.""") # type: ignore | ||
|
||
|
||
class QuantumState: | ||
"""A quantum state. | ||
class HasQuantumState(abc.ABC): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mixed feelings on this name - I'd expect "Has..." to be a method, intuitively. Is |
||
def __init__(self, qid_shape: Tuple[int, ...] = ()): | ||
self._qid_shape = qid_shape | ||
self._dim = np.prod(self.qid_shape, dtype=np.int64).item() | ||
|
||
@property | ||
def qid_shape(self) -> Tuple[int, ...]: | ||
"""The qid shape of the quantum state.""" | ||
return self._qid_shape | ||
|
||
def state_vector(self) -> Optional[np.ndarray]: | ||
"""Return the state vector of this state. | ||
|
||
A state vector stores the amplitudes of a pure state as a | ||
one-dimensional array. | ||
If the state is a density matrix, this method returns None. | ||
""" | ||
return None | ||
|
||
def state_tensor(self) -> Optional[np.ndarray]: | ||
"""Return the state tensor of this state. | ||
|
||
A state tensor stores the amplitudes of a pure state as an array with | ||
shape equal to the qid shape of the state. | ||
If the state is a density matrix, this method returns None. | ||
""" | ||
state_vector = self.state_vector() | ||
if state_vector is None: | ||
return None | ||
return np.reshape(state_vector, self.qid_shape) | ||
|
||
def density_matrix(self) -> np.ndarray: | ||
"""Return the density matrix of this state. | ||
|
||
A density matrix stores the entries of a density matrix as a matrix | ||
(a two-dimensional array). | ||
""" | ||
state_vector = self.state_vector() | ||
return np.outer(state_vector, np.conj(state_vector)) | ||
|
||
def state_vector_or_density_matrix(self) -> np.ndarray: | ||
"""Return the state vector or density matrix of this state. | ||
|
||
If the state is a denity matrix, return the density matrix. Otherwise, return the state | ||
vector. | ||
""" | ||
state_vector = self.state_vector() | ||
if state_vector is not None: | ||
return state_vector | ||
return self.density_matrix() | ||
|
||
@property | ||
def can_represent_mixed_states(self) -> bool: | ||
"""Whether this quantum state is a density matrix.""" | ||
return False | ||
|
||
|
||
class QuantumState(HasQuantumState): | ||
"""A quantum state. | ||
Can be a state vector, a state tensor, or a density matrix. | ||
""" | ||
|
||
|
@@ -83,9 +140,8 @@ def __init__( | |
""" | ||
if qid_shape is None: | ||
qid_shape = infer_qid_shape(data) | ||
super().__init__(qid_shape) | ||
self._data = data | ||
self._qid_shape = qid_shape | ||
self._dim = np.prod(self.qid_shape, dtype=np.int64).item() | ||
if validate: | ||
self.validate(dtype=dtype, atol=atol) | ||
|
||
|
@@ -94,11 +150,6 @@ def data(self) -> np.ndarray: | |
"""The data underlying the quantum state.""" | ||
return self._data | ||
|
||
@property | ||
def qid_shape(self) -> Tuple[int, ...]: | ||
"""The qid shape of the quantum state.""" | ||
return self._qid_shape | ||
|
||
@property | ||
def dtype(self) -> np.dtype: | ||
"""The data type of the quantum state.""" | ||
|
@@ -115,38 +166,14 @@ def state_vector(self) -> Optional[np.ndarray]: | |
return None | ||
return np.reshape(self.data, (self._dim,)) | ||
|
||
def state_tensor(self) -> Optional[np.ndarray]: | ||
"""Return the state tensor of this state. | ||
|
||
A state tensor stores the amplitudes of a pure state as an array with | ||
shape equal to the qid shape of the state. | ||
If the state is a density matrix, this method returns None. | ||
""" | ||
if self._is_density_matrix(): | ||
return None | ||
return np.reshape(self.data, self.qid_shape) | ||
|
||
def density_matrix(self) -> np.ndarray: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keeping this here seems wrong - density matrices should override it, and everything else can call the parent method. |
||
"""Return the density matrix of this state. | ||
|
||
A density matrix stores the entries of a density matrix as a matrix | ||
(a two-dimensional array). | ||
""" | ||
if not self._is_density_matrix(): | ||
state_vector = self.state_vector() | ||
assert state_vector is not None, 'only None if _is_density_matrix' | ||
return np.outer(state_vector, np.conj(state_vector)) | ||
return self.data | ||
|
||
def state_vector_or_density_matrix(self) -> np.ndarray: | ||
"""Return the state vector or density matrix of this state. | ||
|
||
If the state is a denity matrix, return the density matrix. Otherwise, return the state | ||
vector. | ||
""" | ||
state_vector = self.state_vector() | ||
if state_vector is not None: | ||
return state_vector | ||
return super().density_matrix() | ||
return self.data | ||
|
||
def _is_density_matrix(self) -> bool: | ||
|
@@ -184,6 +211,11 @@ def validate( | |
f'compatible with qid shape of {self.qid_shape}.' | ||
) | ||
|
||
@property | ||
def can_represent_mixed_states(self) -> bool: | ||
"""Whether this quantum state is a density matrix.""" | ||
return self._is_density_matrix() | ||
|
||
|
||
def quantum_state( | ||
state: 'cirq.QUANTUM_STATE_LIKE', | ||
|
@@ -263,6 +295,8 @@ def quantum_state( | |
dtype = DEFAULT_COMPLEX_DTYPE | ||
data = one_hot(index=state, shape=(dim,), dtype=dtype) | ||
else: | ||
if isinstance(state, HasQuantumState): | ||
state = state.state_vector_or_density_matrix() | ||
data = np.array(state, copy=False) | ||
if qid_shape is None: | ||
qid_shape = infer_qid_shape(state) | ||
|
@@ -344,7 +378,7 @@ def _infer_qid_shape_from_dimension(dim: int) -> Tuple[int, ...]: | |
# Product state object | ||
'cirq.ProductState', | ||
# Quantum state object | ||
'cirq.QuantumState', | ||
'HasQuantumState', | ||
] | ||
|
||
|
||
|
@@ -410,10 +444,8 @@ def infer_qid_shape(*states: 'cirq.QUANTUM_STATE_LIKE') -> Tuple[int, ...]: | |
|
||
def _potential_qid_shapes(state: _NON_INT_STATE_LIKE) -> '_QidShapeSet': | ||
"""Return a set of qid shapes compatible with a given state.""" | ||
if isinstance(state, QuantumState): | ||
if isinstance(state, HasQuantumState): | ||
return _QidShapeSet(explicit_qid_shapes={state.qid_shape}) | ||
if isinstance(state, value.ProductState): | ||
return _QidShapeSet(explicit_qid_shapes={(2,) * len(state)}) | ||
|
||
if isinstance(state, Sequence): | ||
state = np.array(state) | ||
|
Uh oh!
There was an error while loading. Please reload this page.