classiq 0.56.1__py3-none-any.whl → 0.58.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 (46) hide show
  1. classiq/analyzer/show_interactive_hack.py +16 -4
  2. classiq/applications/combinatorial_helpers/encoding_utils.py +1 -0
  3. classiq/applications/combinatorial_helpers/transformations/encoding.py +3 -1
  4. classiq/execution/jobs.py +8 -1
  5. classiq/executor.py +1 -1
  6. classiq/interface/_version.py +1 -1
  7. classiq/interface/backend/backend_preferences.py +27 -5
  8. classiq/interface/backend/pydantic_backend.py +0 -1
  9. classiq/interface/execution/jobs.py +4 -1
  10. classiq/interface/executor/execution_request.py +19 -5
  11. classiq/interface/generator/arith/arithmetic_expression_validator.py +28 -9
  12. classiq/interface/generator/functions/type_name.py +7 -9
  13. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  14. classiq/model_expansions/closure.py +24 -6
  15. classiq/model_expansions/evaluators/parameter_types.py +1 -2
  16. classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
  17. classiq/model_expansions/function_builder.py +13 -0
  18. classiq/model_expansions/interpreter.py +9 -14
  19. classiq/model_expansions/quantum_operations/call_emitter.py +207 -0
  20. classiq/model_expansions/quantum_operations/classicalif.py +2 -2
  21. classiq/model_expansions/quantum_operations/control.py +7 -5
  22. classiq/model_expansions/quantum_operations/emitter.py +1 -186
  23. classiq/model_expansions/quantum_operations/expression_operation.py +26 -189
  24. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +2 -2
  25. classiq/model_expansions/quantum_operations/invert.py +2 -2
  26. classiq/model_expansions/quantum_operations/phase.py +3 -1
  27. classiq/model_expansions/quantum_operations/power.py +2 -2
  28. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +7 -9
  29. classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -2
  30. classiq/model_expansions/quantum_operations/repeat.py +2 -2
  31. classiq/model_expansions/transformers/__init__.py +0 -0
  32. classiq/model_expansions/transformers/var_splitter.py +237 -0
  33. classiq/qmod/builtins/classical_functions.py +1 -0
  34. classiq/qmod/builtins/functions/state_preparation.py +1 -1
  35. classiq/qmod/create_model_function.py +25 -20
  36. classiq/qmod/native/pretty_printer.py +19 -4
  37. classiq/qmod/pretty_print/pretty_printer.py +53 -28
  38. classiq/qmod/qfunc.py +18 -16
  39. classiq/qmod/quantum_function.py +30 -24
  40. classiq/qmod/semantics/qstruct_annotator.py +23 -0
  41. classiq/qmod/semantics/static_semantics_visitor.py +4 -1
  42. classiq/qmod/write_qmod.py +3 -1
  43. classiq/synthesis.py +3 -1
  44. {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/METADATA +1 -1
  45. {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/RECORD +46 -42
  46. {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/WHEEL +0 -0
@@ -1,79 +1,59 @@
1
1
  import ast
2
- from abc import abstractmethod
3
- from itertools import chain
4
- from typing import TYPE_CHECKING, TypeVar, Union
2
+ from typing import TYPE_CHECKING, Generic, TypeVar, Union
5
3
 
6
- from classiq.interface.exceptions import (
7
- ClassiqExpansionError,
8
- ClassiqInternalExpansionError,
9
- )
10
4
  from classiq.interface.generator.expressions.evaluated_expression import (
11
5
  EvaluatedExpression,
12
6
  )
13
7
  from classiq.interface.generator.expressions.expression import Expression
14
- from classiq.interface.generator.functions.type_name import TypeName
15
- from classiq.interface.generator.visitor import NodeType, Transformer
16
- from classiq.interface.model.bind_operation import BindOperation
8
+ from classiq.interface.generator.visitor import NodeType
17
9
  from classiq.interface.model.control import Control
18
- from classiq.interface.model.handle_binding import (
19
- FieldHandleBinding,
20
- HandleBinding,
21
- SlicedHandleBinding,
22
- SubscriptHandleBinding,
23
- )
24
10
  from classiq.interface.model.quantum_expressions.quantum_expression import (
25
11
  QuantumAssignmentOperation,
26
12
  QuantumExpressionOperation,
27
13
  )
28
14
  from classiq.interface.model.quantum_type import (
29
- QuantumBit,
30
- QuantumBitvector,
31
15
  QuantumNumeric,
32
- QuantumType,
33
- )
34
- from classiq.interface.model.variable_declaration_statement import (
35
- VariableDeclarationStatement,
36
16
  )
37
17
  from classiq.interface.model.within_apply_operation import WithinApply
38
18
 
39
- from classiq.model_expansions.quantum_operations.emitter import Emitter
19
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
40
20
  from classiq.model_expansions.scope import QuantumSymbol
21
+ from classiq.model_expansions.transformers.var_splitter import (
22
+ SymbolParts,
23
+ VarSplitter,
24
+ )
41
25
  from classiq.model_expansions.visitors.variable_references import VarRefCollector
42
26
 
27
+ if TYPE_CHECKING:
28
+ from classiq.model_expansions.interpreter import Interpreter
29
+
43
30
  ExpressionOperationT = TypeVar("ExpressionOperationT", bound=QuantumExpressionOperation)
44
31
  AST_NODE = TypeVar("AST_NODE", bound=NodeType)
45
32
 
46
33
 
47
- class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
48
- @abstractmethod
49
- def emit(self, op: ExpressionOperationT, /) -> None:
50
- pass
34
+ class ExpressionOperationEmitter(
35
+ Generic[ExpressionOperationT], CallEmitter[ExpressionOperationT], VarSplitter
36
+ ):
37
+ def __init__(self, interpreter: "Interpreter") -> None:
38
+ CallEmitter.__init__(self, interpreter) # type:ignore[arg-type]
39
+ VarSplitter.__init__(self, interpreter._current_scope)
51
40
 
52
41
  def _emit_with_split(
53
42
  self,
54
43
  op: ExpressionOperationT,
55
44
  expression: Expression,
56
- symbols_to_split: dict[QuantumSymbol, set[HandleBinding]],
45
+ symbol_parts: SymbolParts,
57
46
  ) -> None:
58
- symbols_parts, bind_ops = self._get_bind_ops(symbols_to_split)
47
+ for var_decl in self.get_var_decls(symbol_parts):
48
+ self._interpreter.emit_statement(var_decl)
49
+ bind_ops = self.get_bind_ops(symbol_parts)
59
50
 
60
- for symbol_parts in symbols_parts:
61
- for symbol, symbol_part_var_name in symbol_parts:
62
- if symbol.handle.identifier not in self._current_scope:
63
- self._interpreter.emit_statement(
64
- VariableDeclarationStatement(
65
- name=symbol_part_var_name,
66
- quantum_type=symbol.quantum_type,
67
- )
68
- )
69
-
70
- symbol_mapping = {
71
- symbol.handle: (symbol_part_var_name, symbol.quantum_type)
72
- for symbol, symbol_part_var_name in chain.from_iterable(symbols_parts)
73
- }
74
- new_expression = self._update_op_expression(symbol_mapping, expression)
51
+ new_expression = self.rewrite(expression, symbol_parts)
52
+ new_expression._evaluated_expr = EvaluatedExpression(
53
+ value=self._interpreter.evaluate(new_expression).value
54
+ )
75
55
  new_op = op.model_copy(update=dict(expression=new_expression))
76
- new_op = self._get_updated_op_split_symbols(new_op, symbol_mapping)
56
+ new_op = self._get_updated_op_split_symbols(new_op, symbol_parts)
77
57
 
78
58
  self._interpreter.emit_statement(
79
59
  WithinApply(
@@ -86,125 +66,10 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
86
66
  def _get_updated_op_split_symbols(
87
67
  self,
88
68
  op: ExpressionOperationT,
89
- symbol_mapping: dict[HandleBinding, tuple[str, QuantumType]],
69
+ symbol_mapping: SymbolParts,
90
70
  ) -> ExpressionOperationT:
91
71
  return op
92
72
 
93
- def _update_op_expression(
94
- self,
95
- symbol_mapping: dict[HandleBinding, tuple[str, QuantumType]],
96
- expression: Expression,
97
- ) -> Expression:
98
- vrc = VarRefCollector(ignore_duplicated_handles=True)
99
- vrc.visit(ast.parse(expression.expr))
100
-
101
- new_expr_str = expression.expr
102
- for handle in vrc.var_handles:
103
- collapsed_handle = handle.collapse()
104
- if collapsed_handle in symbol_mapping:
105
- new_expr_str = new_expr_str.replace(
106
- str(handle), symbol_mapping[collapsed_handle][0]
107
- )
108
- self._check_all_handles_were_replaced(new_expr_str)
109
-
110
- new_expr = Expression(expr=new_expr_str)
111
- new_expr._evaluated_expr = EvaluatedExpression(
112
- value=self._interpreter.evaluate(new_expr).value
113
- )
114
- return new_expr
115
-
116
- def _check_all_handles_were_replaced(self, new_expr_str: str) -> None:
117
- vrc = VarRefCollector(ignore_duplicated_handles=True)
118
- vrc.visit(ast.parse(new_expr_str))
119
- for handle in self._get_handles(vrc):
120
- if isinstance(
121
- handle,
122
- (SubscriptHandleBinding, SlicedHandleBinding, FieldHandleBinding),
123
- ):
124
- raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
125
-
126
- def _get_bind_ops(
127
- self,
128
- symbols_to_split: dict[QuantumSymbol, set[HandleBinding]],
129
- ) -> tuple[list[list[tuple[QuantumSymbol, str]]], list[BindOperation]]:
130
- bind_ops = []
131
- symbols_parts = []
132
- for symbol, target_parts in symbols_to_split.items():
133
- symbol_parts = self._get_symbol_parts(symbol, target_parts)
134
- symbols_parts.append(symbol_parts)
135
- bind_ops.append(
136
- BindOperation(
137
- in_handles=[symbol.handle],
138
- out_handles=[
139
- HandleBinding(name=symbol_part_var_name)
140
- for _, symbol_part_var_name in symbol_parts
141
- ],
142
- )
143
- )
144
- return symbols_parts, bind_ops
145
-
146
- def _get_symbol_parts(
147
- self, symbol: QuantumSymbol, target_parts: set[HandleBinding]
148
- ) -> list[tuple[QuantumSymbol, str]]:
149
- quantum_type = symbol.quantum_type
150
-
151
- if all(
152
- symbol.handle == target_part or symbol.handle not in target_part.prefixes()
153
- for target_part in target_parts
154
- ) or isinstance(quantum_type, (QuantumBit, QuantumNumeric)):
155
- return [
156
- (
157
- symbol,
158
- self._counted_name_allocator.allocate(symbol.handle.identifier),
159
- )
160
- ]
161
-
162
- if isinstance(quantum_type, QuantumBitvector):
163
- if not quantum_type.has_length:
164
- raise ClassiqExpansionError(
165
- f"Could not determine the length of quantum array "
166
- f"{symbol.handle}."
167
- )
168
- return list(
169
- chain.from_iterable(
170
- self._get_symbol_parts(symbol[idx], target_parts)
171
- for idx in range(quantum_type.length_value)
172
- )
173
- )
174
-
175
- if TYPE_CHECKING:
176
- assert isinstance(quantum_type, TypeName)
177
-
178
- return list(
179
- chain.from_iterable(
180
- self._get_symbol_parts(field_symbol, target_parts)
181
- for field_symbol in symbol.fields.values()
182
- )
183
- )
184
-
185
- def _get_symbols_to_split(
186
- self, expression: Expression
187
- ) -> dict[QuantumSymbol, set[HandleBinding]]:
188
- vrc = VarRefCollector(ignore_duplicated_handles=True)
189
- vrc.visit(ast.parse(expression.expr))
190
- symbol_names_to_split = dict.fromkeys(
191
- handle.name
192
- for handle in self._get_handles(vrc)
193
- if isinstance(handle, (SubscriptHandleBinding, FieldHandleBinding))
194
- )
195
- return {
196
- symbol: {
197
- handle.collapse()
198
- for handle in vrc.var_handles
199
- if handle.name == symbol.handle.name
200
- }
201
- for symbol_name in symbol_names_to_split
202
- if isinstance(
203
- symbol := self._current_scope[symbol_name].value,
204
- QuantumSymbol,
205
- )
206
- }
207
-
208
73
  def _evaluate_op_expression(self, op: ExpressionOperationT) -> Expression:
209
74
  return self._evaluate_expression(op.expression)
210
75
 
@@ -248,31 +113,3 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
248
113
  isinstance(op.result_type, QuantumNumeric)
249
114
  and (op.result_type.sign_value or op.result_type.fraction_digits_value > 0)
250
115
  )
251
-
252
- def _get_handles(self, collector: VarRefCollector) -> list[HandleBinding]:
253
- return [
254
- handle
255
- for handle in collector.var_handles
256
- if isinstance(self._interpreter.evaluate(handle.name).value, QuantumSymbol)
257
- ]
258
-
259
- def _rewrite(
260
- self,
261
- subject: AST_NODE,
262
- symbol_mapping: dict[HandleBinding, tuple[str, QuantumType]],
263
- ) -> AST_NODE:
264
- class ReplaceSplitVars(Transformer):
265
- @staticmethod
266
- def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
267
- handle = handle.collapse()
268
- for handle_to_replace, replacement in symbol_mapping.items():
269
- handle = handle.replace_prefix(
270
- handle_to_replace, HandleBinding(name=replacement[0])
271
- )
272
- return handle
273
-
274
- @staticmethod
275
- def visit_Expression(expr: Expression) -> Expression:
276
- return self._update_op_expression(symbol_mapping, expr)
277
-
278
- return ReplaceSplitVars().visit(subject)
@@ -31,7 +31,7 @@ from classiq.model_expansions.evaluators.parameter_types import (
31
31
  from classiq.model_expansions.evaluators.quantum_type_utils import (
32
32
  validate_inplace_binary_op_vars,
33
33
  )
34
- from classiq.model_expansions.quantum_operations.emitter import Emitter
34
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
35
35
  from classiq.model_expansions.scope import QuantumSymbol, Scope
36
36
  from classiq.qmod.builtins.functions import (
37
37
  CX,
@@ -59,7 +59,7 @@ def _binary_function_declaration(
59
59
  }[constant][op]
60
60
 
61
61
 
62
- class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
62
+ class InplaceBinaryOperationEmitter(CallEmitter[InplaceBinaryOperation]):
63
63
  def emit(self, op: InplaceBinaryOperation, /) -> None:
64
64
  if isinstance(op.value, Expression):
65
65
  self._emit_constant_operation(op)
@@ -4,11 +4,11 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
4
4
  from classiq.interface.model.invert import Invert
5
5
 
6
6
  from classiq.model_expansions.closure import Closure
7
- from classiq.model_expansions.quantum_operations.emitter import Emitter
7
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
8
8
  from classiq.model_expansions.scope import Scope
9
9
 
10
10
 
11
- class InvertEmitter(Emitter[Invert]):
11
+ class InvertEmitter(CallEmitter[Invert]):
12
12
  def emit(self, invert: Invert, /) -> None:
13
13
  with self._propagated_var_stack.capture_variables(invert):
14
14
  self._emit_propagated(invert)
@@ -41,7 +41,9 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
41
41
  def emit(self, phase_op: PhaseOperation, /) -> None:
42
42
  phase_expression = self._evaluate_op_expression(phase_op)
43
43
  phase_op = phase_op.model_copy(update=dict(expression=phase_expression))
44
- arrays_with_subscript = self._get_symbols_to_split(phase_op.expression)
44
+ arrays_with_subscript = self.split_symbols(
45
+ phase_op.expression, self._counted_name_allocator.allocate
46
+ )
45
47
  if len(arrays_with_subscript) > 0:
46
48
  self._emit_with_split(phase_op, phase_op.expression, arrays_with_subscript)
47
49
  return
@@ -13,11 +13,11 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
13
13
  from classiq.interface.model.power import Power
14
14
 
15
15
  from classiq.model_expansions.closure import Closure
16
- from classiq.model_expansions.quantum_operations.emitter import Emitter
16
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
17
17
  from classiq.model_expansions.scope import Scope
18
18
 
19
19
 
20
- class PowerEmitter(Emitter[Power]):
20
+ class PowerEmitter(CallEmitter[Power]):
21
21
  _power_value: Union[int, sympy.Basic]
22
22
  _power_expr: Expression
23
23
 
@@ -20,10 +20,7 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
20
20
  QuantumAssignmentOperation,
21
21
  )
22
22
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
23
- from classiq.interface.model.quantum_type import (
24
- QuantumNumeric,
25
- QuantumType,
26
- )
23
+ from classiq.interface.model.quantum_type import QuantumNumeric
27
24
  from classiq.interface.model.variable_declaration_statement import (
28
25
  VariableDeclarationStatement,
29
26
  )
@@ -35,6 +32,7 @@ from classiq.model_expansions.quantum_operations.expression_operation import (
35
32
  ExpressionOperationEmitter,
36
33
  )
37
34
  from classiq.model_expansions.scope import QuantumSymbol
35
+ from classiq.model_expansions.transformers.var_splitter import SymbolParts
38
36
  from classiq.model_expansions.visitors.boolean_expression_transformers import (
39
37
  BooleanExpressionFuncLibAdapter,
40
38
  BooleanExpressionOptimizer,
@@ -70,7 +68,9 @@ class QuantumAssignmentOperationEmitter(
70
68
  new_expression = self._evaluate_op_expression(op)
71
69
  if self._skip_assignment(op, new_expression.expr):
72
70
  return
73
- arrays_with_subscript = self._get_symbols_to_split(new_expression)
71
+ arrays_with_subscript = self.split_symbols(
72
+ new_expression, self._counted_name_allocator.allocate
73
+ )
74
74
  if len(arrays_with_subscript) > 0:
75
75
  self._emit_with_split(op, new_expression, arrays_with_subscript)
76
76
  return
@@ -230,12 +230,10 @@ class QuantumAssignmentOperationEmitter(
230
230
  return True
231
231
 
232
232
  def _get_updated_op_split_symbols(
233
- self,
234
- op: QuantumAssignmentOperation,
235
- symbol_mapping: dict[HandleBinding, tuple[str, QuantumType]],
233
+ self, op: QuantumAssignmentOperation, symbol_parts: SymbolParts
236
234
  ) -> QuantumAssignmentOperation:
237
235
  return op.model_copy(
238
- update=dict(result_var=self._rewrite(op.result_var, symbol_mapping))
236
+ update=dict(result_var=self.rewrite(op.result_var, symbol_parts))
239
237
  )
240
238
 
241
239
 
@@ -3,14 +3,14 @@ from typing import TYPE_CHECKING
3
3
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
4
4
 
5
5
  from classiq.model_expansions.closure import FunctionClosure
6
- from classiq.model_expansions.quantum_operations.emitter import Emitter
6
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
7
7
  from classiq.qmod.semantics.error_manager import ErrorManager
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from classiq.model_expansions.interpreter import Interpreter
11
11
 
12
12
 
13
- class QuantumFunctionCallEmitter(Emitter[QuantumFunctionCall]):
13
+ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
14
14
  def __init__(self, interpreter: "Interpreter") -> None:
15
15
  super().__init__(interpreter)
16
16
  self._model = self._interpreter._model
@@ -10,12 +10,12 @@ from classiq.interface.model.classical_parameter_declaration import (
10
10
  from classiq.interface.model.repeat import Repeat
11
11
 
12
12
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
13
- from classiq.model_expansions.quantum_operations.emitter import Emitter
13
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
14
14
  from classiq.model_expansions.scope import Scope
15
15
  from classiq.qmod.quantum_function import GenerativeQFunc
16
16
 
17
17
 
18
- class RepeatEmitter(Emitter[Repeat]):
18
+ class RepeatEmitter(CallEmitter[Repeat]):
19
19
  def emit(self, repeat: Repeat, /) -> None:
20
20
  count = self._interpreter.evaluate(repeat.count).as_type(int)
21
21
  if count < 0:
File without changes
@@ -0,0 +1,237 @@
1
+ import ast
2
+ from dataclasses import dataclass
3
+ from itertools import chain
4
+ from typing import TYPE_CHECKING, Callable, TypeVar
5
+
6
+ from classiq.interface.exceptions import (
7
+ ClassiqExpansionError,
8
+ ClassiqInternalExpansionError,
9
+ )
10
+ from classiq.interface.generator.expressions.expression import Expression
11
+ from classiq.interface.generator.visitor import NodeType, Transformer
12
+ from classiq.interface.model.bind_operation import BindOperation
13
+ from classiq.interface.model.handle_binding import (
14
+ HandleBinding,
15
+ NestedHandleBinding,
16
+ SlicedHandleBinding,
17
+ )
18
+ from classiq.interface.model.quantum_type import (
19
+ QuantumBitvector,
20
+ QuantumScalar,
21
+ QuantumType,
22
+ )
23
+ from classiq.interface.model.variable_declaration_statement import (
24
+ VariableDeclarationStatement,
25
+ )
26
+
27
+ from classiq.model_expansions.scope import QuantumSymbol, Scope
28
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
29
+
30
+ AST_NODE = TypeVar("AST_NODE", bound=NodeType)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class SymbolPart:
35
+ source_handle: HandleBinding
36
+ target_var_name: str
37
+ target_var_type: QuantumType
38
+
39
+ @property
40
+ def target_var_handle(self) -> HandleBinding:
41
+ return HandleBinding(name=self.target_var_name)
42
+
43
+
44
+ SymbolParts = dict[QuantumSymbol, list[SymbolPart]]
45
+ PartNamer = Callable[[str], str]
46
+
47
+
48
+ class VarSplitter:
49
+ def __init__(self, scope: Scope):
50
+ self._scope = scope
51
+
52
+ def split_symbols(self, expression: Expression, namer: PartNamer) -> SymbolParts:
53
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
54
+ vrc.visit(ast.parse(expression.expr))
55
+ symbol_names_to_split = dict.fromkeys(
56
+ handle.name
57
+ for handle in self._get_handles(vrc)
58
+ if isinstance(handle, NestedHandleBinding)
59
+ )
60
+
61
+ symbol_handles = {
62
+ symbol: list(
63
+ dict.fromkeys(
64
+ handle.collapse()
65
+ for handle in vrc.var_handles
66
+ if handle.name == symbol.handle.name
67
+ )
68
+ )
69
+ for symbol_name in symbol_names_to_split
70
+ if isinstance(
71
+ symbol := self._scope[symbol_name].value,
72
+ QuantumSymbol,
73
+ )
74
+ }
75
+
76
+ return {
77
+ symbol: [
78
+ SymbolPart(
79
+ source_handle=part.handle,
80
+ target_var_name=namer(part.handle.identifier),
81
+ target_var_type=part.quantum_type,
82
+ )
83
+ for part in self._get_symbol_parts(symbol, handles)
84
+ ]
85
+ for symbol, handles in symbol_handles.items()
86
+ }
87
+
88
+ def _get_handles(self, collector: VarRefCollector) -> list[HandleBinding]:
89
+ return [
90
+ handle
91
+ for handle in collector.var_handles
92
+ if isinstance(self._scope[handle.name].value, QuantumSymbol)
93
+ ]
94
+
95
+ def _get_symbol_parts(
96
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
97
+ ) -> list[QuantumSymbol]:
98
+ for i in range(len(target_parts)):
99
+ for j in range(i + 1, len(target_parts)):
100
+ if target_parts[i].overlaps(target_parts[j]):
101
+ raise ClassiqInternalExpansionError(
102
+ f"Handles {str(target_parts[i])!r} and "
103
+ f"{str(target_parts[j])!r} overlapping in expression"
104
+ )
105
+ return self._get_symbol_parts_unsafe(symbol, target_parts)
106
+
107
+ def _get_symbol_parts_unsafe(
108
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
109
+ ) -> list[QuantumSymbol]:
110
+ if all(
111
+ symbol.handle == target_part or symbol.handle not in target_part.prefixes()
112
+ for target_part in target_parts
113
+ ) or isinstance(symbol.quantum_type, QuantumScalar):
114
+ return [symbol]
115
+
116
+ if isinstance(symbol.quantum_type, QuantumBitvector):
117
+ return self._get_array_parts(symbol, target_parts)
118
+
119
+ return self._get_struct_parts(symbol, target_parts)
120
+
121
+ def _get_array_parts(
122
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
123
+ ) -> list[QuantumSymbol]:
124
+ if TYPE_CHECKING:
125
+ assert isinstance(symbol.quantum_type, QuantumBitvector)
126
+
127
+ if not symbol.quantum_type.has_length:
128
+ raise ClassiqExpansionError(
129
+ f"Could not determine the length of quantum array " f"{symbol.handle}."
130
+ )
131
+ target_slices = {
132
+ target_part.start.to_int_value(): target_part.end.to_int_value()
133
+ for target_part in target_parts
134
+ if isinstance(target_part, SlicedHandleBinding)
135
+ and symbol.handle == target_part.base_handle
136
+ }
137
+
138
+ symbol_parts: list[QuantumSymbol] = []
139
+ idx = 0
140
+ while idx < symbol.quantum_type.length_value:
141
+ if idx in target_slices:
142
+ stop = target_slices[idx]
143
+ if stop <= idx:
144
+ raise ClassiqInternalExpansionError(
145
+ f"Illegal sliced handle {str(symbol[idx: stop].handle)!r}"
146
+ )
147
+ symbol_parts.append(symbol[idx:stop])
148
+ idx = stop
149
+ else:
150
+ symbol_parts.extend(
151
+ self._get_symbol_parts_unsafe(symbol[idx], target_parts)
152
+ )
153
+ idx += 1
154
+
155
+ return symbol_parts
156
+
157
+ def _get_struct_parts(
158
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
159
+ ) -> list[QuantumSymbol]:
160
+ return list(
161
+ chain.from_iterable(
162
+ self._get_symbol_parts_unsafe(field_symbol, target_parts)
163
+ for field_symbol in symbol.fields.values()
164
+ )
165
+ )
166
+
167
+ @staticmethod
168
+ def get_bind_ops(symbol_parts: SymbolParts) -> list[BindOperation]:
169
+ return [
170
+ BindOperation(
171
+ in_handles=[symbol.handle],
172
+ out_handles=[part.target_var_handle for part in parts],
173
+ )
174
+ for symbol, parts in symbol_parts.items()
175
+ ]
176
+
177
+ @staticmethod
178
+ def get_var_decls(symbol_parts: SymbolParts) -> list[VariableDeclarationStatement]:
179
+ return [
180
+ VariableDeclarationStatement(
181
+ name=part.target_var_name,
182
+ quantum_type=part.target_var_type,
183
+ )
184
+ for part in chain.from_iterable(symbol_parts.values())
185
+ ]
186
+
187
+ def _check_all_handles_were_replaced(self, new_expr_str: str) -> None:
188
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
189
+ vrc.visit(ast.parse(new_expr_str))
190
+ for handle in self._get_handles(vrc):
191
+ if isinstance(handle, NestedHandleBinding):
192
+ raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
193
+
194
+ def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolParts) -> AST_NODE:
195
+ handle_replacements = {
196
+ part.source_handle: part.target_var_handle
197
+ for parts in symbol_mapping.values()
198
+ for part in parts
199
+ }
200
+
201
+ class ReplaceSplitVars(Transformer):
202
+ @staticmethod
203
+ def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
204
+ handle = handle.collapse()
205
+ for handle_to_replace, replacement in handle_replacements.items():
206
+ handle = handle.replace_prefix(handle_to_replace, replacement)
207
+ return handle
208
+
209
+ @staticmethod
210
+ def visit_Expression(expr: Expression) -> Expression:
211
+ return self._rewrite_expression(symbol_mapping, expr)
212
+
213
+ return ReplaceSplitVars().visit(subject)
214
+
215
+ def _rewrite_expression(
216
+ self,
217
+ symbol_mapping: SymbolParts,
218
+ expression: Expression,
219
+ ) -> Expression:
220
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
221
+ vrc.visit(ast.parse(expression.expr))
222
+
223
+ handle_names = {
224
+ part.source_handle: part.target_var_name
225
+ for parts in symbol_mapping.values()
226
+ for part in parts
227
+ }
228
+ new_expr_str = expression.expr
229
+ for handle in vrc.var_handles:
230
+ collapsed_handle = handle.collapse()
231
+ if collapsed_handle in handle_names:
232
+ new_expr_str = new_expr_str.replace(
233
+ str(handle), handle_names[collapsed_handle]
234
+ )
235
+ self._check_all_handles_were_replaced(new_expr_str)
236
+
237
+ return Expression(expr=new_expr_str)
@@ -1,5 +1,6 @@
1
1
  # This file was generated automatically - do not edit manually
2
2
 
3
+
3
4
  from classiq.qmod.qmod_parameter import CArray, CBool, CInt, CReal
4
5
  from classiq.qmod.symbolic import symbolic_function
5
6
 
@@ -415,7 +415,7 @@ def inplace_prepare_int(value: CInt, target: QArray[QBit]) -> None:
415
415
  @qfunc(external=True)
416
416
  def prepare_int(
417
417
  value: CInt,
418
- out: Output[QNum[Literal["floor(log(value, 2)) + 1"], Literal[False], Literal[0]]],
418
+ out: Output[QNum[Literal["floor(log(value, 2)) + 1"]]],
419
419
  ) -> None:
420
420
  """
421
421
  [Qmod Classiq-library function]