dwave-gate 0.4.0__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.
dwave/gate/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ # Copyright 2026 D-Wave
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
+
15
+ __version__ = "0.4.0"
@@ -0,0 +1,44 @@
1
+ # Copyright 2026 D-Wave
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
+
15
+ from . import operations
16
+ from .components import QCDLModule, QCDLModuleContainer, Scope, procedure
17
+ from .constants import LogicalOutcomeToInteger
18
+ from .exceptions import QCDLInternalError, QCDLUserError
19
+ from .qcdl_circuit import qcdl
20
+ from .qcdl_models import QCDLProgram, QCDLProcedureDef, QCDLStatement
21
+ from .registers import FixedPointRegister, Register, arbitrary_function
22
+ from .statement import Statement
23
+ from .transformer import display_qcdl, print_qcdl
24
+
25
+ __all__ = [
26
+ "FixedPointRegister",
27
+ "LogicalOutcomeToInteger",
28
+ "QCDLInternalError",
29
+ "QCDLUserError",
30
+ "QCDLProgram",
31
+ "QCDLModule",
32
+ "QCDLModuleContainer",
33
+ "QCDLProcedureDef",
34
+ "QCDLStatement",
35
+ "Register",
36
+ "Scope",
37
+ "Statement",
38
+ "arbitrary_function",
39
+ "display_qcdl",
40
+ "operations",
41
+ "print_qcdl",
42
+ "procedure",
43
+ "qcdl",
44
+ ]
@@ -0,0 +1,326 @@
1
+ # Copyright 2026 D-Wave
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
+
15
+ from __future__ import annotations
16
+
17
+ import abc
18
+ import numbers
19
+ from collections import defaultdict
20
+ from collections.abc import MutableSequence, Sequence
21
+ from typing import TYPE_CHECKING, Any
22
+
23
+ from .exceptions import QCDLInternalError
24
+
25
+ if TYPE_CHECKING:
26
+ from .components import Procedure, QCDLModule
27
+ from .qcdl_circuit import QCDLCircuit
28
+
29
+
30
+ class QCDLArgument(abc.ABC):
31
+ """This enables passing arbitrary objects to the compiler as args/kwargs to
32
+ a procedure"""
33
+
34
+ @abc.abstractmethod
35
+ def serialize(self) -> Any:
36
+ """Convert the object into something (jsonifiable) that can go to the
37
+ compiler."""
38
+ raise NotImplementedError
39
+
40
+
41
+ class QCDLModuleContainerBase(QCDLArgument):
42
+ """This lets you pass an object containing QCDLModules to a procedure"""
43
+
44
+ @property
45
+ @abc.abstractmethod
46
+ def qcdl_modules(self) -> Sequence[QCDLModule]:
47
+ """The QCDLModule(s) this container holds
48
+
49
+ Returns:
50
+ sequence of QCDLModule
51
+ """
52
+ raise NotImplementedError
53
+
54
+ @property
55
+ def name(self) -> str:
56
+ names = ", ".join(m.qcdl_module_name for m in self.qcdl_modules)
57
+ return f"<QCDLModuleContainerBase {names}>"
58
+
59
+ def serialize(self) -> list[str]:
60
+ return [m.qcdl_module_name for m in self.qcdl_modules]
61
+
62
+ @property
63
+ @abc.abstractmethod
64
+ def _is_qcdl_module(self) -> bool:
65
+ # The purpose of this method is to provide a way to determine the class
66
+ # w/o hasattr or isinstance
67
+ raise NotImplementedError
68
+
69
+ @property
70
+ def procedure(self) -> Procedure:
71
+ proc = self.qcdl_modules[0].procedure
72
+ if not proc:
73
+ raise QCDLInternalError("qubits must be in a procedure")
74
+ return proc
75
+
76
+ @property
77
+ def state(self) -> QCDLCircuit:
78
+ return self.procedure.state
79
+
80
+ def __str__(self) -> str:
81
+ return self.name
82
+
83
+ @property
84
+ def op_key(self) -> str | None:
85
+ """Intended to represent the contents of a QCDLModuleContainerBase
86
+ besides its qcdl_modules, used for procedure names when this is an
87
+ argument.
88
+ """
89
+ return None
90
+
91
+ def set_procedure(self, new_proc: Procedure) -> None:
92
+ """Rewrap the QCDLModule objects with the new procedure
93
+
94
+ The default implementation assumes that `qcdl_modules` is a list.
95
+
96
+ Args:
97
+ new_proc (Procedure): the new Procedure
98
+ """
99
+
100
+ for idx, m in enumerate(self.qcdl_modules):
101
+ if isinstance(m, QCDLModuleContainerBase) and not m._is_qcdl_module:
102
+ m.set_procedure(new_proc)
103
+ else:
104
+ # QCDLModule's qcdl_modules is not mutable
105
+ if not isinstance(self.qcdl_modules, MutableSequence):
106
+ raise QCDLInternalError(
107
+ f"the default implementation only supports list,"
108
+ f" not {type(self.qcdl_modules)} on {self}"
109
+ )
110
+
111
+ self.qcdl_modules[idx] = m.from_rewrapping(m, new_proc=new_proc)
112
+
113
+
114
+ class VariableExpression(QCDLArgument):
115
+ """A QCDL variable expression
116
+
117
+ This is a compile-time expression.
118
+
119
+ This adds basic expression evaluation on top of variables. There's
120
+ technically no reason to use a Variable instead of this since an
121
+ expression can be just a single variable.
122
+
123
+ This can support any pythonic expression, but will be safer to use than
124
+ `eval`. You may use Variable objects (e.g., qcdl_args) in the expression.
125
+
126
+ For examples, see: https://pypi.org/project/simpleeval/
127
+
128
+
129
+ Args:
130
+ variable_expression (Any): a python variable_expression (converted to
131
+ str internally if not already a string)
132
+ """
133
+
134
+ TYPE = "variable_expression"
135
+
136
+ def __init__(self, variable_expression: Any) -> None:
137
+ if isinstance(variable_expression, VariableExpression):
138
+ variable_expression = variable_expression._variable_expression
139
+ if not isinstance(variable_expression, str):
140
+ variable_expression = str(variable_expression)
141
+ self._variable_expression: str = variable_expression
142
+
143
+ @property
144
+ def variable_expression(self) -> str:
145
+ return self._variable_expression
146
+
147
+ def serialize(self) -> dict:
148
+ return {
149
+ "type": VariableExpression.TYPE,
150
+ VariableExpression.TYPE: self.variable_expression,
151
+ }
152
+
153
+ @classmethod
154
+ def deserialize(cls, val: dict) -> VariableExpression:
155
+ if val["type"] != VariableExpression.TYPE:
156
+ raise TypeError(f"{val} is not a {VariableExpression.TYPE}")
157
+ return VariableExpression(val[VariableExpression.TYPE])
158
+
159
+ def __str__(self) -> str:
160
+ """Backticks for legibility when printed in QCDL"""
161
+ return "`{}`".format(self.variable_expression)
162
+
163
+ def _grouped(self) -> str:
164
+ """Parenthesize the expression, to prevent unforeseen order of
165
+ operations changes"""
166
+ return "(" + self._variable_expression + ")"
167
+
168
+ def _apply_operator(
169
+ self,
170
+ operator: str,
171
+ operand: numbers.Real | VariableExpression,
172
+ right: bool = False,
173
+ ) -> VariableExpression:
174
+ """Apply a mathematical operator to the expression"""
175
+ if isinstance(operand, (numbers.Real, VariableExpression)):
176
+ if isinstance(operand, VariableExpression):
177
+ operand = operand._grouped() # type: ignore[assignment] # grouped; no extra ``
178
+ inputs = [self._grouped(), operator, str(operand)]
179
+ if right:
180
+ inputs.reverse()
181
+ val = VariableExpression("{} {} {}".format(*inputs))
182
+ return val
183
+ else:
184
+ raise TypeError(
185
+ f"Cannot apply {operator} to operands that are not"
186
+ f" Expressions or numbers.Real in this case, {type(operand)}"
187
+ )
188
+
189
+ def __add__(self, operand: numbers.Real | VariableExpression) -> VariableExpression:
190
+ return self._apply_operator("+", operand)
191
+
192
+ def __sub__(self, operand: numbers.Real | VariableExpression) -> VariableExpression:
193
+ return self._apply_operator("-", operand)
194
+
195
+ def __mul__(self, operand: numbers.Real | VariableExpression) -> VariableExpression:
196
+ return self._apply_operator("*", operand)
197
+
198
+ def __truediv__(
199
+ self, operand: numbers.Real | VariableExpression
200
+ ) -> VariableExpression:
201
+ return self._apply_operator("/", operand)
202
+
203
+ def __floordiv__(
204
+ self, operand: numbers.Real | VariableExpression
205
+ ) -> VariableExpression:
206
+ return self._apply_operator("//", operand)
207
+
208
+ def __mod__(self, operand: numbers.Real | VariableExpression) -> VariableExpression:
209
+ return self._apply_operator("%", operand)
210
+
211
+ def __radd__(
212
+ self, operand: numbers.Real | VariableExpression
213
+ ) -> VariableExpression:
214
+ return self._apply_operator("+", operand, right=True)
215
+
216
+ def __rsub__(
217
+ self, operand: numbers.Real | VariableExpression
218
+ ) -> VariableExpression:
219
+ return self._apply_operator("-", operand, right=True)
220
+
221
+ def __rmul__(
222
+ self, operand: numbers.Real | VariableExpression
223
+ ) -> VariableExpression:
224
+ return self._apply_operator("*", operand, right=True)
225
+
226
+ def __rtruediv__(
227
+ self, operand: numbers.Real | VariableExpression
228
+ ) -> VariableExpression:
229
+ return self._apply_operator("/", operand, right=True)
230
+
231
+ def __rfloordiv__(
232
+ self, operand: numbers.Real | VariableExpression
233
+ ) -> VariableExpression:
234
+ return self._apply_operator("//", operand, right=True)
235
+
236
+ def __rmod__(
237
+ self, operand: numbers.Real | VariableExpression
238
+ ) -> VariableExpression:
239
+ return self._apply_operator("%", operand, right=True)
240
+
241
+ def __iadd__(
242
+ self, operand: numbers.Real | VariableExpression
243
+ ) -> VariableExpression:
244
+ return self._apply_operator("+", operand)
245
+
246
+ def __isub__(
247
+ self, operand: numbers.Real | VariableExpression
248
+ ) -> VariableExpression:
249
+ return self._apply_operator("-", operand)
250
+
251
+ def __imul__(
252
+ self, operand: numbers.Real | VariableExpression
253
+ ) -> VariableExpression:
254
+ return self._apply_operator("*", operand)
255
+
256
+ def __itruediv__(
257
+ self, operand: numbers.Real | VariableExpression
258
+ ) -> VariableExpression:
259
+ # May be missing an edge case?
260
+ return self._apply_operator("/", operand)
261
+
262
+ def __ifloordiv__(
263
+ self, operand: numbers.Real | VariableExpression
264
+ ) -> VariableExpression:
265
+ return self._apply_operator("//", operand)
266
+
267
+ def __imod__(
268
+ self, operand: numbers.Real | VariableExpression
269
+ ) -> VariableExpression:
270
+ return self._apply_operator("%", operand)
271
+
272
+
273
+ class Variable(QCDLArgument):
274
+ """This is the QCDL approach for creating a QCDL Variable
275
+
276
+ Corresponds 1-to-1 with the qcdl_objects.py Variable. There will be
277
+ no reason to use this instead of an RegisterExpression, but it's supported anyway
278
+ due to its special handling in the compiler.
279
+
280
+ Args:
281
+ variable (str): Name of the variable
282
+ """
283
+
284
+ TYPE = "variable"
285
+
286
+ def __init__(self, variable: str) -> None:
287
+ self._variable = variable
288
+
289
+ @property
290
+ def variable(self) -> str:
291
+ return self._variable
292
+
293
+ def serialize(self) -> dict[str, Any]:
294
+ return {"type": Variable.TYPE, Variable.TYPE: self.variable}
295
+
296
+
297
+ class IndexerMixin:
298
+ """This is used as a mixin for Procedure and QCDLCircuit"""
299
+
300
+ def __init__(self, next_indices: dict[str, int] | None = None) -> None:
301
+ self._next_indices: defaultdict[str, int] = defaultdict(lambda: 0)
302
+ if next_indices:
303
+ self._next_indices.update(next_indices)
304
+
305
+ def get_next_index(self, name: str) -> int:
306
+ """Unique indices
307
+
308
+ The uniqueness of the index depends on how this class is used.
309
+
310
+ There are many contexts (labels, axes, memory address tags are examples)
311
+ where a user needs a unique name, so a typical approach is to add a
312
+ unique integer to a user provided name. The degree of uniqueness
313
+ required depends on the context. For example, qcdl labels only need to
314
+ be unique within a procedure, while axis ids need to be globally unique.
315
+
316
+ This method will never return the same index twice for a given name.
317
+
318
+ Args:
319
+ name (str): namespace for the index
320
+
321
+ Returns:
322
+ int: index unique for this name
323
+ """
324
+ index = self._next_indices[name]
325
+ self._next_indices[name] += 1
326
+ return index