classiq 0.84.0__py3-none-any.whl → 0.86.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.
Files changed (87) hide show
  1. classiq/applications/combinatorial_optimization/combinatorial_problem.py +24 -45
  2. classiq/evaluators/classical_expression.py +32 -15
  3. classiq/evaluators/qmod_annotated_expression.py +207 -0
  4. classiq/evaluators/qmod_expression_visitors/__init__.py +0 -0
  5. classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +134 -0
  6. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +232 -0
  7. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +44 -0
  8. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +308 -0
  9. classiq/evaluators/qmod_node_evaluators/__init__.py +0 -0
  10. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +112 -0
  11. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +132 -0
  12. classiq/evaluators/qmod_node_evaluators/bool_op_evaluation.py +70 -0
  13. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +311 -0
  14. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +107 -0
  15. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +67 -0
  16. classiq/evaluators/qmod_node_evaluators/list_evaluation.py +107 -0
  17. classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +25 -0
  18. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +50 -0
  19. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +66 -0
  20. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +225 -0
  21. classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +58 -0
  22. classiq/evaluators/qmod_node_evaluators/utils.py +80 -0
  23. classiq/execution/execution_session.py +53 -6
  24. classiq/interface/_version.py +1 -1
  25. classiq/interface/analyzer/analysis_params.py +1 -1
  26. classiq/interface/analyzer/result.py +1 -1
  27. classiq/interface/debug_info/debug_info.py +0 -4
  28. classiq/interface/executor/quantum_code.py +2 -2
  29. classiq/interface/generator/arith/arithmetic_expression_validator.py +5 -1
  30. classiq/interface/generator/arith/binary_ops.py +43 -51
  31. classiq/interface/generator/arith/number_utils.py +3 -2
  32. classiq/interface/generator/arith/register_user_input.py +15 -0
  33. classiq/interface/generator/arith/unary_ops.py +32 -28
  34. classiq/interface/generator/expressions/atomic_expression_functions.py +5 -0
  35. classiq/interface/generator/expressions/expression_types.py +2 -2
  36. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  37. classiq/interface/generator/functions/builtins/internal_operators.py +2 -0
  38. classiq/interface/generator/functions/classical_function_declaration.py +0 -4
  39. classiq/interface/generator/functions/classical_type.py +0 -32
  40. classiq/interface/generator/functions/concrete_types.py +20 -0
  41. classiq/interface/generator/generated_circuit_data.py +7 -10
  42. classiq/interface/generator/quantum_program.py +6 -1
  43. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +29 -0
  44. classiq/interface/ide/operation_registry.py +45 -0
  45. classiq/interface/ide/visual_model.py +84 -2
  46. classiq/interface/model/bounds.py +12 -2
  47. classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
  48. classiq/interface/model/quantum_type.py +67 -33
  49. classiq/interface/model/variable_declaration_statement.py +33 -6
  50. classiq/model_expansions/arithmetic.py +115 -0
  51. classiq/model_expansions/arithmetic_compute_result_attrs.py +71 -0
  52. classiq/model_expansions/atomic_expression_functions_defs.py +10 -6
  53. classiq/model_expansions/function_builder.py +4 -1
  54. classiq/model_expansions/generative_functions.py +15 -2
  55. classiq/model_expansions/interpreters/base_interpreter.py +7 -0
  56. classiq/model_expansions/interpreters/generative_interpreter.py +18 -1
  57. classiq/model_expansions/quantum_operations/assignment_result_processor.py +63 -21
  58. classiq/model_expansions/quantum_operations/bounds.py +7 -1
  59. classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
  60. classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
  61. classiq/model_expansions/quantum_operations/variable_decleration.py +30 -10
  62. classiq/model_expansions/scope.py +7 -0
  63. classiq/model_expansions/scope_initialization.py +2 -0
  64. classiq/model_expansions/sympy_conversion/sympy_to_python.py +1 -1
  65. classiq/model_expansions/transformers/type_modifier_inference.py +5 -0
  66. classiq/model_expansions/transformers/var_splitter.py +1 -1
  67. classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
  68. classiq/open_library/functions/__init__.py +0 -2
  69. classiq/open_library/functions/qaoa_penalty.py +8 -1
  70. classiq/open_library/functions/state_preparation.py +1 -32
  71. classiq/qmod/__init__.py +2 -0
  72. classiq/qmod/builtins/operations.py +66 -2
  73. classiq/qmod/classical_variable.py +74 -0
  74. classiq/qmod/declaration_inferrer.py +5 -3
  75. classiq/qmod/native/pretty_printer.py +18 -14
  76. classiq/qmod/pretty_print/pretty_printer.py +34 -15
  77. classiq/qmod/qfunc.py +2 -19
  78. classiq/qmod/qmod_variable.py +5 -8
  79. classiq/qmod/quantum_expandable.py +1 -1
  80. classiq/qmod/quantum_function.py +42 -2
  81. classiq/qmod/symbolic_type.py +2 -1
  82. classiq/qmod/write_qmod.py +3 -1
  83. {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/METADATA +1 -1
  84. {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/RECORD +86 -62
  85. classiq/interface/model/quantum_variable_declaration.py +0 -7
  86. /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
  87. {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,71 @@
1
+ from classiq.model_expansions.arithmetic import NumericAttributes
2
+
3
+
4
+ def compute_result_attrs_assign(
5
+ source: NumericAttributes,
6
+ machine_precision: int,
7
+ ) -> NumericAttributes:
8
+ if machine_precision >= source.fraction_digits:
9
+ return source
10
+
11
+ trimmed_digits = source.fraction_digits - machine_precision
12
+ return NumericAttributes(
13
+ size=source.size - trimmed_digits,
14
+ is_signed=source.is_signed,
15
+ fraction_digits=machine_precision,
16
+ bounds=source.bounds,
17
+ trim_bounds=True,
18
+ )
19
+
20
+
21
+ def compute_result_attrs_bitwise_invert(
22
+ arg: NumericAttributes,
23
+ machine_precision: int,
24
+ ) -> NumericAttributes:
25
+ fraction_digits = min(arg.fraction_digits, machine_precision)
26
+ trimmed_bits = arg.fraction_digits - fraction_digits
27
+ return NumericAttributes(
28
+ size=arg.size - trimmed_bits,
29
+ is_signed=arg.is_signed,
30
+ fraction_digits=fraction_digits,
31
+ )
32
+
33
+
34
+ def compute_result_attrs_negate(
35
+ arg: NumericAttributes,
36
+ machine_precision: int,
37
+ ) -> NumericAttributes:
38
+ lb = -arg.ub
39
+ ub = -arg.lb
40
+
41
+ if arg.size == 1:
42
+ return NumericAttributes(
43
+ size=1,
44
+ is_signed=lb < 0,
45
+ fraction_digits=arg.fraction_digits,
46
+ bounds=(lb, ub),
47
+ )
48
+ else:
49
+ return NumericAttributes.from_bounds(
50
+ lb, ub, arg.fraction_digits, machine_precision
51
+ )
52
+
53
+
54
+ def compute_result_attrs_add(
55
+ left: NumericAttributes,
56
+ right: NumericAttributes,
57
+ machine_precision: int,
58
+ ) -> NumericAttributes:
59
+ lb = left.lb + right.lb
60
+ ub = left.ub + right.ub
61
+ fraction_places = max(left.fraction_digits, right.fraction_digits)
62
+ return NumericAttributes.from_bounds(lb, ub, fraction_places, machine_precision)
63
+
64
+
65
+ def compute_result_attrs_subtract(
66
+ left: NumericAttributes,
67
+ right: NumericAttributes,
68
+ machine_precision: int,
69
+ ) -> NumericAttributes:
70
+ tmp = compute_result_attrs_negate(right, machine_precision)
71
+ return compute_result_attrs_add(left, tmp, machine_precision)
@@ -9,6 +9,9 @@ from classiq.interface.exceptions import (
9
9
  ClassiqExpansionError,
10
10
  ClassiqInternalExpansionError,
11
11
  )
12
+ from classiq.interface.generator.expressions.atomic_expression_functions import (
13
+ MEASUREMENT_FUNCTIONS,
14
+ )
12
15
  from classiq.interface.generator.expressions.expression_types import (
13
16
  ExpressionValue,
14
17
  QmodStructInstance,
@@ -49,11 +52,7 @@ from classiq.interface.generator.functions.classical_type import (
49
52
  from classiq.interface.generator.functions.type_name import TypeName
50
53
  from classiq.interface.helpers.backward_compatibility import zip_strict
51
54
 
52
- from classiq.model_expansions.model_tables import (
53
- HandleIdentifier,
54
- HandleTable,
55
- )
56
- from classiq.model_expansions.sympy_conversion.arithmetics import (
55
+ from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
57
56
  BitwiseAnd,
58
57
  BitwiseNot,
59
58
  BitwiseOr,
@@ -62,6 +61,10 @@ from classiq.model_expansions.sympy_conversion.arithmetics import (
62
61
  LShift,
63
62
  RShift,
64
63
  )
64
+ from classiq.model_expansions.model_tables import (
65
+ HandleIdentifier,
66
+ HandleTable,
67
+ )
65
68
  from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
66
69
  MISSING_SLICE_VALUE_PLACEHOLDER,
67
70
  )
@@ -398,7 +401,8 @@ def _symbolic_function(func: str) -> Callable:
398
401
 
399
402
 
400
403
  QMOD_CLASSICAL_FUNCTIONS = [
401
- _symbolic_function(func) for func in qmod_classical_functions
404
+ _symbolic_function(func)
405
+ for func in qmod_classical_functions + list(MEASUREMENT_FUNCTIONS)
402
406
  ]
403
407
 
404
408
  ATOMIC_EXPRESSION_FUNCTIONS = {
@@ -9,6 +9,7 @@ from classiq.interface.generator.compiler_keywords import (
9
9
  LAMBDA_KEYWORD,
10
10
  )
11
11
  from classiq.interface.generator.functions.builtins.internal_operators import (
12
+ BLOCK_OPERATOR_NAME,
12
13
  WITHIN_APPLY_NAME,
13
14
  )
14
15
  from classiq.interface.model.model import MAIN_FUNCTION_NAME
@@ -35,6 +36,8 @@ from classiq.model_expansions.utils.counted_name_allocator import CountedNameAll
35
36
 
36
37
  ClosureType = TypeVar("ClosureType", bound=Closure)
37
38
 
39
+ BLOCKS_ALLOWED_CAPTURING = (WITHIN_APPLY_NAME, BLOCK_OPERATOR_NAME)
40
+
38
41
 
39
42
  @dataclass
40
43
  class Block:
@@ -142,7 +145,7 @@ class OperationBuilder:
142
145
  captured_vars = self.current_block.captured_vars
143
146
  if (
144
147
  not isinstance(self.current_operation, FunctionClosure)
145
- and self.current_operation.name != WITHIN_APPLY_NAME
148
+ and self.current_operation.name not in BLOCKS_ALLOWED_CAPTURING
146
149
  ):
147
150
  validate_captured_directions(
148
151
  captured_vars.filter_var_decls(
@@ -20,6 +20,7 @@ from classiq.interface.helpers.datastructures import LenList
20
20
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
21
21
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
22
22
  from classiq.interface.model.port_declaration import PortDeclaration
23
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
23
24
  from classiq.interface.model.quantum_function_declaration import (
24
25
  PositionalArg,
25
26
  QuantumFunctionDeclaration,
@@ -32,6 +33,7 @@ from classiq.model_expansions.closure import (
32
33
  GenerativeClosure,
33
34
  )
34
35
  from classiq.model_expansions.scope import Evaluated
36
+ from classiq.qmod.builtins.functions import __all__ as all_builtin_func_names
35
37
  from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
36
38
  from classiq.qmod.model_state_container import QMODULE
37
39
  from classiq.qmod.qmod_parameter import CParamStruct, create_param
@@ -50,6 +52,9 @@ if TYPE_CHECKING:
50
52
  )
51
53
 
52
54
 
55
+ ALL_STANDARD_GATES = {func for func in all_builtin_func_names if func.isupper()}
56
+
57
+
53
58
  def _unwrap_traceback_frame(e: Exception) -> Exception:
54
59
  fallback_error = ClassiqExpansionError(str(e))
55
60
  traceback = exc_info()[2]
@@ -130,6 +135,15 @@ class _InterpreterExpandable(QFunc):
130
135
  self._interpreter = interpreter
131
136
 
132
137
  def append_statement_to_body(self, stmt: QuantumStatement) -> None:
138
+ if (
139
+ not isinstance(stmt, QuantumFunctionCall)
140
+ or stmt.func_name not in ALL_STANDARD_GATES
141
+ ):
142
+ stmt = self._annotate_statement(stmt)
143
+ with generative_mode_context(False):
144
+ self._interpreter.emit_statement(stmt)
145
+
146
+ def _annotate_statement(self, stmt: QuantumStatement) -> QuantumStatement:
133
147
  current_operation = self._interpreter._builder._operations[-1]
134
148
  dummy_function = NativeFunctionDefinition(
135
149
  name=current_operation.name,
@@ -155,8 +169,7 @@ class _InterpreterExpandable(QFunc):
155
169
  )
156
170
  resolve_function_calls(dummy_function, func_decls)
157
171
  stmt = dummy_function.body[-1]
158
- with generative_mode_context(False):
159
- self._interpreter.emit_statement(stmt)
172
+ return stmt
160
173
 
161
174
  def _get_function_declarations(self) -> Mapping[str, QuantumFunctionDeclaration]:
162
175
  scope_func_decls: dict[str, QuantumFunctionDeclaration] = {}
@@ -20,6 +20,9 @@ from classiq.interface.generator.expressions.atomic_expression_functions import
20
20
  CLASSICAL_ATTRIBUTES,
21
21
  )
22
22
  from classiq.interface.generator.expressions.expression import Expression
23
+ from classiq.interface.generator.functions.classical_function_declaration import (
24
+ ClassicalFunctionDeclaration,
25
+ )
23
26
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
24
27
  from classiq.interface.model.handle_binding import (
25
28
  FieldHandleBinding,
@@ -69,6 +72,7 @@ from classiq.qmod.builtins.constants import __all__ as builtin_constants
69
72
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
70
73
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
71
74
  from classiq.qmod.model_state_container import QMODULE
75
+ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
72
76
  from classiq.qmod.semantics.error_manager import ErrorManager
73
77
  from classiq.qmod.semantics.validation.model_validation import validate_model
74
78
  from classiq.qmod.utilities import qmod_val_to_expr_str
@@ -89,6 +93,9 @@ class BaseInterpreter:
89
93
 
90
94
  init_builtin_types()
91
95
  init_top_level_scope(model, self._top_level_scope)
96
+ QStructAnnotator().visit(
97
+ ClassicalFunctionDeclaration.FOREIGN_FUNCTION_DECLARATIONS
98
+ )
92
99
  self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
93
100
  self._model.functions_compilation_metadata
94
101
  )
@@ -7,6 +7,7 @@ from numpy.random import permutation
7
7
  from classiq.interface.generator.constant import Constant
8
8
  from classiq.interface.generator.expressions.expression import Expression
9
9
  from classiq.interface.generator.functions.builtins.internal_operators import (
10
+ BLOCK_OPERATOR_NAME,
10
11
  CLASSICAL_IF_OPERATOR_NAME,
11
12
  CONTROL_OPERATOR_NAME,
12
13
  INVERT_OPERATOR_NAME,
@@ -15,6 +16,7 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
15
16
  )
16
17
  from classiq.interface.model.allocate import Allocate
17
18
  from classiq.interface.model.bind_operation import BindOperation
19
+ from classiq.interface.model.block import Block
18
20
  from classiq.interface.model.bounds import SetBoundsStatement
19
21
  from classiq.interface.model.classical_if import ClassicalIf
20
22
  from classiq.interface.model.control import Control
@@ -68,6 +70,9 @@ from classiq.model_expansions.quantum_operations.block_evaluator import (
68
70
  RepeatElimination,
69
71
  )
70
72
  from classiq.model_expansions.quantum_operations.bounds import SetBoundsEmitter
73
+ from classiq.model_expansions.quantum_operations.classical_var_emitter import (
74
+ ClassicalVarEmitter,
75
+ )
71
76
  from classiq.model_expansions.quantum_operations.composite_emitter import (
72
77
  CompositeEmitter,
73
78
  )
@@ -87,6 +92,7 @@ from classiq.model_expansions.scope_initialization import (
87
92
  from classiq.qmod.builtins.functions import permute
88
93
  from classiq.qmod.model_state_container import ModelStateContainer
89
94
  from classiq.qmod.quantum_function import GenerativeQFunc
95
+ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
90
96
 
91
97
 
92
98
  class GenerativeInterpreter(BaseInterpreter):
@@ -192,6 +198,7 @@ class GenerativeInterpreter(BaseInterpreter):
192
198
  [
193
199
  HandleEvaluator(self, "result_var"),
194
200
  ExpressionEvaluator(self, "expression"),
201
+ ClassicalVarEmitter(self),
195
202
  AssignmentResultProcessor(self),
196
203
  ],
197
204
  ).emit(op)
@@ -303,9 +310,18 @@ class GenerativeInterpreter(BaseInterpreter):
303
310
  def emit_set_bounds(self, op: SetBoundsStatement) -> None:
304
311
  CompositeEmitter[SetBoundsStatement](
305
312
  self,
306
- [HandleEvaluator(self, "target"), SetBoundsEmitter(self)],
313
+ [
314
+ ExpressionEvaluator(self, "lower_bound"),
315
+ ExpressionEvaluator(self, "upper_bound"),
316
+ HandleEvaluator(self, "target"),
317
+ SetBoundsEmitter(self),
318
+ ],
307
319
  ).emit(op)
308
320
 
321
+ @emit.register
322
+ def emit_block(self, block: Block) -> None:
323
+ BlockEvaluator(self, BLOCK_OPERATOR_NAME, "statements").emit(block)
324
+
309
325
  def _expand_body(self, operation: Closure) -> None:
310
326
  if isinstance(operation, FunctionClosure) and operation.name == "permute":
311
327
  # special expansion since permute is generative
@@ -358,4 +374,5 @@ class GenerativeInterpreter(BaseInterpreter):
358
374
  )
359
375
 
360
376
  def add_constant(self, constant: Constant) -> None:
377
+ QStructAnnotator().visit(constant.const_type)
361
378
  add_constants_to_scope([constant], self._top_level_scope)
@@ -1,10 +1,12 @@
1
- from typing import Optional
1
+ from typing import TYPE_CHECKING, Optional
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqExpansionError
4
4
  from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
5
5
  from classiq.interface.generator.expressions.expression import Expression
6
6
  from classiq.interface.model.allocate import Allocate
7
7
  from classiq.interface.model.bind_operation import BindOperation
8
+ from classiq.interface.model.block import Block
9
+ from classiq.interface.model.bounds import SetBoundsStatement
8
10
  from classiq.interface.model.handle_binding import (
9
11
  ConcreteHandleBinding,
10
12
  HandleBinding,
@@ -19,6 +21,7 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
19
21
  )
20
22
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
21
23
  from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
24
+ from classiq.interface.model.statement_block import StatementBlock
22
25
  from classiq.interface.model.variable_declaration_statement import (
23
26
  VariableDeclarationStatement,
24
27
  )
@@ -30,14 +33,25 @@ from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_exp
30
33
  validate_assignment_bool_expression,
31
34
  )
32
35
  from classiq.model_expansions.quantum_operations.emitter import Emitter
33
- from classiq.model_expansions.scope import QuantumSymbol
36
+ from classiq.model_expansions.scope import ClassicalSymbol
34
37
  from classiq.model_expansions.transformers.ast_renamer import rename_variables
35
38
  from classiq.qmod.builtins.functions.standard_gates import CX
36
39
 
40
+ if TYPE_CHECKING:
41
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
42
+
37
43
 
38
44
  class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
45
+ def __init__(
46
+ self, interpreter: "BaseInterpreter", replace_assignment_if_needed: bool = False
47
+ ) -> None:
48
+ super().__init__(interpreter)
49
+ self._replace_assignment_if_needed = replace_assignment_if_needed
50
+
39
51
  def emit(self, op: QuantumAssignmentOperation, /) -> bool:
40
- result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
52
+ result_symbol = self._interpreter.evaluate(op.result_var).value
53
+ if isinstance(result_symbol, ClassicalSymbol):
54
+ return False
41
55
  result_type = result_symbol.quantum_type
42
56
 
43
57
  if not (
@@ -70,9 +84,11 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
70
84
  self._validate_declared_attributes(
71
85
  result_type, inferred_result_type, str(op.result_var)
72
86
  )
73
- self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
74
- result_type.set_bounds(inferred_result_type.get_bounds())
75
- return True
87
+ if self._replace_assignment_if_needed:
88
+ self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
89
+ return True
90
+ else:
91
+ return False
76
92
 
77
93
  def _infer_result_type(self, op: ArithmeticOperation) -> Optional[QuantumNumeric]:
78
94
  expr = self._evaluate_expression(op.expression)
@@ -182,6 +198,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
182
198
  result_type: QuantumNumeric,
183
199
  inferred_result_type: QuantumNumeric,
184
200
  ) -> None:
201
+ stmts: StatementBlock = []
185
202
  handles: list[HandleBinding] = []
186
203
 
187
204
  extra_fraction_digits = (
@@ -190,20 +207,22 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
190
207
  )
191
208
  if extra_fraction_digits > 0:
192
209
  handles.append(
193
- self._declare_qarray("extra_fraction_digits", extra_fraction_digits)
210
+ self._declare_qarray(
211
+ "extra_fraction_digits", extra_fraction_digits, stmts
212
+ )
194
213
  )
195
214
 
196
215
  inferred_result_name = self._counted_name_allocator.allocate("inferred_result")
197
216
  inferred_result_handle = HandleBinding(name=inferred_result_name)
198
- self._interpreter.emit(
217
+ stmts.append(
199
218
  VariableDeclarationStatement(
200
- name=inferred_result_name, quantum_type=inferred_result_type
219
+ name=inferred_result_name, qmod_type=inferred_result_type
201
220
  )
202
221
  )
203
222
  handles.append(inferred_result_handle)
204
223
  modified_op = op.model_copy(update={"result_var": inferred_result_handle})
205
224
  self._interpreter.add_to_debug_info(modified_op)
206
- self._interpreter.emit(modified_op)
225
+ stmts.append(modified_op)
207
226
 
208
227
  result_integer_size = (
209
228
  result_type.size_in_bits - result_type.fraction_digits_value
@@ -214,11 +233,11 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
214
233
  )
215
234
  extra_integers = result_integer_size - inferred_result_integer_size
216
235
  if extra_integers > 0:
217
- handles.append(self._declare_qarray("extra_integers", extra_integers))
236
+ handles.append(
237
+ self._declare_qarray("extra_integers", extra_integers, stmts)
238
+ )
218
239
 
219
- self._interpreter.emit(
220
- BindOperation(in_handles=handles, out_handles=[op.result_var])
221
- )
240
+ stmts.append(BindOperation(in_handles=handles, out_handles=[op.result_var]))
222
241
 
223
242
  if (
224
243
  result_type.sign_value
@@ -226,22 +245,44 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
226
245
  and extra_integers > 0
227
246
  ):
228
247
  sign_idx = result_type.size_in_bits - extra_integers - 1
229
- self._sign_extension(op.result_var, sign_idx, result_type.size_in_bits)
248
+ self._sign_extension(
249
+ op.result_var, sign_idx, result_type.size_in_bits, stmts
250
+ )
251
+
252
+ if (inferred_bounds := inferred_result_type.get_bounds()) is not None:
253
+ lower_bound = Expression(expr=str(inferred_bounds[0]))
254
+ upper_bound = Expression(expr=str(inferred_bounds[1]))
255
+ else:
256
+ lower_bound, upper_bound = None, None
257
+ stmts.append(
258
+ SetBoundsStatement(
259
+ target=op.result_var,
260
+ lower_bound=lower_bound,
261
+ upper_bound=upper_bound,
262
+ )
263
+ )
264
+
265
+ self._interpreter.emit(
266
+ Block(
267
+ statements=stmts,
268
+ uuid=op.uuid,
269
+ back_ref=op.back_ref,
270
+ )
271
+ )
230
272
 
231
273
  def _declare_qarray(
232
274
  self,
233
275
  prefix: str,
234
276
  size: int,
277
+ stmts: StatementBlock,
235
278
  allocate: bool = True,
236
279
  ) -> HandleBinding:
237
280
  name = self._counted_name_allocator.allocate(prefix)
238
281
  handle = HandleBinding(name=name)
239
282
  quantum_type = QuantumBitvector(length=Expression(expr=str(size)))
240
- self._interpreter.emit(
241
- VariableDeclarationStatement(name=name, quantum_type=quantum_type)
242
- )
283
+ stmts.append(VariableDeclarationStatement(name=name, qmod_type=quantum_type))
243
284
  if allocate:
244
- self._interpreter.emit(Allocate(target=handle))
285
+ stmts.append(Allocate(target=handle))
245
286
  return handle
246
287
 
247
288
  def _sign_extension(
@@ -249,9 +290,10 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
249
290
  result_var: ConcreteHandleBinding,
250
291
  sign_idx: int,
251
292
  size: int,
293
+ stmts: StatementBlock,
252
294
  ) -> None:
253
- aux = self._declare_qarray("inferred_result_aux", size, allocate=False)
254
- self._interpreter.emit(
295
+ aux = self._declare_qarray("inferred_result_aux", size, stmts, allocate=False)
296
+ stmts.append(
255
297
  WithinApply(
256
298
  compute=[BindOperation(in_handles=[result_var], out_handles=[aux])],
257
299
  action=[
@@ -24,7 +24,13 @@ class SetBoundsEmitter(Emitter[SetBoundsStatement]):
24
24
  raise ClassiqExpansionError(
25
25
  f"Cannot set bounds of a non-numeric variable {op.target.qmod_expr!r}"
26
26
  )
27
- target.quantum_type.set_bounds(op.bounds)
27
+
28
+ if op.lower_bound is not None and op.upper_bound is not None:
29
+ target.quantum_type.set_bounds(
30
+ (op.lower_bound.to_float_value(), op.upper_bound.to_float_value())
31
+ )
32
+ else:
33
+ target.quantum_type.reset_bounds()
28
34
  if self._keep_statement:
29
35
  self.emit_statement(op)
30
36
  return True
@@ -157,7 +157,10 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
157
157
  return any(isinstance(stmt, VariableDeclarationStatement) for stmt in body)
158
158
 
159
159
  def _create_expanded_wrapping_function(
160
- self, name: str, body: Sequence[QuantumStatement]
160
+ self,
161
+ name: str,
162
+ body: Sequence[QuantumStatement],
163
+ debug_info: Optional[FunctionDebugInfo] = None,
161
164
  ) -> QuantumFunctionCall:
162
165
  wrapping_function = FunctionClosure.create(
163
166
  name=self._counted_name_allocator.allocate(name),
@@ -165,7 +168,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
165
168
  scope=Scope(parent=self._current_scope),
166
169
  lambda_external_vars=self._builder.current_block.captured_vars,
167
170
  )
168
- return self._create_quantum_function_call(wrapping_function, list(), None)
171
+ return self._create_quantum_function_call(wrapping_function, list(), debug_info)
169
172
 
170
173
  def _emit_quantum_function_call(
171
174
  self,
@@ -0,0 +1,16 @@
1
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
2
+ ArithmeticOperation,
3
+ )
4
+
5
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
6
+ from classiq.model_expansions.scope import ClassicalSymbol
7
+
8
+
9
+ class ClassicalVarEmitter(Emitter[ArithmeticOperation]):
10
+ def emit(self, op: ArithmeticOperation, /) -> bool:
11
+ result_symbol = self._interpreter.evaluate(op.result_var).value
12
+ if not isinstance(result_symbol, ClassicalSymbol):
13
+ return False
14
+ op._classical_assignment = True
15
+ self.emit_statement(op)
16
+ return True
@@ -1,14 +1,19 @@
1
+ from typing import TYPE_CHECKING, Union
2
+
1
3
  from classiq.interface.exceptions import ClassiqExpansionError
4
+ from classiq.interface.generator.functions.classical_type import ClassicalType
2
5
  from classiq.interface.model.handle_binding import HandleBinding
6
+ from classiq.interface.model.quantum_type import QuantumType
3
7
  from classiq.interface.model.variable_declaration_statement import (
4
8
  VariableDeclarationStatement,
5
9
  )
6
10
 
7
11
  from classiq.evaluators.parameter_types import (
12
+ evaluate_type_in_classical_symbol,
8
13
  evaluate_type_in_quantum_symbol,
9
14
  )
10
15
  from classiq.model_expansions.quantum_operations.emitter import Emitter
11
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol
16
+ from classiq.model_expansions.scope import ClassicalSymbol, Evaluated, QuantumSymbol
12
17
 
13
18
 
14
19
  class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement]):
@@ -16,24 +21,39 @@ class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement])
16
21
  var_decl = variable_declaration.model_copy(
17
22
  update=dict(back_ref=variable_declaration.uuid)
18
23
  )
19
- var_decl.quantum_type = variable_declaration.quantum_type.model_copy()
24
+ var_decl.qmod_type = variable_declaration.qmod_type.model_copy()
20
25
  if variable_declaration.name in self._current_scope:
21
26
  raise ClassiqExpansionError(
22
27
  f"Variable {variable_declaration.name!r} is already defined"
23
28
  )
24
- self._current_scope[variable_declaration.name] = Evaluated(
25
- value=QuantumSymbol(
29
+ var_value: Union[QuantumSymbol, ClassicalSymbol]
30
+ if variable_declaration.is_quantum:
31
+ if TYPE_CHECKING:
32
+ assert isinstance(var_decl.qmod_type, QuantumType)
33
+ var_value = QuantumSymbol(
26
34
  handle=HandleBinding(name=var_decl.name),
27
35
  quantum_type=evaluate_type_in_quantum_symbol(
28
- var_decl.quantum_type,
36
+ var_decl.qmod_type,
29
37
  self._current_scope,
30
38
  var_decl.name,
31
39
  ),
32
- ),
33
- defining_function=self._builder.current_function,
34
- )
35
- self._builder.current_block.captured_vars.init_var(
36
- var_decl.name, self._builder.current_function
40
+ )
41
+ self._builder.current_block.captured_vars.init_var(
42
+ var_decl.name, self._builder.current_function
43
+ )
44
+ else:
45
+ if TYPE_CHECKING:
46
+ assert isinstance(var_decl.qmod_type, ClassicalType)
47
+ var_value = ClassicalSymbol(
48
+ handle=HandleBinding(name=var_decl.name),
49
+ classical_type=evaluate_type_in_classical_symbol(
50
+ var_decl.qmod_type,
51
+ self._current_scope,
52
+ var_decl.name,
53
+ ),
54
+ )
55
+ self._current_scope[variable_declaration.name] = Evaluated(
56
+ value=var_value, defining_function=self._builder.current_function
37
57
  )
38
58
  self.emit_statement(var_decl)
39
59
  return True
@@ -21,6 +21,7 @@ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_
21
21
  from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
22
22
  QmodStructInstance,
23
23
  )
24
+ from classiq.interface.generator.functions.classical_type import ClassicalType
24
25
  from classiq.interface.generator.functions.type_name import TypeName
25
26
  from classiq.interface.helpers.text_utils import readable_list, s
26
27
  from classiq.interface.model.handle_binding import (
@@ -202,6 +203,12 @@ class QuantumSymbolList(QuantumVariable):
202
203
  return str(self.handles)
203
204
 
204
205
 
206
+ @dataclass(frozen=True)
207
+ class ClassicalSymbol:
208
+ handle: HandleBinding
209
+ classical_type: ClassicalType
210
+
211
+
205
212
  @singledispatch
206
213
  def evaluated_to_str(value: Any) -> str:
207
214
  return str(value)
@@ -28,6 +28,7 @@ from classiq.qmod.builtins.functions import (
28
28
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
29
29
  from classiq.qmod.model_state_container import QMODULE
30
30
  from classiq.qmod.quantum_function import GenerativeQFunc
31
+ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
31
32
 
32
33
 
33
34
  def add_constants_to_scope(constants: list[Constant], scope: Scope) -> None:
@@ -133,3 +134,4 @@ def init_top_level_scope(model: Model, scope: Scope) -> None:
133
134
  def init_builtin_types() -> None:
134
135
  QMODULE.enum_decls |= BUILTIN_ENUM_DECLARATIONS
135
136
  QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
137
+ QStructAnnotator().visit(BUILTIN_STRUCT_DECLARATIONS)
@@ -21,7 +21,7 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
21
21
  AnyClassicalValue,
22
22
  )
23
23
 
24
- from classiq.model_expansions.sympy_conversion.arithmetics import LogicalXor
24
+ from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import LogicalXor
25
25
 
26
26
 
27
27
  def sympy_to_python(
@@ -16,6 +16,7 @@ from classiq.interface.generator.functions.port_declaration import (
16
16
  from classiq.interface.generator.functions.type_modifier import TypeModifier
17
17
  from classiq.interface.model.allocate import Allocate
18
18
  from classiq.interface.model.bind_operation import BindOperation
19
+ from classiq.interface.model.block import Block
19
20
  from classiq.interface.model.control import Control
20
21
  from classiq.interface.model.invert import Invert
21
22
  from classiq.interface.model.model_visitor import ModelVisitor
@@ -352,6 +353,10 @@ class TypeModifierValidation(ModelVisitor):
352
353
  self.visit(within_apply.compute)
353
354
  self.visit(within_apply.action)
354
355
 
356
+ def visit_Block(self, block: Block) -> None:
357
+ with self.source_reference_context(block.source_ref):
358
+ self.visit(block.statements)
359
+
355
360
 
356
361
  def _merge_overlapping(bound_vars: Sequence[Collection[str]]) -> list[set[str]]:
357
362
  """
@@ -218,7 +218,7 @@ class VarSplitter(ModelRenamer):
218
218
  return [
219
219
  VariableDeclarationStatement(
220
220
  name=part.target_var_name,
221
- quantum_type=part.target_var_type,
221
+ qmod_type=part.target_var_type,
222
222
  )
223
223
  for part in chain.from_iterable(symbol_parts.values())
224
224
  ]
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Union
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqInternalExpansionError
6
6
 
7
- from classiq.model_expansions.sympy_conversion.arithmetics import LogicalXor
7
+ from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import LogicalXor
8
8
 
9
9
 
10
10
  class BooleanExpressionOptimizer(ast.NodeTransformer):