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
@@ -10,20 +10,22 @@ from typing import ( # type: ignore[attr-defined]
10
10
  Generic,
11
11
  Literal,
12
12
  Optional,
13
+ Protocol,
13
14
  TypeVar,
14
15
  Union,
15
16
  _GenericAlias,
16
17
  cast,
17
18
  get_args,
18
19
  get_origin,
20
+ runtime_checkable,
19
21
  )
20
22
 
21
23
  from typing_extensions import ParamSpec, Self, _AnnotatedAlias
22
24
 
23
- from classiq.interface.exceptions import ClassiqValueError
25
+ from classiq.interface.exceptions import ClassiqInternalError, ClassiqValueError
24
26
  from classiq.interface.generator.expressions.expression import Expression
25
27
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
26
- from classiq.interface.generator.expressions.qmod_qarray_proxy import (
28
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qarray_proxy import (
27
29
  ILLEGAL_SLICE_MSG,
28
30
  ILLEGAL_SLICING_STEP_MSG,
29
31
  )
@@ -31,6 +33,7 @@ from classiq.interface.generator.functions.port_declaration import (
31
33
  PortDeclarationDirection,
32
34
  )
33
35
  from classiq.interface.generator.functions.type_name import TypeName
36
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
34
37
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
35
38
  from classiq.interface.model.handle_binding import (
36
39
  FieldHandleBinding,
@@ -72,21 +75,6 @@ from classiq.qmod.utilities import (
72
75
  )
73
76
 
74
77
 
75
- def _is_input_output_typehint(type_hint: Any) -> bool:
76
- return isinstance(type_hint, _AnnotatedAlias) and isinstance(
77
- type_hint.__metadata__[0], PortDeclarationDirection
78
- )
79
-
80
-
81
- def get_type_hint_expr(type_hint: Any) -> str:
82
- if isinstance(type_hint, ForwardRef): # expression in string literal
83
- return str(type_hint.__forward_arg__)
84
- if get_origin(type_hint) == Literal: # explicit numeric literal
85
- return str(get_args(type_hint)[0])
86
- else:
87
- return str(type_hint) # implicit numeric literal
88
-
89
-
90
78
  @contextmanager
91
79
  def _no_current_expandable() -> Iterator[None]:
92
80
  current_expandable = QCallable.CURRENT_EXPANDABLE
@@ -141,23 +129,6 @@ class QVar(Symbolic):
141
129
  def get_qmod_type(self) -> QuantumType:
142
130
  raise NotImplementedError()
143
131
 
144
- @staticmethod
145
- def from_type_hint(type_hint: Any) -> Optional[type["QVar"]]:
146
- if _is_input_output_typehint(type_hint):
147
- return QVar.from_type_hint(type_hint.__args__[0])
148
- type_ = get_origin(type_hint) or type_hint
149
- if issubclass(type_, QVar):
150
- if issubclass(type_, QStruct):
151
- with _no_current_expandable():
152
- type_("DUMMY")._add_qmod_qstruct(qmodule=QMODULE)
153
- return type_
154
- return None
155
-
156
- @classmethod
157
- @abc.abstractmethod
158
- def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
159
- raise NotImplementedError()
160
-
161
132
  @classmethod
162
133
  @abc.abstractmethod
163
134
  def to_qvar(
@@ -168,14 +139,6 @@ class QVar(Symbolic):
168
139
  ) -> Self:
169
140
  raise NotImplementedError()
170
141
 
171
- @classmethod
172
- def port_direction(cls, type_hint: Any) -> PortDeclarationDirection:
173
- if _is_input_output_typehint(type_hint):
174
- assert len(type_hint.__metadata__) >= 1
175
- return type_hint.__metadata__[0]
176
- assert type_hint == cls or get_origin(type_hint) == cls
177
- return PortDeclarationDirection.Inout
178
-
179
142
  def __str__(self) -> str:
180
143
  return self._expr_str
181
144
 
@@ -194,6 +157,12 @@ class QVar(Symbolic):
194
157
  _Q = TypeVar("_Q", bound=QVar)
195
158
  Output = Annotated[_Q, PortDeclarationDirection.Output]
196
159
  Input = Annotated[_Q, PortDeclarationDirection.Input]
160
+ Const = Annotated[
161
+ _Q, TypeQualifier.Const
162
+ ] # A constant variable, up to a phase dependent on the computational basis state
163
+ QFree = Annotated[
164
+ _Q, TypeQualifier.QFree
165
+ ] # A quantum free variable, up to a phase dependent on the computational basis state
197
166
 
198
167
 
199
168
  class QScalar(QVar, SymbolicExpr):
@@ -285,10 +254,6 @@ class QScalar(QVar, SymbolicExpr):
285
254
 
286
255
 
287
256
  class QBit(QScalar):
288
- @classmethod
289
- def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
290
- return QuantumBit()
291
-
292
257
  @classmethod
293
258
  def to_qvar(
294
259
  cls,
@@ -345,43 +310,6 @@ class QNum(Generic[_P], QScalar):
345
310
  )
346
311
  super().__init__(name, _expr_str=_expr_str, depth=3)
347
312
 
348
- @classmethod
349
- def _get_attributes(cls, type_hint: Any) -> tuple[Any, Any, Any]:
350
- type_args = version_portable_get_args(type_hint)
351
- if len(type_args) == 0:
352
- return None, None, None
353
- if len(type_args) not in (1, 3):
354
- raise ClassiqValueError(
355
- "QNum receives three type arguments: QNum[size: int | CInt, "
356
- "is_signed: bool | CBool, fraction_digits: int | CInt]"
357
- )
358
- if len(type_args) == 1:
359
- return unwrap_forward_ref(type_args[0]), None, None
360
- return (
361
- unwrap_forward_ref(type_args[0]),
362
- unwrap_forward_ref(type_args[1]),
363
- unwrap_forward_ref(type_args[2]),
364
- )
365
-
366
- @classmethod
367
- def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
368
- size, is_signed, fraction_digits = cls._get_attributes(type_hint)
369
- return QuantumNumeric(
370
- size=(
371
- Expression(expr=get_type_hint_expr(size)) if size is not None else None
372
- ),
373
- is_signed=(
374
- Expression(expr=get_type_hint_expr(is_signed))
375
- if is_signed is not None
376
- else None
377
- ),
378
- fraction_digits=(
379
- Expression(expr=get_type_hint_expr(fraction_digits))
380
- if fraction_digits is not None
381
- else None
382
- ),
383
- )
384
-
385
313
  @classmethod
386
314
  def to_qvar(
387
315
  cls,
@@ -389,7 +317,7 @@ class QNum(Generic[_P], QScalar):
389
317
  type_hint: Any,
390
318
  expr_str: Optional[str],
391
319
  ) -> "QNum":
392
- return QNum(origin, *cls._get_attributes(type_hint), _expr_str=expr_str)
320
+ return QNum(origin, *_get_qnum_attributes(type_hint), _expr_str=expr_str)
393
321
 
394
322
  def get_qmod_type(self) -> QuantumType:
395
323
  return QuantumNumeric(
@@ -497,36 +425,6 @@ class QArray(ArrayBase[_P], QVar, NonSymbolicExpr):
497
425
  return interpret_expression(str(self.len))
498
426
  return CParamScalar(f"get_field({self}, 'len')")
499
427
 
500
- @classmethod
501
- def _get_attributes(cls, type_hint: Any) -> tuple[type[QVar], Any]:
502
- type_args = version_portable_get_args(type_hint)
503
- if len(type_args) == 0:
504
- return QBit, None
505
- first_arg = unwrap_forward_ref(type_args[0])
506
- if len(type_args) == 1:
507
- if isinstance(first_arg, (str, int)):
508
- return QBit, first_arg
509
- return first_arg, None
510
- if len(type_args) != 2:
511
- raise ClassiqValueError(
512
- "QArray receives two type arguments: QArray[element_type: QVar, "
513
- "length: int | CInt]"
514
- )
515
- second_arg = unwrap_forward_ref(type_args[1])
516
- return cast(tuple[type[QVar], Any], (first_arg, second_arg))
517
-
518
- @classmethod
519
- def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
520
- api_element_type, length = cls._get_attributes(type_hint)
521
- api_element_class = get_origin(api_element_type) or api_element_type
522
- element_type = api_element_class.to_qmod_quantum_type(api_element_type)
523
-
524
- length_expr: Optional[Expression] = None
525
- if length is not None:
526
- length_expr = Expression(expr=get_type_hint_expr(length))
527
-
528
- return QuantumBitvector(element_type=element_type, length=length_expr)
529
-
530
428
  @classmethod
531
429
  def to_qvar(
532
430
  cls,
@@ -534,16 +432,15 @@ class QArray(ArrayBase[_P], QVar, NonSymbolicExpr):
534
432
  type_hint: Any,
535
433
  expr_str: Optional[str],
536
434
  ) -> "QArray":
537
- return QArray(origin, *cls._get_attributes(type_hint), _expr_str=expr_str)
435
+ return QArray(origin, *_get_qarray_attributes(type_hint), _expr_str=expr_str)
538
436
 
539
437
  def get_qmod_type(self) -> QuantumBitvector:
540
- if isinstance(self._element_type, QuantumType):
541
- element_type = self._element_type
542
- else:
543
- element_class = get_origin(self._element_type) or self._element_type
544
- element_type = element_class.to_qmod_quantum_type(self._element_type)
545
438
  return QuantumBitvector(
546
- element_type=element_type,
439
+ element_type=(
440
+ self._element_type
441
+ if isinstance(self._element_type, QuantumType)
442
+ else _to_quantum_type(self._element_type)
443
+ ),
547
444
  length=self._length,
548
445
  )
549
446
 
@@ -561,6 +458,7 @@ class QStruct(QVar):
561
458
  _fields: Optional[Mapping[str, QVar]] = None,
562
459
  _expr_str: Optional[str] = None,
563
460
  ) -> None:
461
+ _register_qstruct(type(self), qmodule=QMODULE)
564
462
  name = _infer_variable_name(origin, self.CONSTRUCTOR_DEPTH)
565
463
  if _struct_name is None or _fields is None:
566
464
  with _no_current_expandable():
@@ -572,17 +470,10 @@ class QStruct(QVar):
572
470
  for field_name, var in _fields.items():
573
471
  setattr(self, field_name, var)
574
472
  super().__init__(name, expr_str=_expr_str)
575
- self._add_qmod_qstruct(qmodule=QMODULE)
576
473
 
577
474
  def get_qmod_type(self) -> QuantumType:
578
475
  return TypeName(name=self._struct_name)
579
476
 
580
- @classmethod
581
- def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
582
- with _no_current_expandable():
583
- type_hint("DUMMY")
584
- return TypeName(name=type_hint.__name__)
585
-
586
477
  @classmethod
587
478
  def to_qvar(
588
479
  cls,
@@ -591,24 +482,13 @@ class QStruct(QVar):
591
482
  expr_str: Optional[str],
592
483
  ) -> "QStruct":
593
484
  field_types = {
594
- field_name: (QVar.from_type_hint(field_type), field_type)
485
+ field_name: (_get_root_type(field_type), field_type)
595
486
  for field_name, field_type in type_hint.__annotations__.items()
596
487
  }
597
- illegal_fields = [
598
- (field_name, field_type)
599
- for field_name, (field_class, field_type) in field_types.items()
600
- if field_class is None
601
- ]
602
- if len(illegal_fields) > 0:
603
- raise ClassiqValueError(
604
- f"Field {illegal_fields[0][0]!r} of quantum struct "
605
- f"{type_hint.__name__} has a non-quantum type "
606
- f"{illegal_fields[0][1].__name__}."
607
- )
608
488
  base_handle = HandleBinding(name=origin) if isinstance(origin, str) else origin
609
489
  with _no_current_expandable():
610
490
  field_vars = {
611
- field_name: cast(type[QVar], field_class).to_qvar(
491
+ field_name: field_class.to_qvar(
612
492
  FieldHandleBinding(base_handle=base_handle, field=field_name),
613
493
  field_type,
614
494
  f"get_field({expr_str if expr_str is not None else str(origin)}, '{field_name}')",
@@ -622,16 +502,6 @@ class QStruct(QVar):
622
502
  _expr_str=expr_str,
623
503
  )
624
504
 
625
- def _add_qmod_qstruct(self, *, qmodule: ModelStateContainer) -> None:
626
- if self._struct_name in qmodule.qstruct_decls:
627
- return
628
-
629
- struct_decl = QStructDeclaration(
630
- name=self._struct_name,
631
- fields={name: qvar.get_qmod_type() for name, qvar in self._fields.items()},
632
- )
633
- qmodule.qstruct_decls[self._struct_name] = struct_decl
634
-
635
505
 
636
506
  def create_qvar_for_port_decl(port: AnonPortDeclaration, name: str) -> QVar:
637
507
  return _create_qvar_for_qtype(port.quantum_type, HandleBinding(name=name))
@@ -691,3 +561,171 @@ def get_qvar(qtype: QuantumType, origin: HandleBinding) -> "QVar":
691
561
  },
692
562
  )
693
563
  raise NotImplementedError
564
+
565
+
566
+ def get_port_from_type_hint(
567
+ py_type: Any,
568
+ ) -> tuple[QuantumType, PortDeclarationDirection, TypeQualifier]:
569
+ direction = PortDeclarationDirection.Inout # default
570
+ qualifier = TypeQualifier.Quantum # default
571
+
572
+ if isinstance(py_type, _AnnotatedAlias):
573
+ quantum_type = _to_quantum_type(py_type.__origin__)
574
+ for metadata in py_type.__metadata__:
575
+ if isinstance(metadata, PortDeclarationDirection):
576
+ direction = metadata
577
+ elif isinstance(metadata, TypeQualifier):
578
+ qualifier = metadata
579
+ else:
580
+ quantum_type = _to_quantum_type(py_type)
581
+
582
+ return quantum_type, direction, qualifier
583
+
584
+
585
+ def _to_quantum_type(py_type: Any) -> QuantumType:
586
+ root_type = _get_root_type(py_type)
587
+ if not issubclass(root_type, QVar):
588
+ raise ClassiqInternalError(f"Invalid quantum type {py_type}")
589
+ if issubclass(root_type, QBit):
590
+ return QuantumBit()
591
+ elif issubclass(root_type, QNum):
592
+ return _get_quantum_numeric(py_type)
593
+ elif issubclass(root_type, QArray):
594
+ return _get_quantum_bit_vector(py_type)
595
+ elif issubclass(root_type, QStruct):
596
+ return _get_quantum_struct(py_type)
597
+ else:
598
+ raise ClassiqInternalError(f"Invalid quantum type {py_type}")
599
+
600
+
601
+ def _get_quantum_numeric(type_hint: type[QNum]) -> QuantumNumeric:
602
+ size, is_signed, fraction_digits = _get_qnum_attributes(type_hint)
603
+ return QuantumNumeric(
604
+ size=(Expression(expr=_get_type_hint_expr(size)) if size is not None else None),
605
+ is_signed=(
606
+ Expression(expr=_get_type_hint_expr(is_signed))
607
+ if is_signed is not None
608
+ else None
609
+ ),
610
+ fraction_digits=(
611
+ Expression(expr=_get_type_hint_expr(fraction_digits))
612
+ if fraction_digits is not None
613
+ else None
614
+ ),
615
+ )
616
+
617
+
618
+ def _get_qnum_attributes(type_hint: type[QNum]) -> tuple[Any, Any, Any]:
619
+ type_args = version_portable_get_args(type_hint)
620
+ if len(type_args) == 0:
621
+ return None, None, None
622
+ if len(type_args) not in (1, 3):
623
+ raise ClassiqValueError(
624
+ "QNum receives three type arguments: QNum[size: int | CInt, "
625
+ "is_signed: bool | CBool, fraction_digits: int | CInt]"
626
+ )
627
+ if len(type_args) == 1:
628
+ return unwrap_forward_ref(type_args[0]), None, None
629
+ return (
630
+ unwrap_forward_ref(type_args[0]),
631
+ unwrap_forward_ref(type_args[1]),
632
+ unwrap_forward_ref(type_args[2]),
633
+ )
634
+
635
+
636
+ def _get_qarray_attributes(type_hint: type[QArray]) -> tuple[Any, Any]:
637
+ type_args = version_portable_get_args(type_hint)
638
+ if len(type_args) == 0:
639
+ return QBit, None
640
+ first_arg = unwrap_forward_ref(type_args[0])
641
+ if len(type_args) == 1:
642
+ if isinstance(first_arg, (str, int)):
643
+ return QBit, first_arg
644
+ return first_arg, None
645
+ if len(type_args) != 2:
646
+ raise ClassiqValueError(
647
+ "QArray receives two type arguments: QArray[element_type: QVar, "
648
+ "length: int | CInt]"
649
+ )
650
+ second_arg = unwrap_forward_ref(type_args[1])
651
+ return cast(tuple[type[QVar], Any], (first_arg, second_arg))
652
+
653
+
654
+ def _get_quantum_bit_vector(type_hint: type[QArray]) -> QuantumBitvector:
655
+ api_element_type, length = _get_qarray_attributes(type_hint)
656
+ element_type = _to_quantum_type(api_element_type)
657
+
658
+ length_expr: Expression | None = None
659
+ if length is not None:
660
+ length_expr = Expression(expr=_get_type_hint_expr(length))
661
+
662
+ return QuantumBitvector(element_type=element_type, length=length_expr)
663
+
664
+
665
+ def _get_quantum_struct(type_hint: type[QStruct]) -> TypeName:
666
+ _register_qstruct(type_hint, qmodule=QMODULE)
667
+ return TypeName(name=type_hint.__name__)
668
+
669
+
670
+ def _register_qstruct(
671
+ type_hint: type[QStruct], *, qmodule: ModelStateContainer
672
+ ) -> None:
673
+ struct_name = type_hint.__name__
674
+ if type_hint is QStruct or struct_name in qmodule.qstruct_decls:
675
+ return
676
+
677
+ _validate_fields(type_hint)
678
+ struct_decl = QStructDeclaration(
679
+ name=struct_name,
680
+ fields={
681
+ field_name: _to_quantum_type(field_type)
682
+ for field_name, field_type in type_hint.__annotations__.items()
683
+ },
684
+ )
685
+ qmodule.qstruct_decls[struct_name] = struct_decl
686
+
687
+
688
+ def _validate_fields(type_hint: type[QStruct]) -> None:
689
+ field_types = {
690
+ field_name: (_get_root_type(field_type), field_type)
691
+ for field_name, field_type in type_hint.__annotations__.items()
692
+ }
693
+ illegal_fields = [
694
+ (field_name, field_type)
695
+ for field_name, (field_class, field_type) in field_types.items()
696
+ if field_class is None
697
+ ]
698
+ if len(illegal_fields) > 0:
699
+ raise ClassiqValueError(
700
+ f"Field {illegal_fields[0][0]!r} of quantum struct "
701
+ f"{type_hint.__name__} has a non-quantum type "
702
+ f"{illegal_fields[0][1].__name__}."
703
+ )
704
+
705
+
706
+ @runtime_checkable
707
+ class _ModelConstant(Protocol):
708
+ # Applies to QConstant
709
+ def add_to_model(self) -> None: ...
710
+
711
+
712
+ def _get_type_hint_expr(type_hint: Any) -> str:
713
+ if isinstance(type_hint, ForwardRef): # expression in string literal
714
+ return str(type_hint.__forward_arg__)
715
+ if get_origin(type_hint) == Literal: # explicit numeric literal
716
+ return str(get_args(type_hint)[0])
717
+ if isinstance(
718
+ type_hint, _ModelConstant
719
+ ): # the Protocol is to prevent circular imports
720
+ type_hint.add_to_model()
721
+ return str(type_hint) # implicit numeric literal
722
+
723
+
724
+ def _get_root_type(py_type: Any) -> type[QVar]:
725
+ non_annotated_type = (
726
+ py_type.__origin__ if isinstance(py_type, _AnnotatedAlias) else py_type
727
+ )
728
+ root_type = get_origin(non_annotated_type) or non_annotated_type
729
+ if not issubclass(root_type, QVar):
730
+ raise ClassiqInternalError(f"Invalid quantum type {root_type}")
731
+ return root_type
@@ -25,11 +25,10 @@ from classiq.interface.model.quantum_function_declaration import (
25
25
 
26
26
  from classiq.qmod.classical_function import CFunc
27
27
  from classiq.qmod.cparam import CParamAbstract
28
- from classiq.qmod.declaration_inferrer import infer_func_decl
28
+ from classiq.qmod.declaration_inferrer import infer_func_decl, is_qvar
29
29
  from classiq.qmod.generative import set_frontend_interpreter
30
30
  from classiq.qmod.qmod_constant import QConstant
31
31
  from classiq.qmod.qmod_parameter import CArray
32
- from classiq.qmod.qmod_variable import QVar
33
32
  from classiq.qmod.quantum_callable import QCallable, QCallableList
34
33
  from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
35
34
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
@@ -57,7 +56,7 @@ class BaseQFunc(QExpandable):
57
56
  )
58
57
 
59
58
  def update_compilation_metadata(self, **kwargs: Any) -> None:
60
- if kwargs["should_synthesize_separately"] and self._has_inputs:
59
+ if kwargs.get("should_synthesize_separately") and self._has_inputs:
61
60
  raise ClassiqError("Can't synthesize separately a function with inputs")
62
61
  self.compilation_metadata = self._compilation_metadata.model_copy(update=kwargs)
63
62
 
@@ -315,7 +314,7 @@ def _validate_no_gen_params(annotations: dict[str, Any]) -> None:
315
314
  or get_origin(annotation) is CArray
316
315
  or (get_origin(annotation) or annotation) is QCallable
317
316
  or (get_origin(annotation) or annotation) is QCallableList
318
- or QVar.from_type_hint(annotation) is not None
317
+ or is_qvar(annotation)
319
318
  )
320
319
  }
321
320
  if _illegal_params:
@@ -3,18 +3,9 @@ from contextlib import contextmanager
3
3
  from typing import Any, Optional
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqError
6
- from classiq.interface.generator.expressions.expression import Expression
7
- from classiq.interface.generator.functions.classical_type import Integer
8
- from classiq.interface.generator.functions.port_declaration import (
9
- PortDeclarationDirection,
10
- )
11
- from classiq.interface.model.classical_parameter_declaration import (
12
- ClassicalParameterDeclaration,
13
- )
14
6
  from classiq.interface.model.model import Model
15
7
  from classiq.interface.model.model_visitor import ModelVisitor
16
8
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
17
- from classiq.interface.model.port_declaration import PortDeclaration
18
9
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
19
10
  from classiq.interface.model.quantum_function_declaration import (
20
11
  AnonQuantumOperandDeclaration,
@@ -24,36 +15,17 @@ from classiq.interface.model.quantum_function_declaration import (
24
15
  from classiq.interface.model.quantum_lambda_function import (
25
16
  QuantumLambdaFunction,
26
17
  )
27
- from classiq.interface.model.quantum_type import QuantumBitvector
28
18
 
29
19
  from classiq.qmod.builtins.functions import BUILTIN_FUNCTION_DECLARATIONS
30
20
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
31
21
  from classiq.qmod.semantics.error_manager import ErrorManager
32
22
  from classiq.qmod.semantics.lambdas import get_renamed_parameters
33
23
 
34
- ALLOCATE_DECL_FOR_COMPATIBILITY = QuantumFunctionDeclaration(
35
- name="allocate",
36
- positional_arg_declarations=[
37
- ClassicalParameterDeclaration(
38
- name="num_qubits",
39
- classical_type=Integer(),
40
- ),
41
- PortDeclaration(
42
- name="out",
43
- quantum_type=QuantumBitvector(length=Expression(expr="num_qubits")),
44
- direction=PortDeclarationDirection.Output,
45
- ),
46
- ],
47
- )
48
-
49
24
 
50
25
  def _annotate_function_call_decl(
51
26
  fc: QuantumFunctionCall,
52
27
  function_dict: Mapping[str, QuantumFunctionDeclaration],
53
28
  ) -> None:
54
- if fc.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
55
- fc.set_func_decl(ALLOCATE_DECL_FOR_COMPATIBILITY)
56
- return
57
29
  if fc._func_decl is None:
58
30
  func_decl = function_dict.get(fc.func_name)
59
31
  if func_decl is None:
@@ -9,8 +9,18 @@ class QStructAnnotator(ModelVisitor):
9
9
  self._visited: set[TypeName] = set()
10
10
 
11
11
  def visit_TypeName(self, type_name: TypeName) -> None:
12
+ self._annotate_quantum_struct(type_name)
13
+ self._annotate_classical_struct(type_name)
14
+
15
+ def _annotate_quantum_struct(self, type_name: TypeName) -> None:
16
+ if (
17
+ type_name.has_classical_struct_decl
18
+ or type_name.has_fields
19
+ or type_name in self._visited
20
+ ):
21
+ return
12
22
  decl = QMODULE.qstruct_decls.get(type_name.name)
13
- if decl is None or type_name.has_fields or (type_name in self._visited):
23
+ if decl is None:
14
24
  return
15
25
  self._visited.add(type_name)
16
26
  new_fields = {
@@ -21,3 +31,13 @@ class QStructAnnotator(ModelVisitor):
21
31
  # qstructs
22
32
  self.visit(new_fields)
23
33
  type_name.set_fields(new_fields)
34
+
35
+ def _annotate_classical_struct(self, type_name: TypeName) -> None:
36
+ if type_name.has_classical_struct_decl or type_name.has_fields:
37
+ return
38
+ decl = QMODULE.type_decls.get(type_name.name)
39
+ if decl is None:
40
+ return
41
+ type_name.set_classical_struct_decl(decl)
42
+ for field_type in decl.variables.values():
43
+ self.visit(field_type)
@@ -25,7 +25,7 @@ def _validate_main_classical_param_type(
25
25
  ) -> None:
26
26
  if isinstance(param, ClassicalList):
27
27
  raise ClassiqExpansionError(
28
- f"Classical array parameter {param_name!r} of function 'main' must must "
28
+ f"Classical array parameter {param_name!r} of function 'main' must "
29
29
  f"specify array length",
30
30
  )
31
31
  if isinstance(param, ClassicalArray):
@@ -0,0 +1,38 @@
1
+ from typing import Any
2
+
3
+ from typing_extensions import _AnnotatedAlias
4
+
5
+ from classiq.interface.exceptions import ClassiqValueError
6
+ from classiq.interface.generator.functions.port_declaration import (
7
+ PortDeclarationDirection,
8
+ )
9
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
10
+
11
+
12
+ def validate_annotation(type_hint: Any) -> None:
13
+ if not isinstance(type_hint, _AnnotatedAlias):
14
+ return
15
+ directions: list[PortDeclarationDirection] = [
16
+ direction
17
+ for direction in type_hint.__metadata__
18
+ if isinstance(direction, PortDeclarationDirection)
19
+ ]
20
+ qualifiers: list[TypeQualifier] = [
21
+ qualifier
22
+ for qualifier in type_hint.__metadata__
23
+ if isinstance(qualifier, TypeQualifier)
24
+ ]
25
+ if len(directions) <= 1 and len(qualifiers) <= 1:
26
+ return
27
+ error_message = ""
28
+ if len(directions) > 1:
29
+ error_message += (
30
+ f"Multiple directions are not allowed in a single type hint: "
31
+ f"[{', '.join(direction.name for direction in reversed(directions))}]\n"
32
+ )
33
+ if len(qualifiers) > 1:
34
+ error_message += (
35
+ f"Multiple qualifiers are not allowed in a single type hint: "
36
+ f"[{', '.join(qualifier.name for qualifier in reversed(qualifiers))}]\n"
37
+ )
38
+ raise ClassiqValueError(error_message)
classiq/qmod/utilities.py CHANGED
@@ -3,6 +3,7 @@ import inspect
3
3
  import itertools
4
4
  import keyword
5
5
  import sys
6
+ from collections import Counter
6
7
  from collections.abc import Iterable
7
8
  from enum import Enum as PythonEnum
8
9
  from types import FrameType
@@ -21,7 +22,7 @@ from typing import (
21
22
 
22
23
  from typing_extensions import ParamSpec
23
24
 
24
- from classiq.interface.generator.expressions.qmod_struct_instance import (
25
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
25
26
  QmodStructInstance,
26
27
  )
27
28
  from classiq.interface.source_reference import SourceReference
@@ -160,3 +161,39 @@ def suppress_return_value(func: Callable[Params, None]) -> Callable[Params, None
160
161
 
161
162
 
162
163
  Statements = Union[None, list[Union[None, "QVar"]], tuple[Union[None, "QVar"], ...]]
164
+
165
+
166
+ def _eval_qnum(val: int, size: int, is_signed: bool, fraction_digits: int) -> float:
167
+ if val < 0 or val >= 2**size:
168
+ raise ValueError
169
+ if size == 1 and is_signed and fraction_digits == 1:
170
+ return -0.5 if val == 1 else 0
171
+ if is_signed and val & (1 << (size - 1)) > 0:
172
+ val ^= 1 << (size - 1)
173
+ val -= 1 << (size - 1)
174
+ return val * 2**-fraction_digits
175
+
176
+
177
+ def qnum_values(size: int, is_signed: bool, fraction_digits: int) -> list[float]:
178
+ return [_eval_qnum(i, size, is_signed, fraction_digits) for i in range(2**size)]
179
+
180
+
181
+ def qnum_attributes(max_size: int) -> list[tuple[int, bool, int]]:
182
+ return [(1, True, 1)] + [
183
+ (size, is_signed, fraction_digits)
184
+ for size in range(1, max_size + 1)
185
+ for is_signed in (False, True)
186
+ for fraction_digits in range(size - int(is_signed) + 1)
187
+ ]
188
+
189
+
190
+ _VAR_NAME_COUNTER: Counter[str] = Counter()
191
+
192
+
193
+ def get_temp_var_name(var_name: str = "temp") -> str:
194
+ n = _VAR_NAME_COUNTER[var_name]
195
+ _VAR_NAME_COUNTER[var_name] += 1
196
+ return f"{var_name}_{n}"
197
+
198
+
199
+ RealFunction = Callable[Params, float]