classiq 0.69.0__py3-none-any.whl → 0.70.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/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_types.py +8 -2
- 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 +75 -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 +31 -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 +23 -29
- classiq/interface/generator/functions/type_name.py +26 -2
- classiq/interface/generator/generated_circuit_data.py +21 -3
- classiq/interface/generator/model/preferences/preferences.py +1 -0
- classiq/interface/generator/quantum_program.py +0 -1
- classiq/interface/model/native_function_definition.py +0 -10
- classiq/interface/model/quantum_type.py +15 -3
- classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
- classiq/model_expansions/evaluators/arg_type_match.py +4 -2
- classiq/model_expansions/evaluators/classical_expression.py +2 -2
- classiq/model_expansions/evaluators/control.py +1 -1
- classiq/model_expansions/evaluators/parameter_types.py +58 -16
- classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
- classiq/model_expansions/expression_evaluator.py +3 -1
- classiq/model_expansions/generative_functions.py +63 -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/quantum_function_call.py +0 -22
- classiq/model_expansions/scope.py +7 -6
- classiq/model_expansions/scope_initialization.py +17 -16
- 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/declaration_inferrer.py +3 -1
- classiq/qmod/qmod_parameter.py +8 -0
- classiq/qmod/qmod_variable.py +11 -14
- 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 +29 -0
- classiq/qmod/utilities.py +38 -1
- {classiq-0.69.0.dist-info → classiq-0.70.0.dist-info}/METADATA +10 -12
- {classiq-0.69.0.dist-info → classiq-0.70.0.dist-info}/RECORD +60 -49
- {classiq-0.69.0.dist-info → classiq-0.70.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
@@ -1,9 +1,5 @@
|
|
1
1
|
from typing import TYPE_CHECKING
|
2
2
|
|
3
|
-
from classiq.interface.exceptions import ClassiqValueError
|
4
|
-
from classiq.interface.generator.expressions.expression import Expression
|
5
|
-
from classiq.interface.model.allocate import Allocate
|
6
|
-
from classiq.interface.model.handle_binding import HandleBinding
|
7
3
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
8
4
|
|
9
5
|
from classiq.model_expansions.closure import FunctionClosure
|
@@ -17,21 +13,12 @@ if TYPE_CHECKING:
|
|
17
13
|
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
18
14
|
|
19
15
|
|
20
|
-
ALLOCATE_COMPATIBILITY_ERROR_MESSAGE = (
|
21
|
-
"'allocate' expects two argument: The number of qubits to allocate (integer) and "
|
22
|
-
"the variable to be allocated (quantum variable)"
|
23
|
-
)
|
24
|
-
|
25
|
-
|
26
16
|
class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
27
17
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
28
18
|
super().__init__(interpreter)
|
29
19
|
self._model = self._interpreter._model
|
30
20
|
|
31
21
|
def emit(self, call: QuantumFunctionCall, /) -> bool:
|
32
|
-
if call.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
|
33
|
-
self._allocate_compatibility(call)
|
34
|
-
return True
|
35
22
|
function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
|
36
23
|
args = call.positional_args
|
37
24
|
with ErrorManager().call(function.name):
|
@@ -40,15 +27,6 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
40
27
|
)
|
41
28
|
return True
|
42
29
|
|
43
|
-
def _allocate_compatibility(self, call: QuantumFunctionCall) -> None:
|
44
|
-
if len(call.positional_args) != 2:
|
45
|
-
raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
|
46
|
-
size, target = call.positional_args
|
47
|
-
if not isinstance(size, Expression) or not isinstance(target, HandleBinding):
|
48
|
-
raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
|
49
|
-
allocate = Allocate(size=size, target=target, source_ref=call.source_ref)
|
50
|
-
self._interpreter.emit_statement(allocate)
|
51
|
-
|
52
30
|
|
53
31
|
class DeclarativeQuantumFunctionCallEmitter(
|
54
32
|
QuantumFunctionCallEmitter, DeclarativeCallEmitter
|
@@ -19,7 +19,7 @@ from classiq.interface.generator.expressions.expression import Expression
|
|
19
19
|
from classiq.interface.generator.expressions.expression_constants import (
|
20
20
|
CPARAM_EXECUTION_SUFFIX_PATTERN,
|
21
21
|
)
|
22
|
-
from classiq.interface.generator.expressions.qmod_struct_instance import (
|
22
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
23
23
|
QmodStructInstance,
|
24
24
|
)
|
25
25
|
from classiq.interface.generator.functions.type_name import TypeName
|
@@ -72,9 +72,9 @@ class QuantumSymbol:
|
|
72
72
|
array_length = self.quantum_type.length_value
|
73
73
|
if start < 0 or end > array_length:
|
74
74
|
raise ClassiqExpansionError(
|
75
|
-
f"Slice [{start}:{end}] is out of bounds
|
76
|
-
f"{self.quantum_type.type_name} {str(self.handle)!r} of
|
77
|
-
f"{array_length}"
|
75
|
+
f"Slice [{start}:{end}] is out of bounds for "
|
76
|
+
f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
|
77
|
+
f"length {array_length})"
|
78
78
|
)
|
79
79
|
return QuantumSymbol(
|
80
80
|
handle=SlicedHandleBinding(
|
@@ -96,8 +96,9 @@ class QuantumSymbol:
|
|
96
96
|
array_length = self.quantum_type.length_value
|
97
97
|
if index < 0 or index >= array_length:
|
98
98
|
raise ClassiqExpansionError(
|
99
|
-
f"
|
100
|
-
f"{str(self.handle)!r} of
|
99
|
+
f"Index {index} is out of bounds for "
|
100
|
+
f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
|
101
|
+
f"length {array_length})"
|
101
102
|
)
|
102
103
|
return QuantumSymbol(
|
103
104
|
handle=SubscriptHandleBinding(
|
@@ -1,16 +1,17 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
|
-
from typing import Any
|
3
2
|
|
4
|
-
from classiq.interface.exceptions import ClassiqError
|
3
|
+
from classiq.interface.exceptions import ClassiqError, ClassiqInternalExpansionError
|
5
4
|
from classiq.interface.generator.constant import Constant
|
5
|
+
from classiq.interface.generator.functions.classical_type import (
|
6
|
+
ClassicalArray,
|
7
|
+
ClassicalList,
|
8
|
+
)
|
6
9
|
from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
|
7
10
|
from classiq.interface.model.handle_binding import HandleBinding
|
8
11
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
|
9
12
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
10
13
|
from classiq.interface.model.port_declaration import PortDeclaration
|
11
|
-
from classiq.interface.model.quantum_function_declaration import
|
12
|
-
PositionalArg,
|
13
|
-
)
|
14
|
+
from classiq.interface.model.quantum_function_declaration import PositionalArg
|
14
15
|
|
15
16
|
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
16
17
|
from classiq.model_expansions.evaluators.classical_expression import (
|
@@ -125,14 +126,6 @@ def init_builtin_types() -> None:
|
|
125
126
|
QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
|
126
127
|
|
127
128
|
|
128
|
-
def _add_exec_param_parts_to_scope(param_val: Any, scope: Scope) -> None:
|
129
|
-
if not isinstance(param_val, list):
|
130
|
-
scope[str(param_val)] = Evaluated(value=param_val)
|
131
|
-
return
|
132
|
-
for param_part in param_val:
|
133
|
-
_add_exec_param_parts_to_scope(param_part, scope)
|
134
|
-
|
135
|
-
|
136
129
|
def init_exec_params(model: Model, scope: Scope) -> dict[str, ConcreteClassicalType]:
|
137
130
|
if model.execution_parameters is not None:
|
138
131
|
exec_params = model.execution_parameters
|
@@ -144,8 +137,16 @@ def init_exec_params(model: Model, scope: Scope) -> dict[str, ConcreteClassicalT
|
|
144
137
|
).param_decls
|
145
138
|
}
|
146
139
|
for param_name, param_type in exec_params.items():
|
147
|
-
param_val = param_type.
|
140
|
+
param_val = param_type.get_classical_proxy(
|
141
|
+
handle=HandleBinding(name=param_name)
|
142
|
+
)
|
148
143
|
scope[param_name] = Evaluated(value=param_val)
|
149
|
-
if isinstance(param_val, list):
|
150
|
-
_add_exec_param_parts_to_scope(param_val, scope)
|
151
144
|
return exec_params
|
145
|
+
|
146
|
+
|
147
|
+
def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
|
148
|
+
if isinstance(classical_type, ClassicalList):
|
149
|
+
raise ClassiqInternalExpansionError("Unexpected classical list")
|
150
|
+
if isinstance(classical_type, ClassicalArray):
|
151
|
+
return classical_type.size, *_get_shape(classical_type.element_type)
|
152
|
+
return ()
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import ast
|
2
|
+
import re
|
2
3
|
from collections.abc import Mapping, Sequence
|
3
4
|
from dataclasses import dataclass
|
4
5
|
from typing import TypeVar, cast
|
@@ -16,6 +17,12 @@ from classiq.model_expansions.visitors.variable_references import VarRefCollecto
|
|
16
17
|
AST_NODE = TypeVar("AST_NODE", bound=NodeType)
|
17
18
|
|
18
19
|
|
20
|
+
def _replace_full_word(pattern: str, substitution: str, target: str) -> str:
|
21
|
+
return re.sub(
|
22
|
+
rf"(^|\b|\W)({re.escape(pattern)})($|\b|\W)", rf"\1{substitution}\3", target
|
23
|
+
)
|
24
|
+
|
25
|
+
|
19
26
|
@dataclass(frozen=True)
|
20
27
|
class HandleRenaming:
|
21
28
|
source_handle: HandleBinding
|
@@ -74,7 +81,7 @@ class ModelRenamer:
|
|
74
81
|
symbol_mapping: SymbolRenaming,
|
75
82
|
expression: Expression,
|
76
83
|
) -> Expression:
|
77
|
-
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
84
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
|
78
85
|
vrc.visit(ast.parse(expression.expr))
|
79
86
|
|
80
87
|
handle_names = {
|
@@ -87,10 +94,12 @@ class ModelRenamer:
|
|
87
94
|
new_handle = handle.collapse()
|
88
95
|
for handle_to_replace, replacement in handle_names.items():
|
89
96
|
new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
|
90
|
-
new_expr_str =
|
97
|
+
new_expr_str = _replace_full_word(
|
98
|
+
str(handle), str(new_handle), new_expr_str
|
99
|
+
)
|
91
100
|
if handle.qmod_expr != str(handle):
|
92
|
-
new_expr_str =
|
93
|
-
handle.qmod_expr, new_handle.qmod_expr
|
101
|
+
new_expr_str = _replace_full_word(
|
102
|
+
str(handle.qmod_expr), str(new_handle.qmod_expr), new_expr_str
|
94
103
|
)
|
95
104
|
|
96
105
|
new_expr = Expression(expr=new_expr_str)
|
@@ -24,10 +24,14 @@ from classiq.interface.model.handle_binding import (
|
|
24
24
|
|
25
25
|
class VarRefCollector(ast.NodeVisitor):
|
26
26
|
def __init__(
|
27
|
-
self,
|
27
|
+
self,
|
28
|
+
ignore_duplicated_handles: bool = False,
|
29
|
+
unevaluated: bool = False,
|
30
|
+
ignore_sympy_symbols: bool = False,
|
28
31
|
) -> None:
|
29
32
|
self._var_handles: dict[HandleBinding, bool] = {}
|
30
33
|
self._ignore_duplicated_handles = ignore_duplicated_handles
|
34
|
+
self._ignore_sympy_symbols = ignore_sympy_symbols
|
31
35
|
self._unevaluated = unevaluated
|
32
36
|
self._is_nested = False
|
33
37
|
|
@@ -117,9 +121,9 @@ class VarRefCollector(ast.NodeVisitor):
|
|
117
121
|
return handle
|
118
122
|
|
119
123
|
def visit_Name(self, node: ast.Name) -> Optional[HandleBinding]:
|
120
|
-
if node.id in set(
|
121
|
-
|
122
|
-
):
|
124
|
+
if not self._ignore_sympy_symbols and node.id in set(
|
125
|
+
SYMPY_SUPPORTED_EXPRESSIONS
|
126
|
+
) | set(DEFAULT_SUPPORTED_FUNC_NAMES):
|
123
127
|
return None
|
124
128
|
handle = HandleBinding(name=node.id)
|
125
129
|
if not self._is_nested:
|
@@ -9,6 +9,7 @@ from .grover import *
|
|
9
9
|
from .hea import *
|
10
10
|
from .linear_pauli_rotation import *
|
11
11
|
from .linear_pauli_rotation import _single_pauli
|
12
|
+
from .lookup_table import span_lookup_table
|
12
13
|
from .modular_exponentiation import *
|
13
14
|
from .modular_exponentiation import _check_msb
|
14
15
|
from .qaoa_penalty import *
|
@@ -131,6 +132,7 @@ __all__ = [
|
|
131
132
|
"qsvt_lcu_step",
|
132
133
|
"qsvt_step",
|
133
134
|
"reflect_about_zero",
|
135
|
+
"span_lookup_table",
|
134
136
|
"suzuki_trotter",
|
135
137
|
"swap_test",
|
136
138
|
"switch",
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from itertools import product
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import ClassiqValueError
|
4
|
+
|
5
|
+
from classiq.qmod.builtins.operations import assign, bind, within_apply
|
6
|
+
from classiq.qmod.qmod_variable import QNum
|
7
|
+
from classiq.qmod.symbolic import subscript
|
8
|
+
from classiq.qmod.utilities import RealFunction, get_temp_var_name, qnum_values
|
9
|
+
|
10
|
+
|
11
|
+
def _get_qnum_values(num: QNum) -> list[float]:
|
12
|
+
size = num.size
|
13
|
+
is_signed = num.is_signed
|
14
|
+
fraction_digits = num.fraction_digits
|
15
|
+
if (
|
16
|
+
not isinstance(size, int)
|
17
|
+
or not isinstance(is_signed, bool)
|
18
|
+
or not isinstance(fraction_digits, int)
|
19
|
+
):
|
20
|
+
raise ClassiqValueError(
|
21
|
+
"Must call 'span_lookup_table' inside a generative qfunc"
|
22
|
+
)
|
23
|
+
|
24
|
+
return qnum_values(size, is_signed, fraction_digits)
|
25
|
+
|
26
|
+
|
27
|
+
def span_lookup_table(func: RealFunction, *targets: QNum) -> QNum:
|
28
|
+
"""
|
29
|
+
Applies a classical function to quantum numbers.
|
30
|
+
|
31
|
+
Works by reducing the function into a lookup table over all the possible values
|
32
|
+
of the quantum numbers.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
func: A Python function
|
36
|
+
*targets: One or more initialized quantum numbers
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
The quantum result of applying func to targets
|
40
|
+
|
41
|
+
Notes:
|
42
|
+
Must be called inside a generative function (`@qfunc(generative=True)`)
|
43
|
+
"""
|
44
|
+
if len(targets) == 0:
|
45
|
+
raise ClassiqValueError("No targets specified")
|
46
|
+
|
47
|
+
target_vals = [_get_qnum_values(target) for target in targets]
|
48
|
+
lookup_table = [func(*vals[::-1]) for vals in product(*target_vals[::-1])]
|
49
|
+
|
50
|
+
index_size = sum(target.size for target in targets)
|
51
|
+
index: QNum = QNum(get_temp_var_name(), size=index_size)
|
52
|
+
result: QNum = QNum(get_temp_var_name("result"))
|
53
|
+
|
54
|
+
within_apply(
|
55
|
+
lambda: bind(list(targets), index),
|
56
|
+
lambda: assign(subscript(lookup_table, index), result),
|
57
|
+
)
|
58
|
+
return result
|
@@ -38,6 +38,7 @@ from classiq.qmod.model_state_container import ModelStateContainer
|
|
38
38
|
from classiq.qmod.python_classical_type import PythonClassicalType
|
39
39
|
from classiq.qmod.qmod_variable import QVar
|
40
40
|
from classiq.qmod.quantum_callable import QCallableList
|
41
|
+
from classiq.qmod.semantics.validation.type_hints import validate_annotation
|
41
42
|
from classiq.qmod.utilities import unmangle_keyword, version_portable_get_args
|
42
43
|
|
43
44
|
if sys.version_info[0:2] >= (3, 9):
|
@@ -123,7 +124,7 @@ def _extract_operand_decl(
|
|
123
124
|
|
124
125
|
|
125
126
|
def _extract_operand_param(py_type: Any) -> tuple[Optional[str], Any]:
|
126
|
-
if
|
127
|
+
if get_origin(py_type) is not Annotated:
|
127
128
|
return None, py_type
|
128
129
|
args = get_args(py_type)
|
129
130
|
if len(args) == 2:
|
@@ -163,6 +164,7 @@ def _extract_positional_args(
|
|
163
164
|
) -> Sequence[AnonPositionalArg]:
|
164
165
|
result: list[AnonPositionalArg] = []
|
165
166
|
for name, py_type in args:
|
167
|
+
validate_annotation(py_type)
|
166
168
|
if name == "return":
|
167
169
|
continue
|
168
170
|
name = unmangle_keyword(name)
|
classiq/qmod/qmod_parameter.py
CHANGED
@@ -22,6 +22,11 @@ from classiq.qmod.cparam import ( # noqa: F401
|
|
22
22
|
CParamScalar,
|
23
23
|
CReal,
|
24
24
|
)
|
25
|
+
from classiq.qmod.generative import (
|
26
|
+
generative_mode_context,
|
27
|
+
interpret_expression,
|
28
|
+
is_generative_mode,
|
29
|
+
)
|
25
30
|
from classiq.qmod.model_state_container import ModelStateContainer
|
26
31
|
from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
|
27
32
|
|
@@ -66,6 +71,9 @@ class CParamList(CParam):
|
|
66
71
|
|
67
72
|
@property
|
68
73
|
def len(self) -> CParamScalar:
|
74
|
+
if is_generative_mode():
|
75
|
+
with generative_mode_context(False):
|
76
|
+
return interpret_expression(str(self.len))
|
69
77
|
return CParamScalar(f"get_field({self}, 'len')")
|
70
78
|
|
71
79
|
|
classiq/qmod/qmod_variable.py
CHANGED
@@ -23,7 +23,7 @@ from typing_extensions import ParamSpec, Self, _AnnotatedAlias
|
|
23
23
|
from classiq.interface.exceptions import ClassiqValueError
|
24
24
|
from classiq.interface.generator.expressions.expression import Expression
|
25
25
|
from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
|
26
|
-
from classiq.interface.generator.expressions.qmod_qarray_proxy import (
|
26
|
+
from classiq.interface.generator.expressions.proxies.quantum.qmod_qarray_proxy import (
|
27
27
|
ILLEGAL_SLICE_MSG,
|
28
28
|
ILLEGAL_SLICING_STEP_MSG,
|
29
29
|
)
|
@@ -72,12 +72,6 @@ from classiq.qmod.utilities import (
|
|
72
72
|
)
|
73
73
|
|
74
74
|
|
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
75
|
def get_type_hint_expr(type_hint: Any) -> str:
|
82
76
|
if isinstance(type_hint, ForwardRef): # expression in string literal
|
83
77
|
return str(type_hint.__forward_arg__)
|
@@ -143,9 +137,12 @@ class QVar(Symbolic):
|
|
143
137
|
|
144
138
|
@staticmethod
|
145
139
|
def from_type_hint(type_hint: Any) -> Optional[type["QVar"]]:
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
149
146
|
if issubclass(type_, QVar):
|
150
147
|
if issubclass(type_, QStruct):
|
151
148
|
with _no_current_expandable():
|
@@ -170,10 +167,10 @@ class QVar(Symbolic):
|
|
170
167
|
|
171
168
|
@classmethod
|
172
169
|
def port_direction(cls, type_hint: Any) -> PortDeclarationDirection:
|
173
|
-
if
|
174
|
-
|
175
|
-
|
176
|
-
|
170
|
+
if isinstance(type_hint, _AnnotatedAlias):
|
171
|
+
for metadata in type_hint.__metadata__:
|
172
|
+
if isinstance(metadata, PortDeclarationDirection):
|
173
|
+
return metadata
|
177
174
|
return PortDeclarationDirection.Inout
|
178
175
|
|
179
176
|
def __str__(self) -> str:
|
@@ -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,29 @@
|
|
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
|
+
|
10
|
+
annotation_map: dict[PortDeclarationDirection, str] = {
|
11
|
+
PortDeclarationDirection.Input: PortDeclarationDirection.Input.name,
|
12
|
+
PortDeclarationDirection.Output: PortDeclarationDirection.Output.name,
|
13
|
+
}
|
14
|
+
|
15
|
+
|
16
|
+
def validate_annotation(type_hint: Any) -> None:
|
17
|
+
if not isinstance(type_hint, _AnnotatedAlias):
|
18
|
+
return
|
19
|
+
directions: list[PortDeclarationDirection] = [
|
20
|
+
direction
|
21
|
+
for direction in type_hint.__metadata__
|
22
|
+
if isinstance(direction, PortDeclarationDirection)
|
23
|
+
]
|
24
|
+
if len(directions) <= 1:
|
25
|
+
return
|
26
|
+
raise ClassiqValueError(
|
27
|
+
f"Multiple directions are not allowed in a single type hint: "
|
28
|
+
f"[{', '.join(annotation_map[direction] for direction in reversed(directions))}]\n"
|
29
|
+
)
|
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]
|
@@ -1,26 +1,25 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: classiq
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.70.0
|
4
4
|
Summary: Classiq's Python SDK for quantum computing
|
5
|
-
Home-page: https://classiq.io
|
6
5
|
License: Proprietary
|
7
6
|
Keywords: quantum computing,quantum circuits,quantum algorithms,QAD,QDL
|
8
7
|
Author: Classiq Technologies
|
9
8
|
Author-email: support@classiq.io
|
10
|
-
Requires-Python: >=3.9
|
9
|
+
Requires-Python: >=3.9, <3.13
|
11
10
|
Classifier: Development Status :: 4 - Beta
|
11
|
+
Classifier: License :: Other/Proprietary License
|
12
12
|
Classifier: Intended Audience :: Developers
|
13
13
|
Classifier: Intended Audience :: Education
|
14
14
|
Classifier: Intended Audience :: Science/Research
|
15
15
|
Classifier: License :: Other/Proprietary License
|
16
16
|
Classifier: Natural Language :: English
|
17
17
|
Classifier: Operating System :: OS Independent
|
18
|
-
Classifier: Programming Language :: Python :: 3
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
19
19
|
Classifier: Programming Language :: Python :: 3.9
|
20
20
|
Classifier: Programming Language :: Python :: 3.10
|
21
21
|
Classifier: Programming Language :: Python :: 3.11
|
22
22
|
Classifier: Programming Language :: Python :: 3.12
|
23
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
24
23
|
Classifier: Topic :: Scientific/Engineering
|
25
24
|
Classifier: Topic :: Software Development
|
26
25
|
Classifier: Topic :: Software Development :: Code Generators
|
@@ -33,12 +32,12 @@ Requires-Dist: ConfigArgParse (>=1.5.3,<2.0.0)
|
|
33
32
|
Requires-Dist: Pyomo (>=6.5,<6.6)
|
34
33
|
Requires-Dist: black (>=24.0,<25.0)
|
35
34
|
Requires-Dist: httpx (>=0.23.0,<1)
|
36
|
-
Requires-Dist: ipywidgets
|
37
|
-
Requires-Dist: jupyterlab
|
35
|
+
Requires-Dist: ipywidgets ; extra == "analyzer-sdk"
|
36
|
+
Requires-Dist: jupyterlab ; extra == "analyzer-sdk"
|
38
37
|
Requires-Dist: keyring (>=23.5.0,<24.0.0)
|
39
38
|
Requires-Dist: matplotlib (>=3.4.3,<4.0.0)
|
40
39
|
Requires-Dist: networkx (>=2.5.1,<3.0.0)
|
41
|
-
Requires-Dist: notebook
|
40
|
+
Requires-Dist: notebook ; extra == "analyzer-sdk"
|
42
41
|
Requires-Dist: numexpr (>=2.7.3,<3.0.0)
|
43
42
|
Requires-Dist: numpy (>=1.20.1,<2.0.0) ; python_version < "3.12"
|
44
43
|
Requires-Dist: numpy (>=1.26.0,<2.0.0) ; python_version >= "3.12"
|
@@ -51,10 +50,9 @@ Requires-Dist: scipy (>=1.10.0,<2.0.0) ; python_version < "3.12"
|
|
51
50
|
Requires-Dist: scipy (>=1.11.0,<2.0.0) ; python_version >= "3.12"
|
52
51
|
Requires-Dist: sympy (>=1.13.0,<2.0.0)
|
53
52
|
Requires-Dist: tabulate (>=0.8.9,<1)
|
54
|
-
Requires-Dist: torch
|
55
|
-
Requires-Dist: torchvision
|
53
|
+
Requires-Dist: torch ; extra == "qml"
|
54
|
+
Requires-Dist: torchvision ; extra == "qml"
|
56
55
|
Requires-Dist: tqdm (>=4.67.1,<5.0.0)
|
57
|
-
Project-URL: Documentation, https://docs.classiq.io/
|
58
56
|
Description-Content-Type: text/markdown
|
59
57
|
|
60
58
|
<p align="center">
|