classiq 0.39.0__py3-none-any.whl → 0.41.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 +5 -2
- classiq/_internals/api_wrapper.py +3 -21
- classiq/applications/chemistry/chemistry_model_constructor.py +87 -101
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +7 -26
- classiq/applications/combinatorial_helpers/optimization_model.py +7 -6
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +33 -55
- classiq/applications/combinatorial_optimization/__init__.py +4 -0
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +29 -26
- classiq/applications/finance/finance_model_constructor.py +23 -26
- classiq/applications/grover/grover_model_constructor.py +37 -38
- classiq/applications/qsvm/qsvm.py +1 -2
- classiq/applications/qsvm/qsvm_model_constructor.py +15 -16
- classiq/execution/__init__.py +4 -0
- classiq/execution/execution_session.py +151 -0
- classiq/execution/qnn.py +80 -0
- classiq/executor.py +2 -109
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +11 -0
- classiq/interface/applications/qsvm.py +0 -8
- classiq/interface/ast_node.py +12 -2
- classiq/interface/backend/backend_preferences.py +30 -6
- classiq/interface/backend/quantum_backend_providers.py +11 -11
- classiq/interface/executor/execution_preferences.py +7 -67
- classiq/interface/executor/execution_result.py +22 -1
- classiq/interface/generator/application_apis/chemistry_declarations.py +2 -4
- classiq/interface/generator/application_apis/finance_declarations.py +1 -1
- classiq/interface/generator/arith/binary_ops.py +88 -25
- classiq/interface/generator/arith/unary_ops.py +28 -19
- classiq/interface/generator/expressions/atomic_expression_functions.py +6 -2
- classiq/interface/generator/expressions/enums/__init__.py +10 -0
- classiq/interface/generator/expressions/enums/classical_enum.py +5 -1
- classiq/interface/generator/expressions/expression.py +9 -2
- classiq/interface/generator/expressions/qmod_qarray_proxy.py +89 -0
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +20 -0
- classiq/interface/generator/expressions/qmod_sized_proxy.py +22 -0
- classiq/interface/generator/expressions/sympy_supported_expressions.py +10 -1
- classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +8 -6
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +10 -4
- classiq/interface/generator/functions/builtins/internal_operators.py +7 -62
- classiq/interface/generator/functions/builtins/open_lib_functions.py +1627 -271
- classiq/interface/generator/functions/classical_type.py +27 -17
- classiq/interface/generator/model/preferences/preferences.py +4 -2
- classiq/interface/generator/synthesis_metadata/synthesis_duration.py +0 -4
- classiq/interface/model/bind_operation.py +3 -1
- classiq/interface/model/call_synthesis_data.py +2 -13
- classiq/interface/model/classical_if.py +3 -1
- classiq/interface/model/classical_parameter_declaration.py +13 -0
- classiq/interface/model/control.py +6 -8
- classiq/interface/model/inplace_binary_operation.py +3 -1
- classiq/interface/model/invert.py +3 -1
- classiq/interface/model/port_declaration.py +8 -1
- classiq/interface/model/power.py +3 -1
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +4 -2
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +3 -1
- classiq/interface/model/quantum_expressions/quantum_expression.py +11 -1
- classiq/interface/model/quantum_function_call.py +4 -10
- classiq/interface/model/quantum_function_declaration.py +26 -4
- classiq/interface/model/quantum_lambda_function.py +1 -20
- classiq/interface/model/quantum_statement.py +9 -2
- classiq/interface/model/quantum_type.py +6 -5
- classiq/interface/model/repeat.py +3 -1
- classiq/interface/model/resolvers/function_call_resolver.py +0 -5
- classiq/interface/model/statement_block.py +19 -16
- classiq/interface/model/validations/handles_validator.py +8 -2
- classiq/interface/model/variable_declaration_statement.py +3 -1
- classiq/interface/model/within_apply_operation.py +3 -1
- classiq/interface/server/routes.py +0 -5
- classiq/qmod/__init__.py +5 -2
- classiq/qmod/builtins/classical_execution_primitives.py +22 -2
- classiq/qmod/builtins/classical_functions.py +30 -35
- classiq/qmod/builtins/functions.py +263 -153
- classiq/qmod/builtins/operations.py +50 -26
- classiq/qmod/builtins/structs.py +50 -48
- classiq/qmod/declaration_inferrer.py +32 -27
- classiq/qmod/native/__init__.py +9 -0
- classiq/qmod/native/expression_to_qmod.py +8 -4
- classiq/qmod/native/pretty_printer.py +11 -18
- classiq/qmod/pretty_print/__init__.py +9 -0
- classiq/qmod/pretty_print/expression_to_python.py +221 -0
- classiq/qmod/pretty_print/pretty_printer.py +421 -0
- classiq/qmod/qmod_constant.py +7 -7
- classiq/qmod/qmod_parameter.py +57 -33
- classiq/qmod/qmod_struct.py +2 -2
- classiq/qmod/qmod_variable.py +40 -29
- classiq/qmod/quantum_callable.py +8 -4
- classiq/qmod/quantum_expandable.py +22 -15
- classiq/qmod/quantum_function.py +15 -4
- classiq/qmod/symbolic.py +73 -68
- classiq/qmod/symbolic_expr.py +1 -1
- classiq/qmod/symbolic_type.py +1 -4
- classiq/qmod/utilities.py +29 -0
- classiq/synthesis.py +15 -16
- {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/METADATA +5 -4
- {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/RECORD +95 -94
- classiq/interface/executor/error_mitigation.py +0 -6
- classiq/interface/generator/functions/builtins/core_library/chemistry_functions.py +0 -0
- classiq/interface/model/common_model_types.py +0 -23
- classiq/interface/model/quantum_expressions/control_state.py +0 -38
- classiq/interface/model/quantum_if_operation.py +0 -94
- {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/WHEEL +0 -0
@@ -154,7 +154,11 @@ class InplacableBinaryOpParams(
|
|
154
154
|
if self.inplace_arg is None:
|
155
155
|
return 0
|
156
156
|
arg = self.left_arg if self.inplace_arg == ArgToInplace.LEFT else self.right_arg
|
157
|
-
return max(
|
157
|
+
return max(
|
158
|
+
0, arg.integer_part_size - self.result_register.integer_part_size # type: ignore[attr-defined]
|
159
|
+
) + max(
|
160
|
+
0, arg.fraction_places - self.result_register.fraction_places # type: ignore[attr-defined]
|
161
|
+
)
|
158
162
|
|
159
163
|
def _carried_arguments(self) -> Tuple[Optional[LeftDataT], Optional[RightDataT]]:
|
160
164
|
if self.inplace_arg == ArgToInplace.RIGHT and isinstance(
|
@@ -259,15 +263,21 @@ class Adder(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
259
263
|
output_name = "sum"
|
260
264
|
|
261
265
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
262
|
-
|
263
|
-
self.
|
266
|
+
left_arg = argument_utils.limit_fraction_places(
|
267
|
+
self.left_arg, machine_precision=self.machine_precision
|
264
268
|
)
|
265
|
-
|
266
|
-
self.right_arg
|
269
|
+
right_arg = argument_utils.limit_fraction_places(
|
270
|
+
self.right_arg, machine_precision=self.machine_precision
|
271
|
+
)
|
272
|
+
lb = argument_utils.lower_bound(left_arg) + argument_utils.lower_bound(
|
273
|
+
right_arg
|
274
|
+
)
|
275
|
+
ub = argument_utils.upper_bound(left_arg) + argument_utils.upper_bound(
|
276
|
+
right_arg
|
267
277
|
)
|
268
278
|
fraction_places = max(
|
269
|
-
argument_utils.fraction_places(
|
270
|
-
argument_utils.fraction_places(
|
279
|
+
argument_utils.fraction_places(left_arg),
|
280
|
+
argument_utils.fraction_places(right_arg),
|
271
281
|
)
|
272
282
|
return RegisterArithmeticInfo(
|
273
283
|
size=self.output_size or self._get_output_size(ub, lb, fraction_places),
|
@@ -291,16 +301,47 @@ class Adder(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
291
301
|
class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
292
302
|
output_name = "difference"
|
293
303
|
|
304
|
+
@staticmethod
|
305
|
+
def _get_effective_arg(
|
306
|
+
arg: RegisterOrConst, machine_precision: int
|
307
|
+
) -> RegisterOrConst:
|
308
|
+
return argument_utils.limit_fraction_places(
|
309
|
+
arg, machine_precision=machine_precision
|
310
|
+
)
|
311
|
+
|
312
|
+
@property
|
313
|
+
def effective_left_arg(self) -> RegisterOrConst:
|
314
|
+
return self._get_effective_arg(self.left_arg, self.machine_precision)
|
315
|
+
|
316
|
+
@property
|
317
|
+
def effective_right_arg(self) -> RegisterOrConst:
|
318
|
+
return self._get_effective_arg(self.right_arg, self.machine_precision)
|
319
|
+
|
320
|
+
@staticmethod
|
321
|
+
def _is_arg_trimmed_register(arg: RegisterOrConst, machine_precision: int) -> bool:
|
322
|
+
return (
|
323
|
+
isinstance(arg, RegisterArithmeticInfo)
|
324
|
+
and arg.fraction_places > machine_precision
|
325
|
+
)
|
326
|
+
|
327
|
+
@property
|
328
|
+
def left_arg_is_trimmed_register(self) -> bool:
|
329
|
+
return self._is_arg_trimmed_register(self.left_arg, self.machine_precision)
|
330
|
+
|
331
|
+
@property
|
332
|
+
def right_arg_is_trimmed_register(self) -> bool:
|
333
|
+
return self._is_arg_trimmed_register(self.right_arg, self.machine_precision)
|
334
|
+
|
294
335
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
295
336
|
bounds = (
|
296
|
-
argument_utils.lower_bound(self.
|
297
|
-
- argument_utils.upper_bound(self.
|
298
|
-
argument_utils.upper_bound(self.
|
299
|
-
- argument_utils.lower_bound(self.
|
337
|
+
argument_utils.lower_bound(self.effective_left_arg)
|
338
|
+
- argument_utils.upper_bound(self.effective_right_arg),
|
339
|
+
argument_utils.upper_bound(self.effective_left_arg)
|
340
|
+
- argument_utils.lower_bound(self.effective_right_arg),
|
300
341
|
)
|
301
342
|
fraction_places = max(
|
302
|
-
argument_utils.fraction_places(self.
|
303
|
-
argument_utils.fraction_places(self.
|
343
|
+
argument_utils.fraction_places(self.effective_left_arg),
|
344
|
+
argument_utils.fraction_places(self.effective_right_arg),
|
304
345
|
)
|
305
346
|
|
306
347
|
return RegisterArithmeticInfo(
|
@@ -313,38 +354,60 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
313
354
|
def _get_output_size(
|
314
355
|
self, bounds: Tuple[float, float], fraction_places: int
|
315
356
|
) -> int:
|
316
|
-
if isinstance(self.right_arg, float) and self.
|
317
|
-
assert isinstance(self.
|
318
|
-
return self.
|
357
|
+
if isinstance(self.right_arg, float) and self.effective_right_arg == 0:
|
358
|
+
assert isinstance(self.effective_left_arg, RegisterArithmeticInfo)
|
359
|
+
return self.effective_left_arg.size
|
319
360
|
integer_part_size = number_utils.bounds_to_integer_part_size(*bounds)
|
320
361
|
size_needed = integer_part_size + fraction_places
|
321
362
|
return size_needed
|
322
363
|
|
323
364
|
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
324
|
-
if
|
365
|
+
if (
|
366
|
+
not self.left_arg_is_trimmed_register
|
367
|
+
and not self.right_arg_is_trimmed_register
|
368
|
+
):
|
369
|
+
return self._untrimmed_garbage_output_size()
|
370
|
+
if not self.is_inplaced():
|
371
|
+
return 0
|
372
|
+
inplace_arg_name = (
|
373
|
+
self.left_arg_name
|
374
|
+
if self.inplace_arg == ArgToInplace.LEFT
|
375
|
+
else self.right_arg_name
|
376
|
+
)
|
377
|
+
return max(
|
378
|
+
0,
|
379
|
+
self._inputs[inplace_arg_name].fraction_places
|
380
|
+
- self.result_register.fraction_places,
|
381
|
+
)
|
382
|
+
|
383
|
+
def _untrimmed_garbage_output_size(self) -> pydantic.NonNegativeInt:
|
384
|
+
if not isinstance(self.effective_right_arg, RegisterArithmeticInfo):
|
325
385
|
adder_params = Adder(
|
326
|
-
left_arg=self.
|
327
|
-
right_arg=-self.
|
386
|
+
left_arg=self.effective_left_arg,
|
387
|
+
right_arg=-self.effective_right_arg,
|
328
388
|
output_size=self.output_size,
|
329
389
|
inplace_arg=self.inplace_arg,
|
330
390
|
)
|
331
391
|
return adder_params.garbage_output_size()
|
332
392
|
|
333
393
|
negation_params = Negation(
|
334
|
-
arg=self.
|
394
|
+
arg=self.effective_right_arg,
|
335
395
|
output_size=self.negation_output_size,
|
336
396
|
inplace=self.should_inplace_negation,
|
337
397
|
)
|
338
398
|
negation_result = negation_params.result_register
|
339
|
-
if self.output_size is None and max(self.
|
399
|
+
if self.output_size is None and max(self.effective_right_arg.bounds) > 0:
|
340
400
|
negation_result = negation_result.copy(
|
341
401
|
update=dict(
|
342
402
|
is_signed=True,
|
343
|
-
bounds=(
|
403
|
+
bounds=(
|
404
|
+
-max(self.effective_right_arg.bounds),
|
405
|
+
-min(self.effective_right_arg.bounds),
|
406
|
+
),
|
344
407
|
)
|
345
408
|
)
|
346
409
|
adder_params = Adder(
|
347
|
-
left_arg=self.
|
410
|
+
left_arg=self.effective_left_arg,
|
348
411
|
right_arg=negation_result,
|
349
412
|
output_size=self.output_size,
|
350
413
|
inplace_arg=self.arg_to_inplace_adder,
|
@@ -359,10 +422,10 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
359
422
|
return self.inplace_arg == ArgToInplace.LEFT
|
360
423
|
|
361
424
|
def _expected_negation_output_size(self) -> int:
|
362
|
-
return argument_utils.fraction_places(self.
|
425
|
+
return argument_utils.fraction_places(self.effective_right_arg) + min(
|
363
426
|
self.result_register.integer_part_size,
|
364
427
|
number_utils.bounds_to_integer_part_size(
|
365
|
-
*(-bound for bound in argument_utils.bounds(self.
|
428
|
+
*(-bound for bound in argument_utils.bounds(self.effective_right_arg))
|
366
429
|
),
|
367
430
|
)
|
368
431
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import TYPE_CHECKING, Iterable, Optional
|
1
|
+
from typing import TYPE_CHECKING, Final, Iterable, Optional
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
@@ -11,7 +11,7 @@ from classiq.interface.generator.function_params import get_zero_input_name
|
|
11
11
|
|
12
12
|
from classiq.exceptions import ClassiqValueError
|
13
13
|
|
14
|
-
UNARY_ARG_NAME: str = "arg"
|
14
|
+
UNARY_ARG_NAME: Final[str] = "arg"
|
15
15
|
|
16
16
|
|
17
17
|
class UnaryOpParams(ArithmeticOperationParams):
|
@@ -19,17 +19,16 @@ class UnaryOpParams(ArithmeticOperationParams):
|
|
19
19
|
inplace: bool = False
|
20
20
|
|
21
21
|
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
22
|
-
return int(self.is_inplaced()) *
|
23
|
-
self.arg.
|
22
|
+
return int(self.is_inplaced()) * (
|
23
|
+
max(self.arg.integer_part_size - self.result_register.integer_part_size, 0)
|
24
|
+
+ max(self.arg.fraction_places - self.result_register.fraction_places, 0)
|
24
25
|
)
|
25
26
|
|
26
27
|
def should_add_zero_inputs(self) -> bool:
|
27
|
-
return not self.is_inplaced() or self.
|
28
|
+
return not self.is_inplaced() or self.zero_input_for_extension() > 0
|
28
29
|
|
29
|
-
def
|
30
|
-
|
31
|
-
) -> bool:
|
32
|
-
return (output_size or self.result_register.size) > self.arg.size
|
30
|
+
def zero_input_for_extension(self) -> pydantic.NonNegativeInt:
|
31
|
+
return max(0, self.result_register.size - self.arg.size)
|
33
32
|
|
34
33
|
def _create_ios(self) -> None:
|
35
34
|
self._inputs = {UNARY_ARG_NAME: self.arg}
|
@@ -41,8 +40,8 @@ class UnaryOpParams(ArithmeticOperationParams):
|
|
41
40
|
zero_input_register = self.result_register
|
42
41
|
self._zero_inputs = {zero_input_name: zero_input_register}
|
43
42
|
return
|
44
|
-
if self.
|
45
|
-
output_extension_size = self.
|
43
|
+
if self.zero_input_for_extension() > 0:
|
44
|
+
output_extension_size = self.zero_input_for_extension()
|
46
45
|
self._create_zero_input_registers({zero_input_name: output_extension_size})
|
47
46
|
if self.garbage_output_size() > 0:
|
48
47
|
self._outputs[self.garbage_output_name] = RegisterArithmeticInfo(
|
@@ -80,23 +79,33 @@ class BitwiseInvert(UnaryOpParams):
|
|
80
79
|
class Negation(UnaryOpParams):
|
81
80
|
output_name = "negated"
|
82
81
|
|
83
|
-
|
84
|
-
|
82
|
+
@staticmethod
|
83
|
+
def _expected_result_size(arg: RegisterArithmeticInfo) -> pydantic.PositiveInt:
|
84
|
+
if arg.size == 1:
|
85
85
|
return 1
|
86
|
-
return
|
87
|
-
*(-bound for bound in
|
86
|
+
return arg.fraction_places + number_utils.bounds_to_integer_part_size(
|
87
|
+
*(-bound for bound in arg.bounds)
|
88
88
|
)
|
89
89
|
|
90
90
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
91
|
-
|
92
|
-
|
91
|
+
eff_arg = self.arg.limit_fraction_places(self.machine_precision)
|
92
|
+
is_signed = max(eff_arg.bounds) > 0 and self._include_sign
|
93
|
+
bounds = (-max(eff_arg.bounds), -min(eff_arg.bounds))
|
93
94
|
return RegisterArithmeticInfo(
|
94
|
-
size=self.output_size or self._expected_result_size(),
|
95
|
-
fraction_places=
|
95
|
+
size=self.output_size or self._expected_result_size(eff_arg),
|
96
|
+
fraction_places=eff_arg.fraction_places,
|
96
97
|
is_signed=is_signed,
|
97
98
|
bounds=bounds if (is_signed or min(bounds) >= 0) else None,
|
98
99
|
)
|
99
100
|
|
101
|
+
def zero_input_for_extension(self) -> pydantic.NonNegativeInt:
|
102
|
+
eff_arg = self.arg.limit_fraction_places(self.machine_precision)
|
103
|
+
if (self.output_size or eff_arg.size) == 1:
|
104
|
+
return 0
|
105
|
+
return (
|
106
|
+
self.output_size or self._expected_result_size(self.arg)
|
107
|
+
) - self.arg.size
|
108
|
+
|
100
109
|
|
101
110
|
class Sign(UnaryOpParams):
|
102
111
|
output_name = "sign"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from classiq.interface.generator.functions.classical_type import CLASSICAL_ATTRIBUTES
|
2
|
+
|
1
3
|
SUPPORTED_BUILTIN_FUNCTIONS = {"len", "sum", "print"}
|
2
4
|
|
3
5
|
SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
|
@@ -9,8 +11,6 @@ SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
|
|
9
11
|
"get_type",
|
10
12
|
"struct_literal",
|
11
13
|
"get_field",
|
12
|
-
"fraction_digits",
|
13
|
-
"is_signed",
|
14
14
|
"molecule_problem_to_hamiltonian",
|
15
15
|
"fock_hamiltonian_problem_to_hamiltonian",
|
16
16
|
"molecule_ground_state_solution_post_process",
|
@@ -20,3 +20,7 @@ SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
|
|
20
20
|
"BitwiseOr",
|
21
21
|
*SUPPORTED_BUILTIN_FUNCTIONS,
|
22
22
|
}
|
23
|
+
|
24
|
+
SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS_QMOD = (
|
25
|
+
SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS - CLASSICAL_ATTRIBUTES
|
26
|
+
)
|
@@ -8,3 +8,13 @@ from .pauli import Pauli
|
|
8
8
|
from .qsvm_feature_map_entanglement import QSVMFeatureMapEntanglement
|
9
9
|
|
10
10
|
BUILTIN_ENUMS = dict(filter(lambda pair: isinstance(pair[1], EnumMeta), vars().items()))
|
11
|
+
|
12
|
+
__all__ = [
|
13
|
+
"Element",
|
14
|
+
"FermionMapping",
|
15
|
+
"FinanceFunctionType",
|
16
|
+
"LadderOperator",
|
17
|
+
"Optimizer",
|
18
|
+
"Pauli",
|
19
|
+
"QSVMFeatureMapEntanglement",
|
20
|
+
]
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import ast
|
2
|
+
import sys
|
2
3
|
from typing import Any, Mapping, Optional, Type
|
3
4
|
|
4
5
|
import pydantic
|
@@ -9,7 +10,7 @@ from classiq.interface.generator.arith.arithmetic_expression_validator import (
|
|
9
10
|
DEFAULT_SUPPORTED_FUNC_NAMES,
|
10
11
|
)
|
11
12
|
from classiq.interface.generator.expressions.atomic_expression_functions import (
|
12
|
-
|
13
|
+
SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS_QMOD,
|
13
14
|
)
|
14
15
|
from classiq.interface.generator.expressions.evaluated_expression import (
|
15
16
|
EvaluatedExpression,
|
@@ -34,13 +35,19 @@ class Expression(HashableASTNode):
|
|
34
35
|
@pydantic.validator("expr")
|
35
36
|
def validate_expression(cls, expr: str) -> str:
|
36
37
|
supported_functions = (
|
37
|
-
|
38
|
+
SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS_QMOD
|
38
39
|
| set(SYMPY_SUPPORTED_EXPRESSIONS)
|
39
40
|
| set(DEFAULT_SUPPORTED_FUNC_NAMES)
|
40
41
|
)
|
41
42
|
validate_expression_str(expr, supported_functions=supported_functions)
|
42
43
|
return expr
|
43
44
|
|
45
|
+
@pydantic.validator("expr")
|
46
|
+
def format_expression(cls, expr: str) -> str:
|
47
|
+
if sys.version_info >= (3, 9):
|
48
|
+
expr = ast.unparse(ast.parse(expr))
|
49
|
+
return expr
|
50
|
+
|
44
51
|
def is_evaluated(self) -> bool:
|
45
52
|
return self._evaluated_expr is not None
|
46
53
|
|
@@ -0,0 +1,89 @@
|
|
1
|
+
from typing import Optional, Tuple, Union
|
2
|
+
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
|
5
|
+
from classiq.interface.model.handle_binding import (
|
6
|
+
HandleBinding,
|
7
|
+
SlicedHandleBinding,
|
8
|
+
SubscriptHandleBinding,
|
9
|
+
)
|
10
|
+
|
11
|
+
from classiq.exceptions import ClassiqValueError
|
12
|
+
|
13
|
+
ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
|
14
|
+
SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
|
15
|
+
QARRAY_ELEMENT_NOT_SUBSCRIPTABLE = "Subscripting an element in QArray is illegal"
|
16
|
+
|
17
|
+
|
18
|
+
class QmodQArrayProxy(QmodSizedProxy):
|
19
|
+
def __init__(
|
20
|
+
self,
|
21
|
+
name: str,
|
22
|
+
size: int,
|
23
|
+
slice_: Optional[Tuple[int, int]] = None,
|
24
|
+
index_: Optional[int] = None,
|
25
|
+
) -> None:
|
26
|
+
super().__init__(size)
|
27
|
+
self._name = name
|
28
|
+
self._slice = slice_
|
29
|
+
self._index = index_
|
30
|
+
|
31
|
+
def __getitem__(self, key: Union[slice, int]) -> "QmodQArrayProxy":
|
32
|
+
if self._index is not None:
|
33
|
+
raise ClassiqValueError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
|
34
|
+
|
35
|
+
new_index: Optional[int] = None
|
36
|
+
|
37
|
+
if isinstance(key, slice):
|
38
|
+
if key.step is not None:
|
39
|
+
raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
|
40
|
+
new_slice = self._get_new_slice(key.start, key.stop)
|
41
|
+
else:
|
42
|
+
new_slice = self._get_new_slice(key, key + 1)
|
43
|
+
new_index = new_slice[0]
|
44
|
+
|
45
|
+
if (self._slice is not None and new_slice[1] > self._slice[1]) or new_slice[
|
46
|
+
1
|
47
|
+
] > self._size:
|
48
|
+
raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
|
49
|
+
|
50
|
+
return QmodQArrayProxy(
|
51
|
+
self._name, self._size, slice_=new_slice, index_=new_index
|
52
|
+
)
|
53
|
+
|
54
|
+
def _get_new_slice(self, start: int, end: int) -> Tuple[int, int]:
|
55
|
+
if self._slice is not None:
|
56
|
+
return self._slice[0] + start, self._slice[0] + end
|
57
|
+
return start, end
|
58
|
+
|
59
|
+
@property
|
60
|
+
def index(self) -> Optional[int]:
|
61
|
+
return self._index
|
62
|
+
|
63
|
+
@property
|
64
|
+
def slice(self) -> Optional[Tuple[int, int]]:
|
65
|
+
return self._slice
|
66
|
+
|
67
|
+
@property
|
68
|
+
def handle(self) -> HandleBinding:
|
69
|
+
if self._index is not None:
|
70
|
+
return SubscriptHandleBinding(
|
71
|
+
name=self._name,
|
72
|
+
index=Expression(expr=str(self._index)),
|
73
|
+
)
|
74
|
+
|
75
|
+
if self._slice is not None:
|
76
|
+
return SlicedHandleBinding(
|
77
|
+
name=self._name,
|
78
|
+
start=Expression(expr=str(self._slice[0])),
|
79
|
+
end=Expression(expr=str(self._slice[1])),
|
80
|
+
)
|
81
|
+
|
82
|
+
return HandleBinding(name=self._name)
|
83
|
+
|
84
|
+
def __len__(self) -> int:
|
85
|
+
if (slice_ := self.slice) is not None:
|
86
|
+
return slice_[1] - slice_[0]
|
87
|
+
elif self.index is not None:
|
88
|
+
return 1
|
89
|
+
return self._size
|
@@ -1,6 +1,9 @@
|
|
1
|
+
from typing import Any, Mapping
|
2
|
+
|
1
3
|
from sympy import Symbol
|
2
4
|
|
3
5
|
from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
|
6
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
4
7
|
|
5
8
|
|
6
9
|
class QmodQScalarProxy(Symbol, QmodSizedProxy):
|
@@ -9,6 +12,11 @@ class QmodQScalarProxy(Symbol, QmodSizedProxy):
|
|
9
12
|
|
10
13
|
def __init__(self, name: str, size: int) -> None:
|
11
14
|
super().__init__(size)
|
15
|
+
self.name = name
|
16
|
+
|
17
|
+
@property
|
18
|
+
def handle(self) -> HandleBinding:
|
19
|
+
return HandleBinding(name=self.name)
|
12
20
|
|
13
21
|
|
14
22
|
class QmodQBitProxy(QmodQScalarProxy):
|
@@ -24,6 +32,10 @@ class QmodQNumProxy(QmodQScalarProxy):
|
|
24
32
|
self._fraction_digits = fraction_digits
|
25
33
|
self._is_signed = is_signed
|
26
34
|
|
35
|
+
@property
|
36
|
+
def size(self) -> int:
|
37
|
+
return self._size
|
38
|
+
|
27
39
|
@property
|
28
40
|
def fraction_digits(self) -> int:
|
29
41
|
return self._fraction_digits
|
@@ -31,3 +43,11 @@ class QmodQNumProxy(QmodQScalarProxy):
|
|
31
43
|
@property
|
32
44
|
def is_signed(self) -> bool:
|
33
45
|
return self._is_signed
|
46
|
+
|
47
|
+
@property
|
48
|
+
def fields(self) -> Mapping[str, Any]:
|
49
|
+
return {
|
50
|
+
"size": self.size,
|
51
|
+
"is_signed": self.is_signed,
|
52
|
+
"fraction_digits": self.fraction_digits,
|
53
|
+
}
|
@@ -1,6 +1,28 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Mapping
|
2
|
+
|
3
|
+
from classiq.exceptions import ClassiqNotImplementedError
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
7
|
+
|
8
|
+
|
1
9
|
class QmodSizedProxy:
|
2
10
|
def __init__(self, size: int) -> None:
|
3
11
|
self._size = size
|
4
12
|
|
5
13
|
def __len__(self) -> int:
|
6
14
|
return self._size
|
15
|
+
|
16
|
+
@property
|
17
|
+
def handle(self) -> "HandleBinding":
|
18
|
+
raise ClassiqNotImplementedError("cannot compute handle")
|
19
|
+
|
20
|
+
@property
|
21
|
+
def len(self) -> int:
|
22
|
+
return self._size
|
23
|
+
|
24
|
+
@property
|
25
|
+
def fields(self) -> Mapping[str, Any]:
|
26
|
+
return {
|
27
|
+
"len": self.len,
|
28
|
+
}
|
@@ -58,7 +58,16 @@ SPECIAL_FUNCTIONS: List[str] = [
|
|
58
58
|
"catalan",
|
59
59
|
]
|
60
60
|
PIECEWISE_FUNCTIONS: List[str] = ["Piecewise", "Heaviside"]
|
61
|
-
|
61
|
+
NUMERIC_CONSTANTS: List[str] = [
|
62
|
+
"pi",
|
63
|
+
"E",
|
64
|
+
"I",
|
65
|
+
"GoldenRatio",
|
66
|
+
"EulerGamma",
|
67
|
+
"Catalan",
|
68
|
+
]
|
69
|
+
CONSTANTS: List[str] = NUMERIC_CONSTANTS + ["true", "false"]
|
70
|
+
|
62
71
|
DATA_TYPES: List[str] = ["Matrix"]
|
63
72
|
LOGIC_OPERATORS: List[str] = [
|
64
73
|
"And",
|
@@ -423,7 +423,7 @@ UNITARY_FUNCTION = QuantumFunctionDeclaration(
|
|
423
423
|
DEFAULT_TARGET_NAME: PortDeclaration(
|
424
424
|
name=DEFAULT_TARGET_NAME,
|
425
425
|
direction=PortDeclarationDirection.Inout,
|
426
|
-
size=Expression(expr="log(
|
426
|
+
size=Expression(expr="log(get_field(elements[0], 'len'), 2)"),
|
427
427
|
)
|
428
428
|
},
|
429
429
|
)
|
@@ -436,7 +436,7 @@ PREPARE_STATE_FUNCTION = QuantumFunctionDeclaration(
|
|
436
436
|
"out": PortDeclaration(
|
437
437
|
name="out",
|
438
438
|
direction=PortDeclarationDirection.Output,
|
439
|
-
size=Expression(expr="log(
|
439
|
+
size=Expression(expr="log(get_field(probabilities, 'len'), 2)"),
|
440
440
|
)
|
441
441
|
},
|
442
442
|
)
|
@@ -448,7 +448,7 @@ PREPARE_AMPLITUDES_FUNCTION = QuantumFunctionDeclaration(
|
|
448
448
|
"out": PortDeclaration(
|
449
449
|
name="out",
|
450
450
|
direction=PortDeclarationDirection.Output,
|
451
|
-
size=Expression(expr="log(
|
451
|
+
size=Expression(expr="log(get_field(amplitudes, 'len'), 2)"),
|
452
452
|
)
|
453
453
|
},
|
454
454
|
)
|
@@ -467,7 +467,9 @@ ADD_FUNCTION = QuantumFunctionDeclaration(
|
|
467
467
|
"result": PortDeclaration(
|
468
468
|
name="result",
|
469
469
|
direction=PortDeclarationDirection.Output,
|
470
|
-
size=Expression(
|
470
|
+
size=Expression(
|
471
|
+
expr="Max(get_field(left, 'len'), get_field(right, 'len')) + 1"
|
472
|
+
),
|
471
473
|
),
|
472
474
|
},
|
473
475
|
)
|
@@ -578,7 +580,7 @@ INPLACE_PREPARE_STATE = QuantumFunctionDeclaration(
|
|
578
580
|
"target": PortDeclaration(
|
579
581
|
name="target",
|
580
582
|
direction=PortDeclarationDirection.Inout,
|
581
|
-
size=Expression(expr="log(
|
583
|
+
size=Expression(expr="log(get_field(probabilities, 'len'), 2)"),
|
582
584
|
)
|
583
585
|
},
|
584
586
|
)
|
@@ -591,7 +593,7 @@ INPLACE_PREPARE_AMPLITUDES = QuantumFunctionDeclaration(
|
|
591
593
|
"target": PortDeclaration(
|
592
594
|
name="target",
|
593
595
|
direction=PortDeclarationDirection.Inout,
|
594
|
-
size=Expression(expr="log(
|
596
|
+
size=Expression(expr="log(get_field(amplitudes, 'len'), 2)"),
|
595
597
|
)
|
596
598
|
},
|
597
599
|
)
|
@@ -26,7 +26,7 @@ SINGLE_PAULI_EXPONENT_FUNCTION = QuantumFunctionDeclaration(
|
|
26
26
|
"qbv": PortDeclaration(
|
27
27
|
name="qbv",
|
28
28
|
direction=PortDeclarationDirection.Inout,
|
29
|
-
size=Expression(expr="
|
29
|
+
size=Expression(expr="get_field(pauli_string, 'len')"),
|
30
30
|
)
|
31
31
|
},
|
32
32
|
)
|
@@ -44,7 +44,9 @@ SUZUKI_TROTTER_FUNCTION = QuantumFunctionDeclaration(
|
|
44
44
|
"qbv": PortDeclaration(
|
45
45
|
name="qbv",
|
46
46
|
direction=PortDeclarationDirection.Inout,
|
47
|
-
size=Expression(
|
47
|
+
size=Expression(
|
48
|
+
expr="get_field(get_field(pauli_operator[0], 'pauli'), 'len')"
|
49
|
+
),
|
48
50
|
)
|
49
51
|
},
|
50
52
|
)
|
@@ -60,7 +62,9 @@ QDRIFT_FUNCTION = QuantumFunctionDeclaration(
|
|
60
62
|
"qbv": PortDeclaration(
|
61
63
|
name="qbv",
|
62
64
|
direction=PortDeclarationDirection.Inout,
|
63
|
-
size=Expression(
|
65
|
+
size=Expression(
|
66
|
+
expr="get_field(get_field(pauli_operator[0], 'pauli'), 'len')"
|
67
|
+
),
|
64
68
|
)
|
65
69
|
},
|
66
70
|
)
|
@@ -76,7 +80,9 @@ EXPONENTIATION_WITH_DEPTH_CONSTRAINT = QuantumFunctionDeclaration(
|
|
76
80
|
"qbv": PortDeclaration(
|
77
81
|
name="qbv",
|
78
82
|
direction=PortDeclarationDirection.Inout,
|
79
|
-
size=Expression(
|
83
|
+
size=Expression(
|
84
|
+
expr="get_field(get_field(pauli_operator[0], 'pauli'), 'len')"
|
85
|
+
),
|
80
86
|
)
|
81
87
|
},
|
82
88
|
)
|