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
@@ -9,6 +9,9 @@ from classiq.interface.generator.arith import arithmetic_expression_parser
9
9
  from classiq.interface.generator.arith.arithmetic_expression_abc import (
10
10
  ArithmeticExpressionABC,
11
11
  )
12
+ from classiq.interface.generator.arith.arithmetic_expression_validator import (
13
+ is_constant,
14
+ )
12
15
  from classiq.interface.generator.arith.arithmetic_param_getters import (
13
16
  id2op,
14
17
  operation_allows_target,
@@ -17,7 +20,9 @@ from classiq.interface.generator.arith.arithmetic_result_builder import (
17
20
  ArithmeticResultBuilder,
18
21
  )
19
22
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
23
+ from classiq.interface.generator.expressions.expression import Expression
20
24
  from classiq.interface.model.quantum_type import (
25
+ QuantumNumeric,
21
26
  QuantumType,
22
27
  quantum_var_to_register,
23
28
  register_info_to_quantum_type,
@@ -28,6 +33,10 @@ ARITHMETIC_EXPRESSION_RESULT_NAME: Final[str] = "expression_result"
28
33
  ARITHMETIC_EXPRESSION_GARBAGE_NAME: Final[str] = "expression_garbage"
29
34
 
30
35
 
36
+ def is_zero(expr: str) -> bool:
37
+ return is_constant(expr) and float(expr) == 0
38
+
39
+
31
40
  class Arithmetic(ArithmeticExpressionABC):
32
41
  target: Optional[RegisterArithmeticInfo] = None
33
42
  inputs_to_save: set[str] = pydantic.Field(default_factory=set)
@@ -99,7 +108,13 @@ def get_arithmetic_params(
99
108
 
100
109
  def compute_arithmetic_result_type(
101
110
  expr_str: str, var_types: dict[str, QuantumType], machine_precision: int
102
- ) -> QuantumType:
111
+ ) -> QuantumNumeric:
112
+ if is_zero(expr_str):
113
+ return QuantumNumeric(
114
+ size=Expression(expr="1"),
115
+ is_signed=Expression(expr="False"),
116
+ fraction_digits=Expression(expr="0"),
117
+ )
103
118
  arith_param = get_arithmetic_params(expr_str, var_types, machine_precision)
104
119
  return register_info_to_quantum_type(
105
120
  arith_param.outputs[ARITHMETIC_EXPRESSION_RESULT_NAME]
@@ -3,6 +3,7 @@ import re
3
3
  from _ast import AST
4
4
  from typing import Any, Optional, Union
5
5
 
6
+ from sympy import Expr
6
7
  from typing_extensions import TypeAlias, get_args
7
8
 
8
9
  from classiq.interface.exceptions import ClassiqArithmeticError, ClassiqValueError
@@ -54,11 +55,11 @@ SupportedNodesTypes = Union[
54
55
  DEFAULT_SUPPORTED_NODE_TYPES = get_args(SupportedNodesTypes)
55
56
 
56
57
 
57
- def _is_constant(expr: str) -> bool:
58
+ def is_constant(expr: Union[str, Expr]) -> bool:
58
59
  try:
59
60
  float(expr)
60
61
  return True
61
- except ValueError:
62
+ except (ValueError, TypeError):
62
63
  return False
63
64
 
64
65
 
@@ -94,7 +95,7 @@ class ExpressionValidator(ast.NodeVisitor):
94
95
  @staticmethod
95
96
  def _get_adjusted_expression(expression: str) -> str:
96
97
  # This works around the simplification of the trivial expressions such as a + 0, 1 * a, etc.
97
- if IDENITIFIER_REGEX.fullmatch(expression) or _is_constant(expression):
98
+ if IDENITIFIER_REGEX.fullmatch(expression) or is_constant(expression):
98
99
  return f"0 + {expression}"
99
100
  return expression
100
101
 
@@ -20,3 +20,6 @@ BOOLEAN_LITERALS = {"True", "False"}
20
20
  FORBIDDEN_LITERALS: set[str] = set(keyword.kwlist) - SUPPORTED_FUNC_NAMES
21
21
  CPARAM_EXECUTION_SUFFIX: Final[str] = "_param"
22
22
  RESERVED_EXPRESSIONS: frozenset[str] = frozenset({"i"})
23
+ CPARAM_EXECUTION_SUFFIX_PATTERN = (
24
+ rf"({CPARAM_EXECUTION_SUFFIX}|^({'|'.join(RESERVED_EXPRESSIONS)}))(_\d+)*$"
25
+ )
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Any, Literal, Optional, Union
2
+ from typing import Literal, Optional, Union
3
3
 
4
4
  import pydantic
5
5
  from pydantic import ConfigDict
@@ -12,6 +12,12 @@ from classiq.interface.generator.synthesis_metadata.synthesis_execution_data imp
12
12
  ExecutionData,
13
13
  )
14
14
 
15
+ from classiq.model_expansions.capturing.mangling_utils import (
16
+ demangle_capture_name,
17
+ demangle_name,
18
+ is_captured_var_name,
19
+ )
20
+
15
21
  _logger = logging.getLogger(__name__)
16
22
  ParameterName = str
17
23
  IOQubitMapping: TypeAlias = dict[str, tuple[int, ...]]
@@ -52,6 +58,16 @@ class GeneratedRegister(pydantic.BaseModel):
52
58
  def width(self) -> int:
53
59
  return len(self)
54
60
 
61
+ @property
62
+ def is_captured(self) -> bool:
63
+ return is_captured_var_name(self.name)
64
+
65
+ @staticmethod
66
+ def demangle_name(name: str) -> str:
67
+ if is_captured_var_name(name):
68
+ return demangle_capture_name(name)
69
+ return demangle_name(name)
70
+
55
71
 
56
72
  class GeneratedFunction(pydantic.BaseModel):
57
73
  name: str
@@ -157,32 +173,54 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
157
173
  for key, value in parameters.items()
158
174
  ]
159
175
 
160
- def update(self, **kwargs: Any) -> None:
161
- for key, value in kwargs.items():
162
- setattr(self, key, value)
163
-
164
- def propagate_absolute_qubits(self) -> None:
176
+ def propagate_absolute_qubits(self) -> "FunctionDebugInfoInterface":
165
177
  if self.absolute_qubits is None:
166
- return
167
-
168
- for register in self.registers:
169
- register.qubit_indexes_absolute = list(
170
- _get_absolute_from_relative(
171
- self.absolute_qubits, tuple(register.qubit_indexes_relative)
178
+ return self
179
+
180
+ updated_registers = [
181
+ register.model_copy(
182
+ update=dict(
183
+ qubit_indexes_absolute=list(
184
+ _get_absolute_from_relative(
185
+ self.absolute_qubits, tuple(register.qubit_indexes_relative)
186
+ )
187
+ )
172
188
  )
173
189
  )
190
+ for register in self.registers
191
+ ]
192
+ updated_generated_function = (
193
+ self.generated_function.model_copy(update=dict(registers=updated_registers))
194
+ if self.generated_function
195
+ else None
196
+ )
174
197
 
198
+ updated_children: list[FunctionDebugInfoInterface] = []
175
199
  for child in self.children:
176
- child.absolute_qubits = _get_absolute_from_relative(
177
- self.absolute_qubits, child.relative_qubits
200
+ updated_child = child.model_copy(
201
+ update=dict(
202
+ absolute_qubits=_get_absolute_from_relative(
203
+ self.absolute_qubits, child.relative_qubits
204
+ )
205
+ )
178
206
  )
179
- child.propagate_absolute_qubits()
207
+ updated_children.append(updated_child.propagate_absolute_qubits())
180
208
 
181
- def inverse(self) -> None:
182
- self.is_inverse = not self.is_inverse
183
- for child in self.children:
184
- child.inverse()
185
- self.children = self.children[::-1]
209
+ return self.model_copy(
210
+ update=dict(
211
+ generated_function=updated_generated_function,
212
+ children=updated_children,
213
+ )
214
+ )
215
+
216
+ def inverse(self) -> "FunctionDebugInfoInterface":
217
+ inverted_children = [child.inverse() for child in self.children[::-1]]
218
+ return self.model_copy(
219
+ update=dict(
220
+ is_inverse=not self.is_inverse,
221
+ children=inverted_children,
222
+ )
223
+ )
186
224
 
187
225
 
188
226
  def _get_absolute_from_relative(
@@ -10,8 +10,8 @@ from classiq.interface.generator.model.preferences import (
10
10
 
11
11
  __all__: list[str] = [
12
12
  "Constraints",
13
- "Preferences",
14
13
  "CustomHardwareSettings",
15
14
  "OptimizationParameter",
15
+ "Preferences",
16
16
  "TranspilerBasisGates",
17
17
  ]
@@ -250,11 +250,11 @@ def get_type_and_size_dict(
250
250
 
251
251
 
252
252
  __all__ = [
253
+ "AuxQReg",
253
254
  "QReg",
254
- "QSInt",
255
- "QUInt",
256
255
  "QSFixed",
256
+ "QSInt",
257
257
  "QUFixed",
258
+ "QUInt",
258
259
  "ZeroQReg",
259
- "AuxQReg",
260
260
  ]
@@ -26,26 +26,6 @@ class ControlledGate(_StandardGate): # type: ignore[misc]
26
26
  num_ctrl_qubits: pydantic.PositiveInt = pydantic.Field(
27
27
  default=DEFAULT_NUM_CTRL_QUBITS
28
28
  )
29
-
30
- def _create_ios(self) -> None:
31
- _StandardGate._create_ios(self)
32
- control = RegisterUserInput(
33
- name=CONTROLLED_GATE_CONTROL, size=self.num_ctrl_qubits
34
- )
35
- self._inputs[CONTROLLED_GATE_CONTROL] = control
36
- self._outputs[CONTROLLED_GATE_CONTROL] = control
37
-
38
- def to_control_state(self) -> ControlState:
39
- return ControlState(
40
- name=CONTROLLED_GATE_CONTROL, num_ctrl_qubits=self.num_ctrl_qubits
41
- )
42
-
43
-
44
- class ControlledGateWithState(ControlledGate): # type: ignore[misc]
45
- """
46
- Base model for controlled Gates with control over the controlled_state
47
- """
48
-
49
29
  ctrl_state: CtrlState = pydantic.Field(
50
30
  description="The control state in decimal or as a bit string (e.g. '1011'). If not specified, the control "
51
31
  "state is 2**num_ctrl_qubits - 1.\n"
@@ -82,12 +62,20 @@ class ControlledGateWithState(ControlledGate): # type: ignore[misc]
82
62
  )
83
63
  return ControlState(name=CONTROLLED_GATE_CONTROL, ctrl_state=ctrl_state_str)
84
64
 
65
+ def _create_ios(self) -> None:
66
+ _StandardGate._create_ios(self)
67
+ control = RegisterUserInput(
68
+ name=CONTROLLED_GATE_CONTROL, size=self.num_ctrl_qubits
69
+ )
70
+ self._inputs[CONTROLLED_GATE_CONTROL] = control
71
+ self._outputs[CONTROLLED_GATE_CONTROL] = control
72
+
85
73
 
86
74
  def _num_to_control_string(ctrl_state_int: int, num_ctrl_qubits: int) -> str:
87
75
  return format(ctrl_state_int, f"0{num_ctrl_qubits}b")
88
76
 
89
77
 
90
- class CXGate(ControlledGateWithState): # type: ignore[misc]
78
+ class CXGate(ControlledGate): # type: ignore[misc]
91
79
  """
92
80
  The Controlled-X Gate
93
81
  """
@@ -98,7 +86,7 @@ class CXGate(ControlledGateWithState): # type: ignore[misc]
98
86
  return 2
99
87
 
100
88
 
101
- class CCXGate(ControlledGateWithState): # type: ignore[misc]
89
+ class CCXGate(ControlledGate): # type: ignore[misc]
102
90
  """
103
91
  The Double Controlled-X Gate
104
92
  """
@@ -109,7 +97,7 @@ class CCXGate(ControlledGateWithState): # type: ignore[misc]
109
97
  return 2
110
98
 
111
99
 
112
- class C3XGate(ControlledGateWithState): # type: ignore[misc]
100
+ class C3XGate(ControlledGate): # type: ignore[misc]
113
101
  """
114
102
  The X Gate controlled on 3 qubits
115
103
  """
@@ -121,7 +109,7 @@ class C3XGate(ControlledGateWithState): # type: ignore[misc]
121
109
  return 2
122
110
 
123
111
 
124
- class C4XGate(ControlledGateWithState): # type: ignore[misc]
112
+ class C4XGate(ControlledGate): # type: ignore[misc]
125
113
  """
126
114
  The X Gate controlled on 4 qubits
127
115
  """
@@ -133,7 +121,7 @@ class C4XGate(ControlledGateWithState): # type: ignore[misc]
133
121
  return 2
134
122
 
135
123
 
136
- class CYGate(ControlledGateWithState): # type: ignore[misc]
124
+ class CYGate(ControlledGate): # type: ignore[misc]
137
125
  """
138
126
  The Controlled-Y Gate
139
127
  """
@@ -142,7 +130,7 @@ class CYGate(ControlledGateWithState): # type: ignore[misc]
142
130
  return 2
143
131
 
144
132
 
145
- class CZGate(ControlledGateWithState): # type: ignore[misc]
133
+ class CZGate(ControlledGate): # type: ignore[misc]
146
134
  """
147
135
  The Controlled-Z Gate
148
136
  """
@@ -151,7 +139,7 @@ class CZGate(ControlledGateWithState): # type: ignore[misc]
151
139
  return 2
152
140
 
153
141
 
154
- class CHGate(ControlledGateWithState): # type: ignore[misc]
142
+ class CHGate(ControlledGate): # type: ignore[misc]
155
143
  """
156
144
  The Controlled-H Gate
157
145
  """
@@ -160,7 +148,7 @@ class CHGate(ControlledGateWithState): # type: ignore[misc]
160
148
  return 2
161
149
 
162
150
 
163
- class CSXGate(ControlledGateWithState): # type: ignore[misc]
151
+ class CSXGate(ControlledGate): # type: ignore[misc]
164
152
  """
165
153
  The Controlled-SX Gate
166
154
  """
@@ -169,25 +157,25 @@ class CSXGate(ControlledGateWithState): # type: ignore[misc]
169
157
  return 4
170
158
 
171
159
 
172
- class CRXGate(ControlledGateWithState, angles=["theta"]): # type: ignore[misc]
160
+ class CRXGate(ControlledGate, angles=["theta"]): # type: ignore[misc]
173
161
  """
174
162
  The Controlled-RX Gate
175
163
  """
176
164
 
177
165
 
178
- class CRYGate(ControlledGateWithState, angles=["theta"]): # type: ignore[misc]
166
+ class CRYGate(ControlledGate, angles=["theta"]): # type: ignore[misc]
179
167
  """
180
168
  The Controlled-RY Gate
181
169
  """
182
170
 
183
171
 
184
- class CRZGate(ControlledGateWithState, angles=["theta"]): # type: ignore[misc]
172
+ class CRZGate(ControlledGate, angles=["theta"]): # type: ignore[misc]
185
173
  """
186
174
  The Controlled-RZ Gate
187
175
  """
188
176
 
189
177
 
190
- class CPhaseGate(ControlledGateWithState, angles=["theta"]): # type: ignore[misc]
178
+ class CPhaseGate(ControlledGate, angles=["theta"]): # type: ignore[misc]
191
179
  """
192
180
  The Controlled-Phase Gate
193
181
  """
@@ -42,6 +42,7 @@ class OperationLink(pydantic.BaseModel):
42
42
  inner_label: Optional[str] = None
43
43
  qubits: tuple[int, ...]
44
44
  type: str
45
+ is_captured: bool = False
45
46
 
46
47
  model_config = ConfigDict(frozen=True)
47
48
 
@@ -1 +1 @@
1
- INTERFACE_VERSION = "5"
1
+ INTERFACE_VERSION = "6"
@@ -180,9 +180,8 @@ class Model(VersionedModel, ASTNode):
180
180
  )
181
181
  return constants
182
182
 
183
- def json_no_preferences_and_constraints(self) -> str:
184
- return self.model_dump_json(
185
- indent=2,
183
+ def dump_no_preferences_and_constraints(self) -> dict[str, Any]:
184
+ return self.model_dump(
186
185
  exclude={
187
186
  "constraints",
188
187
  "execution_preferences",
@@ -7,7 +7,6 @@ from typing import (
7
7
 
8
8
  import pydantic
9
9
 
10
- from classiq.interface.ast_node import ASTNode
11
10
  from classiq.interface.exceptions import ClassiqError, ClassiqValueError
12
11
  from classiq.interface.generator.expressions.expression import Expression
13
12
  from classiq.interface.generator.functions.port_declaration import (
@@ -21,7 +20,10 @@ from classiq.interface.model.port_declaration import AnonPortDeclaration
21
20
  from classiq.interface.model.quantum_function_declaration import (
22
21
  QuantumFunctionDeclaration,
23
22
  )
24
- from classiq.interface.model.quantum_lambda_function import QuantumOperand
23
+ from classiq.interface.model.quantum_lambda_function import (
24
+ OperandIdentifier,
25
+ QuantumOperand,
26
+ )
25
27
  from classiq.interface.model.quantum_statement import HandleMetadata, QuantumOperation
26
28
 
27
29
  ArgValue = Union[
@@ -31,11 +33,6 @@ ArgValue = Union[
31
33
  ]
32
34
 
33
35
 
34
- class OperandIdentifier(ASTNode):
35
- name: str
36
- index: Expression
37
-
38
-
39
36
  class QuantumFunctionCall(QuantumOperation):
40
37
  kind: Literal["QuantumFunctionCall"]
41
38
 
@@ -9,6 +9,7 @@ from typing import (
9
9
  import pydantic
10
10
  from pydantic import SerializeAsAny
11
11
  from pydantic_core.core_schema import ValidationInfo
12
+ from typing_extensions import Self
12
13
 
13
14
  from classiq.interface.exceptions import ClassiqInternalError
14
15
  from classiq.interface.generator.arith.register_user_input import RegisterUserInput
@@ -171,6 +172,12 @@ class AnonQuantumOperandDeclaration(AnonQuantumFunctionDeclaration):
171
172
  new_instance_data["kind"] = "QuantumOperandDeclaration"
172
173
  return QuantumOperandDeclaration(**new_instance_data)
173
174
 
175
+ @property
176
+ def element_declaration(self) -> Self:
177
+ if not self.is_list:
178
+ raise ClassiqInternalError
179
+ return self.model_copy(update={"is_list": False})
180
+
174
181
 
175
182
  AnonQuantumFunctionDeclaration.model_rebuild()
176
183
 
@@ -4,6 +4,7 @@ import pydantic
4
4
 
5
5
  from classiq.interface.ast_node import ASTNode
6
6
  from classiq.interface.exceptions import ClassiqError
7
+ from classiq.interface.generator.expressions.expression import Expression
7
8
  from classiq.interface.model.quantum_function_declaration import (
8
9
  AnonQuantumOperandDeclaration,
9
10
  )
@@ -52,5 +53,13 @@ class QuantumLambdaFunction(ASTNode):
52
53
  self._func_decl = fd
53
54
 
54
55
 
55
- QuantumCallable = Union[str, QuantumLambdaFunction]
56
+ class OperandIdentifier(ASTNode):
57
+ name: str
58
+ index: Expression
59
+
60
+ def __str__(self) -> str:
61
+ return f"{self.name}[{self.index.expr}]"
62
+
63
+
64
+ QuantumCallable = Union[str, OperandIdentifier, QuantumLambdaFunction]
56
65
  QuantumOperand = Union[QuantumCallable, list[QuantumCallable]]
@@ -159,7 +159,9 @@ class QuantumNumeric(QuantumScalar):
159
159
  def _validate_fields(self) -> Self:
160
160
  has_sign = self.is_signed is not None
161
161
  has_fraction_digits = self.fraction_digits is not None
162
- if has_sign and not has_fraction_digits or not has_sign and has_fraction_digits:
162
+ if (has_sign and not has_fraction_digits) or (
163
+ not has_sign and has_fraction_digits
164
+ ):
163
165
  raise ClassiqValueError(
164
166
  "Assign neither or both of is_signed and fraction_digits"
165
167
  )
@@ -61,7 +61,7 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
61
61
  for field_name, field_type in val.struct_declaration.variables.items()
62
62
  }
63
63
 
64
- if isinstance(val, Enum):
64
+ if isinstance(val, (Enum, int)):
65
65
  return val
66
66
 
67
67
  elif isinstance(qmod_type, ClassicalList):
@@ -147,6 +147,8 @@ def get_field(
147
147
  proxy: Union[QmodSizedProxy, QmodStructInstance, QmodQStructProxy, list],
148
148
  field: str,
149
149
  ) -> ExpressionValue:
150
+ if isinstance(proxy, type) and issubclass(proxy, Enum):
151
+ return getattr(proxy, field)
150
152
  if isinstance(proxy, Symbol) and not isinstance(proxy, QmodSizedProxy):
151
153
  raise ClassiqExpansionError(
152
154
  f"Cannot evaluate '{proxy}.{field}': Variable {str(proxy)!r} is not "
@@ -2,7 +2,7 @@ import dataclasses
2
2
  from collections.abc import Iterator, Sequence
3
3
  from contextlib import contextmanager
4
4
  from dataclasses import dataclass, field
5
- from typing import TYPE_CHECKING
5
+ from typing import TYPE_CHECKING, Optional
6
6
 
7
7
  from classiq.interface.enum_utils import StrEnum
8
8
  from classiq.interface.exceptions import (
@@ -16,12 +16,14 @@ from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBi
16
16
  from classiq.interface.model.port_declaration import PortDeclaration
17
17
  from classiq.interface.model.quantum_function_call import ArgValue
18
18
  from classiq.interface.model.quantum_type import QuantumType
19
+ from classiq.interface.model.variable_declaration_statement import (
20
+ VariableDeclarationStatement,
21
+ )
19
22
 
20
23
  from classiq.model_expansions.capturing.mangling_utils import (
21
24
  demangle_handle,
22
25
  mangle_captured_var_name,
23
26
  )
24
- from classiq.model_expansions.scope import QuantumSymbol
25
27
  from classiq.model_expansions.transformers.var_splitter import SymbolPart, SymbolParts
26
28
 
27
29
  if TYPE_CHECKING:
@@ -71,7 +73,11 @@ class _CapturedHandle:
71
73
 
72
74
  @property
73
75
  def mangled_name(self) -> str:
74
- return mangle_captured_var_name(self.handle.identifier, self.defining_function)
76
+ return mangle_captured_var_name(
77
+ self.handle.identifier,
78
+ self.defining_function.name,
79
+ self.defining_function.depth,
80
+ )
75
81
 
76
82
  @property
77
83
  def port(self) -> PortDeclaration:
@@ -211,7 +217,16 @@ class CapturedVars:
211
217
  ]
212
218
  )
213
219
 
214
- def filter(self, current_function: "FunctionClosure") -> "CapturedVars":
220
+ def filter_vars(
221
+ self,
222
+ current_function: "FunctionClosure",
223
+ current_declarations: Optional[list[VariableDeclarationStatement]] = None,
224
+ ) -> "CapturedVars":
225
+ current_declared_vars = (
226
+ None
227
+ if current_declarations is None
228
+ else {decl.name for decl in current_declarations}
229
+ )
215
230
  return CapturedVars(
216
231
  _captured_handles=[
217
232
  captured_handle
@@ -219,6 +234,10 @@ class CapturedVars:
219
234
  if not _same_closure(
220
235
  captured_handle.defining_function, current_function
221
236
  )
237
+ or (
238
+ current_declared_vars is not None
239
+ and captured_handle.handle.name not in current_declared_vars
240
+ )
222
241
  ]
223
242
  )
224
243
 
@@ -247,10 +266,7 @@ class CapturedVars:
247
266
 
248
267
  def get_captured_mapping(self) -> SymbolParts:
249
268
  return {
250
- QuantumSymbol(
251
- handle=captured_handle.handle,
252
- quantum_type=captured_handle.quantum_type,
253
- ): [
269
+ captured_handle.handle: [
254
270
  SymbolPart(
255
271
  source_handle=captured_handle.handle,
256
272
  target_var_name=captured_handle.mangled_name,
@@ -1,12 +1,9 @@
1
1
  import re
2
- from typing import TYPE_CHECKING
2
+ from typing import Optional
3
3
 
4
4
  from classiq.interface.generator.compiler_keywords import CAPTURE_SUFFIX
5
5
  from classiq.interface.model.handle_binding import HANDLE_ID_SEPARATOR, HandleBinding
6
6
 
7
- if TYPE_CHECKING:
8
- from classiq.model_expansions.closure import FunctionClosure
9
-
10
7
  IDENTIFIER_PATTERN = r"[a-zA-Z_][a-zA-Z0-9_]*"
11
8
  CAPTURE_PATTERN = re.compile(
12
9
  rf"({IDENTIFIER_PATTERN}){CAPTURE_SUFFIX}{IDENTIFIER_PATTERN}__\d*"
@@ -15,24 +12,34 @@ ARRAY_CAST_SUFFIX = HANDLE_ID_SEPARATOR + "array_cast"
15
12
 
16
13
 
17
14
  def mangle_captured_var_name(
18
- var_name: str, defining_function: "FunctionClosure"
15
+ var_name: str, function_name: str, function_depth: int
19
16
  ) -> str:
20
- return (
21
- f"{var_name}{CAPTURE_SUFFIX}{defining_function.name}__{defining_function.depth}"
22
- )
17
+ return f"{var_name}{CAPTURE_SUFFIX}{function_name}__{function_depth}"
23
18
 
24
19
 
25
- def demangle_name(name: str) -> str:
26
- match = re.match(CAPTURE_PATTERN, name)
20
+ def _match_capture_pattern(name: str) -> Optional[re.Match[str]]:
21
+ return re.match(CAPTURE_PATTERN, name)
22
+
23
+
24
+ def is_captured_var_name(name: str) -> bool:
25
+ return _match_capture_pattern(name) is not None
26
+
27
+
28
+ def demangle_capture_name(name: str) -> str:
29
+ match = _match_capture_pattern(name)
27
30
  return match.group(1) if match else name
28
31
 
29
32
 
30
33
  def demangle_handle(handle: HandleBinding) -> HandleBinding:
31
- name = handle.name
34
+ demangled_name = demangle_name(handle.name)
35
+ return handle.rename(demangled_name)
36
+
37
+
38
+ def demangle_name(name: str) -> str:
32
39
  if HANDLE_ID_SEPARATOR not in name:
33
- return handle
40
+ return name
34
41
  if ARRAY_CAST_SUFFIX in name:
35
- return HandleBinding(name=name.split(ARRAY_CAST_SUFFIX)[0])
42
+ return name.split(ARRAY_CAST_SUFFIX)[0]
36
43
  name = re.sub(r"([^_])_\d+$", r"\1", name)
37
44
  name_parts = name.split(HANDLE_ID_SEPARATOR)
38
45
  new_name_parts = [name_parts[0]]
@@ -44,5 +51,6 @@ def demangle_handle(handle: HandleBinding) -> HandleBinding:
44
51
  new_name_parts.append(f"[{part_left}:{part_right}]")
45
52
  else:
46
53
  new_name_parts.append(f".{part}")
47
- new_name_parts = list(map(demangle_name, new_name_parts))
48
- return handle.rename("".join(new_name_parts))
54
+ new_name_parts = list(map(demangle_capture_name, new_name_parts))
55
+ new_name = "".join(new_name_parts)
56
+ return new_name
@@ -135,13 +135,13 @@ def _check_classical_type_match(
135
135
  arg_struct_name = None if not arg_is_struct else argument.struct_declaration.name
136
136
  if (
137
137
  arg_is_qvar
138
- or arg_is_builtin
139
- and (type_is_struct or type_is_enum)
140
- or arg_is_struct
141
- and (not type_is_struct or arg_struct_name != type_name)
142
- or arg_is_enum
143
- and get_qmod_type(classical_type) != CInt
144
- and (not type_is_enum or type(argument).__name__ != type_name)
138
+ or (arg_is_builtin and (type_is_struct or type_is_enum))
139
+ or (arg_is_struct and (not type_is_struct or arg_struct_name != type_name))
140
+ or (
141
+ arg_is_enum
142
+ and get_qmod_type(classical_type) != CInt
143
+ and (not type_is_enum or type(argument).__name__ != type_name)
144
+ )
145
145
  ):
146
146
  raise ClassiqExpansionError(error_message)
147
147