Skip to content
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

Projectors for TensorFlow quantum #4331

Merged
merged 31 commits into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0339829
First retry at projectors
tonybruguier Jul 18, 2021
a5ac063
Delete soon-to-be-useless tests
tonybruguier Jul 18, 2021
e428f58
Delete more useless tests
tonybruguier Jul 18, 2021
3f61ecd
Remove ability to use tuples
tonybruguier Jul 18, 2021
6b13d7e
Restrict to only integer, still TODO to make it sparse
tonybruguier Jul 18, 2021
9a1de0c
Now only using integers
tonybruguier Jul 18, 2021
420d2c7
Some nits
tonybruguier Jul 18, 2021
e419db3
nit
tonybruguier Jul 19, 2021
b798ed7
Skeleton for ProjectorSum
tonybruguier Jul 19, 2021
d4600d7
JSON serialization
tonybruguier Jul 19, 2021
c648299
With multiplication
tonybruguier Jul 19, 2021
60b1846
Operations
tonybruguier Jul 19, 2021
f8fc7b4
Merge branch 'master' of github.com:quantumlib/Cirq into projector_st…
tonybruguier Jul 20, 2021
69965e3
Fixes
tonybruguier Jul 20, 2021
6027dbc
Merge branch 'master' of github.com:quantumlib/Cirq into projector_st…
tonybruguier Jul 20, 2021
6c0da89
Factor out ProjectorSum
tonybruguier Jul 20, 2021
33ff2c9
sparse
tonybruguier Jul 21, 2021
89d2b35
Delete dead code
tonybruguier Jul 21, 2021
fd63c73
Merge branch 'master' of github.com:quantumlib/Cirq into projector_st…
tonybruguier Jul 21, 2021
846af79
Coeff, random state vector in testing
tonybruguier Jul 22, 2021
e8d8374
Only qubits
tonybruguier Jul 23, 2021
df91f9e
Faster expectations
tonybruguier Jul 23, 2021
1cfecaf
docstrings
tonybruguier Jul 23, 2021
20b2284
Merge branch 'master' of github.com:quantumlib/Cirq into projector_st…
tonybruguier Jul 24, 2021
2623804
Fix docstring
tonybruguier Jul 24, 2021
b340c01
Merge branch 'master' of github.com:quantumlib/Cirq into projector_st…
tonybruguier Jul 25, 2021
557a1df
Merge branch 'master' of github.com:quantumlib/Cirq into projector_st…
tonybruguier Jul 26, 2021
c9a0fca
Address some of the comments
tonybruguier Jul 27, 2021
a6071c6
More test
tonybruguier Jul 27, 2021
c06648e
Merge branch 'master' of github.com:quantumlib/Cirq into projector_st…
tonybruguier Jul 28, 2021
dcf1997
Simplify how to get 'idx_to_keep'
tonybruguier Jul 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@
PhasedXPowGate,
PhasedXZGate,
PhaseFlipChannel,
ProjectorString,
RandomGateChannel,
qft,
Qid,
Expand Down
1 change: 1 addition & 0 deletions cirq-core/cirq/json_resolver_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def two_qubit_matrix_gate(matrix):
'PhasedISwapPowGate': cirq.PhasedISwapPowGate,
'PhasedXPowGate': cirq.PhasedXPowGate,
'PhasedXZGate': cirq.PhasedXZGate,
'ProjectorString': cirq.ProjectorString,
'RandomGateChannel': cirq.RandomGateChannel,
'QuantumFourierTransformGate': cirq.QuantumFourierTransformGate,
'RepetitionsStoppingCriteria': cirq.work.RepetitionsStoppingCriteria,
Expand Down
4 changes: 4 additions & 0 deletions cirq-core/cirq/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@
ParallelGateOperation,
)

from cirq.ops.projector import (
ProjectorString,
)

from cirq.ops.controlled_operation import (
ControlledOperation,
)
Expand Down
159 changes: 159 additions & 0 deletions cirq-core/cirq/ops/projector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import itertools
from typing import (
Any,
Dict,
Iterable,
List,
Mapping,
Optional,
Union,
)

import numpy as np
from scipy.sparse import csr_matrix

from cirq import value
from cirq.ops import raw_types


def _check_qids_dimension(qids):
"""A utility to check that we only have Qubits."""
for qid in qids:
if qid.dimension != 2:
raise ValueError(f"Only qubits are supported, but {qid} has dimension {qid.dimension}")


@value.value_equality
class ProjectorString:
def __init__(
self,
projector_dict: Dict[raw_types.Qid, int],
tonybruguier marked this conversation as resolved.
Show resolved Hide resolved
coefficient: Union[int, float, complex] = 1,
):
"""Contructor for ProjectorString

Args:
projector_dict: A python dictionary mapping from cirq.Qid to integers. A key value pair
represents the desired computational basis state for that qubit.
coefficient: Initial scalar coefficient. Defaults to 1.
"""
_check_qids_dimension(projector_dict.keys())
self._projector_dict = projector_dict
self._coefficient = complex(coefficient)

@property
def projector_dict(self) -> Dict[raw_types.Qid, int]:
return self._projector_dict

@property
def coefficient(self) -> complex:
return self._coefficient

def matrix(self, projector_qids: Optional[Iterable[raw_types.Qid]] = None) -> csr_matrix:
"""Returns the matrix of self in computational basis of qubits.

Args:
projector_qids: Ordered collection of qubits that determine the subspace
in which the matrix representation of the ProjectorString is to
be computed. Qbits absent from self.qubits are acted on by
the identity. Defaults to the qubits of the projector_dict.

Returns:
A sparse matrix that is the projection in the specified basis.
"""
projector_qids = self._projector_dict.keys() if projector_qids is None else projector_qids
_check_qids_dimension(projector_qids)
idx_to_keep = [
[self._projector_dict[qid]] if qid in self._projector_dict else [0, 1]
for qid in projector_qids
]

total_d = np.prod([qid.dimension for qid in projector_qids])

ones_idx = []
for idx in itertools.product(*idx_to_keep):
d = total_d
kron_idx = 0
for i, qid in zip(idx, projector_qids):
d //= qid.dimension
kron_idx += i * d
ones_idx.append(kron_idx)

return csr_matrix(
([self._coefficient] * len(ones_idx), (ones_idx, ones_idx)), shape=(total_d, total_d)
)

def _get_idx_to_keep(self, qid_map: Mapping[raw_types.Qid, int]):
num_qubits = len(qid_map)
idx_to_keep: List[Any] = [slice(0, 2)] * num_qubits
for q in self.projector_dict.keys():
idx_to_keep[qid_map[q]] = self.projector_dict[q]
return tuple(idx_to_keep)

def expectation_from_state_vector(
self,
state_vector: np.ndarray,
qid_map: Mapping[raw_types.Qid, int],
) -> complex:
"""Expectation of the projection from a state vector.

Computes the expectation value of this ProjectorString on the provided state vector.

Args:
state_vector: An array representing a valid state vector.
qubit_map: A map from all qubits used in this ProjectorString to the
indices of the qubits that `state_vector` is defined over.
Returns:
The expectation value of the input state.
"""
_check_qids_dimension(qid_map.keys())
num_qubits = len(qid_map)
index = self._get_idx_to_keep(qid_map)
return self._coefficient * np.sum(
np.abs(np.reshape(state_vector, (2,) * num_qubits)[index]) ** 2
)

def expectation_from_density_matrix(
self,
state: np.ndarray,
qid_map: Mapping[raw_types.Qid, int],
) -> complex:
"""Expectation of the projection from a density matrix.

Computes the expectation value of this ProjectorString on the provided state.

Args:
state: An array representing a valid density matrix.
qubit_map: A map from all qubits used in this ProjectorString to the
indices of the qubits that `state_vector` is defined over.
Returns:
The expectation value of the input state.
"""
_check_qids_dimension(qid_map.keys())
num_qubits = len(qid_map)
index = self._get_idx_to_keep(qid_map) * 2
result = np.reshape(state, (2,) * (2 * num_qubits))[index]
while any(result.shape):
result = np.trace(result, axis1=0, axis2=len(result.shape) // 2)
return self._coefficient * result

def __repr__(self) -> str:
return (
f"cirq.ProjectorString(projector_dict={self._projector_dict},"
+ f"coefficient={self._coefficient})"
)

def _json_dict_(self) -> Dict[str, Any]:
return {
'cirq_type': self.__class__.__name__,
'projector_dict': list(self._projector_dict.items()),
'coefficient': self._coefficient,
}

@classmethod
def _from_json_dict_(cls, projector_dict, coefficient, **kwargs):
return cls(projector_dict=dict(projector_dict), coefficient=coefficient)

def _value_equality_values_(self) -> Any:
projector_dict = sorted(self._projector_dict.items())
return (tuple(projector_dict), self._coefficient)