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.
Files changed (71) hide show
  1. classiq/_internals/api_wrapper.py +45 -8
  2. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -7
  3. classiq/applications/grover/grover_model_constructor.py +2 -1
  4. classiq/execution/execution_session.py +133 -45
  5. classiq/execution/jobs.py +120 -1
  6. classiq/interface/_version.py +1 -1
  7. classiq/interface/backend/quantum_backend_providers.py +0 -1
  8. classiq/interface/debug_info/debug_info.py +23 -1
  9. classiq/interface/execution/primitives.py +17 -0
  10. classiq/interface/executor/iqae_result.py +3 -3
  11. classiq/interface/executor/result.py +3 -1
  12. classiq/interface/generator/arith/arithmetic_operations.py +5 -2
  13. classiq/interface/generator/arith/binary_ops.py +21 -14
  14. classiq/interface/generator/arith/extremum_operations.py +9 -1
  15. classiq/interface/generator/arith/number_utils.py +6 -0
  16. classiq/interface/generator/arith/register_user_input.py +30 -21
  17. classiq/interface/generator/arith/unary_ops.py +13 -1
  18. classiq/interface/generator/expressions/expression.py +8 -0
  19. classiq/interface/generator/functions/type_name.py +1 -3
  20. classiq/interface/generator/generated_circuit_data.py +47 -2
  21. classiq/interface/generator/quantum_program.py +10 -2
  22. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +17 -3
  23. classiq/interface/ide/visual_model.py +10 -5
  24. classiq/interface/interface_version.py +1 -1
  25. classiq/interface/model/bind_operation.py +0 -3
  26. classiq/interface/model/phase_operation.py +11 -0
  27. classiq/interface/model/port_declaration.py +1 -12
  28. classiq/interface/model/quantum_expressions/arithmetic_operation.py +34 -6
  29. classiq/interface/model/quantum_lambda_function.py +4 -1
  30. classiq/interface/model/quantum_statement.py +16 -1
  31. classiq/interface/model/quantum_variable_declaration.py +0 -22
  32. classiq/interface/model/statement_block.py +3 -0
  33. classiq/interface/server/global_versions.py +4 -4
  34. classiq/interface/server/routes.py +0 -3
  35. classiq/model_expansions/capturing/propagated_var_stack.py +5 -2
  36. classiq/model_expansions/closure.py +7 -2
  37. classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
  38. classiq/model_expansions/generative_functions.py +146 -28
  39. classiq/model_expansions/interpreter.py +17 -5
  40. classiq/model_expansions/quantum_operations/classicalif.py +27 -10
  41. classiq/model_expansions/quantum_operations/control.py +22 -15
  42. classiq/model_expansions/quantum_operations/emitter.py +68 -7
  43. classiq/model_expansions/quantum_operations/expression_operation.py +25 -16
  44. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +167 -95
  45. classiq/model_expansions/quantum_operations/invert.py +12 -6
  46. classiq/model_expansions/quantum_operations/phase.py +189 -0
  47. classiq/model_expansions/quantum_operations/power.py +9 -8
  48. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +20 -5
  49. classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
  50. classiq/model_expansions/quantum_operations/repeat.py +32 -13
  51. classiq/model_expansions/quantum_operations/within_apply.py +19 -6
  52. classiq/model_expansions/scope.py +16 -5
  53. classiq/model_expansions/scope_initialization.py +11 -1
  54. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +23 -1
  55. classiq/model_expansions/visitors/variable_references.py +11 -7
  56. classiq/qmod/builtins/__init__.py +10 -0
  57. classiq/qmod/builtins/constants.py +10 -0
  58. classiq/qmod/builtins/functions/state_preparation.py +4 -1
  59. classiq/qmod/builtins/operations.py +55 -161
  60. classiq/qmod/create_model_function.py +1 -1
  61. classiq/qmod/generative.py +14 -5
  62. classiq/qmod/native/pretty_printer.py +14 -4
  63. classiq/qmod/pretty_print/pretty_printer.py +14 -4
  64. classiq/qmod/qmod_constant.py +28 -18
  65. classiq/qmod/qmod_variable.py +43 -23
  66. classiq/qmod/quantum_expandable.py +14 -1
  67. classiq/qmod/semantics/static_semantics_visitor.py +10 -0
  68. classiq/qmod/semantics/validation/constants_validation.py +16 -0
  69. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/METADATA +9 -4
  70. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/RECORD +71 -66
  71. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,15 @@
1
1
  from abc import abstractmethod
2
- from typing import TYPE_CHECKING, Generic, List, Sequence, TypeVar, cast
2
+ from typing import (
3
+ TYPE_CHECKING,
4
+ Dict,
5
+ Generic,
6
+ List,
7
+ Optional,
8
+ Sequence,
9
+ TypeVar,
10
+ Union,
11
+ cast,
12
+ )
3
13
 
4
14
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
5
15
  from classiq.interface.ide.visual_model import OperationLevel
@@ -14,7 +24,7 @@ from classiq.interface.model.quantum_function_declaration import (
14
24
  NamedParamsQuantumFunctionDeclaration,
15
25
  PositionalArg,
16
26
  )
17
- from classiq.interface.model.quantum_statement import QuantumStatement
27
+ from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
18
28
  from classiq.interface.model.variable_declaration_statement import (
19
29
  VariableDeclarationStatement,
20
30
  )
@@ -23,7 +33,7 @@ from classiq.model_expansions.capturing.propagated_var_stack import (
23
33
  PropagatedVarStack,
24
34
  validate_args_are_not_propagated,
25
35
  )
26
- from classiq.model_expansions.closure import FunctionClosure
36
+ from classiq.model_expansions.closure import Closure, FunctionClosure, GenerativeClosure
27
37
  from classiq.model_expansions.evaluators.argument_types import (
28
38
  add_information_from_output_arguments,
29
39
  )
@@ -33,10 +43,13 @@ from classiq.model_expansions.evaluators.parameter_types import (
33
43
  from classiq.model_expansions.function_builder import (
34
44
  FunctionContext,
35
45
  OperationBuilder,
46
+ OperationContext,
36
47
  )
48
+ from classiq.model_expansions.generative_functions import emit_operands_as_declarative
37
49
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
38
50
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
39
51
  from classiq.qmod.builtins.functions import allocate, free
52
+ from classiq.qmod.quantum_function import GenerativeQFunc
40
53
 
41
54
  if TYPE_CHECKING:
42
55
  from classiq.model_expansions.interpreter import Interpreter
@@ -50,13 +63,24 @@ class Emitter(Generic[QuantumStatementT]):
50
63
  self._interpreter = interpreter
51
64
 
52
65
  self._scope_guard = self._interpreter._scope_guard
53
- self._expand_operation = self._interpreter._expand_operation
54
66
  self._machine_precision = self._interpreter._model.preferences.machine_precision
55
67
 
68
+ self._generative_contexts: Dict[str, OperationContext] = {}
69
+
56
70
  @abstractmethod
57
71
  def emit(self, statement: QuantumStatementT, /) -> None:
58
72
  pass
59
73
 
74
+ def _expand_operation(self, closure: Closure) -> OperationContext:
75
+ if closure.name in self._generative_contexts:
76
+ if isinstance(closure, FunctionClosure):
77
+ return FunctionContext(
78
+ closure=closure,
79
+ blocks=self._generative_contexts[closure.name].blocks,
80
+ )
81
+ return self._generative_contexts[closure.name]
82
+ return self._interpreter._expand_operation(closure)
83
+
60
84
  @property
61
85
  def _propagated_var_stack(self) -> PropagatedVarStack:
62
86
  return self._interpreter._propagated_var_stack
@@ -131,16 +155,22 @@ class Emitter(Generic[QuantumStatementT]):
131
155
  or new_call.func_name == free.func_decl.name
132
156
  )
133
157
  parameters = {
134
- arg_decl.name: FunctionDebugInfo.param_controller(value=valuated_arg.value)
135
- for arg_decl, valuated_arg in zip(new_positional_arg_decls, evaluated_args)
158
+ arg_decl.name: FunctionDebugInfo.param_controller(value=evaluated_arg.value)
159
+ for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
136
160
  if isinstance(arg_decl, ClassicalParameterDeclaration)
137
161
  }
138
162
 
163
+ port_to_passed_variable_map = {
164
+ arg_decl.name: evaluated_arg.value.handle.name
165
+ for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
166
+ if isinstance(arg_decl, PortDeclaration)
167
+ }
139
168
  self._interpreter._model.debug_info[new_call.uuid] = FunctionDebugInfo(
140
169
  name=new_call.func_name,
141
170
  level=OperationLevel.QMOD_FUNCTION_CALL,
142
171
  parameters=parameters,
143
172
  is_allocate_or_free=is_allocate_or_free,
173
+ port_to_passed_variable_map=port_to_passed_variable_map,
144
174
  )
145
175
  if is_atomic:
146
176
  new_call.set_func_decl(new_declaration)
@@ -175,7 +205,10 @@ class Emitter(Generic[QuantumStatementT]):
175
205
  new_positional_arg_decls, evaluated_args
176
206
  )
177
207
  if is_atomic:
178
- return [arg.emit() for arg in evaluated_args]
208
+ return [
209
+ emit_operands_as_declarative(self._interpreter, param, arg)
210
+ for param, arg in zip(new_positional_arg_decls, evaluated_args)
211
+ ]
179
212
 
180
213
  positional_args = [
181
214
  arg.emit() for arg in evaluated_args if isinstance(arg.value, QuantumSymbol)
@@ -213,3 +246,31 @@ class Emitter(Generic[QuantumStatementT]):
213
246
  evaluated_args,
214
247
  ),
215
248
  )
249
+
250
+ def _register_generative_context(
251
+ self,
252
+ op: QuantumOperation,
253
+ context_name: str,
254
+ block_names: Union[None, str, List[str]] = None,
255
+ func_decl: Optional[NamedParamsQuantumFunctionDeclaration] = None,
256
+ ) -> OperationContext:
257
+ if isinstance(block_names, str):
258
+ block_names = [block_names]
259
+ block_names = block_names or ["body"]
260
+ func_decl = func_decl or NamedParamsQuantumFunctionDeclaration(
261
+ name=context_name
262
+ )
263
+ gen_closure = GenerativeClosure(
264
+ name=func_decl.name,
265
+ scope=Scope(parent=self._interpreter._current_scope),
266
+ blocks={},
267
+ generative_blocks={
268
+ block_name: GenerativeQFunc(
269
+ op.get_generative_block(block_name), func_decl
270
+ )
271
+ for block_name in block_names
272
+ },
273
+ )
274
+ context = self._interpreter._expand_operation(gen_closure)
275
+ self._generative_contexts[context_name] = context
276
+ return context
@@ -58,17 +58,20 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
58
58
  symbols_parts, bind_ops = self._get_bind_ops(symbols_to_split)
59
59
 
60
60
  for symbol_parts in symbols_parts:
61
- for symbol in symbol_parts:
61
+ for symbol, symbol_part_var_name in symbol_parts:
62
62
  if symbol.handle.identifier not in self._current_scope:
63
63
  self._interpreter.emit_statement(
64
64
  VariableDeclarationStatement(
65
- name=symbol.handle.identifier,
65
+ name=symbol_part_var_name,
66
66
  quantum_type=symbol.quantum_type,
67
67
  )
68
68
  )
69
69
 
70
70
  new_expression = self._update_op_expression(
71
- {symbol.handle: symbol for symbol in chain.from_iterable(symbols_parts)},
71
+ {
72
+ symbol.handle: symbol_part_var_name
73
+ for symbol, symbol_part_var_name in chain.from_iterable(symbols_parts)
74
+ },
72
75
  expression,
73
76
  )
74
77
  new_op = op.copy(update=dict(expression=new_expression))
@@ -83,7 +86,7 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
83
86
 
84
87
  def _update_op_expression(
85
88
  self,
86
- symbol_parts: Dict[HandleBinding, QuantumSymbol],
89
+ symbol_parts: Dict[HandleBinding, str],
87
90
  expression: Expression,
88
91
  ) -> Expression:
89
92
  vrc = VarRefCollector(ignore_duplicated_handles=True)
@@ -94,7 +97,7 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
94
97
  collapsed_handle = handle.collapse()
95
98
  if collapsed_handle in symbol_parts:
96
99
  new_expr_str = new_expr_str.replace(
97
- str(handle), symbol_parts[collapsed_handle].handle.identifier
100
+ str(handle), symbol_parts[collapsed_handle]
98
101
  )
99
102
  self._check_all_handles_were_replaced(new_expr_str)
100
103
 
@@ -115,32 +118,38 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
115
118
  ):
116
119
  raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
117
120
 
118
- @staticmethod
119
121
  def _get_bind_ops(
122
+ self,
120
123
  symbols_to_split: List[QuantumSymbol],
121
- ) -> Tuple[List[List[QuantumSymbol]], List[BindOperation]]:
124
+ ) -> Tuple[List[List[Tuple[QuantumSymbol, str]]], List[BindOperation]]:
122
125
  bind_ops = []
123
126
  symbols_parts = []
124
127
  for symbol in symbols_to_split:
125
- symbol_parts = ExpressionOperationEmitter._get_symbol_parts(symbol)
128
+ symbol_parts = self._get_symbol_parts(symbol)
126
129
  symbols_parts.append(symbol_parts)
127
130
  bind_ops.append(
128
131
  BindOperation(
129
132
  in_handles=[symbol.handle],
130
133
  out_handles=[
131
- HandleBinding(name=symbol_part.handle.identifier)
132
- for symbol_part in symbol_parts
134
+ HandleBinding(name=symbol_part_var_name)
135
+ for _, symbol_part_var_name in symbol_parts
133
136
  ],
134
137
  )
135
138
  )
136
139
  return symbols_parts, bind_ops
137
140
 
138
- @staticmethod
139
- def _get_symbol_parts(symbol: QuantumSymbol) -> List[QuantumSymbol]:
141
+ def _get_symbol_parts(
142
+ self, symbol: QuantumSymbol
143
+ ) -> List[Tuple[QuantumSymbol, str]]:
140
144
  quantum_type = symbol.quantum_type
141
145
 
142
146
  if isinstance(quantum_type, (QuantumBit, QuantumNumeric)):
143
- return [symbol]
147
+ return [
148
+ (
149
+ symbol,
150
+ self._counted_name_allocator.allocate(symbol.handle.identifier),
151
+ )
152
+ ]
144
153
 
145
154
  if isinstance(quantum_type, QuantumBitvector):
146
155
  if not quantum_type.has_length:
@@ -150,7 +159,7 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
150
159
  )
151
160
  return list(
152
161
  chain.from_iterable(
153
- ExpressionOperationEmitter._get_symbol_parts(symbol[idx])
162
+ self._get_symbol_parts(symbol[idx])
154
163
  for idx in range(quantum_type.length_value)
155
164
  )
156
165
  )
@@ -160,7 +169,7 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
160
169
 
161
170
  return list(
162
171
  chain.from_iterable(
163
- ExpressionOperationEmitter._get_symbol_parts(field_symbol)
172
+ self._get_symbol_parts(field_symbol)
164
173
  for field_symbol in symbol.fields.values()
165
174
  )
166
175
  )
@@ -204,7 +213,7 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
204
213
  op_with_evaluated_types = op.copy(update={"expression": expression})
205
214
  vrc = VarRefCollector()
206
215
  vrc.visit(ast.parse(op_with_evaluated_types.expression.expr))
207
- handles = list(vrc.var_handles)
216
+ handles = vrc.var_handles
208
217
  op_with_evaluated_types.set_var_handles(handles)
209
218
  op_with_evaluated_types.initialize_var_types(
210
219
  {
@@ -1,6 +1,5 @@
1
- from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple
1
+ from typing import TYPE_CHECKING, List, Tuple
2
2
 
3
- from classiq.interface.exceptions import ClassiqInternalExpansionError
4
3
  from classiq.interface.generator.expressions.expression import Expression
5
4
  from classiq.interface.generator.functions.port_declaration import (
6
5
  PortDeclarationDirection,
@@ -25,6 +24,7 @@ from classiq.interface.model.quantum_type import (
25
24
  from classiq.interface.model.variable_declaration_statement import (
26
25
  VariableDeclarationStatement,
27
26
  )
27
+ from classiq.interface.model.within_apply_operation import WithinApply
28
28
 
29
29
  from classiq.model_expansions.closure import FunctionClosure
30
30
  from classiq.model_expansions.evaluators.parameter_types import (
@@ -35,7 +35,7 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
35
35
  )
36
36
  from classiq.model_expansions.quantum_operations.emitter import Emitter
37
37
  from classiq.model_expansions.scope import QuantumSymbol, Scope
38
- from classiq.qmod.builtins.functions import integer_xor, modular_add
38
+ from classiq.qmod.builtins.functions import CX, allocate, integer_xor, modular_add
39
39
 
40
40
 
41
41
  def _binary_function_declaration(
@@ -59,16 +59,13 @@ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
59
59
  assert isinstance(value_var.quantum_type, QuantumNumeric)
60
60
  assert isinstance(target_var.quantum_type, QuantumNumeric)
61
61
 
62
- sign_diff = int(value_var.quantum_type.sign_value) - int(
63
- target_var.quantum_type.sign_value
64
- )
65
62
  frac_digits_diff = (
66
63
  value_var.quantum_type.fraction_digits_value
67
64
  - target_var.quantum_type.fraction_digits_value
68
65
  )
69
66
  if (
70
- sign_diff + frac_digits_diff == value_var.quantum_type.size_in_bits
71
- or -sign_diff - frac_digits_diff == target_var.quantum_type.size_in_bits
67
+ frac_digits_diff == value_var.quantum_type.size_in_bits
68
+ or -frac_digits_diff == target_var.quantum_type.size_in_bits
72
69
  ):
73
70
  with self._propagated_var_stack.capture_variables(op):
74
71
  return
@@ -97,7 +94,6 @@ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
97
94
  body=_build_inplace_binary_operation(
98
95
  value_var=value_var,
99
96
  target_var=target_var,
100
- frac_digits_diff=frac_digits_diff,
101
97
  internal_function_declaration=_binary_function_declaration(
102
98
  op.operation
103
99
  ),
@@ -113,53 +109,60 @@ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
113
109
  def _build_inplace_binary_operation(
114
110
  value_var: QuantumSymbol,
115
111
  target_var: QuantumSymbol,
116
- frac_digits_diff: int,
117
112
  internal_function_declaration: NamedParamsQuantumFunctionDeclaration,
118
113
  ) -> List[QuantumStatement]:
119
114
  if TYPE_CHECKING:
120
115
  assert isinstance(value_var.quantum_type, QuantumNumeric)
121
116
  assert isinstance(target_var.quantum_type, QuantumNumeric)
122
117
 
123
- value_overlap_var, value_sign_var, value_bind_targets = _get_inplace_bind_targets(
124
- "value", value_var, frac_digits_diff
118
+ frac_digits_diff = (
119
+ value_var.quantum_type.fraction_digits_value
120
+ - target_var.quantum_type.fraction_digits_value
125
121
  )
126
- target_overlap_var, target_sign_var, target_bind_targets = (
127
- _get_inplace_bind_targets("target", target_var, -frac_digits_diff)
122
+ size_diff = (
123
+ value_var.quantum_type.size_in_bits - target_var.quantum_type.size_in_bits
128
124
  )
129
125
 
130
- value_pre_ops, value_post_ops = _get_inplace_pre_post_ops(
131
- value_var, value_bind_targets
126
+ target_overlap_var, target_var_decls, target_bind_ops = (
127
+ _trim_superfluous_fraction_digits("target", target_var, -frac_digits_diff)
132
128
  )
133
- target_pre_ops, target_post_ops = _get_inplace_pre_post_ops(
134
- target_var, target_bind_targets
129
+ value_overlap_var, value_trim_var_decls, value_bind_ops = (
130
+ _trim_superfluous_fraction_digits("value", value_var, frac_digits_diff)
135
131
  )
132
+ (
133
+ value_padded_var,
134
+ value_pad_var_decls,
135
+ value_pad_pre_bind_ops,
136
+ value_pad_init_ops,
137
+ value_post_bind_ops,
138
+ ) = _pad_with_sign_bit("value", value_overlap_var, size_diff)
136
139
 
137
- binary_ops = []
138
- if value_overlap_var is not None and target_overlap_var is not None:
139
- binary_ops.append(
140
- _internal_inplace_binary_operation_function_call(
141
- internal_function_declaration,
142
- value_overlap_var.handle,
143
- target_overlap_var.handle,
144
- )
145
- )
146
- if value_sign_var is not None and target_sign_var is not None:
147
- binary_ops.append(
148
- _internal_inplace_binary_operation_function_call(
149
- internal_function_declaration,
150
- value_sign_var.handle,
151
- target_sign_var.handle,
152
- )
153
- )
154
- if len(binary_ops) == 0:
155
- raise ClassiqInternalExpansionError("Bug in unrolling inplace operation")
140
+ op_call = _internal_inplace_binary_operation_function_call(
141
+ internal_function_declaration,
142
+ value_padded_var.handle,
143
+ target_overlap_var.handle,
144
+ )
156
145
 
157
146
  return [
158
- *value_pre_ops,
159
- *target_pre_ops,
160
- *binary_ops,
161
- *target_post_ops,
162
- *value_post_ops,
147
+ *target_var_decls,
148
+ *value_trim_var_decls,
149
+ *value_pad_var_decls,
150
+ WithinApply(
151
+ compute=[
152
+ *target_bind_ops,
153
+ *value_bind_ops,
154
+ *value_pad_pre_bind_ops,
155
+ ],
156
+ action=[
157
+ *value_pad_init_ops,
158
+ WithinApply(
159
+ compute=[
160
+ *value_post_bind_ops,
161
+ ],
162
+ action=[op_call],
163
+ ),
164
+ ],
165
+ ),
163
166
  ]
164
167
 
165
168
 
@@ -176,75 +179,144 @@ def _internal_inplace_binary_operation_function_call(
176
179
  return internal_function_call
177
180
 
178
181
 
179
- def _get_inplace_bind_targets(
182
+ def _trim_superfluous_fraction_digits(
180
183
  kind: str, var: QuantumSymbol, frac_digits_diff: int
181
- ) -> Tuple[Optional[QuantumSymbol], Optional[QuantumSymbol], List[QuantumSymbol]]:
184
+ ) -> Tuple[QuantumSymbol, List[VariableDeclarationStatement], List[BindOperation]]:
185
+ if frac_digits_diff <= 0:
186
+ return var, [], []
187
+
182
188
  quantum_type = var.quantum_type
183
189
  if TYPE_CHECKING:
184
190
  assert isinstance(quantum_type, QuantumNumeric)
185
191
 
186
- if not quantum_type.sign_value and frac_digits_diff <= 0:
187
- return var, None, []
188
-
189
- significand_overlap = (
190
- quantum_type.size_in_bits
191
- - quantum_type.fraction_digits_value
192
- - int(quantum_type.sign_value)
192
+ trimmed_fraction_digits_var = QuantumSymbol(
193
+ handle=HandleBinding(name=f"trimmed_{kind}_fraction_digits"),
194
+ quantum_type=QuantumBitvector(
195
+ length=Expression(expr=str(frac_digits_diff)),
196
+ ),
193
197
  )
194
- fraction_overlap = quantum_type.fraction_digits_value - max(0, frac_digits_diff)
195
- if significand_overlap + fraction_overlap == 0 and quantum_type.size_in_bits == 1:
196
- assert quantum_type.sign_value
197
- return None, var, []
198
-
199
- bind_targets = []
198
+ overlap_var = QuantumSymbol(
199
+ handle=HandleBinding(name=f"{kind}_overlap"),
200
+ quantum_type=QuantumNumeric(
201
+ size=Expression(expr=str(quantum_type.size_in_bits - frac_digits_diff)),
202
+ is_signed=quantum_type.is_signed,
203
+ fraction_digits=Expression(expr="0"),
204
+ ),
205
+ )
206
+ bind_targets = trimmed_fraction_digits_var, overlap_var
200
207
 
201
- if frac_digits_diff > 0:
202
- bind_targets.append(
203
- QuantumSymbol(
204
- handle=HandleBinding(name=f"trimmed_{kind}_fraction_digits"),
205
- quantum_type=QuantumBitvector(
206
- length=Expression(expr=str(frac_digits_diff)),
207
- ),
208
- )
208
+ split_var_declarations = [
209
+ VariableDeclarationStatement(
210
+ name=var.handle.name,
211
+ quantum_type=var.quantum_type,
209
212
  )
213
+ for var in bind_targets
214
+ ]
215
+ bind_op = BindOperation(
216
+ in_handles=[var.handle],
217
+ out_handles=[var.handle for var in bind_targets],
218
+ )
210
219
 
211
- overlap_var = None
212
- if significand_overlap + fraction_overlap > 0:
213
- overlap_var = QuantumSymbol(
214
- handle=HandleBinding(name=f"{kind}_overlap"),
215
- quantum_type=QuantumNumeric(
216
- size=Expression(expr=str(significand_overlap + fraction_overlap)),
217
- is_signed=Expression(expr="False"),
218
- fraction_digits=Expression(expr=str(fraction_overlap)),
219
- ),
220
- )
221
- bind_targets.append(overlap_var)
220
+ return overlap_var, split_var_declarations, [bind_op]
222
221
 
223
- sign_var = None
224
- if quantum_type.sign_value:
225
- sign_var = QuantumSymbol(
226
- handle=HandleBinding(name=f"trimmed_{kind}_sign"),
227
- quantum_type=QuantumBit(),
228
- )
229
- bind_targets.append(sign_var)
230
222
 
231
- return overlap_var, sign_var, bind_targets
223
+ def _pad_with_sign_bit(kind: str, var: QuantumSymbol, size_diff: int) -> Tuple[
224
+ QuantumSymbol,
225
+ List[VariableDeclarationStatement],
226
+ List[QuantumStatement],
227
+ List[QuantumFunctionCall],
228
+ List[BindOperation],
229
+ ]:
230
+ quantum_type = var.quantum_type
231
+ if TYPE_CHECKING:
232
+ assert isinstance(quantum_type, QuantumNumeric)
232
233
 
234
+ if not quantum_type.sign_value or size_diff >= 0:
235
+ return var, [], [], [], []
233
236
 
234
- def _get_inplace_pre_post_ops(
235
- var: QuantumSymbol, bind_targets: List[QuantumSymbol]
236
- ) -> Tuple[Sequence[QuantumStatement], Sequence[QuantumStatement]]:
237
- if len(bind_targets) == 0:
238
- return [], []
237
+ significand_var, sign_var, sign_split_bind = _split_sign(kind, var)
238
+ padding_var, padding_allocation = _allocate_padding(kind, size_diff)
239
+ padding_init_ops = _init_padding(sign_var, padding_var, size_diff)
239
240
 
240
- value_bind_op = BindOperation(
241
- in_handles=[var.handle],
242
- out_handles=[var.handle for var in bind_targets],
241
+ padded_var = QuantumSymbol(
242
+ handle=HandleBinding(name=f"padded_{kind}"),
243
+ quantum_type=QuantumNumeric(
244
+ size=Expression(expr=str(quantum_type.size_in_bits - size_diff)),
245
+ is_signed=Expression(expr="False"),
246
+ fraction_digits=Expression(expr="0"),
247
+ ),
243
248
  )
244
- return [
249
+ padding_rebind = BindOperation(
250
+ in_handles=[significand_var.handle, sign_var.handle, padding_var.handle],
251
+ out_handles=[padded_var.handle],
252
+ )
253
+
254
+ var_decls = [
245
255
  VariableDeclarationStatement(
246
256
  name=var.handle.name,
247
257
  quantum_type=var.quantum_type,
248
258
  )
249
- for var in bind_targets
250
- ] + [value_bind_op], [value_bind_op.reversed()]
259
+ for var in (significand_var, sign_var, padding_var, padded_var)
260
+ ]
261
+
262
+ return (
263
+ padded_var,
264
+ var_decls,
265
+ [sign_split_bind, padding_allocation],
266
+ padding_init_ops,
267
+ [padding_rebind],
268
+ )
269
+
270
+
271
+ def _init_padding(
272
+ sign_var: QuantumSymbol, padding_var: QuantumSymbol, size_diff: int
273
+ ) -> List[QuantumFunctionCall]:
274
+ padding_init_ops = [
275
+ QuantumFunctionCall(
276
+ function=CX.func_decl.name,
277
+ positional_args=[sign_var.handle, padding_var[idx].handle],
278
+ )
279
+ for idx in range(-size_diff)
280
+ ]
281
+ for cx_call in padding_init_ops:
282
+ cx_call.set_func_decl(CX.func_decl)
283
+ return padding_init_ops
284
+
285
+
286
+ def _allocate_padding(
287
+ kind: str, size_diff: int
288
+ ) -> Tuple[QuantumSymbol, QuantumFunctionCall]:
289
+ padding_var = QuantumSymbol(
290
+ handle=HandleBinding(name=f"{kind}_sign_padding"),
291
+ quantum_type=QuantumBitvector(
292
+ length=Expression(expr=str(-size_diff)),
293
+ ),
294
+ )
295
+ padding_allocation = QuantumFunctionCall(
296
+ function=allocate.func_decl.name,
297
+ positional_args=[Expression(expr=str(-size_diff)), padding_var.handle],
298
+ )
299
+ padding_allocation.set_func_decl(allocate.func_decl)
300
+ return padding_var, padding_allocation
301
+
302
+
303
+ def _split_sign(
304
+ kind: str, var: QuantumSymbol
305
+ ) -> Tuple[QuantumSymbol, QuantumSymbol, BindOperation]:
306
+ significand_var = QuantumSymbol(
307
+ handle=HandleBinding(name=f"{kind}_significand"),
308
+ quantum_type=QuantumNumeric(
309
+ size=Expression(expr=str(var.quantum_type.size_in_bits - 1)),
310
+ is_signed=Expression(expr="False"),
311
+ fraction_digits=Expression(expr="0"),
312
+ ),
313
+ )
314
+ sign_var = QuantumSymbol(
315
+ handle=HandleBinding(name=f"{kind}_sign_bit"),
316
+ quantum_type=QuantumBit(),
317
+ )
318
+ sign_split_bind = BindOperation(
319
+ in_handles=[var.handle],
320
+ out_handles=[significand_var.handle, sign_var.handle],
321
+ )
322
+ return significand_var, sign_var, sign_split_bind
@@ -10,6 +10,14 @@ from classiq.model_expansions.scope import Scope
10
10
 
11
11
  class InvertEmitter(Emitter[Invert]):
12
12
  def emit(self, invert: Invert, /) -> None:
13
+ with self._propagated_var_stack.capture_variables(invert):
14
+ self._emit_propagated(invert)
15
+
16
+ def _emit_propagated(self, invert: Invert, /) -> None:
17
+ if invert.is_generative():
18
+ context = self._register_generative_context(invert, INVERT_OPERATOR_NAME)
19
+ invert = invert.copy(update={"body": context.statements("body")})
20
+
13
21
  if self._should_wrap(invert.body):
14
22
  self._emit_wrapped(invert)
15
23
  return
@@ -22,17 +30,15 @@ class InvertEmitter(Emitter[Invert]):
22
30
  blocks={"body": invert.body},
23
31
  scope=Scope(parent=self._current_scope),
24
32
  )
25
- with self._propagated_var_stack.capture_variables(invert):
26
- context = self._expand_operation(invert_operation)
33
+ context = self._expand_operation(invert_operation)
27
34
  self._builder.emit_statement(
28
35
  Invert(body=context.statements("body"), source_ref=invert.source_ref)
29
36
  )
30
37
 
31
38
  def _emit_wrapped(self, invert: Invert) -> None:
32
- with self._propagated_var_stack.capture_variables(invert):
33
- wrapping_function = self._create_expanded_wrapping_function(
34
- INVERT_OPERATOR_NAME, invert.body
35
- )
39
+ wrapping_function = self._create_expanded_wrapping_function(
40
+ INVERT_OPERATOR_NAME, invert.body
41
+ )
36
42
  self._builder.emit_statement(
37
43
  Invert(body=[wrapping_function], source_ref=invert.source_ref)
38
44
  )