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. 

13 

14from __future__ import annotations 

15 

16import re 

17from typing import List 

18 

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 

24 

25 

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

34 

35 

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

41 

42 def __init__(self): 

43 super().__init__(ascii_symbol=["StateVector"]) 

44 

45 def to_ir(self) -> ir.StateVector: 

46 return ir.StateVector.construct() 

47 

48 @staticmethod 

49 @circuit.subroutine(register=True) 

50 def state_vector() -> ResultType: 

51 """Registers this function into the circuit class. 

52 

53 Returns: 

54 ResultType: state vector as a requested result type 

55 

56 Examples: 

57 >>> circ = Circuit().state_vector() 

58 """ 

59 return ResultType.StateVector() 

60 

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

62 if isinstance(other, StateVector): 

63 return True 

64 return False 

65 

66 def __copy__(self) -> StateVector: 

67 return type(self)() 

68 

69 

70ResultType.register_result_type(StateVector) 

71 

72 

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

78 

79 def __init__(self, state: List[str]): 

80 """ 

81 Args: 

82 state (List[str]): list of quantum states as strings with "0" and "1" 

83 

84 Raises: 

85 ValueError: If state is None or an empty list 

86 

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 

98 

99 @property 

100 def state(self) -> List[str]: 

101 return self._state 

102 

103 def to_ir(self) -> ir.Amplitude: 

104 return ir.Amplitude.construct(states=self.state) 

105 

106 @staticmethod 

107 @circuit.subroutine(register=True) 

108 def amplitude(state: List[str]) -> ResultType: 

109 """Registers this function into the circuit class. 

110 

111 Args: 

112 state (List[str]): list of quantum states as strings with "0" and "1" 

113 

114 Returns: 

115 ResultType: state vector as a requested result type 

116 

117 Examples: 

118 >>> circ = Circuit().amplitude(state=["01", "10"]) 

119 """ 

120 return ResultType.Amplitude(state=state) 

121 

122 def __eq__(self, other): 

123 if isinstance(other, Amplitude): 

124 return self.state == other.state 

125 return False 

126 

127 def __repr__(self): 

128 return f"Amplitude(state={self.state})" 

129 

130 def __copy__(self): 

131 return type(self)(state=self.state) 

132 

133 

134ResultType.register_result_type(Amplitude) 

135 

136 

137class Probability(ResultType): 

138 """Probability in the computational basis as the requested result type. 

139 

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. 

142 

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

146 

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. 

153 

154 Examples: 

155 >>> ResultType.Probability(target=[0, 1]) 

156 """ 

157 super().__init__(ascii_symbol=["Prob"]) 

158 self._target = QubitSet(target) 

159 

160 @property 

161 def target(self) -> QubitSet: 

162 return self._target 

163 

164 @target.setter 

165 def target(self, target: QubitSetInput) -> None: 

166 self._target = QubitSet(target) 

167 

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

173 

174 @staticmethod 

175 @circuit.subroutine(register=True) 

176 def probability(target: QubitSetInput = None) -> ResultType: 

177 """Registers this function into the circuit class. 

178 

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. 

183 

184 Returns: 

185 ResultType: probability as a requested result type 

186 

187 Examples: 

188 >>> circ = Circuit().probability(target=[0, 1]) 

189 """ 

190 return ResultType.Probability(target=target) 

191 

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

193 if isinstance(other, Probability): 

194 return self.target == other.target 

195 return False 

196 

197 def __repr__(self) -> str: 

198 return f"Probability(target={self.target})" 

199 

200 def __copy__(self) -> Probability: 

201 return type(self)(target=self.target) 

202 

203 

204ResultType.register_result_type(Probability) 

205 

206 

207class Expectation(ObservableResultType): 

208 """Expectation of specified target qubit set and observable as the requested result type. 

209 

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. 

213 

214 For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported 

215 only by simulators and represents the exact result. 

216 

217 See :mod:`braket.circuits.observables` module for all of the supported observables. 

218 """ 

219 

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 

227 

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. 

231 

232 Examples: 

233 >>> ResultType.Expectation(observable=Observable.Z(), target=0) 

234 

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) 

239 

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

247 

248 @staticmethod 

249 @circuit.subroutine(register=True) 

250 def expectation(observable: Observable, target: QubitSetInput = None) -> ResultType: 

251 """Registers this function into the circuit class. 

252 

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 

258 

259 Returns: 

260 ResultType: expectation as a requested result type 

261 

262 Examples: 

263 >>> circ = Circuit().expectation(observable=Observable.Z(), target=0) 

264 """ 

265 return ResultType.Expectation(observable=observable, target=target) 

266 

267 

268ResultType.register_result_type(Expectation) 

269 

270 

271class Sample(ObservableResultType): 

272 """Sample of specified target qubit set and observable as the requested result type. 

273 

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. 

277 

278 This is only available for `shots>0`. 

279 

280 See :mod:`braket.circuits.observables` module for all of the supported observables. 

281 """ 

282 

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 

290 

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. 

294 

295 Examples: 

296 >>> ResultType.Sample(observable=Observable.Z(), target=0) 

297 

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) 

302 

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

310 

311 @staticmethod 

312 @circuit.subroutine(register=True) 

313 def sample(observable: Observable, target: QubitSetInput = None) -> ResultType: 

314 """Registers this function into the circuit class. 

315 

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 

321 

322 Returns: 

323 ResultType: sample as a requested result type 

324 

325 Examples: 

326 >>> circ = Circuit().sample(observable=Observable.Z(), target=0) 

327 """ 

328 return ResultType.Sample(observable=observable, target=target) 

329 

330 

331ResultType.register_result_type(Sample) 

332 

333 

334class Variance(ObservableResultType): 

335 """Variance of specified target qubit set and observable as the requested result type. 

336 

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. 

340 

341 For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported 

342 only by simulators and represents the exact result. 

343 

344 See :mod:`braket.circuits.observables` module for all of the supported observables. 

345 """ 

346 

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 

354 

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. 

358 

359 Examples: 

360 >>> ResultType.Variance(observable=Observable.Z(), target=0) 

361 

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) 

366 

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

374 

375 @staticmethod 

376 @circuit.subroutine(register=True) 

377 def variance(observable: Observable, target: QubitSetInput = None) -> ResultType: 

378 """Registers this function into the circuit class. 

379 

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 

385 

386 Returns: 

387 ResultType: variance as a requested result type 

388 

389 Examples: 

390 >>> circ = Circuit().variance(observable=Observable.Z(), target=0) 

391 """ 

392 return ResultType.Variance(observable=observable, target=target) 

393 

394 

395ResultType.register_result_type(Variance)