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
16import re
17from typing import List
19import braket.ir.jaqcd as ir
20from braket.circuits import circuit
21from braket.circuits.observable import Observable
22from braket.circuits.qubit_set import QubitSet, QubitSetInput
23from braket.circuits.result_type import ObservableResultType, ResultType
26"""
27To add a new result type:
28 1. Implement the class and extend `ResultType`
29 2. Add a method with the `@circuit.subroutine(register=True)` decorator. Method name
30 is added into the `Circuit` class. This method is the default way
31 clients add this result type to a circuit.
32 3. Register the class with the `ResultType` class via `ResultType.register_result_type()`.
33"""
36class StateVector(ResultType):
37 """
38 The full state vector as a requested result type.
39 This is available on simulators only when `shots=0`.
40 """
42 def __init__(self):
43 super().__init__(ascii_symbols=["StateVector"])
45 def to_ir(self) -> ir.StateVector:
46 return ir.StateVector.construct()
48 @staticmethod
49 @circuit.subroutine(register=True)
50 def state_vector() -> ResultType:
51 """Registers this function into the circuit class.
53 Returns:
54 ResultType: state vector as a requested result type
56 Examples:
57 >>> circ = Circuit().state_vector()
58 """
59 return ResultType.StateVector()
61 def __eq__(self, other) -> bool:
62 if isinstance(other, StateVector):
63 return True
64 return False
66 def __copy__(self) -> StateVector:
67 return type(self)()
70ResultType.register_result_type(StateVector)
73class Amplitude(ResultType):
74 """
75 The amplitude of the specified quantum states as a requested result type.
76 This is available on simulators only when `shots=0`.
77 """
79 def __init__(self, state: List[str]):
80 """
81 Args:
82 state (List[str]): list of quantum states as strings with "0" and "1"
84 Raises:
85 ValueError: If state is None or an empty list
87 Examples:
88 >>> ResultType.Amplitude(state=['01', '10'])
89 """
90 if not state or not all(
91 isinstance(amplitude, str) and re.fullmatch("^[01]+$", amplitude) for amplitude in state
92 ):
93 raise ValueError(
94 "A non-empty list of states must be specified in binary encoding e.g. ['01', '10']"
95 )
96 super().__init__(ascii_symbols=[f"Amplitude({','.join(state)})"])
97 self._state = state
99 @property
100 def state(self) -> List[str]:
101 return self._state
103 def to_ir(self) -> ir.Amplitude:
104 return ir.Amplitude.construct(states=self.state)
106 @staticmethod
107 @circuit.subroutine(register=True)
108 def amplitude(state: List[str]) -> ResultType:
109 """Registers this function into the circuit class.
111 Args:
112 state (List[str]): list of quantum states as strings with "0" and "1"
114 Returns:
115 ResultType: state vector as a requested result type
117 Examples:
118 >>> circ = Circuit().amplitude(state=["01", "10"])
119 """
120 return ResultType.Amplitude(state=state)
122 def __eq__(self, other):
123 if isinstance(other, Amplitude):
124 return self.state == other.state
125 return False
127 def __repr__(self):
128 return f"Amplitude(state={self.state})"
130 def __copy__(self):
131 return type(self)(state=self.state)
134ResultType.register_result_type(Amplitude)
137class Probability(ResultType):
138 """Probability in the computational basis as the requested result type.
140 It can be the probability of all states if no targets are specified, or the marginal
141 probability of a restricted set of states if only a subset of all qubits are specified as
142 targets.
144 For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
145 only on simulators and represents the exact result.
146 """
148 def __init__(self, target: QubitSetInput = None):
149 """
150 Args:
151 target (int, Qubit, or iterable of int / Qubit, optional): The target qubits that the
152 result type is requested for. Default is None, which means all qubits for the
153 circuit.
155 Examples:
156 >>> ResultType.Probability(target=[0, 1])
157 """
158 self._target = QubitSet(target)
159 ascii_symbols = ["Probability"] * len(self._target) if self._target else ["Probability"]
160 super().__init__(ascii_symbols=ascii_symbols)
162 @property
163 def target(self) -> QubitSet:
164 return self._target
166 @target.setter
167 def target(self, target: QubitSetInput) -> None:
168 self._target = QubitSet(target)
170 def to_ir(self) -> ir.Probability:
171 if self.target:
172 return ir.Probability.construct(targets=list(self.target))
173 else:
174 return ir.Probability.construct()
176 @staticmethod
177 @circuit.subroutine(register=True)
178 def probability(target: QubitSetInput = None) -> ResultType:
179 """Registers this function into the circuit class.
181 Args:
182 target (int, Qubit, or iterable of int / Qubit, optional): The target qubits that the
183 result type is requested for. Default is None, which means all qubits for the
184 circuit.
186 Returns:
187 ResultType: probability as a requested result type
189 Examples:
190 >>> circ = Circuit().probability(target=[0, 1])
191 """
192 return ResultType.Probability(target=target)
194 def __eq__(self, other) -> bool:
195 if isinstance(other, Probability):
196 return self.target == other.target
197 return False
199 def __repr__(self) -> str:
200 return f"Probability(target={self.target})"
202 def __copy__(self) -> Probability:
203 return type(self)(target=self.target)
206ResultType.register_result_type(Probability)
209class Expectation(ObservableResultType):
210 """Expectation of the specified target qubit set and observable as the requested result type.
212 If no targets are specified, the observable must operate only on 1 qubit and it
213 is applied to all qubits in parallel. Otherwise, the number of specified targets
214 must be equivalent to the number of qubits the observable can be applied to.
216 For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
217 only by simulators and represents the exact result.
219 See :mod:`braket.circuits.observables` module for all of the supported observables.
220 """
222 def __init__(self, observable: Observable, target: QubitSetInput = None):
223 """
224 Args:
225 observable (Observable): the observable for the result type
226 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
227 result type is requested for. Default is None, which means the observable must
228 operate only on 1 qubit and it is applied to all qubits in parallel.
230 Raises:
231 ValueError: If the observable's qubit count does not equal the number of target
232 qubits, or if target=None and the observable's qubit count is not 1.
234 Examples:
235 >>> ResultType.Expectation(observable=Observable.Z(), target=0)
237 >>> tensor_product = Observable.Y() @ Observable.Z()
238 >>> ResultType.Expectation(observable=tensor_product, target=[0, 1])
239 """
240 super().__init__(
241 ascii_symbols=[f"Expectation({obs_ascii})" for obs_ascii in observable.ascii_symbols],
242 observable=observable,
243 target=target,
244 )
246 def to_ir(self) -> ir.Expectation:
247 if self.target:
248 return ir.Expectation.construct(
249 observable=self.observable.to_ir(), targets=list(self.target)
250 )
251 else:
252 return ir.Expectation.construct(observable=self.observable.to_ir())
254 @staticmethod
255 @circuit.subroutine(register=True)
256 def expectation(observable: Observable, target: QubitSetInput = None) -> ResultType:
257 """Registers this function into the circuit class.
259 Args:
260 observable (Observable): the observable for the result type
261 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
262 result type is requested for. Default is None, which means the observable must
263 operate only on 1 qubit and it is applied to all qubits in parallel.
265 Returns:
266 ResultType: expectation as a requested result type
268 Examples:
269 >>> circ = Circuit().expectation(observable=Observable.Z(), target=0)
270 """
271 return ResultType.Expectation(observable=observable, target=target)
274ResultType.register_result_type(Expectation)
277class Sample(ObservableResultType):
278 """Sample of specified target qubit set and observable as the requested result type.
280 If no targets are specified, the observable must operate only on 1 qubit and it
281 is applied to all qubits in parallel. Otherwise, the number of specified targets
282 must equal the number of qubits the observable can be applied to.
284 This is only available for `shots>0`.
286 See :mod:`braket.circuits.observables` module for all of the supported observables.
287 """
289 def __init__(self, observable: Observable, target: QubitSetInput = None):
290 """
291 Args:
292 observable (Observable): the observable for the result type
293 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
294 result type is requested for. Default is None, which means the observable must
295 operate only on 1 qubit and it is applied to all qubits in parallel.
297 Raises:
298 ValueError: If the observable's qubit count is not equal to the number of target
299 qubits, or if target=None and the observable's qubit count is not 1.
301 Examples:
302 >>> ResultType.Sample(observable=Observable.Z(), target=0)
304 >>> tensor_product = Observable.Y() @ Observable.Z()
305 >>> ResultType.Sample(observable=tensor_product, target=[0, 1])
306 """
307 super().__init__(
308 ascii_symbols=[f"Sample({obs_ascii})" for obs_ascii in observable.ascii_symbols],
309 observable=observable,
310 target=target,
311 )
313 def to_ir(self) -> ir.Sample:
314 if self.target:
315 return ir.Sample.construct(
316 observable=self.observable.to_ir(), targets=list(self.target)
317 )
318 else:
319 return ir.Sample.construct(observable=self.observable.to_ir())
321 @staticmethod
322 @circuit.subroutine(register=True)
323 def sample(observable: Observable, target: QubitSetInput = None) -> ResultType:
324 """Registers this function into the circuit class.
326 Args:
327 observable (Observable): the observable for the result type
328 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
329 result type is requested for. Default is None, which means the observable must
330 operate only on 1 qubit and it is applied to all qubits in parallel.
332 Returns:
333 ResultType: sample as a requested result type
335 Examples:
336 >>> circ = Circuit().sample(observable=Observable.Z(), target=0)
337 """
338 return ResultType.Sample(observable=observable, target=target)
341ResultType.register_result_type(Sample)
344class Variance(ObservableResultType):
345 """Variance of specified target qubit set and observable as the requested result type.
347 If no targets are specified, the observable must operate only on 1 qubit and it
348 is applied to all qubits in parallel. Otherwise, the number of targets specified
349 must equal the number of qubits that the observable can be applied to.
351 For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
352 only by simulators and represents the exact result.
354 See :mod:`braket.circuits.observables` module for all of the supported observables.
355 """
357 def __init__(self, observable: Observable, target: QubitSetInput = None):
358 """
359 Args:
360 observable (Observable): the observable for the result type
361 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
362 result type is requested for. Default is None, which means the observable must
363 operate only on 1 qubit and it is applied to all qubits in parallel.
365 Raises:
366 ValueError: If the observable's qubit count does not equal the number of target
367 qubits, or if target=None and the observable's qubit count is not 1.
369 Examples:
370 >>> ResultType.Variance(observable=Observable.Z(), target=0)
372 >>> tensor_product = Observable.Y() @ Observable.Z()
373 >>> ResultType.Variance(observable=tensor_product, target=[0, 1])
374 """
375 super().__init__(
376 ascii_symbols=[f"Variance({obs_ascii})" for obs_ascii in observable.ascii_symbols],
377 observable=observable,
378 target=target,
379 )
381 def to_ir(self) -> ir.Variance:
382 if self.target:
383 return ir.Variance.construct(
384 observable=self.observable.to_ir(), targets=list(self.target)
385 )
386 else:
387 return ir.Variance.construct(observable=self.observable.to_ir())
389 @staticmethod
390 @circuit.subroutine(register=True)
391 def variance(observable: Observable, target: QubitSetInput = None) -> ResultType:
392 """Registers this function into the circuit class.
394 Args:
395 observable (Observable): the observable for the result type
396 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
397 result type is requested for. Default is None, which means the observable must
398 only operate on 1 qubit and it will be applied to all qubits in parallel
400 Returns:
401 ResultType: variance as a requested result type
403 Examples:
404 >>> circ = Circuit().variance(observable=Observable.Z(), target=0)
405 """
406 return ResultType.Variance(observable=observable, target=target)
409ResultType.register_result_type(Variance)