Skip to content

Commit bc4123e

Browse files
authored
Rename _channel_ to _kraus_ (#4139)
1 parent b95c365 commit bc4123e

17 files changed

+108
-69
lines changed

cirq-core/cirq/ops/common_channels.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ def __init__(self, p: float, gamma: float) -> None:
454454
self._gamma = value.validate_probability(gamma, 'gamma')
455455
self._p = value.validate_probability(p, 'p')
456456

457-
def _channel_(self) -> Iterable[np.ndarray]:
457+
def _kraus_(self) -> Iterable[np.ndarray]:
458458
p0 = np.sqrt(self._p)
459459
p1 = np.sqrt(1.0 - self._p)
460460
sqrt_g = np.sqrt(self._gamma)
@@ -466,7 +466,7 @@ def _channel_(self) -> Iterable[np.ndarray]:
466466
p1 * np.array([[0.0, 0.0], [sqrt_g, 0.0]]),
467467
)
468468

469-
def _has_channel_(self) -> bool:
469+
def _has_kraus_(self) -> bool:
470470
return True
471471

472472
def _value_equality_values_(self):
@@ -597,12 +597,12 @@ def __init__(self, gamma: float) -> None:
597597
self._gamma = value.validate_probability(gamma, 'gamma')
598598
self._delegate = GeneralizedAmplitudeDampingChannel(1.0, self._gamma)
599599

600-
def _channel_(self) -> Iterable[np.ndarray]:
600+
def _kraus_(self) -> Iterable[np.ndarray]:
601601
# just return first two kraus ops, we don't care about
602602
# the last two.
603-
return list(self._delegate._channel_())[:2]
603+
return list(self._delegate._kraus_())[:2]
604604

605-
def _has_channel_(self) -> bool:
605+
def _has_kraus_(self) -> bool:
606606
return True
607607

608608
def _value_equality_values_(self):
@@ -750,13 +750,13 @@ def _act_on_(self, args: Any):
750750

751751
return NotImplemented
752752

753-
def _channel_(self) -> Iterable[np.ndarray]:
753+
def _kraus_(self) -> Iterable[np.ndarray]:
754754
# The first axis is over the list of channel matrices
755755
channel = np.zeros((self._dimension,) * 3, dtype=np.complex64)
756756
channel[:, 0, :] = np.eye(self._dimension)
757757
return channel
758758

759-
def _has_channel_(self) -> bool:
759+
def _has_kraus_(self) -> bool:
760760
return True
761761

762762
def _value_equality_values_(self):
@@ -831,13 +831,13 @@ def __init__(self, gamma: float) -> None:
831831
"""
832832
self._gamma = value.validate_probability(gamma, 'gamma')
833833

834-
def _channel_(self) -> Iterable[np.ndarray]:
834+
def _kraus_(self) -> Iterable[np.ndarray]:
835835
return (
836836
np.array([[1.0, 0.0], [0.0, np.sqrt(1.0 - self._gamma)]]),
837837
np.array([[0.0, 0.0], [0.0, np.sqrt(self._gamma)]]),
838838
)
839839

840-
def _has_channel_(self) -> bool:
840+
def _has_kraus_(self) -> bool:
841841
return True
842842

843843
def _value_equality_values_(self):

cirq-core/cirq/ops/gate_operation.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,14 @@ def _mixture_(self) -> Sequence[Tuple[float, Any]]:
197197
return getter()
198198
return NotImplemented
199199

200-
def _has_channel_(self) -> bool:
201-
getter = getattr(self.gate, '_has_channel_', None)
200+
def _has_kraus_(self) -> bool:
201+
getter = getattr(self.gate, '_has_kraus_', None)
202202
if getter is not None:
203203
return getter()
204204
return NotImplemented
205205

206-
def _channel_(self) -> Union[Tuple[np.ndarray], NotImplementedType]:
207-
getter = getattr(self.gate, '_channel_', None)
206+
def _kraus_(self) -> Union[Tuple[np.ndarray], NotImplementedType]:
207+
getter = getattr(self.gate, '_kraus_', None)
208208
if getter is not None:
209209
return getter()
210210
return NotImplemented

cirq-core/cirq/ops/measurement_gate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def full_invert_mask(self):
125125
def _measurement_key_(self):
126126
return self.key
127127

128-
def _channel_(self):
128+
def _kraus_(self):
129129
size = np.prod(self._qid_shape, dtype=int)
130130

131131
def delta(i):
@@ -135,7 +135,7 @@ def delta(i):
135135

136136
return tuple(delta(i) for i in range(size))
137137

138-
def _has_channel_(self):
138+
def _has_kraus_(self):
139139
return True
140140

141141
def _circuit_diagram_info_(

cirq-core/cirq/ops/random_gate_channel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def _has_unitary_(self):
5656
def _has_mixture_(self):
5757
return not self._is_parameterized_() and protocols.has_mixture(self.sub_gate)
5858

59-
def _has_channel_(self):
59+
def _has_kraus_(self):
6060
return not self._is_parameterized_() and protocols.has_channel(self.sub_gate)
6161

6262
def _is_parameterized_(self) -> bool:
@@ -90,7 +90,7 @@ def _mixture_(self):
9090
result.append((1 - float(self.probability), do_nothing))
9191
return result
9292

93-
def _channel_(self):
93+
def _kraus_(self):
9494
if self._is_parameterized_():
9595
return NotImplemented
9696

cirq-core/cirq/ops/raw_types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,10 +655,10 @@ def _has_mixture_(self) -> bool:
655655
def _mixture_(self) -> Sequence[Tuple[float, Any]]:
656656
return protocols.mixture(self.sub_operation, NotImplemented)
657657

658-
def _has_channel_(self) -> bool:
658+
def _has_kraus_(self) -> bool:
659659
return protocols.has_channel(self.sub_operation)
660660

661-
def _channel_(self) -> Union[Tuple[np.ndarray], NotImplementedType]:
661+
def _kraus_(self) -> Union[Tuple[np.ndarray], NotImplementedType]:
662662
return protocols.channel(self.sub_operation, NotImplemented)
663663

664664
def _measurement_key_(self) -> str:

cirq-core/cirq/protocols/apply_channel_protocol.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def err_str(buf_num_str):
256256
if result is not None:
257257
return result
258258

259-
# Fallback to using the object's `_channel_` matrices.
259+
# Fallback to using the object's `_kraus_` matrices.
260260
kraus = channel(val, None)
261261
if kraus is not None:
262262
return _apply_kraus(kraus, args)
@@ -266,7 +266,7 @@ def err_str(buf_num_str):
266266
return default
267267
raise TypeError(
268268
"object of type '{}' has no _apply_channel_, _apply_unitary_, "
269-
"_unitary_, or _channel_ methods (or they returned None or "
269+
"_unitary_, or _kraus_ methods (or they returned None or "
270270
"NotImplemented).".format(type(val))
271271
)
272272

cirq-core/cirq/protocols/apply_channel_protocol_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def test_apply_channel_channel_fallback_simple():
143143
x = np.array([[0, 1], [1, 0]], dtype=np.complex128)
144144

145145
class HasChannel:
146-
def _channel_(self):
146+
def _kraus_(self):
147147
return (np.sqrt(0.5) * np.eye(2, dtype=np.complex128), np.sqrt(0.5) * x)
148148

149149
rho = np.copy(x)
@@ -161,7 +161,7 @@ def test_apply_channel_channel_fallback_one_qubit_random_on_qubit():
161161
expected = 0.5 * rho + 0.5 * np.dot(np.dot(u, rho), np.conjugate(np.transpose(u)))
162162

163163
class HasChannel:
164-
def _channel_(self):
164+
def _kraus_(self):
165165
return (np.sqrt(0.5) * np.eye(2, dtype=np.complex128), np.sqrt(0.5) * u)
166166

167167
result = apply_channel(HasChannel(), rho, [0], [1], assert_result_is_out_buf=True)
@@ -183,7 +183,7 @@ def test_apply_channel_channel_fallback_one_qubit_random_on_two_qubits():
183183
expected.shape = (2, 2, 2, 2)
184184

185185
class HasChannel:
186-
def _channel_(self):
186+
def _kraus_(self):
187187
return (np.sqrt(0.5) * np.eye(2, dtype=np.complex128), np.sqrt(0.5) * u)
188188

189189
result = apply_channel(HasChannel(), rho, [0], [2], assert_result_is_out_buf=True)
@@ -204,7 +204,7 @@ def test_apply_channel_channel_fallback_two_qubit_random():
204204
expected.shape = (2, 2, 2, 2)
205205

206206
class HasChannel:
207-
def _channel_(self):
207+
def _kraus_(self):
208208
return (np.sqrt(0.5) * np.eye(4, dtype=np.complex128), np.sqrt(0.5) * u)
209209

210210
result = apply_channel(HasChannel(), rho, [0, 1], [2, 3], assert_result_is_out_buf=True)

cirq-core/cirq/protocols/channel.py

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"""Protocol and methods for quantum channels."""
1616

1717
from typing import Any, Sequence, Tuple, TypeVar, Union
18-
18+
import warnings
1919

2020
import numpy as np
2121
from typing_extensions import Protocol
@@ -45,8 +45,8 @@ class SupportsChannel(Protocol):
4545
"""An object that may be describable as a quantum channel."""
4646

4747
@doc_private
48-
def _channel_(self) -> Union[Sequence[np.ndarray], NotImplementedType]:
49-
r"""A list of matrices describing the quantum channel.
48+
def _kraus_(self) -> Union[Sequence[np.ndarray], NotImplementedType]:
49+
r"""A list of Kraus matrices describing the quantum channel.
5050
5151
These matrices are the terms in the operator sum representation of a
5252
quantum channel. If the returned matrices are ${A_0,A_1,..., A_{r-1}}$,
@@ -81,8 +81,8 @@ def _channel_(self) -> Union[Sequence[np.ndarray], NotImplementedType]:
8181
"""
8282

8383
@doc_private
84-
def _has_channel_(self) -> bool:
85-
"""Whether this value has a channel representation.
84+
def _has_kraus_(self) -> bool:
85+
"""Whether this value has a Kraus representation.
8686
8787
This method is used by the global `cirq.has_channel` method. If this
8888
method is not present, or returns NotImplemented, it will fallback
@@ -121,7 +121,7 @@ def channel(
121121
default is set to a value, that value is returned.
122122
123123
Returns:
124-
If `val` has a `_channel_` method and its result is not NotImplemented,
124+
If `val` has a `_kraus_` method and its result is not NotImplemented,
125125
that result is returned. Otherwise, if `val` has a `_mixture_` method
126126
and its results is not NotImplement a tuple made up of channel
127127
corresponding to that mixture being a probabilistic mixture of unitaries
@@ -131,14 +131,21 @@ def channel(
131131
value is returned.
132132
133133
Raises:
134-
TypeError: `val` doesn't have a _channel_ or _unitary_ method (or that
134+
TypeError: `val` doesn't have a _kraus_ or _unitary_ method (or that
135135
method returned NotImplemented) and also no default value was
136136
specified.
137137
"""
138138
channel_getter = getattr(val, '_channel_', None)
139-
channel_result = NotImplemented if channel_getter is None else channel_getter()
140-
if channel_result is not NotImplemented:
141-
return tuple(channel_result)
139+
if channel_getter is not None:
140+
warnings.warn(
141+
'_channel_ is deprecated and will be removed in cirq 0.13, rename to _kraus_',
142+
DeprecationWarning,
143+
)
144+
145+
kraus_getter = getattr(val, '_kraus_', None)
146+
kraus_result = NotImplemented if kraus_getter is None else kraus_getter()
147+
if kraus_result is not NotImplemented:
148+
return tuple(kraus_result)
142149

143150
mixture_getter = getattr(val, '_mixture_', None)
144151
mixture_result = NotImplemented if mixture_getter is None else mixture_getter()
@@ -150,16 +157,21 @@ def channel(
150157
if unitary_result is not NotImplemented and unitary_result is not None:
151158
return (unitary_result,)
152159

160+
channel_result = NotImplemented if channel_getter is None else channel_getter()
161+
if channel_result is not NotImplemented:
162+
return tuple(channel_result)
163+
153164
if default is not RaiseTypeErrorIfNotProvided:
154165
return default
155166

156-
if channel_getter is None and unitary_getter is None and mixture_getter is None:
167+
if kraus_getter is None and unitary_getter is None and mixture_getter is None:
157168
raise TypeError(
158-
"object of type '{}' has no _channel_ or _mixture_ or "
169+
"object of type '{}' has no _kraus_ or _mixture_ or "
159170
"_unitary_ method.".format(type(val))
160171
)
172+
161173
raise TypeError(
162-
"object of type '{}' does have a _channel_, _mixture_ or "
174+
"object of type '{}' does have a _kraus_, _mixture_ or "
163175
"_unitary_ method, but it returned NotImplemented.".format(type(val))
164176
)
165177

@@ -178,29 +190,39 @@ def has_channel(val: Any, *, allow_decompose: bool = True) -> bool:
178190
the result is skipped.
179191
180192
Returns:
181-
If `val` has a `_has_channel_` method and its result is not
193+
If `val` has a `_has_kraus_` method and its result is not
182194
NotImplemented, that result is returned. Otherwise, if `val` has a
183195
`_has_mixture_` method and its result is not NotImplemented, that
184196
result is returned. Otherwise if `val` has a `_has_unitary_` method
185197
and its results is not NotImplemented, that result is returned.
186-
Otherwise, if the value has a _channel_ method return if that
198+
Otherwise, if the value has a _kraus_ method return if that
187199
has a non-default value. Returns False if none of these functions
188200
exists.
189201
"""
190202
channel_getter = getattr(val, '_has_channel_', None)
191-
result = NotImplemented if channel_getter is None else channel_getter()
203+
if channel_getter is not None:
204+
warnings.warn(
205+
'_has_channel_ is deprecated and will be removed in cirq 0.13, rename to _has_kraus_',
206+
DeprecationWarning,
207+
)
192208

209+
kraus_getter = getattr(val, '_has_kraus_', None)
210+
result = NotImplemented if kraus_getter is None else kraus_getter()
193211
if result is not NotImplemented:
194212
return result
195213

196214
result = has_mixture(val, allow_decompose=False)
197215
if result is not NotImplemented and result:
198216
return result
199217

218+
result = NotImplemented if channel_getter is None else channel_getter()
219+
if result is not NotImplemented:
220+
return result
221+
200222
if allow_decompose:
201223
operations, _, _ = _try_decompose_into_operations_and_qubits(val)
202224
if operations is not None:
203225
return all(has_channel(val) for val in operations)
204226

205-
# No has methods, use `_channel_` or delegates instead.
227+
# No has methods, use `_kraus_` or delegates instead.
206228
return channel(val, None) is not None

cirq-core/cirq/protocols/channel_test.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_channel_no_methods():
2929
class NoMethod:
3030
pass
3131

32-
with pytest.raises(TypeError, match='no _channel_ or _mixture_ or _unitary_ method'):
32+
with pytest.raises(TypeError, match='no _kraus_ or _mixture_ or _unitary_ method'):
3333
_ = cirq.channel(NoMethod())
3434

3535
assert cirq.channel(NoMethod(), None) is None
@@ -52,14 +52,31 @@ def assert_not_implemented(val):
5252
assert not cirq.has_channel(val)
5353

5454

55-
def test_channel_returns_not_implemented():
55+
def test_kraus_returns_not_implemented():
5656
class ReturnsNotImplemented:
57-
def _channel_(self):
57+
def _kraus_(self):
5858
return NotImplemented
5959

6060
assert_not_implemented(ReturnsNotImplemented())
6161

6262

63+
def test_channel_generates_deprecation_warning():
64+
class UsesDeprecatedChannelMethod:
65+
def _has_channel_(self):
66+
return True
67+
68+
def _channel_(self):
69+
return (np.eye(2),)
70+
71+
val = UsesDeprecatedChannelMethod()
72+
with pytest.warns(DeprecationWarning, match='_has_kraus_'):
73+
assert cirq.has_channel(val)
74+
with pytest.warns(DeprecationWarning, match='_kraus_'):
75+
ks = cirq.channel(val)
76+
assert len(ks) == 1
77+
assert np.all(ks[0] == np.eye(2))
78+
79+
6380
def test_mixture_returns_not_implemented():
6481
class ReturnsNotImplemented:
6582
def _mixture_(self):
@@ -87,7 +104,7 @@ def test_channel():
87104
c = (a0, a1)
88105

89106
class ReturnsChannel:
90-
def _channel_(self) -> Sequence[np.ndarray]:
107+
def _kraus_(self) -> Sequence[np.ndarray]:
91108
return c
92109

93110
assert cirq.channel(ReturnsChannel()) is c
@@ -138,7 +155,7 @@ def _unitary_(self) -> np.ndarray:
138155

139156

140157
class HasChannel(cirq.SingleQubitGate):
141-
def _has_channel_(self) -> bool:
158+
def _has_kraus_(self) -> bool:
142159
return True
143160

144161

0 commit comments

Comments
 (0)