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.
- divi/__init__.py +8 -0
- divi/_pbar.py +73 -0
- divi/circuits.py +139 -0
- divi/exp/cirq/__init__.py +7 -0
- divi/exp/cirq/_lexer.py +126 -0
- divi/exp/cirq/_parser.py +889 -0
- divi/exp/cirq/_qasm_export.py +37 -0
- divi/exp/cirq/_qasm_import.py +35 -0
- divi/exp/cirq/exception.py +21 -0
- divi/exp/scipy/_cobyla.py +342 -0
- divi/exp/scipy/pyprima/LICENCE.txt +28 -0
- divi/exp/scipy/pyprima/__init__.py +263 -0
- divi/exp/scipy/pyprima/cobyla/__init__.py +0 -0
- divi/exp/scipy/pyprima/cobyla/cobyla.py +599 -0
- divi/exp/scipy/pyprima/cobyla/cobylb.py +849 -0
- divi/exp/scipy/pyprima/cobyla/geometry.py +240 -0
- divi/exp/scipy/pyprima/cobyla/initialize.py +269 -0
- divi/exp/scipy/pyprima/cobyla/trustregion.py +540 -0
- divi/exp/scipy/pyprima/cobyla/update.py +331 -0
- divi/exp/scipy/pyprima/common/__init__.py +0 -0
- divi/exp/scipy/pyprima/common/_bounds.py +41 -0
- divi/exp/scipy/pyprima/common/_linear_constraints.py +46 -0
- divi/exp/scipy/pyprima/common/_nonlinear_constraints.py +64 -0
- divi/exp/scipy/pyprima/common/_project.py +224 -0
- divi/exp/scipy/pyprima/common/checkbreak.py +107 -0
- divi/exp/scipy/pyprima/common/consts.py +48 -0
- divi/exp/scipy/pyprima/common/evaluate.py +101 -0
- divi/exp/scipy/pyprima/common/history.py +39 -0
- divi/exp/scipy/pyprima/common/infos.py +30 -0
- divi/exp/scipy/pyprima/common/linalg.py +452 -0
- divi/exp/scipy/pyprima/common/message.py +336 -0
- divi/exp/scipy/pyprima/common/powalg.py +131 -0
- divi/exp/scipy/pyprima/common/preproc.py +393 -0
- divi/exp/scipy/pyprima/common/present.py +5 -0
- divi/exp/scipy/pyprima/common/ratio.py +56 -0
- divi/exp/scipy/pyprima/common/redrho.py +49 -0
- divi/exp/scipy/pyprima/common/selectx.py +346 -0
- divi/interfaces.py +25 -0
- divi/parallel_simulator.py +258 -0
- divi/qasm.py +220 -0
- divi/qem.py +191 -0
- divi/qlogger.py +119 -0
- divi/qoro_service.py +343 -0
- divi/qprog/__init__.py +13 -0
- divi/qprog/_graph_partitioning.py +619 -0
- divi/qprog/_mlae.py +182 -0
- divi/qprog/_qaoa.py +440 -0
- divi/qprog/_vqe.py +275 -0
- divi/qprog/_vqe_sweep.py +144 -0
- divi/qprog/batch.py +235 -0
- divi/qprog/optimizers.py +75 -0
- divi/qprog/quantum_program.py +493 -0
- divi/utils.py +116 -0
- qoro_divi-0.2.0b1.dist-info/LICENSE +190 -0
- qoro_divi-0.2.0b1.dist-info/LICENSES/Apache-2.0.txt +73 -0
- qoro_divi-0.2.0b1.dist-info/METADATA +57 -0
- qoro_divi-0.2.0b1.dist-info/RECORD +58 -0
- qoro_divi-0.2.0b1.dist-info/WHEEL +4 -0
divi/exp/cirq/_parser.py
ADDED
|
@@ -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
|
+
)
|