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 is 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 available on simulators only when `shots=0`. 

40 """ 

41 

42 def __init__(self): 

43 super().__init__(ascii_symbols=["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 the specified quantum states as a requested result type. 

76 This is available on simulators only when `shots=0`. 

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 if not state or not all( 

91 isinstance(amplitude, str) and re.fullmatch("^[01]+$", amplitude) for amplitude in state 

92 ): 

93 raise ValueError( 

94 "A non-empty list of states must be specified in binary encoding e.g. ['01', '10']" 

95 ) 

96 super().__init__(ascii_symbols=[f"Amplitude({','.join(state)})"]) 

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 

141 probability of a restricted set of states if only a subset of all qubits are specified as 

142 targets. 

143 

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

145 only on simulators and represents the exact result. 

146 """ 

147 

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

149 """ 

150 Args: 

151 target (int, Qubit, or iterable of int / Qubit, optional): The target qubits that the 

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

153 circuit. 

154 

155 Examples: 

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

157 """ 

158 self._target = QubitSet(target) 

159 ascii_symbols = ["Probability"] * len(self._target) if self._target else ["Probability"] 

160 super().__init__(ascii_symbols=ascii_symbols) 

161 

162 @property 

163 def target(self) -> QubitSet: 

164 return self._target 

165 

166 @target.setter 

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

168 self._target = QubitSet(target) 

169 

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

171 if self.target: 

172 return ir.Probability.construct(targets=list(self.target)) 

173 else: 

174 return ir.Probability.construct() 

175 

176 @staticmethod 

177 @circuit.subroutine(register=True) 

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

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

180 

181 Args: 

182 target (int, Qubit, or iterable of int / Qubit, optional): The target qubits that the 

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

184 circuit. 

185 

186 Returns: 

187 ResultType: probability as a requested result type 

188 

189 Examples: 

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

191 """ 

192 return ResultType.Probability(target=target) 

193 

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

195 if isinstance(other, Probability): 

196 return self.target == other.target 

197 return False 

198 

199 def __repr__(self) -> str: 

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

201 

202 def __copy__(self) -> Probability: 

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

204 

205 

206ResultType.register_result_type(Probability) 

207 

208 

209class Expectation(ObservableResultType): 

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

211 

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

213 is applied to all qubits in parallel. Otherwise, the number of specified targets 

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

215 

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

217 only by simulators and represents the exact result. 

218 

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

220 """ 

221 

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

223 """ 

224 Args: 

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

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

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

228 operate only on 1 qubit and it is applied to all qubits in parallel. 

229 

230 Raises: 

231 ValueError: If the observable's qubit count does not equal the number of target 

232 qubits, or if target=None and the observable's qubit count is not 1. 

233 

234 Examples: 

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

236 

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

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

239 """ 

240 super().__init__( 

241 ascii_symbols=[f"Expectation({obs_ascii})" for obs_ascii in observable.ascii_symbols], 

242 observable=observable, 

243 target=target, 

244 ) 

245 

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

247 if self.target: 

248 return ir.Expectation.construct( 

249 observable=self.observable.to_ir(), targets=list(self.target) 

250 ) 

251 else: 

252 return ir.Expectation.construct(observable=self.observable.to_ir()) 

253 

254 @staticmethod 

255 @circuit.subroutine(register=True) 

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

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

258 

259 Args: 

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

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

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

263 operate only on 1 qubit and it is applied to all qubits in parallel. 

264 

265 Returns: 

266 ResultType: expectation as a requested result type 

267 

268 Examples: 

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

270 """ 

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

272 

273 

274ResultType.register_result_type(Expectation) 

275 

276 

277class Sample(ObservableResultType): 

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

279 

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

281 is applied to all qubits in parallel. Otherwise, the number of specified targets 

282 must equal the number of qubits the observable can be applied to. 

283 

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

285 

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

287 """ 

288 

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

290 """ 

291 Args: 

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

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

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

295 operate only on 1 qubit and it is applied to all qubits in parallel. 

296 

297 Raises: 

298 ValueError: If the observable's qubit count is not equal to the number of target 

299 qubits, or if target=None and the observable's qubit count is not 1. 

300 

301 Examples: 

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

303 

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

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

306 """ 

307 super().__init__( 

308 ascii_symbols=[f"Sample({obs_ascii})" for obs_ascii in observable.ascii_symbols], 

309 observable=observable, 

310 target=target, 

311 ) 

312 

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

314 if self.target: 

315 return ir.Sample.construct( 

316 observable=self.observable.to_ir(), targets=list(self.target) 

317 ) 

318 else: 

319 return ir.Sample.construct(observable=self.observable.to_ir()) 

320 

321 @staticmethod 

322 @circuit.subroutine(register=True) 

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

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

325 

326 Args: 

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

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

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

330 operate only on 1 qubit and it is applied to all qubits in parallel. 

331 

332 Returns: 

333 ResultType: sample as a requested result type 

334 

335 Examples: 

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

337 """ 

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

339 

340 

341ResultType.register_result_type(Sample) 

342 

343 

344class Variance(ObservableResultType): 

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

346 

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

348 is applied to all qubits in parallel. Otherwise, the number of targets specified 

349 must equal the number of qubits that the observable can be applied to. 

350 

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

352 only by simulators and represents the exact result. 

353 

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

355 """ 

356 

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

358 """ 

359 Args: 

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

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

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

363 operate only on 1 qubit and it is applied to all qubits in parallel. 

364 

365 Raises: 

366 ValueError: If the observable's qubit count does not equal the number of target 

367 qubits, or if target=None and the observable's qubit count is not 1. 

368 

369 Examples: 

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

371 

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

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

374 """ 

375 super().__init__( 

376 ascii_symbols=[f"Variance({obs_ascii})" for obs_ascii in observable.ascii_symbols], 

377 observable=observable, 

378 target=target, 

379 ) 

380 

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

382 if self.target: 

383 return ir.Variance.construct( 

384 observable=self.observable.to_ir(), targets=list(self.target) 

385 ) 

386 else: 

387 return ir.Variance.construct(observable=self.observable.to_ir()) 

388 

389 @staticmethod 

390 @circuit.subroutine(register=True) 

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

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

393 

394 Args: 

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

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

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

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

399 

400 Returns: 

401 ResultType: variance as a requested result type 

402 

403 Examples: 

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

405 """ 

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

407 

408 

409ResultType.register_result_type(Variance)