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 will be 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 only available when `shots=0` for simulators.
40 """
42 def __init__(self):
43 super().__init__(ascii_symbol=["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 specified quantum states as a requested result type.
76 This is only available when `shots=0` for simulators.
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 super().__init__(ascii_symbol=["Amplitude"])
91 if not state or not all(
92 isinstance(amplitude, str) and re.fullmatch("^[01]+$", amplitude) for amplitude in state
93 ):
94 raise ValueError(
95 "A non-empty list of states must be specified in binary encoding e.g. ['01', '10']"
96 )
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 probability
141 of a restricted set of states if only a subset of all qubits are specified as target.
143 For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
144 only by simulators and represents the exact result.
145 """
147 def __init__(self, target: QubitSetInput = None):
148 """
149 Args:
150 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
151 result type is requested for. Default is None, which means all qubits for the
152 circuit.
154 Examples:
155 >>> ResultType.Probability(target=[0, 1])
156 """
157 super().__init__(ascii_symbol=["Prob"])
158 self._target = QubitSet(target)
160 @property
161 def target(self) -> QubitSet:
162 return self._target
164 @target.setter
165 def target(self, target: QubitSetInput) -> None:
166 self._target = QubitSet(target)
168 def to_ir(self) -> ir.Probability:
169 if self.target:
170 return ir.Probability.construct(targets=list(self.target))
171 else:
172 return ir.Probability.construct()
174 @staticmethod
175 @circuit.subroutine(register=True)
176 def probability(target: QubitSetInput = None) -> ResultType:
177 """Registers this function into the circuit class.
179 Args:
180 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
181 result type is requested for. Default is None, which means all qubits for the
182 circuit.
184 Returns:
185 ResultType: probability as a requested result type
187 Examples:
188 >>> circ = Circuit().probability(target=[0, 1])
189 """
190 return ResultType.Probability(target=target)
192 def __eq__(self, other) -> bool:
193 if isinstance(other, Probability):
194 return self.target == other.target
195 return False
197 def __repr__(self) -> str:
198 return f"Probability(target={self.target})"
200 def __copy__(self) -> Probability:
201 return type(self)(target=self.target)
204ResultType.register_result_type(Probability)
207class Expectation(ObservableResultType):
208 """Expectation of specified target qubit set and observable as the requested result type.
210 If no targets are specified, the observable must only operate on 1 qubit and it
211 will be applied to all qubits in parallel. Otherwise, the number of specified targets
212 must be equivalent to the number of qubits the observable can be applied to.
214 For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
215 only by simulators and represents the exact result.
217 See :mod:`braket.circuits.observables` module for all of the supported observables.
218 """
220 def __init__(self, observable: Observable, target: QubitSetInput = None):
221 """
222 Args:
223 observable (Observable): the observable for the result type
224 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
225 result type is requested for. Default is None, which means the observable must
226 only operate on 1 qubit and it will be applied to all qubits in parallel
228 Raises:
229 ValueError: If the observable's qubit count and the number of target qubits
230 are not equal. Or, if target=None and the observable's qubit count is not 1.
232 Examples:
233 >>> ResultType.Expectation(observable=Observable.Z(), target=0)
235 >>> tensor_product = Observable.Y() @ Observable.Z()
236 >>> ResultType.Expectation(observable=tensor_product, target=[0, 1])
237 """
238 super().__init__(ascii_symbol=["Expectation"], observable=observable, target=target)
240 def to_ir(self) -> ir.Expectation:
241 if self.target:
242 return ir.Expectation.construct(
243 observable=self.observable.to_ir(), targets=list(self.target)
244 )
245 else:
246 return ir.Expectation.construct(observable=self.observable.to_ir())
248 @staticmethod
249 @circuit.subroutine(register=True)
250 def expectation(observable: Observable, target: QubitSetInput = None) -> ResultType:
251 """Registers this function into the circuit class.
253 Args:
254 observable (Observable): the observable for the result type
255 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
256 result type is requested for. Default is None, which means the observable must
257 only operate on 1 qubit and it will be applied to all qubits in parallel
259 Returns:
260 ResultType: expectation as a requested result type
262 Examples:
263 >>> circ = Circuit().expectation(observable=Observable.Z(), target=0)
264 """
265 return ResultType.Expectation(observable=observable, target=target)
268ResultType.register_result_type(Expectation)
271class Sample(ObservableResultType):
272 """Sample of specified target qubit set and observable as the requested result type.
274 If no targets are specified, the observable must only operate on 1 qubit and it
275 will be applied to all qubits in parallel. Otherwise, the number of specified targets
276 must be equivalent to the number of qubits the observable can be applied to.
278 This is only available for `shots>0`.
280 See :mod:`braket.circuits.observables` module for all of the supported observables.
281 """
283 def __init__(self, observable: Observable, target: QubitSetInput = None):
284 """
285 Args:
286 observable (Observable): the observable for the result type
287 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
288 result type is requested for. Default is None, which means the observable must
289 only operate on 1 qubit and it will be applied to all qubits in parallel
291 Raises:
292 ValueError: If the observable's qubit count and the number of target qubits
293 are not equal. Or, if target=None and the observable's qubit count is not 1.
295 Examples:
296 >>> ResultType.Sample(observable=Observable.Z(), target=0)
298 >>> tensor_product = Observable.Y() @ Observable.Z()
299 >>> ResultType.Sample(observable=tensor_product, target=[0, 1])
300 """
301 super().__init__(ascii_symbol=["Sample"], observable=observable, target=target)
303 def to_ir(self) -> ir.Sample:
304 if self.target:
305 return ir.Sample.construct(
306 observable=self.observable.to_ir(), targets=list(self.target)
307 )
308 else:
309 return ir.Sample.construct(observable=self.observable.to_ir())
311 @staticmethod
312 @circuit.subroutine(register=True)
313 def sample(observable: Observable, target: QubitSetInput = None) -> ResultType:
314 """Registers this function into the circuit class.
316 Args:
317 observable (Observable): the observable for the result type
318 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
319 result type is requested for. Default is None, which means the observable must
320 only operate on 1 qubit and it will be applied to all qubits in parallel
322 Returns:
323 ResultType: sample as a requested result type
325 Examples:
326 >>> circ = Circuit().sample(observable=Observable.Z(), target=0)
327 """
328 return ResultType.Sample(observable=observable, target=target)
331ResultType.register_result_type(Sample)
334class Variance(ObservableResultType):
335 """Variance of specified target qubit set and observable as the requested result type.
337 If no targets are specified, the observable must only operate on 1 qubit and it
338 will be applied to all qubits in parallel. Otherwise, the number of specified targets
339 must be equivalent to the number of qubits the observable can be applied to.
341 For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
342 only by simulators and represents the exact result.
344 See :mod:`braket.circuits.observables` module for all of the supported observables.
345 """
347 def __init__(self, observable: Observable, target: QubitSetInput = None):
348 """
349 Args:
350 observable (Observable): the observable for the result type
351 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
352 result type is requested for. Default is None, which means the observable must
353 only operate on 1 qubit and it will be applied to all qubits in parallel
355 Raises:
356 ValueError: If the observable's qubit count and the number of target qubits
357 are not equal. Or, if target=None and the observable's qubit count is not 1.
359 Examples:
360 >>> ResultType.Variance(observable=Observable.Z(), target=0)
362 >>> tensor_product = Observable.Y() @ Observable.Z()
363 >>> ResultType.Variance(observable=tensor_product, target=[0, 1])
364 """
365 super().__init__(ascii_symbol=["Variance"], observable=observable, target=target)
367 def to_ir(self) -> ir.Variance:
368 if self.target:
369 return ir.Variance.construct(
370 observable=self.observable.to_ir(), targets=list(self.target)
371 )
372 else:
373 return ir.Variance.construct(observable=self.observable.to_ir())
375 @staticmethod
376 @circuit.subroutine(register=True)
377 def variance(observable: Observable, target: QubitSetInput = None) -> ResultType:
378 """Registers this function into the circuit class.
380 Args:
381 observable (Observable): the observable for the result type
382 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
383 result type is requested for. Default is None, which means the observable must
384 only operate on 1 qubit and it will be applied to all qubits in parallel
386 Returns:
387 ResultType: variance as a requested result type
389 Examples:
390 >>> circ = Circuit().variance(observable=Observable.Z(), target=0)
391 """
392 return ResultType.Variance(observable=observable, target=target)
395ResultType.register_result_type(Variance)