classiq 0.60.1__py3-none-any.whl → 0.61.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/_internals/client.py +6 -1
- classiq/applications/__init__.py +1 -1
- classiq/applications/chemistry/__init__.py +7 -7
- classiq/applications/chemistry/chemistry_model_constructor.py +17 -6
- classiq/applications/combinatorial_optimization/__init__.py +7 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +197 -0
- classiq/applications/finance/finance_model_constructor.py +6 -6
- classiq/applications/grover/grover_model_constructor.py +3 -0
- classiq/applications/libraries/qmci_library.py +1 -10
- classiq/applications/qnn/__init__.py +1 -1
- classiq/applications/qnn/datasets/__init__.py +8 -8
- classiq/applications/qsvm/qsvm.py +1 -1
- classiq/execution/__init__.py +0 -2
- classiq/execution/execution_session.py +6 -0
- classiq/executor.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +12 -12
- classiq/interface/executor/execution_preferences.py +1 -1
- classiq/interface/generator/application_apis/chemistry_declarations.py +1 -1
- classiq/interface/generator/application_apis/finance_declarations.py +2 -2
- classiq/interface/generator/arith/arithmetic.py +16 -1
- classiq/interface/generator/arith/arithmetic_expression_validator.py +4 -3
- classiq/interface/generator/expressions/expression_constants.py +3 -0
- classiq/interface/generator/generated_circuit_data.py +58 -20
- classiq/interface/generator/model/__init__.py +1 -1
- classiq/interface/generator/model/quantum_register.py +3 -3
- classiq/interface/generator/standard_gates/controlled_standard_gates.py +20 -32
- classiq/interface/ide/visual_model.py +1 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/model.py +2 -3
- classiq/interface/model/quantum_function_call.py +4 -7
- classiq/interface/model/quantum_function_declaration.py +7 -0
- classiq/interface/model/quantum_lambda_function.py +10 -1
- classiq/interface/model/quantum_type.py +3 -1
- classiq/model_expansions/atomic_expression_functions_defs.py +3 -1
- classiq/model_expansions/capturing/captured_vars.py +24 -8
- classiq/model_expansions/capturing/mangling_utils.py +23 -15
- classiq/model_expansions/evaluators/arg_type_match.py +7 -7
- classiq/model_expansions/expression_evaluator.py +5 -2
- classiq/model_expansions/function_builder.py +21 -4
- classiq/model_expansions/generative_functions.py +12 -90
- classiq/model_expansions/interpreter.py +58 -11
- classiq/model_expansions/quantum_operations/call_emitter.py +19 -10
- classiq/model_expansions/quantum_operations/classicalif.py +1 -1
- classiq/model_expansions/quantum_operations/control.py +5 -31
- classiq/model_expansions/quantum_operations/emitter.py +27 -14
- classiq/model_expansions/quantum_operations/expression_operation.py +3 -5
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +57 -15
- classiq/model_expansions/quantum_operations/invert.py +1 -6
- classiq/model_expansions/quantum_operations/phase.py +2 -5
- classiq/model_expansions/quantum_operations/power.py +0 -4
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +19 -30
- classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -1
- classiq/model_expansions/quantum_operations/shallow_emitter.py +155 -0
- classiq/model_expansions/quantum_operations/within_apply.py +0 -14
- classiq/model_expansions/scope.py +10 -4
- classiq/model_expansions/scope_initialization.py +0 -11
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +7 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +12 -2
- classiq/model_expansions/transformers/ast_renamer.py +26 -0
- classiq/model_expansions/transformers/var_splitter.py +11 -12
- classiq/model_expansions/visitors/variable_references.py +20 -12
- classiq/qmod/builtins/classical_execution_primitives.py +6 -6
- classiq/qmod/builtins/classical_functions.py +10 -10
- classiq/qmod/builtins/functions/__init__.py +89 -103
- classiq/qmod/builtins/functions/amplitude_estimation.py +1 -1
- classiq/qmod/builtins/functions/arithmetic.py +1 -1
- classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +6 -6
- classiq/qmod/builtins/functions/grover.py +5 -5
- classiq/qmod/builtins/functions/hea.py +1 -1
- classiq/qmod/builtins/functions/linear_pauli_rotation.py +2 -2
- classiq/qmod/builtins/functions/modular_exponentiation.py +8 -8
- classiq/qmod/builtins/functions/operators.py +1 -1
- classiq/qmod/builtins/functions/qaoa_penalty.py +5 -5
- classiq/qmod/builtins/functions/qft_functions.py +2 -2
- classiq/qmod/builtins/functions/qpe.py +9 -12
- classiq/qmod/builtins/functions/qsvt.py +177 -15
- classiq/qmod/builtins/functions/state_preparation.py +9 -9
- classiq/qmod/builtins/functions/swap_test.py +1 -1
- classiq/qmod/builtins/functions/utility_functions.py +2 -2
- classiq/qmod/builtins/functions/variational.py +2 -2
- classiq/qmod/builtins/operations.py +3 -3
- classiq/qmod/builtins/structs.py +9 -9
- classiq/qmod/native/pretty_printer.py +17 -19
- classiq/qmod/pretty_print/pretty_printer.py +9 -6
- classiq/qmod/qmod_variable.py +2 -5
- classiq/qmod/quantum_expandable.py +18 -4
- classiq/qmod/quantum_function.py +19 -6
- classiq/qmod/semantics/static_semantics_visitor.py +34 -16
- classiq/qmod/semantics/validation/func_call_validation.py +9 -5
- classiq/qmod/semantics/validation/function_name_collisions_validation.py +23 -0
- classiq/qmod/symbolic.py +47 -47
- {classiq-0.60.1.dist-info → classiq-0.61.0.dist-info}/METADATA +1 -1
- {classiq-0.60.1.dist-info → classiq-0.61.0.dist-info}/RECORD +97 -94
- classiq/execution/qaoa.py +0 -86
- {classiq-0.60.1.dist-info → classiq-0.61.0.dist-info}/WHEEL +0 -0
@@ -7,7 +7,7 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
7
7
|
)
|
8
8
|
from classiq.interface.model.bind_operation import BindOperation
|
9
9
|
from classiq.interface.model.control import Control
|
10
|
-
from classiq.interface.model.handle_binding import HandleBinding
|
10
|
+
from classiq.interface.model.handle_binding import HandleBinding, SubscriptHandleBinding
|
11
11
|
from classiq.interface.model.inplace_binary_operation import (
|
12
12
|
BinaryOperation,
|
13
13
|
InplaceBinaryOperation,
|
@@ -19,6 +19,7 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
19
19
|
)
|
20
20
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
21
21
|
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
|
22
|
+
from classiq.interface.model.repeat import Repeat
|
22
23
|
from classiq.interface.model.variable_declaration_statement import (
|
23
24
|
VariableDeclarationStatement,
|
24
25
|
)
|
@@ -33,10 +34,11 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
|
|
33
34
|
)
|
34
35
|
from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
|
35
36
|
from classiq.model_expansions.scope import QuantumSymbol, Scope
|
37
|
+
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
36
38
|
from classiq.qmod.builtins.functions import (
|
37
39
|
CX,
|
40
|
+
X,
|
38
41
|
allocate,
|
39
|
-
apply_to_all,
|
40
42
|
integer_xor,
|
41
43
|
modular_add,
|
42
44
|
modular_add_constant,
|
@@ -97,6 +99,7 @@ class InplaceBinaryOperationEmitter(CallEmitter[InplaceBinaryOperation]):
|
|
97
99
|
body = _build_inplace_xor_operation(
|
98
100
|
value_var=value_var,
|
99
101
|
target_var=target_var,
|
102
|
+
name_allocator=self._interpreter._counted_name_allocator,
|
100
103
|
)
|
101
104
|
else:
|
102
105
|
body = _build_inplace_binary_operation(
|
@@ -128,11 +131,10 @@ class InplaceBinaryOperationEmitter(CallEmitter[InplaceBinaryOperation]):
|
|
128
131
|
def _emit_constant_operation(self, op: InplaceBinaryOperation) -> None:
|
129
132
|
if TYPE_CHECKING:
|
130
133
|
assert isinstance(op.value, Expression)
|
131
|
-
value = self._evaluate_expression(op.value)
|
132
134
|
self._interpreter.emit(
|
133
135
|
_internal_inplace_binary_operation_function_call(
|
134
136
|
_binary_function_declaration(op.operation, constant=True),
|
135
|
-
value,
|
137
|
+
op.value,
|
136
138
|
op.target,
|
137
139
|
)
|
138
140
|
)
|
@@ -196,7 +198,9 @@ def _build_inplace_binary_operation(
|
|
196
198
|
|
197
199
|
|
198
200
|
def _build_inplace_xor_operation(
|
199
|
-
value_var: QuantumSymbol,
|
201
|
+
value_var: QuantumSymbol,
|
202
|
+
target_var: QuantumSymbol,
|
203
|
+
name_allocator: CountedNameAllocator,
|
200
204
|
) -> list[QuantumStatement]:
|
201
205
|
if TYPE_CHECKING:
|
202
206
|
assert isinstance(value_var.quantum_type, QuantumNumeric)
|
@@ -214,7 +218,7 @@ def _build_inplace_xor_operation(
|
|
214
218
|
_trim_superfluous_fraction_digits("value", value_var, frac_digits_diff)
|
215
219
|
)
|
216
220
|
target_left_var, value_left_var, sign_var_decls, sign_bind_ops, sign_xor = (
|
217
|
-
_split_and_xor_sign(target_overlap_var, value_overlap_var)
|
221
|
+
_split_and_xor_sign(target_overlap_var, value_overlap_var, name_allocator)
|
218
222
|
)
|
219
223
|
|
220
224
|
action: list[QuantumStatement] = []
|
@@ -425,7 +429,11 @@ def _split_var(
|
|
425
429
|
return left_var, right_var, split_bind
|
426
430
|
|
427
431
|
|
428
|
-
def _split_and_xor_sign(
|
432
|
+
def _split_and_xor_sign(
|
433
|
+
target_var: QuantumSymbol,
|
434
|
+
value_var: QuantumSymbol,
|
435
|
+
name_allocator: CountedNameAllocator,
|
436
|
+
) -> tuple[
|
429
437
|
Optional[QuantumSymbol],
|
430
438
|
Optional[QuantumSymbol],
|
431
439
|
list[VariableDeclarationStatement],
|
@@ -442,7 +450,7 @@ def _split_and_xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) ->
|
|
442
450
|
return target_var, value_var, [], [], []
|
443
451
|
|
444
452
|
if value_var.quantum_type.size_in_bits == 1:
|
445
|
-
return None, None, [], [], [_xor_sign(target_var, value_var)]
|
453
|
+
return None, None, [], [], [_xor_sign(target_var, value_var, name_allocator)]
|
446
454
|
|
447
455
|
value_rest_var, value_sign_var, value_split_bind = _split_var("value", value_var, 1)
|
448
456
|
target_left_var, target_right_var, target_split_bind = _split_var(
|
@@ -456,11 +464,15 @@ def _split_and_xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) ->
|
|
456
464
|
for var in (value_rest_var, value_sign_var, target_left_var, target_right_var)
|
457
465
|
]
|
458
466
|
bind_ops = [value_split_bind, target_split_bind]
|
459
|
-
sign_xor = _xor_sign(target_right_var, value_sign_var)
|
467
|
+
sign_xor = _xor_sign(target_right_var, value_sign_var, name_allocator)
|
460
468
|
return target_left_var, value_rest_var, var_decls, bind_ops, [sign_xor]
|
461
469
|
|
462
470
|
|
463
|
-
def _xor_sign(
|
471
|
+
def _xor_sign(
|
472
|
+
target_var: QuantumSymbol,
|
473
|
+
value_var: QuantumSymbol,
|
474
|
+
name_allocator: CountedNameAllocator,
|
475
|
+
) -> Control:
|
464
476
|
quantum_type = value_var.quantum_type
|
465
477
|
if TYPE_CHECKING:
|
466
478
|
assert isinstance(quantum_type, QuantumNumeric)
|
@@ -470,11 +482,33 @@ def _xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) -> Control:
|
|
470
482
|
):
|
471
483
|
raise ClassiqInternalExpansionError
|
472
484
|
|
473
|
-
|
474
|
-
|
475
|
-
|
485
|
+
aux_var = name_allocator.allocate("inplace_xor_aux")
|
486
|
+
iteration_var = name_allocator.allocate("i")
|
487
|
+
inner_x_call = QuantumFunctionCall(
|
488
|
+
function=X.func_decl.name,
|
489
|
+
positional_args=[
|
490
|
+
SubscriptHandleBinding(
|
491
|
+
base_handle=HandleBinding(name=aux_var),
|
492
|
+
index=Expression(expr=iteration_var),
|
493
|
+
)
|
494
|
+
],
|
495
|
+
)
|
496
|
+
inner_x_call.set_func_decl(X.func_decl)
|
497
|
+
inner_xor = WithinApply(
|
498
|
+
compute=[
|
499
|
+
BindOperation(
|
500
|
+
in_handles=[target_var.handle],
|
501
|
+
out_handles=[HandleBinding(name=aux_var)],
|
502
|
+
),
|
503
|
+
],
|
504
|
+
action=[
|
505
|
+
Repeat(
|
506
|
+
iter_var=iteration_var,
|
507
|
+
count=Expression(expr=f"{target_var.quantum_type.size_in_bits}"),
|
508
|
+
body=[inner_x_call],
|
509
|
+
)
|
510
|
+
],
|
476
511
|
)
|
477
|
-
inner_xor.set_func_decl(apply_to_all.func_decl)
|
478
512
|
|
479
513
|
if quantum_type.sign_value:
|
480
514
|
if quantum_type.fraction_digits_value == 1:
|
@@ -489,5 +523,13 @@ def _xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) -> Control:
|
|
489
523
|
|
490
524
|
return Control(
|
491
525
|
expression=Expression(expr=f"{value_var.handle} == {ctrl_value}"),
|
492
|
-
body=[
|
526
|
+
body=[
|
527
|
+
VariableDeclarationStatement(
|
528
|
+
name=aux_var,
|
529
|
+
quantum_type=QuantumBitvector(
|
530
|
+
length=Expression(expr=f"{target_var.quantum_type.size_in_bits}")
|
531
|
+
),
|
532
|
+
),
|
533
|
+
inner_xor,
|
534
|
+
],
|
493
535
|
)
|
@@ -10,12 +10,7 @@ from classiq.model_expansions.scope import Scope
|
|
10
10
|
|
11
11
|
class InvertEmitter(CallEmitter[Invert]):
|
12
12
|
def emit(self, invert: Invert, /) -> None:
|
13
|
-
|
14
|
-
if is_generative:
|
15
|
-
context = self._register_generative_context(invert, INVERT_OPERATOR_NAME)
|
16
|
-
invert = invert.model_copy(update={"body": context.statements("body")})
|
17
|
-
|
18
|
-
if not is_generative and self._should_wrap(invert.body):
|
13
|
+
if self._should_wrap(invert.body):
|
19
14
|
self._emit_wrapped(invert)
|
20
15
|
return
|
21
16
|
|
@@ -39,8 +39,6 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
|
|
39
39
|
)
|
40
40
|
|
41
41
|
def emit(self, phase_op: PhaseOperation, /) -> None:
|
42
|
-
phase_expression = self._evaluate_op_expression(phase_op)
|
43
|
-
phase_op = phase_op.model_copy(update=dict(expression=phase_expression))
|
44
42
|
arrays_with_subscript = self.split_symbols(
|
45
43
|
phase_op.expression, self._counted_name_allocator.allocate
|
46
44
|
)
|
@@ -51,7 +49,7 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
|
|
51
49
|
phase_op = phase_op.model_copy(
|
52
50
|
update=dict(expression=self._negate_expression(phase_op.expression))
|
53
51
|
)
|
54
|
-
phase_op = self._evaluate_types_in_expression(phase_op
|
52
|
+
phase_op = self._evaluate_types_in_expression(phase_op)
|
55
53
|
if len(phase_op.var_handles) == 0:
|
56
54
|
ErrorManager().add_error(
|
57
55
|
"Cannot perform phase operation on an expression with no quantum variables."
|
@@ -65,12 +63,11 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
|
|
65
63
|
else:
|
66
64
|
split_join = False
|
67
65
|
evolution_variable = phase_op.var_handles[0]
|
68
|
-
expression = self._evaluate_op_expression(phase_op)
|
69
66
|
expression_evolution_function = QuantumFunctionCall(
|
70
67
|
function=suzuki_trotter.func_decl.name,
|
71
68
|
positional_args=[
|
72
69
|
_convert_cost_expression_to_hamiltonian(
|
73
|
-
expression.expr,
|
70
|
+
phase_op.expression.expr,
|
74
71
|
{
|
75
72
|
var.name: self._current_scope[var.name].value.quantum_type
|
76
73
|
for var in phase_op.var_handles
|
@@ -22,10 +22,6 @@ class PowerEmitter(CallEmitter[Power]):
|
|
22
22
|
_power_expr: Expression
|
23
23
|
|
24
24
|
def emit(self, power: Power, /) -> None:
|
25
|
-
if power.is_generative():
|
26
|
-
context = self._register_generative_context(power, POWER_OPERATOR_NAME)
|
27
|
-
power = power.model_copy(update={"body": context.statements("body")})
|
28
|
-
|
29
25
|
self._power = power
|
30
26
|
self._power_value = self._get_power_value()
|
31
27
|
self._power_expr = Expression(
|
@@ -5,6 +5,10 @@ from classiq.interface.exceptions import (
|
|
5
5
|
ClassiqExpansionError,
|
6
6
|
ClassiqInternalExpansionError,
|
7
7
|
)
|
8
|
+
from classiq.interface.generator.arith.arithmetic import is_zero
|
9
|
+
from classiq.interface.generator.arith.arithmetic_expression_validator import (
|
10
|
+
is_constant,
|
11
|
+
)
|
8
12
|
from classiq.interface.generator.compiler_keywords import INPLACE_ARITH_AUX_VAR_PREFIX
|
9
13
|
from classiq.interface.generator.expressions.expression import Expression
|
10
14
|
from classiq.interface.model.handle_binding import HandleBinding
|
@@ -45,18 +49,6 @@ HANDLE_ERROR_MESSAGE = (
|
|
45
49
|
)
|
46
50
|
|
47
51
|
|
48
|
-
def _is_constant(expr: str) -> bool:
|
49
|
-
try:
|
50
|
-
float(expr)
|
51
|
-
return True
|
52
|
-
except ValueError:
|
53
|
-
return False
|
54
|
-
|
55
|
-
|
56
|
-
def _is_zero(expr: str) -> bool:
|
57
|
-
return _is_constant(expr) and float(expr) == 0
|
58
|
-
|
59
|
-
|
60
52
|
def _is_variable(expr: str) -> bool:
|
61
53
|
return re.fullmatch("[a-zA-Z_][a-zA-Z0-9_]*", expr) is not None
|
62
54
|
|
@@ -65,29 +57,26 @@ class QuantumAssignmentOperationEmitter(
|
|
65
57
|
ExpressionOperationEmitter[QuantumAssignmentOperation]
|
66
58
|
):
|
67
59
|
def emit(self, op: QuantumAssignmentOperation, /) -> None:
|
68
|
-
|
69
|
-
if self._skip_assignment(op, new_expression.expr):
|
60
|
+
if self._skip_assignment(op, op.expression.expr):
|
70
61
|
return
|
71
62
|
arrays_with_subscript = self.split_symbols(
|
72
|
-
|
63
|
+
op.expression, self._counted_name_allocator.allocate
|
73
64
|
)
|
74
65
|
if len(arrays_with_subscript) > 0:
|
75
|
-
self._emit_with_split(op,
|
66
|
+
self._emit_with_split(op, op.expression, arrays_with_subscript)
|
76
67
|
return
|
77
68
|
|
78
|
-
self._emit_op(
|
69
|
+
self._emit_op(op)
|
79
70
|
|
80
|
-
def _emit_op(self,
|
81
|
-
op = self._evaluate_types_in_expression(op
|
71
|
+
def _emit_op(self, op: QuantumAssignmentOperation) -> None:
|
72
|
+
op = self._evaluate_types_in_expression(op)
|
82
73
|
if isinstance(op, ArithmeticOperation):
|
83
|
-
self._emit_arithmetic_op(op
|
74
|
+
self._emit_arithmetic_op(op)
|
84
75
|
return
|
85
76
|
self._emit_general_assignment_operation(op)
|
86
77
|
|
87
|
-
def _emit_arithmetic_op(
|
88
|
-
|
89
|
-
) -> None:
|
90
|
-
op, expression, is_bool_opt = self._optimize_boolean_expression(op, expression)
|
78
|
+
def _emit_arithmetic_op(self, op: ArithmeticOperation) -> None:
|
79
|
+
op, expression, is_bool_opt = self._optimize_boolean_expression(op)
|
91
80
|
if op.is_inplace:
|
92
81
|
self._emit_inplace_arithmetic_op(op, expression, is_bool_opt)
|
93
82
|
else:
|
@@ -102,7 +91,7 @@ class QuantumAssignmentOperationEmitter(
|
|
102
91
|
or op.result_type.size_in_bits > 1
|
103
92
|
or not self._is_res_boolean(op)
|
104
93
|
or target.quantum_type.size_in_bits > 1
|
105
|
-
or
|
94
|
+
or is_constant(expression.expr)
|
106
95
|
):
|
107
96
|
_validate_naive_inplace_handles(op)
|
108
97
|
self._build_naive_inplace(op, expression)
|
@@ -131,7 +120,7 @@ class QuantumAssignmentOperationEmitter(
|
|
131
120
|
self.emit_statement(op)
|
132
121
|
|
133
122
|
def _optimize_boolean_expression(
|
134
|
-
self, op: ArithmeticOperation
|
123
|
+
self, op: ArithmeticOperation
|
135
124
|
) -> tuple[ArithmeticOperation, Expression, bool]:
|
136
125
|
if (
|
137
126
|
self._interpreter._is_frontend
|
@@ -143,10 +132,10 @@ class QuantumAssignmentOperationEmitter(
|
|
143
132
|
or not self._all_vars_boolean(op)
|
144
133
|
or not self._is_res_boolean(op)
|
145
134
|
):
|
146
|
-
return op, expression, False
|
135
|
+
return op, op.expression, False
|
147
136
|
optimizer = BooleanExpressionOptimizer()
|
148
137
|
optimized_expression = Expression(
|
149
|
-
expr=ast.unparse(optimizer.visit(ast.parse(expression.expr)))
|
138
|
+
expr=ast.unparse(optimizer.visit(ast.parse(op.expression.expr)))
|
150
139
|
)
|
151
140
|
optimized_expression = self._evaluate_expression(optimized_expression)
|
152
141
|
optimized_op = op.model_copy(update=dict(expression=optimized_expression))
|
@@ -174,7 +163,7 @@ class QuantumAssignmentOperationEmitter(
|
|
174
163
|
f"Unrecognized operation kind {qe.operation_kind!r}"
|
175
164
|
)
|
176
165
|
|
177
|
-
if
|
166
|
+
if is_constant(new_expression.expr):
|
178
167
|
self._interpreter.emit_statement(
|
179
168
|
InplaceBinaryOperation(
|
180
169
|
operation=op,
|
@@ -215,7 +204,7 @@ class QuantumAssignmentOperationEmitter(
|
|
215
204
|
)
|
216
205
|
|
217
206
|
def _skip_assignment(self, op: QuantumAssignmentOperation, expr: str) -> bool:
|
218
|
-
if not isinstance(op, ArithmeticOperation) or not
|
207
|
+
if not isinstance(op, ArithmeticOperation) or not is_zero(expr):
|
219
208
|
return False
|
220
209
|
if op.operation_kind != ArithmeticOperationKind.Assignment:
|
221
210
|
return True
|
@@ -19,4 +19,6 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
19
19
|
function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
|
20
20
|
args = call.positional_args
|
21
21
|
with ErrorManager().call(function.name), function.freeze():
|
22
|
-
self._emit_quantum_function_call(
|
22
|
+
self._emit_quantum_function_call(
|
23
|
+
function, args, self._debug_info.get(call.uuid)
|
24
|
+
)
|
@@ -0,0 +1,155 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import TYPE_CHECKING, Any, Optional, TypeVar
|
3
|
+
|
4
|
+
from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
|
5
|
+
from classiq.interface.generator.expressions.expression import Expression
|
6
|
+
from classiq.interface.generator.functions.port_declaration import (
|
7
|
+
PortDeclarationDirection,
|
8
|
+
)
|
9
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
10
|
+
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
11
|
+
ArithmeticOperation,
|
12
|
+
ArithmeticOperationKind,
|
13
|
+
)
|
14
|
+
from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
|
15
|
+
|
16
|
+
from classiq.model_expansions.closure import Closure
|
17
|
+
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
18
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
19
|
+
from classiq.model_expansions.scope import QuantumSymbol, Scope
|
20
|
+
from classiq.model_expansions.transformers.ast_renamer import rename_variables
|
21
|
+
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
22
|
+
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from classiq.model_expansions.interpreter import Interpreter
|
25
|
+
|
26
|
+
|
27
|
+
QuantumOperationT = TypeVar("QuantumOperationT", bound=QuantumOperation)
|
28
|
+
_BLOCK_RENAMES = {
|
29
|
+
"compute": "within",
|
30
|
+
"action": "apply",
|
31
|
+
}
|
32
|
+
_REVERSE_BLOCK_RENAMES = {rename: name for name, rename in _BLOCK_RENAMES.items()}
|
33
|
+
|
34
|
+
|
35
|
+
class ShallowEmitter(Emitter[QuantumOperation]):
|
36
|
+
def __init__(
|
37
|
+
self,
|
38
|
+
interpreter: "Interpreter",
|
39
|
+
operation_name: str,
|
40
|
+
*,
|
41
|
+
components: Optional[list[str]] = None,
|
42
|
+
) -> None:
|
43
|
+
super().__init__(interpreter)
|
44
|
+
self._operation_name = operation_name
|
45
|
+
self._components: list[str] = components or []
|
46
|
+
|
47
|
+
def emit(self, op: QuantumOperation, /) -> None:
|
48
|
+
expanded_components: dict[str, Any] = {}
|
49
|
+
blocks, expressions, handles = self._split_components(op)
|
50
|
+
|
51
|
+
if len(blocks) > 0:
|
52
|
+
if op.is_generative():
|
53
|
+
expanded_blocks = self.expand_generative_blocks(op)
|
54
|
+
else:
|
55
|
+
expanded_blocks = self.expand_blocks(op, blocks)
|
56
|
+
expanded_components.update(expanded_blocks)
|
57
|
+
|
58
|
+
for expression_name in expressions:
|
59
|
+
expression = getattr(op, expression_name)
|
60
|
+
expression = self._evaluate_expression(expression, preserve_bool_ops=True)
|
61
|
+
self.capture_handles_in_expression(expression)
|
62
|
+
expanded_components[expression_name] = expression
|
63
|
+
|
64
|
+
for handle_name in handles:
|
65
|
+
handle = getattr(op, handle_name)
|
66
|
+
expanded_components[handle_name] = self._interpreter.evaluate(
|
67
|
+
handle
|
68
|
+
).value.handle
|
69
|
+
|
70
|
+
op = op.model_copy(update=expanded_components)
|
71
|
+
if isinstance(op, ArithmeticOperation):
|
72
|
+
self._post_process_assignment(op)
|
73
|
+
self._builder.emit_statement(op)
|
74
|
+
|
75
|
+
def _post_process_assignment(self, op: ArithmeticOperation) -> None:
|
76
|
+
if op.operation_kind == ArithmeticOperationKind.Assignment:
|
77
|
+
direction = PortDeclarationDirection.Output
|
78
|
+
self._update_result_type(op)
|
79
|
+
else:
|
80
|
+
direction = PortDeclarationDirection.Inout
|
81
|
+
self._capture_handle(op.result_var, direction)
|
82
|
+
|
83
|
+
def _split_components(
|
84
|
+
self, op: QuantumOperation
|
85
|
+
) -> tuple[list[str], list[str], list[str]]:
|
86
|
+
blocks = self._filter_components(op, list)
|
87
|
+
expressions = self._filter_components(op, Expression)
|
88
|
+
handles = self._filter_components(op, HandleBinding)
|
89
|
+
return blocks, expressions, handles
|
90
|
+
|
91
|
+
def _filter_components(
|
92
|
+
self, op: QuantumOperation, component_type: type
|
93
|
+
) -> list[str]:
|
94
|
+
return [
|
95
|
+
component
|
96
|
+
for component in self._components
|
97
|
+
if isinstance(getattr(op, component, None), component_type)
|
98
|
+
]
|
99
|
+
|
100
|
+
def expand_blocks(
|
101
|
+
self, op: QuantumOperation, block_names: list[str]
|
102
|
+
) -> dict[str, list[QuantumStatement]]:
|
103
|
+
blocks = {
|
104
|
+
_BLOCK_RENAMES.get(block, block): block_statements
|
105
|
+
for block in block_names
|
106
|
+
if (block_statements := getattr(op, block)) is not None
|
107
|
+
}
|
108
|
+
block_closure = Closure(
|
109
|
+
name=self._operation_name,
|
110
|
+
scope=Scope(parent=self._current_scope),
|
111
|
+
blocks=blocks,
|
112
|
+
)
|
113
|
+
context = self._expand_operation(block_closure)
|
114
|
+
return {
|
115
|
+
block: context.statements(_BLOCK_RENAMES.get(block, block))
|
116
|
+
for block in block_names
|
117
|
+
}
|
118
|
+
|
119
|
+
def expand_generative_blocks(
|
120
|
+
self, op: QuantumOperation
|
121
|
+
) -> dict[str, list[QuantumStatement]]:
|
122
|
+
blocks = [block for block in self._components if op.has_generative_block(block)]
|
123
|
+
context = self._expand_generative_context(op, self._operation_name, blocks)
|
124
|
+
return {
|
125
|
+
_REVERSE_BLOCK_RENAMES.get(block, block): context.statements(block)
|
126
|
+
for block in blocks
|
127
|
+
}
|
128
|
+
|
129
|
+
def _update_result_type(self, op: ArithmeticOperation) -> None:
|
130
|
+
expr = str(self._evaluate_expression(op.expression))
|
131
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
132
|
+
vrc.visit(ast.parse(expr))
|
133
|
+
symbols: list[QuantumSymbol] = [
|
134
|
+
symbol
|
135
|
+
for handle in vrc.var_handles
|
136
|
+
if isinstance(
|
137
|
+
symbol := self._interpreter.evaluate(handle).value, QuantumSymbol
|
138
|
+
)
|
139
|
+
]
|
140
|
+
expr = rename_variables(
|
141
|
+
expr,
|
142
|
+
{str(symbol.handle): symbol.handle.identifier for symbol in symbols}
|
143
|
+
| {symbol.handle.qmod_expr: symbol.handle.identifier for symbol in symbols},
|
144
|
+
)
|
145
|
+
for symbol in symbols:
|
146
|
+
expr = expr.replace(symbol.handle.qmod_expr, symbol.handle.identifier)
|
147
|
+
result_type = compute_arithmetic_result_type(
|
148
|
+
expr,
|
149
|
+
{symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
|
150
|
+
self._machine_precision,
|
151
|
+
)
|
152
|
+
result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
153
|
+
copy_type_information(
|
154
|
+
result_type, result_symbol.quantum_type, str(op.result_var)
|
155
|
+
)
|
@@ -10,20 +10,6 @@ from classiq.model_expansions.scope import Scope
|
|
10
10
|
|
11
11
|
class WithinApplyEmitter(Emitter[WithinApply]):
|
12
12
|
def emit(self, within_apply: WithinApply, /) -> None:
|
13
|
-
if within_apply.is_generative():
|
14
|
-
within_apply_context = self._register_generative_context(
|
15
|
-
within_apply, WITHIN_APPLY_NAME, ["within", "apply"]
|
16
|
-
)
|
17
|
-
within_apply = within_apply.model_copy(
|
18
|
-
update={
|
19
|
-
"compute": within_apply_context.statements("within"),
|
20
|
-
"action": within_apply_context.statements("apply"),
|
21
|
-
}
|
22
|
-
)
|
23
|
-
|
24
|
-
self._emit_as_operation(within_apply)
|
25
|
-
|
26
|
-
def _emit_as_operation(self, within_apply: WithinApply) -> None:
|
27
13
|
within_apply_operation = Closure(
|
28
14
|
name=WITHIN_APPLY_NAME,
|
29
15
|
blocks=dict(within=within_apply.compute, apply=within_apply.action),
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import itertools
|
2
|
+
import re
|
2
3
|
from collections import UserDict
|
3
4
|
from collections.abc import Iterator
|
4
5
|
from contextlib import contextmanager
|
@@ -6,6 +7,8 @@ from dataclasses import dataclass
|
|
6
7
|
from functools import singledispatch
|
7
8
|
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
|
8
9
|
|
10
|
+
from sympy import Symbol
|
11
|
+
|
9
12
|
from classiq.interface.exceptions import (
|
10
13
|
ClassiqExpansionError,
|
11
14
|
ClassiqInternalExpansionError,
|
@@ -14,6 +17,9 @@ from classiq.interface.generator.expressions.evaluated_expression import (
|
|
14
17
|
EvaluatedExpression,
|
15
18
|
)
|
16
19
|
from classiq.interface.generator.expressions.expression import Expression
|
20
|
+
from classiq.interface.generator.expressions.expression_constants import (
|
21
|
+
CPARAM_EXECUTION_SUFFIX_PATTERN,
|
22
|
+
)
|
17
23
|
from classiq.interface.generator.expressions.qmod_qarray_proxy import (
|
18
24
|
ILLEGAL_SLICE_BOUNDS_MSG,
|
19
25
|
)
|
@@ -88,10 +94,8 @@ class QuantumSymbol:
|
|
88
94
|
raise ClassiqExpansionError(
|
89
95
|
f"{self.quantum_type.type_name} is not subscriptable."
|
90
96
|
)
|
91
|
-
if (
|
92
|
-
index
|
93
|
-
or self.quantum_type.has_length
|
94
|
-
and index > self.quantum_type.length_value
|
97
|
+
if index < 0 or (
|
98
|
+
self.quantum_type.has_length and index > self.quantum_type.length_value
|
95
99
|
):
|
96
100
|
raise ClassiqExpansionError(
|
97
101
|
f"Subscript [{index}] is out of bounds for {self.handle}."
|
@@ -194,6 +198,8 @@ class Scope(EvaluatedUserDict):
|
|
194
198
|
return self.data[name]
|
195
199
|
if self._parent is not None:
|
196
200
|
return self._parent[name]
|
201
|
+
if re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, name):
|
202
|
+
return Evaluated(value=Symbol(name))
|
197
203
|
raise ClassiqExpansionError(f"Variable {name!r} is undefined")
|
198
204
|
|
199
205
|
def __contains__(self, item: Any) -> bool:
|
@@ -26,7 +26,6 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
|
26
26
|
from classiq.qmod.builtins import BUILTIN_CONSTANTS
|
27
27
|
from classiq.qmod.builtins.functions import (
|
28
28
|
CORE_LIB_DECLS,
|
29
|
-
OPEN_LIB_DECLS,
|
30
29
|
STD_QMOD_OPERATORS,
|
31
30
|
)
|
32
31
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
@@ -96,16 +95,6 @@ def _init_builtins_scope(scope: Scope) -> None:
|
|
96
95
|
scope=Scope(parent=scope),
|
97
96
|
)
|
98
97
|
)
|
99
|
-
for func_decl in OPEN_LIB_DECLS:
|
100
|
-
if func_decl.name not in scope:
|
101
|
-
scope[func_decl.name] = Evaluated(
|
102
|
-
value=FunctionClosure.create(
|
103
|
-
name=func_decl.name,
|
104
|
-
positional_arg_declarations=func_decl.positional_arg_declarations,
|
105
|
-
scope=Scope(parent=scope),
|
106
|
-
is_atomic=True,
|
107
|
-
)
|
108
|
-
)
|
109
98
|
for constant in BUILTIN_CONSTANTS:
|
110
99
|
value = constant.value
|
111
100
|
if not value.is_evaluated():
|
@@ -170,3 +170,10 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
|
|
170
170
|
args=[self.visit(node.value), self.visit(node.slice)],
|
171
171
|
keywords=[],
|
172
172
|
)
|
173
|
+
|
174
|
+
def visit_Attribute(self, node: ast.Attribute) -> ast.Call:
|
175
|
+
return ast.Call(
|
176
|
+
func=ast.Name("get_field"),
|
177
|
+
args=[node.value, ast.Constant(value=node.attr)],
|
178
|
+
keywords=[],
|
179
|
+
)
|
@@ -109,5 +109,15 @@ class SympyToQuantumExpressionTranslator(PythonCodePrinter):
|
|
109
109
|
return f"(({self._print(expr.args[0])}) ^ ({self._print(expr.args[1])}))"
|
110
110
|
|
111
111
|
|
112
|
-
|
113
|
-
|
112
|
+
class SympyToBoolExpressionTranslator(SympyToQuantumExpressionTranslator):
|
113
|
+
_operators = {
|
114
|
+
**SympyToQuantumExpressionTranslator._operators,
|
115
|
+
**{"not": "not ", "xor": "xor"},
|
116
|
+
}
|
117
|
+
|
118
|
+
|
119
|
+
def translate_sympy_quantum_expression(expr: Basic, preserve_bool_ops: bool) -> str:
|
120
|
+
if preserve_bool_ops:
|
121
|
+
return SympyToBoolExpressionTranslator().doprint(expr)
|
122
|
+
else:
|
123
|
+
return SympyToQuantumExpressionTranslator().doprint(expr)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import ast
|
2
|
+
|
3
|
+
_POSSIBLE_HANDLE_AST_TYPES = (ast.Subscript, ast.Attribute, ast.Name)
|
4
|
+
|
5
|
+
|
6
|
+
class _ASTRenamer(ast.NodeTransformer):
|
7
|
+
def __init__(self, sub: dict[str, str]) -> None:
|
8
|
+
self._sub = sub
|
9
|
+
|
10
|
+
def visit(self, node: ast.AST) -> ast.AST:
|
11
|
+
if isinstance(node, _POSSIBLE_HANDLE_AST_TYPES):
|
12
|
+
node_expr = ast.unparse(node)
|
13
|
+
if node_expr in self._sub:
|
14
|
+
return ast.Name(id=self._sub[node_expr])
|
15
|
+
return super().visit(node)
|
16
|
+
|
17
|
+
def visit_Call(self, node: ast.Call) -> ast.Call:
|
18
|
+
return ast.Call(
|
19
|
+
func=node.func,
|
20
|
+
args=[self.visit(arg) for arg in node.args],
|
21
|
+
keywords=[],
|
22
|
+
)
|
23
|
+
|
24
|
+
|
25
|
+
def rename_variables(expr: str, sub: dict[str, str]) -> str:
|
26
|
+
return ast.unparse(_ASTRenamer(sub).visit(ast.parse(expr)))
|