classiq 0.46.1__py3-none-any.whl → 0.48.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. classiq/_internals/api_wrapper.py +45 -8
  2. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -7
  3. classiq/applications/grover/grover_model_constructor.py +2 -1
  4. classiq/execution/execution_session.py +133 -45
  5. classiq/execution/jobs.py +120 -1
  6. classiq/interface/_version.py +1 -1
  7. classiq/interface/backend/quantum_backend_providers.py +0 -1
  8. classiq/interface/debug_info/debug_info.py +23 -1
  9. classiq/interface/execution/primitives.py +17 -0
  10. classiq/interface/executor/iqae_result.py +3 -3
  11. classiq/interface/executor/result.py +3 -1
  12. classiq/interface/generator/arith/arithmetic_operations.py +5 -2
  13. classiq/interface/generator/arith/binary_ops.py +21 -14
  14. classiq/interface/generator/arith/extremum_operations.py +9 -1
  15. classiq/interface/generator/arith/number_utils.py +6 -0
  16. classiq/interface/generator/arith/register_user_input.py +30 -21
  17. classiq/interface/generator/arith/unary_ops.py +13 -1
  18. classiq/interface/generator/expressions/expression.py +8 -0
  19. classiq/interface/generator/functions/type_name.py +1 -3
  20. classiq/interface/generator/generated_circuit_data.py +47 -2
  21. classiq/interface/generator/quantum_program.py +10 -2
  22. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +17 -3
  23. classiq/interface/ide/visual_model.py +10 -5
  24. classiq/interface/interface_version.py +1 -1
  25. classiq/interface/model/bind_operation.py +0 -3
  26. classiq/interface/model/phase_operation.py +11 -0
  27. classiq/interface/model/port_declaration.py +1 -12
  28. classiq/interface/model/quantum_expressions/arithmetic_operation.py +34 -6
  29. classiq/interface/model/quantum_lambda_function.py +4 -1
  30. classiq/interface/model/quantum_statement.py +16 -1
  31. classiq/interface/model/quantum_variable_declaration.py +0 -22
  32. classiq/interface/model/statement_block.py +3 -0
  33. classiq/interface/server/global_versions.py +4 -4
  34. classiq/interface/server/routes.py +0 -3
  35. classiq/model_expansions/capturing/propagated_var_stack.py +5 -2
  36. classiq/model_expansions/closure.py +7 -2
  37. classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
  38. classiq/model_expansions/generative_functions.py +146 -28
  39. classiq/model_expansions/interpreter.py +17 -5
  40. classiq/model_expansions/quantum_operations/classicalif.py +27 -10
  41. classiq/model_expansions/quantum_operations/control.py +22 -15
  42. classiq/model_expansions/quantum_operations/emitter.py +68 -7
  43. classiq/model_expansions/quantum_operations/expression_operation.py +25 -16
  44. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +167 -95
  45. classiq/model_expansions/quantum_operations/invert.py +12 -6
  46. classiq/model_expansions/quantum_operations/phase.py +189 -0
  47. classiq/model_expansions/quantum_operations/power.py +9 -8
  48. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +20 -5
  49. classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
  50. classiq/model_expansions/quantum_operations/repeat.py +32 -13
  51. classiq/model_expansions/quantum_operations/within_apply.py +19 -6
  52. classiq/model_expansions/scope.py +16 -5
  53. classiq/model_expansions/scope_initialization.py +11 -1
  54. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +23 -1
  55. classiq/model_expansions/visitors/variable_references.py +11 -7
  56. classiq/qmod/builtins/__init__.py +10 -0
  57. classiq/qmod/builtins/constants.py +10 -0
  58. classiq/qmod/builtins/functions/state_preparation.py +4 -1
  59. classiq/qmod/builtins/operations.py +55 -161
  60. classiq/qmod/create_model_function.py +1 -1
  61. classiq/qmod/generative.py +14 -5
  62. classiq/qmod/native/pretty_printer.py +14 -4
  63. classiq/qmod/pretty_print/pretty_printer.py +14 -4
  64. classiq/qmod/qmod_constant.py +28 -18
  65. classiq/qmod/qmod_variable.py +43 -23
  66. classiq/qmod/quantum_expandable.py +14 -1
  67. classiq/qmod/semantics/static_semantics_visitor.py +10 -0
  68. classiq/qmod/semantics/validation/constants_validation.py +16 -0
  69. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/METADATA +9 -4
  70. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/RECORD +71 -66
  71. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/WHEEL +0 -0
@@ -1,29 +1,7 @@
1
- from typing import Any, Mapping, Optional
2
-
3
- import pydantic
4
-
5
1
  from classiq.interface.ast_node import ASTNode
6
- from classiq.interface.generator.expressions.expression import Expression
7
2
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
8
- from classiq.interface.model.quantum_type import (
9
- QuantumBitvector,
10
- QuantumNumeric,
11
- )
12
3
 
13
4
 
14
5
  class QuantumVariableDeclaration(ASTNode):
15
6
  name: str
16
7
  quantum_type: ConcreteQuantumType
17
- size: Optional[Expression] = pydantic.Field(default=None, exclude=True)
18
-
19
- @pydantic.validator("size")
20
- def _propagate_size_to_type(
21
- cls, size: Optional[Expression], values: Mapping[str, Any]
22
- ) -> Optional[Expression]:
23
- if size is not None:
24
- quantum_type = values.get("quantum_type")
25
- if isinstance(quantum_type, QuantumBitvector):
26
- quantum_type.length = size
27
- elif isinstance(quantum_type, QuantumNumeric):
28
- quantum_type.size = size
29
- return size
@@ -9,6 +9,7 @@ from classiq.interface.model.control import Control
9
9
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
10
10
  from classiq.interface.model.invert import Invert
11
11
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
12
+ from classiq.interface.model.phase_operation import PhaseOperation
12
13
  from classiq.interface.model.power import Power
13
14
  from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
14
15
  AmplitudeLoadingOperation,
@@ -38,6 +39,7 @@ ConcreteQuantumStatement = Annotated[
38
39
  ClassicalIf,
39
40
  Control,
40
41
  WithinApply,
42
+ PhaseOperation,
41
43
  ],
42
44
  Field(..., discriminator="kind"),
43
45
  ]
@@ -52,3 +54,4 @@ Invert.update_forward_refs(StatementBlock=StatementBlock)
52
54
  WithinApply.update_forward_refs(StatementBlock=StatementBlock)
53
55
  ClassicalIf.update_forward_refs(StatementBlock=StatementBlock)
54
56
  NativeFunctionDefinition.update_forward_refs(StatementBlock=StatementBlock)
57
+ PhaseOperation.update_forward_refs(StatementBlock=StatementBlock)
@@ -1,13 +1,13 @@
1
- from datetime import date, datetime
2
- from typing import Any, Dict, Union
1
+ from datetime import date
2
+ from typing import Any, Dict
3
3
 
4
4
  import pydantic
5
5
  from pydantic import BaseModel
6
6
 
7
7
 
8
8
  class DeprecationInfo(BaseModel):
9
- deprecation_date: Union[datetime, date] = pydantic.Field()
10
- removal_date: Union[datetime, date] = pydantic.Field()
9
+ deprecation_date: date = pydantic.Field()
10
+ removal_date: date = pydantic.Field()
11
11
 
12
12
 
13
13
  class GlobalVersions(BaseModel):
@@ -17,9 +17,6 @@ ANALYZER_HC_TABLE_GRAPH_FULL_PATH = ANALYZER_PREFIX + ANALYZER_HC_TABLE_GRAPH
17
17
 
18
18
  ANALYZER_HC_GRAPH_NEW = "/graphs/hardware_connectivity/new"
19
19
 
20
- ANALYZER_OPTIONAL_DEVICES = "/graphs/available_devices"
21
- ANALYZER_OPTIONAL_DEVICES_FULL_PATH = ANALYZER_PREFIX + ANALYZER_OPTIONAL_DEVICES
22
-
23
20
  TASKS_SUFFIX = "/tasks"
24
21
  RB = "/rb"
25
22
  ANALYZER_DATA_TASK = f"{TASKS_SUFFIX}/data"
@@ -15,7 +15,7 @@ from classiq.interface.model.quantum_function_call import ArgValue
15
15
  from classiq.interface.model.quantum_statement import QuantumOperation
16
16
 
17
17
  from classiq.model_expansions.capturing.mangling_utils import mangle_captured_var_name
18
- from classiq.model_expansions.closure import FunctionClosure
18
+ from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
19
19
  from classiq.model_expansions.function_builder import OperationBuilder
20
20
  from classiq.model_expansions.scope import QuantumSymbol, Scope
21
21
 
@@ -144,7 +144,10 @@ class PropagatedVarStack:
144
144
  def _get_propagated_var_name(self, var: PropagatedVariable) -> str:
145
145
  if (
146
146
  var.defining_function == self._builder.current_function.name
147
- or self._no_name_conflict(var)
147
+ or not isinstance(
148
+ self._builder.current_function, GenerativeFunctionClosure
149
+ ) # FIXME doesn't work for all cases (CAD-22663)
150
+ and self._no_name_conflict(var)
148
151
  ):
149
152
  handle_name = var.name
150
153
  if var in self._to_mangle:
@@ -40,6 +40,11 @@ class Closure:
40
40
  }
41
41
 
42
42
 
43
+ @dataclass(frozen=True)
44
+ class GenerativeClosure(Closure):
45
+ generative_blocks: Dict[str, GenerativeQFunc] = None # type:ignore[assignment]
46
+
47
+
43
48
  @dataclass(frozen=True)
44
49
  class FunctionClosure(Closure):
45
50
  is_lambda: bool = False
@@ -101,8 +106,8 @@ class FunctionClosure(Closure):
101
106
 
102
107
 
103
108
  @dataclass(frozen=True)
104
- class GenerativeFunctionClosure(FunctionClosure):
105
- generative_function: GenerativeQFunc = None # type:ignore[assignment]
109
+ class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
110
+ pass
106
111
 
107
112
 
108
113
  NestedFunctionClosureT = Union[FunctionClosure, List["NestedFunctionClosureT"]]
@@ -10,7 +10,6 @@ from classiq.interface.generator.functions.type_name import (
10
10
  TypeName,
11
11
  )
12
12
  from classiq.interface.model.bind_operation import BindOperation
13
- from classiq.interface.model.inplace_binary_operation import BinaryOperation
14
13
  from classiq.interface.model.quantum_type import (
15
14
  QuantumBit,
16
15
  QuantumBitvector,
@@ -227,9 +226,3 @@ def validate_inplace_binary_op_vars(
227
226
  raise ClassiqExpansionError(
228
227
  f"Cannot perform `{operation_name}` operation with non numeric target {target_var.handle}"
229
228
  )
230
- if operation_name != BinaryOperation.Xor.value and (
231
- value_var.quantum_type.sign_value or target_var.quantum_type.sign_value
232
- ):
233
- raise ClassiqExpansionError(
234
- f"Cannot perform `{operation_name}` operation with signed variables"
235
- )
@@ -1,51 +1,82 @@
1
- from typing import TYPE_CHECKING, Any, List
1
+ from typing import TYPE_CHECKING, Any, List, Mapping
2
2
 
3
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
3
4
  from classiq.interface.generator.expressions.qmod_struct_instance import (
4
5
  QmodStructInstance,
5
6
  )
6
7
  from classiq.interface.generator.functions.type_name import Struct
8
+ from classiq.interface.generator.visitor import Visitor
7
9
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
8
10
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
9
11
  from classiq.interface.model.port_declaration import PortDeclaration
12
+ from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
10
13
  from classiq.interface.model.quantum_function_declaration import (
11
14
  PositionalArg,
12
15
  QuantumFunctionDeclaration,
13
16
  QuantumOperandDeclaration,
14
17
  )
18
+ from classiq.interface.model.quantum_lambda_function import (
19
+ QuantumCallable,
20
+ QuantumLambdaFunction,
21
+ )
15
22
  from classiq.interface.model.quantum_statement import QuantumStatement
16
23
 
17
- from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
24
+ from classiq.model_expansions.closure import (
25
+ FunctionClosure,
26
+ GenerativeClosure,
27
+ GenerativeFunctionClosure,
28
+ )
18
29
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol
19
30
  from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
20
31
  from classiq.qmod.model_state_container import QMODULE
21
32
  from classiq.qmod.qmod_parameter import CParamStruct
22
- from classiq.qmod.qmod_variable import get_qvar
33
+ from classiq.qmod.qmod_variable import QNum, _create_qvar_for_qtype
23
34
  from classiq.qmod.quantum_callable import QCallable
24
- from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
35
+ from classiq.qmod.quantum_expandable import (
36
+ QExpandable,
37
+ QLambdaFunction,
38
+ QTerminalCallable,
39
+ )
25
40
  from classiq.qmod.quantum_function import QFunc
26
41
  from classiq.qmod.semantics.static_semantics_visitor import resolve_function_calls
42
+ from classiq.qmod.symbolic_expr import SymbolicExpr
27
43
 
28
44
  if TYPE_CHECKING:
29
45
  from classiq.model_expansions.interpreter import Interpreter
30
46
 
31
47
 
48
+ class LenList(list):
49
+ @property
50
+ def len(self) -> int:
51
+ return len(self)
52
+
53
+ def __getitem__(self, item: Any) -> Any:
54
+ if isinstance(item, QNum):
55
+ return SymbolicExpr(f"{self}[{item}]", True)
56
+ return super().__getitem__(item)
57
+
58
+ @classmethod
59
+ def wrap(cls, obj: Any) -> Any:
60
+ if not isinstance(obj, list):
61
+ return obj
62
+ return LenList([cls.wrap(item) for item in obj])
63
+
64
+
32
65
  def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated) -> Any:
33
66
  if isinstance(param, PortDeclaration):
34
67
  quantum_symbol = evaluated.as_type(QuantumSymbol)
35
- return get_qvar(quantum_symbol.quantum_type, quantum_symbol.handle)
68
+ return _create_qvar_for_qtype(
69
+ quantum_symbol.quantum_type, quantum_symbol.handle
70
+ )
36
71
  if isinstance(param, QuantumOperandDeclaration):
37
72
  if param.is_list:
38
- func_list: List[FunctionClosure] = evaluated.as_type(list)
39
- return [
40
- QTerminalCallable(
41
- QuantumFunctionDeclaration(
42
- name=param.name,
43
- positional_arg_declarations=param.positional_arg_declarations,
44
- ),
45
- index_=idx,
46
- )
47
- for idx, func in enumerate(func_list)
48
- ]
73
+ return QTerminalCallable(
74
+ QuantumOperandDeclaration(
75
+ name=param.name,
76
+ positional_arg_declarations=param.positional_arg_declarations,
77
+ is_list=True,
78
+ ),
79
+ )
49
80
  else:
50
81
  func = evaluated.as_type(FunctionClosure)
51
82
  return QTerminalCallable(
@@ -61,7 +92,7 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
61
92
  struct_type=Struct(name=classical_value.struct_declaration.name),
62
93
  qmodule=QMODULE,
63
94
  )
64
- return classical_value
95
+ return LenList.wrap(classical_value)
65
96
 
66
97
 
67
98
  class _InterpreterExpandable(QFunc):
@@ -70,23 +101,32 @@ class _InterpreterExpandable(QFunc):
70
101
  self._interpreter = interpreter
71
102
 
72
103
  def append_statement_to_body(self, stmt: QuantumStatement) -> None:
73
- current_function = self._interpreter._builder.current_function
104
+ current_operation = self._interpreter._builder._operations[-1]
74
105
  dummy_function = NativeFunctionDefinition(
75
- name=current_function.name,
76
- positional_arg_declarations=current_function.positional_arg_declarations,
106
+ name=current_operation.name,
107
+ positional_arg_declarations=current_operation.positional_arg_declarations,
77
108
  body=self._interpreter._builder._current_statements + [stmt],
78
109
  )
79
- resolve_function_calls(
80
- dummy_function,
81
- nameables_to_dict(self._interpreter._get_function_declarations()),
82
- )
110
+ resolve_function_calls(dummy_function, self._get_function_declarations())
83
111
  stmt = dummy_function.body[-1]
84
- self._interpreter.emit_statement(stmt)
112
+ with generative_mode_context(False):
113
+ self._interpreter.emit_statement(stmt)
114
+
115
+ def _get_function_declarations(self) -> Mapping[str, QuantumFunctionDeclaration]:
116
+ return {
117
+ name: QuantumFunctionDeclaration(
118
+ name=name,
119
+ positional_arg_declarations=evaluated.value.positional_arg_declarations,
120
+ )
121
+ for name, evaluated in self._interpreter._current_scope.items()
122
+ if isinstance(evaluated, Evaluated)
123
+ and isinstance(evaluated.value, FunctionClosure)
124
+ } | nameables_to_dict(self._interpreter._get_function_declarations())
85
125
 
86
126
 
87
127
  def emit_generative_statements(
88
128
  interpreter: "Interpreter",
89
- operation: GenerativeFunctionClosure,
129
+ operation: GenerativeClosure,
90
130
  args: List[Evaluated],
91
131
  ) -> None:
92
132
  python_qmod_args = [
@@ -97,5 +137,83 @@ def emit_generative_statements(
97
137
  QExpandable.STACK.append(interpreter_expandable)
98
138
  QCallable.CURRENT_EXPANDABLE = interpreter_expandable
99
139
  set_frontend_interpreter(interpreter)
100
- with interpreter._builder.block_context("body"), generative_mode_context(True):
101
- operation.generative_function._py_callable(*python_qmod_args)
140
+ for block_name, generative_function in operation.generative_blocks.items():
141
+ with interpreter._builder.block_context(block_name), generative_mode_context(
142
+ True
143
+ ):
144
+ generative_function._py_callable(*python_qmod_args)
145
+
146
+
147
+ def emit_operands_as_declarative(
148
+ interpreter: "Interpreter", param: PositionalArg, arg: Evaluated
149
+ ) -> ArgValue:
150
+ if not isinstance(param, QuantumOperandDeclaration):
151
+ return arg.emit()
152
+ value = arg.value
153
+ if isinstance(value, list):
154
+ return [
155
+ _expand_operand_as_declarative(interpreter, param, item) for item in value
156
+ ]
157
+ if isinstance(value, GenerativeFunctionClosure):
158
+ return _expand_operand_as_declarative(interpreter, param, value)
159
+ if isinstance(value, FunctionClosure):
160
+ if value.is_lambda:
161
+ raise ClassiqInternalExpansionError
162
+ _register_declarative_function(interpreter, value.name)
163
+ return value.name
164
+ raise ClassiqInternalExpansionError
165
+
166
+
167
+ def _expand_operand_as_declarative(
168
+ interpreter: "Interpreter",
169
+ param: QuantumOperandDeclaration,
170
+ arg: GenerativeFunctionClosure,
171
+ ) -> QuantumCallable:
172
+ if not arg.is_lambda:
173
+ _register_declarative_function(interpreter, arg.name)
174
+ return arg.name
175
+ val = QLambdaFunction(param, arg.generative_blocks["body"]._py_callable)
176
+ with generative_mode_context(False):
177
+ val.expand()
178
+ _DecFuncVisitor(interpreter).visit(val.body)
179
+ qlambda = QuantumLambdaFunction(
180
+ pos_rename_params=val.infer_rename_params(),
181
+ body=val.body,
182
+ )
183
+ qlambda.set_op_decl(param)
184
+ return qlambda
185
+
186
+
187
+ def _register_declarative_function(interpreter: "Interpreter", func_name: str) -> None:
188
+ if func_name in nameables_to_dict(interpreter._expanded_functions):
189
+ return
190
+
191
+ for user_gen_func in interpreter._generative_functions:
192
+ if user_gen_func.func_decl.name == func_name:
193
+ break
194
+ else:
195
+ return
196
+
197
+ with generative_mode_context(False):
198
+ dec_func = QFunc(user_gen_func._py_callable)
199
+ dec_func.expand()
200
+ dec_func_def = QMODULE.native_defs[func_name]
201
+ interpreter._expanded_functions.append(dec_func_def)
202
+ _DecFuncVisitor(interpreter).visit(dec_func_def)
203
+
204
+
205
+ class _DecFuncVisitor(Visitor):
206
+ def __init__(self, interpreter: "Interpreter"):
207
+ self._interpreter = interpreter
208
+
209
+ def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
210
+ _register_declarative_function(self._interpreter, call.func_name)
211
+ for arg in call.positional_args:
212
+ if isinstance(arg, str):
213
+ arg = [arg]
214
+ if isinstance(arg, list):
215
+ for possible_func_name in arg:
216
+ if isinstance(possible_func_name, str):
217
+ _register_declarative_function(
218
+ self._interpreter, possible_func_name
219
+ )
@@ -24,6 +24,7 @@ from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperat
24
24
  from classiq.interface.model.invert import Invert
25
25
  from classiq.interface.model.model import Model
26
26
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
27
+ from classiq.interface.model.phase_operation import PhaseOperation
27
28
  from classiq.interface.model.power import Power
28
29
  from classiq.interface.model.quantum_expressions.quantum_expression import (
29
30
  QuantumAssignmentOperation,
@@ -48,6 +49,7 @@ from classiq.model_expansions.capturing.propagated_var_stack import PropagatedVa
48
49
  from classiq.model_expansions.closure import (
49
50
  Closure,
50
51
  FunctionClosure,
52
+ GenerativeClosure,
51
53
  GenerativeFunctionClosure,
52
54
  )
53
55
  from classiq.model_expansions.debug_flag import debug_mode
@@ -74,6 +76,7 @@ from classiq.model_expansions.quantum_operations import (
74
76
  VariableDeclarationStatementEmitter,
75
77
  WithinApplyEmitter,
76
78
  )
79
+ from classiq.model_expansions.quantum_operations.phase import PhaseEmitter
77
80
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
78
81
  from classiq.model_expansions.scope_initialization import (
79
82
  add_entry_point_params_to_scope,
@@ -82,7 +85,6 @@ from classiq.model_expansions.scope_initialization import (
82
85
  )
83
86
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
84
87
  from classiq.qmod.builtins.functions import permute
85
- from classiq.qmod.generative import is_generative_mode
86
88
  from classiq.qmod.quantum_function import GenerativeQFunc
87
89
  from classiq.qmod.semantics.error_manager import ErrorManager
88
90
 
@@ -95,8 +97,12 @@ STATEMENT_TYPES_FOR_SOURCE_REFERENCE_PROPAGATION: Tuple[Type[QuantumStatement],
95
97
 
96
98
  class Interpreter:
97
99
  def __init__(
98
- self, model: Model, generative_functions: Optional[List[GenerativeQFunc]] = None
100
+ self,
101
+ model: Model,
102
+ generative_functions: Optional[List[GenerativeQFunc]] = None,
103
+ is_frontend: bool = False,
99
104
  ) -> None:
105
+ self._is_frontend = is_frontend
100
106
  self._model = model
101
107
  self._current_scope = Scope()
102
108
  self._builder = OperationBuilder()
@@ -200,10 +206,12 @@ class Interpreter:
200
206
 
201
207
  closure_class: Type[FunctionClosure]
202
208
  extra_args: dict[str, Any]
203
- if is_generative_mode():
209
+ if function.is_generative():
204
210
  closure_class = GenerativeFunctionClosure
205
211
  extra_args = {
206
- "generative_function": GenerativeQFunc(function.py_callable, func_decl)
212
+ "generative_blocks": {
213
+ "body": GenerativeQFunc(function.py_callable, func_decl),
214
+ }
207
215
  }
208
216
  else:
209
217
  closure_class = FunctionClosure
@@ -307,6 +315,10 @@ class Interpreter:
307
315
  def emit_power(self, power: Power) -> None:
308
316
  PowerEmitter(self).emit(power)
309
317
 
318
+ @emit.register
319
+ def emit_phase(self, phase: PhaseOperation) -> None:
320
+ PhaseEmitter(self).emit(phase)
321
+
310
322
  def _expand_block(self, block: Sequence[QuantumStatement], block_name: str) -> None:
311
323
  with self._builder.block_context(block_name):
312
324
  for statement in block:
@@ -331,7 +343,7 @@ class Interpreter:
331
343
  # special expansion since permute is generative
332
344
  with self._scope_guard(operation.scope):
333
345
  self._expand_permute()
334
- elif isinstance(operation, GenerativeFunctionClosure):
346
+ elif isinstance(operation, GenerativeClosure):
335
347
  with self._scope_guard(operation.scope):
336
348
  args = [
337
349
  self.evaluate(param.name)
@@ -1,15 +1,15 @@
1
- from typing import List
1
+ from typing import Sequence
2
2
 
3
3
  from classiq.interface.model.classical_if import ClassicalIf
4
4
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
5
- from classiq.interface.model.statement_block import ConcreteQuantumStatement
5
+ from classiq.interface.model.quantum_statement import QuantumStatement
6
6
 
7
7
  from classiq.model_expansions.closure import FunctionClosure
8
8
  from classiq.model_expansions.quantum_operations.emitter import Emitter
9
9
  from classiq.model_expansions.scope import Scope
10
10
 
11
11
 
12
- def _is_all_identity_calls(body: List[ConcreteQuantumStatement]) -> bool:
12
+ def _is_all_identity_calls(body: Sequence[QuantumStatement]) -> bool:
13
13
  return all(
14
14
  isinstance(stmt, QuantumFunctionCall) and stmt.func_name.lower() == "identity"
15
15
  for stmt in body
@@ -18,22 +18,39 @@ def _is_all_identity_calls(body: List[ConcreteQuantumStatement]) -> bool:
18
18
 
19
19
  class ClassicalIfEmitter(Emitter[ClassicalIf]):
20
20
  def emit(self, classical_if: ClassicalIf, /) -> None:
21
+ with self._propagated_var_stack.capture_variables(classical_if):
22
+ self._emit_propagated(classical_if)
23
+
24
+ def _emit_propagated(self, classical_if: ClassicalIf) -> None:
21
25
  condition = self._interpreter.evaluate(classical_if.condition).as_type(bool)
22
- body: List[ConcreteQuantumStatement] = (
23
- classical_if.then if condition else classical_if.else_
24
- )
26
+ op_name = "then" if condition else "else"
27
+ is_generative = classical_if.is_generative()
28
+
29
+ body: Sequence[QuantumStatement]
30
+ if is_generative:
31
+ if not classical_if.has_generative_block(op_name):
32
+ return
33
+ context = self._register_generative_context(classical_if, op_name, op_name)
34
+ context.blocks["body"] = context.blocks[op_name]
35
+ context.blocks.pop(op_name)
36
+ body = context.statements("body")
37
+ else:
38
+ body = classical_if.then if condition else classical_if.else_
39
+
25
40
  if _is_all_identity_calls(body):
26
41
  return
27
42
 
28
43
  if not self._should_wrap(body):
29
44
  for stmt in body:
30
- self._interpreter.emit_statement(stmt)
45
+ if is_generative:
46
+ self._interpreter._builder.emit_statement(stmt)
47
+ else:
48
+ self._interpreter.emit_statement(stmt)
31
49
  return
32
50
 
33
51
  then_else_func = FunctionClosure.create(
34
- name="then" if condition else "else",
52
+ name=op_name,
35
53
  body=body,
36
54
  scope=Scope(parent=self._current_scope),
37
55
  )
38
- with self._propagated_var_stack.capture_variables(classical_if):
39
- self._emit_quantum_function_call(then_else_func, list())
56
+ self._emit_quantum_function_call(then_else_func, list())
@@ -21,6 +21,7 @@ from classiq.interface.model.control import Control
21
21
  from classiq.interface.model.handle_binding import HANDLE_ID_SEPARATOR, HandleBinding
22
22
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
23
23
  ArithmeticOperation,
24
+ ArithmeticOperationKind,
24
25
  )
25
26
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
26
27
  from classiq.interface.model.quantum_type import QuantumBit, QuantumBitvector
@@ -69,6 +70,14 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
69
70
  def _emit_canonical_control(self, control: Control) -> None:
70
71
  # canonical means control(q, body) where q is a single quantum variable
71
72
  control = self._evaluate_types_in_expression(control, control.expression)
73
+ with self._propagated_var_stack.capture_variables(control):
74
+ self._emit_propagated(control)
75
+
76
+ def _emit_propagated(self, control: Control) -> None:
77
+ if control.is_generative():
78
+ context = self._register_generative_context(control, CONTROL_OPERATOR_NAME)
79
+ control = control.copy(update={"body": context.statements("body")})
80
+
72
81
  if self._should_wrap_control(control):
73
82
  self._emit_wrapped(control)
74
83
  return
@@ -90,26 +99,24 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
90
99
  blocks=dict(body=control.body),
91
100
  scope=Scope(parent=self._current_scope),
92
101
  )
93
- with self._propagated_var_stack.capture_variables(control):
94
- context = self._expand_operation(control_operation)
95
- validate_args_are_not_propagated(
96
- control.var_handles,
97
- self._propagated_var_stack.get_propagated_variables(),
98
- )
102
+ context = self._expand_operation(control_operation)
103
+ validate_args_are_not_propagated(
104
+ control.var_handles,
105
+ self._propagated_var_stack.get_propagated_variables(),
106
+ )
99
107
  self._update_control_state(control)
100
108
  self._builder.emit_statement(
101
109
  control.copy(update=dict(body=context.statements("body")))
102
110
  )
103
111
 
104
112
  def _emit_wrapped(self, control: Control) -> None:
105
- with self._propagated_var_stack.capture_variables(control):
106
- wrapping_function = self._create_expanded_wrapping_function(
107
- CONTROL_OPERATOR_NAME, control.body
108
- )
109
- validate_args_are_not_propagated(
110
- control.var_handles,
111
- self._propagated_var_stack.get_propagated_variables(),
112
- )
113
+ wrapping_function = self._create_expanded_wrapping_function(
114
+ CONTROL_OPERATOR_NAME, control.body
115
+ )
116
+ validate_args_are_not_propagated(
117
+ control.var_handles,
118
+ self._propagated_var_stack.get_propagated_variables(),
119
+ )
113
120
  self._update_control_state(control)
114
121
  self._builder.emit_statement(
115
122
  control.copy(update=dict(body=[wrapping_function]))
@@ -208,7 +215,7 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
208
215
  arith_expression = ArithmeticOperation(
209
216
  result_var=HandleBinding(name=aux_var),
210
217
  expression=control.expression,
211
- inplace_result=False,
218
+ operation_kind=ArithmeticOperationKind.Assignment,
212
219
  )
213
220
  self._interpreter.emit_statement(
214
221
  WithinApply(