classiq 0.64.0__py3-none-any.whl → 0.65.1__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 (51) hide show
  1. classiq/_internals/api_wrapper.py +30 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +8 -9
  3. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +4 -6
  4. classiq/applications/combinatorial_optimization/combinatorial_problem.py +2 -5
  5. classiq/applications/finance/finance_model_constructor.py +7 -12
  6. classiq/applications/grover/grover_model_constructor.py +4 -6
  7. classiq/applications/qsvm/qsvm_model_constructor.py +6 -4
  8. classiq/execution/execution_session.py +14 -13
  9. classiq/interface/_version.py +1 -1
  10. classiq/interface/backend/backend_preferences.py +1 -9
  11. classiq/interface/generator/expressions/qmod_qarray_proxy.py +11 -13
  12. classiq/interface/generator/functions/type_name.py +6 -0
  13. classiq/interface/model/allocate.py +16 -0
  14. classiq/interface/model/quantum_type.py +26 -0
  15. classiq/interface/model/statement_block.py +2 -0
  16. classiq/interface/server/routes.py +1 -0
  17. classiq/model_expansions/evaluators/quantum_type_utils.py +10 -0
  18. classiq/model_expansions/function_builder.py +35 -11
  19. classiq/model_expansions/generative_functions.py +6 -4
  20. classiq/model_expansions/interpreters/base_interpreter.py +37 -138
  21. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +28 -0
  22. classiq/model_expansions/interpreters/generative_interpreter.py +144 -3
  23. classiq/model_expansions/quantum_operations/call_emitter.py +43 -91
  24. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +87 -0
  25. classiq/model_expansions/quantum_operations/emitter.py +5 -0
  26. classiq/model_expansions/quantum_operations/quantum_function_call.py +9 -0
  27. classiq/model_expansions/quantum_operations/shallow_emitter.py +20 -1
  28. classiq/model_expansions/scope.py +15 -15
  29. classiq/model_expansions/scope_initialization.py +3 -5
  30. classiq/open_library/functions/discrete_sine_cosine_transform.py +8 -2
  31. classiq/open_library/functions/grover.py +1 -1
  32. classiq/open_library/functions/modular_exponentiation.py +8 -2
  33. classiq/open_library/functions/state_preparation.py +23 -13
  34. classiq/open_library/functions/swap_test.py +1 -2
  35. classiq/open_library/functions/variational.py +1 -2
  36. classiq/qmod/builtins/__init__.py +1 -1
  37. classiq/qmod/builtins/operations.py +51 -0
  38. classiq/qmod/native/pretty_printer.py +9 -1
  39. classiq/qmod/pretty_print/pretty_printer.py +12 -1
  40. classiq/qmod/qmod_variable.py +38 -38
  41. classiq/qmod/quantum_function.py +4 -4
  42. classiq/qmod/semantics/annotation/__init__.py +0 -0
  43. classiq/qmod/semantics/annotation/call_annotation.py +92 -0
  44. classiq/qmod/semantics/lambdas.py +25 -0
  45. classiq/qmod/semantics/static_semantics_visitor.py +8 -46
  46. classiq/qmod/utilities.py +16 -0
  47. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/METADATA +1 -1
  48. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/RECORD +50 -45
  49. classiq/qmod/semantics/annotation.py +0 -36
  50. /classiq/qmod/semantics/{qstruct_annotator.py → annotation/qstruct_annotator.py} +0 -0
  51. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/WHEEL +0 -0
@@ -22,6 +22,7 @@ from classiq.interface.execution.jobs import (
22
22
  ExecutionJobDetailsV1,
23
23
  ExecutionJobsQueryResultsV1,
24
24
  )
25
+ from classiq.interface.execution.primitives import PrimitivesInput
25
26
  from classiq.interface.executor import execution_request
26
27
  from classiq.interface.generator import quantum_program as generator_result
27
28
  from classiq.interface.hardware import HardwareInformation, Provider
@@ -120,6 +121,35 @@ class ApiWrapper:
120
121
  )
121
122
  return _parse_job_response(result, generator_result.QuantumProgram)
122
123
 
124
+ @classmethod
125
+ async def call_create_execution_session(
126
+ cls,
127
+ circuit: generator_result.QuantumProgram,
128
+ http_client: Optional[httpx.AsyncClient] = None,
129
+ ) -> str:
130
+ raw_result = await cls._call_task_pydantic(
131
+ http_method=HTTPMethod.POST,
132
+ url=routes.EXECUTION_SESSIONS_PREFIX,
133
+ model=circuit,
134
+ http_client=http_client,
135
+ )
136
+ return raw_result["id"]
137
+
138
+ @classmethod
139
+ async def call_create_session_job(
140
+ cls,
141
+ session_id: str,
142
+ primitives_input: PrimitivesInput,
143
+ http_client: Optional[httpx.AsyncClient] = None,
144
+ ) -> execution_request.ExecutionJobDetails:
145
+ data = await cls._call_task_pydantic(
146
+ http_method=HTTPMethod.POST,
147
+ url=routes.EXECUTION_SESSIONS_PREFIX + f"/{session_id}",
148
+ model=primitives_input,
149
+ http_client=http_client,
150
+ )
151
+ return execution_request.ExecutionJobDetails.model_validate(data)
152
+
123
153
  @classmethod
124
154
  async def call_convert_quantum_program(
125
155
  cls,
@@ -7,6 +7,8 @@ from classiq.interface.chemistry.fermionic_operator import (
7
7
  FermionicOperator,
8
8
  SummedFermionicOperator,
9
9
  )
10
+ from classiq.interface.model.allocate import Allocate
11
+ from classiq.interface.model.quantum_statement import QuantumStatement
10
12
  from classiq.qmod.builtins.structs import (
11
13
  MoleculeProblem as QmodMoleculeProblem,
12
14
  Molecule as QmodMolecule,
@@ -441,16 +443,13 @@ def _get_chemistry_quantum_main(
441
443
  use_hartree_fock: bool,
442
444
  ansatz_parameters: AnsatzParameters,
443
445
  ) -> NativeFunctionDefinition:
444
- body = []
446
+ body: list[QuantumStatement] = []
445
447
  body.append(
446
- QuantumFunctionCall(
447
- function="allocate",
448
- positional_args=[
449
- Expression(
450
- expr=f"get_field(get_field({_get_problem_to_hamiltonian_name(chemistry_problem)}({_convert_library_problem_to_qmod_problem(chemistry_problem)})[0], 'pauli'), 'len')"
451
- ),
452
- HandleBinding(name="qbv"),
453
- ],
448
+ Allocate(
449
+ size=Expression(
450
+ expr=f"get_field(get_field({_get_problem_to_hamiltonian_name(chemistry_problem)}({_convert_library_problem_to_qmod_problem(chemistry_problem)})[0], 'pauli'), 'len')"
451
+ ),
452
+ target=HandleBinding(name="qbv"),
454
453
  ),
455
454
  )
456
455
  if use_hartree_fock:
@@ -14,6 +14,7 @@ from classiq.interface.generator.functions.port_declaration import (
14
14
  PortDeclarationDirection,
15
15
  )
16
16
  from classiq.interface.generator.functions.type_name import Struct
17
+ from classiq.interface.model.allocate import Allocate
17
18
  from classiq.interface.model.classical_parameter_declaration import (
18
19
  ClassicalParameterDeclaration,
19
20
  )
@@ -89,12 +90,9 @@ def construct_combi_opt_py_model(
89
90
  ),
90
91
  ],
91
92
  body=[
92
- QuantumFunctionCall(
93
- function="allocate",
94
- positional_args=[
95
- Expression(expr="get_field(target, 'len')"),
96
- HandleBinding(name="target"),
97
- ],
93
+ Allocate(
94
+ size=Expression(expr="get_field(target, 'len')"),
95
+ target=HandleBinding(name="target"),
98
96
  ),
99
97
  QuantumFunctionCall(
100
98
  function="qaoa_penalty",
@@ -21,11 +21,8 @@ from classiq.open_library.functions.utility_functions import (
21
21
  apply_to_all,
22
22
  hadamard_transform,
23
23
  )
24
- from classiq.qmod.builtins.functions import (
25
- RX,
26
- allocate,
27
- )
28
- from classiq.qmod.builtins.operations import phase, repeat
24
+ from classiq.qmod.builtins.functions import RX
25
+ from classiq.qmod.builtins.operations import allocate, phase, repeat
29
26
  from classiq.qmod.cparam import CReal
30
27
  from classiq.qmod.create_model_function import create_model
31
28
  from classiq.qmod.qfunc import qfunc
@@ -9,6 +9,7 @@ from classiq.interface.generator.expressions.expression import Expression
9
9
  from classiq.interface.generator.functions.port_declaration import (
10
10
  PortDeclarationDirection,
11
11
  )
12
+ from classiq.interface.model.allocate import Allocate
12
13
  from classiq.interface.model.handle_binding import HandleBinding
13
14
  from classiq.interface.model.model import Model, SerializedModel
14
15
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
@@ -78,19 +79,13 @@ def construct_finance_model(
78
79
  ],
79
80
  body=[
80
81
  VariableDeclarationStatement(name="unitary_port"),
81
- QuantumFunctionCall(
82
- function="allocate",
83
- positional_args=[
84
- Expression(expr=f"{num_unitary_qubits}"),
85
- HandleBinding(name="unitary_port"),
86
- ],
82
+ Allocate(
83
+ size=Expression(expr=f"{num_unitary_qubits}"),
84
+ target=HandleBinding(name="unitary_port"),
87
85
  ),
88
- QuantumFunctionCall(
89
- function="allocate",
90
- positional_args=[
91
- Expression(expr=f"{phase_port_size}"),
92
- HandleBinding(name="phase_port"),
93
- ],
86
+ Allocate(
87
+ size=Expression(expr=f"{phase_port_size}"),
88
+ target=HandleBinding(name="phase_port"),
94
89
  ),
95
90
  QuantumFunctionCall(
96
91
  function="qmci",
@@ -2,6 +2,7 @@ from classiq.interface.generator.expressions.expression import Expression
2
2
  from classiq.interface.generator.functions.port_declaration import (
3
3
  PortDeclarationDirection,
4
4
  )
5
+ from classiq.interface.model.allocate import Allocate
5
6
  from classiq.interface.model.bind_operation import BindOperation
6
7
  from classiq.interface.model.handle_binding import HandleBinding, SlicedHandleBinding
7
8
  from classiq.interface.model.model import Model, SerializedModel
@@ -121,12 +122,9 @@ def construct_grover_model(
121
122
  ),
122
123
  body=[
123
124
  VariableDeclarationStatement(name="packed_vars"),
124
- QuantumFunctionCall(
125
- function="allocate",
126
- positional_args=[
127
- Expression(expr=f"{num_qubits}"),
128
- HandleBinding(name="packed_vars"),
129
- ],
125
+ Allocate(
126
+ size=Expression(expr=f"{num_qubits}"),
127
+ target=HandleBinding(name="packed_vars"),
130
128
  ),
131
129
  QuantumFunctionCall(
132
130
  function="grover_search",
@@ -6,11 +6,13 @@ from classiq.interface.generator.expressions.expression import Expression
6
6
  from classiq.interface.generator.functions.port_declaration import (
7
7
  PortDeclarationDirection,
8
8
  )
9
+ from classiq.interface.model.allocate import Allocate
9
10
  from classiq.interface.model.handle_binding import HandleBinding
10
11
  from classiq.interface.model.model import Model, SerializedModel
11
12
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
12
13
  from classiq.interface.model.port_declaration import PortDeclaration
13
14
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
15
+ from classiq.interface.model.quantum_statement import QuantumStatement
14
16
 
15
17
  from classiq.qmod.builtins.enums import Pauli, QSVMFeatureMapEntanglement
16
18
  from classiq.qmod.utilities import qmod_val_to_expr_str
@@ -61,7 +63,7 @@ def _pauli_feature_map_function_params(
61
63
 
62
64
  def get_qsvm_qmain_body(
63
65
  feature_map_function_name: str, **kwargs: Any
64
- ) -> list[QuantumFunctionCall]:
66
+ ) -> list[QuantumStatement]:
65
67
  if feature_map_function_name == "bloch_sphere_feature_map":
66
68
  params, size_expr = _bloch_sphere_feature_map_function_params(**kwargs)
67
69
  elif feature_map_function_name == "pauli_feature_map":
@@ -70,9 +72,9 @@ def get_qsvm_qmain_body(
70
72
  raise ClassiqValueError(INVALID_FEATURE_MAP_FUNC_NAME_MSG)
71
73
 
72
74
  return [
73
- QuantumFunctionCall(
74
- function="allocate",
75
- positional_args=[Expression(expr=size_expr), HandleBinding(name="qbv")],
75
+ Allocate(
76
+ size=Expression(expr=size_expr),
77
+ target=HandleBinding(name="qbv"),
76
78
  ),
77
79
  QuantumFunctionCall(
78
80
  function=feature_map_function_name,
@@ -1,6 +1,5 @@
1
1
  import json
2
2
  import random
3
- from functools import cached_property
4
3
  from types import TracebackType
5
4
  from typing import Callable, Optional, Union, cast
6
5
 
@@ -110,6 +109,8 @@ class ExecutionSession:
110
109
 
111
110
  self._async_client = client().async_client()
112
111
 
112
+ self._session_id: str | None = None
113
+
113
114
  def __enter__(self) -> "ExecutionSession":
114
115
  return self
115
116
 
@@ -127,22 +128,22 @@ class ExecutionSession:
127
128
  """
128
129
  async_utils.run(self._async_client.aclose())
129
130
 
130
- @cached_property
131
- def _execution_input(self) -> dict:
132
- return async_utils.run(
133
- ApiWrapper.call_convert_quantum_program(self.program, self._async_client)
134
- )
131
+ def get_session_id(self) -> str:
132
+ if self._session_id is None:
133
+ self._session_id = async_utils.run(
134
+ ApiWrapper.call_create_execution_session(
135
+ self.program, self._async_client
136
+ )
137
+ )
138
+ return self._session_id
135
139
 
136
140
  def _execute(self, primitives_input: PrimitivesInput) -> ExecutionJob:
137
141
  primitives_input.random_seed = self._random_seed
138
142
  self._random_seed = self._rng.randint(0, 2**32 - 1)
139
- execution_input = self._execution_input.copy()
140
- # The use of `model_dump_json` is necessary for complex numbers serialization
141
- execution_input["primitives_input"] = json.loads(
142
- primitives_input.model_dump_json()
143
- )
144
143
  result = async_utils.run(
145
- ApiWrapper.call_execute_execution_input(execution_input, self._async_client)
144
+ ApiWrapper.call_create_session_job(
145
+ self.get_session_id(), primitives_input, self._async_client
146
+ )
146
147
  )
147
148
  return ExecutionJob(details=result)
148
149
 
@@ -360,7 +361,7 @@ class ExecutionSession:
360
361
  output_name: The name of the register to filter
361
362
  condition: Filter out values of the statevector for which this callable is False
362
363
  """
363
- if "_execution_input" in self.__dict__:
364
+ if self._session_id is not None:
364
365
  raise ClassiqError(
365
366
  "set_measured_state_filter must be called before use of the first primitive (sample, estimate...)"
366
367
  )
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.64.0'
6
+ SEMVER_VERSION = '0.65.1'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -22,7 +22,7 @@ from classiq.interface.backend.quantum_backend_providers import (
22
22
  ProviderTypeVendor,
23
23
  ProviderVendor,
24
24
  )
25
- from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqValueError
25
+ from classiq.interface.exceptions import ClassiqDeprecationWarning
26
26
  from classiq.interface.hardware import Provider
27
27
 
28
28
 
@@ -225,14 +225,6 @@ class AwsBackendPreferences(BackendPreferences):
225
225
  description="Run through Classiq's credentials while using user's allocated budget.",
226
226
  )
227
227
 
228
- @pydantic.field_validator("s3_bucket_name", mode="before")
229
- @classmethod
230
- def _validate_s3_bucket_name(cls, s3_bucket_name: str) -> str:
231
- s3_bucket_name = s3_bucket_name.strip()
232
- if not s3_bucket_name.startswith("amazon-braket-"):
233
- raise ClassiqValueError('S3 bucket name should start with "amazon-braket-"')
234
- return s3_bucket_name
235
-
236
228
 
237
229
  class IBMBackendProvider(BaseModel):
238
230
  """
@@ -3,7 +3,10 @@ from typing import TYPE_CHECKING, Any, Union
3
3
 
4
4
  from sympy import Integer
5
5
 
6
- from classiq.interface.exceptions import ClassiqValueError
6
+ from classiq.interface.exceptions import (
7
+ ClassiqInternalExpansionError,
8
+ ClassiqValueError,
9
+ )
7
10
  from classiq.interface.generator.expressions.expression import Expression
8
11
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
9
12
  from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
@@ -18,12 +21,7 @@ if TYPE_CHECKING:
18
21
 
19
22
 
20
23
  ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
21
- SLICE_OUT_OF_BOUNDS_MSG = "Slice indices out of bounds"
22
- SUBSCRIPT_OUT_OF_BOUNDS_MSG = "Subscript index out of bounds"
23
24
  ILLEGAL_SLICE_MSG = "Quantum array slice must be of the form [<int-value>:<int-value>]."
24
- ILLEGAL_SLICE_BOUNDS_MSG = (
25
- "The quantum array slice start value ({}) must be lower than its stop value ({})."
26
- )
27
25
 
28
26
 
29
27
  class QmodQArrayProxy(NonSymbolicExpr, QmodSizedProxy):
@@ -48,7 +46,7 @@ class QmodQArrayProxy(NonSymbolicExpr, QmodSizedProxy):
48
46
  if isinstance(index, Integer):
49
47
  index = int(index)
50
48
  if index < 0 or index >= self._length:
51
- raise ClassiqValueError(SUBSCRIPT_OUT_OF_BOUNDS_MSG)
49
+ raise ClassiqInternalExpansionError
52
50
 
53
51
  return self._element_type.get_proxy(
54
52
  SubscriptHandleBinding(
@@ -66,12 +64,12 @@ class QmodQArrayProxy(NonSymbolicExpr, QmodSizedProxy):
66
64
  slice_ = slice(slice_.start, int(slice_.stop))
67
65
  if not isinstance(slice_.start, int) or not isinstance(slice_.stop, int):
68
66
  raise ClassiqValueError(ILLEGAL_SLICE_MSG)
69
- if slice_.start >= slice_.stop:
70
- raise ClassiqValueError(
71
- ILLEGAL_SLICE_BOUNDS_MSG.format(slice_.start, slice_.stop)
72
- )
73
- if slice_.start < 0 or slice_.stop > self._length:
74
- raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
67
+ if (
68
+ slice_.start >= slice_.stop
69
+ or slice_.start < 0
70
+ or slice_.stop > self._length
71
+ ):
72
+ raise ClassiqInternalExpansionError
75
73
 
76
74
  return QmodQArrayProxy(
77
75
  SlicedHandleBinding(
@@ -75,6 +75,12 @@ class TypeName(ClassicalType, QuantumType):
75
75
  field_type.is_instantiated for field_type in self.fields.values()
76
76
  )
77
77
 
78
+ @property
79
+ def is_evaluated(self) -> bool:
80
+ return self.has_fields and all(
81
+ field_type.is_evaluated for field_type in self.fields.values()
82
+ )
83
+
78
84
 
79
85
  class Enum(TypeName):
80
86
  pass
@@ -0,0 +1,16 @@
1
+ from collections.abc import Mapping
2
+ from typing import Literal, Optional
3
+
4
+ from classiq.interface.generator.expressions.expression import Expression
5
+ from classiq.interface.model.handle_binding import ConcreteHandleBinding, HandleBinding
6
+ from classiq.interface.model.quantum_statement import QuantumOperation
7
+
8
+
9
+ class Allocate(QuantumOperation):
10
+ kind: Literal["Allocate"]
11
+ size: Optional[Expression] = None
12
+ target: ConcreteHandleBinding
13
+
14
+ @property
15
+ def wiring_outputs(self) -> Mapping[str, HandleBinding]:
16
+ return {"out": self.target}
@@ -63,6 +63,10 @@ class QuantumType(HashableASTNode):
63
63
  def is_instantiated(self) -> bool:
64
64
  raise NotImplementedError
65
65
 
66
+ @property
67
+ def is_evaluated(self) -> bool:
68
+ raise NotImplementedError
69
+
66
70
 
67
71
  class QuantumScalar(QuantumType):
68
72
  def get_proxy(self, handle: "HandleBinding") -> QmodQScalarProxy:
@@ -96,6 +100,10 @@ class QuantumBit(QuantumScalar):
96
100
  def is_instantiated(self) -> bool:
97
101
  return True
98
102
 
103
+ @property
104
+ def is_evaluated(self) -> bool:
105
+ return True
106
+
99
107
 
100
108
  class QuantumBitvector(QuantumType):
101
109
  element_type: "ConcreteQuantumType" = Field(
@@ -154,6 +162,14 @@ class QuantumBitvector(QuantumType):
154
162
  def is_instantiated(self) -> bool:
155
163
  return self.length is not None and self.element_type.is_instantiated
156
164
 
165
+ @property
166
+ def is_evaluated(self) -> bool:
167
+ return (
168
+ self.length is not None
169
+ and self.length.is_evaluated()
170
+ and self.element_type.is_evaluated
171
+ )
172
+
157
173
 
158
174
  class QuantumNumeric(QuantumScalar):
159
175
  kind: Literal["qnum"]
@@ -227,6 +243,16 @@ class QuantumNumeric(QuantumScalar):
227
243
  def is_instantiated(self) -> bool:
228
244
  return self.size is not None
229
245
 
246
+ @property
247
+ def is_evaluated(self) -> bool:
248
+ if self.size is None or not self.size.is_evaluated():
249
+ return False
250
+ if self.is_signed is not None and not self.is_signed.is_evaluated():
251
+ return False
252
+ return not (
253
+ self.fraction_digits is not None and not self.fraction_digits.is_evaluated()
254
+ )
255
+
230
256
 
231
257
  class RegisterQuantumType(BaseModel):
232
258
  quantum_types: "ConcreteQuantumType"
@@ -2,6 +2,7 @@ from typing import Annotated, Union
2
2
 
3
3
  from pydantic import Field
4
4
 
5
+ from classiq.interface.model.allocate import Allocate
5
6
  from classiq.interface.model.bind_operation import BindOperation
6
7
  from classiq.interface.model.classical_if import ClassicalIf
7
8
  from classiq.interface.model.control import Control
@@ -26,6 +27,7 @@ from classiq.interface.model.within_apply_operation import WithinApply
26
27
 
27
28
  ConcreteQuantumStatement = Annotated[
28
29
  Union[
30
+ Allocate,
29
31
  QuantumFunctionCall,
30
32
  ArithmeticOperation,
31
33
  AmplitudeLoadingOperation,
@@ -1,6 +1,7 @@
1
1
  ANALYZER_PREFIX = "/analyzer"
2
2
  CHEMISTRY_PREFIX = "/chemistry"
3
3
  EXECUTION_PREFIX = "/execution"
4
+ EXECUTION_SESSIONS_PREFIX = EXECUTION_PREFIX + "/sessions"
4
5
  CONVERSION_PREFIX = "/conversion"
5
6
  PROVIDERS_PREFIX = "/providers"
6
7
 
@@ -1,3 +1,4 @@
1
+ from collections.abc import Sequence
1
2
  from typing import Optional
2
3
 
3
4
  from classiq.interface.exceptions import (
@@ -11,6 +12,8 @@ from classiq.interface.generator.functions.type_name import (
11
12
  )
12
13
  from classiq.interface.model.bind_operation import BindOperation
13
14
  from classiq.interface.model.inplace_binary_operation import BinaryOperation
15
+ from classiq.interface.model.port_declaration import PortDeclaration
16
+ from classiq.interface.model.quantum_function_declaration import PositionalArg
14
17
  from classiq.interface.model.quantum_type import (
15
18
  QuantumBit,
16
19
  QuantumBitvector,
@@ -227,3 +230,10 @@ def get_inplace_op_scalar_as_numeric(
227
230
  fraction_digits=Expression(expr="0"),
228
231
  )
229
232
  raise ClassiqInternalExpansionError(f"Unexpected scalar type {var.quantum_type}")
233
+
234
+
235
+ def is_signature_monomorphic(params: Sequence[PositionalArg]) -> bool:
236
+ return all(
237
+ isinstance(param, PortDeclaration) and param.quantum_type.is_evaluated
238
+ for param in params
239
+ )
@@ -29,7 +29,14 @@ from classiq.model_expansions.capturing.captured_vars import (
29
29
  CapturedVars,
30
30
  validate_captured_directions,
31
31
  )
32
- from classiq.model_expansions.closure import Closure, FunctionClosure
32
+ from classiq.model_expansions.closure import (
33
+ Closure,
34
+ FunctionClosure,
35
+ GenerativeFunctionClosure,
36
+ )
37
+ from classiq.model_expansions.evaluators.quantum_type_utils import (
38
+ is_signature_monomorphic,
39
+ )
33
40
  from classiq.model_expansions.scope import Scope
34
41
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
35
42
 
@@ -200,16 +207,7 @@ class OperationBuilder:
200
207
  def create_definition(
201
208
  self, function_context: FunctionContext
202
209
  ) -> NativeFunctionDefinition:
203
- name = function_context.name
204
- if name != MAIN_FUNCTION_NAME:
205
- for _ in self.current_scope:
206
- name = self._counted_name_allocator.allocate(
207
- f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}"
208
- )
209
- if name not in self.current_scope:
210
- break
211
- else:
212
- raise ClassiqInternalExpansionError("Could not allocate function name")
210
+ name = self._get_expanded_function_name(function_context)
213
211
  new_parameters: list[PortDeclaration] = [
214
212
  param
215
213
  for param in function_context.positional_arg_declarations
@@ -221,3 +219,29 @@ class OperationBuilder:
221
219
  body=function_context.body,
222
220
  positional_arg_declarations=new_parameters,
223
221
  )
222
+
223
+ def _get_expanded_function_name(self, function_context: FunctionContext) -> str:
224
+ name = function_context.name
225
+
226
+ if name == MAIN_FUNCTION_NAME:
227
+ return name
228
+
229
+ if name in self.current_scope:
230
+ orig_func = self.current_scope[name].value
231
+ if (
232
+ isinstance(orig_func, FunctionClosure)
233
+ and not isinstance(orig_func, GenerativeFunctionClosure)
234
+ and is_signature_monomorphic(orig_func.positional_arg_declarations)
235
+ ):
236
+ return name
237
+
238
+ for _ in self.current_scope:
239
+ name = self._counted_name_allocator.allocate(
240
+ f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}"
241
+ )
242
+ if name not in self.current_scope:
243
+ break
244
+ else:
245
+ raise ClassiqInternalExpansionError("Could not allocate function name")
246
+
247
+ return name
@@ -30,11 +30,13 @@ from classiq.qmod.quantum_expandable import (
30
30
  QTerminalCallable,
31
31
  )
32
32
  from classiq.qmod.quantum_function import QFunc
33
- from classiq.qmod.semantics.static_semantics_visitor import resolve_function_calls
33
+ from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
34
34
  from classiq.qmod.symbolic_expr import SymbolicExpr
35
35
 
36
36
  if TYPE_CHECKING:
37
- from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
37
+ from classiq.model_expansions.interpreters.generative_interpreter import (
38
+ GenerativeInterpreter,
39
+ )
38
40
 
39
41
 
40
42
  class LenList(list):
@@ -94,7 +96,7 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
94
96
 
95
97
 
96
98
  class _InterpreterExpandable(QFunc):
97
- def __init__(self, interpreter: "BaseInterpreter"):
99
+ def __init__(self, interpreter: "GenerativeInterpreter"):
98
100
  super().__init__(lambda: None)
99
101
  self._interpreter = interpreter
100
102
 
@@ -137,7 +139,7 @@ class _InterpreterExpandable(QFunc):
137
139
 
138
140
 
139
141
  def emit_generative_statements(
140
- interpreter: "BaseInterpreter",
142
+ interpreter: "GenerativeInterpreter",
141
143
  operation: GenerativeClosure,
142
144
  args: list[Evaluated],
143
145
  ) -> None: