classiq 0.86.0__py3-none-any.whl → 0.87.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.
- classiq/__init__.py +2 -0
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- classiq/applications/combinatorial_helpers/encoding_mapping.py +11 -15
- classiq/applications/combinatorial_helpers/encoding_utils.py +6 -6
- classiq/applications/combinatorial_helpers/memory.py +4 -4
- classiq/applications/combinatorial_helpers/optimization_model.py +5 -5
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +6 -10
- classiq/applications/combinatorial_helpers/pyomo_utils.py +27 -26
- classiq/applications/combinatorial_helpers/sympy_utils.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/encoding.py +4 -6
- classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +4 -4
- classiq/applications/combinatorial_helpers/transformations/ising_converter.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/penalty_support.py +3 -3
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -0
- classiq/applications/hamiltonian/pauli_decomposition.py +33 -1
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +88 -11
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +54 -11
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +40 -25
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +29 -59
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +12 -5
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +21 -14
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +9 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +20 -1
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +97 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +11 -26
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +56 -0
- classiq/evaluators/qmod_node_evaluators/piecewise_evaluation.py +40 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +2 -3
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +48 -21
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +27 -5
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +292 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/execution/execution_session.py +1 -1
- classiq/execution/qnn.py +4 -1
- classiq/execution/user_budgets.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +10 -30
- classiq/interface/backend/quantum_backend_providers.py +63 -52
- classiq/interface/generator/arith/binary_ops.py +107 -115
- classiq/interface/generator/arith/extremum_operations.py +33 -45
- classiq/interface/generator/arith/number_utils.py +4 -1
- classiq/interface/generator/circuit_code/types_and_constants.py +0 -9
- classiq/interface/generator/compiler_keywords.py +2 -0
- classiq/interface/generator/function_param_list.py +133 -5
- classiq/interface/generator/functions/classical_type.py +59 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +6 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- classiq/interface/helpers/model_normalizer.py +2 -2
- classiq/interface/helpers/text_utils.py +7 -2
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/classical_if.py +40 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/quantum_type.py +61 -2
- classiq/interface/pretty_print/expression_to_qmod.py +24 -11
- classiq/interface/pyomo_extension/__init__.py +0 -4
- classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +2 -2
- classiq/model_expansions/arithmetic.py +43 -1
- classiq/model_expansions/arithmetic_compute_result_attrs.py +255 -0
- classiq/model_expansions/capturing/captured_vars.py +2 -5
- classiq/model_expansions/quantum_operations/allocate.py +22 -15
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -71
- classiq/model_expansions/quantum_operations/bind.py +15 -7
- classiq/model_expansions/quantum_operations/call_emitter.py +2 -10
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -0
- classiq/open_library/functions/__init__.py +3 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/qmod/builtins/enums.py +2 -2
- classiq/qmod/builtins/structs.py +33 -18
- classiq/qmod/pretty_print/expression_to_python.py +7 -9
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/METADATA +3 -3
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/RECORD +83 -89
- classiq/interface/generator/amplitude_estimation.py +0 -34
- classiq/interface/generator/function_param_list_without_self_reference.py +0 -160
- classiq/interface/generator/grover_diffuser.py +0 -93
- classiq/interface/generator/grover_operator.py +0 -106
- classiq/interface/generator/oracles/__init__.py +0 -3
- classiq/interface/generator/oracles/arithmetic_oracle.py +0 -82
- classiq/interface/generator/oracles/custom_oracle.py +0 -65
- classiq/interface/generator/oracles/oracle_abc.py +0 -76
- classiq/interface/generator/oracles/oracle_function_param_list.py +0 -6
- classiq/interface/generator/piecewise_linear_amplitude_loading.py +0 -165
- classiq/interface/generator/qpe.py +0 -169
- classiq/interface/grover/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- /classiq/{interface/grover → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/WHEEL +0 -0
@@ -57,6 +57,10 @@ class QuantumType(HashableASTNode):
|
|
57
57
|
def set_size_in_bits(self, val: int) -> None:
|
58
58
|
self._size_in_bits = val
|
59
59
|
|
60
|
+
@property
|
61
|
+
def minimal_size_in_bits(self) -> int:
|
62
|
+
raise NotImplementedError
|
63
|
+
|
60
64
|
def get_proxy(self, handle: "HandleBinding") -> QmodSizedProxy:
|
61
65
|
return QmodSizedProxy(handle=handle, size=self.size_in_bits)
|
62
66
|
|
@@ -64,6 +68,10 @@ class QuantumType(HashableASTNode):
|
|
64
68
|
def qmod_type_name(self) -> str:
|
65
69
|
raise NotImplementedError
|
66
70
|
|
71
|
+
@property
|
72
|
+
def raw_qmod_type_name(self) -> str:
|
73
|
+
return self.qmod_type_name
|
74
|
+
|
67
75
|
@property
|
68
76
|
def type_name(self) -> str:
|
69
77
|
raise NotImplementedError
|
@@ -167,6 +175,10 @@ class QuantumBit(QuantumScalar):
|
|
167
175
|
) -> tuple[float, float]:
|
168
176
|
return (0, 1)
|
169
177
|
|
178
|
+
@property
|
179
|
+
def minimal_size_in_bits(self) -> int:
|
180
|
+
return 1
|
181
|
+
|
170
182
|
|
171
183
|
class QuantumBitvector(QuantumType):
|
172
184
|
element_type: "ConcreteQuantumType" = Field(
|
@@ -182,7 +194,7 @@ class QuantumBitvector(QuantumType):
|
|
182
194
|
|
183
195
|
def _update_size_in_bits_from_declaration(self) -> None:
|
184
196
|
self.element_type._update_size_in_bits_from_declaration()
|
185
|
-
if self.element_type.has_size_in_bits and self.
|
197
|
+
if self.element_type.has_size_in_bits and self.has_constant_length:
|
186
198
|
assert self.length is not None
|
187
199
|
self._size_in_bits = (
|
188
200
|
self.element_type.size_in_bits * self.length.to_int_value()
|
@@ -192,6 +204,14 @@ class QuantumBitvector(QuantumType):
|
|
192
204
|
def has_length(self) -> bool:
|
193
205
|
return self.length is not None and self.length.is_evaluated()
|
194
206
|
|
207
|
+
@property
|
208
|
+
def has_constant_length(self) -> bool:
|
209
|
+
return (
|
210
|
+
self.length is not None
|
211
|
+
and self.length.is_evaluated()
|
212
|
+
and self.length.is_constant()
|
213
|
+
)
|
214
|
+
|
195
215
|
@property
|
196
216
|
def length_value(self) -> int:
|
197
217
|
if not self.has_length:
|
@@ -217,6 +237,10 @@ class QuantumBitvector(QuantumType):
|
|
217
237
|
length = [self.length.expr] if self.length is not None else []
|
218
238
|
return f"QArray[{', '.join(element_type + length)}]"
|
219
239
|
|
240
|
+
@property
|
241
|
+
def raw_qmod_type_name(self) -> str:
|
242
|
+
return "QArray"
|
243
|
+
|
220
244
|
@property
|
221
245
|
def type_name(self) -> str:
|
222
246
|
return "Quantum array"
|
@@ -240,6 +264,14 @@ class QuantumBitvector(QuantumType):
|
|
240
264
|
exprs.append(self.length)
|
241
265
|
return exprs
|
242
266
|
|
267
|
+
@property
|
268
|
+
def minimal_size_in_bits(self) -> int:
|
269
|
+
if self.has_constant_length:
|
270
|
+
length = self.length_value
|
271
|
+
else:
|
272
|
+
length = 1
|
273
|
+
return length * self.element_type.minimal_size_in_bits
|
274
|
+
|
243
275
|
|
244
276
|
class QuantumNumeric(QuantumScalar):
|
245
277
|
kind: Literal["qnum"]
|
@@ -294,7 +326,11 @@ class QuantumNumeric(QuantumScalar):
|
|
294
326
|
)
|
295
327
|
|
296
328
|
def _update_size_in_bits_from_declaration(self) -> None:
|
297
|
-
if
|
329
|
+
if (
|
330
|
+
self.size is not None
|
331
|
+
and self.size.is_evaluated()
|
332
|
+
and self.size.is_constant()
|
333
|
+
):
|
298
334
|
self._size_in_bits = self.size.to_int_value()
|
299
335
|
|
300
336
|
def get_proxy(self, handle: "HandleBinding") -> QmodQNumProxy:
|
@@ -307,6 +343,21 @@ class QuantumNumeric(QuantumScalar):
|
|
307
343
|
|
308
344
|
@property
|
309
345
|
def qmod_type_name(self) -> str:
|
346
|
+
if (
|
347
|
+
self.size is not None
|
348
|
+
and (
|
349
|
+
self.is_signed is None
|
350
|
+
or (self.is_signed.is_evaluated() and not self.is_signed.value.value)
|
351
|
+
)
|
352
|
+
and (
|
353
|
+
self.fraction_digits is None
|
354
|
+
or (
|
355
|
+
self.fraction_digits.is_evaluated()
|
356
|
+
and self.fraction_digits.value.value == 0
|
357
|
+
)
|
358
|
+
)
|
359
|
+
):
|
360
|
+
return f"QNum[{self.size.expr}]"
|
310
361
|
if (
|
311
362
|
self.size is not None
|
312
363
|
and self.is_signed is not None
|
@@ -315,6 +366,10 @@ class QuantumNumeric(QuantumScalar):
|
|
315
366
|
return f"QNum[{self.size.expr}, {self.is_signed.expr}, {self.fraction_digits.expr}]"
|
316
367
|
return "QNum"
|
317
368
|
|
369
|
+
@property
|
370
|
+
def raw_qmod_type_name(self) -> str:
|
371
|
+
return "QNum"
|
372
|
+
|
318
373
|
@property
|
319
374
|
def type_name(self) -> str:
|
320
375
|
return "Quantum numeric"
|
@@ -372,6 +427,10 @@ class QuantumNumeric(QuantumScalar):
|
|
372
427
|
number_utils.limit_fraction_places(bounds[1], machine_precision),
|
373
428
|
)
|
374
429
|
|
430
|
+
@property
|
431
|
+
def minimal_size_in_bits(self) -> int:
|
432
|
+
return self.size_in_bits if self.has_size_in_bits else 1
|
433
|
+
|
375
434
|
|
376
435
|
class RegisterQuantumType(BaseModel):
|
377
436
|
quantum_types: "ConcreteQuantumType"
|
@@ -61,15 +61,14 @@ class ASTToQMODCode:
|
|
61
61
|
if isinstance(node, ast.Module):
|
62
62
|
return self.indent.join(self.ast_to_code(child) for child in node.body)
|
63
63
|
elif isinstance(node, ast.Attribute):
|
64
|
-
# Enum attribute access
|
65
|
-
if not isinstance(node.value, ast.Name) or not isinstance(node.attr, str):
|
66
|
-
raise PrettyPrinterError("Error parsing enum attribute access")
|
67
|
-
if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
|
68
|
-
raise PrettyPrinterError("Error parsing enum attribute access")
|
69
64
|
# FIXME: identify enum member accesses by type name (CLS-2858)
|
70
|
-
if
|
71
|
-
|
72
|
-
|
65
|
+
if (
|
66
|
+
isinstance(node.value, ast.Name)
|
67
|
+
and len(node.value.id) > 0
|
68
|
+
and node.value.id[0].isupper()
|
69
|
+
):
|
70
|
+
return f"{node.value.id}::{node.attr}"
|
71
|
+
return f"{self.visit(node.value)}.{node.attr}"
|
73
72
|
elif isinstance(node, ast.Name):
|
74
73
|
return node.id
|
75
74
|
elif isinstance(node, ast.Num):
|
@@ -142,10 +141,24 @@ class ASTToQMODCode:
|
|
142
141
|
if len(node.args) != 2:
|
143
142
|
raise PrettyPrinterError("Error parsing array access.")
|
144
143
|
return f"{self.ast_to_code(node.args[0])}[{self.ast_to_code(node.args[1])}]"
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
elif len(node.keywords) > 0:
|
145
|
+
# struct instance
|
146
|
+
keywords = node.keywords
|
147
|
+
initializer_list = self.indent_items(
|
148
|
+
lambda: [
|
149
|
+
f"{keyword.arg}={self._cleaned_ast_to_code(keyword.value)}"
|
150
|
+
for keyword in keywords
|
151
|
+
if keyword.arg is not None
|
152
|
+
]
|
148
153
|
)
|
154
|
+
return f"{func} {{{initializer_list}}}"
|
155
|
+
else:
|
156
|
+
args = [self._cleaned_ast_to_code(arg) for arg in node.args]
|
157
|
+
kwargs = [
|
158
|
+
f"{kwarg.arg}={self._cleaned_ast_to_code(kwarg.value)}"
|
159
|
+
for kwarg in node.keywords
|
160
|
+
]
|
161
|
+
return "{}({})".format(func, ", ".join(args + kwargs))
|
149
162
|
elif isinstance(node, ast.Expr):
|
150
163
|
return self._cleaned_ast_to_code(node.value)
|
151
164
|
else:
|
@@ -17,10 +17,6 @@ pyomo.core.expr.relational_expr.EqualityExpression.getname = equality_expression
|
|
17
17
|
|
18
18
|
pyomo.core.base.set.Set._pprint_members = staticmethod(set_pprint.pprint_members)
|
19
19
|
|
20
|
-
pyomo.core.expr.sympy_tools._operatorMap.update({sympy.LessThan: lambda x, y: x <= y})
|
21
|
-
pyomo.core.expr.sympy_tools._operatorMap.update(
|
22
|
-
{sympy.GreaterThan: lambda x, y: x >= y}
|
23
|
-
)
|
24
20
|
pyomo.core.expr.sympy_tools._pyomo_operator_map.update({EqualityExpression: sympy.Eq})
|
25
21
|
|
26
22
|
pyomo.core.expr.sympy_tools.PyomoSympyBimap.getSympySymbol = (
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from typing import Any
|
2
2
|
|
3
3
|
import sympy
|
4
|
-
from pyomo.core.base import
|
4
|
+
from pyomo.core.base.var import VarData
|
5
5
|
from pyomo.core.expr.sympy_tools import PyomoSympyBimap
|
6
6
|
|
7
7
|
|
@@ -25,5 +25,5 @@ def get_sympy_symbol(self: PyomoSympyBimap, pyomo_object: Any) -> sympy.Symbol:
|
|
25
25
|
# The name for the new sympy object is derived
|
26
26
|
# from the pyomo object, instead of using generic name with serial number.
|
27
27
|
# This is intended to have better corelation between the the pyomo variables and sympy variables.
|
28
|
-
def _get_sympy_name(pyomo_object:
|
28
|
+
def _get_sympy_name(pyomo_object: VarData) -> str:
|
29
29
|
return pyomo_object.name.replace("[", "").replace("]", "")
|
@@ -3,7 +3,9 @@ from typing import Optional, Union
|
|
3
3
|
|
4
4
|
from classiq.interface.generator.arith import number_utils
|
5
5
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
6
|
+
from classiq.interface.generator.expressions.expression import Expression
|
6
7
|
from classiq.interface.model.quantum_type import (
|
8
|
+
QuantumNumeric,
|
7
9
|
QuantumScalar,
|
8
10
|
register_info_to_quantum_type,
|
9
11
|
)
|
@@ -48,9 +50,48 @@ class NumericAttributes:
|
|
48
50
|
def ub(self) -> float:
|
49
51
|
return self.bounds[1]
|
50
52
|
|
53
|
+
@property
|
54
|
+
def integer_digits(self) -> int:
|
55
|
+
return self.size - self.fraction_digits
|
56
|
+
|
57
|
+
def to_quantum_numeric(self) -> QuantumNumeric:
|
58
|
+
quantum_numeric = QuantumNumeric(
|
59
|
+
size=Expression(expr=str(self.size)),
|
60
|
+
is_signed=Expression(expr=str(self.is_signed)),
|
61
|
+
fraction_digits=Expression(expr=str(self.fraction_digits)),
|
62
|
+
)
|
63
|
+
quantum_numeric.set_bounds(self.bounds)
|
64
|
+
return quantum_numeric
|
65
|
+
|
66
|
+
def to_register(self) -> RegisterArithmeticInfo:
|
67
|
+
return RegisterArithmeticInfo(
|
68
|
+
size=self.size,
|
69
|
+
is_signed=self.is_signed,
|
70
|
+
fraction_places=self.fraction_digits,
|
71
|
+
bounds=self.bounds,
|
72
|
+
)
|
73
|
+
|
74
|
+
def trim_fraction_digits(self, machine_precision: int) -> "NumericAttributes":
|
75
|
+
trimmed_digits = self.fraction_digits - machine_precision
|
76
|
+
if trimmed_digits < 0:
|
77
|
+
return self
|
78
|
+
|
79
|
+
return NumericAttributes(
|
80
|
+
size=self.size - trimmed_digits,
|
81
|
+
is_signed=self.is_signed,
|
82
|
+
fraction_digits=self.fraction_digits - trimmed_digits,
|
83
|
+
bounds=self.bounds,
|
84
|
+
trim_bounds=True,
|
85
|
+
)
|
86
|
+
|
51
87
|
@classmethod
|
52
88
|
def from_bounds(
|
53
|
-
cls,
|
89
|
+
cls,
|
90
|
+
lb: float,
|
91
|
+
ub: float,
|
92
|
+
fraction_places: int,
|
93
|
+
machine_precision: int,
|
94
|
+
trim_bounds: bool = False,
|
54
95
|
) -> "NumericAttributes":
|
55
96
|
size, is_signed, fraction_digits = number_utils.bounds_to_attributes(
|
56
97
|
lb, ub, fraction_places, machine_precision
|
@@ -60,6 +101,7 @@ class NumericAttributes:
|
|
60
101
|
is_signed=is_signed,
|
61
102
|
fraction_digits=fraction_digits,
|
62
103
|
bounds=(lb, ub),
|
104
|
+
trim_bounds=trim_bounds,
|
63
105
|
)
|
64
106
|
|
65
107
|
@classmethod
|
@@ -1,3 +1,9 @@
|
|
1
|
+
import math
|
2
|
+
from collections.abc import Sequence
|
3
|
+
from typing import Union
|
4
|
+
|
5
|
+
from classiq.interface.exceptions import ClassiqValueError
|
6
|
+
|
1
7
|
from classiq.model_expansions.arithmetic import NumericAttributes
|
2
8
|
|
3
9
|
|
@@ -51,6 +57,62 @@ def compute_result_attrs_negate(
|
|
51
57
|
)
|
52
58
|
|
53
59
|
|
60
|
+
def compute_result_attrs_bitwise_and(
|
61
|
+
left: NumericAttributes,
|
62
|
+
right: NumericAttributes,
|
63
|
+
machine_precision: int,
|
64
|
+
) -> NumericAttributes:
|
65
|
+
if left.fraction_digits > 0 or right.fraction_digits > 0:
|
66
|
+
raise ClassiqValueError("Bitwise AND is only defined for integers")
|
67
|
+
|
68
|
+
# we comply with python, which uses arbitrary precision, so a positive number can
|
69
|
+
# always be represented by "0..." and a negative number by "1...", thus their
|
70
|
+
# bitwise AND is always non-negative
|
71
|
+
return NumericAttributes(
|
72
|
+
size=max(left.size, right.size),
|
73
|
+
is_signed=left.is_signed and right.is_signed,
|
74
|
+
fraction_digits=0,
|
75
|
+
)
|
76
|
+
|
77
|
+
|
78
|
+
def compute_result_attrs_bitwise_or(
|
79
|
+
left: NumericAttributes,
|
80
|
+
right: NumericAttributes,
|
81
|
+
machine_precision: int,
|
82
|
+
) -> NumericAttributes:
|
83
|
+
if left.fraction_digits > 0 or right.fraction_digits > 0:
|
84
|
+
raise ClassiqValueError("Bitwise OR is only defined for integers")
|
85
|
+
|
86
|
+
# we comply with python, which uses arbitrary precision, so a positive number can
|
87
|
+
# always be represented by "0..." and a negative number by "1...", thus their
|
88
|
+
# bitwise OR is always negative
|
89
|
+
|
90
|
+
if left.is_signed and not right.is_signed:
|
91
|
+
# we need to extend right so its MSB is always 0
|
92
|
+
size = max(left.size, right.size + 1)
|
93
|
+
elif not left.is_signed and right.is_signed:
|
94
|
+
# we need to extend left so its MSB is always 0
|
95
|
+
size = max(left.size + 1, right.size)
|
96
|
+
else:
|
97
|
+
size = max(left.size, right.size)
|
98
|
+
|
99
|
+
return NumericAttributes(
|
100
|
+
size=size,
|
101
|
+
is_signed=left.is_signed or right.is_signed,
|
102
|
+
fraction_digits=0,
|
103
|
+
)
|
104
|
+
|
105
|
+
|
106
|
+
def compute_result_attrs_bitwise_xor(
|
107
|
+
left: NumericAttributes,
|
108
|
+
right: NumericAttributes,
|
109
|
+
machine_precision: int,
|
110
|
+
) -> NumericAttributes:
|
111
|
+
if left.fraction_digits > 0 or right.fraction_digits > 0:
|
112
|
+
raise ClassiqValueError("Bitwise XOR is only defined for integers")
|
113
|
+
return compute_result_attrs_bitwise_or(left, right, machine_precision)
|
114
|
+
|
115
|
+
|
54
116
|
def compute_result_attrs_add(
|
55
117
|
left: NumericAttributes,
|
56
118
|
right: NumericAttributes,
|
@@ -69,3 +131,196 @@ def compute_result_attrs_subtract(
|
|
69
131
|
) -> NumericAttributes:
|
70
132
|
tmp = compute_result_attrs_negate(right, machine_precision)
|
71
133
|
return compute_result_attrs_add(left, tmp, machine_precision)
|
134
|
+
|
135
|
+
|
136
|
+
def compute_result_attrs_multiply(
|
137
|
+
left: NumericAttributes,
|
138
|
+
right: NumericAttributes,
|
139
|
+
machine_precision: int,
|
140
|
+
) -> NumericAttributes:
|
141
|
+
extremal_values = [
|
142
|
+
left_val * right_val for left_val in left.bounds for right_val in right.bounds
|
143
|
+
]
|
144
|
+
fraction_places = left.fraction_digits + right.fraction_digits
|
145
|
+
return NumericAttributes.from_bounds(
|
146
|
+
min(extremal_values),
|
147
|
+
max(extremal_values),
|
148
|
+
fraction_places,
|
149
|
+
machine_precision,
|
150
|
+
trim_bounds=True,
|
151
|
+
)
|
152
|
+
|
153
|
+
|
154
|
+
def compute_result_attrs_power(
|
155
|
+
left: NumericAttributes,
|
156
|
+
right: Union[int, float],
|
157
|
+
machine_precision: int,
|
158
|
+
) -> NumericAttributes:
|
159
|
+
if not float(right).is_integer() or right <= 0:
|
160
|
+
raise ClassiqValueError("Power must be a positive integer")
|
161
|
+
right = int(right)
|
162
|
+
|
163
|
+
bounds: tuple[float, float]
|
164
|
+
if (right % 2 == 0) and (left.lb < 0 < left.ub):
|
165
|
+
bounds = (0, max(left.lb**right, left.ub**right))
|
166
|
+
else:
|
167
|
+
extremal_values = (left.lb**right, left.ub**right)
|
168
|
+
bounds = (min(extremal_values), max(extremal_values))
|
169
|
+
fraction_places = left.fraction_digits * right
|
170
|
+
return NumericAttributes.from_bounds(
|
171
|
+
bounds[0], bounds[1], fraction_places, machine_precision, trim_bounds=True
|
172
|
+
)
|
173
|
+
|
174
|
+
|
175
|
+
def compute_result_attrs_lshift(
|
176
|
+
left: NumericAttributes,
|
177
|
+
right: Union[int, float],
|
178
|
+
machine_precision: int,
|
179
|
+
) -> NumericAttributes:
|
180
|
+
if not float(right).is_integer() or right < 0:
|
181
|
+
raise ClassiqValueError("Shift must be a non-negative integer")
|
182
|
+
right = int(right)
|
183
|
+
|
184
|
+
scale = 1 << left.fraction_digits
|
185
|
+
lb = (int(left.lb * scale) << right) / scale
|
186
|
+
ub = (int(left.ub * scale) << right) / scale
|
187
|
+
fraction_digits = max(left.fraction_digits - right, 0)
|
188
|
+
integer_digits = left.integer_digits + right
|
189
|
+
return NumericAttributes(
|
190
|
+
size=integer_digits + fraction_digits,
|
191
|
+
is_signed=left.is_signed,
|
192
|
+
fraction_digits=fraction_digits,
|
193
|
+
bounds=(lb, ub),
|
194
|
+
)
|
195
|
+
|
196
|
+
|
197
|
+
def compute_result_attrs_rshift(
|
198
|
+
left: NumericAttributes,
|
199
|
+
right: Union[int, float],
|
200
|
+
machine_precision: int,
|
201
|
+
) -> NumericAttributes:
|
202
|
+
if not float(right).is_integer() or right < 0:
|
203
|
+
raise ClassiqValueError("Shift must be a non-negative integer")
|
204
|
+
right = int(right)
|
205
|
+
|
206
|
+
scale = 1 << left.fraction_digits
|
207
|
+
lb = (int(left.lb * scale) >> right) / scale
|
208
|
+
ub = (int(left.ub * scale) >> right) / scale
|
209
|
+
fraction_digits = (
|
210
|
+
0 if (right >= left.size and not left.is_signed) else left.fraction_digits
|
211
|
+
)
|
212
|
+
return NumericAttributes(
|
213
|
+
size=max(left.size - right, fraction_digits, 1),
|
214
|
+
is_signed=left.is_signed,
|
215
|
+
fraction_digits=fraction_digits,
|
216
|
+
bounds=(lb, ub),
|
217
|
+
)
|
218
|
+
|
219
|
+
|
220
|
+
def compute_result_attrs_modulo(
|
221
|
+
left: NumericAttributes,
|
222
|
+
right: Union[int, float],
|
223
|
+
machine_precision: int,
|
224
|
+
) -> NumericAttributes:
|
225
|
+
if not float(right).is_integer() or right < 2:
|
226
|
+
raise ClassiqValueError("Modulus must be a positive power of two")
|
227
|
+
right = int(right)
|
228
|
+
if right & (right - 1) != 0:
|
229
|
+
raise ClassiqValueError("Modulus must be a positive power of two")
|
230
|
+
|
231
|
+
if left.fraction_digits > 0:
|
232
|
+
raise ClassiqValueError("Modulo is supported for integers only")
|
233
|
+
|
234
|
+
size = int(math.log2(right))
|
235
|
+
if not left.is_signed and size >= left.size:
|
236
|
+
return left
|
237
|
+
|
238
|
+
return NumericAttributes(
|
239
|
+
size=size,
|
240
|
+
fraction_digits=0,
|
241
|
+
is_signed=False,
|
242
|
+
)
|
243
|
+
|
244
|
+
|
245
|
+
def compute_result_attrs_min(
|
246
|
+
args: Sequence[NumericAttributes],
|
247
|
+
machine_precision: int,
|
248
|
+
) -> NumericAttributes:
|
249
|
+
if len(args) < 1:
|
250
|
+
raise ClassiqValueError("Min expects at least one argument")
|
251
|
+
|
252
|
+
args = [arg.trim_fraction_digits(machine_precision) for arg in args]
|
253
|
+
|
254
|
+
result_attrs = args[0]
|
255
|
+
for attrs in args[1:]:
|
256
|
+
if result_attrs.lb == result_attrs.ub == attrs.lb == attrs.ub:
|
257
|
+
if attrs.size < result_attrs.size:
|
258
|
+
result_attrs = attrs
|
259
|
+
elif result_attrs.ub <= attrs.lb:
|
260
|
+
pass
|
261
|
+
elif attrs.ub <= result_attrs.lb:
|
262
|
+
result_attrs = attrs
|
263
|
+
else:
|
264
|
+
integer_digits = max(result_attrs.integer_digits, attrs.integer_digits)
|
265
|
+
fraction_digits = max(result_attrs.fraction_digits, attrs.fraction_digits)
|
266
|
+
bounds = (min(result_attrs.lb, attrs.lb), min(result_attrs.ub, attrs.ub))
|
267
|
+
result_attrs = NumericAttributes(
|
268
|
+
size=integer_digits + fraction_digits,
|
269
|
+
is_signed=bounds[0] < 0,
|
270
|
+
fraction_digits=fraction_digits,
|
271
|
+
bounds=bounds,
|
272
|
+
)
|
273
|
+
|
274
|
+
return result_attrs
|
275
|
+
|
276
|
+
|
277
|
+
def compute_result_attrs_max(
|
278
|
+
args: Sequence[NumericAttributes],
|
279
|
+
machine_precision: int,
|
280
|
+
) -> NumericAttributes:
|
281
|
+
if len(args) < 1:
|
282
|
+
raise ClassiqValueError("Max expects at least one argument")
|
283
|
+
|
284
|
+
args = [arg.trim_fraction_digits(machine_precision) for arg in args]
|
285
|
+
|
286
|
+
result_attrs = args[0]
|
287
|
+
for attrs in args[1:]:
|
288
|
+
if result_attrs.lb == result_attrs.ub == attrs.lb == attrs.ub:
|
289
|
+
if attrs.size < result_attrs.size:
|
290
|
+
result_attrs = attrs
|
291
|
+
elif result_attrs.lb >= attrs.ub:
|
292
|
+
pass
|
293
|
+
elif attrs.lb >= result_attrs.ub:
|
294
|
+
result_attrs = attrs
|
295
|
+
else:
|
296
|
+
integer_digits = max(result_attrs.integer_digits, attrs.integer_digits)
|
297
|
+
fraction_digits = max(result_attrs.fraction_digits, attrs.fraction_digits)
|
298
|
+
bounds = (max(result_attrs.lb, attrs.lb), max(result_attrs.ub, attrs.ub))
|
299
|
+
result_attrs = NumericAttributes(
|
300
|
+
size=integer_digits + fraction_digits,
|
301
|
+
is_signed=bounds[0] < 0,
|
302
|
+
fraction_digits=fraction_digits,
|
303
|
+
bounds=bounds,
|
304
|
+
)
|
305
|
+
|
306
|
+
return result_attrs
|
307
|
+
|
308
|
+
|
309
|
+
def compute_result_attrs_quantum_subscript(
|
310
|
+
values: Sequence[float],
|
311
|
+
machine_precision: int,
|
312
|
+
) -> NumericAttributes:
|
313
|
+
if len(values) < 1:
|
314
|
+
raise ClassiqValueError("Quantum subscript expects at least one argument")
|
315
|
+
|
316
|
+
values_attrs = [
|
317
|
+
NumericAttributes.from_constant(val, machine_precision) for val in values
|
318
|
+
]
|
319
|
+
values = [attrs.lb for attrs in values_attrs]
|
320
|
+
fraction_digits = max(attrs.fraction_digits for attrs in values_attrs)
|
321
|
+
return NumericAttributes.from_bounds(
|
322
|
+
min(values),
|
323
|
+
max(values),
|
324
|
+
fraction_digits,
|
325
|
+
machine_precision,
|
326
|
+
)
|
@@ -49,7 +49,6 @@ from classiq.model_expansions.transformers.model_renamer import (
|
|
49
49
|
HandleRenaming,
|
50
50
|
SymbolRenaming,
|
51
51
|
)
|
52
|
-
from classiq.model_expansions.transformers.var_splitter import SymbolPart
|
53
52
|
|
54
53
|
if TYPE_CHECKING:
|
55
54
|
from classiq.model_expansions.closure import FunctionClosure
|
@@ -603,10 +602,9 @@ class CapturedVars:
|
|
603
602
|
def _get_immediate_captured_mapping(self) -> SymbolRenaming:
|
604
603
|
return {
|
605
604
|
captured_handle.handle: [
|
606
|
-
|
605
|
+
HandleRenaming(
|
607
606
|
source_handle=captured_handle.handle,
|
608
607
|
target_var_name=captured_handle.mangled_name,
|
609
|
-
target_var_type=captured_handle.quantum_type,
|
610
608
|
)
|
611
609
|
]
|
612
610
|
for captured_handle in self._captured_handles
|
@@ -616,10 +614,9 @@ class CapturedVars:
|
|
616
614
|
def _get_propagated_captured_mapping(self) -> SymbolRenaming:
|
617
615
|
return {
|
618
616
|
captured_handle.mangled_handle: [
|
619
|
-
|
617
|
+
HandleRenaming(
|
620
618
|
source_handle=captured_handle.mangled_handle,
|
621
619
|
target_var_name=captured_handle.mangled_name,
|
622
|
-
target_var_type=captured_handle.quantum_type,
|
623
620
|
)
|
624
621
|
]
|
625
622
|
for captured_handle in self._captured_handles
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Union
|
|
3
3
|
import sympy
|
4
4
|
|
5
5
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
6
|
-
from classiq.interface.exceptions import ClassiqValueError
|
6
|
+
from classiq.interface.exceptions import ClassiqExpansionError, ClassiqValueError
|
7
7
|
from classiq.interface.generator.expressions.expression import Expression
|
8
8
|
from classiq.interface.model.allocate import Allocate
|
9
9
|
from classiq.interface.model.handle_binding import NestedHandleBinding
|
@@ -12,7 +12,9 @@ from classiq.interface.model.quantum_type import (
|
|
12
12
|
QuantumNumeric,
|
13
13
|
)
|
14
14
|
|
15
|
-
from classiq.evaluators.
|
15
|
+
from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
|
16
|
+
inject_quantum_type_attributes_inplace,
|
17
|
+
)
|
16
18
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
17
19
|
from classiq.model_expansions.scope import QuantumSymbol
|
18
20
|
|
@@ -95,11 +97,14 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
95
97
|
size_value = self._interpret_size(size, str(target.handle))
|
96
98
|
op_update_dict["size"] = Expression(expr=str(size_value))
|
97
99
|
|
98
|
-
if not isinstance(
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
100
|
+
if not isinstance(
|
101
|
+
size_value, sympy.Basic
|
102
|
+
) and not inject_quantum_type_attributes_inplace(
|
103
|
+
QuantumBitvector(length=op_update_dict["size"]), target.quantum_type
|
104
|
+
):
|
105
|
+
raise ClassiqExpansionError(
|
106
|
+
f"Cannot allocate {op_update_dict['size']} qubits for variable "
|
107
|
+
f"{str(target)!r} of type {target.quantum_type.qmod_type_name}"
|
103
108
|
)
|
104
109
|
|
105
110
|
def _handle_with_numeric_attrs(
|
@@ -127,15 +132,17 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
127
132
|
isinstance(size_value, sympy.Basic)
|
128
133
|
or isinstance(is_signed_value, sympy.Basic)
|
129
134
|
or isinstance(fraction_digits_value, sympy.Basic)
|
135
|
+
) and not inject_quantum_type_attributes_inplace(
|
136
|
+
QuantumNumeric(
|
137
|
+
size=op_update_dict["size"],
|
138
|
+
is_signed=op_update_dict["is_signed"],
|
139
|
+
fraction_digits=op_update_dict["fraction_digits"],
|
140
|
+
),
|
141
|
+
target.quantum_type,
|
130
142
|
):
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
is_signed=op_update_dict["is_signed"],
|
135
|
-
fraction_digits=op_update_dict["fraction_digits"],
|
136
|
-
),
|
137
|
-
target.quantum_type,
|
138
|
-
var_name,
|
143
|
+
raise ClassiqExpansionError(
|
144
|
+
f"Cannot allocate {op_update_dict['size']} qubits for variable "
|
145
|
+
f"{var_name!r} of type {target.quantum_type.qmod_type_name}"
|
139
146
|
)
|
140
147
|
|
141
148
|
def _interpret_size(
|
@@ -44,9 +44,7 @@ def convert_inplace_op_bool_expression(
|
|
44
44
|
if not is_bool(op.expression.expr):
|
45
45
|
return
|
46
46
|
_validate_target_type(target, op.expression.expr, op.operation_kind)
|
47
|
-
op.expression = op.expression.
|
48
|
-
update=dict(expr="1" if op.expression.expr == "True" else "0")
|
49
|
-
)
|
47
|
+
op.expression = Expression(expr="1" if op.expression.expr == "True" else "0")
|
50
48
|
|
51
49
|
|
52
50
|
def _supported_types() -> tuple[str, ...]:
|