classiq 0.86.1__py3-none-any.whl → 0.88.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.
Potentially problematic release.
This version of classiq might be problematic. Click here for more details.
- classiq/__init__.py +2 -0
- classiq/applications/__init__.py +1 -2
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
- 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 +34 -2
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +117 -17
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -5
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +66 -16
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +48 -26
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +65 -72
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +13 -6
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +36 -19
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +17 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +24 -2
- 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 +3 -4
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +51 -24
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +28 -6
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +330 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/evaluators/type_type_match.py +1 -1
- classiq/execution/execution_session.py +18 -3
- 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/execution/primitives.py +1 -0
- classiq/interface/generator/application_apis/__init__.py +0 -1
- 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/expressions/atomic_expression_functions.py +0 -2
- classiq/interface/generator/function_param_list.py +129 -5
- classiq/interface/generator/functions/classical_type.py +67 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +12 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- classiq/interface/generator/transpiler_basis_gates.py +5 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +0 -8
- 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 +48 -0
- classiq/interface/model/classical_parameter_declaration.py +4 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/port_declaration.py +12 -0
- classiq/interface/model/quantum_function_declaration.py +12 -0
- classiq/interface/model/quantum_type.py +117 -2
- classiq/interface/pretty_print/expression_to_qmod.py +7 -8
- 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 +23 -16
- 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/model_expansions/quantum_operations/handle_evaluator.py +2 -8
- classiq/open_library/functions/__init__.py +4 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/open_library/functions/state_preparation.py +47 -5
- classiq/qmod/builtins/__init__.py +0 -3
- classiq/qmod/builtins/classical_functions.py +0 -28
- classiq/qmod/builtins/enums.py +26 -20
- classiq/qmod/builtins/functions/__init__.py +0 -5
- classiq/qmod/builtins/operations.py +142 -0
- classiq/qmod/builtins/structs.py +33 -29
- classiq/qmod/native/pretty_printer.py +1 -1
- classiq/qmod/pretty_print/expression_to_python.py +1 -6
- classiq/qmod/pretty_print/pretty_printer.py +4 -1
- classiq/qmod/qmod_variable.py +94 -2
- classiq/qmod/semantics/annotation/call_annotation.py +4 -2
- classiq/qmod/semantics/annotation/qstruct_annotator.py +20 -5
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/METADATA +5 -5
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/RECORD +106 -124
- classiq/applications/finance/__init__.py +0 -15
- classiq/interface/finance/finance_modelling_params.py +0 -11
- classiq/interface/finance/function_input.py +0 -102
- classiq/interface/finance/gaussian_model_input.py +0 -50
- classiq/interface/finance/log_normal_model_input.py +0 -40
- classiq/interface/finance/model_input.py +0 -22
- classiq/interface/generator/amplitude_estimation.py +0 -34
- classiq/interface/generator/application_apis/finance_declarations.py +0 -108
- classiq/interface/generator/expressions/enums/__init__.py +0 -0
- classiq/interface/generator/expressions/enums/finance_functions.py +0 -12
- classiq/interface/generator/finance.py +0 -107
- 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/__init__.py +0 -0
- classiq/interface/grover/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- classiq/qmod/builtins/functions/finance.py +0 -34
- /classiq/{interface/finance → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.1.dist-info → classiq-0.88.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
|
|
@@ -76,6 +84,10 @@ class QuantumType(HashableASTNode):
|
|
|
76
84
|
def is_evaluated(self) -> bool:
|
|
77
85
|
raise NotImplementedError
|
|
78
86
|
|
|
87
|
+
@property
|
|
88
|
+
def is_constant(self) -> bool:
|
|
89
|
+
raise NotImplementedError
|
|
90
|
+
|
|
79
91
|
@property
|
|
80
92
|
def expressions(self) -> list[Expression]:
|
|
81
93
|
return []
|
|
@@ -101,6 +113,9 @@ class QuantumScalar(QuantumType):
|
|
|
101
113
|
def fraction_digits_value(self) -> int:
|
|
102
114
|
raise NotImplementedError
|
|
103
115
|
|
|
116
|
+
def get_bounds(self) -> Optional[tuple[float, float]]:
|
|
117
|
+
return None
|
|
118
|
+
|
|
104
119
|
def get_effective_bounds(
|
|
105
120
|
self, machine_precision: Optional[int] = None
|
|
106
121
|
) -> tuple[float, float]:
|
|
@@ -146,6 +161,10 @@ class QuantumBit(QuantumScalar):
|
|
|
146
161
|
def is_evaluated(self) -> bool:
|
|
147
162
|
return True
|
|
148
163
|
|
|
164
|
+
@property
|
|
165
|
+
def is_constant(self) -> bool:
|
|
166
|
+
return True
|
|
167
|
+
|
|
149
168
|
@property
|
|
150
169
|
def has_sign(self) -> bool:
|
|
151
170
|
return True
|
|
@@ -167,6 +186,10 @@ class QuantumBit(QuantumScalar):
|
|
|
167
186
|
) -> tuple[float, float]:
|
|
168
187
|
return (0, 1)
|
|
169
188
|
|
|
189
|
+
@property
|
|
190
|
+
def minimal_size_in_bits(self) -> int:
|
|
191
|
+
return 1
|
|
192
|
+
|
|
170
193
|
|
|
171
194
|
class QuantumBitvector(QuantumType):
|
|
172
195
|
element_type: "ConcreteQuantumType" = Field(
|
|
@@ -182,7 +205,7 @@ class QuantumBitvector(QuantumType):
|
|
|
182
205
|
|
|
183
206
|
def _update_size_in_bits_from_declaration(self) -> None:
|
|
184
207
|
self.element_type._update_size_in_bits_from_declaration()
|
|
185
|
-
if self.element_type.has_size_in_bits and self.
|
|
208
|
+
if self.element_type.has_size_in_bits and self.has_constant_length:
|
|
186
209
|
assert self.length is not None
|
|
187
210
|
self._size_in_bits = (
|
|
188
211
|
self.element_type.size_in_bits * self.length.to_int_value()
|
|
@@ -192,6 +215,14 @@ class QuantumBitvector(QuantumType):
|
|
|
192
215
|
def has_length(self) -> bool:
|
|
193
216
|
return self.length is not None and self.length.is_evaluated()
|
|
194
217
|
|
|
218
|
+
@property
|
|
219
|
+
def has_constant_length(self) -> bool:
|
|
220
|
+
return (
|
|
221
|
+
self.length is not None
|
|
222
|
+
and self.length.is_evaluated()
|
|
223
|
+
and self.length.is_constant()
|
|
224
|
+
)
|
|
225
|
+
|
|
195
226
|
@property
|
|
196
227
|
def length_value(self) -> int:
|
|
197
228
|
if not self.has_length:
|
|
@@ -217,6 +248,10 @@ class QuantumBitvector(QuantumType):
|
|
|
217
248
|
length = [self.length.expr] if self.length is not None else []
|
|
218
249
|
return f"QArray[{', '.join(element_type + length)}]"
|
|
219
250
|
|
|
251
|
+
@property
|
|
252
|
+
def raw_qmod_type_name(self) -> str:
|
|
253
|
+
return "QArray"
|
|
254
|
+
|
|
220
255
|
@property
|
|
221
256
|
def type_name(self) -> str:
|
|
222
257
|
return "Quantum array"
|
|
@@ -233,6 +268,15 @@ class QuantumBitvector(QuantumType):
|
|
|
233
268
|
and self.element_type.is_evaluated
|
|
234
269
|
)
|
|
235
270
|
|
|
271
|
+
@property
|
|
272
|
+
def is_constant(self) -> bool:
|
|
273
|
+
return (
|
|
274
|
+
self.length is not None
|
|
275
|
+
and self.length.is_evaluated()
|
|
276
|
+
and self.length.is_constant()
|
|
277
|
+
and self.element_type.is_constant
|
|
278
|
+
)
|
|
279
|
+
|
|
236
280
|
@property
|
|
237
281
|
def expressions(self) -> list[Expression]:
|
|
238
282
|
exprs = self.element_type.expressions
|
|
@@ -240,6 +284,14 @@ class QuantumBitvector(QuantumType):
|
|
|
240
284
|
exprs.append(self.length)
|
|
241
285
|
return exprs
|
|
242
286
|
|
|
287
|
+
@property
|
|
288
|
+
def minimal_size_in_bits(self) -> int:
|
|
289
|
+
if self.has_constant_length:
|
|
290
|
+
length = self.length_value
|
|
291
|
+
else:
|
|
292
|
+
length = 1
|
|
293
|
+
return length * self.element_type.minimal_size_in_bits
|
|
294
|
+
|
|
243
295
|
|
|
244
296
|
class QuantumNumeric(QuantumScalar):
|
|
245
297
|
kind: Literal["qnum"]
|
|
@@ -279,6 +331,14 @@ class QuantumNumeric(QuantumScalar):
|
|
|
279
331
|
def has_sign(self) -> bool:
|
|
280
332
|
return self.is_signed is not None
|
|
281
333
|
|
|
334
|
+
@property
|
|
335
|
+
def has_constant_sign(self) -> bool:
|
|
336
|
+
return (
|
|
337
|
+
self.is_signed is not None
|
|
338
|
+
and self.is_signed.is_evaluated()
|
|
339
|
+
and self.is_signed.is_constant()
|
|
340
|
+
)
|
|
341
|
+
|
|
282
342
|
@property
|
|
283
343
|
def sign_value(self) -> bool:
|
|
284
344
|
return False if self.is_signed is None else self.is_signed.to_bool_value()
|
|
@@ -287,6 +347,14 @@ class QuantumNumeric(QuantumScalar):
|
|
|
287
347
|
def has_fraction_digits(self) -> bool:
|
|
288
348
|
return self.fraction_digits is not None
|
|
289
349
|
|
|
350
|
+
@property
|
|
351
|
+
def has_constant_fraction_digits(self) -> bool:
|
|
352
|
+
return (
|
|
353
|
+
self.fraction_digits is not None
|
|
354
|
+
and self.fraction_digits.is_evaluated()
|
|
355
|
+
and self.fraction_digits.is_constant()
|
|
356
|
+
)
|
|
357
|
+
|
|
290
358
|
@property
|
|
291
359
|
def fraction_digits_value(self) -> int:
|
|
292
360
|
return (
|
|
@@ -294,7 +362,11 @@ class QuantumNumeric(QuantumScalar):
|
|
|
294
362
|
)
|
|
295
363
|
|
|
296
364
|
def _update_size_in_bits_from_declaration(self) -> None:
|
|
297
|
-
if
|
|
365
|
+
if (
|
|
366
|
+
self.size is not None
|
|
367
|
+
and self.size.is_evaluated()
|
|
368
|
+
and self.size.is_constant()
|
|
369
|
+
):
|
|
298
370
|
self._size_in_bits = self.size.to_int_value()
|
|
299
371
|
|
|
300
372
|
def get_proxy(self, handle: "HandleBinding") -> QmodQNumProxy:
|
|
@@ -307,6 +379,21 @@ class QuantumNumeric(QuantumScalar):
|
|
|
307
379
|
|
|
308
380
|
@property
|
|
309
381
|
def qmod_type_name(self) -> str:
|
|
382
|
+
if (
|
|
383
|
+
self.size is not None
|
|
384
|
+
and (
|
|
385
|
+
self.is_signed is None
|
|
386
|
+
or (self.is_signed.is_evaluated() and not self.is_signed.value.value)
|
|
387
|
+
)
|
|
388
|
+
and (
|
|
389
|
+
self.fraction_digits is None
|
|
390
|
+
or (
|
|
391
|
+
self.fraction_digits.is_evaluated()
|
|
392
|
+
and self.fraction_digits.value.value == 0
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
):
|
|
396
|
+
return f"QNum[{self.size.expr}]"
|
|
310
397
|
if (
|
|
311
398
|
self.size is not None
|
|
312
399
|
and self.is_signed is not None
|
|
@@ -315,6 +402,10 @@ class QuantumNumeric(QuantumScalar):
|
|
|
315
402
|
return f"QNum[{self.size.expr}, {self.is_signed.expr}, {self.fraction_digits.expr}]"
|
|
316
403
|
return "QNum"
|
|
317
404
|
|
|
405
|
+
@property
|
|
406
|
+
def raw_qmod_type_name(self) -> str:
|
|
407
|
+
return "QNum"
|
|
408
|
+
|
|
318
409
|
@property
|
|
319
410
|
def type_name(self) -> str:
|
|
320
411
|
return "Quantum numeric"
|
|
@@ -333,6 +424,26 @@ class QuantumNumeric(QuantumScalar):
|
|
|
333
424
|
self.fraction_digits is not None and not self.fraction_digits.is_evaluated()
|
|
334
425
|
)
|
|
335
426
|
|
|
427
|
+
@property
|
|
428
|
+
def is_constant(self) -> bool:
|
|
429
|
+
if (
|
|
430
|
+
self.size is None
|
|
431
|
+
or not self.size.is_evaluated()
|
|
432
|
+
or not self.size.is_constant()
|
|
433
|
+
):
|
|
434
|
+
return False
|
|
435
|
+
if self.is_signed is not None and (
|
|
436
|
+
not self.is_signed.is_evaluated() or not self.is_signed.is_constant()
|
|
437
|
+
):
|
|
438
|
+
return False
|
|
439
|
+
return not (
|
|
440
|
+
self.fraction_digits is not None
|
|
441
|
+
and (
|
|
442
|
+
not self.fraction_digits.is_evaluated()
|
|
443
|
+
or not self.fraction_digits.is_constant()
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
|
|
336
447
|
@property
|
|
337
448
|
def expressions(self) -> list[Expression]:
|
|
338
449
|
exprs = []
|
|
@@ -372,6 +483,10 @@ class QuantumNumeric(QuantumScalar):
|
|
|
372
483
|
number_utils.limit_fraction_places(bounds[1], machine_precision),
|
|
373
484
|
)
|
|
374
485
|
|
|
486
|
+
@property
|
|
487
|
+
def minimal_size_in_bits(self) -> int:
|
|
488
|
+
return self.size_in_bits if self.has_size_in_bits else 1
|
|
489
|
+
|
|
375
490
|
|
|
376
491
|
class RegisterQuantumType(BaseModel):
|
|
377
492
|
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):
|
|
@@ -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
|