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
« 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.
15from enum import Enum, unique
16from math import pi
17from typing import NamedTuple
19from lark import Lark, Transformer, Tree
21from pytket.circuit import CircBox, Circuit, OpType
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.
29# Types
30class Wire(NamedTuple):
31 i: int
34class ControlWire(NamedTuple):
35 wire: Wire
36 negative: bool
39class Control(NamedTuple):
40 controlled: list[ControlWire]
41 no_control: bool
44@unique
45class TypeAssignment_Type(Enum):
46 Qbit = 1
47 Cbit = 2
50class TypeAssignment(NamedTuple):
51 wire: Wire
52 type: TypeAssignment_Type
55class Gate:
56 pass
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
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
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)
97class QRot(
98 Gate,
99 NamedTuple(
100 "QRot",
101 [("op", QRot_Op), ("inverted", bool), ("timestep", float), ("wire", Wire)],
102 ),
103):
104 pass
107class QInit(Gate, NamedTuple("QInit", [("value", bool), ("wire", Wire)])):
108 pass
111class CInit(Gate, NamedTuple("CInit", [("value", bool), ("wire", Wire)])):
112 pass
115class QTerm(Gate, NamedTuple("QTerm", [("value", bool), ("wire", Wire)])):
116 pass
119class CTerm(Gate, NamedTuple("CTerm", [("value", bool), ("wire", Wire)])):
120 pass
123class QMeas(Gate, NamedTuple("QMeas", [("wire", Wire)])):
124 pass
127class QDiscard(Gate, NamedTuple("QDiscard", [("wire", Wire)])):
128 pass
131class CDiscard(Gate, NamedTuple("CDiscard", [("wire", Wire)])):
132 pass
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
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
167class Program(NamedTuple):
168 inputs: list[TypeAssignment]
169 gates: list[Gate]
170 outputs: list[TypeAssignment]
173@unique
174class Subroutine_Control(Enum):
175 yes = 1
176 no = 2
177 classically = 3
180class Subroutine(NamedTuple):
181 name: str
182 shape: str
183 controllable: Subroutine_Control
184 circuit: Program
187class Start(NamedTuple):
188 circuit: Program
189 subroutines: list[Subroutine]
192# Transformer
193class QuipperTransformer(Transformer):
194 def int(self, t: list) -> int:
195 return int(t[0])
197 def float(self, t: list) -> float:
198 return float(t[0])
200 def string(self, t: list) -> str:
201 return str(t[0][1:-1])
203 def wire(self, t: list) -> Wire:
204 return Wire(t[0])
206 wire_list = list
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))
213 def pos_control_wire(self, t: list) -> ControlWire:
214 return ControlWire(t[0], False)
216 control_wire_list = list
218 def neg_control_wire(self, t: list) -> ControlWire:
219 return ControlWire(t[0], True)
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)
225 def arity(self, t: list) -> list[Tree]:
226 return list(t)
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])
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 )
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 )
271 def qinit(self, t: list) -> QInit:
272 return QInit(value=(t[0] == "QInit1"), wire=t[1])
274 def cinit(self, t: list) -> CInit:
275 return CInit(value=(t[0] == "CInit1"), wire=t[1])
277 def qterm(self, t: list) -> QTerm:
278 return QTerm(value=(t[0] == "QTerm1"), wire=t[1])
280 def cterm(self, t: list) -> CTerm:
281 return CTerm(value=(t[0] == "CTerm1"), wire=t[1])
283 def qmeas(self, t: list) -> QMeas:
284 return QMeas(wire=t[0])
286 def qdiscard(self, t: list) -> QDiscard:
287 return QDiscard(wire=t[0])
289 def cdiscard(self, t: list) -> CDiscard:
290 return CDiscard(wire=t[0])
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 )
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 )
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)
322 def circuit(self, t: list) -> Program:
323 return Program(inputs=t[0], gates=t[1:-1], outputs=t[-1])
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 )
336 def start(self, t: list) -> Start:
337 circuit = t.pop(0)
338 return Start(circuit, list(t))
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
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")
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
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`):
607 * Subroutines must be defined over the full set of qubits.
609 * Global phases are ignored.
611 * Only limited support for controlled gates (depending on the gate type).
613 * No support for 'QInit' and 'QTerm'. All qubits must run from the begining
614 to the end of the circuit.
616 * No support for the legacy keywords ('QNot', 'QMultinot', 'QHad', 'QSwap',
617 'QW'). These are now represented in Quipper as types of 'QGate'.
619 * No support for 'QMeas' (which in Quipper converts a Q wire to a C wire).
621 * No support for: 'GPhase' (global phase), 'QPrep', 'QUnprep', 'QDiscard',
622 or 'DTerm'.
624 * No support for classical operations ('CNot', 'CGate', 'CSwap', 'CInit',
625 'CTerm', 'CDiscard').
627 :param input_file: name of file to read
628 """
630 # Read Quipper program from file.
631 with open(input_file) as f:
632 quip = f.read()
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)
717 # Load the subroutine list.
718 maker = CircuitMaker(x.subroutines) # type: ignore
720 # Make the tket circuit.
721 return maker.make_circuit(x.circuit) # type: ignore