classiq 0.60.0__py3-none-any.whl → 0.61.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 (98) hide show
  1. classiq/__init__.py +2 -0
  2. classiq/_internals/client.py +28 -1
  3. classiq/applications/__init__.py +1 -1
  4. classiq/applications/chemistry/__init__.py +7 -7
  5. classiq/applications/chemistry/chemistry_model_constructor.py +17 -6
  6. classiq/applications/combinatorial_optimization/__init__.py +7 -1
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  8. classiq/applications/combinatorial_optimization/combinatorial_problem.py +197 -0
  9. classiq/applications/finance/finance_model_constructor.py +6 -6
  10. classiq/applications/grover/grover_model_constructor.py +3 -0
  11. classiq/applications/libraries/qmci_library.py +1 -10
  12. classiq/applications/qnn/__init__.py +1 -1
  13. classiq/applications/qnn/datasets/__init__.py +8 -8
  14. classiq/applications/qsvm/qsvm.py +1 -1
  15. classiq/execution/__init__.py +0 -2
  16. classiq/execution/execution_session.py +6 -0
  17. classiq/executor.py +1 -1
  18. classiq/interface/_version.py +1 -1
  19. classiq/interface/backend/backend_preferences.py +12 -12
  20. classiq/interface/executor/execution_preferences.py +1 -1
  21. classiq/interface/generator/application_apis/chemistry_declarations.py +1 -1
  22. classiq/interface/generator/application_apis/finance_declarations.py +2 -2
  23. classiq/interface/generator/arith/arithmetic.py +16 -1
  24. classiq/interface/generator/arith/arithmetic_expression_validator.py +4 -3
  25. classiq/interface/generator/expressions/expression_constants.py +3 -0
  26. classiq/interface/generator/generated_circuit_data.py +58 -20
  27. classiq/interface/generator/model/__init__.py +1 -1
  28. classiq/interface/generator/model/quantum_register.py +3 -3
  29. classiq/interface/generator/standard_gates/controlled_standard_gates.py +20 -32
  30. classiq/interface/ide/visual_model.py +1 -0
  31. classiq/interface/interface_version.py +1 -1
  32. classiq/interface/model/model.py +2 -3
  33. classiq/interface/model/quantum_function_call.py +4 -7
  34. classiq/interface/model/quantum_function_declaration.py +7 -0
  35. classiq/interface/model/quantum_lambda_function.py +10 -1
  36. classiq/interface/model/quantum_type.py +3 -1
  37. classiq/model_expansions/atomic_expression_functions_defs.py +3 -1
  38. classiq/model_expansions/capturing/captured_vars.py +24 -8
  39. classiq/model_expansions/capturing/mangling_utils.py +23 -15
  40. classiq/model_expansions/evaluators/arg_type_match.py +7 -7
  41. classiq/model_expansions/expression_evaluator.py +5 -2
  42. classiq/model_expansions/function_builder.py +21 -4
  43. classiq/model_expansions/generative_functions.py +12 -90
  44. classiq/model_expansions/interpreter.py +58 -11
  45. classiq/model_expansions/quantum_operations/call_emitter.py +19 -10
  46. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  47. classiq/model_expansions/quantum_operations/control.py +5 -31
  48. classiq/model_expansions/quantum_operations/emitter.py +27 -14
  49. classiq/model_expansions/quantum_operations/expression_operation.py +3 -5
  50. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +57 -15
  51. classiq/model_expansions/quantum_operations/invert.py +1 -6
  52. classiq/model_expansions/quantum_operations/phase.py +2 -5
  53. classiq/model_expansions/quantum_operations/power.py +0 -4
  54. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +19 -30
  55. classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -1
  56. classiq/model_expansions/quantum_operations/shallow_emitter.py +155 -0
  57. classiq/model_expansions/quantum_operations/within_apply.py +0 -14
  58. classiq/model_expansions/scope.py +10 -4
  59. classiq/model_expansions/scope_initialization.py +0 -11
  60. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +7 -0
  61. classiq/model_expansions/sympy_conversion/sympy_to_python.py +12 -2
  62. classiq/model_expansions/transformers/ast_renamer.py +26 -0
  63. classiq/model_expansions/transformers/var_splitter.py +11 -12
  64. classiq/model_expansions/visitors/variable_references.py +20 -12
  65. classiq/qmod/builtins/classical_execution_primitives.py +6 -6
  66. classiq/qmod/builtins/classical_functions.py +10 -10
  67. classiq/qmod/builtins/functions/__init__.py +89 -103
  68. classiq/qmod/builtins/functions/amplitude_estimation.py +1 -1
  69. classiq/qmod/builtins/functions/arithmetic.py +1 -1
  70. classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +6 -6
  71. classiq/qmod/builtins/functions/grover.py +5 -5
  72. classiq/qmod/builtins/functions/hea.py +1 -1
  73. classiq/qmod/builtins/functions/linear_pauli_rotation.py +2 -2
  74. classiq/qmod/builtins/functions/modular_exponentiation.py +8 -8
  75. classiq/qmod/builtins/functions/operators.py +1 -1
  76. classiq/qmod/builtins/functions/qaoa_penalty.py +5 -5
  77. classiq/qmod/builtins/functions/qft_functions.py +2 -2
  78. classiq/qmod/builtins/functions/qpe.py +9 -12
  79. classiq/qmod/builtins/functions/qsvt.py +177 -15
  80. classiq/qmod/builtins/functions/state_preparation.py +9 -9
  81. classiq/qmod/builtins/functions/swap_test.py +1 -1
  82. classiq/qmod/builtins/functions/utility_functions.py +2 -2
  83. classiq/qmod/builtins/functions/variational.py +2 -2
  84. classiq/qmod/builtins/operations.py +3 -3
  85. classiq/qmod/builtins/structs.py +9 -9
  86. classiq/qmod/native/pretty_printer.py +17 -19
  87. classiq/qmod/pretty_print/pretty_printer.py +9 -6
  88. classiq/qmod/qmod_variable.py +2 -5
  89. classiq/qmod/quantum_expandable.py +18 -4
  90. classiq/qmod/quantum_function.py +19 -6
  91. classiq/qmod/semantics/static_semantics_visitor.py +34 -16
  92. classiq/qmod/semantics/validation/func_call_validation.py +9 -5
  93. classiq/qmod/semantics/validation/function_name_collisions_validation.py +23 -0
  94. classiq/qmod/symbolic.py +47 -47
  95. {classiq-0.60.0.dist-info → classiq-0.61.0.dist-info}/METADATA +1 -1
  96. {classiq-0.60.0.dist-info → classiq-0.61.0.dist-info}/RECORD +97 -94
  97. classiq/execution/qaoa.py +0 -86
  98. {classiq-0.60.0.dist-info → classiq-0.61.0.dist-info}/WHEEL +0 -0
@@ -7,7 +7,7 @@ from classiq.interface.generator.functions.port_declaration import (
7
7
  )
8
8
  from classiq.interface.model.bind_operation import BindOperation
9
9
  from classiq.interface.model.control import Control
10
- from classiq.interface.model.handle_binding import HandleBinding
10
+ from classiq.interface.model.handle_binding import HandleBinding, SubscriptHandleBinding
11
11
  from classiq.interface.model.inplace_binary_operation import (
12
12
  BinaryOperation,
13
13
  InplaceBinaryOperation,
@@ -19,6 +19,7 @@ from classiq.interface.model.quantum_function_declaration import (
19
19
  )
20
20
  from classiq.interface.model.quantum_statement import QuantumStatement
21
21
  from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
22
+ from classiq.interface.model.repeat import Repeat
22
23
  from classiq.interface.model.variable_declaration_statement import (
23
24
  VariableDeclarationStatement,
24
25
  )
@@ -33,10 +34,11 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
33
34
  )
34
35
  from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
35
36
  from classiq.model_expansions.scope import QuantumSymbol, Scope
37
+ from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
36
38
  from classiq.qmod.builtins.functions import (
37
39
  CX,
40
+ X,
38
41
  allocate,
39
- apply_to_all,
40
42
  integer_xor,
41
43
  modular_add,
42
44
  modular_add_constant,
@@ -97,6 +99,7 @@ class InplaceBinaryOperationEmitter(CallEmitter[InplaceBinaryOperation]):
97
99
  body = _build_inplace_xor_operation(
98
100
  value_var=value_var,
99
101
  target_var=target_var,
102
+ name_allocator=self._interpreter._counted_name_allocator,
100
103
  )
101
104
  else:
102
105
  body = _build_inplace_binary_operation(
@@ -128,11 +131,10 @@ class InplaceBinaryOperationEmitter(CallEmitter[InplaceBinaryOperation]):
128
131
  def _emit_constant_operation(self, op: InplaceBinaryOperation) -> None:
129
132
  if TYPE_CHECKING:
130
133
  assert isinstance(op.value, Expression)
131
- value = self._evaluate_expression(op.value)
132
134
  self._interpreter.emit(
133
135
  _internal_inplace_binary_operation_function_call(
134
136
  _binary_function_declaration(op.operation, constant=True),
135
- value,
137
+ op.value,
136
138
  op.target,
137
139
  )
138
140
  )
@@ -196,7 +198,9 @@ def _build_inplace_binary_operation(
196
198
 
197
199
 
198
200
  def _build_inplace_xor_operation(
199
- value_var: QuantumSymbol, target_var: QuantumSymbol
201
+ value_var: QuantumSymbol,
202
+ target_var: QuantumSymbol,
203
+ name_allocator: CountedNameAllocator,
200
204
  ) -> list[QuantumStatement]:
201
205
  if TYPE_CHECKING:
202
206
  assert isinstance(value_var.quantum_type, QuantumNumeric)
@@ -214,7 +218,7 @@ def _build_inplace_xor_operation(
214
218
  _trim_superfluous_fraction_digits("value", value_var, frac_digits_diff)
215
219
  )
216
220
  target_left_var, value_left_var, sign_var_decls, sign_bind_ops, sign_xor = (
217
- _split_and_xor_sign(target_overlap_var, value_overlap_var)
221
+ _split_and_xor_sign(target_overlap_var, value_overlap_var, name_allocator)
218
222
  )
219
223
 
220
224
  action: list[QuantumStatement] = []
@@ -425,7 +429,11 @@ def _split_var(
425
429
  return left_var, right_var, split_bind
426
430
 
427
431
 
428
- def _split_and_xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) -> tuple[
432
+ def _split_and_xor_sign(
433
+ target_var: QuantumSymbol,
434
+ value_var: QuantumSymbol,
435
+ name_allocator: CountedNameAllocator,
436
+ ) -> tuple[
429
437
  Optional[QuantumSymbol],
430
438
  Optional[QuantumSymbol],
431
439
  list[VariableDeclarationStatement],
@@ -442,7 +450,7 @@ def _split_and_xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) ->
442
450
  return target_var, value_var, [], [], []
443
451
 
444
452
  if value_var.quantum_type.size_in_bits == 1:
445
- return None, None, [], [], [_xor_sign(target_var, value_var)]
453
+ return None, None, [], [], [_xor_sign(target_var, value_var, name_allocator)]
446
454
 
447
455
  value_rest_var, value_sign_var, value_split_bind = _split_var("value", value_var, 1)
448
456
  target_left_var, target_right_var, target_split_bind = _split_var(
@@ -456,11 +464,15 @@ def _split_and_xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) ->
456
464
  for var in (value_rest_var, value_sign_var, target_left_var, target_right_var)
457
465
  ]
458
466
  bind_ops = [value_split_bind, target_split_bind]
459
- sign_xor = _xor_sign(target_right_var, value_sign_var)
467
+ sign_xor = _xor_sign(target_right_var, value_sign_var, name_allocator)
460
468
  return target_left_var, value_rest_var, var_decls, bind_ops, [sign_xor]
461
469
 
462
470
 
463
- def _xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) -> Control:
471
+ def _xor_sign(
472
+ target_var: QuantumSymbol,
473
+ value_var: QuantumSymbol,
474
+ name_allocator: CountedNameAllocator,
475
+ ) -> Control:
464
476
  quantum_type = value_var.quantum_type
465
477
  if TYPE_CHECKING:
466
478
  assert isinstance(quantum_type, QuantumNumeric)
@@ -470,11 +482,33 @@ def _xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) -> Control:
470
482
  ):
471
483
  raise ClassiqInternalExpansionError
472
484
 
473
- inner_xor = QuantumFunctionCall(
474
- function=apply_to_all.func_decl.name,
475
- positional_args=["X", target_var.handle],
485
+ aux_var = name_allocator.allocate("inplace_xor_aux")
486
+ iteration_var = name_allocator.allocate("i")
487
+ inner_x_call = QuantumFunctionCall(
488
+ function=X.func_decl.name,
489
+ positional_args=[
490
+ SubscriptHandleBinding(
491
+ base_handle=HandleBinding(name=aux_var),
492
+ index=Expression(expr=iteration_var),
493
+ )
494
+ ],
495
+ )
496
+ inner_x_call.set_func_decl(X.func_decl)
497
+ inner_xor = WithinApply(
498
+ compute=[
499
+ BindOperation(
500
+ in_handles=[target_var.handle],
501
+ out_handles=[HandleBinding(name=aux_var)],
502
+ ),
503
+ ],
504
+ action=[
505
+ Repeat(
506
+ iter_var=iteration_var,
507
+ count=Expression(expr=f"{target_var.quantum_type.size_in_bits}"),
508
+ body=[inner_x_call],
509
+ )
510
+ ],
476
511
  )
477
- inner_xor.set_func_decl(apply_to_all.func_decl)
478
512
 
479
513
  if quantum_type.sign_value:
480
514
  if quantum_type.fraction_digits_value == 1:
@@ -489,5 +523,13 @@ def _xor_sign(target_var: QuantumSymbol, value_var: QuantumSymbol) -> Control:
489
523
 
490
524
  return Control(
491
525
  expression=Expression(expr=f"{value_var.handle} == {ctrl_value}"),
492
- body=[inner_xor],
526
+ body=[
527
+ VariableDeclarationStatement(
528
+ name=aux_var,
529
+ quantum_type=QuantumBitvector(
530
+ length=Expression(expr=f"{target_var.quantum_type.size_in_bits}")
531
+ ),
532
+ ),
533
+ inner_xor,
534
+ ],
493
535
  )
@@ -10,12 +10,7 @@ from classiq.model_expansions.scope import Scope
10
10
 
11
11
  class InvertEmitter(CallEmitter[Invert]):
12
12
  def emit(self, invert: Invert, /) -> None:
13
- is_generative = invert.is_generative()
14
- if is_generative:
15
- context = self._register_generative_context(invert, INVERT_OPERATOR_NAME)
16
- invert = invert.model_copy(update={"body": context.statements("body")})
17
-
18
- if not is_generative and self._should_wrap(invert.body):
13
+ if self._should_wrap(invert.body):
19
14
  self._emit_wrapped(invert)
20
15
  return
21
16
 
@@ -39,8 +39,6 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
39
39
  )
40
40
 
41
41
  def emit(self, phase_op: PhaseOperation, /) -> None:
42
- phase_expression = self._evaluate_op_expression(phase_op)
43
- phase_op = phase_op.model_copy(update=dict(expression=phase_expression))
44
42
  arrays_with_subscript = self.split_symbols(
45
43
  phase_op.expression, self._counted_name_allocator.allocate
46
44
  )
@@ -51,7 +49,7 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
51
49
  phase_op = phase_op.model_copy(
52
50
  update=dict(expression=self._negate_expression(phase_op.expression))
53
51
  )
54
- phase_op = self._evaluate_types_in_expression(phase_op, phase_op.expression)
52
+ phase_op = self._evaluate_types_in_expression(phase_op)
55
53
  if len(phase_op.var_handles) == 0:
56
54
  ErrorManager().add_error(
57
55
  "Cannot perform phase operation on an expression with no quantum variables."
@@ -65,12 +63,11 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
65
63
  else:
66
64
  split_join = False
67
65
  evolution_variable = phase_op.var_handles[0]
68
- expression = self._evaluate_op_expression(phase_op)
69
66
  expression_evolution_function = QuantumFunctionCall(
70
67
  function=suzuki_trotter.func_decl.name,
71
68
  positional_args=[
72
69
  _convert_cost_expression_to_hamiltonian(
73
- expression.expr,
70
+ phase_op.expression.expr,
74
71
  {
75
72
  var.name: self._current_scope[var.name].value.quantum_type
76
73
  for var in phase_op.var_handles
@@ -22,10 +22,6 @@ class PowerEmitter(CallEmitter[Power]):
22
22
  _power_expr: Expression
23
23
 
24
24
  def emit(self, power: Power, /) -> None:
25
- if power.is_generative():
26
- context = self._register_generative_context(power, POWER_OPERATOR_NAME)
27
- power = power.model_copy(update={"body": context.statements("body")})
28
-
29
25
  self._power = power
30
26
  self._power_value = self._get_power_value()
31
27
  self._power_expr = Expression(
@@ -5,6 +5,10 @@ from classiq.interface.exceptions import (
5
5
  ClassiqExpansionError,
6
6
  ClassiqInternalExpansionError,
7
7
  )
8
+ from classiq.interface.generator.arith.arithmetic import is_zero
9
+ from classiq.interface.generator.arith.arithmetic_expression_validator import (
10
+ is_constant,
11
+ )
8
12
  from classiq.interface.generator.compiler_keywords import INPLACE_ARITH_AUX_VAR_PREFIX
9
13
  from classiq.interface.generator.expressions.expression import Expression
10
14
  from classiq.interface.model.handle_binding import HandleBinding
@@ -45,18 +49,6 @@ HANDLE_ERROR_MESSAGE = (
45
49
  )
46
50
 
47
51
 
48
- def _is_constant(expr: str) -> bool:
49
- try:
50
- float(expr)
51
- return True
52
- except ValueError:
53
- return False
54
-
55
-
56
- def _is_zero(expr: str) -> bool:
57
- return _is_constant(expr) and float(expr) == 0
58
-
59
-
60
52
  def _is_variable(expr: str) -> bool:
61
53
  return re.fullmatch("[a-zA-Z_][a-zA-Z0-9_]*", expr) is not None
62
54
 
@@ -65,29 +57,26 @@ class QuantumAssignmentOperationEmitter(
65
57
  ExpressionOperationEmitter[QuantumAssignmentOperation]
66
58
  ):
67
59
  def emit(self, op: QuantumAssignmentOperation, /) -> None:
68
- new_expression = self._evaluate_op_expression(op)
69
- if self._skip_assignment(op, new_expression.expr):
60
+ if self._skip_assignment(op, op.expression.expr):
70
61
  return
71
62
  arrays_with_subscript = self.split_symbols(
72
- new_expression, self._counted_name_allocator.allocate
63
+ op.expression, self._counted_name_allocator.allocate
73
64
  )
74
65
  if len(arrays_with_subscript) > 0:
75
- self._emit_with_split(op, new_expression, arrays_with_subscript)
66
+ self._emit_with_split(op, op.expression, arrays_with_subscript)
76
67
  return
77
68
 
78
- self._emit_op(new_expression, op)
69
+ self._emit_op(op)
79
70
 
80
- def _emit_op(self, expression: Expression, op: QuantumAssignmentOperation) -> None:
81
- op = self._evaluate_types_in_expression(op, expression)
71
+ def _emit_op(self, op: QuantumAssignmentOperation) -> None:
72
+ op = self._evaluate_types_in_expression(op)
82
73
  if isinstance(op, ArithmeticOperation):
83
- self._emit_arithmetic_op(op, expression)
74
+ self._emit_arithmetic_op(op)
84
75
  return
85
76
  self._emit_general_assignment_operation(op)
86
77
 
87
- def _emit_arithmetic_op(
88
- self, op: ArithmeticOperation, expression: Expression
89
- ) -> None:
90
- op, expression, is_bool_opt = self._optimize_boolean_expression(op, expression)
78
+ def _emit_arithmetic_op(self, op: ArithmeticOperation) -> None:
79
+ op, expression, is_bool_opt = self._optimize_boolean_expression(op)
91
80
  if op.is_inplace:
92
81
  self._emit_inplace_arithmetic_op(op, expression, is_bool_opt)
93
82
  else:
@@ -102,7 +91,7 @@ class QuantumAssignmentOperationEmitter(
102
91
  or op.result_type.size_in_bits > 1
103
92
  or not self._is_res_boolean(op)
104
93
  or target.quantum_type.size_in_bits > 1
105
- or _is_constant(expression.expr)
94
+ or is_constant(expression.expr)
106
95
  ):
107
96
  _validate_naive_inplace_handles(op)
108
97
  self._build_naive_inplace(op, expression)
@@ -131,7 +120,7 @@ class QuantumAssignmentOperationEmitter(
131
120
  self.emit_statement(op)
132
121
 
133
122
  def _optimize_boolean_expression(
134
- self, op: ArithmeticOperation, expression: Expression
123
+ self, op: ArithmeticOperation
135
124
  ) -> tuple[ArithmeticOperation, Expression, bool]:
136
125
  if (
137
126
  self._interpreter._is_frontend
@@ -143,10 +132,10 @@ class QuantumAssignmentOperationEmitter(
143
132
  or not self._all_vars_boolean(op)
144
133
  or not self._is_res_boolean(op)
145
134
  ):
146
- return op, expression, False
135
+ return op, op.expression, False
147
136
  optimizer = BooleanExpressionOptimizer()
148
137
  optimized_expression = Expression(
149
- expr=ast.unparse(optimizer.visit(ast.parse(expression.expr)))
138
+ expr=ast.unparse(optimizer.visit(ast.parse(op.expression.expr)))
150
139
  )
151
140
  optimized_expression = self._evaluate_expression(optimized_expression)
152
141
  optimized_op = op.model_copy(update=dict(expression=optimized_expression))
@@ -174,7 +163,7 @@ class QuantumAssignmentOperationEmitter(
174
163
  f"Unrecognized operation kind {qe.operation_kind!r}"
175
164
  )
176
165
 
177
- if _is_constant(new_expression.expr):
166
+ if is_constant(new_expression.expr):
178
167
  self._interpreter.emit_statement(
179
168
  InplaceBinaryOperation(
180
169
  operation=op,
@@ -215,7 +204,7 @@ class QuantumAssignmentOperationEmitter(
215
204
  )
216
205
 
217
206
  def _skip_assignment(self, op: QuantumAssignmentOperation, expr: str) -> bool:
218
- if not isinstance(op, ArithmeticOperation) or not _is_zero(expr):
207
+ if not isinstance(op, ArithmeticOperation) or not is_zero(expr):
219
208
  return False
220
209
  if op.operation_kind != ArithmeticOperationKind.Assignment:
221
210
  return True
@@ -19,4 +19,6 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
19
19
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
20
20
  args = call.positional_args
21
21
  with ErrorManager().call(function.name), function.freeze():
22
- self._emit_quantum_function_call(function, args)
22
+ self._emit_quantum_function_call(
23
+ function, args, self._debug_info.get(call.uuid)
24
+ )
@@ -0,0 +1,155 @@
1
+ import ast
2
+ from typing import TYPE_CHECKING, Any, Optional, TypeVar
3
+
4
+ from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
5
+ from classiq.interface.generator.expressions.expression import Expression
6
+ from classiq.interface.generator.functions.port_declaration import (
7
+ PortDeclarationDirection,
8
+ )
9
+ from classiq.interface.model.handle_binding import HandleBinding
10
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
11
+ ArithmeticOperation,
12
+ ArithmeticOperationKind,
13
+ )
14
+ from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
15
+
16
+ from classiq.model_expansions.closure import Closure
17
+ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
18
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
19
+ from classiq.model_expansions.scope import QuantumSymbol, Scope
20
+ from classiq.model_expansions.transformers.ast_renamer import rename_variables
21
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
22
+
23
+ if TYPE_CHECKING:
24
+ from classiq.model_expansions.interpreter import Interpreter
25
+
26
+
27
+ QuantumOperationT = TypeVar("QuantumOperationT", bound=QuantumOperation)
28
+ _BLOCK_RENAMES = {
29
+ "compute": "within",
30
+ "action": "apply",
31
+ }
32
+ _REVERSE_BLOCK_RENAMES = {rename: name for name, rename in _BLOCK_RENAMES.items()}
33
+
34
+
35
+ class ShallowEmitter(Emitter[QuantumOperation]):
36
+ def __init__(
37
+ self,
38
+ interpreter: "Interpreter",
39
+ operation_name: str,
40
+ *,
41
+ components: Optional[list[str]] = None,
42
+ ) -> None:
43
+ super().__init__(interpreter)
44
+ self._operation_name = operation_name
45
+ self._components: list[str] = components or []
46
+
47
+ def emit(self, op: QuantumOperation, /) -> None:
48
+ expanded_components: dict[str, Any] = {}
49
+ blocks, expressions, handles = self._split_components(op)
50
+
51
+ if len(blocks) > 0:
52
+ if op.is_generative():
53
+ expanded_blocks = self.expand_generative_blocks(op)
54
+ else:
55
+ expanded_blocks = self.expand_blocks(op, blocks)
56
+ expanded_components.update(expanded_blocks)
57
+
58
+ for expression_name in expressions:
59
+ expression = getattr(op, expression_name)
60
+ expression = self._evaluate_expression(expression, preserve_bool_ops=True)
61
+ self.capture_handles_in_expression(expression)
62
+ expanded_components[expression_name] = expression
63
+
64
+ for handle_name in handles:
65
+ handle = getattr(op, handle_name)
66
+ expanded_components[handle_name] = self._interpreter.evaluate(
67
+ handle
68
+ ).value.handle
69
+
70
+ op = op.model_copy(update=expanded_components)
71
+ if isinstance(op, ArithmeticOperation):
72
+ self._post_process_assignment(op)
73
+ self._builder.emit_statement(op)
74
+
75
+ def _post_process_assignment(self, op: ArithmeticOperation) -> None:
76
+ if op.operation_kind == ArithmeticOperationKind.Assignment:
77
+ direction = PortDeclarationDirection.Output
78
+ self._update_result_type(op)
79
+ else:
80
+ direction = PortDeclarationDirection.Inout
81
+ self._capture_handle(op.result_var, direction)
82
+
83
+ def _split_components(
84
+ self, op: QuantumOperation
85
+ ) -> tuple[list[str], list[str], list[str]]:
86
+ blocks = self._filter_components(op, list)
87
+ expressions = self._filter_components(op, Expression)
88
+ handles = self._filter_components(op, HandleBinding)
89
+ return blocks, expressions, handles
90
+
91
+ def _filter_components(
92
+ self, op: QuantumOperation, component_type: type
93
+ ) -> list[str]:
94
+ return [
95
+ component
96
+ for component in self._components
97
+ if isinstance(getattr(op, component, None), component_type)
98
+ ]
99
+
100
+ def expand_blocks(
101
+ self, op: QuantumOperation, block_names: list[str]
102
+ ) -> dict[str, list[QuantumStatement]]:
103
+ blocks = {
104
+ _BLOCK_RENAMES.get(block, block): block_statements
105
+ for block in block_names
106
+ if (block_statements := getattr(op, block)) is not None
107
+ }
108
+ block_closure = Closure(
109
+ name=self._operation_name,
110
+ scope=Scope(parent=self._current_scope),
111
+ blocks=blocks,
112
+ )
113
+ context = self._expand_operation(block_closure)
114
+ return {
115
+ block: context.statements(_BLOCK_RENAMES.get(block, block))
116
+ for block in block_names
117
+ }
118
+
119
+ def expand_generative_blocks(
120
+ self, op: QuantumOperation
121
+ ) -> dict[str, list[QuantumStatement]]:
122
+ blocks = [block for block in self._components if op.has_generative_block(block)]
123
+ context = self._expand_generative_context(op, self._operation_name, blocks)
124
+ return {
125
+ _REVERSE_BLOCK_RENAMES.get(block, block): context.statements(block)
126
+ for block in blocks
127
+ }
128
+
129
+ def _update_result_type(self, op: ArithmeticOperation) -> None:
130
+ expr = str(self._evaluate_expression(op.expression))
131
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
132
+ vrc.visit(ast.parse(expr))
133
+ symbols: list[QuantumSymbol] = [
134
+ symbol
135
+ for handle in vrc.var_handles
136
+ if isinstance(
137
+ symbol := self._interpreter.evaluate(handle).value, QuantumSymbol
138
+ )
139
+ ]
140
+ expr = rename_variables(
141
+ expr,
142
+ {str(symbol.handle): symbol.handle.identifier for symbol in symbols}
143
+ | {symbol.handle.qmod_expr: symbol.handle.identifier for symbol in symbols},
144
+ )
145
+ for symbol in symbols:
146
+ expr = expr.replace(symbol.handle.qmod_expr, symbol.handle.identifier)
147
+ result_type = compute_arithmetic_result_type(
148
+ expr,
149
+ {symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
150
+ self._machine_precision,
151
+ )
152
+ result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
153
+ copy_type_information(
154
+ result_type, result_symbol.quantum_type, str(op.result_var)
155
+ )
@@ -10,20 +10,6 @@ from classiq.model_expansions.scope import Scope
10
10
 
11
11
  class WithinApplyEmitter(Emitter[WithinApply]):
12
12
  def emit(self, within_apply: WithinApply, /) -> None:
13
- if within_apply.is_generative():
14
- within_apply_context = self._register_generative_context(
15
- within_apply, WITHIN_APPLY_NAME, ["within", "apply"]
16
- )
17
- within_apply = within_apply.model_copy(
18
- update={
19
- "compute": within_apply_context.statements("within"),
20
- "action": within_apply_context.statements("apply"),
21
- }
22
- )
23
-
24
- self._emit_as_operation(within_apply)
25
-
26
- def _emit_as_operation(self, within_apply: WithinApply) -> None:
27
13
  within_apply_operation = Closure(
28
14
  name=WITHIN_APPLY_NAME,
29
15
  blocks=dict(within=within_apply.compute, apply=within_apply.action),
@@ -1,4 +1,5 @@
1
1
  import itertools
2
+ import re
2
3
  from collections import UserDict
3
4
  from collections.abc import Iterator
4
5
  from contextlib import contextmanager
@@ -6,6 +7,8 @@ from dataclasses import dataclass
6
7
  from functools import singledispatch
7
8
  from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
8
9
 
10
+ from sympy import Symbol
11
+
9
12
  from classiq.interface.exceptions import (
10
13
  ClassiqExpansionError,
11
14
  ClassiqInternalExpansionError,
@@ -14,6 +17,9 @@ from classiq.interface.generator.expressions.evaluated_expression import (
14
17
  EvaluatedExpression,
15
18
  )
16
19
  from classiq.interface.generator.expressions.expression import Expression
20
+ from classiq.interface.generator.expressions.expression_constants import (
21
+ CPARAM_EXECUTION_SUFFIX_PATTERN,
22
+ )
17
23
  from classiq.interface.generator.expressions.qmod_qarray_proxy import (
18
24
  ILLEGAL_SLICE_BOUNDS_MSG,
19
25
  )
@@ -88,10 +94,8 @@ class QuantumSymbol:
88
94
  raise ClassiqExpansionError(
89
95
  f"{self.quantum_type.type_name} is not subscriptable."
90
96
  )
91
- if (
92
- index < 0
93
- or self.quantum_type.has_length
94
- and index > self.quantum_type.length_value
97
+ if index < 0 or (
98
+ self.quantum_type.has_length and index > self.quantum_type.length_value
95
99
  ):
96
100
  raise ClassiqExpansionError(
97
101
  f"Subscript [{index}] is out of bounds for {self.handle}."
@@ -194,6 +198,8 @@ class Scope(EvaluatedUserDict):
194
198
  return self.data[name]
195
199
  if self._parent is not None:
196
200
  return self._parent[name]
201
+ if re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, name):
202
+ return Evaluated(value=Symbol(name))
197
203
  raise ClassiqExpansionError(f"Variable {name!r} is undefined")
198
204
 
199
205
  def __contains__(self, item: Any) -> bool:
@@ -26,7 +26,6 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
26
26
  from classiq.qmod.builtins import BUILTIN_CONSTANTS
27
27
  from classiq.qmod.builtins.functions import (
28
28
  CORE_LIB_DECLS,
29
- OPEN_LIB_DECLS,
30
29
  STD_QMOD_OPERATORS,
31
30
  )
32
31
  from classiq.qmod.quantum_function import GenerativeQFunc
@@ -96,16 +95,6 @@ def _init_builtins_scope(scope: Scope) -> None:
96
95
  scope=Scope(parent=scope),
97
96
  )
98
97
  )
99
- for func_decl in OPEN_LIB_DECLS:
100
- if func_decl.name not in scope:
101
- scope[func_decl.name] = Evaluated(
102
- value=FunctionClosure.create(
103
- name=func_decl.name,
104
- positional_arg_declarations=func_decl.positional_arg_declarations,
105
- scope=Scope(parent=scope),
106
- is_atomic=True,
107
- )
108
- )
109
98
  for constant in BUILTIN_CONSTANTS:
110
99
  value = constant.value
111
100
  if not value.is_evaluated():
@@ -170,3 +170,10 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
170
170
  args=[self.visit(node.value), self.visit(node.slice)],
171
171
  keywords=[],
172
172
  )
173
+
174
+ def visit_Attribute(self, node: ast.Attribute) -> ast.Call:
175
+ return ast.Call(
176
+ func=ast.Name("get_field"),
177
+ args=[node.value, ast.Constant(value=node.attr)],
178
+ keywords=[],
179
+ )
@@ -109,5 +109,15 @@ class SympyToQuantumExpressionTranslator(PythonCodePrinter):
109
109
  return f"(({self._print(expr.args[0])}) ^ ({self._print(expr.args[1])}))"
110
110
 
111
111
 
112
- def translate_sympy_quantum_expression(expr: Basic) -> str:
113
- return SympyToQuantumExpressionTranslator().doprint(expr)
112
+ class SympyToBoolExpressionTranslator(SympyToQuantumExpressionTranslator):
113
+ _operators = {
114
+ **SympyToQuantumExpressionTranslator._operators,
115
+ **{"not": "not ", "xor": "xor"},
116
+ }
117
+
118
+
119
+ def translate_sympy_quantum_expression(expr: Basic, preserve_bool_ops: bool) -> str:
120
+ if preserve_bool_ops:
121
+ return SympyToBoolExpressionTranslator().doprint(expr)
122
+ else:
123
+ return SympyToQuantumExpressionTranslator().doprint(expr)
@@ -0,0 +1,26 @@
1
+ import ast
2
+
3
+ _POSSIBLE_HANDLE_AST_TYPES = (ast.Subscript, ast.Attribute, ast.Name)
4
+
5
+
6
+ class _ASTRenamer(ast.NodeTransformer):
7
+ def __init__(self, sub: dict[str, str]) -> None:
8
+ self._sub = sub
9
+
10
+ def visit(self, node: ast.AST) -> ast.AST:
11
+ if isinstance(node, _POSSIBLE_HANDLE_AST_TYPES):
12
+ node_expr = ast.unparse(node)
13
+ if node_expr in self._sub:
14
+ return ast.Name(id=self._sub[node_expr])
15
+ return super().visit(node)
16
+
17
+ def visit_Call(self, node: ast.Call) -> ast.Call:
18
+ return ast.Call(
19
+ func=node.func,
20
+ args=[self.visit(arg) for arg in node.args],
21
+ keywords=[],
22
+ )
23
+
24
+
25
+ def rename_variables(expr: str, sub: dict[str, str]) -> str:
26
+ return ast.unparse(_ASTRenamer(sub).visit(ast.parse(expr)))