Coverage for src/braket/circuits/result_types.py : 100%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright 2019-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"). You
4# may not use this file except in compliance with the License. A copy of
5# the License is located at
6#
7# http://aws.amazon.com/apache2.0/
8#
9# or in the "license" file accompanying this file. This file is
10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11# ANY KIND, either express or implied. See the License for the specific
12# language governing permissions and limitations under the License.
14from __future__ import annotations
16from typing import List
18import braket.ir.jaqcd as ir
19from braket.circuits import circuit
20from braket.circuits.observable import Observable
21from braket.circuits.qubit_set import QubitSet, QubitSetInput
22from braket.circuits.result_type import ResultType
25"""
26To add a new result type:
27 1. Implement the class and extend `ResultType`
28 2. Add a method with the `@circuit.subroutine(register=True)` decorator. Method name
29 will be added into the `Circuit` class. This method is the default way
30 clients add this result type to a circuit.
31 3. Register the class with the `ResultType` class via `ResultType.register_result_type()`.
32"""
35class StateVector(ResultType):
36 """The full state vector as a requested result type."""
38 def __init__(self):
39 super().__init__(ascii_symbol=["StateVector"])
41 def to_ir(self) -> ir.StateVector:
42 return ir.StateVector()
44 @staticmethod
45 @circuit.subroutine(register=True)
46 def state_vector() -> ResultType:
47 """Registers this function into the circuit class.
49 Returns:
50 ResultType: state vector as a requested result type
52 Examples:
53 >>> circ = Circuit().state_vector()
54 """
55 return ResultType.StateVector()
57 def __eq__(self, other) -> bool:
58 if isinstance(other, StateVector):
59 return True
60 return False
62 def __copy__(self) -> StateVector:
63 return type(self)()
66ResultType.register_result_type(StateVector)
69class Amplitude(ResultType):
70 """The amplitude of specified quantum states as a requested result type."""
72 def __init__(self, state: List[str]):
73 """
74 Args:
75 state (List[str]): list of quantum states as strings with "0" and "1"
77 Raises:
78 ValueError: If state is None or an empty list
80 Examples:
81 >>> ResultType.Amplitude(state=['01', '10'])
82 """
83 super().__init__(ascii_symbol=["Amplitude"])
84 if not state:
85 raise ValueError("A non-empty list of states must be specified e.g. ['01', '10']")
86 self._state = state
88 @property
89 def state(self) -> List[str]:
90 return self._state
92 def to_ir(self) -> ir.Amplitude:
93 return ir.Amplitude(states=self.state)
95 @staticmethod
96 @circuit.subroutine(register=True)
97 def amplitude(state: List[str]) -> ResultType:
98 """Registers this function into the circuit class.
100 Args:
101 state (List[str]): list of quantum states as strings with "0" and "1"
103 Returns:
104 ResultType: state vector as a requested result type
106 Examples:
107 >>> circ = Circuit().amplitude(state=["01", "10"])
108 """
109 return ResultType.Amplitude(state=state)
111 def __eq__(self, other):
112 if isinstance(other, Amplitude):
113 return self.state == other.state
114 return False
116 def __repr__(self):
117 return f"Amplitude(state={self.state})"
119 def __copy__(self):
120 return type(self)(state=self.state)
123ResultType.register_result_type(Amplitude)
126class Probability(ResultType):
127 """Probability as the requested result type.
129 It can be the probability of all states if no targets are specified or the marginal probability
130 of a restricted set of states if only a subset of all qubits are specified as target."""
132 def __init__(self, target: QubitSetInput = None):
133 """
134 Args:
135 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
136 result type is requested for. Default is None, which means all qubits for the
137 circuit.
139 Examples:
140 >>> ResultType.Probability(target=[0, 1])
141 """
142 super().__init__(ascii_symbol=["Prob"])
143 self._target = QubitSet(target)
145 @property
146 def target(self) -> QubitSet:
147 return self._target
149 @target.setter
150 def target(self, target: QubitSetInput) -> None:
151 self._target = QubitSet(target)
153 def to_ir(self) -> ir.Probability:
154 return ir.Probability(targets=list(self.target)) if self.target else ir.Probability()
156 @staticmethod
157 @circuit.subroutine(register=True)
158 def probability(target: QubitSetInput = None) -> ResultType:
159 """Registers this function into the circuit class.
161 Args:
162 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
163 result type is requested for. Default is None, which means all qubits for the
164 circuit.
166 Returns:
167 ResultType: probability as a requested result type
169 Examples:
170 >>> circ = Circuit().probability(target=[0, 1])
171 """
172 return ResultType.Probability(target=target)
174 def __eq__(self, other) -> bool:
175 if isinstance(other, Probability):
176 return self.target == other.target
177 return False
179 def __repr__(self) -> str:
180 return f"Probability(target={self.target})"
182 def __copy__(self) -> Probability:
183 return type(self)(target=self.target)
186ResultType.register_result_type(Probability)
189class ObservableResultType(ResultType):
190 """
191 Result types with observables and targets.
192 If no targets are specified, the observable must only operate on 1 qubit and it
193 will be applied to all qubits in parallel. Otherwise, the number of specified targets
194 must be equivalent to the number of qubits the observable can be applied to.
196 See :mod:`braket.circuits.observables` module for all of the supported observables.
197 """
199 def __init__(self, ascii_symbol: str, observable: Observable, target: QubitSetInput = None):
200 """
201 Args:
202 observable (Observable): the observable for the result type
203 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
204 result type is requested for. Default is None, which means the observable must
205 only operate on 1 qubit and it will be applied to all qubits in parallel
207 Raises:
208 ValueError: If the observable's qubit count and the number of target qubits
209 are not equal. Or, if target=None and the observable's qubit count is not 1.
210 """
211 super().__init__(ascii_symbol)
212 self._observable = observable
213 self._target = QubitSet(target)
214 if not self._target:
215 if self._observable.qubit_count != 1:
216 raise ValueError(
217 f"Observable {self._observable} must only operate on 1 qubit for target=None"
218 )
219 elif self._observable.qubit_count != len(self._target):
220 raise ValueError(
221 f"Observable's qubit count and the number of target qubits must be equal"
222 )
224 @property
225 def observable(self) -> Observable:
226 return self._observable
228 @property
229 def target(self) -> QubitSet:
230 return self._target
232 @target.setter
233 def target(self, target: QubitSetInput) -> None:
234 self._target = QubitSet(target)
236 def __eq__(self, other) -> bool:
237 if isinstance(other, ObservableResultType):
238 return self.target == other.target and self.observable == other.observable
239 return False
241 def __repr__(self) -> str:
242 return f"{self.name}(observable={self.observable}, target={self.target})"
244 def __copy__(self) -> Expectation:
245 return type(self)(observable=self.observable, target=self.target)
248class Expectation(ObservableResultType):
249 """Expectation of specified target qubit set and observable as the requested result type.
251 If no targets are specified, the observable must only operate on 1 qubit and it
252 will be applied to all qubits in parallel. Otherwise, the number of specified targets
253 must be equivalent to the number of qubits the observable can be applied to.
255 See :mod:`braket.circuits.observables` module for all of the supported observables.
256 """
258 def __init__(self, observable: Observable, target: QubitSetInput = None):
259 """
260 Args:
261 observable (Observable): the observable for the result type
262 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
263 result type is requested for. Default is None, which means the observable must
264 only operate on 1 qubit and it will be applied to all qubits in parallel
266 Raises:
267 ValueError: If the observable's qubit count and the number of target qubits
268 are not equal. Or, if target=None and the observable's qubit count is not 1.
270 Examples:
271 >>> ResultType.Expectation(observable=Observable.Z(), target=0)
273 >>> tensor_product = Observable.Y() @ Observable.Z()
274 >>> ResultType.Expectation(observable=tensor_product, target=[0, 1])
275 """
276 super().__init__(ascii_symbol=["Expectation"], observable=observable, target=target)
278 def to_ir(self) -> ir.Expectation:
279 if self.target:
280 return ir.Expectation(observable=self.observable.to_ir(), targets=list(self.target))
281 else:
282 return ir.Expectation(observable=self.observable.to_ir())
284 @staticmethod
285 @circuit.subroutine(register=True)
286 def expectation(observable: Observable, target: QubitSetInput = None) -> ResultType:
287 """Registers this function into the circuit class.
289 Args:
290 observable (Observable): the observable for the result type
291 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
292 result type is requested for. Default is None, which means the observable must
293 only operate on 1 qubit and it will be applied to all qubits in parallel
295 Returns:
296 ResultType: expectation as a requested result type
298 Examples:
299 >>> circ = Circuit().expectation(observable=Observable.Z(), target=0)
300 """
301 return ResultType.Expectation(observable=observable, target=target)
304ResultType.register_result_type(Expectation)
307class Sample(ObservableResultType):
308 """Sample of specified target qubit set and observable as the requested result type.
310 If no targets are specified, the observable must only operate on 1 qubit and it
311 will be applied to all qubits in parallel. Otherwise, the number of specified targets
312 must be equivalent to the number of qubits the observable can be applied to.
314 See :mod:`braket.circuits.observables` module for all of the supported observables.
315 """
317 def __init__(self, observable: Observable, target: QubitSetInput = None):
318 """
319 Args:
320 observable (Observable): the observable for the result type
321 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
322 result type is requested for. Default is None, which means the observable must
323 only operate on 1 qubit and it will be applied to all qubits in parallel
325 Raises:
326 ValueError: If the observable's qubit count and the number of target qubits
327 are not equal. Or, if target=None and the observable's qubit count is not 1.
329 Examples:
330 >>> ResultType.Sample(observable=Observable.Z(), target=0)
332 >>> tensor_product = Observable.Y() @ Observable.Z()
333 >>> ResultType.Sample(observable=tensor_product, target=[0, 1])
334 """
335 super().__init__(ascii_symbol=["Sample"], observable=observable, target=target)
337 def to_ir(self) -> ir.Sample:
338 if self.target:
339 return ir.Sample(observable=self.observable.to_ir(), targets=list(self.target))
340 else:
341 return ir.Sample(observable=self.observable.to_ir())
343 @staticmethod
344 @circuit.subroutine(register=True)
345 def sample(observable: Observable, target: QubitSetInput = None) -> ResultType:
346 """Registers this function into the circuit class.
348 Args:
349 observable (Observable): the observable for the result type
350 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
351 result type is requested for. Default is None, which means the observable must
352 only operate on 1 qubit and it will be applied to all qubits in parallel
354 Returns:
355 ResultType: sample as a requested result type
357 Examples:
358 >>> circ = Circuit().sample(observable=Observable.Z(), target=0)
359 """
360 return ResultType.Sample(observable=observable, target=target)
363ResultType.register_result_type(Sample)
366class Variance(ObservableResultType):
367 """Variance of specified target qubit set and observable as the requested result type.
369 If no targets are specified, the observable must only operate on 1 qubit and it
370 will be applied to all qubits in parallel. Otherwise, the number of specified targets
371 must be equivalent to the number of qubits the observable can be applied to.
373 See :mod:`braket.circuits.observables` module for all of the supported observables.
374 """
376 def __init__(self, observable: Observable, target: QubitSetInput = None):
377 """
378 Args:
379 observable (Observable): the observable for the result type
380 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
381 result type is requested for. Default is None, which means the observable must
382 only operate on 1 qubit and it will be applied to all qubits in parallel
384 Raises:
385 ValueError: If the observable's qubit count and the number of target qubits
386 are not equal. Or, if target=None and the observable's qubit count is not 1.
388 Examples:
389 >>> ResultType.Variance(observable=Observable.Z(), target=0)
391 >>> tensor_product = Observable.Y() @ Observable.Z()
392 >>> ResultType.Variance(observable=tensor_product, target=[0, 1])
393 """
394 super().__init__(ascii_symbol=["Variance"], observable=observable, target=target)
396 def to_ir(self) -> ir.Variance:
397 if self.target:
398 return ir.Variance(observable=self.observable.to_ir(), targets=list(self.target))
399 else:
400 return ir.Variance(observable=self.observable.to_ir())
402 @staticmethod
403 @circuit.subroutine(register=True)
404 def variance(observable: Observable, target: QubitSetInput = None) -> ResultType:
405 """Registers this function into the circuit class.
407 Args:
408 observable (Observable): the observable for the result type
409 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
410 result type is requested for. Default is None, which means the observable must
411 only operate on 1 qubit and it will be applied to all qubits in parallel
413 Returns:
414 ResultType: variance as a requested result type
416 Examples:
417 >>> circ = Circuit().variance(observable=Observable.Z(), target=0)
418 """
419 return ResultType.Variance(observable=observable, target=target)
422ResultType.register_result_type(Variance)