classiq 0.69.0__py3-none-any.whl → 0.71.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 (97) hide show
  1. classiq/analyzer/analyzer.py +0 -18
  2. classiq/analyzer/url_utils.py +9 -4
  3. classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/backend/quantum_backend_providers.py +6 -0
  6. classiq/interface/chemistry/operator.py +1 -21
  7. classiq/interface/debug_info/debug_info.py +4 -0
  8. classiq/interface/executor/quantum_instruction_set.py +1 -0
  9. classiq/interface/generator/arith/arithmetic.py +21 -6
  10. classiq/interface/generator/circuit_code/circuit_code.py +4 -0
  11. classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
  12. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
  13. classiq/interface/generator/expressions/expression_constants.py +0 -3
  14. classiq/interface/generator/expressions/expression_types.py +12 -4
  15. classiq/interface/generator/expressions/proxies/__init__.py +0 -0
  16. classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
  17. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +79 -0
  18. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
  19. classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
  20. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +35 -0
  21. classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
  22. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  23. classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
  24. classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
  25. classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
  26. classiq/interface/generator/functions/classical_type.py +24 -30
  27. classiq/interface/generator/functions/type_name.py +42 -2
  28. classiq/interface/generator/functions/type_qualifier.py +7 -0
  29. classiq/interface/generator/generated_circuit_data.py +22 -4
  30. classiq/interface/generator/model/preferences/preferences.py +1 -0
  31. classiq/interface/generator/quantum_function_call.py +8 -1
  32. classiq/interface/generator/quantum_program.py +0 -1
  33. classiq/interface/generator/synthesis_execution_parameter.py +1 -0
  34. classiq/interface/generator/types/compilation_metadata.py +1 -0
  35. classiq/interface/ide/visual_model.py +1 -0
  36. classiq/interface/interface_version.py +1 -1
  37. classiq/interface/model/allocate.py +7 -0
  38. classiq/interface/model/block.py +12 -0
  39. classiq/interface/model/classical_if.py +4 -0
  40. classiq/interface/model/inplace_binary_operation.py +4 -0
  41. classiq/interface/model/model.py +3 -1
  42. classiq/interface/model/native_function_definition.py +0 -10
  43. classiq/interface/model/phase_operation.py +4 -0
  44. classiq/interface/model/port_declaration.py +3 -0
  45. classiq/interface/model/power.py +4 -0
  46. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -0
  47. classiq/interface/model/quantum_function_call.py +4 -0
  48. classiq/interface/model/quantum_function_declaration.py +1 -1
  49. classiq/interface/model/quantum_statement.py +5 -0
  50. classiq/interface/model/quantum_type.py +37 -3
  51. classiq/interface/model/repeat.py +4 -0
  52. classiq/interface/model/statement_block.py +3 -0
  53. classiq/interface/model/variable_declaration_statement.py +5 -0
  54. classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
  55. classiq/model_expansions/capturing/captured_vars.py +156 -34
  56. classiq/model_expansions/evaluators/arg_type_match.py +4 -2
  57. classiq/model_expansions/evaluators/classical_expression.py +2 -2
  58. classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
  59. classiq/model_expansions/evaluators/control.py +1 -1
  60. classiq/model_expansions/evaluators/parameter_types.py +72 -16
  61. classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
  62. classiq/model_expansions/expression_evaluator.py +3 -12
  63. classiq/model_expansions/function_builder.py +2 -8
  64. classiq/model_expansions/generative_functions.py +39 -3
  65. classiq/model_expansions/interpreters/base_interpreter.py +3 -4
  66. classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
  67. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
  68. classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
  69. classiq/model_expansions/quantum_operations/call_emitter.py +46 -6
  70. classiq/model_expansions/quantum_operations/emitter.py +41 -0
  71. classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
  72. classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
  73. classiq/model_expansions/scope.py +7 -14
  74. classiq/model_expansions/scope_initialization.py +32 -39
  75. classiq/model_expansions/transformers/model_renamer.py +13 -4
  76. classiq/model_expansions/visitors/variable_references.py +8 -4
  77. classiq/open_library/functions/__init__.py +2 -0
  78. classiq/open_library/functions/lookup_table.py +58 -0
  79. classiq/qmod/__init__.py +3 -1
  80. classiq/qmod/declaration_inferrer.py +55 -25
  81. classiq/qmod/native/pretty_printer.py +25 -3
  82. classiq/qmod/pretty_print/pretty_printer.py +31 -14
  83. classiq/qmod/python_classical_type.py +12 -1
  84. classiq/qmod/qfunc.py +33 -8
  85. classiq/qmod/qmod_parameter.py +8 -0
  86. classiq/qmod/qmod_variable.py +189 -151
  87. classiq/qmod/quantum_function.py +3 -4
  88. classiq/qmod/semantics/annotation/call_annotation.py +0 -28
  89. classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
  90. classiq/qmod/semantics/validation/main_validation.py +1 -1
  91. classiq/qmod/semantics/validation/type_hints.py +38 -0
  92. classiq/qmod/utilities.py +38 -1
  93. {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/METADATA +10 -12
  94. {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/RECORD +97 -82
  95. {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/WHEEL +1 -1
  96. /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
  97. /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -3,15 +3,21 @@ from collections.abc import Sequence
3
3
  from dataclasses import dataclass, field
4
4
  from typing import TYPE_CHECKING
5
5
 
6
+ from typing_extensions import Self
7
+
6
8
  from classiq.interface.enum_utils import StrEnum
7
9
  from classiq.interface.exceptions import (
8
10
  ClassiqExpansionError,
9
11
  ClassiqInternalExpansionError,
10
12
  )
11
13
  from classiq.interface.generator.expressions.expression import Expression
14
+ from classiq.interface.generator.functions.classical_type import ClassicalType
12
15
  from classiq.interface.generator.functions.port_declaration import (
13
16
  PortDeclarationDirection,
14
17
  )
18
+ from classiq.interface.model.classical_parameter_declaration import (
19
+ ClassicalParameterDeclaration,
20
+ )
15
21
  from classiq.interface.model.handle_binding import (
16
22
  HandleBinding,
17
23
  NestedHandleBinding,
@@ -19,6 +25,7 @@ from classiq.interface.model.handle_binding import (
19
25
  )
20
26
  from classiq.interface.model.port_declaration import PortDeclaration
21
27
  from classiq.interface.model.quantum_function_call import ArgValue
28
+ from classiq.interface.model.quantum_function_declaration import PositionalArg
22
29
  from classiq.interface.model.quantum_type import QuantumBitvector, QuantumType
23
30
  from classiq.interface.model.variable_declaration_statement import (
24
31
  VariableDeclarationStatement,
@@ -28,7 +35,11 @@ from classiq.model_expansions.capturing.mangling_utils import (
28
35
  demangle_handle,
29
36
  mangle_captured_var_name,
30
37
  )
31
- from classiq.model_expansions.transformers.var_splitter import SymbolPart, SymbolParts
38
+ from classiq.model_expansions.transformers.model_renamer import (
39
+ HandleRenaming,
40
+ SymbolRenaming,
41
+ )
42
+ from classiq.model_expansions.transformers.var_splitter import SymbolPart
32
43
 
33
44
  if TYPE_CHECKING:
34
45
  from classiq.model_expansions.closure import FunctionClosure
@@ -72,12 +83,29 @@ class PortDirection(StrEnum):
72
83
 
73
84
 
74
85
  @dataclass(frozen=True)
75
- class _CapturedHandle:
86
+ class _Captured:
87
+ defining_function: "FunctionClosure"
88
+ is_propagated: bool
89
+
90
+ def change_defining_function(
91
+ self, new_defining_function: "FunctionClosure"
92
+ ) -> Self:
93
+ return dataclasses.replace(self, defining_function=new_defining_function)
94
+
95
+ def set_propagated(self) -> Self:
96
+ return dataclasses.replace(self, is_propagated=True)
97
+
98
+ def update_propagation(self, other_captured_handle: Self) -> Self:
99
+ if self.is_propagated and not other_captured_handle.is_propagated:
100
+ return dataclasses.replace(self, is_propagated=False)
101
+ return self
102
+
103
+
104
+ @dataclass(frozen=True)
105
+ class _CapturedHandle(_Captured):
76
106
  handle: HandleBinding
77
107
  quantum_type: QuantumType
78
- defining_function: "FunctionClosure"
79
108
  direction: PortDirection
80
- is_propagated: bool
81
109
 
82
110
  @property
83
111
  def mangled_name(self) -> str:
@@ -103,27 +131,33 @@ class _CapturedHandle:
103
131
  def change_direction(self, new_direction: PortDirection) -> "_CapturedHandle":
104
132
  return dataclasses.replace(self, direction=new_direction)
105
133
 
106
- def change_defining_function(
107
- self, new_defining_function: "FunctionClosure"
108
- ) -> "_CapturedHandle":
109
- return dataclasses.replace(self, defining_function=new_defining_function)
110
-
111
- def set_propagated(self) -> "_CapturedHandle":
112
- return dataclasses.replace(self, is_propagated=True)
113
-
114
- def update_propagation(
115
- self, other_captured_handle: "_CapturedHandle"
116
- ) -> "_CapturedHandle":
117
- if self.is_propagated and not other_captured_handle.is_propagated:
118
- return dataclasses.replace(self, is_propagated=False)
119
- return self
120
-
121
134
  def set_symbol(
122
135
  self, handle: HandleBinding, quantum_type: QuantumType
123
136
  ) -> "_CapturedHandle":
124
137
  return dataclasses.replace(self, handle=handle, quantum_type=quantum_type)
125
138
 
126
139
 
140
+ @dataclass(frozen=True)
141
+ class _CapturedClassicalVar(_Captured):
142
+ name: str
143
+ classical_type: ClassicalType
144
+ defining_function: "FunctionClosure"
145
+
146
+ @property
147
+ def mangled_name(self) -> str:
148
+ return mangle_captured_var_name(
149
+ self.name,
150
+ self.defining_function.name,
151
+ self.defining_function.depth,
152
+ )
153
+
154
+ @property
155
+ def parameter_declaration(self) -> ClassicalParameterDeclaration:
156
+ return ClassicalParameterDeclaration(
157
+ name=self.mangled_name, classical_type=self.classical_type
158
+ )
159
+
160
+
127
161
  HandleState = tuple[str, "FunctionClosure", bool]
128
162
 
129
163
 
@@ -131,6 +165,7 @@ HandleState = tuple[str, "FunctionClosure", bool]
131
165
  class CapturedVars:
132
166
  _captured_handles: list[_CapturedHandle] = field(default_factory=list)
133
167
  _handle_states: list[HandleState] = field(default_factory=list)
168
+ _captured_classical_vars: list[_CapturedClassicalVar] = field(default_factory=list)
134
169
 
135
170
  def capture_handle(
136
171
  self,
@@ -332,16 +367,48 @@ class CapturedVars:
332
367
  )
333
368
  return merged_handle, merged_quantum_type
334
369
 
370
+ def capture_classical_var(
371
+ self,
372
+ var_name: str,
373
+ var_type: ClassicalType,
374
+ defining_function: "FunctionClosure",
375
+ ) -> None:
376
+ self._capture_classical_var(
377
+ _CapturedClassicalVar(
378
+ name=var_name,
379
+ classical_type=var_type,
380
+ defining_function=defining_function,
381
+ is_propagated=False,
382
+ )
383
+ )
384
+
385
+ def _capture_classical_var(
386
+ self, captured_classical_var: _CapturedClassicalVar
387
+ ) -> None:
388
+ for existing_captured_classical_var in self._captured_classical_vars:
389
+ if (
390
+ existing_captured_classical_var.name == captured_classical_var.name
391
+ and _same_closure(
392
+ existing_captured_classical_var.defining_function,
393
+ captured_classical_var.defining_function,
394
+ )
395
+ ):
396
+ return
397
+ self._captured_classical_vars.append(captured_classical_var)
398
+
335
399
  def update(self, other_captured_vars: "CapturedVars") -> None:
336
400
  for captured_handle in other_captured_vars._captured_handles:
337
401
  self._capture_handle(captured_handle)
402
+ for captured_classical_var in other_captured_vars._captured_classical_vars:
403
+ self._capture_classical_var(captured_classical_var)
338
404
 
339
405
  def negate(self) -> "CapturedVars":
340
406
  return CapturedVars(
341
407
  _captured_handles=[
342
408
  captured_handle.change_direction(captured_handle.direction.negate())
343
409
  for captured_handle in self._captured_handles
344
- ]
410
+ ],
411
+ _captured_classical_vars=self._captured_classical_vars,
345
412
  )
346
413
 
347
414
  def filter_vars(self, current_function: "FunctionClosure") -> "CapturedVars":
@@ -352,7 +419,14 @@ class CapturedVars:
352
419
  if not _same_closure(
353
420
  captured_handle.defining_function, current_function
354
421
  )
355
- ]
422
+ ],
423
+ _captured_classical_vars=[
424
+ captured_classical_var
425
+ for captured_classical_var in self._captured_classical_vars
426
+ if not _same_closure(
427
+ captured_classical_var.defining_function, current_function
428
+ )
429
+ ],
356
430
  )
357
431
 
358
432
  def filter_var_decls(
@@ -367,7 +441,8 @@ class CapturedVars:
367
441
  current_declared_vars is not None
368
442
  and captured_handle.handle.name not in current_declared_vars
369
443
  )
370
- ]
444
+ ],
445
+ _captured_classical_vars=self._captured_classical_vars,
371
446
  )
372
447
 
373
448
  def set_propagated(self) -> "CapturedVars":
@@ -375,16 +450,37 @@ class CapturedVars:
375
450
  _captured_handles=[
376
451
  captured_handle.set_propagated()
377
452
  for captured_handle in self._captured_handles
378
- ]
453
+ ],
454
+ _captured_classical_vars=[
455
+ captured_classical_var.set_propagated()
456
+ for captured_classical_var in self._captured_classical_vars
457
+ ],
379
458
  )
380
459
 
381
- def get_captured_ports(self) -> list[PortDeclaration]:
382
- return [captured_handle.port for captured_handle in self._captured_handles]
383
-
384
- def get_captured_args(
385
- self, current_function: "FunctionClosure"
386
- ) -> list[HandleBinding]:
387
- return [
460
+ def get_captured_parameters(self) -> list[PositionalArg]:
461
+ decls: list[PositionalArg]
462
+ decls = [
463
+ captured_classical_var.parameter_declaration
464
+ for captured_classical_var in self._captured_classical_vars
465
+ ]
466
+ decls += [captured_handle.port for captured_handle in self._captured_handles]
467
+ return decls
468
+
469
+ def get_captured_args(self, current_function: "FunctionClosure") -> list[ArgValue]:
470
+ args: list[ArgValue]
471
+ args = [
472
+ Expression(
473
+ expr=(
474
+ captured_classical_var.name
475
+ if _same_closure(
476
+ current_function, captured_classical_var.defining_function
477
+ )
478
+ else captured_classical_var.mangled_name
479
+ )
480
+ )
481
+ for captured_classical_var in self._captured_classical_vars
482
+ ]
483
+ args += [
388
484
  (
389
485
  captured_handle.handle
390
486
  if _same_closure(current_function, captured_handle.defining_function)
@@ -392,9 +488,11 @@ class CapturedVars:
392
488
  )
393
489
  for captured_handle in self._captured_handles
394
490
  ]
491
+ return args
395
492
 
396
- def get_captured_mapping(self) -> SymbolParts:
397
- return {
493
+ def get_captured_mapping(self) -> SymbolRenaming:
494
+ mapping: SymbolRenaming
495
+ mapping = {
398
496
  captured_handle.handle: [
399
497
  SymbolPart(
400
498
  source_handle=captured_handle.handle,
@@ -405,6 +503,17 @@ class CapturedVars:
405
503
  for captured_handle in self._captured_handles
406
504
  if not captured_handle.is_propagated
407
505
  }
506
+ mapping |= {
507
+ (handle := HandleBinding(name=captured_classical_var.name)): [
508
+ HandleRenaming(
509
+ source_handle=handle,
510
+ target_var_name=captured_classical_var.mangled_name,
511
+ )
512
+ ]
513
+ for captured_classical_var in self._captured_classical_vars
514
+ if not captured_classical_var.is_propagated
515
+ }
516
+ return mapping
408
517
 
409
518
  def init_var(self, var_name: str, defining_function: "FunctionClosure") -> None:
410
519
  self._handle_states.append((var_name, defining_function, False))
@@ -470,6 +579,7 @@ class CapturedVars:
470
579
  return CapturedVars(
471
580
  _captured_handles=list(self._captured_handles),
472
581
  _handle_states=list(self._handle_states),
582
+ _captured_classical_vars=list(self._captured_classical_vars),
473
583
  )
474
584
 
475
585
  def set(
@@ -492,6 +602,14 @@ class CapturedVars:
492
602
  self._handle_states.append((var, target_func, state))
493
603
  else:
494
604
  self._handle_states.append((var, defining_function, state))
605
+ self._captured_classical_vars = []
606
+ for captured_classical_var in other._captured_classical_vars:
607
+ if _same_closure(captured_classical_var.defining_function, source_func):
608
+ self._captured_classical_vars.append(
609
+ captured_classical_var.change_defining_function(target_func)
610
+ )
611
+ else:
612
+ self._captured_classical_vars.append(captured_classical_var)
495
613
 
496
614
 
497
615
  def _same_closure(closure_1: "FunctionClosure", closure_2: "FunctionClosure") -> bool:
@@ -499,11 +617,15 @@ def _same_closure(closure_1: "FunctionClosure", closure_2: "FunctionClosure") ->
499
617
 
500
618
 
501
619
  def validate_args_are_not_propagated(
502
- args: Sequence[ArgValue], captured_vars: Sequence[HandleBinding]
620
+ args: Sequence[ArgValue], captured_vars: Sequence[ArgValue]
503
621
  ) -> None:
504
622
  if not captured_vars:
505
623
  return
506
- captured_handles = {demangle_handle(handle) for handle in captured_vars}
624
+ captured_handles = {
625
+ demangle_handle(handle)
626
+ for handle in captured_vars
627
+ if isinstance(handle, HandleBinding)
628
+ }
507
629
  arg_handles = {
508
630
  demangle_handle(arg) for arg in args if isinstance(arg, HandleBinding)
509
631
  }
@@ -3,10 +3,12 @@ from enum import Enum
3
3
  from typing import Any
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqExpansionError
6
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
7
- from classiq.interface.generator.expressions.qmod_struct_instance import (
6
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
8
7
  QmodStructInstance,
9
8
  )
9
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
10
+ QmodSizedProxy,
11
+ )
10
12
  from classiq.interface.generator.functions.classical_type import (
11
13
  StructMetaType,
12
14
  )
@@ -23,13 +23,13 @@ def evaluate_classical_expression(expr: Expression, scope: Scope) -> Evaluated:
23
23
  )
24
24
  for name, evaluated in all_symbols
25
25
  if isinstance(evaluated.value, QuantumSymbol)
26
- and evaluated.value.quantum_type.has_size_in_bits
26
+ and evaluated.value.quantum_type.is_evaluated
27
27
  }
28
28
  uninitialized_locals = {
29
29
  name
30
30
  for name, evaluated in all_symbols
31
31
  if isinstance(evaluated.value, QuantumSymbol)
32
- and not evaluated.value.quantum_type.has_size_in_bits
32
+ and not evaluated.value.quantum_type.is_evaluated
33
33
  }
34
34
 
35
35
  ret = evaluate(expr, locals_dict, uninitialized_locals)
@@ -0,0 +1,70 @@
1
+ from typing import Any, Union
2
+
3
+ from classiq.interface.exceptions import ClassiqExpansionError
4
+ from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
5
+ ClassicalArrayProxy,
6
+ )
7
+ from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
8
+ ClassicalStructProxy,
9
+ )
10
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
11
+ QmodStructInstance,
12
+ )
13
+ from classiq.interface.generator.functions.classical_type import (
14
+ ClassicalArray,
15
+ ClassicalList,
16
+ ClassicalType,
17
+ )
18
+ from classiq.interface.generator.functions.type_name import TypeName
19
+ from classiq.interface.generator.types.struct_declaration import StructDeclaration
20
+
21
+
22
+ def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalType:
23
+ if isinstance(classical_type, TypeName):
24
+ return _infer_classical_struct_type(val, classical_type)
25
+ if isinstance(classical_type, (ClassicalArray, ClassicalList)):
26
+ return _infer_classical_array_type(val, classical_type)
27
+ return classical_type
28
+
29
+
30
+ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> ClassicalType:
31
+ if not isinstance(val, (QmodStructInstance, ClassicalStructProxy)):
32
+ return classical_type
33
+ decl = val.struct_declaration
34
+ new_fields = {
35
+ field_name: infer_classical_type(field_val, field_type)
36
+ for (field_name, field_val), field_type in zip(
37
+ val.fields.items(),
38
+ decl.variables.values(),
39
+ strict=True,
40
+ )
41
+ }
42
+ new_classical_type = TypeName(name=decl.name)
43
+ new_classical_type.set_classical_struct_decl(
44
+ StructDeclaration(name=decl.name, variables=new_fields)
45
+ )
46
+ return new_classical_type
47
+
48
+
49
+ def _infer_classical_array_type(
50
+ val: Any, classical_type: Union[ClassicalArray, ClassicalList]
51
+ ) -> ClassicalType:
52
+ if isinstance(val, list):
53
+ val_length = len(val)
54
+ elif isinstance(val, ClassicalArrayProxy):
55
+ val_length = val.length
56
+ else:
57
+ raise ClassiqExpansionError(f"Array expected, got {str(val)!r}")
58
+ if isinstance(classical_type, ClassicalArray) and val_length != classical_type.size:
59
+ raise ClassiqExpansionError(
60
+ f"Type mismatch: Argument has {val_length} items but "
61
+ f"{classical_type.size} expected"
62
+ )
63
+ return ClassicalArray(
64
+ element_type=(
65
+ infer_classical_type(val[0], classical_type.element_type)
66
+ if val_length > 0
67
+ else classical_type.element_type
68
+ ),
69
+ size=val_length,
70
+ )
@@ -8,7 +8,7 @@ from classiq.interface.generator.arith.argument_utils import (
8
8
  unsigned_integer_interpretation,
9
9
  )
10
10
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
11
- from classiq.interface.generator.expressions.qmod_qscalar_proxy import (
11
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qscalar_proxy import (
12
12
  QmodQNumProxy,
13
13
  QmodQScalarProxy,
14
14
  QmodSizedProxy,
@@ -1,9 +1,10 @@
1
- from typing import Union
1
+ from typing import TypeVar, Union
2
2
 
3
3
  from classiq.interface.exceptions import (
4
4
  ClassiqExpansionError,
5
5
  ClassiqInternalExpansionError,
6
6
  )
7
+ from classiq.interface.generator.expressions.expression import Expression
7
8
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
8
9
  from classiq.interface.generator.functions.port_declaration import (
9
10
  PortDeclarationDirection,
@@ -11,6 +12,9 @@ from classiq.interface.generator.functions.port_declaration import (
11
12
  from classiq.interface.generator.functions.type_name import (
12
13
  TypeName,
13
14
  )
15
+ from classiq.interface.model.classical_parameter_declaration import (
16
+ ClassicalParameterDeclaration,
17
+ )
14
18
  from classiq.interface.model.port_declaration import PortDeclaration
15
19
  from classiq.interface.model.quantum_function_declaration import (
16
20
  PositionalArg,
@@ -28,11 +32,12 @@ from classiq.model_expansions.evaluators.arg_type_match import check_type_match
28
32
  from classiq.model_expansions.evaluators.classical_expression import (
29
33
  evaluate_classical_expression,
30
34
  )
35
+ from classiq.model_expansions.evaluators.classical_type_inference import (
36
+ infer_classical_type,
37
+ )
31
38
  from classiq.model_expansions.evaluators.quantum_type_utils import (
32
39
  copy_type_information,
33
40
  set_element_type,
34
- set_fraction_digits,
35
- set_is_signed,
36
41
  set_length,
37
42
  set_size,
38
43
  )
@@ -127,6 +132,14 @@ def _cast(
127
132
  def _evaluate_type_from_arg(
128
133
  parameter: PositionalArg, argument: Evaluated, inner_scope: Scope
129
134
  ) -> PositionalArg:
135
+ if isinstance(parameter, ClassicalParameterDeclaration):
136
+ return ClassicalParameterDeclaration(
137
+ name=parameter.name,
138
+ classical_type=infer_classical_type(
139
+ argument.value, parameter.classical_type
140
+ ),
141
+ )
142
+
130
143
  if not isinstance(parameter, PortDeclaration):
131
144
  return parameter
132
145
 
@@ -162,9 +175,14 @@ def _evaluate_qarray_in_quantum_symbol(
162
175
  )
163
176
  set_element_type(type_to_update, new_element_type)
164
177
  if type_to_update.length is not None:
165
- new_length = evaluate_classical_expression(
166
- type_to_update.length, scope
167
- ).as_type(int)
178
+ new_length = _eval_expr(
179
+ type_to_update.length,
180
+ scope,
181
+ int,
182
+ type_to_update.type_name,
183
+ "length",
184
+ param_name,
185
+ )
168
186
  set_length(type_to_update, new_length)
169
187
  return type_to_update
170
188
 
@@ -173,23 +191,61 @@ def _evaluate_qnum_in_quantum_symbol(
173
191
  type_to_update: QuantumNumeric, scope: Scope, param_name: str
174
192
  ) -> QuantumNumeric:
175
193
  if type_to_update.is_signed is not None:
176
- new_is_sign = evaluate_classical_expression(
177
- type_to_update.is_signed, scope
178
- ).as_type(bool)
179
- set_is_signed(type_to_update, new_is_sign, param_name)
194
+ new_is_sign = _eval_expr(
195
+ type_to_update.is_signed,
196
+ scope,
197
+ bool,
198
+ type_to_update.type_name,
199
+ "sign",
200
+ param_name,
201
+ )
202
+ type_to_update.is_signed = Expression(expr=str(new_is_sign))
180
203
  if type_to_update.fraction_digits is not None:
181
- new_fraction_digits = evaluate_classical_expression(
182
- type_to_update.fraction_digits, scope
183
- ).as_type(int)
184
- set_fraction_digits(type_to_update, new_fraction_digits, param_name)
204
+ new_fraction_digits = _eval_expr(
205
+ type_to_update.fraction_digits,
206
+ scope,
207
+ int,
208
+ type_to_update.type_name,
209
+ "fraction digits",
210
+ param_name,
211
+ )
212
+ type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
185
213
  if type_to_update.size is not None:
186
- new_size = evaluate_classical_expression(type_to_update.size, scope).as_type(
187
- int
214
+ new_size = _eval_expr(
215
+ type_to_update.size,
216
+ scope,
217
+ int,
218
+ type_to_update.type_name,
219
+ "size",
220
+ param_name,
188
221
  )
189
222
  set_size(type_to_update, new_size, param_name)
190
223
  return type_to_update
191
224
 
192
225
 
226
+ _EXPR_TYPE = TypeVar("_EXPR_TYPE")
227
+
228
+
229
+ def _eval_expr(
230
+ expression: Expression,
231
+ scope: Scope,
232
+ expected_type: type[_EXPR_TYPE],
233
+ type_name: str,
234
+ attr_name: str,
235
+ param_name: str,
236
+ ) -> _EXPR_TYPE:
237
+ val = evaluate_classical_expression(expression, scope).value
238
+ if expected_type is int and isinstance(val, float):
239
+ val = int(val)
240
+ if not isinstance(val, expected_type):
241
+ raise ClassiqExpansionError(
242
+ f"When inferring the type of parameter {param_name!r}: "
243
+ f"{type_name} {attr_name} must be {expected_type.__name__}, got "
244
+ f"{str(val)!r}"
245
+ )
246
+ return val
247
+
248
+
193
249
  def _evaluate_qstruct_in_quantum_symbol(
194
250
  type_to_update: TypeName, scope: Scope, param_name: str
195
251
  ) -> TypeName:
@@ -1,5 +1,4 @@
1
1
  from collections.abc import Sequence
2
- from typing import Optional
3
2
 
4
3
  from classiq.interface.exceptions import (
5
4
  ClassiqExpansionError,
@@ -33,12 +32,11 @@ def copy_type_information(
33
32
  if isinstance(to_type, QuantumBit):
34
33
  set_size(to_type, from_type.size_in_bits, to_param_name)
35
34
  elif isinstance(to_type, QuantumNumeric):
36
- set_is_signed(to_type, getattr(from_type, "sign_value", None), to_param_name)
37
- set_fraction_digits(
38
- to_type,
39
- getattr(from_type, "fraction_digits_value", None),
40
- to_param_name,
41
- )
35
+ if to_type.size is None and isinstance(from_type, QuantumNumeric):
36
+ to_type.is_signed = Expression(expr=str(from_type.sign_value))
37
+ to_type.fraction_digits = Expression(
38
+ expr=str(from_type.fraction_digits_value)
39
+ )
42
40
  set_size(to_type, from_type.size_in_bits, to_param_name)
43
41
  elif isinstance(to_type, QuantumBitvector):
44
42
  if isinstance(from_type, QuantumBitvector) and type( # noqa: E721
@@ -73,8 +71,8 @@ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
73
71
  if isinstance(quantum_type, QuantumNumeric):
74
72
  quantum_type.size = Expression(expr=str(size))
75
73
  if not quantum_type.has_sign or not quantum_type.has_fraction_digits:
76
- set_is_signed(quantum_type, False, param_name)
77
- set_fraction_digits(quantum_type, 0, param_name)
74
+ quantum_type.is_signed = Expression(expr="False")
75
+ quantum_type.fraction_digits = Expression(expr="0")
78
76
  elif isinstance(quantum_type, QuantumBitvector):
79
77
  if quantum_type.has_length:
80
78
  if size % quantum_type.length_value != 0:
@@ -107,54 +105,6 @@ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
107
105
  set_size(fields_without_size[0], size - predetermined_size_part, param_name)
108
106
 
109
107
 
110
- def set_fraction_digits(
111
- quantum_numeric: QuantumNumeric, fraction_digits: Optional[int], param_name: str
112
- ) -> None:
113
- if fraction_digits is not None and fraction_digits < 0:
114
- raise ClassiqExpansionError(
115
- f"Number of fraction digits for {param_name!r} was deduced to be negative: "
116
- f"{fraction_digits!r}"
117
- )
118
-
119
- if (
120
- fraction_digits is not None
121
- and quantum_numeric.fraction_digits is not None
122
- and quantum_numeric.fraction_digits.is_evaluated()
123
- and quantum_numeric.fraction_digits_value != fraction_digits
124
- ):
125
- raise ClassiqExpansionError(
126
- f"Fraction digits mismatch for variable {param_name!r} between declared "
127
- f"fraction digits {quantum_numeric.fraction_digits_value!r} and assigned fraction "
128
- f"digits {fraction_digits!r}"
129
- )
130
-
131
- if not (
132
- quantum_numeric.fraction_digits is not None
133
- and quantum_numeric.fraction_digits.is_evaluated()
134
- ):
135
- quantum_numeric.fraction_digits = Expression(expr=str(fraction_digits or 0))
136
-
137
-
138
- def set_is_signed(
139
- quantum_numeric: QuantumNumeric, is_signed: Optional[bool], param_name: str
140
- ) -> None:
141
- if (
142
- is_signed is not None
143
- and quantum_numeric.is_signed is not None
144
- and quantum_numeric.is_signed.is_evaluated()
145
- and quantum_numeric.sign_value != is_signed
146
- ):
147
- raise ClassiqExpansionError(
148
- f"Sign mismatch for variable {param_name!r} between declared sign {quantum_numeric.sign_value!r} and assigned sign {is_signed!r}"
149
- )
150
-
151
- if not (
152
- quantum_numeric.is_signed is not None
153
- and quantum_numeric.is_signed.is_evaluated()
154
- ):
155
- quantum_numeric.is_signed = Expression(expr=str(is_signed or False))
156
-
157
-
158
108
  def set_element_type(
159
109
  quantum_array: QuantumBitvector, element_type: ConcreteQuantumType
160
110
  ) -> None: