Coverage for /home/runner/work/tket/tket/pytket/pytket/quipper/quipper.py: 80%

395 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-14 11:30 +0000

1# Copyright Quantinuum 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15from enum import Enum, unique 

16from math import pi 

17from typing import NamedTuple 

18 

19from lark import Lark, Transformer, Tree 

20 

21from pytket.circuit import CircBox, Circuit, OpType 

22 

23# The Lark grammar, transformer and type definitions below are adapted from the 

24# code in Eddie Schoute's `quippy` project 

25# (https://github.com/eddieschoute/quippy). The main enhancements are support 

26# for multi-qubit gates and correct handling of negative controls on qubit 0. 

27 

28 

29# Types 

30class Wire(NamedTuple): 

31 i: int 

32 

33 

34class ControlWire(NamedTuple): 

35 wire: Wire 

36 negative: bool 

37 

38 

39class Control(NamedTuple): 

40 controlled: list[ControlWire] 

41 no_control: bool 

42 

43 

44@unique 

45class TypeAssignment_Type(Enum): 

46 Qbit = 1 

47 Cbit = 2 

48 

49 

50class TypeAssignment(NamedTuple): 

51 wire: Wire 

52 type: TypeAssignment_Type 

53 

54 

55class Gate: 

56 pass 

57 

58 

59@unique 

60class QGate_Op(Enum): 

61 Not = 1 # Pauli X 

62 H = 2 # Hadamard 

63 MultiNot = 3 # Multi-target not 

64 Y = 4 # Pauli Y 

65 Z = 5 # Pauli Z 

66 S = 6 # Clifford S 

67 T = 7 # Clifford T = sqrt(S) 

68 E = 8 # Clifford E = H S (omega)^3 

69 Omega = 9 # scalar = exp(i pi / 4) 

70 V = 10 # V = sqrt(X) 

71 Swap = 11 

72 W = 12 # W is self-inverse and diagonalizes Swap 

73 IX = 13 # i X 

74 

75 

76class QGate( 

77 Gate, 

78 NamedTuple( 

79 "QGate", 

80 [ 

81 ("op", QGate_Op), 

82 ("inverted", bool), 

83 ("wires", list[Wire]), 

84 ("control", Control), 

85 ], 

86 ), 

87): 

88 pass 

89 

90 

91@unique 

92class QRot_Op(Enum): 

93 ExpZt = 1 # exp(−i Z t) 

94 R = 2 # R(2 / 2^t) (notation is confusing but see Monad.hs) 

95 

96 

97class QRot( 

98 Gate, 

99 NamedTuple( 

100 "QRot", 

101 [("op", QRot_Op), ("inverted", bool), ("timestep", float), ("wire", Wire)], 

102 ), 

103): 

104 pass 

105 

106 

107class QInit(Gate, NamedTuple("QInit", [("value", bool), ("wire", Wire)])): 

108 pass 

109 

110 

111class CInit(Gate, NamedTuple("CInit", [("value", bool), ("wire", Wire)])): 

112 pass 

113 

114 

115class QTerm(Gate, NamedTuple("QTerm", [("value", bool), ("wire", Wire)])): 

116 pass 

117 

118 

119class CTerm(Gate, NamedTuple("CTerm", [("value", bool), ("wire", Wire)])): 

120 pass 

121 

122 

123class QMeas(Gate, NamedTuple("QMeas", [("wire", Wire)])): 

124 pass 

125 

126 

127class QDiscard(Gate, NamedTuple("QDiscard", [("wire", Wire)])): 

128 pass 

129 

130 

131class CDiscard(Gate, NamedTuple("CDiscard", [("wire", Wire)])): 

132 pass 

133 

134 

135class SubroutineCall( 

136 Gate, 

137 NamedTuple( 

138 "SubroutineCall", 

139 [ 

140 ("repetitions", int), 

141 ("name", str), 

142 ("shape", str), 

143 ("inverted", bool), 

144 ("inputs", list[Wire]), 

145 ("outputs", list[Wire]), 

146 ("control", Control), 

147 ], 

148 ), 

149): 

150 pass 

151 

152 

153class Comment( 

154 Gate, 

155 NamedTuple( 

156 "Comment", 

157 [ 

158 ("comment", str), 

159 ("inverted", bool), 

160 ("wire_comments", list[tuple[Wire, str]]), 

161 ], 

162 ), 

163): 

164 pass 

165 

166 

167class Program(NamedTuple): 

168 inputs: list[TypeAssignment] 

169 gates: list[Gate] 

170 outputs: list[TypeAssignment] 

171 

172 

173@unique 

174class Subroutine_Control(Enum): 

175 yes = 1 

176 no = 2 

177 classically = 3 

178 

179 

180class Subroutine(NamedTuple): 

181 name: str 

182 shape: str 

183 controllable: Subroutine_Control 

184 circuit: Program 

185 

186 

187class Start(NamedTuple): 

188 circuit: Program 

189 subroutines: list[Subroutine] 

190 

191 

192# Transformer 

193class QuipperTransformer(Transformer): 

194 def int(self, t: list) -> int: 

195 return int(t[0]) 

196 

197 def float(self, t: list) -> float: 

198 return float(t[0]) 

199 

200 def string(self, t: list) -> str: 

201 return str(t[0][1:-1]) 

202 

203 def wire(self, t: list) -> Wire: 

204 return Wire(t[0]) 

205 

206 wire_list = list 

207 

208 def wire_string_list(self, t: list) -> list[tuple[Wire, str]]: 

209 wires = (el for i, el in enumerate(t) if i % 2 == 0) 

210 labels = (el for i, el in enumerate(t) if i % 2 == 1) 

211 return list(zip(wires, labels)) 

212 

213 def pos_control_wire(self, t: list) -> ControlWire: 

214 return ControlWire(t[0], False) 

215 

216 control_wire_list = list 

217 

218 def neg_control_wire(self, t: list) -> ControlWire: 

219 return ControlWire(t[0], True) 

220 

221 def type_assignment(self, t: list) -> TypeAssignment: 

222 ty = TypeAssignment_Type.Qbit if t[1] == "Qbit" else TypeAssignment_Type.Cbit 

223 return TypeAssignment(t[0], ty) 

224 

225 def arity(self, t: list) -> list[Tree]: 

226 return list(t) 

227 

228 def qgate(self, t: list) -> QGate: 

229 ops = QGate_Op 

230 n = t[0] 

231 if n == "not" or n == "x" or n == "X": 

232 op = ops.Not 

233 elif n == "H": 

234 op = ops.H 

235 elif n == "multinot": 

236 op = ops.MultiNot 

237 elif n == "Y": 

238 op = ops.Y 

239 elif n == "Z": 

240 op = ops.Z 

241 elif n == "S": 

242 op = ops.S 

243 elif n == "E": 

244 op = ops.E 

245 elif n == "T": 

246 op = ops.T 

247 elif n == "V": 

248 op = ops.V 

249 elif n == "swap": 

250 op = ops.Swap 

251 elif n == "omega": 

252 op = ops.Omega 

253 elif n == "iX": 

254 op = ops.IX 

255 elif n == "W": 255 ↛ 258line 255 didn't jump to line 258 because the condition on line 255 was always true

256 op = ops.W 

257 else: 

258 raise RuntimeError(f"Unknown QGate operation: {n}") 

259 return QGate(op=op, inverted=len(t[1].children) > 0, wires=t[2], control=t[3]) 

260 

261 def qrot1(self, t: list) -> QRot: 

262 return QRot( 

263 op=QRot_Op.ExpZt, timestep=t[0], inverted=len(t[1].children) > 0, wire=t[2] 

264 ) 

265 

266 def qrot2(self, t: list) -> QRot: 

267 return QRot( 

268 op=QRot_Op.R, timestep=t[0], inverted=len(t[1].children) > 0, wire=t[2] 

269 ) 

270 

271 def qinit(self, t: list) -> QInit: 

272 return QInit(value=(t[0] == "QInit1"), wire=t[1]) 

273 

274 def cinit(self, t: list) -> CInit: 

275 return CInit(value=(t[0] == "CInit1"), wire=t[1]) 

276 

277 def qterm(self, t: list) -> QTerm: 

278 return QTerm(value=(t[0] == "QTerm1"), wire=t[1]) 

279 

280 def cterm(self, t: list) -> CTerm: 

281 return CTerm(value=(t[0] == "CTerm1"), wire=t[1]) 

282 

283 def qmeas(self, t: list) -> QMeas: 

284 return QMeas(wire=t[0]) 

285 

286 def qdiscard(self, t: list) -> QDiscard: 

287 return QDiscard(wire=t[0]) 

288 

289 def cdiscard(self, t: list) -> CDiscard: 

290 return CDiscard(wire=t[0]) 

291 

292 def subroutine_call(self, t: list) -> SubroutineCall: 

293 repetitions = 1 

294 if t[0] is not None: 

295 assert isinstance(t[0], int) 

296 repetitions = t[0] 

297 return SubroutineCall( 

298 repetitions=repetitions, 

299 name=t[1], 

300 shape=t[2], 

301 inverted=len(t[3].children) > 0, 

302 inputs=t[4], 

303 outputs=t[5], 

304 control=t[6], 

305 ) 

306 

307 def comment(self, t: list) -> Comment: 

308 wire_comments = t[2] if len(t) > 2 else [] 

309 return Comment( 

310 comment=t[0], inverted=len(t[1].children) > 0, wire_comments=wire_comments 

311 ) 

312 

313 def control_app(self, t: list) -> Control: 

314 if not t: 

315 return Control(controlled=list(), no_control=False) 

316 if len(t) == 2: 

317 return Control(controlled=t[0], no_control=True) 

318 if t[0] == "with nocontrol": 

319 return Control(controlled=list(), no_control=True) 

320 return Control(controlled=t[0], no_control=False) 

321 

322 def circuit(self, t: list) -> Program: 

323 return Program(inputs=t[0], gates=t[1:-1], outputs=t[-1]) 

324 

325 def subroutine(self, t: list) -> Subroutine: 

326 if t[2] == "yes": 

327 controllable = Subroutine_Control.yes 

328 elif t[2] == "no": 328 ↛ 331line 328 didn't jump to line 331 because the condition on line 328 was always true

329 controllable = Subroutine_Control.no 

330 else: 

331 controllable = Subroutine_Control.classically 

332 return Subroutine( 

333 name=t[0], shape=t[1], controllable=controllable, circuit=t[3] 

334 ) 

335 

336 def start(self, t: list) -> Start: 

337 circuit = t.pop(0) 

338 return Start(circuit, list(t)) 

339 

340 

341# Utility function 

342def allowed(op: str, arity: int) -> bool: 

343 if op in ["Not", "IX", "H", "Y", "Z", "S", "T", "E", "Omega", "V"]: 

344 return arity == 1 

345 if op in ["Swap", "W"]: 

346 return arity == 2 

347 # MultiNot 

348 return True 

349 

350 

351# Class for constructing a pytket Circuit from a parsed Quipper program 

352class CircuitMaker: 

353 def __init__(self, subr: list[Subroutine]) -> None: 

354 self.subrd = dict((s.name, s) for s in subr) 

355 if len(self.subrd) != len(subr): 355 ↛ 356line 355 didn't jump to line 356 because the condition on line 355 was never true

356 raise TypeError("Repeated subroutine names") 

357 

358 def make_circuit(self, circ: Program) -> Circuit: 

359 inps, outs, gates = circ.inputs, circ.outputs, circ.gates 

360 if inps != outs: 360 ↛ 361line 360 didn't jump to line 361 because the condition on line 360 was never true

361 raise TypeError("Inputs don't match outputs") 

362 qbits = [inp.wire.i for inp in inps if inp.type.name == "Qbit"] 

363 cbits = [inp.wire.i for inp in inps if inp.type.name == "Cbit"] 

364 n_qbits = len(qbits) 

365 n_cbits = len(cbits) 

366 # Construct mappings from wire labels to tket indices. 

367 tkqbits = dict((qbits[i], i) for i in range(n_qbits)) 

368 tkcbits = dict((cbits[i], i) for i in range(n_cbits)) 

369 # Construct circuit in tket. 

370 c = Circuit(n_qbits, n_cbits) 

371 for gate in gates: 

372 if isinstance(gate, SubroutineCall): 

373 s = self.subrd[gate.name] 

374 if gate.shape != s.shape: 374 ↛ 376line 374 didn't jump to line 376 because the condition on line 374 was never true

375 # Just a sanity check, otherwise we assume 'shape' OK. 

376 raise TypeError("Mismatched shape in subroutine call") 

377 if gate.inputs != gate.outputs: 377 ↛ 378line 377 didn't jump to line 378 because the condition on line 377 was never true

378 raise TypeError("Mismatched outputs in subroutine call") 

379 if gate.control.controlled: 379 ↛ 380line 379 didn't jump to line 380 because the condition on line 379 was never true

380 raise NotImplementedError("Controls on subroutine") 

381 subcirc = self.make_circuit(s.circuit) # recurse 

382 if gate.inverted: 

383 subcirc = subcirc.dagger() 

384 cbox = CircBox(subcirc) 

385 for _ in range(gate.repetitions): 

386 c.add_circbox(cbox, [wire.i for wire in gate.inputs]) 

387 elif isinstance(gate, QGate): 

388 # The `nocontrol' flag is irrelevant here. 

389 op = gate.op.name 

390 qctrls, neg_qctrls, cctrls = [], [], [] 

391 for w in gate.control.controlled: 

392 idx = w.children[0].wire.i # type: ignore 

393 is_neg = w.children[0].negative # type: ignore 

394 if idx in qbits: 394 ↛ 399line 394 didn't jump to line 399 because the condition on line 394 was always true

395 tkqidx = tkqbits[idx] 

396 qctrls.append(tkqidx) 

397 if is_neg: 

398 neg_qctrls.append(tkqidx) 

399 elif idx in cbits: 

400 tkcidx = tkcbits[idx] 

401 cctrls.append(tkcidx) 

402 if cctrls: 402 ↛ 403line 402 didn't jump to line 403 because the condition on line 402 was never true

403 raise NotImplementedError("Classical controls") 

404 inv = gate.inverted 

405 wires = [tkqbits[wire.i] for wire in gate.wires] # all must be qubits 

406 n_wires = len(wires) 

407 if not allowed(op, n_wires): 407 ↛ 408line 407 didn't jump to line 408 because the condition on line 407 was never true

408 raise TypeError("'%s' gate with %d wires" % (op, n_wires)) 

409 n_ctrls = len(qctrls) 

410 # Negative control values must be handled using NOT gates 

411 # either side. 

412 for ctrl in neg_qctrls: 

413 c.X(ctrl) 

414 if op == "Not": 

415 if n_ctrls == 0: 

416 c.X(wires[0]) 

417 elif n_ctrls == 1: 

418 c.CX(qctrls[0], wires[0]) 

419 elif n_ctrls == 2: 

420 c.CCX(qctrls[0], qctrls[1], wires[0]) 

421 else: 

422 c.add_gate(OpType.CnX, qctrls + wires) 

423 elif op == "IX": 

424 if n_ctrls == 0: 424 ↛ 426line 424 didn't jump to line 426 because the condition on line 424 was always true

425 c.X(wires[0]) # ignore phase 

426 elif n_ctrls == 1: 

427 c.S(qctrls[0]) 

428 c.CX(qctrls[0], wires[0]) 

429 else: 

430 raise NotImplementedError("IX with more than 1 control") 

431 elif op == "MultiNot": 

432 # Implement as a series of (controlled) X gates. 

433 for wire in wires: 

434 if n_ctrls == 0: 434 ↛ 435line 434 didn't jump to line 435 because the condition on line 434 was never true

435 c.X(wire) 

436 elif n_ctrls == 1: 

437 c.CX(qctrls[0], wire) 

438 elif n_ctrls == 2: 

439 c.CCX(qctrls[0], qctrls[1], wire) 

440 else: 

441 c.add_gate(OpType.CnX, qctrls + [wire]) 

442 elif op == "H": 

443 if n_ctrls == 0: 

444 c.H(wires[0]) 

445 elif n_ctrls == 1: 

446 c.CH(qctrls[0], wires[0]) 

447 else: 

448 raise NotImplementedError("H with more than 1 control") 

449 elif op == "Y": 

450 if n_ctrls == 0: 

451 c.Y(wires[0]) 

452 elif n_ctrls == 1: 452 ↛ 455line 452 didn't jump to line 455 because the condition on line 452 was always true

453 c.CY(qctrls[0], wires[0]) 

454 else: 

455 raise NotImplementedError("Y with more than 1 control") 

456 elif op == "Z": 

457 if n_ctrls == 0: 

458 c.Z(wires[0]) 

459 elif n_ctrls == 1: 459 ↛ 462line 459 didn't jump to line 462 because the condition on line 459 was always true

460 c.CZ(qctrls[0], wires[0]) 

461 else: 

462 raise NotImplementedError("Z with more than 1 control") 

463 elif op == "S": 

464 if n_ctrls == 0: 

465 if inv: 

466 c.Sdg(wires[0]) 

467 else: 

468 c.S(wires[0]) 

469 elif n_ctrls == 1: 469 ↛ 476line 469 didn't jump to line 476 because the condition on line 469 was always true

470 # S = U1(1/2) 

471 if inv: 471 ↛ 472line 471 didn't jump to line 472 because the condition on line 471 was never true

472 c.add_gate(OpType.CU1, -0.5, [qctrls[0], wires[0]]) 

473 else: 

474 c.add_gate(OpType.CU1, 0.5, [qctrls[0], wires[0]]) 

475 else: 

476 raise NotImplementedError("S with more than 1 control") 

477 elif op == "T": 

478 if n_ctrls == 0: 

479 if inv: 

480 c.Tdg(wires[0]) 

481 else: 

482 c.T(wires[0]) 

483 elif n_ctrls == 1: 483 ↛ 490line 483 didn't jump to line 490 because the condition on line 483 was always true

484 # T = U1(1/4) 

485 if inv: 485 ↛ 486line 485 didn't jump to line 486 because the condition on line 485 was never true

486 c.add_gate(OpType.CU1, -0.25, [qctrls[0], wires[0]]) 

487 else: 

488 c.add_gate(OpType.CU1, 0.25, [qctrls[0], wires[0]]) 

489 else: 

490 raise NotImplementedError("T with more than 1 control") 

491 elif op == "E": 

492 # E = H S^3 omega^3 = H Sdg up to a scalar. 

493 if n_ctrls == 0: 493 ↛ 501line 493 didn't jump to line 501 because the condition on line 493 was always true

494 if inv: 

495 c.H(wires[0]) 

496 c.S(wires[0]) 

497 else: 

498 c.Sdg(wires[0]) 

499 c.H(wires[0]) 

500 else: 

501 raise NotImplementedError("Controlled E") 

502 elif op == "Omega": 

503 if n_ctrls == 0: 503 ↛ 505line 503 didn't jump to line 505 because the condition on line 503 was always true

504 pass # ignore phase 

505 elif n_ctrls == 1: 

506 c.Rz(0.25, qctrls[0]) 

507 else: 

508 raise NotImplementedError("Omega with more than 1 control") 

509 elif op == "V": 

510 if n_ctrls == 0: 510 ↛ 516line 510 didn't jump to line 516 because the condition on line 510 was always true

511 if inv: 

512 c.add_gate(OpType.Vdg, wires) 

513 else: 

514 c.add_gate(OpType.V, wires) 

515 else: 

516 raise NotImplementedError("Controlled V") 

517 elif op == "Swap": 

518 if n_ctrls == 0: 

519 c.SWAP(wires[0], wires[1]) 

520 elif n_ctrls == 1: 520 ↛ 523line 520 didn't jump to line 523 because the condition on line 520 was always true

521 c.CSWAP(qctrls[0], wires[0], wires[1]) 

522 else: 

523 raise NotImplementedError("SWAP with more than 2 controls") 

524 elif op == "W": 524 ↛ 544line 524 didn't jump to line 544 because the condition on line 524 was always true

525 # W is self-inverse. 

526 # Is there a simpler way to synthesize this? 

527 if n_ctrls == 0: 527 ↛ 542line 527 didn't jump to line 542 because the condition on line 527 was always true

528 c.Rz(1.25, wires[0]) 

529 c.Ry(1.0, wires[0]) 

530 c.T(wires[1]) 

531 c.Ry(1.0, wires[1]) 

532 c.CX(wires[1], wires[0]) 

533 c.Ry(-0.25, wires[1]) 

534 c.CX(wires[0], wires[1]) 

535 c.Ry(0.25, wires[1]) 

536 c.CX(wires[1], wires[0]) 

537 c.T(wires[0]) 

538 c.Ry(1.0, wires[0]) 

539 c.Rz(1.25, wires[1]) 

540 c.Ry(1.0, wires[1]) 

541 else: 

542 raise NotImplementedError("Controlled W") 

543 else: 

544 raise TypeError("Unknown op type: %s" % op) 

545 # Apply the NOT gates again for the negative controls. 

546 for ctrl in neg_qctrls: 

547 c.X(ctrl) 

548 elif isinstance(gate, QRot): 548 ↛ 568line 548 didn't jump to line 568 because the condition on line 548 was always true

549 op = gate.op.name 

550 inv = gate.inverted 

551 t = gate.timestep 

552 wire = tkqbits[gate.wire.i] # must be quantum 

553 if op == "ExpZt": 

554 if inv: 

555 c.Rz(-2 * t / pi, wire) 

556 else: 

557 c.Rz(2 * t / pi, wire) 

558 elif op == "R": 558 ↛ 564line 558 didn't jump to line 564 because the condition on line 558 was always true

559 if inv: 

560 c.Rz(-2 / t, wire) 

561 else: 

562 c.Rz(2 / t, wire) 

563 else: 

564 raise TypeError("Unknown op type: %s" % op) 

565 # QInit, QTerm, CInit, CTerm represent 'temporary' wires that 

566 # only occupy part of a circuit (and can be initialized with 0/1). 

567 # Not supported in pytket. 

568 elif isinstance(gate, QInit): 

569 wire = tkqbits[gate.wire.i] # must be quantum 

570 raise NotImplementedError("QInit") 

571 elif isinstance(gate, CInit): 

572 wire = tkcbits[gate.wire.i] # must be clasical 

573 raise NotImplementedError("CInit") 

574 elif isinstance(gate, QTerm): 

575 wire = tkqbits[gate.wire.i] # must be quantum 

576 raise NotImplementedError("QTerm") 

577 elif isinstance(gate, CTerm): 

578 wire = tkcbits[gate.wire.i] # must be clasical 

579 raise NotImplementedError("CTerm") 

580 elif isinstance(gate, QMeas): 

581 # QMeas turns a qubit into a bit. 

582 # Not supported in pytket. 

583 wire = tkqbits[gate.wire.i] # must be quantum 

584 raise NotImplementedError("QMeas") 

585 elif isinstance(gate, QDiscard): 

586 # QDiscard discards a qubit. 

587 # Not supported in pytket. 

588 wire = tkqbits[gate.wire.i] # must be quantum 

589 raise NotImplementedError("QDiscard") 

590 elif isinstance(gate, CDiscard): 

591 # CDiscard discards a bit. 

592 # Not supported in pytket. 

593 wire = tkcbits[gate.wire.i] # must be classical 

594 raise NotImplementedError("CDiscard") 

595 elif isinstance(gate, Comment): 

596 pass 

597 else: 

598 raise TypeError("Unknown gate type: %s" % type(gate)) 

599 return c 

600 

601 

602def circuit_from_quipper(input_file: str) -> Circuit: 

603 # pylint: disable=C0301 

604 """Generate a pytket :py:class:`Circuit` given a program in Quipper ASCII format. 

605 Limitations (due to current limitations of `pytket`): 

606 

607 * Subroutines must be defined over the full set of qubits. 

608 

609 * Global phases are ignored. 

610 

611 * Only limited support for controlled gates (depending on the gate type). 

612 

613 * No support for 'QInit' and 'QTerm'. All qubits must run from the begining 

614 to the end of the circuit. 

615 

616 * No support for the legacy keywords ('QNot', 'QMultinot', 'QHad', 'QSwap', 

617 'QW'). These are now represented in Quipper as types of 'QGate'. 

618 

619 * No support for 'QMeas' (which in Quipper converts a Q wire to a C wire). 

620 

621 * No support for: 'GPhase' (global phase), 'QPrep', 'QUnprep', 'QDiscard', 

622 or 'DTerm'. 

623 

624 * No support for classical operations ('CNot', 'CGate', 'CSwap', 'CInit', 

625 'CTerm', 'CDiscard'). 

626 

627 :param input_file: name of file to read 

628 """ 

629 

630 # Read Quipper program from file. 

631 with open(input_file) as f: 

632 quip = f.read() 

633 

634 # Parse the circuit using the QuipperTransformer. 

635 x = Lark( 

636 """ 

637 start : circuit subroutine* _NEWLINE* 

638 circuit : "Inputs:" arity (gate _NEWLINE)* "Outputs:" arity 

639 subroutine: _NEWLINE "Subroutine:" string _NEWLINE "Shape:" string _NEWLINE "Controllable:" SUB_CONTROL _NEWLINE circuit 

640 SUB_CONTROL : "yes" 

641 | "no" 

642 | "classically" 

643 arity : type_assignment ("," type_assignment)* ","? _NEWLINE 

644 type_assignment : wire ":" TYPE 

645 TYPE: "Qbit" 

646 | "Cbit" 

647 control_app : controlled? NO_CONTROL? 

648 ?controlled : "with controls=[" control_wire_list "]" 

649 NO_CONTROL : "with nocontrol" 

650 ?gate : qgate 

651 | qrot1 

652 | qrot2 

653 | gphase 

654 | cnot 

655 | cgate 

656 | cswap 

657 | qprep 

658 | qunprep 

659 | qinit 

660 | cinit 

661 | qterm 

662 | cterm 

663 | qmeas 

664 | qdiscard 

665 | cdiscard 

666 | dterm 

667 | subroutine_call 

668 | comment 

669 !inversion : "*"? 

670 qgate : "QGate[" string "]" inversion "(" wire_list ")" control_app 

671 qrot1 : "QRot[\\"exp(-i%Z)\\"," float "]" inversion "(" wire ")" 

672 qrot2 : "QRot[\\"R(2pi/%)\\"," int "]" inversion "(" wire ")" 

673 gphase : "Gphase() with t=" float control_app "with anchors=[" wire_list "]" 

674 cnot : "CNot(" wire ")" control_app 

675 cgate : "CGate[" string "]" inversion "(" wire_list ")" NO_CONTROL? 

676 cswap : "CSwap(" wire "," wire ")" control_app 

677 qprep : "QPrep(" wire ")" NO_CONTROL? 

678 qunprep : "QUnprep(" wire ")" NO_CONTROL? 

679 qinit : QINIT_STATE "(" wire ")" NO_CONTROL? 

680 QINIT_STATE : "QInit0" | "QInit1" 

681 cinit : CINIT_STATE "(" wire ")" NO_CONTROL? 

682 CINIT_STATE : "CInit0" | "CInit1" 

683 qterm : QTERM_STATE "(" wire ")" NO_CONTROL? 

684 QTERM_STATE : "QTerm0" | "QTerm1" 

685 cterm : CTERM_STATE "(" wire ")" NO_CONTROL? 

686 CTERM_STATE : "CTerm0" | "CTerm1" 

687 qmeas : "QMeas(" wire ")" 

688 qdiscard : "QDiscard(" wire ")" 

689 cdiscard : "CDiscard(" wire ")" 

690 dterm : DTERM_STATE "(" wire ")" 

691 DTERM_STATE : "DTerm0" | "DTerm1" 

692 subroutine_call : "Subroutine" ["(x" int ")"] "[" string ", shape" string "]" inversion "(" wire_list ") -> (" wire_list ")" control_app 

693 comment : "Comment[" string "]" inversion "(" wire_string_list? ")" 

694 wire_string_list: wire ":" string ("," wire ":" string)* 

695 wire : int 

696 wire_list : wire ("," wire)* 

697 pos_control_wire : "+" wire 

698 neg_control_wire : "-" wire 

699 control_wire : pos_control_wire | neg_control_wire 

700 control_wire_list : control_wire ("," control_wire)* 

701 %import common.WS_INLINE -> WS 

702 %ignore WS 

703 %import common.CR 

704 %import common.LF 

705 _NEWLINE: CR? LF 

706 %import common.ESCAPED_STRING 

707 string: ESCAPED_STRING 

708 %import common.SIGNED_FLOAT 

709 float : SIGNED_FLOAT 

710 %import common.INT 

711 int : INT 

712 """, 

713 parser="lalr", 

714 transformer=QuipperTransformer(), 

715 ).parse(quip) 

716 

717 # Load the subroutine list. 

718 maker = CircuitMaker(x.subroutines) # type: ignore 

719 

720 # Make the tket circuit. 

721 return maker.make_circuit(x.circuit) # type: ignore