Skip to content

Commit

Permalink
Add missing _has_*_ methods (#4735)
Browse files Browse the repository at this point in the history
I missed them in #4550.
  • Loading branch information
viathor authored Dec 8, 2021
1 parent 5ae8a2e commit 45fd829
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 1 deletion.
4 changes: 4 additions & 0 deletions cirq-core/cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,10 @@ def unitary(
result = _apply_unitary_circuit(self, state, qs, dtype)
return result.reshape((side_len, side_len))

def _has_superoperator_(self) -> bool:
"""Returns True if self has superoperator representation."""
return all(m._has_superoperator_() for m in self)

def _superoperator_(self) -> np.ndarray:
"""Compute superoperator matrix for quantum channel specified by this circuit."""
all_qubits = self.all_qubits()
Expand Down
6 changes: 6 additions & 0 deletions cirq-core/cirq/circuits/circuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2856,6 +2856,7 @@ def _decompose_(self, qubits):

def test_circuit_superoperator_too_many_qubits():
circuit = cirq.Circuit(cirq.IdentityGate(num_qubits=11).on(*cirq.LineQubit.range(11)))
assert not circuit._has_superoperator_()
with pytest.raises(ValueError, match="too many"):
_ = circuit._superoperator_()

Expand Down Expand Up @@ -2896,6 +2897,7 @@ def test_circuit_superoperator_too_many_qubits():
)
def test_circuit_superoperator_fixed_values(circuit, expected_superoperator):
"""Tests Circuit._superoperator_() on a few simple circuits."""
assert circuit._has_superoperator_()
assert np.allclose(circuit._superoperator_(), expected_superoperator)


Expand Down Expand Up @@ -2932,6 +2934,9 @@ def depolarize(r: float, n_qubits: int) -> cirq.DepolarizingChannel:
circuit1 = cirq.Circuit(depolarize(r, n_qubits).on(*qubits) for r in rs)
circuit2 = cirq.Circuit(depolarize(np.prod(rs), n_qubits).on(*qubits))

assert circuit1._has_superoperator_()
assert circuit2._has_superoperator_()

cm1 = circuit1._superoperator_()
cm2 = circuit2._superoperator_()
assert np.allclose(cm1, cm2)
Expand Down Expand Up @@ -2996,6 +3001,7 @@ def density_operator_basis(n_qubits: int) -> Iterator[np.ndarray]:
)
def test_compare_circuits_superoperator_to_simulation(circuit, initial_state):
"""Compares action of circuit superoperator and circuit simulation."""
assert circuit._has_superoperator_()
superoperator = circuit._superoperator_()
vectorized_initial_state = np.reshape(initial_state, np.prod(initial_state.shape))
vectorized_final_state = superoperator @ vectorized_initial_state
Expand Down
8 changes: 8 additions & 0 deletions cirq-core/cirq/ops/moment.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@ def expand_to(self, qubits: Iterable['cirq.Qid']) -> 'cirq.Moment':
operations.append(ops.I(q))
return Moment(*operations)

def _has_kraus_(self) -> bool:
"""Returns True if self has a Kraus representation."""
return all(protocols.has_kraus(op) for op in self.operations) and len(self.qubits) <= 10

def _kraus_(self) -> Sequence[np.ndarray]:
r"""Returns Kraus representation of self.
Expand Down Expand Up @@ -402,6 +406,10 @@ def kraus_tensors(op: 'cirq.Operation') -> Sequence[np.ndarray]:
r.append(np.reshape(k, (d, d)))
return r

def _has_superoperator_(self) -> bool:
"""Returns True if self has superoperator representation."""
return self._has_kraus_()

def _superoperator_(self) -> np.ndarray:
"""Returns superoperator representation of self."""
return qis.kraus_to_superoperator(self._kraus_())
Expand Down
16 changes: 15 additions & 1 deletion cirq-core/cirq/ops/moment_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,23 +645,27 @@ def test_kraus():
a, b = cirq.LineQubit.range(2)

m = cirq.Moment()
assert cirq.has_kraus(m)
k = cirq.kraus(m)
assert len(k) == 1
assert np.allclose(k[0], np.array([[1.0]]))

m = cirq.Moment(cirq.S(a))
assert cirq.has_kraus(m)
k = cirq.kraus(m)
assert len(k) == 1
assert np.allclose(k[0], np.diag([1, 1j]))

m = cirq.Moment(cirq.CNOT(a, b))
assert cirq.has_kraus(m)
k = cirq.kraus(m)
print(k[0])
assert len(k) == 1
assert np.allclose(k[0], np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]))

p = 0.1
m = cirq.Moment(cirq.depolarize(p).on(a))
assert cirq.has_kraus(m)
k = cirq.kraus(m)
assert len(k) == 4
assert np.allclose(k[0], np.sqrt(1 - p) * I)
Expand All @@ -672,6 +676,7 @@ def test_kraus():
p = 0.2
q = 0.3
m = cirq.Moment(cirq.bit_flip(p).on(a), cirq.phase_flip(q).on(b))
assert cirq.has_kraus(m)
k = cirq.kraus(m)
assert len(k) == 4
assert np.allclose(k[0], np.sqrt((1 - p) * (1 - q)) * np.kron(I, I))
Expand All @@ -681,8 +686,11 @@ def test_kraus():


def test_kraus_too_big():
m = cirq.Moment(cirq.IdentityGate(11).on(*cirq.LineQubit.range(11)))
assert not cirq.has_kraus(m)
assert not m._has_superoperator_()
with pytest.raises(ValueError, match='11 > 10 qubits'):
_ = cirq.kraus(cirq.Moment(cirq.IdentityGate(11).on(*cirq.LineQubit.range(11))))
_ = cirq.kraus(m)


def test_superoperator():
Expand All @@ -691,25 +699,31 @@ def test_superoperator():
a, b = cirq.LineQubit.range(2)

m = cirq.Moment()
assert m._has_superoperator_()
s = m._superoperator_()
assert np.allclose(s, np.array([[1.0]]))

m = cirq.Moment(cirq.I(a))
assert m._has_superoperator_()
s = m._superoperator_()
assert np.allclose(s, np.eye(4))

m = cirq.Moment(cirq.IdentityGate(2).on(a, b))
assert m._has_superoperator_()
s = m._superoperator_()
assert np.allclose(s, np.eye(16))

m = cirq.Moment(cirq.S(a))
assert m._has_superoperator_()
s = m._superoperator_()
assert np.allclose(s, np.diag([1, -1j, 1j, 1]))

m = cirq.Moment(cirq.CNOT(a, b))
assert m._has_superoperator_()
s = m._superoperator_()
assert np.allclose(s, np.kron(cnot, cnot))

m = cirq.Moment(cirq.depolarize(0.75).on(a))
assert m._has_superoperator_()
s = m._superoperator_()
assert np.allclose(s, np.array([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 1]]) / 2)

0 comments on commit 45fd829

Please sign in to comment.