Coverage for src/braket/circuits/observables.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.
13from __future__ import annotations
15import functools
16from typing import List, Tuple
18import numpy as np
19from braket.circuits.observable import Observable
20from braket.circuits.quantum_operator_helpers import (
21 is_hermitian,
22 verify_quantum_operator_matrix_dimensions,
23)
26class H(Observable):
27 """Hadamard operation as an observable."""
29 def __init__(self):
30 """
31 Examples:
32 >>> Observable.I()
33 """
34 super().__init__(qubit_count=1, ascii_symbols=["H"])
36 def to_ir(self) -> List[str]:
37 return ["h"]
39 def to_matrix(self) -> np.ndarray:
40 return 1.0 / np.sqrt(2.0) * np.array([[1.0, 1.0], [1.0, -1.0]], dtype=complex)
43Observable.register_observable(H)
46class I(Observable): # noqa: E742, E261
47 """Identity operation as an observable."""
49 def __init__(self):
50 """
51 Examples:
52 >>> Observable.I()
53 """
54 super().__init__(qubit_count=1, ascii_symbols=["I"])
56 def to_ir(self) -> List[str]:
57 return ["i"]
59 def to_matrix(self) -> np.ndarray:
60 return np.array([[1.0, 0.0], [0.0, 1.0]], dtype=complex)
63Observable.register_observable(I)
66class X(Observable):
67 """Pauli-X operation as an observable."""
69 def __init__(self):
70 """
71 Examples:
72 >>> Observable.X()
73 """
74 super().__init__(qubit_count=1, ascii_symbols=["X"])
76 def to_ir(self) -> List[str]:
77 return ["x"]
79 def to_matrix(self) -> np.ndarray:
80 return np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex)
83Observable.register_observable(X)
86class Y(Observable):
87 """Pauli-Y operation as an observable."""
89 def __init__(self):
90 """
91 Examples:
92 >>> Observable.Y()
93 """
94 super().__init__(qubit_count=1, ascii_symbols=["Y"])
96 def to_ir(self) -> List[str]:
97 return ["y"]
99 def to_matrix(self) -> np.ndarray:
100 return np.array([[0.0, -1.0j], [1.0j, 0.0]], dtype=complex)
103Observable.register_observable(Y)
106class Z(Observable):
107 """Pauli-Z operation as an observable."""
109 def __init__(self):
110 """
111 Examples:
112 >>> Observable.Z()
113 """
114 super().__init__(qubit_count=1, ascii_symbols=["Z"])
116 def to_ir(self) -> List[str]:
117 return ["z"]
119 def to_matrix(self) -> np.ndarray:
120 return np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)
123Observable.register_observable(Z)
126class TensorProduct(Observable):
127 """Tensor product of observables"""
129 def __init__(self, observables: List[Observable]):
130 """
131 Args:
132 observables (List[Observable]): List of observables for tensor product
134 Examples:
135 >>> t1 = Observable.Y() @ Observable.X()
136 >>> t1.to_matrix()
137 array([[0.+0.j, 0.+0.j, 0.-0.j, 0.-1.j],
138 [0.+0.j, 0.+0.j, 0.-1.j, 0.-0.j],
139 [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j],
140 [0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j]])
141 >>> t2 = Observable.Z() @ t1
142 >>> t2.observables
143 (Z('qubit_count': 1), Y('qubit_count': 1), X('qubit_count': 1))
145 Note: list of observables for tensor product must be given in the desired order that
146 the tensor product will be calculated. For `TensorProduct(observables=[ob1, ob2, ob3])`,
147 the tensor product's matrix will be the result of the tensor product of `ob1`, `ob2`,
148 `ob3`, or `np.kron(np.kron(ob1.to_matrix(), ob2.to_matrix()), ob3.to_matrix())`
149 """
150 self._observables = tuple(observables)
151 qubit_count = sum([obs.qubit_count for obs in observables])
152 display_name = "@".join([obs.ascii_symbols[0] for obs in observables])
153 super().__init__(qubit_count=qubit_count, ascii_symbols=[display_name] * qubit_count)
155 def to_ir(self) -> List[str]:
156 ir = []
157 for obs in self.observables:
158 ir.extend(obs.to_ir())
159 return ir
161 @property
162 def observables(self) -> Tuple[Observable]:
163 """Tuple[Observable]: observables part of tensor product"""
164 return self._observables
166 def to_matrix(self) -> np.ndarray:
167 return functools.reduce(np.kron, [obs.to_matrix() for obs in self.observables])
169 def __matmul__(self, other):
170 if isinstance(other, TensorProduct):
171 return TensorProduct(list(self.observables) + list(other.observables))
173 if isinstance(other, Observable):
174 return TensorProduct(list(self.observables) + [other])
176 raise ValueError("Can only perform tensor products between observables.")
178 def __rmatmul__(self, other):
179 if isinstance(other, Observable):
180 return TensorProduct([other] + list(self.observables))
182 raise ValueError("Can only perform tensor products between observables.")
184 def __repr__(self):
185 return "TensorProduct(" + ", ".join([repr(o) for o in self.observables]) + ")"
187 def __eq__(self, other):
188 return self.matrix_equivalence(other)
191Observable.register_observable(TensorProduct)
194class Hermitian(Observable):
195 """Hermitian matrix as an observable."""
197 def __init__(self, matrix: np.ndarray, display_name: str = "Hermitian"):
198 """
199 Args:
200 matrix (numpy.ndarray): Hermitian matrix which defines the observable.
201 display_name (str): Name to be used for an instance of this Hermitian matrix
202 observable for circuit diagrams. Defaults to `Hermitian`.
204 Raises:
205 ValueError: If `matrix` is not a two-dimensional square matrix,
206 or has a dimension length which is not a positive exponent of 2,
207 or is non-hermitian.
209 Example:
210 >>> Observable.Hermitian(matrix=np.array([[0, 1],[1, 0]]))
211 """
212 verify_quantum_operator_matrix_dimensions(matrix)
213 self._matrix = np.array(matrix, dtype=complex)
214 qubit_count = int(np.log2(self._matrix.shape[0]))
216 if not is_hermitian(self._matrix):
217 raise ValueError(f"{self._matrix} is not hermitian")
219 super().__init__(qubit_count=qubit_count, ascii_symbols=[display_name] * qubit_count)
221 def to_ir(self) -> List[List[List[List[float]]]]:
222 return [
223 [[[element.real, element.imag] for element in row] for row in self._matrix.tolist()]
224 ]
226 def to_matrix(self) -> np.ndarray:
227 return self._matrix
229 def __eq__(self, other) -> bool:
230 return self.matrix_equivalence(other)
233Observable.register_observable(Hermitian)