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.
- classiq/analyzer/analyzer.py +0 -18
- classiq/analyzer/url_utils.py +9 -4
- classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +6 -0
- classiq/interface/chemistry/operator.py +1 -21
- classiq/interface/debug_info/debug_info.py +4 -0
- classiq/interface/executor/quantum_instruction_set.py +1 -0
- classiq/interface/generator/arith/arithmetic.py +21 -6
- classiq/interface/generator/circuit_code/circuit_code.py +4 -0
- classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
- classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
- classiq/interface/generator/expressions/expression_constants.py +0 -3
- classiq/interface/generator/expressions/expression_types.py +12 -4
- classiq/interface/generator/expressions/proxies/__init__.py +0 -0
- classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +79 -0
- classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
- classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
- classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +35 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
- classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
- classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
- classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
- classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
- classiq/interface/generator/functions/classical_type.py +24 -30
- classiq/interface/generator/functions/type_name.py +42 -2
- classiq/interface/generator/functions/type_qualifier.py +7 -0
- classiq/interface/generator/generated_circuit_data.py +22 -4
- classiq/interface/generator/model/preferences/preferences.py +1 -0
- classiq/interface/generator/quantum_function_call.py +8 -1
- classiq/interface/generator/quantum_program.py +0 -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/native_function_definition.py +0 -10
- 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 +37 -3
- 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/atomic_expression_functions_defs.py +9 -3
- classiq/model_expansions/capturing/captured_vars.py +156 -34
- classiq/model_expansions/evaluators/arg_type_match.py +4 -2
- classiq/model_expansions/evaluators/classical_expression.py +2 -2
- classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
- classiq/model_expansions/evaluators/control.py +1 -1
- classiq/model_expansions/evaluators/parameter_types.py +72 -16
- classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
- classiq/model_expansions/expression_evaluator.py +3 -12
- classiq/model_expansions/function_builder.py +2 -8
- classiq/model_expansions/generative_functions.py +39 -3
- classiq/model_expansions/interpreters/base_interpreter.py +3 -4
- classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
- 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/quantum_operations/quantum_function_call.py +0 -22
- classiq/model_expansions/scope.py +7 -14
- classiq/model_expansions/scope_initialization.py +32 -39
- classiq/model_expansions/transformers/model_renamer.py +13 -4
- classiq/model_expansions/visitors/variable_references.py +8 -4
- classiq/open_library/functions/__init__.py +2 -0
- classiq/open_library/functions/lookup_table.py +58 -0
- classiq/qmod/__init__.py +3 -1
- classiq/qmod/declaration_inferrer.py +55 -25
- 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_parameter.py +8 -0
- classiq/qmod/qmod_variable.py +189 -151
- classiq/qmod/quantum_function.py +3 -4
- classiq/qmod/semantics/annotation/call_annotation.py +0 -28
- classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
- classiq/qmod/semantics/validation/main_validation.py +1 -1
- classiq/qmod/semantics/validation/type_hints.py +38 -0
- classiq/qmod/utilities.py +38 -1
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/METADATA +10 -12
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/RECORD +97 -82
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/WHEEL +1 -1
- /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
- /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
classiq/qmod/qmod_variable.py
CHANGED
@@ -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, *
|
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, *
|
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=
|
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: (
|
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:
|
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
|
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:
|
@@ -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
|
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
|
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]
|