classiq 0.63.1__py3-none-any.whl → 0.65.1__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.
Files changed (77) hide show
  1. classiq/_internals/api_wrapper.py +30 -0
  2. classiq/analyzer/url_utils.py +2 -3
  3. classiq/applications/chemistry/chemistry_model_constructor.py +8 -9
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +4 -6
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +2 -5
  6. classiq/applications/finance/finance_model_constructor.py +7 -12
  7. classiq/applications/grover/grover_model_constructor.py +4 -6
  8. classiq/applications/qsvm/qsvm_model_constructor.py +6 -4
  9. classiq/execution/execution_session.py +14 -13
  10. classiq/interface/_version.py +1 -1
  11. classiq/interface/analyzer/result.py +1 -2
  12. classiq/interface/backend/backend_preferences.py +1 -9
  13. classiq/interface/executor/result.py +6 -3
  14. classiq/interface/generator/expressions/qmod_qarray_proxy.py +11 -13
  15. classiq/interface/generator/functions/type_name.py +6 -0
  16. classiq/interface/helpers/datastructures.py +26 -0
  17. classiq/interface/interface_version.py +1 -1
  18. classiq/interface/model/allocate.py +16 -0
  19. classiq/interface/model/handle_binding.py +11 -3
  20. classiq/interface/model/quantum_type.py +26 -0
  21. classiq/interface/model/statement_block.py +2 -0
  22. classiq/interface/server/routes.py +5 -0
  23. classiq/model_expansions/atomic_expression_functions_defs.py +6 -6
  24. classiq/model_expansions/capturing/captured_vars.py +27 -10
  25. classiq/model_expansions/evaluators/arg_type_match.py +4 -7
  26. classiq/model_expansions/evaluators/quantum_type_utils.py +25 -8
  27. classiq/model_expansions/expression_evaluator.py +8 -2
  28. classiq/model_expansions/function_builder.py +35 -11
  29. classiq/model_expansions/generative_functions.py +6 -4
  30. classiq/model_expansions/interpreters/__init__.py +0 -0
  31. classiq/model_expansions/interpreters/base_interpreter.py +263 -0
  32. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +28 -0
  33. classiq/model_expansions/interpreters/generative_interpreter.py +249 -0
  34. classiq/model_expansions/model_tables.py +1 -92
  35. classiq/model_expansions/quantum_operations/__init__.py +0 -10
  36. classiq/model_expansions/quantum_operations/call_emitter.py +45 -93
  37. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +87 -0
  38. classiq/model_expansions/quantum_operations/emitter.py +7 -2
  39. classiq/model_expansions/quantum_operations/quantum_function_call.py +11 -2
  40. classiq/model_expansions/quantum_operations/shallow_emitter.py +22 -3
  41. classiq/model_expansions/scope.py +15 -15
  42. classiq/model_expansions/scope_initialization.py +11 -5
  43. classiq/open_library/functions/discrete_sine_cosine_transform.py +8 -2
  44. classiq/open_library/functions/grover.py +1 -1
  45. classiq/open_library/functions/modular_exponentiation.py +8 -2
  46. classiq/open_library/functions/state_preparation.py +23 -13
  47. classiq/open_library/functions/swap_test.py +1 -2
  48. classiq/open_library/functions/variational.py +1 -2
  49. classiq/qmod/builtins/__init__.py +1 -1
  50. classiq/qmod/builtins/operations.py +51 -0
  51. classiq/qmod/declaration_inferrer.py +0 -3
  52. classiq/qmod/generative.py +4 -4
  53. classiq/qmod/native/pretty_printer.py +9 -1
  54. classiq/qmod/pretty_print/pretty_printer.py +12 -1
  55. classiq/qmod/qfunc.py +4 -2
  56. classiq/qmod/qmod_variable.py +55 -48
  57. classiq/qmod/quantum_function.py +7 -5
  58. classiq/qmod/semantics/annotation/__init__.py +0 -0
  59. classiq/qmod/semantics/annotation/call_annotation.py +92 -0
  60. classiq/qmod/semantics/lambdas.py +25 -0
  61. classiq/qmod/semantics/static_semantics_visitor.py +8 -46
  62. classiq/qmod/utilities.py +23 -1
  63. {classiq-0.63.1.dist-info → classiq-0.65.1.dist-info}/METADATA +1 -1
  64. {classiq-0.63.1.dist-info → classiq-0.65.1.dist-info}/RECORD +66 -67
  65. classiq/interface/helpers/dotdict.py +0 -18
  66. classiq/model_expansions/interpreter.py +0 -475
  67. classiq/model_expansions/quantum_operations/control.py +0 -290
  68. classiq/model_expansions/quantum_operations/expression_operation.py +0 -103
  69. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +0 -535
  70. classiq/model_expansions/quantum_operations/invert.py +0 -36
  71. classiq/model_expansions/quantum_operations/phase.py +0 -187
  72. classiq/model_expansions/quantum_operations/power.py +0 -71
  73. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +0 -230
  74. classiq/model_expansions/quantum_operations/within_apply.py +0 -25
  75. classiq/qmod/semantics/annotation.py +0 -36
  76. /classiq/qmod/semantics/{qstruct_annotator.py → annotation/qstruct_annotator.py} +0 -0
  77. {classiq-0.63.1.dist-info → classiq-0.65.1.dist-info}/WHEEL +0 -0
@@ -1,290 +0,0 @@
1
- from sympy import Equality
2
- from sympy.logic.boolalg import Boolean
3
- from typing_extensions import TypeGuard
4
-
5
- from classiq.interface.exceptions import (
6
- ClassiqExpansionError,
7
- ClassiqInternalExpansionError,
8
- )
9
- from classiq.interface.generator.compiler_keywords import INPLACE_ARITH_AUX_VAR_PREFIX
10
- from classiq.interface.generator.expressions.expression import Expression
11
- from classiq.interface.generator.expressions.expression_types import ExpressionValue
12
- from classiq.interface.generator.expressions.qmod_qarray_proxy import QmodQArrayProxy
13
- from classiq.interface.generator.expressions.qmod_qscalar_proxy import QmodQNumProxy
14
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
15
- from classiq.interface.generator.functions.builtins.internal_operators import (
16
- CONTROL_OPERATOR_NAME,
17
- )
18
- from classiq.interface.model.bind_operation import BindOperation
19
- from classiq.interface.model.control import Control
20
- from classiq.interface.model.handle_binding import HandleBinding
21
- from classiq.interface.model.quantum_expressions.arithmetic_operation import (
22
- ArithmeticOperation,
23
- ArithmeticOperationKind,
24
- )
25
- from classiq.interface.model.quantum_function_call import QuantumFunctionCall
26
- from classiq.interface.model.quantum_type import (
27
- QuantumBit,
28
- QuantumBitvector,
29
- )
30
- from classiq.interface.model.statement_block import ConcreteQuantumStatement
31
- from classiq.interface.model.variable_declaration_statement import (
32
- VariableDeclarationStatement,
33
- )
34
- from classiq.interface.model.within_apply_operation import WithinApply
35
-
36
- from classiq.model_expansions.capturing.captured_vars import (
37
- validate_args_are_not_propagated,
38
- )
39
- from classiq.model_expansions.capturing.mangling_utils import ARRAY_CAST_SUFFIX
40
- from classiq.model_expansions.closure import Closure
41
- from classiq.model_expansions.evaluators.control import (
42
- resolve_num_condition,
43
- type_name,
44
- )
45
- from classiq.model_expansions.quantum_operations.expression_operation import (
46
- ExpressionOperationEmitter,
47
- )
48
- from classiq.model_expansions.scope import Scope
49
- from classiq.model_expansions.transformers.var_splitter import SymbolParts
50
- from classiq.model_expansions.utils.handles_collector import extract_handles
51
- from classiq.qmod.builtins.functions.standard_gates import X
52
-
53
-
54
- class ControlEmitter(ExpressionOperationEmitter[Control]):
55
- def emit(self, control: Control, /) -> None:
56
- validate_args_are_not_propagated(
57
- extract_handles(control.expression), extract_handles(control.body)
58
- )
59
- condition = control.expression
60
- arrays_with_subscript = self.split_symbols(
61
- condition, self._counted_name_allocator.allocate
62
- )
63
- if len(arrays_with_subscript) > 0:
64
- self._emit_with_split(control, condition, arrays_with_subscript)
65
- return
66
-
67
- control = self._evaluate_types_in_expression(control)
68
- condition_val = control.expression.value.value
69
- if isinstance(condition_val, QmodSizedProxy):
70
- if control.else_block is None:
71
- self._validate_canonical_condition(condition_val)
72
- self._emit_canonical_control(control)
73
- return
74
- else:
75
- self._interpreter.emit_statement(
76
- control.model_copy(
77
- update=dict(
78
- expression=self._uncanonize_condition(condition_val)
79
- )
80
- )
81
- )
82
- return
83
-
84
- self._validate_condition(control)
85
- self._emit_with_boolean(control)
86
-
87
- def _uncanonize_condition(self, condition_val: QmodSizedProxy) -> Expression:
88
- lhs = (
89
- " & ".join(
90
- f"{condition_val.handle}[{idx}]" for idx in range(condition_val.size)
91
- )
92
- if isinstance(condition_val, QmodQArrayProxy)
93
- else condition_val.handle
94
- )
95
- return Expression(expr=f"{lhs} == 1")
96
-
97
- def _emit_canonical_control(self, control: Control) -> None:
98
- # canonical means control(q, body) where q is a single quantum variable
99
- if self._should_wrap_control(control):
100
- self._emit_wrapped(control)
101
- return
102
- self._emit_as_operation(control)
103
-
104
- def _should_wrap_control(self, control: Control) -> bool:
105
- # TODO we can return back to the general case (as in _should_wrap function)
106
- # once we implement the "smart control" pass to blocks:
107
- # Control(q, body) -> WithinApply(
108
- # compute=aux:=QBit(), allocate(1, aux), Control(q, [X(aux)]),
109
- # action=Control(aux, body)
110
- # )
111
- # We also need to be able to nest multiple Control statements to a single one
112
- return len(control.body) > 1
113
-
114
- def _emit_as_operation(self, control: Control) -> None:
115
- control_operation = Closure(
116
- name=CONTROL_OPERATOR_NAME,
117
- blocks=dict(body=control.body),
118
- scope=Scope(parent=self._current_scope),
119
- )
120
- context = self._expand_operation(control_operation)
121
- self._update_control_state(control)
122
- self.emit_statement(
123
- control.model_copy(update=dict(body=context.statements("body")))
124
- )
125
-
126
- def _emit_wrapped(self, control: Control) -> None:
127
- wrapping_function = self._create_expanded_wrapping_function(
128
- CONTROL_OPERATOR_NAME, control.body
129
- )
130
- self._update_control_state(control)
131
- self.emit_statement(control.model_copy(update=dict(body=[wrapping_function])))
132
-
133
- @staticmethod
134
- def _update_control_state(control: Control) -> None:
135
- condition_val = control.expression.value.value
136
- if not isinstance(condition_val, QmodSizedProxy):
137
- raise ClassiqInternalExpansionError("Control is not in canonical form")
138
- control.set_ctrl_size(condition_val.size)
139
-
140
- def _emit_with_boolean(self, control: Control) -> None:
141
- condition_val = control.expression.value.value
142
- if self._is_simple_equality(condition_val):
143
- ctrl, ctrl_state = resolve_num_condition(condition_val)
144
- if control.else_block is None or ctrl.size == 1:
145
- self._emit_with_x_gates(control, ctrl, ctrl_state)
146
- return
147
- self._emit_with_arithmetic(control)
148
-
149
- @staticmethod
150
- def _is_simple_equality(condition_val: ExpressionValue) -> TypeGuard[Equality]:
151
- # Note that we don't support equalities with non-integer values yet
152
- return isinstance(condition_val, Equality) and (
153
- (
154
- condition_val.args[0].is_Atom
155
- and not isinstance(condition_val.args[0], QmodSizedProxy)
156
- and isinstance(condition_val.args[1], QmodSizedProxy)
157
- )
158
- or (
159
- condition_val.args[1].is_Atom
160
- and not isinstance(condition_val.args[1], QmodSizedProxy)
161
- and isinstance(condition_val.args[0], QmodSizedProxy)
162
- )
163
- )
164
-
165
- def _create_canonical_control_op(
166
- self, control: Control, handle_name: str
167
- ) -> list[ConcreteQuantumStatement]:
168
- handle_expr = self._interpreter.evaluate(Expression(expr=handle_name)).emit()
169
- control_then = control.model_copy(
170
- update=dict(expression=handle_expr, else_block=None)
171
- )
172
- if control.else_block is None:
173
- return [control_then]
174
- else_compute_call = QuantumFunctionCall(
175
- function="X", positional_args=[HandleBinding(name=handle_name)]
176
- )
177
- else_compute_call.set_func_decl(X.func_decl)
178
- control_else_inner = control.model_copy(
179
- update=dict(
180
- expression=handle_expr, body=control.else_block, else_block=None
181
- ),
182
- deep=True,
183
- )
184
- control_else = WithinApply(
185
- compute=[else_compute_call],
186
- action=[control_else_inner],
187
- )
188
- return [control_then, control_else]
189
-
190
- def _control_with_x_gates(
191
- self, control: Control, ctrl: QmodSizedProxy, ctrl_state: str
192
- ) -> ConcreteQuantumStatement:
193
- compute_op: list[ConcreteQuantumStatement] = []
194
-
195
- x_gate_value = self._get_x_gate_value(ctrl_state)
196
- if x_gate_value != 0:
197
- compute_op.append(
198
- ArithmeticOperation(
199
- result_var=ctrl.handle,
200
- expression=Expression(expr=str(x_gate_value)),
201
- operation_kind=ArithmeticOperationKind.InplaceXor,
202
- )
203
- )
204
-
205
- if isinstance(ctrl, QmodQNumProxy):
206
- # Canonical control does not accept QNum, so we have to cast it
207
- cast_decl, bind_op = self._get_array_cast_ops(ctrl)
208
- self._interpreter.emit_statement(cast_decl)
209
- compute_op.append(bind_op)
210
- control_ops = self._create_canonical_control_op(
211
- control, str(cast_decl.name)
212
- )
213
- else:
214
- control_ops = self._create_canonical_control_op(control, str(ctrl.handle))
215
-
216
- return WithinApply(compute=compute_op, action=control_ops)
217
-
218
- def _emit_with_x_gates(
219
- self, control: Control, ctrl: QmodSizedProxy, ctrl_state: str
220
- ) -> None:
221
- self._interpreter.emit_statement(
222
- self._control_with_x_gates(control, ctrl, ctrl_state)
223
- )
224
-
225
- @staticmethod
226
- def _get_x_gate_value(ctrl_state: str) -> int:
227
- x_gate_value = 0
228
- for idx, bit in enumerate(ctrl_state):
229
- x_gate_value += int(bit == "0") << idx
230
- return x_gate_value
231
-
232
- def _get_array_cast_ops(
233
- self, ctrl: QmodQNumProxy
234
- ) -> tuple[VariableDeclarationStatement, BindOperation]:
235
- array_cast = self._counted_name_allocator.allocate(
236
- f"{ctrl.handle}{ARRAY_CAST_SUFFIX}"
237
- )
238
- cast_decl = VariableDeclarationStatement(
239
- name=array_cast, quantum_type=QuantumBitvector()
240
- )
241
- bind_op = BindOperation(
242
- in_handles=[ctrl.handle], out_handles=[HandleBinding(name=array_cast)]
243
- )
244
- return cast_decl, bind_op
245
-
246
- def _emit_with_arithmetic(self, control: Control) -> None:
247
- aux_var = self._counted_name_allocator.allocate(INPLACE_ARITH_AUX_VAR_PREFIX)
248
- self._interpreter.emit_statement(
249
- VariableDeclarationStatement(name=aux_var, quantum_type=QuantumBit())
250
- )
251
- arith_expression = ArithmeticOperation(
252
- result_var=HandleBinding(name=aux_var),
253
- expression=control.expression,
254
- operation_kind=ArithmeticOperationKind.Assignment,
255
- )
256
- self._interpreter.emit_statement(
257
- WithinApply(
258
- compute=[arith_expression],
259
- action=self._create_canonical_control_op(control, aux_var),
260
- )
261
- )
262
-
263
- def _validate_condition(self, control: Control) -> None:
264
- condition_value = control.expression.value.value
265
- if not (
266
- isinstance(condition_value, Boolean)
267
- or (self._all_vars_boolean(control) and self._is_res_boolean(control))
268
- ):
269
- raise ClassiqExpansionError(_condition_err_msg(condition_value))
270
-
271
- @staticmethod
272
- def _validate_canonical_condition(condition_val: ExpressionValue) -> None:
273
- if isinstance(condition_val, QmodQNumProxy):
274
- raise ClassiqExpansionError(_condition_err_msg(condition_val))
275
-
276
- def _get_updated_op_split_symbols(
277
- self, op: Control, symbol_parts: SymbolParts
278
- ) -> Control:
279
- new_body = self.rewrite(op.body, symbol_parts)
280
- new_else = None
281
- if op.else_block is not None:
282
- new_else = self.rewrite(op.else_block, symbol_parts)
283
- return op.model_copy(update=dict(body=new_body, else_block=new_else))
284
-
285
-
286
- def _condition_err_msg(condition_val: ExpressionValue) -> str:
287
- return (
288
- f"Control condition {str(condition_val)!r} must be a qubit, an array of "
289
- f"qubits, or a boolean expression, but is {type_name(condition_val)}"
290
- )
@@ -1,103 +0,0 @@
1
- import ast
2
- from typing import Generic, TypeVar, Union
3
-
4
- from classiq.interface.exceptions import ClassiqInternalExpansionError
5
- from classiq.interface.generator.expressions.expression import Expression
6
- from classiq.interface.generator.visitor import NodeType
7
- from classiq.interface.model.control import Control
8
- from classiq.interface.model.quantum_expressions.quantum_expression import (
9
- QuantumAssignmentOperation,
10
- QuantumExpressionOperation,
11
- )
12
- from classiq.interface.model.quantum_type import (
13
- QuantumNumeric,
14
- )
15
- from classiq.interface.model.within_apply_operation import WithinApply
16
-
17
- from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
18
- from classiq.model_expansions.scope import QuantumSymbol
19
- from classiq.model_expansions.transformers.var_splitter import SymbolParts
20
- from classiq.model_expansions.visitors.variable_references import VarRefCollector
21
-
22
- ExpressionOperationT = TypeVar("ExpressionOperationT", bound=QuantumExpressionOperation)
23
- AST_NODE = TypeVar("AST_NODE", bound=NodeType)
24
-
25
-
26
- class ExpressionOperationEmitter(
27
- Generic[ExpressionOperationT], CallEmitter[ExpressionOperationT]
28
- ):
29
- def _emit_with_split(
30
- self,
31
- op: ExpressionOperationT,
32
- expression: Expression,
33
- symbol_parts: SymbolParts,
34
- ) -> None:
35
- for var_decl in self.get_var_decls(symbol_parts):
36
- self._interpreter.emit_statement(var_decl)
37
- bind_ops = self.get_bind_ops(symbol_parts)
38
-
39
- new_expression = self.rewrite(expression, symbol_parts)
40
- if len(self.split_symbols(new_expression, lambda name: name)) > 0:
41
- raise ClassiqInternalExpansionError(
42
- f"Did not replace all handles in expression: {expression.expr!r} -> "
43
- f"{new_expression.expr!r}"
44
- )
45
- new_op = op.model_copy(update=dict(expression=new_expression))
46
- new_op = self._get_updated_op_split_symbols(new_op, symbol_parts)
47
-
48
- self._interpreter.emit_statement(
49
- WithinApply(
50
- compute=bind_ops,
51
- action=[new_op],
52
- source_ref=op.source_ref,
53
- )
54
- )
55
-
56
- def _get_updated_op_split_symbols(
57
- self,
58
- op: ExpressionOperationT,
59
- symbol_mapping: SymbolParts,
60
- ) -> ExpressionOperationT:
61
- return op
62
-
63
- def _evaluate_types_in_expression(
64
- self, op: ExpressionOperationT
65
- ) -> ExpressionOperationT:
66
- new_expression = self._evaluate_expression(op.expression)
67
- op_with_evaluated_types = op.model_copy(update={"expression": new_expression})
68
- vrc = VarRefCollector()
69
- vrc.visit(ast.parse(op_with_evaluated_types.expression.expr))
70
- handles = vrc.var_handles
71
- op_with_evaluated_types.set_var_handles(handles)
72
- op_with_evaluated_types.initialize_var_types(
73
- {
74
- handle.name: self._interpreter.evaluate(handle)
75
- .as_type(QuantumSymbol)
76
- .quantum_type
77
- for handle in handles
78
- },
79
- self._machine_precision,
80
- )
81
- return op_with_evaluated_types
82
-
83
- @staticmethod
84
- def _all_vars_boolean(op: QuantumExpressionOperation) -> bool:
85
- if not all(
86
- var_type.has_size_in_bits and var_type.size_in_bits == 1
87
- for var_type in op.var_types.values()
88
- ):
89
- return False
90
- return not any(
91
- isinstance(var_type, QuantumNumeric)
92
- and (var_type.sign_value or var_type.fraction_digits_value > 0)
93
- for var_type in op.var_types.values()
94
- )
95
-
96
- @staticmethod
97
- def _is_res_boolean(op: Union[QuantumAssignmentOperation, Control]) -> bool:
98
- if not (op.result_type.has_size_in_bits and op.result_type.size_in_bits == 1):
99
- return False
100
- return not (
101
- isinstance(op.result_type, QuantumNumeric)
102
- and (op.result_type.sign_value or op.result_type.fraction_digits_value > 0)
103
- )