classiq 0.51.0__py3-none-any.whl → 0.52.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 (150) hide show
  1. classiq/_internals/api_wrapper.py +41 -15
  2. classiq/_internals/authentication/auth0.py +20 -4
  3. classiq/_internals/authentication/password_manager.py +16 -4
  4. classiq/_internals/client.py +2 -2
  5. classiq/_internals/host_checker.py +5 -3
  6. classiq/_internals/jobs.py +3 -3
  7. classiq/analyzer/analyzer_utilities.py +1 -1
  8. classiq/applications/chemistry/ground_state_problem.py +1 -1
  9. classiq/applications/combinatorial_helpers/pyomo_utils.py +3 -1
  10. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  11. classiq/applications/qnn/qlayer.py +2 -2
  12. classiq/execution/__init__.py +3 -0
  13. classiq/execution/execution_session.py +2 -2
  14. classiq/execution/iqcc.py +63 -0
  15. classiq/execution/jobs.py +2 -2
  16. classiq/executor.py +2 -2
  17. classiq/interface/_version.py +1 -1
  18. classiq/interface/analyzer/analysis_params.py +19 -9
  19. classiq/interface/analyzer/cytoscape_graph.py +10 -3
  20. classiq/interface/analyzer/result.py +6 -5
  21. classiq/interface/applications/qsvm.py +13 -12
  22. classiq/interface/backend/backend_preferences.py +78 -105
  23. classiq/interface/backend/ionq/ionq_quantum_program.py +12 -19
  24. classiq/interface/backend/pydantic_backend.py +24 -12
  25. classiq/interface/backend/quantum_backend_providers.py +2 -0
  26. classiq/interface/chemistry/fermionic_operator.py +7 -7
  27. classiq/interface/chemistry/ground_state_problem.py +23 -18
  28. classiq/interface/chemistry/molecule.py +10 -5
  29. classiq/interface/chemistry/operator.py +71 -44
  30. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +2 -1
  31. classiq/interface/debug_info/debug_info.py +3 -4
  32. classiq/interface/execution/iqcc.py +21 -0
  33. classiq/interface/execution/jobs.py +10 -10
  34. classiq/interface/executor/aws_execution_cost.py +37 -20
  35. classiq/interface/executor/execution_preferences.py +1 -2
  36. classiq/interface/executor/execution_request.py +2 -2
  37. classiq/interface/executor/execution_result.py +4 -2
  38. classiq/interface/executor/iqae_result.py +1 -1
  39. classiq/interface/executor/optimizer_preferences.py +14 -10
  40. classiq/interface/executor/quantum_code.py +21 -16
  41. classiq/interface/executor/register_initialization.py +10 -10
  42. classiq/interface/executor/result.py +19 -16
  43. classiq/interface/executor/vqe_result.py +1 -1
  44. classiq/interface/finance/function_input.py +27 -18
  45. classiq/interface/finance/log_normal_model_input.py +2 -2
  46. classiq/interface/finance/model_input.py +3 -2
  47. classiq/interface/generator/amplitude_loading.py +8 -6
  48. classiq/interface/generator/arith/argument_utils.py +24 -0
  49. classiq/interface/generator/arith/arithmetic.py +5 -3
  50. classiq/interface/generator/arith/arithmetic_expression_abc.py +36 -14
  51. classiq/interface/generator/arith/arithmetic_operations.py +6 -3
  52. classiq/interface/generator/arith/binary_ops.py +88 -63
  53. classiq/interface/generator/arith/extremum_operations.py +22 -13
  54. classiq/interface/generator/arith/logical_ops.py +6 -4
  55. classiq/interface/generator/arith/number_utils.py +3 -3
  56. classiq/interface/generator/arith/register_user_input.py +32 -17
  57. classiq/interface/generator/arith/unary_ops.py +5 -4
  58. classiq/interface/generator/chemistry_function_params.py +2 -1
  59. classiq/interface/generator/circuit_code/circuit_code.py +2 -1
  60. classiq/interface/generator/commuting_pauli_exponentiation.py +6 -5
  61. classiq/interface/generator/complex_type.py +14 -18
  62. classiq/interface/generator/control_state.py +32 -26
  63. classiq/interface/generator/expressions/expression.py +6 -5
  64. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +3 -3
  65. classiq/interface/generator/function_params.py +22 -39
  66. classiq/interface/generator/functions/classical_function_declaration.py +1 -1
  67. classiq/interface/generator/functions/classical_type.py +32 -23
  68. classiq/interface/generator/functions/concrete_types.py +8 -7
  69. classiq/interface/generator/functions/function_declaration.py +4 -5
  70. classiq/interface/generator/functions/type_name.py +5 -4
  71. classiq/interface/generator/generated_circuit_data.py +9 -6
  72. classiq/interface/generator/grover_diffuser.py +26 -18
  73. classiq/interface/generator/grover_operator.py +32 -22
  74. classiq/interface/generator/hamiltonian_evolution/exponentiation.py +3 -4
  75. classiq/interface/generator/hamiltonian_evolution/qdrift.py +4 -4
  76. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +8 -7
  77. classiq/interface/generator/hardware/hardware_data.py +27 -26
  78. classiq/interface/generator/hardware_efficient_ansatz.py +11 -6
  79. classiq/interface/generator/hartree_fock.py +2 -1
  80. classiq/interface/generator/identity.py +7 -2
  81. classiq/interface/generator/linear_pauli_rotations.py +27 -14
  82. classiq/interface/generator/mcu.py +15 -12
  83. classiq/interface/generator/mcx.py +18 -10
  84. classiq/interface/generator/model/constraints.py +4 -2
  85. classiq/interface/generator/model/model.py +2 -1
  86. classiq/interface/generator/model/preferences/preferences.py +30 -32
  87. classiq/interface/generator/oracles/custom_oracle.py +13 -10
  88. classiq/interface/generator/piecewise_linear_amplitude_loading.py +37 -21
  89. classiq/interface/generator/qpe.py +38 -26
  90. classiq/interface/generator/qsvm.py +4 -4
  91. classiq/interface/generator/quantum_function_call.py +57 -44
  92. classiq/interface/generator/quantum_program.py +8 -6
  93. classiq/interface/generator/range_types.py +10 -11
  94. classiq/interface/generator/standard_gates/controlled_standard_gates.py +9 -5
  95. classiq/interface/generator/standard_gates/standard_angle_metaclass.py +2 -6
  96. classiq/interface/generator/standard_gates/u_gate.py +7 -10
  97. classiq/interface/generator/state_preparation/computational_basis_state_preparation.py +2 -1
  98. classiq/interface/generator/state_preparation/distributions.py +12 -12
  99. classiq/interface/generator/state_preparation/state_preparation.py +22 -16
  100. classiq/interface/generator/types/enum_declaration.py +2 -1
  101. classiq/interface/generator/ucc.py +2 -1
  102. classiq/interface/generator/unitary_gate.py +2 -1
  103. classiq/interface/generator/user_defined_function_params.py +3 -0
  104. classiq/interface/generator/visitor.py +1 -1
  105. classiq/interface/hardware.py +18 -3
  106. classiq/interface/helpers/custom_pydantic_types.py +38 -47
  107. classiq/interface/helpers/pydantic_model_helpers.py +3 -2
  108. classiq/interface/helpers/versioned_model.py +1 -4
  109. classiq/interface/ide/ide_data.py +5 -5
  110. classiq/interface/ide/visual_model.py +5 -5
  111. classiq/interface/interface_version.py +1 -1
  112. classiq/interface/jobs.py +12 -22
  113. classiq/interface/model/bind_operation.py +2 -1
  114. classiq/interface/model/classical_parameter_declaration.py +10 -4
  115. classiq/interface/model/handle_binding.py +20 -24
  116. classiq/interface/model/inplace_binary_operation.py +16 -9
  117. classiq/interface/model/model.py +21 -11
  118. classiq/interface/model/port_declaration.py +10 -7
  119. classiq/interface/model/quantum_expressions/arithmetic_operation.py +6 -4
  120. classiq/interface/model/quantum_function_declaration.py +22 -11
  121. classiq/interface/model/quantum_statement.py +6 -7
  122. classiq/interface/model/quantum_type.py +22 -19
  123. classiq/interface/model/statement_block.py +9 -9
  124. classiq/interface/server/global_versions.py +4 -5
  125. classiq/interface/server/routes.py +8 -0
  126. classiq/model_expansions/evaluators/parameter_types.py +3 -3
  127. classiq/model_expansions/expression_renamer.py +1 -1
  128. classiq/model_expansions/quantum_operations/control.py +11 -12
  129. classiq/model_expansions/quantum_operations/emitter.py +22 -0
  130. classiq/model_expansions/quantum_operations/expression_operation.py +2 -20
  131. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +65 -14
  132. classiq/model_expansions/quantum_operations/invert.py +1 -1
  133. classiq/model_expansions/quantum_operations/phase.py +4 -5
  134. classiq/model_expansions/quantum_operations/power.py +1 -1
  135. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +50 -9
  136. classiq/model_expansions/quantum_operations/variable_decleration.py +2 -2
  137. classiq/model_expansions/quantum_operations/within_apply.py +1 -1
  138. classiq/qmod/builtins/__init__.py +1 -3
  139. classiq/qmod/builtins/functions/__init__.py +4 -0
  140. classiq/qmod/builtins/functions/arithmetic.py +10 -0
  141. classiq/qmod/create_model_function.py +14 -8
  142. classiq/qmod/quantum_expandable.py +22 -9
  143. classiq/qmod/quantum_function.py +1 -1
  144. classiq/qmod/semantics/static_semantics_visitor.py +3 -1
  145. classiq/qmod/type_attribute_remover.py +1 -1
  146. classiq/qmod/write_qmod.py +2 -4
  147. classiq/synthesis.py +11 -13
  148. {classiq-0.51.0.dist-info → classiq-0.52.0.dist-info}/METADATA +3 -2
  149. {classiq-0.51.0.dist-info → classiq-0.52.0.dist-info}/RECORD +150 -148
  150. {classiq-0.51.0.dist-info → classiq-0.52.0.dist-info}/WHEEL +0 -0
@@ -2,6 +2,9 @@ ANALYZER_PREFIX = "/analyzer"
2
2
  CHEMISTRY_PREFIX = "/chemistry"
3
3
  EXECUTION_PREFIX = "/execution"
4
4
  CONVERSION_PREFIX = "/conversion"
5
+ PROVIDERS_PREFIX = "/providers"
6
+
7
+ IQCC_PREFIX = PROVIDERS_PREFIX + "/iqcc"
5
8
 
6
9
  EXECUTION_NON_VERSIONED_PREFIX = "/execution/v1"
7
10
  SYNTHESIS_NON_VERSIONED_PREFIX = "/synthesis/v1"
@@ -71,3 +74,8 @@ CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_FULL = (
71
74
  )
72
75
 
73
76
  STATIC_SEMANTICS_VALIDATION_PATH = "/validate_static_semantics"
77
+
78
+ IQCC_INIT_AUTH_SUFFIX = "/init_auth"
79
+ IQCC_INIT_AUTH_FULL_PATH = IQCC_PREFIX + IQCC_INIT_AUTH_SUFFIX
80
+ IQCC_PROBE_AUTH_SUFFIX = "/probe_auth"
81
+ IQCC_PROBE_AUTH_FULL_PATH = IQCC_PREFIX + IQCC_PROBE_AUTH_SUFFIX
@@ -118,7 +118,7 @@ def _update_operand_signature_environment(
118
118
  def _cast(
119
119
  quantum_type: QuantumType, quantum_symbol: QuantumSymbol, param_name: str
120
120
  ) -> QuantumSymbol:
121
- updated_quantum_type = quantum_type.copy()
121
+ updated_quantum_type = quantum_type.model_copy()
122
122
  _inject_quantum_arg_info_to_type(updated_quantum_type, quantum_symbol, param_name)
123
123
  return QuantumSymbol(
124
124
  handle=quantum_symbol.handle, quantum_type=updated_quantum_type
@@ -132,13 +132,13 @@ def _evaluate_type_from_arg(
132
132
  return parameter
133
133
 
134
134
  updated_quantum_type: QuantumType = evaluate_type_in_quantum_symbol(
135
- parameter.quantum_type.copy(), inner_scope, parameter.name
135
+ parameter.quantum_type.model_copy(), inner_scope, parameter.name
136
136
  )
137
137
  if parameter.direction != PortDeclarationDirection.Output:
138
138
  updated_quantum_type = _inject_quantum_arg_info_to_type(
139
139
  updated_quantum_type, argument.as_type(QuantumSymbol), parameter.name
140
140
  )
141
- return parameter.copy(update={"quantum_type": updated_quantum_type})
141
+ return parameter.model_copy(update={"quantum_type": updated_quantum_type})
142
142
 
143
143
 
144
144
  def evaluate_type_in_quantum_symbol(
@@ -30,7 +30,7 @@ class ExpressionRenamer(Transformer):
30
30
  update_dict = dict(
31
31
  positional_args=[self.visit(arg) for arg in node.positional_args],
32
32
  )
33
- return node.copy(update=update_dict)
33
+ return node.model_copy(update=update_dict)
34
34
 
35
35
  def visit_Expression(self, expression: Expression) -> Expression:
36
36
  parsed_expr = ast.parse(expression.expr)
@@ -23,7 +23,6 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
23
23
  ArithmeticOperation,
24
24
  ArithmeticOperationKind,
25
25
  )
26
- from classiq.interface.model.quantum_function_call import QuantumFunctionCall
27
26
  from classiq.interface.model.quantum_type import QuantumBit, QuantumBitvector
28
27
  from classiq.interface.model.statement_block import ConcreteQuantumStatement
29
28
  from classiq.interface.model.variable_declaration_statement import (
@@ -43,7 +42,6 @@ from classiq.model_expansions.quantum_operations.expression_operation import (
43
42
  ExpressionOperationEmitter,
44
43
  )
45
44
  from classiq.model_expansions.scope import Scope
46
- from classiq.qmod.builtins.functions import inplace_prepare_int
47
45
 
48
46
  ARRAY_CAST_SUFFIX = HANDLE_ID_SEPARATOR + "array_cast"
49
47
 
@@ -51,7 +49,7 @@ ARRAY_CAST_SUFFIX = HANDLE_ID_SEPARATOR + "array_cast"
51
49
  class ControlEmitter(ExpressionOperationEmitter[Control]):
52
50
  def emit(self, control: Control, /) -> None:
53
51
  condition = self._evaluate_op_expression(control)
54
- control = control.copy(update=dict(expression=condition))
52
+ control = control.model_copy(update=dict(expression=condition))
55
53
 
56
54
  arrays_with_subscript = self._get_symbols_to_split(condition)
57
55
  if len(arrays_with_subscript) > 0:
@@ -76,7 +74,7 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
76
74
  def _emit_propagated(self, control: Control) -> None:
77
75
  if control.is_generative():
78
76
  context = self._register_generative_context(control, CONTROL_OPERATOR_NAME)
79
- control = control.copy(update={"body": context.statements("body")})
77
+ control = control.model_copy(update={"body": context.statements("body")})
80
78
 
81
79
  if self._should_wrap_control(control):
82
80
  self._emit_wrapped(control)
@@ -106,7 +104,7 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
106
104
  )
107
105
  self._update_control_state(control)
108
106
  self._builder.emit_statement(
109
- control.copy(update=dict(body=context.statements("body")))
107
+ control.model_copy(update=dict(body=context.statements("body")))
110
108
  )
111
109
 
112
110
  def _emit_wrapped(self, control: Control) -> None:
@@ -119,7 +117,7 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
119
117
  )
120
118
  self._update_control_state(control)
121
119
  self._builder.emit_statement(
122
- control.copy(update=dict(body=[wrapping_function]))
120
+ control.model_copy(update=dict(body=[wrapping_function]))
123
121
  )
124
122
 
125
123
  @staticmethod
@@ -157,7 +155,7 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
157
155
  self, control: Control, handle_name: str
158
156
  ) -> Control:
159
157
  handle_expr = self._interpreter.evaluate(Expression(expr=handle_name)).emit()
160
- return control.copy(update=dict(expression=handle_expr))
158
+ return control.model_copy(update=dict(expression=handle_expr))
161
159
 
162
160
  def _emit_with_x_gates(
163
161
  self, control: Control, ctrl: QmodSizedProxy, ctrl_state: str
@@ -166,12 +164,13 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
166
164
 
167
165
  x_gate_value = self._get_x_gate_value(ctrl_state)
168
166
  if x_gate_value != 0:
169
- prepare_int_call = QuantumFunctionCall(
170
- function=inplace_prepare_int.func_decl.name,
171
- positional_args=[Expression(expr=str(x_gate_value)), ctrl.handle],
167
+ compute_op.append(
168
+ ArithmeticOperation(
169
+ result_var=ctrl.handle,
170
+ expression=Expression(expr=str(x_gate_value)),
171
+ operation_kind=ArithmeticOperationKind.InplaceXor,
172
+ )
172
173
  )
173
- prepare_int_call.set_func_decl(inplace_prepare_int.func_decl)
174
- compute_op.append(prepare_int_call)
175
174
 
176
175
  if isinstance(ctrl, QmodQNumProxy):
177
176
  # Canonical control does not accept QNum, so we have to cast it
@@ -11,7 +11,13 @@ from typing import (
11
11
  cast,
12
12
  )
13
13
 
14
+ import sympy
15
+
14
16
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
17
+ from classiq.interface.generator.expressions.evaluated_expression import (
18
+ EvaluatedExpression,
19
+ )
20
+ from classiq.interface.generator.expressions.expression import Expression
15
21
  from classiq.interface.ide.visual_model import OperationLevel
16
22
  from classiq.interface.model.classical_parameter_declaration import (
17
23
  ClassicalParameterDeclaration,
@@ -47,6 +53,9 @@ from classiq.model_expansions.function_builder import (
47
53
  )
48
54
  from classiq.model_expansions.generative_functions import emit_operands_as_declarative
49
55
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
56
+ from classiq.model_expansions.sympy_conversion.sympy_to_python import (
57
+ translate_sympy_quantum_expression,
58
+ )
50
59
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
51
60
  from classiq.qmod.builtins.functions import allocate, free
52
61
  from classiq.qmod.quantum_function import GenerativeQFunc
@@ -274,3 +283,16 @@ class Emitter(Generic[QuantumStatementT]):
274
283
  context = self._interpreter._expand_operation(gen_closure)
275
284
  self._generative_contexts[context_name] = context
276
285
  return context
286
+
287
+ def _evaluate_expression(self, expression: Expression) -> Expression:
288
+ evaluated_expression = self._interpreter.evaluate(expression)
289
+ if isinstance(evaluated_expression.value, sympy.Basic):
290
+ new_expression = Expression(
291
+ expr=translate_sympy_quantum_expression(evaluated_expression.value)
292
+ )
293
+ else:
294
+ new_expression = Expression(expr=str(evaluated_expression.value))
295
+ new_expression._evaluated_expr = EvaluatedExpression(
296
+ value=evaluated_expression.value
297
+ )
298
+ return new_expression
@@ -3,8 +3,6 @@ from abc import abstractmethod
3
3
  from itertools import chain
4
4
  from typing import TYPE_CHECKING, Dict, List, Tuple, TypeVar
5
5
 
6
- import sympy
7
-
8
6
  from classiq.interface.exceptions import (
9
7
  ClassiqExpansionError,
10
8
  ClassiqInternalExpansionError,
@@ -36,9 +34,6 @@ from classiq.interface.model.within_apply_operation import WithinApply
36
34
 
37
35
  from classiq.model_expansions.quantum_operations.emitter import Emitter
38
36
  from classiq.model_expansions.scope import QuantumSymbol
39
- from classiq.model_expansions.sympy_conversion.sympy_to_python import (
40
- translate_sympy_quantum_expression,
41
- )
42
37
  from classiq.model_expansions.visitors.variable_references import VarRefCollector
43
38
 
44
39
  ExpressionOperationT = TypeVar("ExpressionOperationT", bound=QuantumExpressionOperation)
@@ -74,7 +69,7 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
74
69
  },
75
70
  expression,
76
71
  )
77
- new_op = op.copy(update=dict(expression=new_expression))
72
+ new_op = op.model_copy(update=dict(expression=new_expression))
78
73
 
79
74
  self._interpreter.emit_statement(
80
75
  WithinApply(
@@ -194,23 +189,10 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
194
189
  def _evaluate_op_expression(self, op: ExpressionOperationT) -> Expression:
195
190
  return self._evaluate_expression(op.expression)
196
191
 
197
- def _evaluate_expression(self, expression: Expression) -> Expression:
198
- evaluated_expression = self._interpreter.evaluate(expression)
199
- if isinstance(evaluated_expression.value, sympy.Basic):
200
- new_expression = Expression(
201
- expr=translate_sympy_quantum_expression(evaluated_expression.value)
202
- )
203
- else:
204
- new_expression = Expression(expr=str(evaluated_expression.value))
205
- new_expression._evaluated_expr = EvaluatedExpression(
206
- value=evaluated_expression.value
207
- )
208
- return new_expression
209
-
210
192
  def _evaluate_types_in_expression(
211
193
  self, op: ExpressionOperationT, expression: Expression
212
194
  ) -> ExpressionOperationT:
213
- op_with_evaluated_types = op.copy(update={"expression": expression})
195
+ op_with_evaluated_types = op.model_copy(update={"expression": expression})
214
196
  vrc = VarRefCollector()
215
197
  vrc.visit(ast.parse(op_with_evaluated_types.expression.expr))
216
198
  handles = vrc.var_handles
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, List, Tuple
1
+ from typing import TYPE_CHECKING, List, Tuple, Union
2
2
 
3
3
  from classiq.interface.generator.expressions.expression import Expression
4
4
  from classiq.interface.generator.functions.port_declaration import (
@@ -35,20 +35,37 @@ 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 CX, allocate, integer_xor, modular_add
38
+ from classiq.qmod.builtins.functions import (
39
+ CX,
40
+ allocate,
41
+ integer_xor,
42
+ modular_add,
43
+ modular_add_constant,
44
+ real_xor_constant,
45
+ )
39
46
 
40
47
 
41
48
  def _binary_function_declaration(
42
- op: BinaryOperation,
49
+ op: BinaryOperation, constant: bool
43
50
  ) -> NamedParamsQuantumFunctionDeclaration:
44
51
  return {
45
- BinaryOperation.Addition: modular_add.func_decl,
46
- BinaryOperation.Xor: integer_xor.func_decl,
47
- }[op]
52
+ False: {
53
+ BinaryOperation.Addition: modular_add.func_decl,
54
+ BinaryOperation.Xor: integer_xor.func_decl,
55
+ },
56
+ True: {
57
+ BinaryOperation.Addition: modular_add_constant.func_decl,
58
+ BinaryOperation.Xor: real_xor_constant.func_decl,
59
+ },
60
+ }[constant][op]
48
61
 
49
62
 
50
63
  class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
51
64
  def emit(self, op: InplaceBinaryOperation, /) -> None:
65
+ if isinstance(op.value, Expression):
66
+ self._emit_constant_operation(op)
67
+ return
68
+
52
69
  value_var = self._interpreter.evaluate(op.value).as_type(QuantumSymbol)
53
70
  target_var = self._interpreter.evaluate(op.target).as_type(QuantumSymbol)
54
71
  value_var, target_var = evaluate_types_in_quantum_symbols(
@@ -95,7 +112,7 @@ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
95
112
  value_var=value_var,
96
113
  target_var=target_var,
97
114
  internal_function_declaration=_binary_function_declaration(
98
- op.operation
115
+ op.operation, constant=False
99
116
  ),
100
117
  ),
101
118
  scope=Scope(parent=self._current_scope),
@@ -105,6 +122,18 @@ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
105
122
  inplace_binary_op_function, [op.value, op.target]
106
123
  )
107
124
 
125
+ def _emit_constant_operation(self, op: InplaceBinaryOperation) -> None:
126
+ if TYPE_CHECKING:
127
+ assert isinstance(op.value, Expression)
128
+ value = self._evaluate_expression(op.value)
129
+ self._interpreter.emit(
130
+ _internal_inplace_binary_operation_function_call(
131
+ _binary_function_declaration(op.operation, constant=True),
132
+ value,
133
+ op.target,
134
+ )
135
+ )
136
+
108
137
 
109
138
  def _build_inplace_binary_operation(
110
139
  value_var: QuantumSymbol,
@@ -164,12 +193,12 @@ def _build_inplace_binary_operation(
164
193
 
165
194
  def _internal_inplace_binary_operation_function_call(
166
195
  internal_function_declaration: NamedParamsQuantumFunctionDeclaration,
167
- value_var: HandleBinding,
196
+ value: Union[HandleBinding, Expression],
168
197
  target_var: HandleBinding,
169
198
  ) -> QuantumFunctionCall:
170
199
  internal_function_call = QuantumFunctionCall(
171
200
  function=internal_function_declaration.name,
172
- positional_args=[value_var, target_var],
201
+ positional_args=[value, target_var],
173
202
  )
174
203
  internal_function_call.set_func_decl(internal_function_declaration)
175
204
  return internal_function_call
@@ -230,10 +259,7 @@ def _pad_with_sign_bit(kind: str, var: QuantumSymbol, size_diff: int) -> Tuple[
230
259
  if not quantum_type.sign_value or size_diff >= 0:
231
260
  return var, [], [], [], []
232
261
 
233
- significand_var, sign_var, sign_split_bind = _split_sign(kind, var)
234
262
  padding_var, padding_allocation = _allocate_padding(kind, size_diff)
235
- padding_init_ops = _init_padding(sign_var, padding_var, size_diff)
236
-
237
263
  padded_var = QuantumSymbol(
238
264
  handle=HandleBinding(name=f"padded_{kind}"),
239
265
  quantum_type=QuantumNumeric(
@@ -242,17 +268,42 @@ def _pad_with_sign_bit(kind: str, var: QuantumSymbol, size_diff: int) -> Tuple[
242
268
  fraction_digits=Expression(expr="0"),
243
269
  ),
244
270
  )
271
+ var_decls = [
272
+ VariableDeclarationStatement(
273
+ name=var.handle.name,
274
+ quantum_type=var.quantum_type,
275
+ )
276
+ for var in (padding_var, padded_var)
277
+ ]
278
+
279
+ if quantum_type.size_in_bits == 1: # qnum<1, SIGNED, ?>
280
+ padding_init_ops = _init_padding(var, padding_var, size_diff)
281
+ padding_rebind = BindOperation(
282
+ in_handles=[var.handle, padding_var.handle],
283
+ out_handles=[padded_var.handle],
284
+ )
285
+ return (
286
+ padded_var,
287
+ var_decls,
288
+ [padding_allocation],
289
+ padding_init_ops,
290
+ [padding_rebind],
291
+ )
292
+
293
+ significand_var, sign_var, sign_split_bind = _split_sign(kind, var)
294
+ padding_init_ops = _init_padding(sign_var, padding_var, size_diff)
295
+
245
296
  padding_rebind = BindOperation(
246
297
  in_handles=[significand_var.handle, sign_var.handle, padding_var.handle],
247
298
  out_handles=[padded_var.handle],
248
299
  )
249
300
 
250
- var_decls = [
301
+ var_decls += [
251
302
  VariableDeclarationStatement(
252
303
  name=var.handle.name,
253
304
  quantum_type=var.quantum_type,
254
305
  )
255
- for var in (significand_var, sign_var, padding_var, padded_var)
306
+ for var in (significand_var, sign_var)
256
307
  ]
257
308
 
258
309
  return (
@@ -16,7 +16,7 @@ class InvertEmitter(Emitter[Invert]):
16
16
  def _emit_propagated(self, invert: Invert, /) -> None:
17
17
  if invert.is_generative():
18
18
  context = self._register_generative_context(invert, INVERT_OPERATOR_NAME)
19
- invert = invert.copy(update={"body": context.statements("body")})
19
+ invert = invert.model_copy(update={"body": context.statements("body")})
20
20
 
21
21
  if self._should_wrap(invert.body):
22
22
  self._emit_wrapped(invert)
@@ -35,19 +35,18 @@ from classiq.qmod.semantics.error_manager import ErrorManager
35
35
  class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
36
36
  def _negate_expression(self, expression: Expression, /) -> Expression:
37
37
  return self._evaluate_expression(
38
- # TODO: change to model_copy for pydantic V2
39
- expression.copy(update=dict(expr=f"-({expression.expr})"))
38
+ expression.model_copy(update=dict(expr=f"-({expression.expr})"))
40
39
  )
41
40
 
42
41
  def emit(self, phase_op: PhaseOperation, /) -> None:
43
42
  phase_expression = self._evaluate_op_expression(phase_op)
44
- phase_op = phase_op.copy(update=dict(expression=phase_expression))
43
+ phase_op = phase_op.model_copy(update=dict(expression=phase_expression))
45
44
  arrays_with_subscript = self._get_symbols_to_split(phase_op.expression)
46
45
  if len(arrays_with_subscript) > 0:
47
46
  self._emit_with_split(phase_op, phase_op.expression, arrays_with_subscript)
48
47
  return
49
- # TODO: change to model_copy for pydantic V2
50
- phase_op = phase_op.copy(
48
+
49
+ phase_op = phase_op.model_copy(
51
50
  update=dict(expression=self._negate_expression(phase_op.expression))
52
51
  )
53
52
  phase_op = self._evaluate_types_in_expression(phase_op, phase_op.expression)
@@ -27,7 +27,7 @@ class PowerEmitter(Emitter[Power]):
27
27
  def _emit_propagated(self, power: Power) -> None:
28
28
  if power.is_generative():
29
29
  context = self._register_generative_context(power, POWER_OPERATOR_NAME)
30
- power = power.copy(update={"body": context.statements("body")})
30
+ power = power.model_copy(update={"body": context.statements("body")})
31
31
 
32
32
  self._power = power
33
33
  self._power_value = self._get_power_value()
@@ -1,7 +1,11 @@
1
1
  import ast
2
+ import re
2
3
  from typing import Tuple
3
4
 
4
- from classiq.interface.exceptions import ClassiqExpansionError
5
+ from classiq.interface.exceptions import (
6
+ ClassiqExpansionError,
7
+ ClassiqInternalExpansionError,
8
+ )
5
9
  from classiq.interface.generator.compiler_keywords import INPLACE_ARITH_AUX_VAR_PREFIX
6
10
  from classiq.interface.generator.expressions.expression import Expression
7
11
  from classiq.interface.model.handle_binding import HandleBinding
@@ -38,13 +42,22 @@ from classiq.qmod import builtins
38
42
  from classiq.qmod.builtins.functions import X, allocate
39
43
 
40
44
 
41
- def _is_zero(expr: str) -> bool:
45
+ def _is_constant(expr: str) -> bool:
42
46
  try:
43
- return float(expr) == 0.0
47
+ float(expr)
48
+ return True
44
49
  except ValueError:
45
50
  return False
46
51
 
47
52
 
53
+ def _is_zero(expr: str) -> bool:
54
+ return _is_constant(expr) and float(expr) == 0
55
+
56
+
57
+ def _is_variable(expr: str) -> bool:
58
+ return re.fullmatch("[a-zA-Z_][a-zA-Z0-9_]*", expr) is not None
59
+
60
+
48
61
  class QuantumAssignmentOperationEmitter(
49
62
  ExpressionOperationEmitter[QuantumAssignmentOperation]
50
63
  ):
@@ -86,6 +99,7 @@ class QuantumAssignmentOperationEmitter(
86
99
  or op.result_type.size_in_bits > 1
87
100
  or not _is_res_boolean(op)
88
101
  or target.quantum_type.size_in_bits > 1
102
+ or _is_constant(expression.expr)
89
103
  ):
90
104
  _validate_naive_inplace_handles(op)
91
105
  self._build_naive_inplace(op, expression)
@@ -131,7 +145,7 @@ class QuantumAssignmentOperationEmitter(
131
145
  expr=ast.unparse(optimizer.visit(ast.parse(expression.expr)))
132
146
  )
133
147
  optimized_expression = self._evaluate_expression(optimized_expression)
134
- optimized_op = op.copy(update=dict(expression=optimized_expression))
148
+ optimized_op = op.model_copy(update=dict(expression=optimized_expression))
135
149
  return optimized_op, optimized_expression, optimizer.is_convertible
136
150
 
137
151
  def _adapt_boolean_inplace(
@@ -141,12 +155,43 @@ class QuantumAssignmentOperationEmitter(
141
155
  adapted_expression = self._evaluate_expression(
142
156
  Expression(expr=ast.unparse(adapter.visit(ast.parse(expression.expr))))
143
157
  )
144
- adapted_op = op.copy(update=dict(expression=adapted_expression))
158
+ adapted_op = op.model_copy(update=dict(expression=adapted_expression))
145
159
  return adapted_op, adapted_expression, adapter.to_invert
146
160
 
147
161
  def _build_naive_inplace(
148
162
  self, qe: ArithmeticOperation, new_expression: Expression
149
163
  ) -> None:
164
+ if qe.operation_kind == ArithmeticOperationKind.InplaceXor:
165
+ op = BinaryOperation.Xor
166
+ elif qe.operation_kind == ArithmeticOperationKind.InplaceAdd:
167
+ op = BinaryOperation.Addition
168
+ else:
169
+ raise ClassiqInternalExpansionError(
170
+ f"Unrecognized operation kind {qe.operation_kind!r}"
171
+ )
172
+
173
+ if _is_constant(new_expression.expr):
174
+ self._interpreter.emit_statement(
175
+ InplaceBinaryOperation(
176
+ operation=op,
177
+ target=qe.result_var,
178
+ value=new_expression,
179
+ )
180
+ )
181
+ return
182
+
183
+ if _is_variable(new_expression.expr):
184
+ value_var = self._interpreter.evaluate(new_expression.expr).value
185
+ if isinstance(value_var, QuantumSymbol):
186
+ self._interpreter.emit_statement(
187
+ InplaceBinaryOperation(
188
+ operation=op,
189
+ target=qe.result_var,
190
+ value=value_var.handle,
191
+ )
192
+ )
193
+ return
194
+
150
195
  aux_var = self._counted_name_allocator.allocate(INPLACE_ARITH_AUX_VAR_PREFIX)
151
196
  self._interpreter.emit_statement(
152
197
  VariableDeclarationStatement(name=aux_var, quantum_type=QuantumNumeric())
@@ -156,10 +201,6 @@ class QuantumAssignmentOperationEmitter(
156
201
  expression=new_expression,
157
202
  operation_kind=ArithmeticOperationKind.Assignment,
158
203
  )
159
- if qe.operation_kind == ArithmeticOperationKind.InplaceXor:
160
- op = BinaryOperation.Xor
161
- else:
162
- op = BinaryOperation.Addition
163
204
  inplace_store = InplaceBinaryOperation(
164
205
  operation=op,
165
206
  target=qe.result_var,
@@ -12,8 +12,8 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol
12
12
 
13
13
  class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement]):
14
14
  def emit(self, variable_declaration: VariableDeclarationStatement, /) -> None:
15
- var_decl = variable_declaration.copy()
16
- var_decl.quantum_type = variable_declaration.quantum_type.copy()
15
+ var_decl = variable_declaration.model_copy()
16
+ var_decl.quantum_type = variable_declaration.quantum_type.model_copy()
17
17
  self._current_scope[variable_declaration.name] = Evaluated(
18
18
  value=QuantumSymbol(
19
19
  handle=HandleBinding(name=var_decl.name),
@@ -19,7 +19,7 @@ class WithinApplyEmitter(Emitter[WithinApply]):
19
19
  within_apply_context = self._register_generative_context(
20
20
  within_apply, WITHIN_APPLY_NAME, ["within", "apply"]
21
21
  )
22
- within_apply = within_apply.copy(
22
+ within_apply = within_apply.model_copy(
23
23
  update={
24
24
  "compute": within_apply_context.statements("within"),
25
25
  "action": within_apply_context.statements("apply"),
@@ -17,9 +17,7 @@ from .operations import __all__ as _builtin_operations
17
17
  from .structs import * # noqa: F403
18
18
  from .structs import __all__ as _builtin_structs
19
19
 
20
- FinanceFunctionInput.update_forward_refs(
21
- FinanceFunctionType=FinanceFunctionType # noqa: F405
22
- )
20
+ FinanceFunctionInput.model_rebuild()
23
21
  BUILTIN_CONSTANTS = [
24
22
  constant._get_constant_node()
25
23
  for constant in [
@@ -69,6 +69,8 @@ CORE_LIB_DECLS = [
69
69
  add,
70
70
  modular_add,
71
71
  integer_xor,
72
+ modular_add_constant,
73
+ real_xor_constant,
72
74
  U,
73
75
  CCX,
74
76
  allocate,
@@ -192,6 +194,8 @@ __all__ = [
192
194
  "add",
193
195
  "modular_add",
194
196
  "integer_xor",
197
+ "modular_add_constant",
198
+ "real_xor_constant",
195
199
  "U",
196
200
  "CCX",
197
201
  "allocate",
@@ -43,11 +43,21 @@ def modular_add(left: QArray[QBit], right: QArray[QBit]) -> None:
43
43
  pass
44
44
 
45
45
 
46
+ @qfunc(external=True)
47
+ def modular_add_constant(left: CReal, right: QNum) -> None:
48
+ pass
49
+
50
+
46
51
  @qfunc(external=True)
47
52
  def integer_xor(left: QArray[QBit], right: QArray[QBit]) -> None:
48
53
  pass
49
54
 
50
55
 
56
+ @qfunc(external=True)
57
+ def real_xor_constant(left: CReal, right: QNum) -> None:
58
+ pass
59
+
60
+
51
61
  @qfunc(external=True)
52
62
  def modular_increment(a: CInt, x: QNum) -> None:
53
63
  """
@@ -106,7 +106,7 @@ def _expand_generative_model(
106
106
  preferences,
107
107
  classical_execution_function,
108
108
  )
109
- generative_functions = _get_generative_functions(gen_main)
109
+ generative_functions = _get_generative_functions(gen_main, preferences)
110
110
  model.functions = generative_functions
111
111
  model.types = list(QMODULE.type_decls.values())
112
112
  model.enums = list(QMODULE.enum_decls.values())
@@ -114,20 +114,25 @@ def _expand_generative_model(
114
114
  return model
115
115
 
116
116
 
117
- def _get_generative_functions(gen_main: QFunc) -> List[NativeFunctionDefinition]:
117
+ def _get_generative_functions(
118
+ gen_main: QFunc, preferences: Optional[Preferences]
119
+ ) -> List[NativeFunctionDefinition]:
118
120
  # The Interpreter accepts a model and a list of generative functions.
119
121
  # Since the main function is generative, it can only be expanded using the
120
122
  # Interpreter.
121
123
  # To solve this deadlock, we create a wrapper model
122
124
  # `qfunc main(...) { _gen_main(...); }` and rename `main` to `_gen_main` before
123
125
  # passing them to the Interpreter.
124
- gen_model = _get_wrapper_main(gen_main)
126
+ gen_model = _get_wrapper_main(gen_main, preferences)
125
127
  gen_functions = _get_all_model_functions_as_generative_functions()
126
128
  functions_dict = _interpret_generative_model(gen_model, gen_functions)
127
129
  return list(functions_dict.values())
128
130
 
129
131
 
130
- def _get_wrapper_main(gen_main: QFunc) -> Model:
132
+ def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Model:
133
+ extra_args = {}
134
+ if preferences is not None:
135
+ extra_args["preferences"] = preferences
131
136
  return Model(
132
137
  functions=[
133
138
  NativeFunctionDefinition(
@@ -145,6 +150,7 @@ def _get_wrapper_main(gen_main: QFunc) -> Model:
145
150
  ],
146
151
  ),
147
152
  ],
153
+ **extra_args,
148
154
  )
149
155
 
150
156
 
@@ -159,7 +165,7 @@ def _get_all_model_functions_as_generative_functions() -> List[GenerativeQFunc]:
159
165
  if gen_func.func_decl.name != MAIN_FUNCTION_NAME
160
166
  else GenerativeQFunc(
161
167
  gen_func._py_callable,
162
- gen_func.func_decl.copy(update={"name": GEN_MAIN_NAME}),
168
+ gen_func.func_decl.model_copy(update={"name": GEN_MAIN_NAME}),
163
169
  )
164
170
  )
165
171
  for gen_func in gen_functions
@@ -182,9 +188,9 @@ def _interpret_generative_model(
182
188
  expanded_gen_main_name = cast(
183
189
  QuantumFunctionCall, functions_dict[MAIN_FUNCTION_NAME].body[0]
184
190
  ).func_name
185
- functions_dict[MAIN_FUNCTION_NAME] = functions_dict[expanded_gen_main_name].copy(
186
- update={"name": MAIN_FUNCTION_NAME}
187
- )
191
+ functions_dict[MAIN_FUNCTION_NAME] = functions_dict[
192
+ expanded_gen_main_name
193
+ ].model_copy(update={"name": MAIN_FUNCTION_NAME})
188
194
  functions_dict.pop(expanded_gen_main_name)
189
195
 
190
196
  return functions_dict