From 765393f9e2e6e87e53f926a2bb0082e0df049fea Mon Sep 17 00:00:00 2001 From: "Antoine (Tony) Bruguier" Date: Wed, 18 Aug 2021 19:07:44 -0700 Subject: [PATCH] ProjectorSum object for TFQ (#4364) This is a follow-up on #4331 which introduced ProjectorString objects. The present PR aims at adding ProjectorSum objects. --- cirq-core/cirq/__init__.py | 1 + cirq-core/cirq/json_resolver_cache.py | 1 + cirq-core/cirq/ops/__init__.py | 1 + cirq-core/cirq/ops/linear_combinations.py | 210 +++++++++++++ .../cirq/ops/linear_combinations_test.py | 285 ++++++++++++++++++ .../json_test_data/ProjectorSum.json | 21 ++ .../json_test_data/ProjectorSum.repr | 1 + 7 files changed, 520 insertions(+) create mode 100644 cirq-core/cirq/protocols/json_test_data/ProjectorSum.json create mode 100644 cirq-core/cirq/protocols/json_test_data/ProjectorSum.repr diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py index 2e5d1a9d96f..da6797ca6e6 100644 --- a/cirq-core/cirq/__init__.py +++ b/cirq-core/cirq/__init__.py @@ -256,6 +256,7 @@ PhasedXZGate, PhaseFlipChannel, ProjectorString, + ProjectorSum, RandomGateChannel, qft, Qid, diff --git a/cirq-core/cirq/json_resolver_cache.py b/cirq-core/cirq/json_resolver_cache.py index 18269df99c3..86b873fdb60 100644 --- a/cirq-core/cirq/json_resolver_cache.py +++ b/cirq-core/cirq/json_resolver_cache.py @@ -121,6 +121,7 @@ def two_qubit_matrix_gate(matrix): 'PhasedXPowGate': cirq.PhasedXPowGate, 'PhasedXZGate': cirq.PhasedXZGate, 'ProjectorString': cirq.ProjectorString, + 'ProjectorSum': cirq.ProjectorSum, 'RandomGateChannel': cirq.RandomGateChannel, 'QuantumFourierTransformGate': cirq.QuantumFourierTransformGate, 'RepetitionsStoppingCriteria': cirq.work.RepetitionsStoppingCriteria, diff --git a/cirq-core/cirq/ops/__init__.py b/cirq-core/cirq/ops/__init__.py index 2a506e6968f..726c852258e 100644 --- a/cirq-core/cirq/ops/__init__.py +++ b/cirq-core/cirq/ops/__init__.py @@ -129,6 +129,7 @@ LinearCombinationOfOperations, PauliSum, PauliSumLike, + ProjectorSum, ) from cirq.ops.mixed_unitary_channel import ( diff --git a/cirq-core/cirq/ops/linear_combinations.py b/cirq-core/cirq/ops/linear_combinations.py index feebc9d8a9e..0f71d2a4065 100644 --- a/cirq-core/cirq/ops/linear_combinations.py +++ b/cirq-core/cirq/ops/linear_combinations.py @@ -14,6 +14,7 @@ from collections import defaultdict from typing import ( AbstractSet, + Any, Dict, Iterable, Mapping, @@ -31,12 +32,14 @@ from sympy.logic.boolalg import And, Not, Or, Xor from sympy.core.expr import Expr from sympy.core.symbol import Symbol +from scipy.sparse import csr_matrix from cirq import linalg, protocols, qis, value from cirq._doc import document from cirq.linalg import operator_spaces from cirq.ops import identity, raw_types, pauli_gates, pauli_string from cirq.ops.pauli_string import PauliString, _validate_qubit_mapping +from cirq.ops.projector import ProjectorString from cirq.value.linear_dict import _format_terms if TYPE_CHECKING: @@ -739,3 +742,210 @@ def __format__(self, format_spec: str) -> str: def __str__(self) -> str: return self.__format__('.3f') + + +def _projector_string_from_projector_dict(projector_dict, coefficient=1.0): + return ProjectorString(dict(projector_dict), coefficient) + + +@value.value_equality(approximate=True) +class ProjectorSum: + def __init__( + self, linear_dict: Optional[value.LinearDict[FrozenSet[Tuple[raw_types.Qid, int]]]] = None + ): + """Constructor for ProjectorSum + + Args: + linear_dict: A linear dictionary from a set of tuples of (Qubit, integer) to a complex + number. The tuple is a projector onto the qubit and the complex number is the + weight of these projections. + """ + self._linear_dict: value.LinearDict[FrozenSet[Tuple[raw_types.Qid, int]]] = ( + linear_dict if linear_dict is not None else value.LinearDict({}) + ) + + def _value_equality_values_(self): + return self._linear_dict + + def _json_dict_(self) -> Dict[str, Any]: + linear_dict = [] + for projector_dict, scalar in dict(self._linear_dict).items(): + key = [[k, v] for k, v in dict(projector_dict).items()] + linear_dict.append([key, scalar]) + return { + 'cirq_type': self.__class__.__name__, + 'linear_dict': linear_dict, + } + + @classmethod + def _from_json_dict_(cls, linear_dict, **kwargs): + converted_dict = {} + for projector_string in linear_dict: + projector_dict = {x[0]: x[1] for x in projector_string[0]} + scalar = projector_string[1] + key = frozenset(projector_dict.items()) + converted_dict[key] = scalar + return cls(linear_dict=value.LinearDict(converted_dict)) + + @classmethod + def from_projector_strings( + cls, terms: Union[ProjectorString, List[ProjectorString]] + ) -> 'ProjectorSum': + """Builds a ProjectorSum from one or more ProjectorString(s). + + Args: + terms: Either a single ProjectorString or a list of ProjectorStrings. + + Returns: + A ProjectorSum. + """ + if isinstance(terms, ProjectorString): + terms = [terms] + termdict: DefaultDict[FrozenSet[Tuple[raw_types.Qid, int]], value.Scalar] = defaultdict( + lambda: 0.0 + ) + for pstring in terms: + key = frozenset(pstring.projector_dict.items()) + termdict[key] += pstring.coefficient + return cls(linear_dict=value.LinearDict(termdict)) + + def copy(self) -> 'ProjectorSum': + return ProjectorSum(self._linear_dict.copy()) + + 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 ProjectorSum 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. + """ + return sum( + coeff * _projector_string_from_projector_dict(vec).matrix(projector_qids) + for vec, coeff in self._linear_dict.items() + ) + + def expectation_from_state_vector( + self, + state_vector: np.ndarray, + qid_map: Mapping[raw_types.Qid, int], + ) -> float: + """Compute the expectation value of this ProjectorSum given a state vector. + + Projects the state vector onto the sum of projectors and computes the expectation of the + measurements. + + Args: + state_vector: An array representing a valid state vector. + qubit_map: A map from all qubits used in this ProjectorSum to the indices of the qubits + that `state_vector` is defined over. + Returns: + The expectation value of the input state. + """ + return sum( + coeff + * _projector_string_from_projector_dict(vec).expectation_from_state_vector( + state_vector, qid_map + ) + for vec, coeff in self._linear_dict.items() + ) + + def expectation_from_density_matrix( + self, + state: np.ndarray, + qid_map: Mapping[raw_types.Qid, int], + ) -> float: + """Expectation of the sum of projections from a density matrix. + + Projects the density matrix onto the sum of projectors and computes the expectation of the + measurements. + + Args: + state: An array representing a valid density matrix. + qubit_map: A map from all qubits used in this ProjectorSum to the indices of the qubits + that `state_vector` is defined over. + Returns: + The expectation value of the input state. + """ + return sum( + coeff + * _projector_string_from_projector_dict(vec).expectation_from_density_matrix( + state, qid_map + ) + for vec, coeff in self._linear_dict.items() + ) + + def __iter__(self): + for vec, coeff in self._linear_dict.items(): + yield _projector_string_from_projector_dict(vec, coeff) + + def __len__(self) -> int: + return len(self._linear_dict) + + def __truediv__(self, a: value.Scalar): + return self.__mul__(1 / a) + + def __bool__(self) -> bool: + return bool(self._linear_dict) + + def __iadd__(self, other: Union['ProjectorString', 'ProjectorSum']): + if isinstance(other, ProjectorString): + other = ProjectorSum.from_projector_strings(other) + elif not isinstance(other, ProjectorSum): + return NotImplemented + self._linear_dict += other._linear_dict + return self + + def __add__(self, other: Union['ProjectorString', 'ProjectorSum']): + if isinstance(other, ProjectorString): + other = ProjectorSum.from_projector_strings(other) + elif not isinstance(other, ProjectorSum): + return NotImplemented + result = self.copy() + result += other + return result + + def __isub__(self, other: Union['ProjectorString', 'ProjectorSum']): + if isinstance(other, ProjectorString): + other = ProjectorSum.from_projector_strings(other) + elif not isinstance(other, ProjectorSum): + return NotImplemented + self._linear_dict -= other._linear_dict + return self + + def __sub__(self, other: Union['ProjectorString', 'ProjectorSum']): + if isinstance(other, ProjectorString): + other = ProjectorSum.from_projector_strings(other) + elif not isinstance(other, ProjectorSum): + return NotImplemented + result = self.copy() + result -= other + return result + + def __neg__(self): + factory = type(self) + return factory(-self._linear_dict) + + def __imul__(self, other: value.Scalar): + if not isinstance(other, numbers.Complex): + return NotImplemented + self._linear_dict *= other + return self + + def __mul__(self, other: value.Scalar): + if not isinstance(other, numbers.Complex): + return NotImplemented + result = self.copy() + result *= other + return result + + def __rmul__(self, other: value.Scalar): + if not isinstance(other, numbers.Complex): + return NotImplemented + result = self.copy() + result *= other + return result diff --git a/cirq-core/cirq/ops/linear_combinations_test.py b/cirq-core/cirq/ops/linear_combinations_test.py index 370d1bce478..867426b0fa2 100644 --- a/cirq-core/cirq/ops/linear_combinations_test.py +++ b/cirq-core/cirq/ops/linear_combinations_test.py @@ -1662,3 +1662,288 @@ def test_expectation_from_density_matrix_two_qubit_states(): np.testing.assert_allclose( psum3.expectation_from_density_matrix(state, qubit_map=q_map_2), 0 ) + + +def test_projector_sum_expectations_matrix(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings( + cirq.ProjectorString({q0: 0}, coefficient=0.2016) + ) + one_projector_sum = cirq.ProjectorSum.from_projector_strings( + cirq.ProjectorString({q0: 1}, coefficient=0.0913) + ) + proj_sum = 0.6 * zero_projector_sum + 0.4 * one_projector_sum + + np.testing.assert_allclose( + proj_sum.matrix().toarray(), + 0.6 * zero_projector_sum.matrix().toarray() + 0.4 * one_projector_sum.matrix().toarray(), + ) + + +def test_projector_sum_expectations_from_state_vector(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings( + cirq.ProjectorString({q0: 0}, coefficient=0.2016) + ) + one_projector_sum = cirq.ProjectorSum.from_projector_strings( + cirq.ProjectorString({q0: 1}, coefficient=0.0913) + ) + proj_sum = 0.6 * zero_projector_sum + 0.4 * one_projector_sum + + random_state_vector = cirq.testing.random_superposition(2) + + np.testing.assert_allclose( + proj_sum.expectation_from_state_vector(random_state_vector, qid_map={q0: 0}), + 0.6 * zero_projector_sum.expectation_from_state_vector(random_state_vector, qid_map={q0: 0}) + + 0.4 + * one_projector_sum.expectation_from_state_vector(random_state_vector, qid_map={q0: 0}), + ) + + +def test_projector_sum_expectations_from_density_matrix(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings( + cirq.ProjectorString({q0: 0}, coefficient=0.2016) + ) + one_projector_sum = cirq.ProjectorSum.from_projector_strings( + cirq.ProjectorString({q0: 1}, coefficient=0.0913) + ) + proj_sum = 0.6 * zero_projector_sum + 0.4 * one_projector_sum + + ranom_density_matrix = cirq.testing.random_density_matrix(2) + + np.testing.assert_allclose( + proj_sum.expectation_from_density_matrix(ranom_density_matrix, qid_map={q0: 0}), + 0.6 + * zero_projector_sum.expectation_from_density_matrix(ranom_density_matrix, qid_map={q0: 0}) + + 0.4 + * one_projector_sum.expectation_from_density_matrix(ranom_density_matrix, qid_map={q0: 0}), + ) + + +def test_projector_sum_accessor(): + q0 = cirq.NamedQubit('q0') + + projector_string_1 = cirq.ProjectorString({q0: 0}, 0.2016) + projector_string_2 = cirq.ProjectorString({q0: 1}, 0.0913) + + projector_sum = cirq.ProjectorSum.from_projector_strings( + [projector_string_1, projector_string_2] + ) + + assert len(projector_sum) == 2 + expanded_projector_strings = list(projector_sum) + assert expanded_projector_strings == [projector_string_1, projector_string_2] + + +def test_projector_bool_operation(): + q0 = cirq.NamedQubit('q0') + + empty_projector_sum = cirq.ProjectorSum.from_projector_strings([]) + non_empty_projector_sum = cirq.ProjectorSum.from_projector_strings( + [cirq.ProjectorString({q0: 0})] + ) + + assert not empty_projector_sum + assert non_empty_projector_sum + + +def test_projector_sum_addition(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + one_projector_string = cirq.ProjectorString({q0: 1}) + one_projector_sum = cirq.ProjectorSum.from_projector_strings(one_projector_string) + + simple_addition = zero_projector_sum + one_projector_sum + np.testing.assert_allclose(simple_addition.matrix().toarray(), [[1.0, 0.0], [0.0, 1.0]]) + + simple_addition = zero_projector_sum + one_projector_string + np.testing.assert_allclose(simple_addition.matrix().toarray(), [[1.0, 0.0], [0.0, 1.0]]) + + # Check that the inputs are not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + np.testing.assert_allclose(one_projector_sum.matrix().toarray(), [[0.0, 0.0], [0.0, 1.0]]) + + with pytest.raises(TypeError): + _ = zero_projector_sum + 0.20160913 + + +def test_projector_sum_subtraction(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + one_projector_string = cirq.ProjectorString({q0: 1}) + one_projector_sum = cirq.ProjectorSum.from_projector_strings(one_projector_string) + + simple_subtraction = zero_projector_sum - one_projector_sum + np.testing.assert_allclose(simple_subtraction.matrix().toarray(), [[1.0, 0.0], [0.0, -1.0]]) + + simple_subtraction = zero_projector_sum - one_projector_string + np.testing.assert_allclose(simple_subtraction.matrix().toarray(), [[1.0, 0.0], [0.0, -1.0]]) + + # Check that the inputs are not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + np.testing.assert_allclose(one_projector_sum.matrix().toarray(), [[0.0, 0.0], [0.0, 1.0]]) + + with pytest.raises(TypeError): + _ = zero_projector_sum - 0.87539319 + + +def test_projector_sum_negation(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + + negation = -zero_projector_sum.copy() + np.testing.assert_allclose(negation.matrix().toarray(), [[-1.0, 0.0], [0.0, 0.0]]) + + # Check that the input is not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + + +def test_projector_sum_incrementation(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + one_projector_string = cirq.ProjectorString({q0: 1}) + one_projector_sum = cirq.ProjectorSum.from_projector_strings(one_projector_string) + + incrementation = zero_projector_sum.copy() + incrementation += one_projector_sum + np.testing.assert_allclose(incrementation.matrix().toarray(), [[1.0, 0.0], [0.0, 1.0]]) + + incrementation = zero_projector_sum.copy() + incrementation += one_projector_string + np.testing.assert_allclose(incrementation.matrix().toarray(), [[1.0, 0.0], [0.0, 1.0]]) + + # Check that the inputs are not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + np.testing.assert_allclose(one_projector_sum.matrix().toarray(), [[0.0, 0.0], [0.0, 1.0]]) + + with pytest.raises(TypeError): + zero_projector_sum += 0.6963472309248 + + +def test_projector_sum_decrementation(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + one_projector_string = cirq.ProjectorString({q0: 1}) + one_projector_sum = cirq.ProjectorSum.from_projector_strings(one_projector_string) + + decrementation = zero_projector_sum.copy() + decrementation -= one_projector_sum + np.testing.assert_allclose(decrementation.matrix().toarray(), [[1.0, 0.0], [0.0, -1.0]]) + + decrementation = zero_projector_sum.copy() + decrementation -= one_projector_string + np.testing.assert_allclose(decrementation.matrix().toarray(), [[1.0, 0.0], [0.0, -1.0]]) + + # Check that the inputs are not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + np.testing.assert_allclose(one_projector_sum.matrix().toarray(), [[0.0, 0.0], [0.0, 1.0]]) + + with pytest.raises(TypeError): + zero_projector_sum -= 0.12345 + + +def test_projector_sum_multiplication_left(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + + multiplication_float = 2.0 * zero_projector_sum + np.testing.assert_allclose(multiplication_float.matrix().toarray(), [[2.0, 0.0], [0.0, 0.0]]) + + multiplication_int = 2 * zero_projector_sum + np.testing.assert_allclose(multiplication_int.matrix().toarray(), [[2.0, 0.0], [0.0, 0.0]]) + + multiplication_complex = 2j * zero_projector_sum + np.testing.assert_allclose(multiplication_complex.matrix().toarray(), [[2.0j, 0.0], [0.0, 0.0]]) + + # Check that the input is not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + + with pytest.raises(TypeError): + _ = 'not_the_correct_type' * zero_projector_sum + + +def test_projector_sum_multiplication_right(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + + multiplication_float = zero_projector_sum * 2.0 + np.testing.assert_allclose(multiplication_float.matrix().toarray(), [[2.0, 0.0], [0.0, 0.0]]) + + multiplication_int = zero_projector_sum * 2 + np.testing.assert_allclose(multiplication_int.matrix().toarray(), [[2.0, 0.0], [0.0, 0.0]]) + + multiplication_complex = zero_projector_sum * 2j + np.testing.assert_allclose(multiplication_complex.matrix().toarray(), [[2.0j, 0.0], [0.0, 0.0]]) + + # Check that the input is not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + + with pytest.raises(TypeError): + _ = zero_projector_sum * 'not_the_correct_type' + + +def test_projector_sum_self_multiplication(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + + multiplication_float = zero_projector_sum.copy() + multiplication_float *= 2.0 + np.testing.assert_allclose(multiplication_float.matrix().toarray(), [[2.0, 0.0], [0.0, 0.0]]) + + multiplication_int = zero_projector_sum.copy() + multiplication_int *= 2 + np.testing.assert_allclose(multiplication_int.matrix().toarray(), [[2.0, 0.0], [0.0, 0.0]]) + + multiplication_complex = zero_projector_sum.copy() + multiplication_complex *= 2j + np.testing.assert_allclose(multiplication_complex.matrix().toarray(), [[2.0j, 0.0], [0.0, 0.0]]) + + with pytest.raises(TypeError): + zero_projector_sum *= 'not_the_correct_type' + + +def test_projector_sum_weighted_sum(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + one_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 1})) + + weighted_sum = 0.6 * zero_projector_sum + 0.4 * one_projector_sum + np.testing.assert_allclose(weighted_sum.matrix().toarray(), [[0.6, 0.0], [0.0, 0.4]]) + + # Check that the inputs are not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + np.testing.assert_allclose(one_projector_sum.matrix().toarray(), [[0.0, 0.0], [0.0, 1.0]]) + + +def test_projector_sum_division(): + q0 = cirq.NamedQubit('q0') + + zero_projector_sum = cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString({q0: 0})) + + true_division_float = zero_projector_sum / 2.0 + np.testing.assert_allclose(true_division_float.matrix().toarray(), [[0.5, 0.0], [0.0, 0.0]]) + + true_division_int = zero_projector_sum / 2 + np.testing.assert_allclose(true_division_int.matrix().toarray(), [[0.5, 0.0], [0.0, 0.0]]) + + true_division_complex = zero_projector_sum / 2j + np.testing.assert_allclose(true_division_complex.matrix().toarray(), [[-0.5j, 0.0], [0.0, 0.0]]) + + # Check that the input is not changed: + np.testing.assert_allclose(zero_projector_sum.matrix().toarray(), [[1.0, 0.0], [0.0, 0.0]]) + + with pytest.raises(TypeError): + _ = zero_projector_sum / 'not_the_correct_type' diff --git a/cirq-core/cirq/protocols/json_test_data/ProjectorSum.json b/cirq-core/cirq/protocols/json_test_data/ProjectorSum.json new file mode 100644 index 00000000000..5ec3c37fc45 --- /dev/null +++ b/cirq-core/cirq/protocols/json_test_data/ProjectorSum.json @@ -0,0 +1,21 @@ +{ + "cirq_type": "ProjectorSum", + "linear_dict": [ + [ + [ + [ + { + "cirq_type": "NamedQubit", + "name": "q0" + }, + 0 + ] + ], + { + "cirq_type": "complex", + "real": 1.0, + "imag": 0.0 + } + ] + ] +} \ No newline at end of file diff --git a/cirq-core/cirq/protocols/json_test_data/ProjectorSum.repr b/cirq-core/cirq/protocols/json_test_data/ProjectorSum.repr new file mode 100644 index 00000000000..4c4b953489b --- /dev/null +++ b/cirq-core/cirq/protocols/json_test_data/ProjectorSum.repr @@ -0,0 +1 @@ +cirq.ProjectorSum.from_projector_strings(cirq.ProjectorString(projector_dict={cirq.NamedQubit('q0'): 0})) \ No newline at end of file