classiq 0.46.1__py3-none-any.whl → 0.48.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/_internals/api_wrapper.py +45 -8
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -7
- classiq/applications/grover/grover_model_constructor.py +2 -1
- classiq/execution/execution_session.py +133 -45
- classiq/execution/jobs.py +120 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +0 -1
- classiq/interface/debug_info/debug_info.py +23 -1
- classiq/interface/execution/primitives.py +17 -0
- classiq/interface/executor/iqae_result.py +3 -3
- classiq/interface/executor/result.py +3 -1
- classiq/interface/generator/arith/arithmetic_operations.py +5 -2
- classiq/interface/generator/arith/binary_ops.py +21 -14
- classiq/interface/generator/arith/extremum_operations.py +9 -1
- classiq/interface/generator/arith/number_utils.py +6 -0
- classiq/interface/generator/arith/register_user_input.py +30 -21
- classiq/interface/generator/arith/unary_ops.py +13 -1
- classiq/interface/generator/expressions/expression.py +8 -0
- classiq/interface/generator/functions/type_name.py +1 -3
- classiq/interface/generator/generated_circuit_data.py +47 -2
- classiq/interface/generator/quantum_program.py +10 -2
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +17 -3
- classiq/interface/ide/visual_model.py +10 -5
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/bind_operation.py +0 -3
- classiq/interface/model/phase_operation.py +11 -0
- classiq/interface/model/port_declaration.py +1 -12
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +34 -6
- classiq/interface/model/quantum_lambda_function.py +4 -1
- classiq/interface/model/quantum_statement.py +16 -1
- classiq/interface/model/quantum_variable_declaration.py +0 -22
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/server/global_versions.py +4 -4
- classiq/interface/server/routes.py +0 -3
- classiq/model_expansions/capturing/propagated_var_stack.py +5 -2
- classiq/model_expansions/closure.py +7 -2
- classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
- classiq/model_expansions/generative_functions.py +146 -28
- classiq/model_expansions/interpreter.py +17 -5
- classiq/model_expansions/quantum_operations/classicalif.py +27 -10
- classiq/model_expansions/quantum_operations/control.py +22 -15
- classiq/model_expansions/quantum_operations/emitter.py +68 -7
- classiq/model_expansions/quantum_operations/expression_operation.py +25 -16
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +167 -95
- classiq/model_expansions/quantum_operations/invert.py +12 -6
- classiq/model_expansions/quantum_operations/phase.py +189 -0
- classiq/model_expansions/quantum_operations/power.py +9 -8
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +20 -5
- classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
- classiq/model_expansions/quantum_operations/repeat.py +32 -13
- classiq/model_expansions/quantum_operations/within_apply.py +19 -6
- classiq/model_expansions/scope.py +16 -5
- classiq/model_expansions/scope_initialization.py +11 -1
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +23 -1
- classiq/model_expansions/visitors/variable_references.py +11 -7
- classiq/qmod/builtins/__init__.py +10 -0
- classiq/qmod/builtins/constants.py +10 -0
- classiq/qmod/builtins/functions/state_preparation.py +4 -1
- classiq/qmod/builtins/operations.py +55 -161
- classiq/qmod/create_model_function.py +1 -1
- classiq/qmod/generative.py +14 -5
- classiq/qmod/native/pretty_printer.py +14 -4
- classiq/qmod/pretty_print/pretty_printer.py +14 -4
- classiq/qmod/qmod_constant.py +28 -18
- classiq/qmod/qmod_variable.py +43 -23
- classiq/qmod/quantum_expandable.py +14 -1
- classiq/qmod/semantics/static_semantics_visitor.py +10 -0
- classiq/qmod/semantics/validation/constants_validation.py +16 -0
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/METADATA +9 -4
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/RECORD +71 -66
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Dict, List, Tuple
|
2
|
+
|
3
|
+
import sympy
|
4
|
+
from sympy import sympify
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
7
|
+
from classiq.interface.generator.expressions.expression import Expression
|
8
|
+
from classiq.interface.model.bind_operation import BindOperation
|
9
|
+
from classiq.interface.model.handle_binding import HANDLE_ID_SEPARATOR, HandleBinding
|
10
|
+
from classiq.interface.model.phase_operation import PhaseOperation
|
11
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
12
|
+
from classiq.interface.model.quantum_type import (
|
13
|
+
QuantumBitvector,
|
14
|
+
QuantumNumeric,
|
15
|
+
QuantumScalar,
|
16
|
+
)
|
17
|
+
from classiq.interface.model.variable_declaration_statement import (
|
18
|
+
VariableDeclarationStatement,
|
19
|
+
)
|
20
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
21
|
+
|
22
|
+
from classiq.applications.combinatorial_helpers.transformations.ising_converter import (
|
23
|
+
_find_sub_list_items,
|
24
|
+
_get_vars,
|
25
|
+
_refine_ising_expr,
|
26
|
+
_to_ising_symbolic_objective_function,
|
27
|
+
)
|
28
|
+
from classiq.model_expansions.quantum_operations.expression_operation import (
|
29
|
+
ExpressionOperationEmitter,
|
30
|
+
)
|
31
|
+
from classiq.qmod.builtins.functions.exponentiation import suzuki_trotter
|
32
|
+
from classiq.qmod.semantics.error_manager import ErrorManager
|
33
|
+
|
34
|
+
|
35
|
+
class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
|
36
|
+
def _negate_expression(self, expression: Expression, /) -> Expression:
|
37
|
+
return self._evaluate_expression(
|
38
|
+
# TODO: change to model_copy for pydantic V2
|
39
|
+
expression.copy(update=dict(expr=f"-({expression.expr})"))
|
40
|
+
)
|
41
|
+
|
42
|
+
def emit(self, phase_op: PhaseOperation, /) -> None:
|
43
|
+
phase_expression = self._evaluate_op_expression(phase_op)
|
44
|
+
phase_op = phase_op.copy(update=dict(expression=phase_expression))
|
45
|
+
arrays_with_subscript = self._get_symbols_to_split(phase_op.expression)
|
46
|
+
if len(arrays_with_subscript) > 0:
|
47
|
+
self._emit_with_split(phase_op, phase_op.expression, arrays_with_subscript)
|
48
|
+
return
|
49
|
+
# TODO: change to model_copy for pydantic V2
|
50
|
+
phase_op = phase_op.copy(
|
51
|
+
update=dict(expression=self._negate_expression(phase_op.expression))
|
52
|
+
)
|
53
|
+
phase_op = self._evaluate_types_in_expression(phase_op, phase_op.expression)
|
54
|
+
if len(phase_op.var_handles) == 0:
|
55
|
+
ErrorManager().add_error(
|
56
|
+
"Cannot perform phase operation on an expression with no quantum variables."
|
57
|
+
)
|
58
|
+
return
|
59
|
+
|
60
|
+
aux_name = self._counted_name_allocator.allocate("phase_aux")
|
61
|
+
if len(phase_op.var_handles) > 1:
|
62
|
+
split_join = True
|
63
|
+
evolution_variable = HandleBinding(name=aux_name)
|
64
|
+
else:
|
65
|
+
split_join = False
|
66
|
+
evolution_variable = phase_op.var_handles[0]
|
67
|
+
expression = self._evaluate_op_expression(phase_op)
|
68
|
+
expression_evolution_function = QuantumFunctionCall(
|
69
|
+
function=suzuki_trotter.func_decl.name,
|
70
|
+
positional_args=[
|
71
|
+
_convert_cost_expression_to_hamiltonian(
|
72
|
+
expression.expr,
|
73
|
+
{
|
74
|
+
var.name: self._current_scope[var.name].value.quantum_type
|
75
|
+
for var in phase_op.var_handles
|
76
|
+
},
|
77
|
+
),
|
78
|
+
phase_op.theta,
|
79
|
+
Expression(expr="1"),
|
80
|
+
Expression(expr="1"),
|
81
|
+
evolution_variable,
|
82
|
+
],
|
83
|
+
source_ref=phase_op.source_ref,
|
84
|
+
)
|
85
|
+
expression_evolution_function.set_func_decl(suzuki_trotter.func_decl)
|
86
|
+
|
87
|
+
if split_join:
|
88
|
+
self._interpreter.emit_statement(
|
89
|
+
VariableDeclarationStatement(
|
90
|
+
name=aux_name, quantum_type=QuantumBitvector()
|
91
|
+
)
|
92
|
+
)
|
93
|
+
self._interpreter.emit_statement(
|
94
|
+
WithinApply(
|
95
|
+
compute=[
|
96
|
+
BindOperation(
|
97
|
+
in_handles=phase_op.var_handles,
|
98
|
+
out_handles=[HandleBinding(name=aux_name)],
|
99
|
+
)
|
100
|
+
],
|
101
|
+
action=[expression_evolution_function],
|
102
|
+
source_ref=phase_op.source_ref,
|
103
|
+
)
|
104
|
+
)
|
105
|
+
else:
|
106
|
+
self._interpreter.emit_statement(
|
107
|
+
expression_evolution_function,
|
108
|
+
)
|
109
|
+
|
110
|
+
|
111
|
+
def _get_single_bit_vars_expression(
|
112
|
+
expr: sympy.Expr, vars_info: Dict[str, QuantumScalar]
|
113
|
+
) -> Tuple[sympy.Expr, List[sympy.Symbol]]:
|
114
|
+
bit_vars = []
|
115
|
+
for var_name, var_info in vars_info.items():
|
116
|
+
size = var_info.size_in_bits
|
117
|
+
var = sympy.Symbol(var_name)
|
118
|
+
if size == 1:
|
119
|
+
bits = [var]
|
120
|
+
is_signed = False
|
121
|
+
fraction_places = 0
|
122
|
+
else:
|
123
|
+
if TYPE_CHECKING:
|
124
|
+
assert isinstance(var_info, QuantumNumeric)
|
125
|
+
bits = [
|
126
|
+
sympy.Symbol(f"{var_name}{HANDLE_ID_SEPARATOR}{i}__split__")
|
127
|
+
for i in range(size)
|
128
|
+
]
|
129
|
+
is_signed = var_info.sign_value
|
130
|
+
fraction_places = var_info.fraction_digits_value
|
131
|
+
bit_vars.extend(bits)
|
132
|
+
split_var = 0
|
133
|
+
for i, bit in enumerate(bits):
|
134
|
+
if is_signed and i == size - 1: # sign bit (MSB)
|
135
|
+
split_var -= bit * 2 ** (size - 1 - fraction_places)
|
136
|
+
else:
|
137
|
+
split_var += bit * 2 ** (i - fraction_places)
|
138
|
+
expr = expr.subs(var, split_var)
|
139
|
+
return expr, bit_vars
|
140
|
+
|
141
|
+
|
142
|
+
def _convert_ising_sympy_to_pauli_terms(
|
143
|
+
ising_expr: sympy.Expr, ordered_sympy_vars: List[sympy.Symbol]
|
144
|
+
) -> str:
|
145
|
+
pauli_terms: List[str] = []
|
146
|
+
coefficients = ising_expr.as_coefficients_dict(*ordered_sympy_vars)
|
147
|
+
for expr_term in ising_expr.args:
|
148
|
+
expr_vars = _get_vars(expr_term)
|
149
|
+
z_vec = _find_sub_list_items(ordered_sympy_vars, expr_vars)
|
150
|
+
pauli_elements = ["I"] * len(z_vec)
|
151
|
+
for index, is_z_op in enumerate(z_vec):
|
152
|
+
if is_z_op:
|
153
|
+
pauli_elements[len(z_vec) - index - 1] = (
|
154
|
+
"Z" # reminder: Pauli reverses the order!
|
155
|
+
)
|
156
|
+
term_var = sympy.Mul(
|
157
|
+
*(var for i, var in enumerate(ordered_sympy_vars) if z_vec[i])
|
158
|
+
)
|
159
|
+
coeff = float(coefficients[term_var])
|
160
|
+
paulis = [f"Pauli.{pauli}" for pauli in pauli_elements]
|
161
|
+
pauli_terms.append(
|
162
|
+
# fmt: off
|
163
|
+
"struct_literal("
|
164
|
+
"PauliTerm,"
|
165
|
+
f"pauli=[{', '.join(paulis)}],"
|
166
|
+
f"coefficient={Expression(expr=str(coeff))},"
|
167
|
+
")"
|
168
|
+
# fmt: on,
|
169
|
+
)
|
170
|
+
return f"[{', '.join(pauli_terms)}]"
|
171
|
+
|
172
|
+
|
173
|
+
def _convert_cost_expression_to_hamiltonian(
|
174
|
+
expr: str,
|
175
|
+
vars: Dict[str, QuantumScalar],
|
176
|
+
) -> Expression:
|
177
|
+
sympy_expr = sympify(expr)
|
178
|
+
single_bit_vars_expression, single_bit_vars = _get_single_bit_vars_expression(
|
179
|
+
sympy_expr, vars
|
180
|
+
)
|
181
|
+
if not single_bit_vars_expression.is_polynomial():
|
182
|
+
raise ClassiqExpansionError(f"phased expression {expr!r} is not polynomial")
|
183
|
+
|
184
|
+
ising_expr = _to_ising_symbolic_objective_function(single_bit_vars_expression)
|
185
|
+
ising_expr = _refine_ising_expr(ising_expr)
|
186
|
+
|
187
|
+
return Expression(
|
188
|
+
expr=_convert_ising_sympy_to_pauli_terms(ising_expr, single_bit_vars)
|
189
|
+
)
|
@@ -2,7 +2,6 @@ from typing import Union
|
|
2
2
|
|
3
3
|
import sympy
|
4
4
|
|
5
|
-
from classiq.interface.exceptions import ClassiqExpansionError
|
6
5
|
from classiq.interface.generator.expressions.evaluated_expression import (
|
7
6
|
EvaluatedExpression,
|
8
7
|
)
|
@@ -22,6 +21,14 @@ class PowerEmitter(Emitter[Power]):
|
|
22
21
|
_power_expr: Expression
|
23
22
|
|
24
23
|
def emit(self, power: Power, /) -> None:
|
24
|
+
with self._propagated_var_stack.capture_variables(power):
|
25
|
+
self._emit_propagated(power)
|
26
|
+
|
27
|
+
def _emit_propagated(self, power: Power) -> None:
|
28
|
+
if power.is_generative():
|
29
|
+
context = self._register_generative_context(power, POWER_OPERATOR_NAME)
|
30
|
+
power = power.copy(update={"body": context.statements("body")})
|
31
|
+
|
25
32
|
self._power = power
|
26
33
|
self._power_value = self._get_power_value()
|
27
34
|
self._power_expr = Expression(
|
@@ -41,8 +48,7 @@ class PowerEmitter(Emitter[Power]):
|
|
41
48
|
blocks=dict(body=self._power.body),
|
42
49
|
scope=Scope(parent=self._current_scope),
|
43
50
|
)
|
44
|
-
|
45
|
-
context = self._expand_operation(power_operation)
|
51
|
+
context = self._expand_operation(power_operation)
|
46
52
|
self._builder.emit_statement(
|
47
53
|
Power(
|
48
54
|
body=context.statements("body"),
|
@@ -66,9 +72,4 @@ class PowerEmitter(Emitter[Power]):
|
|
66
72
|
|
67
73
|
def _get_power_value(self) -> Union[int, sympy.Basic]:
|
68
74
|
power_value = self._interpreter.evaluate(self._power.power).value
|
69
|
-
if not (isinstance(power_value, int) or power_value.is_symbol):
|
70
|
-
raise ClassiqExpansionError(
|
71
|
-
f"`power`'s argument should be an integer or identifier. Complex "
|
72
|
-
f"expressions are not supported. Got {str(power_value)!r}"
|
73
|
-
)
|
74
75
|
return power_value
|
@@ -11,6 +11,7 @@ from classiq.interface.model.inplace_binary_operation import (
|
|
11
11
|
)
|
12
12
|
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
13
13
|
ArithmeticOperation,
|
14
|
+
ArithmeticOperationKind,
|
14
15
|
)
|
15
16
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
16
17
|
QuantumAssignmentOperation,
|
@@ -62,7 +63,7 @@ class QuantumAssignmentOperationEmitter(
|
|
62
63
|
self, op: ArithmeticOperation, expression: Expression
|
63
64
|
) -> None:
|
64
65
|
op, expression, is_bool_opt = self._optimize_boolean_expression(op, expression)
|
65
|
-
if op.
|
66
|
+
if op.is_inplace:
|
66
67
|
self._emit_inplace_arithmetic_op(op, expression, is_bool_opt)
|
67
68
|
else:
|
68
69
|
self._emit_general_assignment_operation(op)
|
@@ -70,7 +71,9 @@ class QuantumAssignmentOperationEmitter(
|
|
70
71
|
def _emit_inplace_arithmetic_op(
|
71
72
|
self, op: ArithmeticOperation, expression: Expression, is_bool_opt: bool
|
72
73
|
) -> None:
|
73
|
-
if op.
|
74
|
+
if op.get_operation_kind() != ArithmeticOperationKind.InplaceXor or (
|
75
|
+
op.result_type.size_in_bits > 1 or not _is_res_boolean(op)
|
76
|
+
):
|
74
77
|
_validate_naive_inplace_handles(op)
|
75
78
|
self._build_naive_inplace(op, expression)
|
76
79
|
return
|
@@ -100,7 +103,15 @@ class QuantumAssignmentOperationEmitter(
|
|
100
103
|
def _optimize_boolean_expression(
|
101
104
|
self, op: ArithmeticOperation, expression: Expression
|
102
105
|
) -> Tuple[ArithmeticOperation, Expression, bool]:
|
103
|
-
if
|
106
|
+
if (
|
107
|
+
self._interpreter._is_frontend
|
108
|
+
or op.get_operation_kind()
|
109
|
+
not in (
|
110
|
+
ArithmeticOperationKind.Assignment,
|
111
|
+
ArithmeticOperationKind.InplaceXor,
|
112
|
+
)
|
113
|
+
or not _all_vars_boolean(op)
|
114
|
+
):
|
104
115
|
return op, expression, False
|
105
116
|
optimizer = BooleanExpressionOptimizer()
|
106
117
|
optimized_expression = Expression(
|
@@ -130,10 +141,14 @@ class QuantumAssignmentOperationEmitter(
|
|
130
141
|
arith_expression = ArithmeticOperation(
|
131
142
|
result_var=HandleBinding(name=aux_var),
|
132
143
|
expression=new_expression,
|
133
|
-
|
144
|
+
operation_kind=ArithmeticOperationKind.Assignment,
|
134
145
|
)
|
146
|
+
if qe.get_operation_kind() == ArithmeticOperationKind.InplaceXor:
|
147
|
+
op = BinaryOperation.Xor
|
148
|
+
else:
|
149
|
+
op = BinaryOperation.Addition
|
135
150
|
inplace_store = InplaceBinaryOperation(
|
136
|
-
operation=
|
151
|
+
operation=op,
|
137
152
|
target=qe.result_var,
|
138
153
|
value=HandleBinding(name=aux_var),
|
139
154
|
)
|
@@ -11,5 +11,5 @@ class QuantumFunctionCallEmitter(Emitter[QuantumFunctionCall]):
|
|
11
11
|
args = call.positional_args
|
12
12
|
with ErrorManager().call(
|
13
13
|
function.name
|
14
|
-
), self._propagated_var_stack.capture_variables(call):
|
14
|
+
), function.scope.freeze(), self._propagated_var_stack.capture_variables(call):
|
15
15
|
self._emit_quantum_function_call(function, args)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import Type
|
2
|
+
|
1
3
|
from classiq.interface.generator.expressions.expression import Expression
|
2
4
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
3
5
|
REPEAT_OPERATOR_NAME,
|
@@ -8,9 +10,10 @@ from classiq.interface.model.classical_parameter_declaration import (
|
|
8
10
|
)
|
9
11
|
from classiq.interface.model.repeat import Repeat
|
10
12
|
|
11
|
-
from classiq.model_expansions.closure import FunctionClosure
|
13
|
+
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
12
14
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
13
15
|
from classiq.model_expansions.scope import Scope
|
16
|
+
from classiq.qmod.quantum_function import GenerativeQFunc
|
14
17
|
|
15
18
|
|
16
19
|
class RepeatEmitter(Emitter[Repeat]):
|
@@ -18,16 +21,32 @@ class RepeatEmitter(Emitter[Repeat]):
|
|
18
21
|
count = self._interpreter.evaluate(repeat.count).as_type(int)
|
19
22
|
for i in range(count):
|
20
23
|
with self._propagated_var_stack.capture_variables(repeat):
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
self._emit_propagated(repeat, i)
|
25
|
+
|
26
|
+
def _emit_propagated(self, repeat: Repeat, i: int) -> None:
|
27
|
+
closure_constructor: Type[FunctionClosure]
|
28
|
+
extra_args: dict
|
29
|
+
if repeat.is_generative():
|
30
|
+
closure_constructor = GenerativeFunctionClosure
|
31
|
+
extra_args = {
|
32
|
+
"generative_blocks": {
|
33
|
+
"body": GenerativeQFunc(
|
34
|
+
repeat.get_generative_block("body"),
|
35
|
+
),
|
36
|
+
}
|
37
|
+
}
|
38
|
+
else:
|
39
|
+
closure_constructor = FunctionClosure
|
40
|
+
extra_args = {}
|
41
|
+
iteration_function = closure_constructor.create(
|
42
|
+
name=REPEAT_OPERATOR_NAME,
|
43
|
+
positional_arg_declarations=[
|
44
|
+
ClassicalParameterDeclaration(
|
45
|
+
name=repeat.iter_var, classical_type=Integer()
|
33
46
|
)
|
47
|
+
],
|
48
|
+
body=repeat.body,
|
49
|
+
scope=Scope(parent=self._current_scope),
|
50
|
+
**extra_args,
|
51
|
+
)
|
52
|
+
self._emit_quantum_function_call(iteration_function, [Expression(expr=str(i))])
|
@@ -11,6 +11,21 @@ from classiq.model_expansions.scope import Scope
|
|
11
11
|
|
12
12
|
class WithinApplyEmitter(Emitter[WithinApply]):
|
13
13
|
def emit(self, within_apply: WithinApply, /) -> None:
|
14
|
+
with self._propagated_var_stack.capture_variables(within_apply):
|
15
|
+
self._emit_propagated(within_apply)
|
16
|
+
|
17
|
+
def _emit_propagated(self, within_apply: WithinApply) -> None:
|
18
|
+
if within_apply.is_generative():
|
19
|
+
within_apply_context = self._register_generative_context(
|
20
|
+
within_apply, WITHIN_APPLY_NAME, ["within", "apply"]
|
21
|
+
)
|
22
|
+
within_apply = within_apply.copy(
|
23
|
+
update={
|
24
|
+
"compute": within_apply_context.statements("within"),
|
25
|
+
"action": within_apply_context.statements("apply"),
|
26
|
+
}
|
27
|
+
)
|
28
|
+
|
14
29
|
if self._should_wrap(within_apply.compute):
|
15
30
|
self._emit_wrapped(within_apply)
|
16
31
|
return
|
@@ -23,8 +38,7 @@ class WithinApplyEmitter(Emitter[WithinApply]):
|
|
23
38
|
blocks=dict(within=within_apply.compute, apply=within_apply.action),
|
24
39
|
scope=Scope(parent=self._current_scope),
|
25
40
|
)
|
26
|
-
|
27
|
-
context = self._expand_operation(within_apply_operation)
|
41
|
+
context = self._expand_operation(within_apply_operation)
|
28
42
|
self._builder.emit_statement(
|
29
43
|
WithinApply(
|
30
44
|
compute=context.statements("within"),
|
@@ -34,10 +48,9 @@ class WithinApplyEmitter(Emitter[WithinApply]):
|
|
34
48
|
)
|
35
49
|
|
36
50
|
def _emit_wrapped(self, within_apply: WithinApply) -> None:
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
)
|
51
|
+
wrapped_compute = self._create_expanded_wrapping_function(
|
52
|
+
COMPUTE_OPERATOR_NAME, within_apply.compute
|
53
|
+
)
|
41
54
|
wrapped_within_apply = WithinApply(
|
42
55
|
compute=[wrapped_compute],
|
43
56
|
action=within_apply.action,
|
@@ -1,11 +1,10 @@
|
|
1
1
|
import itertools
|
2
2
|
from collections import UserDict
|
3
|
+
from contextlib import contextmanager
|
3
4
|
from dataclasses import dataclass
|
4
5
|
from functools import singledispatch
|
5
6
|
from typing import TYPE_CHECKING, Any, Dict, Iterator, Optional, Type, TypeVar, Union
|
6
7
|
|
7
|
-
from typing_extensions import Self
|
8
|
-
|
9
8
|
from classiq.interface.exceptions import (
|
10
9
|
ClassiqExpansionError,
|
11
10
|
ClassiqInternalExpansionError,
|
@@ -180,13 +179,13 @@ class Scope(EvaluatedUserDict):
|
|
180
179
|
data: Optional[Dict[str, Evaluated]] = None,
|
181
180
|
/,
|
182
181
|
*,
|
183
|
-
parent: Optional[
|
182
|
+
parent: Optional["Scope"] = None,
|
184
183
|
) -> None:
|
185
184
|
super().__init__(data or {})
|
186
|
-
self._parent: Optional[
|
185
|
+
self._parent: Optional["Scope"] = parent
|
187
186
|
|
188
187
|
@property
|
189
|
-
def parent(self) -> Optional[
|
188
|
+
def parent(self) -> Optional["Scope"]:
|
190
189
|
return self._parent
|
191
190
|
|
192
191
|
def __getitem__(self, name: str) -> Evaluated:
|
@@ -224,3 +223,15 @@ class Scope(EvaluatedUserDict):
|
|
224
223
|
(self.data or {}) | (other.data or {}),
|
225
224
|
parent=parent,
|
226
225
|
)
|
226
|
+
|
227
|
+
def _copy(self) -> "Scope":
|
228
|
+
return Scope(
|
229
|
+
self.data, parent=None if self._parent is None else self._parent.copy()
|
230
|
+
)
|
231
|
+
|
232
|
+
@contextmanager
|
233
|
+
def freeze(self) -> Iterator[None]:
|
234
|
+
previous = self._copy()
|
235
|
+
yield
|
236
|
+
self.data = previous.data
|
237
|
+
self._parent = previous._parent
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from typing import List, Sequence
|
2
2
|
|
3
|
+
from classiq.interface.exceptions import ClassiqError
|
3
4
|
from classiq.interface.generator.constant import Constant
|
4
5
|
from classiq.interface.generator.expressions.expression_constants import (
|
5
6
|
CPARAM_EXECUTION_SUFFIX,
|
@@ -22,6 +23,7 @@ from classiq.model_expansions.evaluators.parameter_types import (
|
|
22
23
|
)
|
23
24
|
from classiq.model_expansions.expression_renamer import ExpressionRenamer
|
24
25
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
26
|
+
from classiq.qmod.builtins import BUILTIN_CONSTANTS
|
25
27
|
from classiq.qmod.builtins.functions import (
|
26
28
|
CORE_LIB_DECLS,
|
27
29
|
OPEN_LIB_DECLS,
|
@@ -71,7 +73,7 @@ def _add_generative_functions_to_scope(
|
|
71
73
|
name=function.func_decl.name,
|
72
74
|
positional_arg_declarations=function.func_decl.positional_arg_declarations,
|
73
75
|
scope=Scope(parent=scope),
|
74
|
-
|
76
|
+
generative_blocks={"body": function},
|
75
77
|
)
|
76
78
|
)
|
77
79
|
|
@@ -104,6 +106,14 @@ def _init_builtins_scope(scope: Scope) -> None:
|
|
104
106
|
is_atomic=True,
|
105
107
|
)
|
106
108
|
)
|
109
|
+
for constant in BUILTIN_CONSTANTS:
|
110
|
+
value = constant.value
|
111
|
+
if not value.is_evaluated():
|
112
|
+
raise ClassiqError(
|
113
|
+
f"Unevaluated built-in constants not supported. Offending constant: "
|
114
|
+
f"{constant.name} = {value}"
|
115
|
+
)
|
116
|
+
scope[constant.name] = Evaluated(value=value.value.value)
|
107
117
|
|
108
118
|
|
109
119
|
def add_entry_point_params_to_scope(
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import ast
|
2
|
-
from typing import TYPE_CHECKING, Dict, Type
|
2
|
+
from typing import TYPE_CHECKING, Dict, Type, cast
|
3
3
|
|
4
4
|
from classiq.interface.exceptions import ClassiqExpansionError
|
5
5
|
|
@@ -116,6 +116,9 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
|
|
116
116
|
return self.generic_visit(node)
|
117
117
|
|
118
118
|
def visit_Call(self, node: ast.Call) -> ast.AST:
|
119
|
+
if isinstance(node.func, ast.Name) and node.func.id == "Piecewise":
|
120
|
+
return self._visit_piecewise(node)
|
121
|
+
|
119
122
|
if (
|
120
123
|
not isinstance(node.func, ast.Name)
|
121
124
|
or node.func.id not in self.SPECIAL_FUNCTIONS
|
@@ -128,6 +131,25 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
|
|
128
131
|
keywords=[self.visit(arg) for arg in node.keywords],
|
129
132
|
)
|
130
133
|
|
134
|
+
def _visit_piecewise(self, node: ast.Call) -> ast.AST:
|
135
|
+
# sympy Piecewise expression may include bitwise operations:
|
136
|
+
# Piecewise((0, Eq(x, 0)), (0.5, Eq(x, 1) | Eq(x, 2)), (1, True))
|
137
|
+
# ^
|
138
|
+
# We should avoid converting these to 'BitwiseOr' and such.
|
139
|
+
return ast.Call(
|
140
|
+
func=node.func,
|
141
|
+
args=[
|
142
|
+
ast.Tuple(
|
143
|
+
elts=(
|
144
|
+
self.generic_visit(cast(ast.Tuple, arg).elts[0]),
|
145
|
+
cast(ast.Tuple, arg).elts[1],
|
146
|
+
)
|
147
|
+
)
|
148
|
+
for arg in node.args
|
149
|
+
],
|
150
|
+
keywords=node.keywords,
|
151
|
+
)
|
152
|
+
|
131
153
|
def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
|
132
154
|
if isinstance(node.slice, ast.Slice):
|
133
155
|
if node.slice.lower is not None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import ast
|
2
2
|
from contextlib import contextmanager
|
3
|
-
from typing import Dict, Iterator,
|
3
|
+
from typing import Dict, Iterator, List, Optional, Union
|
4
4
|
|
5
5
|
from classiq.interface.exceptions import (
|
6
6
|
ClassiqExpansionError,
|
@@ -23,10 +23,14 @@ from classiq.interface.model.handle_binding import (
|
|
23
23
|
|
24
24
|
class VarRefCollector(ast.NodeVisitor):
|
25
25
|
def __init__(self, ignore_duplicated_handles: bool = False) -> None:
|
26
|
-
self.
|
26
|
+
self._var_handles: Dict[HandleBinding, bool] = {}
|
27
27
|
self._ignore_duplicated_handles = ignore_duplicated_handles
|
28
28
|
self._is_nested = False
|
29
29
|
|
30
|
+
@property
|
31
|
+
def var_handles(self) -> List[HandleBinding]:
|
32
|
+
return list(self._var_handles)
|
33
|
+
|
30
34
|
def visit(self, node: ast.AST) -> Union[
|
31
35
|
SubscriptHandleBinding,
|
32
36
|
SlicedHandleBinding,
|
@@ -35,8 +39,8 @@ class VarRefCollector(ast.NodeVisitor):
|
|
35
39
|
None,
|
36
40
|
]:
|
37
41
|
res = super().visit(node)
|
38
|
-
if not self._ignore_duplicated_handles and len(self.
|
39
|
-
{handle.name for handle in self.
|
42
|
+
if not self._ignore_duplicated_handles and len(self._var_handles) != len(
|
43
|
+
{handle.name for handle in self._var_handles}
|
40
44
|
):
|
41
45
|
raise ClassiqExpansionError(
|
42
46
|
"Multiple non-identical variable references in an expression are not supported."
|
@@ -71,7 +75,7 @@ class VarRefCollector(ast.NodeVisitor):
|
|
71
75
|
raise ClassiqInternalExpansionError("Unevaluated slice.")
|
72
76
|
|
73
77
|
if not self._is_nested:
|
74
|
-
self.
|
78
|
+
self._var_handles[handle] = True
|
75
79
|
return handle
|
76
80
|
|
77
81
|
def visit_Attribute(self, node: ast.Attribute) -> Optional[FieldHandleBinding]:
|
@@ -84,7 +88,7 @@ class VarRefCollector(ast.NodeVisitor):
|
|
84
88
|
field=node.attr,
|
85
89
|
)
|
86
90
|
if not self._is_nested:
|
87
|
-
self.
|
91
|
+
self._var_handles[handle] = True
|
88
92
|
return handle
|
89
93
|
|
90
94
|
def visit_Name(self, node: ast.Name) -> Optional[HandleBinding]:
|
@@ -94,7 +98,7 @@ class VarRefCollector(ast.NodeVisitor):
|
|
94
98
|
return None
|
95
99
|
handle = HandleBinding(name=node.id)
|
96
100
|
if not self._is_nested:
|
97
|
-
self.
|
101
|
+
self._var_handles[handle] = True
|
98
102
|
return handle
|
99
103
|
|
100
104
|
@contextmanager
|
@@ -6,6 +6,8 @@ from .classical_execution_primitives import (
|
|
6
6
|
)
|
7
7
|
from .classical_functions import * # noqa: F403
|
8
8
|
from .classical_functions import __all__ as _builtin_classical_functions
|
9
|
+
from .constants import * # noqa: F403
|
10
|
+
from .constants import __all__ as _builtin_constants
|
9
11
|
from .enums import * # noqa: F403
|
10
12
|
from .enums import __all__ as _builtin_enums
|
11
13
|
from .functions import * # noqa: F403
|
@@ -18,6 +20,13 @@ from .structs import __all__ as _builtin_structs
|
|
18
20
|
FinanceFunctionInput.update_forward_refs(
|
19
21
|
FinanceFunctionType=FinanceFunctionType # noqa: F405
|
20
22
|
)
|
23
|
+
BUILTIN_CONSTANTS = [
|
24
|
+
constant._get_constant_node()
|
25
|
+
for constant in [
|
26
|
+
SIGNED, # noqa: F405
|
27
|
+
UNSIGNED, # noqa: F405
|
28
|
+
]
|
29
|
+
]
|
21
30
|
|
22
31
|
__all__ = (
|
23
32
|
_builtin_enums
|
@@ -26,4 +35,5 @@ __all__ = (
|
|
26
35
|
+ _builtin_operations
|
27
36
|
+ _builtin_classical_execution_primitives
|
28
37
|
+ _builtin_classical_functions
|
38
|
+
+ _builtin_constants
|
29
39
|
)
|
@@ -340,7 +340,10 @@ def inplace_prepare_int(value: CInt, target: QArray[QBit]) -> None:
|
|
340
340
|
|
341
341
|
|
342
342
|
@qfunc(external=True)
|
343
|
-
def prepare_int(
|
343
|
+
def prepare_int(
|
344
|
+
value: CInt,
|
345
|
+
out: Output[QNum[Literal["floor(log(value, 2)) + 1"], Literal[False], Literal[0]]],
|
346
|
+
) -> None:
|
344
347
|
"""
|
345
348
|
[Qmod Classiq-library function]
|
346
349
|
|