classiq 0.80.0__py3-none-any.whl → 0.81.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 (40) hide show
  1. classiq/interface/_version.py +1 -1
  2. classiq/interface/debug_info/debug_info.py +0 -1
  3. classiq/interface/generator/compiler_keywords.py +1 -1
  4. classiq/interface/generator/expressions/atomic_expression_functions.py +11 -7
  5. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +6 -2
  6. classiq/interface/generator/function_params.py +1 -1
  7. classiq/interface/generator/functions/classical_type.py +8 -0
  8. classiq/interface/generator/generated_circuit_data.py +1 -2
  9. classiq/interface/generator/types/compilation_metadata.py +4 -1
  10. classiq/interface/model/handle_binding.py +12 -2
  11. classiq/interface/model/quantum_type.py +12 -1
  12. classiq/interface/server/routes.py +0 -1
  13. classiq/model_expansions/atomic_expression_functions_defs.py +1 -1
  14. classiq/model_expansions/capturing/captured_vars.py +123 -9
  15. classiq/model_expansions/closure.py +2 -0
  16. classiq/model_expansions/evaluators/quantum_type_utils.py +3 -18
  17. classiq/model_expansions/function_builder.py +1 -17
  18. classiq/model_expansions/quantum_operations/allocate.py +18 -7
  19. classiq/model_expansions/quantum_operations/assignment_result_processor.py +4 -0
  20. classiq/model_expansions/quantum_operations/bind.py +2 -1
  21. classiq/model_expansions/quantum_operations/call_emitter.py +27 -21
  22. classiq/model_expansions/quantum_operations/emitter.py +28 -0
  23. classiq/model_expansions/quantum_operations/function_calls_cache.py +1 -16
  24. classiq/model_expansions/transformers/type_qualifier_inference.py +72 -19
  25. classiq/model_expansions/visitors/symbolic_param_inference.py +0 -6
  26. classiq/open_library/functions/amplitude_amplification.py +3 -5
  27. classiq/open_library/functions/state_preparation.py +18 -13
  28. classiq/qmod/builtins/functions/__init__.py +3 -1
  29. classiq/qmod/builtins/functions/exponentiation.py +41 -3
  30. classiq/qmod/builtins/operations.py +65 -37
  31. classiq/qmod/declaration_inferrer.py +2 -1
  32. classiq/qmod/native/pretty_printer.py +8 -9
  33. classiq/qmod/pretty_print/pretty_printer.py +9 -9
  34. classiq/qmod/qfunc.py +11 -11
  35. classiq/qmod/quantum_expandable.py +4 -0
  36. {classiq-0.80.0.dist-info → classiq-0.81.0.dist-info}/METADATA +1 -1
  37. {classiq-0.80.0.dist-info → classiq-0.81.0.dist-info}/RECORD +38 -40
  38. classiq/interface/execution/resource_estimator.py +0 -7
  39. classiq/interface/execution/result.py +0 -5
  40. {classiq-0.80.0.dist-info → classiq-0.81.0.dist-info}/WHEEL +0 -0
@@ -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.80.0'
6
+ SEMVER_VERSION = '0.81.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -23,7 +23,6 @@ class FunctionDebugInfo(BaseModel):
23
23
  name: str
24
24
  statement_type: Union[StatementType, None] = None
25
25
  is_inverse: bool = Field(default=False)
26
- release_by_inverse: bool = Field(default=False)
27
26
  control_variable: Optional[str] = Field(default=None)
28
27
  port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
29
28
  node: Optional[ConcreteQuantumStatement] = None
@@ -1,7 +1,7 @@
1
1
  EXPANDED_KEYWORD = "expanded__"
2
2
  CAPTURE_SUFFIX = "_captured__"
3
3
  LAMBDA_KEYWORD = "lambda__"
4
- INPLACE_ARITH_AUX_VAR_PREFIX = "result__temp__"
4
+ INPLACE_ARITH_AUX_VAR_PREFIX = "_tmp"
5
5
 
6
6
 
7
7
  def generate_original_function_name(name: str) -> str:
@@ -1,20 +1,24 @@
1
1
  SUPPORTED_PYTHON_BUILTIN_FUNCTIONS = {"len", "sum", "print"}
2
2
 
3
- SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
4
- "do_div",
5
- "do_slice",
6
- "do_subscript",
3
+ CLASSIQ_BUILTIN_CLASSICAL_FUNCTIONS = {
7
4
  "hypercube_entangler_graph",
8
5
  "grid_entangler_graph",
9
6
  "qft_const_adder_phase",
10
7
  "log_normal_finance_post_process",
11
8
  "gaussian_finance_post_process",
12
- "get_type",
13
- "struct_literal",
14
- "get_field",
15
9
  "molecule_problem_to_hamiltonian",
16
10
  "fock_hamiltonian_problem_to_hamiltonian",
17
11
  "molecule_ground_state_solution_post_process",
12
+ }
13
+
14
+ SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
15
+ *CLASSIQ_BUILTIN_CLASSICAL_FUNCTIONS,
16
+ "do_div",
17
+ "do_slice",
18
+ "do_subscript",
19
+ "get_type",
20
+ "struct_literal",
21
+ "get_field",
18
22
  "mod_inverse",
19
23
  }
20
24
 
@@ -64,8 +64,8 @@ class ClassicalSequenceProxy(NonSymbolicExpr, ClassicalProxy):
64
64
  if _is_int(start_) and _is_int(stop_):
65
65
  start = int(start_)
66
66
  stop = int(stop_)
67
- if start >= stop:
68
- raise ClassiqIndexError("Array slice has non-positive length")
67
+ if start > stop:
68
+ raise ClassiqIndexError("Array slice has negative length")
69
69
  if start < 0 or (isinstance(self.length, int) and stop > self.length):
70
70
  raise ClassiqIndexError("Array slice is out of bounds")
71
71
  return self.get_slice_at(start_, stop_)
@@ -84,6 +84,10 @@ class ClassicalSequenceProxy(NonSymbolicExpr, ClassicalProxy):
84
84
  )
85
85
  if isinstance(self.length, int) and index >= self.length:
86
86
  raise ClassiqIndexError("Array index is out of bounds")
87
+ if isinstance(index_, tuple):
88
+ raise ClassiqIndexError(
89
+ "list indices must be integers or slices, not tuple"
90
+ )
87
91
  return self.get_subscript_at(index_)
88
92
 
89
93
  def get_subscript_at(self, index: Any) -> ClassicalProxy:
@@ -47,7 +47,7 @@ END_BAD_REGISTER_ERROR_MSG = (
47
47
  )
48
48
 
49
49
  ALPHANUM_AND_UNDERSCORE = r"[0-9a-zA-Z_]*"
50
- NAME_REGEX = rf"[a-zA-Z]{ALPHANUM_AND_UNDERSCORE}"
50
+ NAME_REGEX = rf"_?[a-zA-Z]{ALPHANUM_AND_UNDERSCORE}"
51
51
 
52
52
  _UNVALIDATED_FUNCTIONS = ["Arithmetic", "CustomFunction"]
53
53
 
@@ -316,3 +316,11 @@ class IQAERes(OpaqueHandle):
316
316
 
317
317
  class QmodPyObject:
318
318
  pass
319
+
320
+
321
+ CLASSICAL_ATTRIBUTES_TYPES = {
322
+ "len": Integer(),
323
+ "size": Integer(),
324
+ "is_signed": Bool(),
325
+ "fraction_digits": Integer(),
326
+ }
@@ -54,7 +54,7 @@ VISUALIZATION_HIDE_LIST = [
54
54
  "stmt_block",
55
55
  ]
56
56
 
57
- CONTROLLED_PREFIX = "c-"
57
+ CONTROLLED_PREFIX = "c_"
58
58
 
59
59
 
60
60
  def last_name_in_call_hierarchy(name: str) -> str:
@@ -189,7 +189,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
189
189
  is_unitary: bool = Field(default=True, exclude=True)
190
190
  uuid: Optional[UUID] = Field(default=None, exclude=True)
191
191
  port_to_passed_variable_map: dict[str, str] = Field(default={})
192
- release_by_inverse: bool = Field(default=False)
193
192
  back_refs: StatementBlock = Field(default_factory=list)
194
193
 
195
194
  model_config = ConfigDict(extra="allow")
@@ -4,4 +4,7 @@ from pydantic import BaseModel, Field, NonNegativeInt
4
4
  class CompilationMetadata(BaseModel):
5
5
  should_synthesize_separately: bool = Field(default=False)
6
6
  occurrences_number: NonNegativeInt = Field(default=1)
7
- atomic_qualifiers: list[str] = Field(default_factory=list)
7
+ unchecked: list[str] = Field(default_factory=list)
8
+ atomic_qualifiers: list[str] = Field(
9
+ default_factory=list, exclude=True
10
+ ) # TODO remove after deprecation https://classiq.atlassian.net/browse/CLS-2671
@@ -12,6 +12,12 @@ from classiq.interface.generator.expressions.expression import Expression
12
12
  HANDLE_ID_SEPARATOR = "___"
13
13
 
14
14
 
15
+ def _get_expr_id(expr: Expression) -> str:
16
+ if expr.expr.isidentifier() or expr.expr.isnumeric():
17
+ return expr.expr
18
+ return str(abs(hash(expr.expr)))
19
+
20
+
15
21
  class HandleBinding(ASTNode):
16
22
  name: str = Field(default=None)
17
23
  model_config = ConfigDict(frozen=True, extra="forbid")
@@ -131,7 +137,10 @@ class SubscriptHandleBinding(NestedHandleBinding):
131
137
 
132
138
  @property
133
139
  def identifier(self) -> str:
134
- return f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}{self.index}"
140
+ return (
141
+ f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}"
142
+ f"{_get_expr_id(self.index)}"
143
+ )
135
144
 
136
145
  def collapse(self) -> HandleBinding:
137
146
  if isinstance(self.base_handle, SlicedHandleBinding):
@@ -216,7 +225,8 @@ class SlicedHandleBinding(NestedHandleBinding):
216
225
  @property
217
226
  def identifier(self) -> str:
218
227
  return (
219
- f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}{self.start}_{self.end}"
228
+ f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}"
229
+ f"{_get_expr_id(self.start)}_{_get_expr_id(self.end)}"
220
230
  )
221
231
 
222
232
  def collapse(self) -> HandleBinding:
@@ -5,7 +5,10 @@ from pydantic import BaseModel, ConfigDict, Field
5
5
  from typing_extensions import Self
6
6
 
7
7
  from classiq.interface.ast_node import HashableASTNode
8
- from classiq.interface.exceptions import ClassiqValueError
8
+ from classiq.interface.exceptions import (
9
+ ClassiqInternalExpansionError,
10
+ ClassiqValueError,
11
+ )
9
12
  from classiq.interface.generator.arith import number_utils
10
13
  from classiq.interface.generator.arith.register_user_input import (
11
14
  RegisterArithmeticInfo,
@@ -214,6 +217,14 @@ class QuantumNumeric(QuantumScalar):
214
217
  )
215
218
  return self
216
219
 
220
+ def set_size_in_bits(self, val: int) -> None:
221
+ super().set_size_in_bits(val)
222
+ if self.size is not None:
223
+ if self.size.is_evaluated() and self.size.value == val:
224
+ return
225
+ raise ClassiqInternalExpansionError("Numeric size mismatch")
226
+ self.size = Expression(expr=str(val))
227
+
217
228
  @property
218
229
  def has_sign(self) -> bool:
219
230
  return self.is_signed is not None
@@ -65,7 +65,6 @@ ASSIGN_PARAMETERS_FULL_PATH = QUANTUM_PROGRAM_PREFIX + ASSIGN_PARAMETERS_SUFFIX
65
65
 
66
66
  ANALYZER_FULL_PATH = ANALYZER_PREFIX + TASKS_SUFFIX
67
67
  ANALYZER_RB_FULL_PATH = ANALYZER_PREFIX + TASK_RB_SUFFIX
68
- GENERATE_RESOURCE_ESTIMATOR_REPORT = "/resource_estimator_report"
69
68
 
70
69
  TASKS_SOLVE_EXACT_SUFFIX = "/tasks/solve_exact"
71
70
 
@@ -284,7 +284,7 @@ def do_subscript(value: Any, index: Any) -> Any:
284
284
  length = value.length
285
285
  else:
286
286
  length = len(value)
287
- if length != 2**index.size:
287
+ if not isinstance(length, sympy.Basic) and length != 2**index.size:
288
288
  raise ClassiqExpansionError(
289
289
  f"Quantum numeric subscript size mismatch: The quantum numeric has "
290
290
  f"{index.size} qubits but the list size is {length} != 2**{index.size}"
@@ -11,7 +11,10 @@ from classiq.interface.exceptions import (
11
11
  ClassiqInternalExpansionError,
12
12
  )
13
13
  from classiq.interface.generator.expressions.expression import Expression
14
- from classiq.interface.generator.functions.classical_type import ClassicalType
14
+ from classiq.interface.generator.functions.classical_type import (
15
+ CLASSICAL_ATTRIBUTES_TYPES,
16
+ ClassicalType,
17
+ )
15
18
  from classiq.interface.generator.functions.port_declaration import (
16
19
  PortDeclarationDirection,
17
20
  )
@@ -21,6 +24,7 @@ from classiq.interface.model.classical_parameter_declaration import (
21
24
  ClassicalParameterDeclaration,
22
25
  )
23
26
  from classiq.interface.model.handle_binding import (
27
+ FieldHandleBinding,
24
28
  HandleBinding,
25
29
  HandlesList,
26
30
  NestedHandleBinding,
@@ -172,6 +176,27 @@ class _CapturedClassicalVar(_Captured):
172
176
  )
173
177
 
174
178
 
179
+ @dataclass(frozen=True)
180
+ class _CapturedQuantumTypeAttribute(_Captured):
181
+ handle: FieldHandleBinding
182
+ classical_type: ClassicalType
183
+ defining_function: "FunctionClosure"
184
+
185
+ @property
186
+ def mangled_name(self) -> str:
187
+ return mangle_captured_var_name(
188
+ self.handle.identifier,
189
+ self.defining_function.name,
190
+ self.defining_function.depth,
191
+ )
192
+
193
+ @property
194
+ def parameter_declaration(self) -> ClassicalParameterDeclaration:
195
+ return ClassicalParameterDeclaration(
196
+ name=self.mangled_name, classical_type=self.classical_type
197
+ )
198
+
199
+
175
200
  HandleState = tuple[str, "FunctionClosure", bool]
176
201
 
177
202
 
@@ -180,6 +205,9 @@ class CapturedVars:
180
205
  _captured_handles: list[_CapturedHandle] = field(default_factory=list)
181
206
  _handle_states: list[HandleState] = field(default_factory=list)
182
207
  _captured_classical_vars: list[_CapturedClassicalVar] = field(default_factory=list)
208
+ _captured_quantum_type_attributes: list[_CapturedQuantumTypeAttribute] = field(
209
+ default_factory=list
210
+ )
183
211
 
184
212
  def capture_handle(
185
213
  self,
@@ -410,11 +438,38 @@ class CapturedVars:
410
438
  return
411
439
  self._captured_classical_vars.append(captured_classical_var)
412
440
 
441
+ def capture_quantum_type_attribute(
442
+ self,
443
+ handle: FieldHandleBinding,
444
+ defining_function: "FunctionClosure",
445
+ ) -> None:
446
+ self._capture_quantum_type_attribute(
447
+ _CapturedQuantumTypeAttribute(
448
+ handle=handle,
449
+ classical_type=CLASSICAL_ATTRIBUTES_TYPES[handle.field],
450
+ defining_function=defining_function,
451
+ is_propagated=False,
452
+ )
453
+ )
454
+
455
+ def _capture_quantum_type_attribute(
456
+ self, captured_qta: _CapturedQuantumTypeAttribute
457
+ ) -> None:
458
+ for existing_captured_qta in self._captured_quantum_type_attributes:
459
+ if existing_captured_qta.handle == captured_qta.handle and _same_closure(
460
+ existing_captured_qta.defining_function,
461
+ captured_qta.defining_function,
462
+ ):
463
+ return
464
+ self._captured_quantum_type_attributes.append(captured_qta)
465
+
413
466
  def update(self, other_captured_vars: "CapturedVars") -> None:
414
467
  for captured_handle in other_captured_vars._captured_handles:
415
468
  self._capture_handle(captured_handle)
416
469
  for captured_classical_var in other_captured_vars._captured_classical_vars:
417
470
  self._capture_classical_var(captured_classical_var)
471
+ for captured_qta in other_captured_vars._captured_quantum_type_attributes:
472
+ self._capture_quantum_type_attribute(captured_qta)
418
473
 
419
474
  def negate(self) -> "CapturedVars":
420
475
  return CapturedVars(
@@ -422,7 +477,10 @@ class CapturedVars:
422
477
  captured_handle.change_direction(captured_handle.direction.negate())
423
478
  for captured_handle in self._captured_handles
424
479
  ],
425
- _captured_classical_vars=self._captured_classical_vars,
480
+ _captured_classical_vars=list(self._captured_classical_vars),
481
+ _captured_quantum_type_attributes=list(
482
+ self._captured_quantum_type_attributes
483
+ ),
426
484
  )
427
485
 
428
486
  def filter_vars(self, current_function: "FunctionClosure") -> "CapturedVars":
@@ -441,6 +499,11 @@ class CapturedVars:
441
499
  captured_classical_var.defining_function, current_function
442
500
  )
443
501
  ],
502
+ _captured_quantum_type_attributes=[
503
+ captured_qta
504
+ for captured_qta in self._captured_quantum_type_attributes
505
+ if not _same_closure(captured_qta.defining_function, current_function)
506
+ ],
444
507
  )
445
508
 
446
509
  def filter_var_decls(
@@ -451,12 +514,14 @@ class CapturedVars:
451
514
  _captured_handles=[
452
515
  captured_handle
453
516
  for captured_handle in self._captured_handles
454
- if (
455
- current_declared_vars is not None
456
- and captured_handle.handle.name not in current_declared_vars
457
- )
517
+ if captured_handle.handle.name not in current_declared_vars
458
518
  ],
459
519
  _captured_classical_vars=self._captured_classical_vars,
520
+ _captured_quantum_type_attributes=[
521
+ captured_qta
522
+ for captured_qta in self._captured_quantum_type_attributes
523
+ if (captured_qta.handle.name not in current_declared_vars)
524
+ ],
460
525
  )
461
526
 
462
527
  def set_propagated(self) -> "CapturedVars":
@@ -469,6 +534,10 @@ class CapturedVars:
469
534
  captured_classical_var.set_propagated()
470
535
  for captured_classical_var in self._captured_classical_vars
471
536
  ],
537
+ _captured_quantum_type_attributes=[
538
+ captured_qta.set_propagated()
539
+ for captured_qta in self._captured_quantum_type_attributes
540
+ ],
472
541
  )
473
542
 
474
543
  def get_captured_parameters(self) -> list[PositionalArg]:
@@ -477,6 +546,10 @@ class CapturedVars:
477
546
  captured_classical_var.parameter_declaration
478
547
  for captured_classical_var in self._captured_classical_vars
479
548
  ]
549
+ decls += [
550
+ captured_qta.parameter_declaration
551
+ for captured_qta in self._captured_quantum_type_attributes
552
+ ]
480
553
  decls += [captured_handle.port for captured_handle in self._captured_handles]
481
554
  return decls
482
555
 
@@ -494,6 +567,16 @@ class CapturedVars:
494
567
  )
495
568
  for captured_classical_var in self._captured_classical_vars
496
569
  ]
570
+ args += [
571
+ Expression(
572
+ expr=(
573
+ str(captured_qta.handle)
574
+ if _same_closure(current_function, captured_qta.defining_function)
575
+ else captured_qta.mangled_name
576
+ )
577
+ )
578
+ for captured_qta in self._captured_quantum_type_attributes
579
+ ]
497
580
  args += [
498
581
  (
499
582
  captured_handle.handle
@@ -504,7 +587,7 @@ class CapturedVars:
504
587
  ]
505
588
  return args
506
589
 
507
- def get_immediate_captured_mapping(self) -> SymbolRenaming:
590
+ def _get_immediate_captured_mapping(self) -> SymbolRenaming:
508
591
  return {
509
592
  captured_handle.handle: [
510
593
  SymbolPart(
@@ -517,7 +600,7 @@ class CapturedVars:
517
600
  if not captured_handle.is_propagated
518
601
  }
519
602
 
520
- def get_propagated_captured_mapping(self) -> SymbolRenaming:
603
+ def _get_propagated_captured_mapping(self) -> SymbolRenaming:
521
604
  return {
522
605
  captured_handle.mangled_handle: [
523
606
  SymbolPart(
@@ -529,7 +612,7 @@ class CapturedVars:
529
612
  for captured_handle in self._captured_handles
530
613
  }
531
614
 
532
- def get_classical_captured_mapping(self) -> SymbolRenaming:
615
+ def _get_classical_captured_mapping(self) -> SymbolRenaming:
533
616
  return {
534
617
  (handle := HandleBinding(name=captured_classical_var.name)): [
535
618
  HandleRenaming(
@@ -541,6 +624,26 @@ class CapturedVars:
541
624
  if not captured_classical_var.is_propagated
542
625
  }
543
626
 
627
+ def _get_quantum_type_attributes_captured_mapping(self) -> SymbolRenaming:
628
+ return {
629
+ (handle := captured_qta.handle): [
630
+ HandleRenaming(
631
+ source_handle=handle,
632
+ target_var_name=captured_qta.mangled_name,
633
+ )
634
+ ]
635
+ for captured_qta in self._captured_quantum_type_attributes
636
+ if not captured_qta.is_propagated
637
+ }
638
+
639
+ def get_captured_mapping(self, is_lambda: bool) -> SymbolRenaming:
640
+ rewrite_mapping = dict(self._get_propagated_captured_mapping())
641
+ if is_lambda:
642
+ rewrite_mapping |= self._get_immediate_captured_mapping()
643
+ rewrite_mapping |= self._get_classical_captured_mapping()
644
+ rewrite_mapping |= self._get_quantum_type_attributes_captured_mapping()
645
+ return rewrite_mapping
646
+
544
647
  def init_var(self, var_name: str, defining_function: "FunctionClosure") -> None:
545
648
  self._handle_states.append((var_name, defining_function, False))
546
649
 
@@ -606,6 +709,9 @@ class CapturedVars:
606
709
  _captured_handles=list(self._captured_handles),
607
710
  _handle_states=list(self._handle_states),
608
711
  _captured_classical_vars=list(self._captured_classical_vars),
712
+ _captured_quantum_type_attributes=list(
713
+ self._captured_quantum_type_attributes
714
+ ),
609
715
  )
610
716
 
611
717
  def set(
@@ -636,6 +742,14 @@ class CapturedVars:
636
742
  )
637
743
  else:
638
744
  self._captured_classical_vars.append(captured_classical_var)
745
+ self._captured_quantum_type_attributes = []
746
+ for captured_qta in other._captured_quantum_type_attributes:
747
+ if _same_closure(captured_qta.defining_function, source_func):
748
+ self._captured_quantum_type_attributes.append(
749
+ captured_qta.change_defining_function(target_func)
750
+ )
751
+ else:
752
+ self._captured_quantum_type_attributes.append(captured_qta)
639
753
 
640
754
 
641
755
  def _same_closure(closure_1: "FunctionClosure", closure_2: "FunctionClosure") -> bool:
@@ -1,5 +1,6 @@
1
1
  import dataclasses
2
2
  from collections.abc import Sequence
3
+ from copy import deepcopy
3
4
  from dataclasses import dataclass, field
4
5
  from typing import Any, Optional
5
6
 
@@ -104,6 +105,7 @@ class FunctionClosure(Closure):
104
105
  scope=self.scope.clone(),
105
106
  signature_scope=self.signature_scope.clone(),
106
107
  captured_vars=self.captured_vars.clone(),
108
+ positional_arg_declarations=deepcopy(self.positional_arg_declarations),
107
109
  )
108
110
 
109
111
  def emit(self) -> QuantumCallable:
@@ -1,5 +1,3 @@
1
- from collections.abc import Sequence
2
-
3
1
  from classiq.interface.exceptions import (
4
2
  ClassiqExpansionError,
5
3
  ClassiqInternalExpansionError,
@@ -11,8 +9,6 @@ from classiq.interface.generator.functions.type_name import (
11
9
  )
12
10
  from classiq.interface.model.bind_operation import BindOperation
13
11
  from classiq.interface.model.inplace_binary_operation import BinaryOperation
14
- from classiq.interface.model.port_declaration import PortDeclaration
15
- from classiq.interface.model.quantum_function_declaration import PositionalArg
16
12
  from classiq.interface.model.quantum_type import (
17
13
  QuantumBit,
18
14
  QuantumBitvector,
@@ -147,20 +143,16 @@ def set_length_by_size(
147
143
  quantum_array.length = Expression(expr=str(size // element_size))
148
144
 
149
145
 
150
- def validate_bind_targets(
151
- bind: BindOperation, scope: Scope, allow_symbolic_size: bool
152
- ) -> None:
146
+ def validate_bind_targets(bind: BindOperation, scope: Scope) -> None:
153
147
  illegal_qnum_bind_targets = []
154
148
  for out_handle in bind.out_handles:
155
149
  out_var = scope[out_handle.name].as_type(QuantumSymbol)
156
150
  out_var_type = out_var.quantum_type
157
151
  if not isinstance(out_var_type, QuantumNumeric):
158
152
  continue
159
- if (allow_symbolic_size and not out_var_type.is_instantiated) or (
160
- not allow_symbolic_size and not out_var_type.has_size_in_bits
161
- ):
153
+ if not out_var_type.has_size_in_bits:
162
154
  illegal_qnum_bind_targets.append(str(out_var.handle))
163
- elif not allow_symbolic_size and not out_var_type.has_sign:
155
+ elif not out_var_type.has_sign:
164
156
  assert not out_var_type.has_fraction_digits
165
157
  illegal_qnum_bind_targets.append(str(out_var.handle))
166
158
  if len(illegal_qnum_bind_targets) > 0:
@@ -187,13 +179,6 @@ def get_inplace_op_scalar_as_numeric(
187
179
  raise ClassiqInternalExpansionError(f"Unexpected scalar type {var.quantum_type}")
188
180
 
189
181
 
190
- def is_signature_monomorphic(params: Sequence[PositionalArg]) -> bool:
191
- return all(
192
- isinstance(param, PortDeclaration) and param.quantum_type.is_evaluated
193
- for param in params
194
- )
195
-
196
-
197
182
  def set_bounds(from_type: QuantumType, to_type: QuantumNumeric) -> None:
198
183
  if not isinstance(from_type, QuantumNumeric):
199
184
  to_type.reset_bounds()
@@ -29,14 +29,7 @@ from classiq.model_expansions.capturing.captured_vars import (
29
29
  validate_captured_directions,
30
30
  validate_end_state,
31
31
  )
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
- )
32
+ from classiq.model_expansions.closure import Closure, FunctionClosure
40
33
  from classiq.model_expansions.scope import Scope
41
34
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
42
35
 
@@ -225,15 +218,6 @@ class OperationBuilder:
225
218
  if name == MAIN_FUNCTION_NAME:
226
219
  return name
227
220
 
228
- if name in self.current_scope:
229
- orig_func = self.current_scope[name].value
230
- if (
231
- isinstance(orig_func, FunctionClosure)
232
- and not isinstance(orig_func, GenerativeFunctionClosure)
233
- and is_signature_monomorphic(orig_func.positional_arg_declarations)
234
- ):
235
- return name
236
-
237
221
  for _ in self.current_scope:
238
222
  name = self._counted_name_allocator.allocate(
239
223
  f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}"
@@ -76,11 +76,15 @@ class AllocateEmitter(Emitter[Allocate]):
76
76
  target: QuantumSymbol,
77
77
  op_update_dict: dict[str, Expression],
78
78
  ) -> None:
79
- if not target.quantum_type.is_evaluated:
79
+ if target.quantum_type.is_evaluated:
80
+ expr = str(target.quantum_type.size_in_bits)
81
+ elif self._allow_symbolic_attrs:
82
+ expr = f"{target.handle}.size"
83
+ else:
80
84
  raise ClassiqValueError(
81
85
  f"Could not infer the size of variable {str(target.handle)!r}"
82
86
  )
83
- op_update_dict["size"] = Expression(expr=str(target.quantum_type.size_in_bits))
87
+ op_update_dict["size"] = Expression(expr=expr)
84
88
 
85
89
  def _handle_with_size(
86
90
  self,
@@ -88,7 +92,7 @@ class AllocateEmitter(Emitter[Allocate]):
88
92
  size: Expression,
89
93
  op_update_dict: dict[str, Expression],
90
94
  ) -> None:
91
- size_value = self._interpret_size(size)
95
+ size_value = self._interpret_size(size, str(target.handle))
92
96
  op_update_dict["size"] = Expression(expr=str(size_value))
93
97
 
94
98
  if not isinstance(size_value, sympy.Basic):
@@ -106,12 +110,13 @@ class AllocateEmitter(Emitter[Allocate]):
106
110
  fraction_digits: Expression,
107
111
  op_update_dict: dict[str, Expression],
108
112
  ) -> None:
113
+ var_name = str(target.handle)
109
114
  if not isinstance(target.quantum_type, QuantumNumeric):
110
115
  raise ClassiqValueError(
111
- f"Non-numeric variable {str(target.handle)!r} cannot be allocated with numeric attributes"
116
+ f"Non-numeric variable {var_name!r} cannot be allocated with numeric attributes"
112
117
  )
113
118
 
114
- size_value = self._interpret_size(size)
119
+ size_value = self._interpret_size(size, var_name)
115
120
  op_update_dict["size"] = Expression(expr=str(size_value))
116
121
  is_signed_value = self._interpret_is_signed(is_signed)
117
122
  op_update_dict["is_signed"] = Expression(expr=str(is_signed_value))
@@ -130,12 +135,18 @@ class AllocateEmitter(Emitter[Allocate]):
130
135
  fraction_digits=op_update_dict["fraction_digits"],
131
136
  ),
132
137
  target.quantum_type,
133
- str(target.handle),
138
+ var_name,
134
139
  )
135
140
 
136
- def _interpret_size(self, size: Expression) -> Union[int, float, sympy.Basic]:
141
+ def _interpret_size(
142
+ self, size: Expression, var_name: str
143
+ ) -> Union[int, float, sympy.Basic]:
137
144
  size_value = self._interpreter.evaluate(size).value
138
145
  if not self._allow_symbolic_attrs and not isinstance(size_value, (int, float)):
146
+ if size.expr == f"{var_name}.size":
147
+ raise ClassiqValueError(
148
+ f"Could not infer the size of variable {var_name!r}"
149
+ )
139
150
  raise ClassiqValueError(
140
151
  f"The number of allocated qubits must be an integer. Got "
141
152
  f"{str(size_value)!r}"
@@ -78,7 +78,11 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
78
78
  expr = self._evaluate_expression(op.expression)
79
79
  if len(self._get_classical_vars_in_expression(expr)):
80
80
  return None
81
+
81
82
  symbols = self._get_symbols_in_expression(expr)
83
+ if any(not symbol.quantum_type.is_instantiated for symbol in symbols):
84
+ return None
85
+
82
86
  expr_str = rename_variables(
83
87
  expr.expr,
84
88
  {str(symbol.handle): symbol.handle.identifier for symbol in symbols}
@@ -31,7 +31,8 @@ class BindEmitter(Emitter[BindOperation]):
31
31
 
32
32
  def emit(self, bind: BindOperation, /) -> bool:
33
33
  inputs, outputs = self._get_inputs_outputs(bind)
34
- validate_bind_targets(bind, self._current_scope, self._allow_symbolic_size)
34
+ if not self._allow_symbolic_size:
35
+ validate_bind_targets(bind, self._current_scope)
35
36
  self._process_var_sizes(bind, inputs, outputs)
36
37
 
37
38
  for symbol in chain(inputs, outputs):