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 

16from typing import List 

17 

18import braket.ir.jaqcd as ir 

19from braket.circuits import circuit 

20from braket.circuits.observable import Observable 

21from braket.circuits.qubit_set import QubitSet, QubitSetInput 

22from braket.circuits.result_type import ResultType 

23 

24 

25""" 

26To add a new result type: 

27 1. Implement the class and extend `ResultType` 

28 2. Add a method with the `@circuit.subroutine(register=True)` decorator. Method name 

29 will be added into the `Circuit` class. This method is the default way 

30 clients add this result type to a circuit. 

31 3. Register the class with the `ResultType` class via `ResultType.register_result_type()`. 

32""" 

33 

34 

35class StateVector(ResultType): 

36 """The full state vector as a requested result type.""" 

37 

38 def __init__(self): 

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

40 

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

42 return ir.StateVector() 

43 

44 @staticmethod 

45 @circuit.subroutine(register=True) 

46 def state_vector() -> ResultType: 

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

48 

49 Returns: 

50 ResultType: state vector as a requested result type 

51 

52 Examples: 

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

54 """ 

55 return ResultType.StateVector() 

56 

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

58 if isinstance(other, StateVector): 

59 return True 

60 return False 

61 

62 def __copy__(self) -> StateVector: 

63 return type(self)() 

64 

65 

66ResultType.register_result_type(StateVector) 

67 

68 

69class Amplitude(ResultType): 

70 """The amplitude of specified quantum states as a requested result type.""" 

71 

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

73 """ 

74 Args: 

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

76 

77 Raises: 

78 ValueError: If state is None or an empty list 

79 

80 Examples: 

81 >>> ResultType.Amplitude(state=['01', '10']) 

82 """ 

83 super().__init__(ascii_symbol=["Amplitude"]) 

84 if not state: 

85 raise ValueError("A non-empty list of states must be specified e.g. ['01', '10']") 

86 self._state = state 

87 

88 @property 

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

90 return self._state 

91 

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

93 return ir.Amplitude(states=self.state) 

94 

95 @staticmethod 

96 @circuit.subroutine(register=True) 

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

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

99 

100 Args: 

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

102 

103 Returns: 

104 ResultType: state vector as a requested result type 

105 

106 Examples: 

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

108 """ 

109 return ResultType.Amplitude(state=state) 

110 

111 def __eq__(self, other): 

112 if isinstance(other, Amplitude): 

113 return self.state == other.state 

114 return False 

115 

116 def __repr__(self): 

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

118 

119 def __copy__(self): 

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

121 

122 

123ResultType.register_result_type(Amplitude) 

124 

125 

126class Probability(ResultType): 

127 """Probability as the requested result type. 

128 

129 It can be the probability of all states if no targets are specified or the marginal probability 

130 of a restricted set of states if only a subset of all qubits are specified as target.""" 

131 

132 def __init__(self, target: QubitSetInput = None): 

133 """ 

134 Args: 

135 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

136 result type is requested for. Default is None, which means all qubits for the 

137 circuit. 

138 

139 Examples: 

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

141 """ 

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

143 self._target = QubitSet(target) 

144 

145 @property 

146 def target(self) -> QubitSet: 

147 return self._target 

148 

149 @target.setter 

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

151 self._target = QubitSet(target) 

152 

153 def to_ir(self) -> ir.Probability: 

154 return ir.Probability(targets=list(self.target)) if self.target else ir.Probability() 

155 

156 @staticmethod 

157 @circuit.subroutine(register=True) 

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

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

160 

161 Args: 

162 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

163 result type is requested for. Default is None, which means all qubits for the 

164 circuit. 

165 

166 Returns: 

167 ResultType: probability as a requested result type 

168 

169 Examples: 

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

171 """ 

172 return ResultType.Probability(target=target) 

173 

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

175 if isinstance(other, Probability): 

176 return self.target == other.target 

177 return False 

178 

179 def __repr__(self) -> str: 

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

181 

182 def __copy__(self) -> Probability: 

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

184 

185 

186ResultType.register_result_type(Probability) 

187 

188 

189class ObservableResultType(ResultType): 

190 """ 

191 Result types with observables and targets. 

192 If no targets are specified, the observable must only operate on 1 qubit and it 

193 will be applied to all qubits in parallel. Otherwise, the number of specified targets 

194 must be equivalent to the number of qubits the observable can be applied to. 

195 

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

197 """ 

198 

199 def __init__(self, ascii_symbol: str, observable: Observable, target: QubitSetInput = None): 

200 """ 

201 Args: 

202 observable (Observable): the observable for the result type 

203 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

204 result type is requested for. Default is None, which means the observable must 

205 only operate on 1 qubit and it will be applied to all qubits in parallel 

206 

207 Raises: 

208 ValueError: If the observable's qubit count and the number of target qubits 

209 are not equal. Or, if target=None and the observable's qubit count is not 1. 

210 """ 

211 super().__init__(ascii_symbol) 

212 self._observable = observable 

213 self._target = QubitSet(target) 

214 if not self._target: 

215 if self._observable.qubit_count != 1: 

216 raise ValueError( 

217 f"Observable {self._observable} must only operate on 1 qubit for target=None" 

218 ) 

219 elif self._observable.qubit_count != len(self._target): 

220 raise ValueError( 

221 f"Observable's qubit count and the number of target qubits must be equal" 

222 ) 

223 

224 @property 

225 def observable(self) -> Observable: 

226 return self._observable 

227 

228 @property 

229 def target(self) -> QubitSet: 

230 return self._target 

231 

232 @target.setter 

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

234 self._target = QubitSet(target) 

235 

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

237 if isinstance(other, ObservableResultType): 

238 return self.target == other.target and self.observable == other.observable 

239 return False 

240 

241 def __repr__(self) -> str: 

242 return f"{self.name}(observable={self.observable}, target={self.target})" 

243 

244 def __copy__(self) -> Expectation: 

245 return type(self)(observable=self.observable, target=self.target) 

246 

247 

248class Expectation(ObservableResultType): 

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

250 

251 If no targets are specified, the observable must only operate on 1 qubit and it 

252 will be applied to all qubits in parallel. Otherwise, the number of specified targets 

253 must be equivalent to the number of qubits the observable can be applied to. 

254 

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

256 """ 

257 

258 def __init__(self, observable: Observable, target: QubitSetInput = None): 

259 """ 

260 Args: 

261 observable (Observable): the observable for the result type 

262 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

263 result type is requested for. Default is None, which means the observable must 

264 only operate on 1 qubit and it will be applied to all qubits in parallel 

265 

266 Raises: 

267 ValueError: If the observable's qubit count and the number of target qubits 

268 are not equal. Or, if target=None and the observable's qubit count is not 1. 

269 

270 Examples: 

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

272 

273 >>> tensor_product = Observable.Y() @ Observable.Z() 

274 >>> ResultType.Expectation(observable=tensor_product, target=[0, 1]) 

275 """ 

276 super().__init__(ascii_symbol=["Expectation"], observable=observable, target=target) 

277 

278 def to_ir(self) -> ir.Expectation: 

279 if self.target: 

280 return ir.Expectation(observable=self.observable.to_ir(), targets=list(self.target)) 

281 else: 

282 return ir.Expectation(observable=self.observable.to_ir()) 

283 

284 @staticmethod 

285 @circuit.subroutine(register=True) 

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

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

288 

289 Args: 

290 observable (Observable): the observable for the result type 

291 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

292 result type is requested for. Default is None, which means the observable must 

293 only operate on 1 qubit and it will be applied to all qubits in parallel 

294 

295 Returns: 

296 ResultType: expectation as a requested result type 

297 

298 Examples: 

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

300 """ 

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

302 

303 

304ResultType.register_result_type(Expectation) 

305 

306 

307class Sample(ObservableResultType): 

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

309 

310 If no targets are specified, the observable must only operate on 1 qubit and it 

311 will be applied to all qubits in parallel. Otherwise, the number of specified targets 

312 must be equivalent to the number of qubits the observable can be applied to. 

313 

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

315 """ 

316 

317 def __init__(self, observable: Observable, target: QubitSetInput = None): 

318 """ 

319 Args: 

320 observable (Observable): the observable for the result type 

321 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

322 result type is requested for. Default is None, which means the observable must 

323 only operate on 1 qubit and it will be applied to all qubits in parallel 

324 

325 Raises: 

326 ValueError: If the observable's qubit count and the number of target qubits 

327 are not equal. Or, if target=None and the observable's qubit count is not 1. 

328 

329 Examples: 

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

331 

332 >>> tensor_product = Observable.Y() @ Observable.Z() 

333 >>> ResultType.Sample(observable=tensor_product, target=[0, 1]) 

334 """ 

335 super().__init__(ascii_symbol=["Sample"], observable=observable, target=target) 

336 

337 def to_ir(self) -> ir.Sample: 

338 if self.target: 

339 return ir.Sample(observable=self.observable.to_ir(), targets=list(self.target)) 

340 else: 

341 return ir.Sample(observable=self.observable.to_ir()) 

342 

343 @staticmethod 

344 @circuit.subroutine(register=True) 

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

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

347 

348 Args: 

349 observable (Observable): the observable for the result type 

350 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

351 result type is requested for. Default is None, which means the observable must 

352 only operate on 1 qubit and it will be applied to all qubits in parallel 

353 

354 Returns: 

355 ResultType: sample as a requested result type 

356 

357 Examples: 

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

359 """ 

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

361 

362 

363ResultType.register_result_type(Sample) 

364 

365 

366class Variance(ObservableResultType): 

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

368 

369 If no targets are specified, the observable must only operate on 1 qubit and it 

370 will be applied to all qubits in parallel. Otherwise, the number of specified targets 

371 must be equivalent to the number of qubits the observable can be applied to. 

372 

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

374 """ 

375 

376 def __init__(self, observable: Observable, target: QubitSetInput = None): 

377 """ 

378 Args: 

379 observable (Observable): the observable for the result type 

380 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

381 result type is requested for. Default is None, which means the observable must 

382 only operate on 1 qubit and it will be applied to all qubits in parallel 

383 

384 Raises: 

385 ValueError: If the observable's qubit count and the number of target qubits 

386 are not equal. Or, if target=None and the observable's qubit count is not 1. 

387 

388 Examples: 

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

390 

391 >>> tensor_product = Observable.Y() @ Observable.Z() 

392 >>> ResultType.Variance(observable=tensor_product, target=[0, 1]) 

393 """ 

394 super().__init__(ascii_symbol=["Variance"], observable=observable, target=target) 

395 

396 def to_ir(self) -> ir.Variance: 

397 if self.target: 

398 return ir.Variance(observable=self.observable.to_ir(), targets=list(self.target)) 

399 else: 

400 return ir.Variance(observable=self.observable.to_ir()) 

401 

402 @staticmethod 

403 @circuit.subroutine(register=True) 

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

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

406 

407 Args: 

408 observable (Observable): the observable for the result type 

409 target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the 

410 result type is requested for. Default is None, which means the observable must 

411 only operate on 1 qubit and it will be applied to all qubits in parallel 

412 

413 Returns: 

414 ResultType: variance as a requested result type 

415 

416 Examples: 

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

418 """ 

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

420 

421 

422ResultType.register_result_type(Variance)