classiq 0.70.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.
- classiq/interface/_version.py +1 -1
- classiq/interface/debug_info/debug_info.py +4 -0
- classiq/interface/generator/expressions/expression_constants.py +0 -3
- classiq/interface/generator/expressions/expression_types.py +4 -2
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +4 -0
- classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +5 -1
- classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
- classiq/interface/generator/functions/classical_type.py +1 -1
- classiq/interface/generator/functions/type_name.py +16 -0
- classiq/interface/generator/functions/type_qualifier.py +7 -0
- classiq/interface/generator/generated_circuit_data.py +1 -1
- classiq/interface/generator/quantum_function_call.py +8 -1
- classiq/interface/generator/synthesis_execution_parameter.py +1 -0
- classiq/interface/generator/types/compilation_metadata.py +1 -0
- classiq/interface/ide/visual_model.py +1 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/allocate.py +7 -0
- classiq/interface/model/block.py +12 -0
- classiq/interface/model/classical_if.py +4 -0
- classiq/interface/model/inplace_binary_operation.py +4 -0
- classiq/interface/model/model.py +3 -1
- classiq/interface/model/phase_operation.py +4 -0
- classiq/interface/model/port_declaration.py +3 -0
- classiq/interface/model/power.py +4 -0
- classiq/interface/model/quantum_expressions/quantum_expression.py +4 -0
- classiq/interface/model/quantum_function_call.py +4 -0
- classiq/interface/model/quantum_function_declaration.py +1 -1
- classiq/interface/model/quantum_statement.py +5 -0
- classiq/interface/model/quantum_type.py +22 -0
- classiq/interface/model/repeat.py +4 -0
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/model/variable_declaration_statement.py +5 -0
- classiq/model_expansions/capturing/captured_vars.py +156 -34
- classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
- classiq/model_expansions/evaluators/parameter_types.py +14 -0
- classiq/model_expansions/expression_evaluator.py +0 -11
- classiq/model_expansions/function_builder.py +2 -8
- classiq/model_expansions/generative_functions.py +7 -30
- classiq/model_expansions/interpreters/base_interpreter.py +3 -4
- classiq/model_expansions/quantum_operations/call_emitter.py +46 -6
- classiq/model_expansions/quantum_operations/emitter.py +41 -0
- classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
- classiq/model_expansions/scope.py +0 -8
- classiq/model_expansions/scope_initialization.py +20 -28
- classiq/qmod/__init__.py +3 -1
- classiq/qmod/declaration_inferrer.py +52 -24
- classiq/qmod/native/pretty_printer.py +25 -3
- classiq/qmod/pretty_print/pretty_printer.py +31 -14
- classiq/qmod/python_classical_type.py +12 -1
- classiq/qmod/qfunc.py +33 -8
- classiq/qmod/qmod_variable.py +188 -147
- classiq/qmod/quantum_function.py +3 -4
- classiq/qmod/semantics/validation/type_hints.py +19 -10
- {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/METADATA +1 -1
- {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/RECORD +56 -52
- {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/WHEEL +0 -0
classiq/qmod/qfunc.py
CHANGED
@@ -35,6 +35,7 @@ def qfunc(
|
|
35
35
|
*,
|
36
36
|
external: Literal[True],
|
37
37
|
synthesize_separately: Literal[False] = False,
|
38
|
+
atomic_qualifiers: Optional[list[str]] = None,
|
38
39
|
) -> Callable[[Callable], ExternalQFunc]: ...
|
39
40
|
|
40
41
|
|
@@ -43,11 +44,22 @@ def qfunc(
|
|
43
44
|
*,
|
44
45
|
generative: Literal[True],
|
45
46
|
synthesize_separately: bool = False,
|
47
|
+
atomic_qualifiers: Optional[list[str]] = None,
|
46
48
|
) -> Callable[[Callable], GenerativeQFunc]: ...
|
47
49
|
|
48
50
|
|
49
51
|
@overload
|
50
|
-
def qfunc(
|
52
|
+
def qfunc(
|
53
|
+
*, synthesize_separately: bool, atomic_qualifiers: Optional[list[str]] = None
|
54
|
+
) -> Callable[[Callable], QFunc]: ...
|
55
|
+
|
56
|
+
|
57
|
+
@overload
|
58
|
+
def qfunc(
|
59
|
+
*,
|
60
|
+
synthesize_separately: bool = False,
|
61
|
+
atomic_qualifiers: Optional[list[str]] = None,
|
62
|
+
) -> Callable[[Callable], QFunc]: ...
|
51
63
|
|
52
64
|
|
53
65
|
def qfunc(
|
@@ -56,24 +68,37 @@ def qfunc(
|
|
56
68
|
external: bool = False,
|
57
69
|
generative: bool = False,
|
58
70
|
synthesize_separately: bool = False,
|
71
|
+
atomic_qualifiers: Optional[list[str]] = None,
|
59
72
|
) -> Union[Callable[[Callable], QCallable], QCallable]:
|
60
73
|
def wrapper(func: Callable) -> QCallable:
|
61
74
|
qfunc: BaseQFunc
|
75
|
+
|
76
|
+
if external:
|
77
|
+
_validate_directives(synthesize_separately, atomic_qualifiers)
|
78
|
+
return ExternalQFunc(func)
|
79
|
+
|
62
80
|
if generative or _GENERATIVE_SWITCH:
|
63
81
|
qfunc = GenerativeQFunc(func)
|
64
|
-
elif external:
|
65
|
-
if synthesize_separately:
|
66
|
-
raise ClassiqInternalError(
|
67
|
-
"External functions can't be marked as synthesized separately"
|
68
|
-
)
|
69
|
-
return ExternalQFunc(func)
|
70
82
|
else:
|
71
83
|
qfunc = QFunc(func)
|
72
84
|
if synthesize_separately:
|
73
85
|
qfunc.update_compilation_metadata(should_synthesize_separately=True)
|
86
|
+
if atomic_qualifiers is not None and len(atomic_qualifiers) > 0:
|
87
|
+
qfunc.update_compilation_metadata(atomic_qualifiers=atomic_qualifiers)
|
74
88
|
return qfunc
|
75
89
|
|
76
90
|
if func is not None:
|
77
91
|
return wrapper(func)
|
78
|
-
|
79
92
|
return wrapper
|
93
|
+
|
94
|
+
|
95
|
+
def _validate_directives(
|
96
|
+
synthesize_separately: bool, atomic_qualifiers: Optional[list[str]] = None
|
97
|
+
) -> None:
|
98
|
+
error_msg = ""
|
99
|
+
if synthesize_separately:
|
100
|
+
error_msg += "External functions can't be marked as synthesized separately. \n"
|
101
|
+
if atomic_qualifiers is not None and len(atomic_qualifiers) > 0:
|
102
|
+
error_msg += "External functions can't have atomic qualifiers."
|
103
|
+
if error_msg:
|
104
|
+
raise ClassiqInternalError(error_msg)
|
classiq/qmod/qmod_variable.py
CHANGED
@@ -10,17 +10,19 @@ 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
28
|
from classiq.interface.generator.expressions.proxies.quantum.qmod_qarray_proxy import (
|
@@ -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,15 +75,6 @@ from classiq.qmod.utilities import (
|
|
72
75
|
)
|
73
76
|
|
74
77
|
|
75
|
-
def get_type_hint_expr(type_hint: Any) -> str:
|
76
|
-
if isinstance(type_hint, ForwardRef): # expression in string literal
|
77
|
-
return str(type_hint.__forward_arg__)
|
78
|
-
if get_origin(type_hint) == Literal: # explicit numeric literal
|
79
|
-
return str(get_args(type_hint)[0])
|
80
|
-
else:
|
81
|
-
return str(type_hint) # implicit numeric literal
|
82
|
-
|
83
|
-
|
84
78
|
@contextmanager
|
85
79
|
def _no_current_expandable() -> Iterator[None]:
|
86
80
|
current_expandable = QCallable.CURRENT_EXPANDABLE
|
@@ -135,26 +129,6 @@ class QVar(Symbolic):
|
|
135
129
|
def get_qmod_type(self) -> QuantumType:
|
136
130
|
raise NotImplementedError()
|
137
131
|
|
138
|
-
@staticmethod
|
139
|
-
def from_type_hint(type_hint: Any) -> Optional[type["QVar"]]:
|
140
|
-
non_annotated_type = (
|
141
|
-
type_hint.__origin__
|
142
|
-
if isinstance(type_hint, _AnnotatedAlias)
|
143
|
-
else type_hint
|
144
|
-
)
|
145
|
-
type_ = get_origin(non_annotated_type) or non_annotated_type
|
146
|
-
if issubclass(type_, QVar):
|
147
|
-
if issubclass(type_, QStruct):
|
148
|
-
with _no_current_expandable():
|
149
|
-
type_("DUMMY")._add_qmod_qstruct(qmodule=QMODULE)
|
150
|
-
return type_
|
151
|
-
return None
|
152
|
-
|
153
|
-
@classmethod
|
154
|
-
@abc.abstractmethod
|
155
|
-
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
156
|
-
raise NotImplementedError()
|
157
|
-
|
158
132
|
@classmethod
|
159
133
|
@abc.abstractmethod
|
160
134
|
def to_qvar(
|
@@ -165,14 +139,6 @@ class QVar(Symbolic):
|
|
165
139
|
) -> Self:
|
166
140
|
raise NotImplementedError()
|
167
141
|
|
168
|
-
@classmethod
|
169
|
-
def port_direction(cls, type_hint: Any) -> PortDeclarationDirection:
|
170
|
-
if isinstance(type_hint, _AnnotatedAlias):
|
171
|
-
for metadata in type_hint.__metadata__:
|
172
|
-
if isinstance(metadata, PortDeclarationDirection):
|
173
|
-
return metadata
|
174
|
-
return PortDeclarationDirection.Inout
|
175
|
-
|
176
142
|
def __str__(self) -> str:
|
177
143
|
return self._expr_str
|
178
144
|
|
@@ -191,6 +157,12 @@ class QVar(Symbolic):
|
|
191
157
|
_Q = TypeVar("_Q", bound=QVar)
|
192
158
|
Output = Annotated[_Q, PortDeclarationDirection.Output]
|
193
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
|
194
166
|
|
195
167
|
|
196
168
|
class QScalar(QVar, SymbolicExpr):
|
@@ -282,10 +254,6 @@ class QScalar(QVar, SymbolicExpr):
|
|
282
254
|
|
283
255
|
|
284
256
|
class QBit(QScalar):
|
285
|
-
@classmethod
|
286
|
-
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
287
|
-
return QuantumBit()
|
288
|
-
|
289
257
|
@classmethod
|
290
258
|
def to_qvar(
|
291
259
|
cls,
|
@@ -342,43 +310,6 @@ class QNum(Generic[_P], QScalar):
|
|
342
310
|
)
|
343
311
|
super().__init__(name, _expr_str=_expr_str, depth=3)
|
344
312
|
|
345
|
-
@classmethod
|
346
|
-
def _get_attributes(cls, type_hint: Any) -> tuple[Any, Any, Any]:
|
347
|
-
type_args = version_portable_get_args(type_hint)
|
348
|
-
if len(type_args) == 0:
|
349
|
-
return None, None, None
|
350
|
-
if len(type_args) not in (1, 3):
|
351
|
-
raise ClassiqValueError(
|
352
|
-
"QNum receives three type arguments: QNum[size: int | CInt, "
|
353
|
-
"is_signed: bool | CBool, fraction_digits: int | CInt]"
|
354
|
-
)
|
355
|
-
if len(type_args) == 1:
|
356
|
-
return unwrap_forward_ref(type_args[0]), None, None
|
357
|
-
return (
|
358
|
-
unwrap_forward_ref(type_args[0]),
|
359
|
-
unwrap_forward_ref(type_args[1]),
|
360
|
-
unwrap_forward_ref(type_args[2]),
|
361
|
-
)
|
362
|
-
|
363
|
-
@classmethod
|
364
|
-
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
365
|
-
size, is_signed, fraction_digits = cls._get_attributes(type_hint)
|
366
|
-
return QuantumNumeric(
|
367
|
-
size=(
|
368
|
-
Expression(expr=get_type_hint_expr(size)) if size is not None else None
|
369
|
-
),
|
370
|
-
is_signed=(
|
371
|
-
Expression(expr=get_type_hint_expr(is_signed))
|
372
|
-
if is_signed is not None
|
373
|
-
else None
|
374
|
-
),
|
375
|
-
fraction_digits=(
|
376
|
-
Expression(expr=get_type_hint_expr(fraction_digits))
|
377
|
-
if fraction_digits is not None
|
378
|
-
else None
|
379
|
-
),
|
380
|
-
)
|
381
|
-
|
382
313
|
@classmethod
|
383
314
|
def to_qvar(
|
384
315
|
cls,
|
@@ -386,7 +317,7 @@ class QNum(Generic[_P], QScalar):
|
|
386
317
|
type_hint: Any,
|
387
318
|
expr_str: Optional[str],
|
388
319
|
) -> "QNum":
|
389
|
-
return QNum(origin, *
|
320
|
+
return QNum(origin, *_get_qnum_attributes(type_hint), _expr_str=expr_str)
|
390
321
|
|
391
322
|
def get_qmod_type(self) -> QuantumType:
|
392
323
|
return QuantumNumeric(
|
@@ -494,36 +425,6 @@ class QArray(ArrayBase[_P], QVar, NonSymbolicExpr):
|
|
494
425
|
return interpret_expression(str(self.len))
|
495
426
|
return CParamScalar(f"get_field({self}, 'len')")
|
496
427
|
|
497
|
-
@classmethod
|
498
|
-
def _get_attributes(cls, type_hint: Any) -> tuple[type[QVar], Any]:
|
499
|
-
type_args = version_portable_get_args(type_hint)
|
500
|
-
if len(type_args) == 0:
|
501
|
-
return QBit, None
|
502
|
-
first_arg = unwrap_forward_ref(type_args[0])
|
503
|
-
if len(type_args) == 1:
|
504
|
-
if isinstance(first_arg, (str, int)):
|
505
|
-
return QBit, first_arg
|
506
|
-
return first_arg, None
|
507
|
-
if len(type_args) != 2:
|
508
|
-
raise ClassiqValueError(
|
509
|
-
"QArray receives two type arguments: QArray[element_type: QVar, "
|
510
|
-
"length: int | CInt]"
|
511
|
-
)
|
512
|
-
second_arg = unwrap_forward_ref(type_args[1])
|
513
|
-
return cast(tuple[type[QVar], Any], (first_arg, second_arg))
|
514
|
-
|
515
|
-
@classmethod
|
516
|
-
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
517
|
-
api_element_type, length = cls._get_attributes(type_hint)
|
518
|
-
api_element_class = get_origin(api_element_type) or api_element_type
|
519
|
-
element_type = api_element_class.to_qmod_quantum_type(api_element_type)
|
520
|
-
|
521
|
-
length_expr: Optional[Expression] = None
|
522
|
-
if length is not None:
|
523
|
-
length_expr = Expression(expr=get_type_hint_expr(length))
|
524
|
-
|
525
|
-
return QuantumBitvector(element_type=element_type, length=length_expr)
|
526
|
-
|
527
428
|
@classmethod
|
528
429
|
def to_qvar(
|
529
430
|
cls,
|
@@ -531,16 +432,15 @@ class QArray(ArrayBase[_P], QVar, NonSymbolicExpr):
|
|
531
432
|
type_hint: Any,
|
532
433
|
expr_str: Optional[str],
|
533
434
|
) -> "QArray":
|
534
|
-
return QArray(origin, *
|
435
|
+
return QArray(origin, *_get_qarray_attributes(type_hint), _expr_str=expr_str)
|
535
436
|
|
536
437
|
def get_qmod_type(self) -> QuantumBitvector:
|
537
|
-
if isinstance(self._element_type, QuantumType):
|
538
|
-
element_type = self._element_type
|
539
|
-
else:
|
540
|
-
element_class = get_origin(self._element_type) or self._element_type
|
541
|
-
element_type = element_class.to_qmod_quantum_type(self._element_type)
|
542
438
|
return QuantumBitvector(
|
543
|
-
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
|
+
),
|
544
444
|
length=self._length,
|
545
445
|
)
|
546
446
|
|
@@ -558,6 +458,7 @@ class QStruct(QVar):
|
|
558
458
|
_fields: Optional[Mapping[str, QVar]] = None,
|
559
459
|
_expr_str: Optional[str] = None,
|
560
460
|
) -> None:
|
461
|
+
_register_qstruct(type(self), qmodule=QMODULE)
|
561
462
|
name = _infer_variable_name(origin, self.CONSTRUCTOR_DEPTH)
|
562
463
|
if _struct_name is None or _fields is None:
|
563
464
|
with _no_current_expandable():
|
@@ -569,17 +470,10 @@ class QStruct(QVar):
|
|
569
470
|
for field_name, var in _fields.items():
|
570
471
|
setattr(self, field_name, var)
|
571
472
|
super().__init__(name, expr_str=_expr_str)
|
572
|
-
self._add_qmod_qstruct(qmodule=QMODULE)
|
573
473
|
|
574
474
|
def get_qmod_type(self) -> QuantumType:
|
575
475
|
return TypeName(name=self._struct_name)
|
576
476
|
|
577
|
-
@classmethod
|
578
|
-
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
579
|
-
with _no_current_expandable():
|
580
|
-
type_hint("DUMMY")
|
581
|
-
return TypeName(name=type_hint.__name__)
|
582
|
-
|
583
477
|
@classmethod
|
584
478
|
def to_qvar(
|
585
479
|
cls,
|
@@ -588,24 +482,13 @@ class QStruct(QVar):
|
|
588
482
|
expr_str: Optional[str],
|
589
483
|
) -> "QStruct":
|
590
484
|
field_types = {
|
591
|
-
field_name: (
|
485
|
+
field_name: (_get_root_type(field_type), field_type)
|
592
486
|
for field_name, field_type in type_hint.__annotations__.items()
|
593
487
|
}
|
594
|
-
illegal_fields = [
|
595
|
-
(field_name, field_type)
|
596
|
-
for field_name, (field_class, field_type) in field_types.items()
|
597
|
-
if field_class is None
|
598
|
-
]
|
599
|
-
if len(illegal_fields) > 0:
|
600
|
-
raise ClassiqValueError(
|
601
|
-
f"Field {illegal_fields[0][0]!r} of quantum struct "
|
602
|
-
f"{type_hint.__name__} has a non-quantum type "
|
603
|
-
f"{illegal_fields[0][1].__name__}."
|
604
|
-
)
|
605
488
|
base_handle = HandleBinding(name=origin) if isinstance(origin, str) else origin
|
606
489
|
with _no_current_expandable():
|
607
490
|
field_vars = {
|
608
|
-
field_name:
|
491
|
+
field_name: field_class.to_qvar(
|
609
492
|
FieldHandleBinding(base_handle=base_handle, field=field_name),
|
610
493
|
field_type,
|
611
494
|
f"get_field({expr_str if expr_str is not None else str(origin)}, '{field_name}')",
|
@@ -619,16 +502,6 @@ class QStruct(QVar):
|
|
619
502
|
_expr_str=expr_str,
|
620
503
|
)
|
621
504
|
|
622
|
-
def _add_qmod_qstruct(self, *, qmodule: ModelStateContainer) -> None:
|
623
|
-
if self._struct_name in qmodule.qstruct_decls:
|
624
|
-
return
|
625
|
-
|
626
|
-
struct_decl = QStructDeclaration(
|
627
|
-
name=self._struct_name,
|
628
|
-
fields={name: qvar.get_qmod_type() for name, qvar in self._fields.items()},
|
629
|
-
)
|
630
|
-
qmodule.qstruct_decls[self._struct_name] = struct_decl
|
631
|
-
|
632
505
|
|
633
506
|
def create_qvar_for_port_decl(port: AnonPortDeclaration, name: str) -> QVar:
|
634
507
|
return _create_qvar_for_qtype(port.quantum_type, HandleBinding(name=name))
|
@@ -688,3 +561,171 @@ def get_qvar(qtype: QuantumType, origin: HandleBinding) -> "QVar":
|
|
688
561
|
},
|
689
562
|
)
|
690
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
|
classiq/qmod/quantum_function.py
CHANGED
@@ -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
|
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
|
317
|
+
or is_qvar(annotation)
|
319
318
|
)
|
320
319
|
}
|
321
320
|
if _illegal_params:
|
@@ -6,11 +6,7 @@ from classiq.interface.exceptions import ClassiqValueError
|
|
6
6
|
from classiq.interface.generator.functions.port_declaration import (
|
7
7
|
PortDeclarationDirection,
|
8
8
|
)
|
9
|
-
|
10
|
-
annotation_map: dict[PortDeclarationDirection, str] = {
|
11
|
-
PortDeclarationDirection.Input: PortDeclarationDirection.Input.name,
|
12
|
-
PortDeclarationDirection.Output: PortDeclarationDirection.Output.name,
|
13
|
-
}
|
9
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
14
10
|
|
15
11
|
|
16
12
|
def validate_annotation(type_hint: Any) -> None:
|
@@ -21,9 +17,22 @@ def validate_annotation(type_hint: Any) -> None:
|
|
21
17
|
for direction in type_hint.__metadata__
|
22
18
|
if isinstance(direction, PortDeclarationDirection)
|
23
19
|
]
|
24
|
-
|
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:
|
25
26
|
return
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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)
|