Hide keyboard shortcuts

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 

14 

15import functools 

16from typing import List, Tuple 

17 

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) 

24 

25 

26class H(Observable): 

27 """Hadamard operation as an observable.""" 

28 

29 def __init__(self): 

30 """ 

31 Examples: 

32 >>> Observable.I() 

33 """ 

34 super().__init__(qubit_count=1, ascii_symbols=["H"]) 

35 

36 def to_ir(self) -> List[str]: 

37 return ["h"] 

38 

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) 

41 

42 

43Observable.register_observable(H) 

44 

45 

46class I(Observable): # noqa: E742, E261 

47 """Identity operation as an observable.""" 

48 

49 def __init__(self): 

50 """ 

51 Examples: 

52 >>> Observable.I() 

53 """ 

54 super().__init__(qubit_count=1, ascii_symbols=["I"]) 

55 

56 def to_ir(self) -> List[str]: 

57 return ["i"] 

58 

59 def to_matrix(self) -> np.ndarray: 

60 return np.array([[1.0, 0.0], [0.0, 1.0]], dtype=complex) 

61 

62 

63Observable.register_observable(I) 

64 

65 

66class X(Observable): 

67 """Pauli-X operation as an observable.""" 

68 

69 def __init__(self): 

70 """ 

71 Examples: 

72 >>> Observable.X() 

73 """ 

74 super().__init__(qubit_count=1, ascii_symbols=["X"]) 

75 

76 def to_ir(self) -> List[str]: 

77 return ["x"] 

78 

79 def to_matrix(self) -> np.ndarray: 

80 return np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex) 

81 

82 

83Observable.register_observable(X) 

84 

85 

86class Y(Observable): 

87 """Pauli-Y operation as an observable.""" 

88 

89 def __init__(self): 

90 """ 

91 Examples: 

92 >>> Observable.Y() 

93 """ 

94 super().__init__(qubit_count=1, ascii_symbols=["Y"]) 

95 

96 def to_ir(self) -> List[str]: 

97 return ["y"] 

98 

99 def to_matrix(self) -> np.ndarray: 

100 return np.array([[0.0, -1.0j], [1.0j, 0.0]], dtype=complex) 

101 

102 

103Observable.register_observable(Y) 

104 

105 

106class Z(Observable): 

107 """Pauli-Z operation as an observable.""" 

108 

109 def __init__(self): 

110 """ 

111 Examples: 

112 >>> Observable.Z() 

113 """ 

114 super().__init__(qubit_count=1, ascii_symbols=["Z"]) 

115 

116 def to_ir(self) -> List[str]: 

117 return ["z"] 

118 

119 def to_matrix(self) -> np.ndarray: 

120 return np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex) 

121 

122 

123Observable.register_observable(Z) 

124 

125 

126class TensorProduct(Observable): 

127 """Tensor product of observables""" 

128 

129 def __init__(self, observables: List[Observable]): 

130 """ 

131 Args: 

132 observables (List[Observable]): List of observables for tensor product 

133 

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)) 

144 

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) 

154 

155 def to_ir(self) -> List[str]: 

156 ir = [] 

157 for obs in self.observables: 

158 ir.extend(obs.to_ir()) 

159 return ir 

160 

161 @property 

162 def observables(self) -> Tuple[Observable]: 

163 """Tuple[Observable]: observables part of tensor product""" 

164 return self._observables 

165 

166 def to_matrix(self) -> np.ndarray: 

167 return functools.reduce(np.kron, [obs.to_matrix() for obs in self.observables]) 

168 

169 def __matmul__(self, other): 

170 if isinstance(other, TensorProduct): 

171 return TensorProduct(list(self.observables) + list(other.observables)) 

172 

173 if isinstance(other, Observable): 

174 return TensorProduct(list(self.observables) + [other]) 

175 

176 raise ValueError("Can only perform tensor products between observables.") 

177 

178 def __rmatmul__(self, other): 

179 if isinstance(other, Observable): 

180 return TensorProduct([other] + list(self.observables)) 

181 

182 raise ValueError("Can only perform tensor products between observables.") 

183 

184 def __repr__(self): 

185 return "TensorProduct(" + ", ".join([repr(o) for o in self.observables]) + ")" 

186 

187 def __eq__(self, other): 

188 return self.matrix_equivalence(other) 

189 

190 

191Observable.register_observable(TensorProduct) 

192 

193 

194class Hermitian(Observable): 

195 """Hermitian matrix as an observable.""" 

196 

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`. 

203 

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. 

208 

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])) 

215 

216 if not is_hermitian(self._matrix): 

217 raise ValueError(f"{self._matrix} is not hermitian") 

218 

219 super().__init__(qubit_count=qubit_count, ascii_symbols=[display_name] * qubit_count) 

220 

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 ] 

225 

226 def to_matrix(self) -> np.ndarray: 

227 return self._matrix 

228 

229 def __eq__(self, other) -> bool: 

230 return self.matrix_equivalence(other) 

231 

232 

233Observable.register_observable(Hermitian)