classiq 0.67.0__py3-none-any.whl → 0.69.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 (87) hide show
  1. classiq/_internals/api_wrapper.py +9 -9
  2. classiq/_internals/async_utils.py +1 -1
  3. classiq/_internals/authentication/password_manager.py +1 -1
  4. classiq/_internals/client.py +1 -1
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -11
  6. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
  8. classiq/applications/qnn/torch_utils.py +1 -1
  9. classiq/execution/execution_session.py +7 -3
  10. classiq/execution/jobs.py +2 -5
  11. classiq/executor.py +7 -2
  12. classiq/interface/_version.py +1 -1
  13. classiq/interface/ast_node.py +1 -1
  14. classiq/interface/backend/quantum_backend_providers.py +2 -3
  15. classiq/interface/chemistry/operator.py +12 -8
  16. classiq/interface/debug_info/back_ref_util.py +22 -0
  17. classiq/interface/debug_info/debug_info.py +26 -21
  18. classiq/interface/executor/optimizer_preferences.py +1 -0
  19. classiq/interface/generator/arith/arithmetic.py +96 -1
  20. classiq/interface/generator/arith/arithmetic_expression_parser.py +1 -1
  21. classiq/interface/generator/arith/arithmetic_param_getters.py +3 -3
  22. classiq/interface/generator/functions/classical_type.py +12 -1
  23. classiq/interface/generator/generated_circuit_data.py +64 -23
  24. classiq/interface/generator/quantum_program.py +18 -1
  25. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  26. classiq/interface/generator/types/enum_declaration.py +45 -3
  27. classiq/interface/ide/visual_model.py +0 -2
  28. classiq/interface/model/classical_if.py +2 -2
  29. classiq/interface/model/control.py +2 -2
  30. classiq/interface/model/invert.py +2 -2
  31. classiq/interface/model/power.py +2 -2
  32. classiq/interface/model/quantum_function_call.py +4 -0
  33. classiq/interface/model/quantum_statement.py +1 -1
  34. classiq/interface/model/repeat.py +2 -2
  35. classiq/interface/model/statement_block.py +1 -1
  36. classiq/interface/model/within_apply_operation.py +2 -2
  37. classiq/interface/server/routes.py +0 -6
  38. classiq/model_expansions/generative_functions.py +4 -3
  39. classiq/model_expansions/interpreters/generative_interpreter.py +78 -18
  40. classiq/model_expansions/quantum_operations/allocate.py +3 -1
  41. classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -0
  42. classiq/model_expansions/quantum_operations/bind.py +2 -1
  43. classiq/model_expansions/quantum_operations/block_evaluator.py +76 -0
  44. classiq/model_expansions/quantum_operations/call_emitter.py +0 -13
  45. classiq/model_expansions/quantum_operations/classicalif.py +5 -4
  46. classiq/model_expansions/quantum_operations/composite_emitter.py +27 -0
  47. classiq/model_expansions/quantum_operations/emitter.py +16 -2
  48. classiq/model_expansions/quantum_operations/expression_evaluator.py +33 -0
  49. classiq/model_expansions/quantum_operations/handle_evaluator.py +28 -0
  50. classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -2
  51. classiq/model_expansions/quantum_operations/repeat.py +2 -1
  52. classiq/model_expansions/quantum_operations/variable_decleration.py +2 -1
  53. classiq/model_expansions/scope_initialization.py +5 -19
  54. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +1 -1
  55. classiq/open_library/functions/__init__.py +1 -2
  56. classiq/open_library/functions/amplitude_amplification.py +11 -12
  57. classiq/open_library/functions/discrete_sine_cosine_transform.py +17 -14
  58. classiq/open_library/functions/grover.py +7 -11
  59. classiq/open_library/functions/hea.py +3 -3
  60. classiq/open_library/functions/modular_exponentiation.py +17 -33
  61. classiq/open_library/functions/qft_functions.py +2 -2
  62. classiq/open_library/functions/qsvt.py +8 -8
  63. classiq/open_library/functions/state_preparation.py +16 -17
  64. classiq/open_library/functions/swap_test.py +1 -1
  65. classiq/open_library/functions/utility_functions.py +12 -4
  66. classiq/qmod/builtins/classical_functions.py +24 -7
  67. classiq/qmod/builtins/enums.py +1 -0
  68. classiq/qmod/builtins/functions/__init__.py +2 -0
  69. classiq/qmod/builtins/functions/chemistry.py +6 -38
  70. classiq/qmod/builtins/functions/exponentiation.py +24 -0
  71. classiq/qmod/builtins/operations.py +26 -11
  72. classiq/qmod/cparam.py +32 -5
  73. classiq/qmod/python_classical_type.py +10 -4
  74. classiq/qmod/quantum_callable.py +2 -1
  75. classiq/qmod/quantum_expandable.py +30 -6
  76. classiq/qmod/quantum_function.py +3 -2
  77. classiq/qmod/semantics/error_manager.py +1 -1
  78. classiq/qmod/semantics/validation/types_validation.py +1 -1
  79. classiq/qmod/symbolic.py +2 -1
  80. classiq/qmod/utilities.py +31 -2
  81. classiq/qmod/write_qmod.py +10 -7
  82. classiq/synthesis.py +25 -9
  83. {classiq-0.67.0.dist-info → classiq-0.69.0.dist-info}/METADATA +1 -1
  84. {classiq-0.67.0.dist-info → classiq-0.69.0.dist-info}/RECORD +85 -81
  85. classiq/interface/execution/jobs.py +0 -31
  86. classiq/model_expansions/quantum_operations/shallow_emitter.py +0 -166
  87. {classiq-0.67.0.dist-info → classiq-0.69.0.dist-info}/WHEEL +0 -0
@@ -2,15 +2,24 @@ import logging
2
2
  from typing import Literal, Optional, Union
3
3
 
4
4
  import pydantic
5
- from pydantic import ConfigDict
5
+ from pydantic import ConfigDict, Field
6
6
  from typing_extensions import TypeAlias
7
7
 
8
+ from classiq.interface.debug_info.back_ref_util import is_allocate_or_free_by_backref
8
9
  from classiq.interface.enum_utils import StrEnum
9
10
  from classiq.interface.generator.control_state import ControlState
10
11
  from classiq.interface.generator.register_role import RegisterRole
11
12
  from classiq.interface.generator.synthesis_metadata.synthesis_execution_data import (
12
13
  ExecutionData,
13
14
  )
15
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
16
+ QuantumExpressionOperation,
17
+ )
18
+ from classiq.interface.model.statement_block import (
19
+ ConcreteQuantumStatement,
20
+ QuantumFunctionCall,
21
+ StatementBlock,
22
+ )
14
23
 
15
24
  from classiq.model_expansions.capturing.mangling_utils import (
16
25
  demangle_capture_name,
@@ -22,7 +31,7 @@ _logger = logging.getLogger(__name__)
22
31
  ParameterName = str
23
32
  IOQubitMapping: TypeAlias = dict[str, tuple[int, ...]]
24
33
 
25
- CLASSIQ_HIERARCHY_SEPARATOR: Literal["."] = "."
34
+ CLASSIQ_HIERARCHY_SEPARATOR: Literal["__"] = "__"
26
35
 
27
36
  VISUALIZATION_HIDE_LIST = [
28
37
  "apply_to_all",
@@ -119,34 +128,75 @@ class OperationLevel(StrEnum):
119
128
  UNKNOWN = "UNKNOWN"
120
129
 
121
130
 
122
- class OperationParameter(pydantic.BaseModel):
123
- label: str
124
- value: Optional[str] = None
131
+ class StatementType(StrEnum):
132
+ CONTROL = "control"
133
+ POWER = "power"
134
+ INVERT = "invert"
135
+ WITHIN_APPLY = "within_apply"
136
+ ASSIGNMENT = "assignment"
137
+ REPEAT = "repeat"
125
138
 
126
139
 
127
140
  class FunctionDebugInfoInterface(pydantic.BaseModel):
128
- generated_function: Optional[GeneratedFunction] = pydantic.Field(default=None)
141
+ generated_function: Optional[GeneratedFunction] = Field(default=None)
129
142
  children: list["FunctionDebugInfoInterface"]
130
143
  relative_qubits: tuple[int, ...]
131
- absolute_qubits: Optional[tuple[int, ...]] = pydantic.Field(default=None)
132
- is_basis_gate: Optional[bool] = pydantic.Field(default=None)
133
- is_inverse: bool = pydantic.Field(default=False)
134
- is_allocate_or_free: bool = pydantic.Field(default=False)
135
- level: OperationLevel = pydantic.Field(default=OperationLevel.UNKNOWN)
136
- parameters: list[OperationParameter] = list()
137
- port_to_passed_variable_map: dict[str, str] = pydantic.Field(default={})
138
- release_by_inverse: bool = pydantic.Field(default=False)
144
+ absolute_qubits: Optional[tuple[int, ...]] = Field(default=None)
145
+ is_basis_gate: Optional[bool] = Field(default=None)
146
+ is_inverse: bool = Field(default=False)
147
+ is_allocate_or_free: bool = Field(default=False)
148
+ level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
149
+ port_to_passed_variable_map: dict[str, str] = Field(default={})
150
+ release_by_inverse: bool = Field(default=False)
151
+ back_refs: StatementBlock = Field(default_factory=list)
139
152
 
140
153
  model_config = ConfigDict(extra="allow")
141
154
  # Temporary field to store the override debug info for parallel old/new visualization
142
155
  override_debug_info: Optional["FunctionDebugInfoInterface"] = None
143
156
 
157
+ @property
158
+ def is_allocate_or_free_(self) -> bool:
159
+ """
160
+ temporary measure to handle the fact that in the current release we do not have
161
+ the backref, and therefore fallback to the old field of is_allocate_or_free
162
+ """
163
+ return (
164
+ is_allocate_or_free_by_backref(self.back_refs)
165
+ if bool(self.back_refs)
166
+ else self.is_allocate_or_free
167
+ )
168
+
144
169
  @property
145
170
  def name(self) -> str:
146
171
  if self.generated_function is None:
147
172
  return ""
148
173
  return self.generated_function.name
149
174
 
175
+ @property
176
+ def first_back_ref(self) -> Optional[ConcreteQuantumStatement]:
177
+ return self.back_refs[0] if self.back_refs else None
178
+
179
+ @property
180
+ def level_(self) -> OperationLevel:
181
+ # Temp fix for currently "supported" statements
182
+ if self.name in {StatementType.CONTROL, StatementType.POWER}:
183
+ return OperationLevel.QMOD_STATEMENT
184
+
185
+ back_ref = self.first_back_ref
186
+ if back_ref is None:
187
+ # This is the expected behavior for the case where there's not back ref.
188
+ # We will use it once we can rely on the existence of the back ref in
189
+ # all expected cases (non-engine calls)
190
+ # return OperationLevel.ENGINE_FUNCTION_CALL
191
+ return self.level
192
+ if isinstance(back_ref, QuantumFunctionCall):
193
+ return OperationLevel.QMOD_FUNCTION_CALL
194
+ # Temp "fix" for assignment statements until we have a way to fully
195
+ # distinguish them and to properly display them
196
+ if isinstance(back_ref, QuantumExpressionOperation):
197
+ return OperationLevel.ENGINE_FUNCTION_CALL
198
+ return OperationLevel.QMOD_STATEMENT
199
+
150
200
  @property
151
201
  def registers(self) -> list[GeneratedRegister]:
152
202
  if self.generated_function is None:
@@ -165,15 +215,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
165
215
  return list()
166
216
  return self.generated_function.control_states
167
217
 
168
- @staticmethod
169
- def create_parameters_from_dict(
170
- parameters: dict[str, str]
171
- ) -> list[OperationParameter]:
172
- return [
173
- OperationParameter(label=key, value=value)
174
- for key, value in parameters.items()
175
- ]
176
-
177
218
  def propagate_absolute_qubits(self) -> "FunctionDebugInfoInterface":
178
219
  if self.absolute_qubits is None:
179
220
  return self
@@ -1,9 +1,11 @@
1
1
  import uuid
2
2
  from datetime import datetime, timezone
3
3
  from pathlib import Path
4
- from typing import Optional, Union
4
+ from typing import Any, Optional, Union
5
5
 
6
6
  import pydantic
7
+ from pydantic import model_validator
8
+ from pydantic_core.core_schema import ValidationInfo
7
9
  from typing_extensions import TypeAlias
8
10
 
9
11
  from classiq.interface.exceptions import (
@@ -36,6 +38,8 @@ from classiq.interface.ide.visual_model import CircuitMetrics
36
38
  RegisterName: TypeAlias = str
37
39
  InitialConditions: TypeAlias = dict[RegisterName, int]
38
40
 
41
+ OMIT_DEBUG_INFO_FLAG = "omit_debug_info"
42
+
39
43
 
40
44
  class TranspiledCircuitData(CircuitCodeInterface):
41
45
  depth: int
@@ -71,6 +75,19 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
71
75
  program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
72
76
  execution_primitives_input: Optional[PrimitivesInput] = pydantic.Field(default=None)
73
77
 
78
+ @model_validator(mode="before")
79
+ @classmethod
80
+ def remove_debug_info(
81
+ cls, data: dict[str, Any], info: ValidationInfo
82
+ ) -> dict[str, Any]:
83
+ if (
84
+ isinstance(data, dict)
85
+ and info.context is not None
86
+ and info.context.get(OMIT_DEBUG_INFO_FLAG, False)
87
+ ):
88
+ data.pop("debug_info", None)
89
+ return data
90
+
74
91
  def _hardware_agnostic_program_code(self) -> CodeAndSyntax:
75
92
  circuit_code = self.program_circuit.get_code_by_priority()
76
93
  if circuit_code is not None:
@@ -150,6 +150,7 @@ class Optimizer(IntEnum):
150
150
  L_BFGS_B = 3
151
151
  NELDER_MEAD = 4
152
152
  ADAM = 5
153
+ SLSQP = 6
153
154
 
154
155
 
155
156
  class Pauli(IntEnum):
@@ -1,5 +1,6 @@
1
1
  from collections import Counter
2
2
  from enum import Enum, EnumMeta, IntEnum
3
+ from typing import Any, Callable
3
4
 
4
5
  import pydantic
5
6
 
@@ -7,6 +8,33 @@ from classiq.interface.ast_node import HashableASTNode
7
8
  from classiq.interface.exceptions import ClassiqValueError
8
9
 
9
10
 
11
+ def rebuild_dynamic_enum(name: str, members: dict[str, int]) -> type[IntEnum]:
12
+ """
13
+ Rebuilds the dynamic enum from its name and members.
14
+ Returns a new enum type.
15
+ """
16
+ new_enum = IntEnum(name, members) # type: ignore[misc]
17
+ setattr(new_enum, "__members_data__", members) # noqa: B010
18
+ setattr(new_enum, "__reduce_ex__", dynamic_enum_reduce_ex) # noqa: B010
19
+ return new_enum
20
+
21
+
22
+ def dynamic_enum_reduce_ex(
23
+ obj: Any, protocol: int
24
+ ) -> tuple[Callable[..., type[IntEnum]], tuple[str, dict[str, int]]]:
25
+ """
26
+ Custom __reduce_ex__ for dynamic enums.
27
+ This function will be used when pickling an enum type or one of its members.
28
+ """
29
+ # Get the enum type and its member data.
30
+ enum_type = type(obj)
31
+ members = getattr(enum_type, "__members_data__", None)
32
+ if members is None:
33
+ raise ValueError("Dynamic enum is missing __members_data__ attribute")
34
+ # Return the callable and arguments needed to rebuild the enum type.
35
+ return rebuild_dynamic_enum, (enum_type.__name__, members)
36
+
37
+
10
38
  class EnumDeclaration(HashableASTNode):
11
39
  name: str
12
40
 
@@ -39,12 +67,15 @@ class EnumDeclaration(HashableASTNode):
39
67
 
40
68
  return members
41
69
 
42
- def create_enum(self) -> IntEnum:
43
- return IntEnum(self.name, self.members)
70
+ def create_enum(self) -> type[IntEnum]:
71
+ dynamic_enum = IntEnum(self.name, self.members) # type: ignore[misc]
72
+ setattr(dynamic_enum, "__members_data__", self.members) # noqa: B010
73
+ setattr(dynamic_enum, "__reduce_ex__", dynamic_enum_reduce_ex) # noqa: B010
74
+ return dynamic_enum
44
75
 
45
76
 
46
77
  def declaration_from_enum(enum_type: EnumMeta) -> EnumDeclaration:
47
- members: list[Enum] = list(enum_type)
78
+ members = _get_members(enum_type)
48
79
  return EnumDeclaration(
49
80
  name=enum_type.__name__,
50
81
  members={
@@ -52,3 +83,14 @@ def declaration_from_enum(enum_type: EnumMeta) -> EnumDeclaration:
52
83
  for member in sorted(members, key=lambda member: member.value)
53
84
  },
54
85
  )
86
+
87
+
88
+ def _get_members(enum_type: EnumMeta) -> list[Enum]:
89
+ members: list[Enum] = list(enum_type)
90
+ for member in members:
91
+ if not isinstance(member.value, int):
92
+ raise ClassiqValueError(
93
+ f"Member {member.name!r} of enum {enum_type.__name__!r} has a "
94
+ f"non-integer value {member.value!r}"
95
+ )
96
+ return members
@@ -7,7 +7,6 @@ from pydantic import ConfigDict
7
7
  from classiq.interface.enum_utils import StrEnum
8
8
  from classiq.interface.generator.generated_circuit_data import (
9
9
  OperationLevel,
10
- OperationParameter,
11
10
  )
12
11
  from classiq.interface.generator.hardware.hardware_data import SynthesisHardwareData
13
12
  from classiq.interface.helpers.versioned_model import VersionedModel
@@ -114,7 +113,6 @@ class Operation(pydantic.BaseModel):
114
113
  control_qubits: tuple[int, ...] = pydantic.Field(default_factory=tuple)
115
114
  auxiliary_qubits: tuple[int, ...]
116
115
  target_qubits: tuple[int, ...]
117
- parameters: list[OperationParameter] = pydantic.Field(default_factory=list)
118
116
  operation_level: OperationLevel
119
117
  operation_type: OperationType = pydantic.Field(
120
118
  description="Identifies unique operations that are visualized differently"
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Literal
2
2
 
3
- from classiq.interface.ast_node import ASTNodeType, without_statement_blocks
3
+ from classiq.interface.ast_node import ASTNodeType, reset_lists
4
4
  from classiq.interface.generator.expressions.expression import Expression
5
5
  from classiq.interface.model.quantum_statement import QuantumOperation
6
6
 
@@ -16,4 +16,4 @@ class ClassicalIf(QuantumOperation):
16
16
  else_: "StatementBlock"
17
17
 
18
18
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
19
- return without_statement_blocks(self, ["then", "else_"])
19
+ return reset_lists(self, ["then", "else_"])
@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Literal, Optional
2
2
 
3
3
  import pydantic
4
4
 
5
- from classiq.interface.ast_node import ASTNodeType, without_statement_blocks
5
+ from classiq.interface.ast_node import ASTNodeType, reset_lists
6
6
  from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
7
7
  from classiq.interface.model.quantum_expressions.quantum_expression import (
8
8
  QuantumExpressionOperation,
@@ -46,4 +46,4 @@ class Control(QuantumExpressionOperation):
46
46
  )
47
47
 
48
48
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
49
- return without_statement_blocks(self, ["body", "else_block"])
49
+ return reset_lists(self, ["body", "else_block"])
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Literal
2
2
 
3
- from classiq.interface.ast_node import ASTNodeType, without_statement_blocks
3
+ from classiq.interface.ast_node import ASTNodeType, reset_lists
4
4
  from classiq.interface.model.quantum_statement import QuantumOperation
5
5
 
6
6
  if TYPE_CHECKING:
@@ -13,4 +13,4 @@ class Invert(QuantumOperation):
13
13
  body: "StatementBlock"
14
14
 
15
15
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
16
- return without_statement_blocks(self, ["body"])
16
+ return reset_lists(self, ["body"])
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Literal
2
2
 
3
- from classiq.interface.ast_node import ASTNodeType, without_statement_blocks
3
+ from classiq.interface.ast_node import ASTNodeType, reset_lists
4
4
  from classiq.interface.generator.expressions.expression import Expression
5
5
  from classiq.interface.model.quantum_statement import QuantumOperation
6
6
 
@@ -15,4 +15,4 @@ class Power(QuantumOperation):
15
15
  body: "StatementBlock"
16
16
 
17
17
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
18
- return without_statement_blocks(self, ["body"])
18
+ return reset_lists(self, ["body"])
@@ -7,6 +7,7 @@ from typing import (
7
7
 
8
8
  import pydantic
9
9
 
10
+ from classiq.interface.ast_node import ASTNodeType, reset_lists
10
11
  from classiq.interface.exceptions import ClassiqError, ClassiqValueError
11
12
  from classiq.interface.generator.expressions.expression import Expression
12
13
  from classiq.interface.generator.functions.port_declaration import (
@@ -45,6 +46,9 @@ class QuantumFunctionCall(QuantumOperation):
45
46
  default=None
46
47
  )
47
48
 
49
+ def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
50
+ return reset_lists(self, ["positional_args"])
51
+
48
52
  @property
49
53
  def func_decl(self) -> QuantumFunctionDeclaration:
50
54
  if self._func_decl is None:
@@ -27,7 +27,7 @@ class QuantumStatement(ASTNode):
27
27
  *,
28
28
  update: Optional[dict[str, Any]] = None,
29
29
  deep: bool = False,
30
- keep_uuid: bool = False
30
+ keep_uuid: bool = False,
31
31
  ) -> Self:
32
32
  if not keep_uuid:
33
33
  update = update or dict()
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Literal
2
2
 
3
- from classiq.interface.ast_node import ASTNodeType, without_statement_blocks
3
+ from classiq.interface.ast_node import ASTNodeType, reset_lists
4
4
  from classiq.interface.generator.expressions.expression import Expression
5
5
  from classiq.interface.model.quantum_statement import QuantumOperation
6
6
 
@@ -16,4 +16,4 @@ class Repeat(QuantumOperation):
16
16
  body: "StatementBlock"
17
17
 
18
18
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
19
- return without_statement_blocks(self, ["body"])
19
+ return reset_lists(self, ["body"])
@@ -27,8 +27,8 @@ from classiq.interface.model.within_apply_operation import WithinApply
27
27
 
28
28
  ConcreteQuantumStatement = Annotated[
29
29
  Union[
30
- Allocate,
31
30
  QuantumFunctionCall,
31
+ Allocate,
32
32
  ArithmeticOperation,
33
33
  AmplitudeLoadingOperation,
34
34
  VariableDeclarationStatement,
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Literal
2
2
 
3
- from classiq.interface.ast_node import ASTNodeType, without_statement_blocks
3
+ from classiq.interface.ast_node import ASTNodeType, reset_lists
4
4
  from classiq.interface.model.quantum_statement import QuantumOperation
5
5
 
6
6
  if TYPE_CHECKING:
@@ -14,4 +14,4 @@ class WithinApply(QuantumOperation):
14
14
  action: "StatementBlock"
15
15
 
16
16
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
17
- return without_statement_blocks(self, ["compute", "action"])
17
+ return reset_lists(self, ["compute", "action"])
@@ -7,9 +7,6 @@ PROVIDERS_PREFIX = "/providers"
7
7
 
8
8
  IQCC_PREFIX = PROVIDERS_PREFIX + "/iqcc"
9
9
 
10
- EXECUTION_NON_VERSIONED_PREFIX = "/execution/v1"
11
- SYNTHESIS_NON_VERSIONED_PREFIX = "/synthesis/v1"
12
-
13
10
  ANALYZER_CIRCUIT_PAGE = "circuit"
14
11
  DEFAULT_IDE_FE_APP = "https://platform.classiq.io/"
15
12
 
@@ -58,9 +55,6 @@ TASKS_GENERATE_FULL_PATH = TASKS_GENERATE_SUFFIX
58
55
 
59
56
  EXECUTION_JOBS_SUFFIX = "/jobs"
60
57
  EXECUTION_JOBS_FULL_PATH = EXECUTION_PREFIX + EXECUTION_JOBS_SUFFIX
61
- EXECUTION_JOBS_NON_VERSIONED_FULL_PATH = (
62
- EXECUTION_NON_VERSIONED_PREFIX + EXECUTION_JOBS_SUFFIX
63
- )
64
58
 
65
59
  ANALYZER_FULL_PATH = ANALYZER_PREFIX + TASKS_SUFFIX
66
60
  ANALYZER_RB_FULL_PATH = ANALYZER_PREFIX + TASK_RB_SUFFIX
@@ -148,7 +148,8 @@ def emit_generative_statements(
148
148
  with _InterpreterExpandable(interpreter):
149
149
  set_frontend_interpreter(interpreter)
150
150
  for block_name, generative_function in operation.generative_blocks.items():
151
- with interpreter._builder.block_context(
152
- block_name
153
- ), generative_mode_context(True):
151
+ with (
152
+ interpreter._builder.block_context(block_name),
153
+ generative_mode_context(True),
154
+ ):
154
155
  generative_function._py_callable(*python_qmod_args)
@@ -21,8 +21,11 @@ from classiq.interface.model.model import Model
21
21
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
22
22
  from classiq.interface.model.phase_operation import PhaseOperation
23
23
  from classiq.interface.model.power import Power
24
- from classiq.interface.model.quantum_expressions.quantum_expression import (
25
- QuantumAssignmentOperation,
24
+ from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
25
+ AmplitudeLoadingOperation,
26
+ )
27
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
28
+ ArithmeticOperation,
26
29
  )
27
30
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
28
31
  from classiq.interface.model.quantum_function_declaration import (
@@ -55,7 +58,17 @@ from classiq.model_expansions.quantum_operations import (
55
58
  VariableDeclarationStatementEmitter,
56
59
  )
57
60
  from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
58
- from classiq.model_expansions.quantum_operations.shallow_emitter import ShallowEmitter
61
+ from classiq.model_expansions.quantum_operations.assignment_result_processor import (
62
+ AssignmentResultProcessor,
63
+ )
64
+ from classiq.model_expansions.quantum_operations.block_evaluator import BlockEvaluator
65
+ from classiq.model_expansions.quantum_operations.composite_emitter import (
66
+ CompositeEmitter,
67
+ )
68
+ from classiq.model_expansions.quantum_operations.expression_evaluator import (
69
+ ExpressionEvaluator,
70
+ )
71
+ from classiq.model_expansions.quantum_operations.handle_evaluator import HandleEvaluator
59
72
  from classiq.model_expansions.scope import Evaluated, Scope
60
73
  from classiq.model_expansions.scope_initialization import (
61
74
  add_constants_to_scope,
@@ -134,15 +147,39 @@ class GenerativeInterpreter(BaseInterpreter):
134
147
  BindEmitter(self).emit(bind)
135
148
 
136
149
  @emit.register
137
- def emit_quantum_assignment_operation(self, op: QuantumAssignmentOperation) -> None:
138
- ShallowEmitter(
139
- self, "assignment_operation", components=["expression", "result_var"]
150
+ def emit_amplitude_loading_operation(self, op: AmplitudeLoadingOperation) -> None:
151
+ CompositeEmitter[AmplitudeLoadingOperation](
152
+ self,
153
+ [
154
+ HandleEvaluator(self, "result_var"),
155
+ ExpressionEvaluator(self, "expression"),
156
+ AssignmentResultProcessor(self),
157
+ ],
158
+ ).emit(op)
159
+
160
+ @emit.register
161
+ def _emit_arithmetic_operation(self, op: ArithmeticOperation) -> None:
162
+ self.emit_arithmetic_operation(op)
163
+
164
+ def emit_arithmetic_operation(self, op: ArithmeticOperation) -> None:
165
+ CompositeEmitter[ArithmeticOperation](
166
+ self,
167
+ [
168
+ HandleEvaluator(self, "result_var"),
169
+ ExpressionEvaluator(self, "expression"),
170
+ AssignmentResultProcessor(self),
171
+ ],
140
172
  ).emit(op)
141
173
 
142
174
  @emit.register
143
175
  def emit_inplace_binary_operation(self, op: InplaceBinaryOperation) -> None:
144
- ShallowEmitter(
145
- self, "inplace_binary_operation", components=["target", "value"]
176
+ CompositeEmitter[InplaceBinaryOperation](
177
+ self,
178
+ [
179
+ HandleEvaluator(self, "target"),
180
+ HandleEvaluator(self, "value"),
181
+ ExpressionEvaluator(self, "value"),
182
+ ],
146
183
  ).emit(op)
147
184
 
148
185
  @emit.register
@@ -157,37 +194,60 @@ class GenerativeInterpreter(BaseInterpreter):
157
194
 
158
195
  @emit.register
159
196
  def emit_within_apply(self, within_apply: WithinApply) -> None:
160
- ShallowEmitter(
197
+ BlockEvaluator(
161
198
  self,
162
199
  WITHIN_APPLY_NAME,
163
- components=["within", "apply", "compute", "action"],
200
+ "within",
201
+ "apply",
202
+ "compute",
203
+ "action",
164
204
  ).emit(within_apply)
165
205
 
166
206
  @emit.register
167
207
  def emit_invert(self, invert: Invert) -> None:
168
- ShallowEmitter(self, INVERT_OPERATOR_NAME, components=["body"]).emit(invert)
208
+ BlockEvaluator(self, INVERT_OPERATOR_NAME, "body").emit(invert)
169
209
 
170
210
  @emit.register
171
211
  def emit_repeat(self, repeat: Repeat) -> None:
172
212
  RepeatEmitter(self).emit(repeat)
173
213
 
174
214
  @emit.register
215
+ def _emit_control(self, control: Control) -> None:
216
+ self.emit_control(control)
217
+
175
218
  def emit_control(self, control: Control) -> None:
176
- ShallowEmitter(
219
+ CompositeEmitter[Control](
177
220
  self,
178
- CONTROL_OPERATOR_NAME,
179
- components=["expression", "body", "else_block"],
221
+ [
222
+ ExpressionEvaluator(self, "expression"),
223
+ BlockEvaluator(
224
+ self,
225
+ CONTROL_OPERATOR_NAME,
226
+ "body",
227
+ "else_block",
228
+ ),
229
+ ],
180
230
  ).emit(control)
181
231
 
182
232
  @emit.register
183
233
  def emit_power(self, power: Power) -> None:
184
- ShallowEmitter(self, CONTROL_OPERATOR_NAME, components=["power", "body"]).emit(
185
- power
186
- )
234
+ CompositeEmitter[Power](
235
+ self,
236
+ [
237
+ ExpressionEvaluator(self, "power"),
238
+ BlockEvaluator(self, CONTROL_OPERATOR_NAME, "body"),
239
+ ],
240
+ ).emit(power)
187
241
 
188
242
  @emit.register
189
243
  def emit_phase(self, phase: PhaseOperation) -> None:
190
- ShallowEmitter(self, "phase", components=["expression", "theta"]).emit(phase)
244
+ CompositeEmitter[PhaseOperation](
245
+ self,
246
+ [
247
+ ExpressionEvaluator(self, "expression"),
248
+ ExpressionEvaluator(self, "theta"),
249
+ ],
250
+ ).emit(phase)
191
251
 
192
252
  def _expand_body(self, operation: Closure) -> None:
193
253
  if isinstance(operation, FunctionClosure) and operation.name == "permute":
@@ -12,7 +12,7 @@ from classiq.model_expansions.scope import QuantumSymbol
12
12
 
13
13
 
14
14
  class AllocateEmitter(Emitter[Allocate]):
15
- def emit(self, allocate: Allocate, /) -> None:
15
+ def emit(self, allocate: Allocate, /) -> bool:
16
16
  target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
17
17
  QuantumSymbol
18
18
  )
@@ -32,6 +32,7 @@ class AllocateEmitter(Emitter[Allocate]):
32
32
  )
33
33
  self._register_debug_info(allocate)
34
34
  self.emit_statement(allocate)
35
+ return True
35
36
 
36
37
  def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> int:
37
38
  if size is None:
@@ -70,4 +71,5 @@ class AllocateEmitter(Emitter[Allocate]):
70
71
  level=OperationLevel.QMOD_STATEMENT,
71
72
  is_allocate_or_free=True,
72
73
  port_to_passed_variable_map={"ARG": str(allocate.target)},
74
+ node=allocate._as_back_ref(),
73
75
  )
@@ -0,0 +1,52 @@
1
+ from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
2
+ from classiq.interface.generator.functions.port_declaration import (
3
+ PortDeclarationDirection,
4
+ )
5
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
6
+ ArithmeticOperation,
7
+ ArithmeticOperationKind,
8
+ )
9
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
10
+ QuantumAssignmentOperation,
11
+ )
12
+
13
+ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
14
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
15
+ from classiq.model_expansions.scope import QuantumSymbol
16
+ from classiq.model_expansions.transformers.ast_renamer import rename_variables
17
+
18
+
19
+ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
20
+ def emit(self, op: QuantumAssignmentOperation, /) -> bool:
21
+ if (
22
+ isinstance(op, ArithmeticOperation)
23
+ and op.operation_kind == ArithmeticOperationKind.Assignment
24
+ ):
25
+ direction = PortDeclarationDirection.Output
26
+ self._update_result_type(op)
27
+ else:
28
+ direction = PortDeclarationDirection.Inout
29
+ self._capture_handle(op.result_var, direction)
30
+ return False
31
+
32
+ def _update_result_type(self, op: ArithmeticOperation) -> None:
33
+ expr = self._evaluate_expression(op.expression)
34
+ symbols = self._get_symbols_in_expression(expr)
35
+ expr_str = rename_variables(
36
+ expr.expr,
37
+ {str(symbol.handle): symbol.handle.identifier for symbol in symbols}
38
+ | {symbol.handle.qmod_expr: symbol.handle.identifier for symbol in symbols},
39
+ )
40
+ for symbol in symbols:
41
+ expr_str = expr_str.replace(
42
+ symbol.handle.qmod_expr, symbol.handle.identifier
43
+ )
44
+ result_type = compute_arithmetic_result_type(
45
+ expr_str,
46
+ {symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
47
+ self._machine_precision,
48
+ )
49
+ result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
50
+ copy_type_information(
51
+ result_type, result_symbol.quantum_type, str(op.result_var)
52
+ )