qoro-divi 0.2.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. divi/__init__.py +8 -0
  2. divi/_pbar.py +73 -0
  3. divi/circuits.py +139 -0
  4. divi/exp/cirq/__init__.py +7 -0
  5. divi/exp/cirq/_lexer.py +126 -0
  6. divi/exp/cirq/_parser.py +889 -0
  7. divi/exp/cirq/_qasm_export.py +37 -0
  8. divi/exp/cirq/_qasm_import.py +35 -0
  9. divi/exp/cirq/exception.py +21 -0
  10. divi/exp/scipy/_cobyla.py +342 -0
  11. divi/exp/scipy/pyprima/LICENCE.txt +28 -0
  12. divi/exp/scipy/pyprima/__init__.py +263 -0
  13. divi/exp/scipy/pyprima/cobyla/__init__.py +0 -0
  14. divi/exp/scipy/pyprima/cobyla/cobyla.py +599 -0
  15. divi/exp/scipy/pyprima/cobyla/cobylb.py +849 -0
  16. divi/exp/scipy/pyprima/cobyla/geometry.py +240 -0
  17. divi/exp/scipy/pyprima/cobyla/initialize.py +269 -0
  18. divi/exp/scipy/pyprima/cobyla/trustregion.py +540 -0
  19. divi/exp/scipy/pyprima/cobyla/update.py +331 -0
  20. divi/exp/scipy/pyprima/common/__init__.py +0 -0
  21. divi/exp/scipy/pyprima/common/_bounds.py +41 -0
  22. divi/exp/scipy/pyprima/common/_linear_constraints.py +46 -0
  23. divi/exp/scipy/pyprima/common/_nonlinear_constraints.py +64 -0
  24. divi/exp/scipy/pyprima/common/_project.py +224 -0
  25. divi/exp/scipy/pyprima/common/checkbreak.py +107 -0
  26. divi/exp/scipy/pyprima/common/consts.py +48 -0
  27. divi/exp/scipy/pyprima/common/evaluate.py +101 -0
  28. divi/exp/scipy/pyprima/common/history.py +39 -0
  29. divi/exp/scipy/pyprima/common/infos.py +30 -0
  30. divi/exp/scipy/pyprima/common/linalg.py +452 -0
  31. divi/exp/scipy/pyprima/common/message.py +336 -0
  32. divi/exp/scipy/pyprima/common/powalg.py +131 -0
  33. divi/exp/scipy/pyprima/common/preproc.py +393 -0
  34. divi/exp/scipy/pyprima/common/present.py +5 -0
  35. divi/exp/scipy/pyprima/common/ratio.py +56 -0
  36. divi/exp/scipy/pyprima/common/redrho.py +49 -0
  37. divi/exp/scipy/pyprima/common/selectx.py +346 -0
  38. divi/interfaces.py +25 -0
  39. divi/parallel_simulator.py +258 -0
  40. divi/qasm.py +220 -0
  41. divi/qem.py +191 -0
  42. divi/qlogger.py +119 -0
  43. divi/qoro_service.py +343 -0
  44. divi/qprog/__init__.py +13 -0
  45. divi/qprog/_graph_partitioning.py +619 -0
  46. divi/qprog/_mlae.py +182 -0
  47. divi/qprog/_qaoa.py +440 -0
  48. divi/qprog/_vqe.py +275 -0
  49. divi/qprog/_vqe_sweep.py +144 -0
  50. divi/qprog/batch.py +235 -0
  51. divi/qprog/optimizers.py +75 -0
  52. divi/qprog/quantum_program.py +493 -0
  53. divi/utils.py +116 -0
  54. qoro_divi-0.2.0b1.dist-info/LICENSE +190 -0
  55. qoro_divi-0.2.0b1.dist-info/LICENSES/Apache-2.0.txt +73 -0
  56. qoro_divi-0.2.0b1.dist-info/METADATA +57 -0
  57. qoro_divi-0.2.0b1.dist-info/RECORD +58 -0
  58. qoro_divi-0.2.0b1.dist-info/WHEEL +4 -0
@@ -0,0 +1,889 @@
1
+ # Copyright 2018 The Cirq Developers
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
+ # https://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
+
15
+ from __future__ import annotations
16
+
17
+ import dataclasses
18
+ import functools
19
+ import operator
20
+ from typing import TYPE_CHECKING, Any, Callable, Iterable, cast
21
+
22
+ import numpy as np
23
+ import sympy
24
+ from cirq import CX, Circuit, CircuitOperation, FrozenCircuit, NamedQubit, ops, value
25
+ from cirq.circuits.qasm_output import QasmUGate
26
+ from ply import yacc
27
+
28
+ from ._lexer import QasmLexer
29
+ from .exception import QasmException
30
+
31
+ if TYPE_CHECKING:
32
+ import cirq
33
+
34
+
35
+ class Qasm:
36
+ """Qasm stores the final result of the Qasm parsing."""
37
+
38
+ def __init__(
39
+ self,
40
+ supported_format: bool,
41
+ qelib1_include: bool,
42
+ qregs: dict,
43
+ cregs: dict,
44
+ c: Circuit,
45
+ ):
46
+ # defines whether the Quantum Experience standard header
47
+ # is present or not
48
+ self.qelib1Include = qelib1_include
49
+ # defines if it has a supported format or not
50
+ self.supportedFormat = supported_format
51
+ # circuit
52
+ self.qregs = qregs
53
+ self.cregs = cregs
54
+ self.circuit = c
55
+
56
+
57
+ def _generate_op_qubits(args: list[list[ops.Qid]], lineno: int) -> list[list[ops.Qid]]:
58
+ """Generates the Cirq qubits for an operation from the OpenQASM qregs.
59
+
60
+ OpenQASM gates can be applied on single qubits and qubit registers.
61
+ We represent single qubits as registers of size 1.
62
+ Based on the OpenQASM spec (https://arxiv.org/abs/1707.03429),
63
+ single qubit arguments can be mixed with qubit registers.
64
+ Given quantum registers of length reg_size and single qubits are both
65
+ used as arguments, we generate reg_size GateOperations via iterating
66
+ through each qubit of the registers 0 to n-1 and use the same one
67
+ qubit from the "single-qubit registers" for each operation."""
68
+ reg_sizes = np.unique([len(reg) for reg in args])
69
+ if len(reg_sizes) > 2 or (len(reg_sizes) > 1 and reg_sizes[0] != 1):
70
+ raise QasmException(
71
+ f"Non matching quantum registers of length {reg_sizes} at line {lineno}"
72
+ )
73
+ op_qubits_gen = functools.reduce(
74
+ cast(
75
+ Callable[[list["cirq.Qid"], list["cirq.Qid"]], list["cirq.Qid"]],
76
+ np.broadcast,
77
+ ),
78
+ args,
79
+ )
80
+ op_qubits = [[q] if isinstance(q, ops.Qid) else q for q in op_qubits_gen]
81
+ if any(len(set(q)) < len(q) for q in op_qubits):
82
+ raise QasmException(f"Overlapping qubits in arguments at line {lineno}")
83
+ return op_qubits
84
+
85
+
86
+ class QasmGateStatement:
87
+ """Specifies how to convert a call to an OpenQASM gate
88
+ to a list of `cirq.GateOperation`s.
89
+
90
+ Has the responsibility to validate the arguments
91
+ and parameters of the call and to generate a list of corresponding
92
+ `cirq.GateOperation`s in the `on` method.
93
+ """
94
+
95
+ def __init__(
96
+ self,
97
+ qasm_gate: str,
98
+ cirq_gate: ops.Gate | Callable[[list[float]], ops.Gate],
99
+ num_params: int,
100
+ num_args: int,
101
+ ):
102
+ """Initializes a Qasm gate statement.
103
+
104
+ Args:
105
+ qasm_gate: The symbol of the QASM gate.
106
+ cirq_gate: The gate class on the cirq side.
107
+ num_params: The number of params taken by this gate.
108
+ num_args: The number of qubits (used in validation) this
109
+ gate takes.
110
+ """
111
+ self.qasm_gate = qasm_gate
112
+ self.cirq_gate = cirq_gate
113
+ self.num_params = num_params
114
+
115
+ # at least one quantum argument is mandatory for gates to act on
116
+ assert num_args >= 1
117
+ self.num_args = num_args
118
+
119
+ def _validate_args(self, args: list[list[ops.Qid]], lineno: int):
120
+ if len(args) != self.num_args:
121
+ raise QasmException(
122
+ f"{self.qasm_gate} only takes {self.num_args} arg(s) (qubits and/or registers), "
123
+ f"got: {len(args)}, at line {lineno}"
124
+ )
125
+
126
+ def _validate_params(self, params: list[value.TParamVal], lineno: int):
127
+ if len(params) != self.num_params:
128
+ raise QasmException(
129
+ f"{self.qasm_gate} takes {self.num_params} parameter(s), "
130
+ f"got: {len(params)}, at line {lineno}"
131
+ )
132
+
133
+ def on(
134
+ self, params: list[value.TParamVal], args: list[list[ops.Qid]], lineno: int
135
+ ) -> Iterable[ops.Operation]:
136
+ self._validate_args(args, lineno)
137
+ self._validate_params(params, lineno)
138
+
139
+ # the actual gate we'll apply the arguments to might be a parameterized
140
+ # or non-parameterized gate
141
+ final_gate: ops.Gate = (
142
+ self.cirq_gate
143
+ if isinstance(self.cirq_gate, ops.Gate)
144
+ else self.cirq_gate(params)
145
+ )
146
+ for qubits in _generate_op_qubits(args, lineno):
147
+ yield final_gate.on(*qubits)
148
+
149
+
150
+ @dataclasses.dataclass
151
+ class CustomGate:
152
+ """Represents an invocation of a user-defined gate.
153
+
154
+ The custom gate definition is encoded here as a `FrozenCircuit`, and the
155
+ arguments (params and qubits) of the specific invocation of that gate are
156
+ stored here too. When `on` is called, we create a CircuitOperation, mapping
157
+ the qubits and params to the values provided."""
158
+
159
+ name: str
160
+ circuit: FrozenCircuit
161
+ params: tuple[str, ...]
162
+ qubits: tuple[ops.Qid, ...]
163
+
164
+ def on(
165
+ self, params: list[value.TParamVal], args: list[list[ops.Qid]], lineno: int
166
+ ) -> Iterable[ops.Operation]:
167
+ if len(params) != len(self.params):
168
+ raise QasmException(
169
+ f"Wrong number of params for '{self.name}' at line {lineno}"
170
+ )
171
+ if len(args) != len(self.qubits):
172
+ raise QasmException(
173
+ f"Wrong number of qregs for '{self.name}' at line {lineno}"
174
+ )
175
+ for qubits in _generate_op_qubits(args, lineno):
176
+ yield CircuitOperation(
177
+ self.circuit,
178
+ param_resolver={k: v for k, v in zip(self.params, params)},
179
+ qubit_map={k: v for k, v in zip(self.qubits, qubits)},
180
+ )
181
+
182
+
183
+ class QasmParser:
184
+ """Parser for QASM strings.
185
+
186
+ Example:
187
+
188
+ qasm = "OPENQASM 2.0; qreg q1[2]; CX q1[0], q1[1];"
189
+ parsedQasm = QasmParser().parse(qasm)
190
+ """
191
+
192
+ def __init__(self) -> None:
193
+ self.parser = yacc.yacc(module=self, debug=False, write_tables=False)
194
+ self.circuit = Circuit()
195
+ self.qregs: dict[str, int] = {}
196
+ self.cregs: dict[str, int] = {}
197
+ self.gate_set: dict[str, CustomGate | QasmGateStatement] = {**self.basic_gates}
198
+ """The gates available to use in the circuit, including those from libraries, and
199
+ user-defined ones."""
200
+ self.in_custom_gate_scope = False
201
+ """This is set to True when the parser is in the middle of parsing a custom gate
202
+ definition."""
203
+ self.custom_gate_scoped_params: set[str] = set()
204
+ """The params declared within the current custom gate definition. Empty if not in
205
+ custom gate scope."""
206
+ self.custom_gate_scoped_qubits: dict[str, ops.Qid] = {}
207
+ """The qubits declared within the current custom gate definition. Empty if not in
208
+ custom gate scope."""
209
+ self.qelibinc = False
210
+ self.lexer = QasmLexer()
211
+ self.supported_format = False
212
+ self.parsedQasm: Qasm | None = None
213
+ self.qubits: dict[str, ops.Qid] = {}
214
+ self.functions = {
215
+ "sin": np.sin,
216
+ "cos": np.cos,
217
+ "tan": np.tan,
218
+ "exp": np.exp,
219
+ "ln": np.log,
220
+ "sqrt": np.sqrt,
221
+ "acos": np.arccos,
222
+ "atan": np.arctan,
223
+ "asin": np.arcsin,
224
+ }
225
+
226
+ self.binary_operators = {
227
+ "+": operator.add,
228
+ "-": operator.sub,
229
+ "*": operator.mul,
230
+ "/": operator.truediv,
231
+ "^": operator.pow,
232
+ }
233
+ self.input_params: dict[str, sympy.Symbol] = {} # <-- Add this
234
+
235
+ basic_gates: dict[str, QasmGateStatement] = {
236
+ "CX": QasmGateStatement(qasm_gate="CX", cirq_gate=CX, num_params=0, num_args=2),
237
+ "U": QasmGateStatement(
238
+ qasm_gate="U",
239
+ num_params=3,
240
+ num_args=1,
241
+ # QasmUGate expects half turns
242
+ cirq_gate=(lambda params: QasmUGate(*[p / np.pi for p in params])),
243
+ ),
244
+ }
245
+
246
+ qelib_gates = {
247
+ "ccx": QasmGateStatement(
248
+ qasm_gate="ccx", num_params=0, num_args=3, cirq_gate=ops.CCX
249
+ ),
250
+ "ch": QasmGateStatement(
251
+ qasm_gate="ch",
252
+ cirq_gate=ops.ControlledGate(ops.H),
253
+ num_params=0,
254
+ num_args=2,
255
+ ),
256
+ "crx": QasmGateStatement(
257
+ qasm_gate="crx",
258
+ num_params=1,
259
+ num_args=2,
260
+ cirq_gate=(lambda params: ops.ControlledGate(ops.rx(params[0]))),
261
+ ),
262
+ "cry": QasmGateStatement(
263
+ qasm_gate="cry",
264
+ num_params=1,
265
+ num_args=2,
266
+ cirq_gate=(lambda params: ops.ControlledGate(ops.ry(params[0]))),
267
+ ),
268
+ "crz": QasmGateStatement(
269
+ qasm_gate="crz",
270
+ num_params=1,
271
+ num_args=2,
272
+ cirq_gate=(lambda params: ops.ControlledGate(ops.rz(params[0]))),
273
+ ),
274
+ "cswap": QasmGateStatement(
275
+ qasm_gate="cswap", num_params=0, num_args=3, cirq_gate=ops.CSWAP
276
+ ),
277
+ "cu1": QasmGateStatement(
278
+ qasm_gate="cu1",
279
+ num_params=1,
280
+ num_args=2,
281
+ cirq_gate=(
282
+ lambda params: ops.ControlledGate(QasmUGate(0, 0, params[0] / np.pi))
283
+ ),
284
+ ),
285
+ "cu3": QasmGateStatement(
286
+ qasm_gate="cu3",
287
+ num_params=3,
288
+ num_args=2,
289
+ cirq_gate=(
290
+ lambda params: ops.ControlledGate(
291
+ QasmUGate(*[p / np.pi for p in params])
292
+ )
293
+ ),
294
+ ),
295
+ "cx": QasmGateStatement(qasm_gate="cx", cirq_gate=CX, num_params=0, num_args=2),
296
+ "cy": QasmGateStatement(
297
+ qasm_gate="cy",
298
+ cirq_gate=ops.ControlledGate(ops.Y),
299
+ num_params=0,
300
+ num_args=2,
301
+ ),
302
+ "cz": QasmGateStatement(
303
+ qasm_gate="cz", cirq_gate=ops.CZ, num_params=0, num_args=2
304
+ ),
305
+ "h": QasmGateStatement(
306
+ qasm_gate="h", num_params=0, num_args=1, cirq_gate=ops.H
307
+ ),
308
+ "id": QasmGateStatement(
309
+ qasm_gate="id", cirq_gate=ops.IdentityGate(1), num_params=0, num_args=1
310
+ ),
311
+ "iswap": QasmGateStatement(
312
+ qasm_gate="iswap", cirq_gate=ops.ISwapPowGate(), num_params=0, num_args=2
313
+ ),
314
+ "r": QasmGateStatement(
315
+ qasm_gate="r",
316
+ num_params=2,
317
+ num_args=1,
318
+ cirq_gate=(
319
+ lambda params: QasmUGate(
320
+ params[0] / np.pi,
321
+ (params[1] / np.pi) - 0.5,
322
+ (-params[1] / np.pi) + 0.5,
323
+ )
324
+ ),
325
+ ),
326
+ "rx": QasmGateStatement(
327
+ qasm_gate="rx",
328
+ cirq_gate=(lambda params: ops.rx(params[0])),
329
+ num_params=1,
330
+ num_args=1,
331
+ ),
332
+ "ry": QasmGateStatement(
333
+ qasm_gate="ry",
334
+ cirq_gate=(lambda params: ops.ry(params[0])),
335
+ num_params=1,
336
+ num_args=1,
337
+ ),
338
+ "ryy": QasmGateStatement(
339
+ qasm_gate="ryy",
340
+ num_params=1,
341
+ num_args=2,
342
+ cirq_gate=(lambda params: ops.YYPowGate(exponent=params[0] / np.pi)),
343
+ ),
344
+ "rz": QasmGateStatement(
345
+ qasm_gate="rz",
346
+ cirq_gate=(lambda params: ops.rz(params[0])),
347
+ num_params=1,
348
+ num_args=1,
349
+ ),
350
+ "rxx": QasmGateStatement(
351
+ qasm_gate="rxx",
352
+ num_params=1,
353
+ num_args=2,
354
+ cirq_gate=(lambda params: ops.XXPowGate(exponent=params[0] / np.pi)),
355
+ ),
356
+ "rzz": QasmGateStatement(
357
+ qasm_gate="rzz",
358
+ num_params=1,
359
+ num_args=2,
360
+ cirq_gate=(lambda params: ops.ZZPowGate(exponent=params[0] / np.pi)),
361
+ ),
362
+ "s": QasmGateStatement(
363
+ qasm_gate="s", num_params=0, num_args=1, cirq_gate=ops.S
364
+ ),
365
+ "sdg": QasmGateStatement(
366
+ qasm_gate="sdg", num_params=0, num_args=1, cirq_gate=ops.S**-1
367
+ ),
368
+ "swap": QasmGateStatement(
369
+ qasm_gate="swap", cirq_gate=ops.SWAP, num_params=0, num_args=2
370
+ ),
371
+ "sx": QasmGateStatement(
372
+ qasm_gate="sx",
373
+ num_params=0,
374
+ num_args=1,
375
+ cirq_gate=ops.XPowGate(exponent=0.5),
376
+ ),
377
+ "sxdg": QasmGateStatement(
378
+ qasm_gate="sxdg",
379
+ num_params=0,
380
+ num_args=1,
381
+ cirq_gate=ops.XPowGate(exponent=-0.5),
382
+ ),
383
+ "t": QasmGateStatement(
384
+ qasm_gate="t", num_params=0, num_args=1, cirq_gate=ops.T
385
+ ),
386
+ "tdg": QasmGateStatement(
387
+ qasm_gate="tdg", num_params=0, num_args=1, cirq_gate=ops.T**-1
388
+ ),
389
+ "u1": QasmGateStatement(
390
+ qasm_gate="u1",
391
+ cirq_gate=(lambda params: QasmUGate(0, 0, params[0] / np.pi)),
392
+ num_params=1,
393
+ num_args=1,
394
+ ),
395
+ "u2": QasmGateStatement(
396
+ qasm_gate="u2",
397
+ cirq_gate=(
398
+ lambda params: QasmUGate(0.5, params[0] / np.pi, params[1] / np.pi)
399
+ ),
400
+ num_params=2,
401
+ num_args=1,
402
+ ),
403
+ "u3": QasmGateStatement(
404
+ qasm_gate="u3",
405
+ num_params=3,
406
+ num_args=1,
407
+ cirq_gate=(lambda params: QasmUGate(*[p / np.pi for p in params])),
408
+ ),
409
+ "x": QasmGateStatement(
410
+ qasm_gate="x", num_params=0, num_args=1, cirq_gate=ops.X
411
+ ),
412
+ "y": QasmGateStatement(
413
+ qasm_gate="y", num_params=0, num_args=1, cirq_gate=ops.Y
414
+ ),
415
+ "z": QasmGateStatement(
416
+ qasm_gate="z", num_params=0, num_args=1, cirq_gate=ops.Z
417
+ ),
418
+ }
419
+
420
+ tokens = QasmLexer.tokens
421
+ start = "start"
422
+
423
+ precedence = (("left", "+", "-"), ("left", "*", "/"), ("right", "^"))
424
+
425
+ def p_start(self, p):
426
+ """start : qasm"""
427
+ p[0] = p[1]
428
+
429
+ def p_qasm_format_only(self, p):
430
+ """qasm : format"""
431
+ self.supported_format = True
432
+ p[0] = Qasm(
433
+ self.supported_format, self.qelibinc, self.qregs, self.cregs, self.circuit
434
+ )
435
+
436
+ def p_qasm_no_format_specified_error(self, p):
437
+ """qasm : QELIBINC
438
+ | STDGATESINC
439
+ | circuit"""
440
+ if self.supported_format is False:
441
+ raise QasmException("Missing 'OPENQASM 2.0;' statement")
442
+
443
+ def p_qasm_include(self, p):
444
+ """qasm : qasm QELIBINC"""
445
+ self.qelibinc = True
446
+ self.gate_set |= self.qelib_gates
447
+ p[0] = Qasm(
448
+ self.supported_format, self.qelibinc, self.qregs, self.cregs, self.circuit
449
+ )
450
+
451
+ def p_qasm_include_stdgates(self, p):
452
+ """qasm : qasm STDGATESINC"""
453
+ self.qelibinc = True
454
+ self.gate_set |= self.qelib_gates
455
+ p[0] = Qasm(
456
+ self.supported_format, self.qelibinc, self.qregs, self.cregs, self.circuit
457
+ )
458
+
459
+ def p_qasm_circuit(self, p):
460
+ """qasm : qasm circuit"""
461
+ p[0] = Qasm(self.supported_format, self.qelibinc, self.qregs, self.cregs, p[2])
462
+
463
+ def p_format(self, p):
464
+ """format : FORMAT_SPEC"""
465
+ if p[1] not in ["2.0", "3.0"]:
466
+ raise QasmException(
467
+ f"Unsupported OpenQASM version: {p[1]}, "
468
+ "only 2.0 and 3.0 are supported currently by Cirq"
469
+ )
470
+
471
+ # circuit : new_reg circuit
472
+ # | gate_op circuit
473
+ # | measurement circuit
474
+ # | reset circuit
475
+ # | if circuit
476
+ # | empty
477
+
478
+ def p_circuit_reg(self, p):
479
+ """circuit : new_reg circuit"""
480
+ p[0] = self.circuit
481
+
482
+ def p_circuit_gate_or_measurement_or_if(self, p):
483
+ """circuit : circuit gate_op
484
+ | circuit measurement
485
+ | circuit reset
486
+ | circuit if"""
487
+ self.circuit.append(p[2])
488
+ p[0] = self.circuit
489
+
490
+ def p_circuit_empty(self, p):
491
+ """circuit : empty"""
492
+ p[0] = self.circuit
493
+
494
+ def p_circuit_gate_def(self, p):
495
+ """circuit : gate_def"""
496
+ p[0] = self.circuit
497
+
498
+ def p_circuit_input_angle(self, p):
499
+ """circuit : input_angle circuit"""
500
+ p[0] = self.circuit
501
+
502
+ def p_input_angle(self, p):
503
+ """input_angle : INPUT ANGLE '[' NATURAL_NUMBER ']' ID ';'"""
504
+ param_name = p[6]
505
+ self.input_params[param_name] = sympy.Symbol(param_name)
506
+ p[0] = None
507
+
508
+ # qreg and creg
509
+
510
+ def p_new_reg(self, p):
511
+ """new_reg : QREG ID '[' NATURAL_NUMBER ']' ';'
512
+ | QUBIT '[' NATURAL_NUMBER ']' ID ';'
513
+ | QUBIT ID ';'
514
+ | CREG ID '[' NATURAL_NUMBER ']' ';'
515
+ | BIT '[' NATURAL_NUMBER ']' ID ';'
516
+ | BIT ID ';'
517
+ """
518
+ if p[1] == "qreg" or p[1] == "creg":
519
+ # QREG ID '[' NATURAL_NUMBER ']' ';'
520
+ name, length = p[2], p[4]
521
+ else:
522
+ if len(p) < 5:
523
+ # QUBIT ID ';' | BIT ID ';'
524
+ name = p[2]
525
+ length = 1
526
+ else:
527
+ # QUBIT '[' NATURAL_NUMBER ']' ID ';'
528
+ name, length = p[5], p[3]
529
+ if name in self.qregs.keys() or name in self.cregs.keys():
530
+ raise QasmException(f"{name} is already defined at line {p.lineno(2)}")
531
+ if length == 0:
532
+ raise QasmException(
533
+ f"Illegal, zero-length register '{name}' at line {p.lineno(4)}"
534
+ )
535
+ if p[1] == "qreg" or p[1] == "qubit":
536
+ self.qregs[name] = length
537
+ else:
538
+ self.cregs[name] = length
539
+ p[0] = (name, length)
540
+
541
+ # gate operations
542
+ # gate_op : ID qargs
543
+ # | ID ( params ) qargs
544
+
545
+ def p_gate_op_no_params(self, p):
546
+ """gate_op : ID qargs"""
547
+ self._resolve_gate_operation(p[2], gate=p[1], p=p, params=[])
548
+
549
+ def p_gate_op_with_params(self, p):
550
+ """gate_op : ID '(' params ')' qargs"""
551
+ self._resolve_gate_operation(args=p[5], gate=p[1], p=p, params=p[3])
552
+
553
+ def _resolve_gate_operation(
554
+ self,
555
+ args: list[list[ops.Qid]],
556
+ gate: str,
557
+ p: Any,
558
+ params: list[value.TParamVal],
559
+ ):
560
+ if gate not in self.gate_set:
561
+ tip = ", did you forget to include qelib1.inc?" if not self.qelibinc else ""
562
+ msg = f'Unknown gate "{gate}" at line {p.lineno(1)}{tip}'
563
+ raise QasmException(msg)
564
+ p[0] = self.gate_set[gate].on(args=args, params=params, lineno=p.lineno(1))
565
+
566
+ # params : parameter ',' params
567
+ # | parameter
568
+
569
+ def p_params_multiple(self, p):
570
+ """params : expr ',' params"""
571
+ p[3].insert(0, p[1])
572
+ p[0] = p[3]
573
+
574
+ def p_params_single(self, p):
575
+ """params : expr"""
576
+ p[0] = [p[1]]
577
+
578
+ # expr : term
579
+ # | ID
580
+ # | func '(' expression ')'
581
+ # | binary_op
582
+ # | unary_op
583
+
584
+ def p_expr_term(self, p):
585
+ """expr : term"""
586
+ p[0] = p[1]
587
+
588
+ # def p_expr_identifier(self, p):
589
+ # """expr : ID"""
590
+ # if not self.in_custom_gate_scope:
591
+ # raise QasmException(
592
+ # f"Parameter '{p[1]}' in line {p.lineno(1)} not supported"
593
+ # )
594
+ # if p[1] not in self.custom_gate_scoped_params:
595
+ # raise QasmException(f"Undefined parameter '{p[1]}' in line {p.lineno(1)}'")
596
+ # p[0] = sympy.Symbol(p[1])
597
+
598
+ def p_expr_identifier(self, p):
599
+ """expr : ID"""
600
+ if p[1] in getattr(self, "input_params", {}): # <-- Check input_params first
601
+ p[0] = self.input_params[p[1]]
602
+ return
603
+
604
+ if not self.in_custom_gate_scope:
605
+ raise QasmException(
606
+ f"Parameter '{p[1]}' in line {p.lineno(1)} not supported"
607
+ )
608
+
609
+ if p[1] not in self.custom_gate_scoped_params:
610
+ raise QasmException(f"Undefined parameter '{p[1]}' in line {p.lineno(1)}'")
611
+
612
+ p[0] = sympy.Symbol(p[1])
613
+
614
+ def p_expr_parens(self, p):
615
+ """expr : '(' expr ')'"""
616
+ p[0] = p[2]
617
+
618
+ def p_expr_function_call(self, p):
619
+ """expr : ID '(' expr ')'"""
620
+ func = p[1]
621
+ if func not in self.functions.keys():
622
+ raise QasmException(
623
+ f"Function not recognized: '{func}' at line {p.lineno(1)}"
624
+ )
625
+ p[0] = self.functions[func](p[3])
626
+
627
+ def p_expr_unary(self, p):
628
+ """expr : '-' expr
629
+ | '+' expr"""
630
+ if p[1] == "-":
631
+ p[0] = -p[2]
632
+ else:
633
+ p[0] = p[2]
634
+
635
+ def p_expr_binary(self, p):
636
+ """expr : expr '*' expr
637
+ | expr '/' expr
638
+ | expr '+' expr
639
+ | expr '-' expr
640
+ | expr '^' expr
641
+ """
642
+ p[0] = self.binary_operators[p[2]](p[1], p[3])
643
+
644
+ def p_term(self, p):
645
+ """term : NUMBER
646
+ | NATURAL_NUMBER"""
647
+ p[0] = p[1]
648
+
649
+ def p_pi(self, p):
650
+ """term : PI"""
651
+ p[0] = np.pi
652
+
653
+ # qargs : qarg ',' qargs
654
+ # | qarg ';'
655
+
656
+ def p_args_multiple(self, p):
657
+ """qargs : qarg ',' qargs"""
658
+ p[3].insert(0, p[1])
659
+ p[0] = p[3]
660
+
661
+ def p_args_single(self, p):
662
+ """qargs : qarg ';'"""
663
+ p[0] = [p[1]]
664
+
665
+ # qarg : ID
666
+ # | ID '[' NATURAL_NUMBER ']'
667
+
668
+ def p_quantum_arg_register(self, p):
669
+ """qarg : ID"""
670
+ reg = p[1]
671
+ if self.in_custom_gate_scope:
672
+ if reg not in self.custom_gate_scoped_qubits:
673
+ if reg not in self.qregs:
674
+ msg = f"Undefined qubit '{reg}'"
675
+ else:
676
+ msg = f"'{reg}' is a register, not a qubit"
677
+ raise QasmException(f"{msg} at line {p.lineno(1)}")
678
+ p[0] = [self.custom_gate_scoped_qubits[reg]]
679
+ return
680
+ if reg not in self.qregs.keys():
681
+ raise QasmException(
682
+ f'Undefined quantum register "{reg}" at line {p.lineno(1)}'
683
+ )
684
+ qubits = []
685
+ for idx in range(self.qregs[reg]):
686
+ arg_name = self.make_name(idx, reg)
687
+ if arg_name not in self.qubits.keys():
688
+ self.qubits[arg_name] = NamedQubit(arg_name)
689
+ qubits.append(self.qubits[arg_name])
690
+ p[0] = qubits
691
+
692
+ # carg : ID
693
+ # | ID '[' NATURAL_NUMBER ']'
694
+
695
+ def p_classical_arg_register(self, p):
696
+ """carg : ID"""
697
+ reg = p[1]
698
+ if reg not in self.cregs.keys():
699
+ raise QasmException(
700
+ f'Undefined classical register "{reg}" at line {p.lineno(1)}'
701
+ )
702
+
703
+ p[0] = [self.make_name(idx, reg) for idx in range(self.cregs[reg])]
704
+
705
+ def make_name(self, idx, reg):
706
+ return str(reg) + "_" + str(idx)
707
+
708
+ def p_quantum_arg_bit(self, p):
709
+ """qarg : ID '[' NATURAL_NUMBER ']'"""
710
+ reg = p[1]
711
+ idx = p[3]
712
+ if self.in_custom_gate_scope:
713
+ raise QasmException(
714
+ f"Unsupported indexed qreg '{reg}[{idx}]' at line {p.lineno(1)}"
715
+ )
716
+ arg_name = self.make_name(idx, reg)
717
+ if reg not in self.qregs.keys():
718
+ raise QasmException(
719
+ f'Undefined quantum register "{reg}" at line {p.lineno(1)}'
720
+ )
721
+ size = self.qregs[reg]
722
+ if idx >= size:
723
+ raise QasmException(
724
+ f"Out of bounds qubit index {idx} "
725
+ f"on register {reg} of size {size} "
726
+ f"at line {p.lineno(1)}"
727
+ )
728
+ if arg_name not in self.qubits.keys():
729
+ self.qubits[arg_name] = NamedQubit(arg_name)
730
+ p[0] = [self.qubits[arg_name]]
731
+
732
+ def p_classical_arg_bit(self, p):
733
+ """carg : ID '[' NATURAL_NUMBER ']'"""
734
+ reg = p[1]
735
+ idx = p[3]
736
+ arg_name = self.make_name(idx, reg)
737
+ if reg not in self.cregs.keys():
738
+ raise QasmException(
739
+ f'Undefined classical register "{reg}" at line {p.lineno(1)}'
740
+ )
741
+
742
+ size = self.cregs[reg]
743
+ if idx >= size:
744
+ raise QasmException(
745
+ f"Out of bounds bit index {idx} "
746
+ f"on classical register {reg} of size {size} "
747
+ f"at line {p.lineno(1)}"
748
+ )
749
+ p[0] = [arg_name]
750
+
751
+ # measurement operations
752
+ # measurement : MEASURE qarg ARROW carg
753
+
754
+ def p_measurement(self, p):
755
+ """measurement : MEASURE qarg ARROW carg ';'
756
+ | carg '=' MEASURE qarg ';'"""
757
+ if p[1] == "measure":
758
+ qreg = p[2]
759
+ creg = p[4]
760
+ else:
761
+ qreg = p[4]
762
+ creg = p[1]
763
+
764
+ if len(qreg) != len(creg):
765
+ raise QasmException(
766
+ f"mismatched register sizes {len(qreg)} -> {len(creg)} for measurement "
767
+ f"at line {p.lineno(1)}"
768
+ )
769
+
770
+ p[0] = [
771
+ ops.MeasurementGate(num_qubits=1, key=creg[i]).on(qreg[i])
772
+ for i in range(len(qreg))
773
+ ]
774
+
775
+ # reset operations
776
+ # reset : RESET qarg
777
+
778
+ def p_reset(self, p):
779
+ """reset : RESET qarg ';'"""
780
+ qreg = p[2]
781
+
782
+ p[0] = [ops.ResetChannel().on(qreg[i]) for i in range(len(qreg))]
783
+
784
+ # if operations
785
+ # if : IF '(' carg EQ NATURAL_NUMBER ')' ID qargs
786
+
787
+ def p_if(self, p):
788
+ """if : IF '(' carg EQ NATURAL_NUMBER ')' gate_op"""
789
+ # We have to split the register into bits (since that's what measurement does above),
790
+ # and create one condition per bit, checking against that part of the binary value.
791
+ conditions = []
792
+ for i, key in enumerate(p[3]):
793
+ v = (p[5] >> i) & 1
794
+ conditions.append(sympy.Eq(sympy.Symbol(key), v))
795
+ p[0] = [
796
+ ops.ClassicallyControlledOperation(
797
+ conditions=conditions, sub_operation=tuple(p[7])[0]
798
+ )
799
+ ]
800
+
801
+ def p_gate_params_multiple(self, p):
802
+ """gate_params : ID ',' gate_params"""
803
+ self.p_gate_params_single(p)
804
+ p[0] += p[3]
805
+
806
+ def p_gate_params_single(self, p):
807
+ """gate_params : ID"""
808
+ self.in_custom_gate_scope = True
809
+ self.custom_gate_scoped_params.add(p[1])
810
+ p[0] = [p[1]]
811
+
812
+ def p_gate_qubits_multiple(self, p):
813
+ """gate_qubits : ID ',' gate_qubits"""
814
+ self.p_gate_qubits_single(p)
815
+ p[0] += p[3]
816
+
817
+ def p_gate_qubits_single(self, p):
818
+ """gate_qubits : ID"""
819
+ self.in_custom_gate_scope = True
820
+ q = NamedQubit(p[1])
821
+ self.custom_gate_scoped_qubits[p[1]] = q
822
+ p[0] = [q]
823
+
824
+ def p_gate_ops(self, p):
825
+ """gate_ops : gate_op gate_ops"""
826
+ p[0] = [p[1]] + p[2]
827
+
828
+ def p_gate_ops_empty(self, p):
829
+ """gate_ops : empty"""
830
+ self.in_custom_gate_scope = True
831
+ p[0] = []
832
+
833
+ def p_gate_def_parameterized(self, p):
834
+ """gate_def : GATE ID '(' gate_params ')' gate_qubits '{' gate_ops '}'"""
835
+ self._gate_def(p, has_params=True)
836
+
837
+ def p_gate_def(self, p):
838
+ """gate_def : GATE ID gate_qubits '{' gate_ops '}'"""
839
+ self._gate_def(p, has_params=False)
840
+
841
+ def _gate_def(self, p: list[Any], *, has_params: bool):
842
+ name = p[2]
843
+ gate_params = tuple(p[4]) if has_params else ()
844
+ offset = 3 if has_params else 0
845
+ gate_qubits = tuple(p[3 + offset])
846
+ gate_ops = p[5 + offset]
847
+ circuit = Circuit(gate_ops).freeze()
848
+ gate_def = CustomGate(name, circuit, gate_params, gate_qubits)
849
+ self.gate_set[name] = gate_def
850
+ self.custom_gate_scoped_params.clear()
851
+ self.custom_gate_scoped_qubits.clear()
852
+ self.in_custom_gate_scope = False
853
+ p[0] = gate_def
854
+
855
+ def p_error(self, p):
856
+ if p is None:
857
+ raise QasmException("Unexpected end of file")
858
+
859
+ raise QasmException(
860
+ f"""Syntax error: '{p.value}'
861
+ {self.debug_context(p)}
862
+ at line {p.lineno}, column {self.find_column(p)}"""
863
+ )
864
+
865
+ def find_column(self, p):
866
+ line_start = self.qasm.rfind("\n", 0, p.lexpos) + 1
867
+ return (p.lexpos - line_start) + 1
868
+
869
+ def p_empty(self, p):
870
+ """empty :"""
871
+
872
+ def parse(self, qasm: str) -> Qasm:
873
+ if self.parsedQasm is None:
874
+ self.qasm = qasm
875
+ self.lexer.input(self.qasm)
876
+ self.parsedQasm = self.parser.parse(lexer=self.lexer)
877
+ return self.parsedQasm
878
+
879
+ def debug_context(self, p):
880
+ debug_start = max(self.qasm.rfind("\n", 0, p.lexpos) + 1, p.lexpos - 5)
881
+ debug_end = min(self.qasm.find("\n", p.lexpos, p.lexpos + 5), p.lexpos + 5)
882
+
883
+ return (
884
+ "..."
885
+ + self.qasm[debug_start:debug_end]
886
+ + "\n"
887
+ + (" " * (3 + p.lexpos - debug_start))
888
+ + "^"
889
+ )