classiq 0.76.0__py3-none-any.whl → 0.77.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/applications/chemistry/chemistry_model_constructor.py +7 -6
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +9 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
- classiq/interface/_version.py +1 -1
- classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
- classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +14 -5
- classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +5 -3
- classiq/interface/model/handle_binding.py +8 -0
- classiq/interface/model/model.py +3 -6
- classiq/interface/model/quantum_function_call.py +31 -1
- classiq/interface/model/quantum_statement.py +14 -1
- classiq/interface/source_reference.py +7 -2
- classiq/model_expansions/capturing/captured_vars.py +16 -6
- classiq/model_expansions/evaluators/arg_type_match.py +2 -2
- classiq/model_expansions/evaluators/argument_types.py +3 -3
- classiq/model_expansions/evaluators/classical_expression.py +9 -9
- classiq/model_expansions/evaluators/parameter_types.py +19 -11
- classiq/model_expansions/expression_evaluator.py +20 -11
- classiq/model_expansions/function_builder.py +45 -0
- classiq/model_expansions/generative_functions.py +1 -1
- classiq/model_expansions/interpreters/base_interpreter.py +15 -1
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +4 -3
- classiq/model_expansions/interpreters/generative_interpreter.py +4 -4
- classiq/model_expansions/quantum_operations/allocate.py +2 -2
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +3 -1
- classiq/model_expansions/quantum_operations/call_emitter.py +21 -11
- classiq/model_expansions/quantum_operations/emitter.py +1 -6
- classiq/model_expansions/scope.py +53 -3
- classiq/model_expansions/transformers/model_renamer.py +2 -2
- classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
- classiq/open_library/functions/lookup_table.py +1 -1
- classiq/open_library/functions/state_preparation.py +1 -1
- classiq/qmod/create_model_function.py +21 -3
- classiq/qmod/global_declarative_switch.py +19 -0
- classiq/qmod/native/pretty_printer.py +4 -0
- classiq/qmod/pretty_print/pretty_printer.py +4 -0
- classiq/qmod/qfunc.py +31 -23
- classiq/qmod/quantum_expandable.py +29 -1
- classiq/qmod/quantum_function.py +26 -19
- classiq/qmod/write_qmod.py +36 -10
- classiq/synthesis.py +7 -6
- {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
- {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/RECORD +44 -43
- {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -11,12 +11,15 @@ from classiq.interface.generator.compiler_keywords import (
|
|
11
11
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
12
12
|
WITHIN_APPLY_NAME,
|
13
13
|
)
|
14
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
14
15
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME
|
15
16
|
from classiq.interface.model.native_function_definition import (
|
16
17
|
NativeFunctionDefinition,
|
17
18
|
)
|
19
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
18
20
|
from classiq.interface.model.quantum_function_declaration import (
|
19
21
|
PositionalArg,
|
22
|
+
QuantumOperandDeclaration,
|
20
23
|
)
|
21
24
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
22
25
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -212,6 +215,7 @@ class OperationBuilder:
|
|
212
215
|
self, function_context: FunctionContext, params: Sequence[PositionalArg]
|
213
216
|
) -> NativeFunctionDefinition:
|
214
217
|
name = self._get_expanded_function_name(function_context)
|
218
|
+
self._override_type_qualifier(function_context, params)
|
215
219
|
|
216
220
|
return NativeFunctionDefinition(
|
217
221
|
name=name,
|
@@ -244,3 +248,44 @@ class OperationBuilder:
|
|
244
248
|
raise ClassiqInternalExpansionError("Could not allocate function name")
|
245
249
|
|
246
250
|
return name
|
251
|
+
|
252
|
+
def _override_type_qualifier(
|
253
|
+
self, function_context: FunctionContext, params: Sequence[PositionalArg]
|
254
|
+
) -> None:
|
255
|
+
"""
|
256
|
+
The type qualifier can be changed according to the operand passed to the
|
257
|
+
function. For example,
|
258
|
+
apply_to_all(X, q) --> q will be QFree after expansion
|
259
|
+
apply_to_all(H, q) --> q will be Quantum after expansion
|
260
|
+
This also holds for the intermediate lambda created during the expansion.
|
261
|
+
|
262
|
+
We don't override the type qualifier if it's explicitly specified (QFree or
|
263
|
+
Const), neither in the function declaration nor in the operand declaration.
|
264
|
+
"""
|
265
|
+
|
266
|
+
if function_context.is_lambda:
|
267
|
+
self._update_type_qualifiers(params)
|
268
|
+
return
|
269
|
+
|
270
|
+
orig_name = function_context.name
|
271
|
+
if orig_name == MAIN_FUNCTION_NAME or orig_name not in self.current_scope:
|
272
|
+
return
|
273
|
+
|
274
|
+
orig_func = self.current_scope[orig_name].value
|
275
|
+
if not any(
|
276
|
+
isinstance(param_decl, QuantumOperandDeclaration)
|
277
|
+
for param_decl in orig_func.positional_arg_declarations
|
278
|
+
):
|
279
|
+
return
|
280
|
+
|
281
|
+
self._update_type_qualifiers(params)
|
282
|
+
|
283
|
+
@staticmethod
|
284
|
+
def _update_type_qualifiers(params: Sequence[PositionalArg]) -> None:
|
285
|
+
# only override the qualifier if it's unspecified (not QFree or Const)
|
286
|
+
for param in params:
|
287
|
+
if (
|
288
|
+
isinstance(param, PortDeclaration)
|
289
|
+
and param.type_qualifier is TypeQualifier.Quantum
|
290
|
+
):
|
291
|
+
param.type_qualifier = TypeQualifier.Inferred
|
@@ -134,7 +134,7 @@ class _InterpreterExpandable(QFunc):
|
|
134
134
|
dummy_function = NativeFunctionDefinition(
|
135
135
|
name=current_operation.name,
|
136
136
|
positional_arg_declarations=current_operation.positional_arg_declarations,
|
137
|
-
body=
|
137
|
+
body=[stmt],
|
138
138
|
)
|
139
139
|
declarative_functions = {
|
140
140
|
name: func
|
@@ -24,6 +24,7 @@ from classiq.interface.generator.types.compilation_metadata import CompilationMe
|
|
24
24
|
from classiq.interface.model.handle_binding import (
|
25
25
|
FieldHandleBinding,
|
26
26
|
HandleBinding,
|
27
|
+
HandlesList,
|
27
28
|
SlicedHandleBinding,
|
28
29
|
SubscriptHandleBinding,
|
29
30
|
)
|
@@ -51,7 +52,12 @@ from classiq.model_expansions.function_builder import (
|
|
51
52
|
OperationBuilder,
|
52
53
|
OperationContext,
|
53
54
|
)
|
54
|
-
from classiq.model_expansions.scope import
|
55
|
+
from classiq.model_expansions.scope import (
|
56
|
+
Evaluated,
|
57
|
+
QuantumSymbol,
|
58
|
+
QuantumSymbolList,
|
59
|
+
Scope,
|
60
|
+
)
|
55
61
|
from classiq.model_expansions.scope_initialization import (
|
56
62
|
add_entry_point_params_to_scope,
|
57
63
|
init_builtin_types,
|
@@ -233,6 +239,14 @@ class BaseInterpreter:
|
|
233
239
|
)
|
234
240
|
return Evaluated(value=fields[field_name])
|
235
241
|
|
242
|
+
@evaluate.register
|
243
|
+
def evaluate_handles_list(self, handles_list: HandlesList) -> Evaluated:
|
244
|
+
return Evaluated(
|
245
|
+
value=QuantumSymbolList.from_symbols(
|
246
|
+
[self.evaluate(handle).value for handle in handles_list.handles]
|
247
|
+
)
|
248
|
+
)
|
249
|
+
|
236
250
|
@abstractmethod
|
237
251
|
def emit(self, statement: QuantumStatement) -> None:
|
238
252
|
pass
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import inspect
|
2
2
|
import os
|
3
|
+
from typing import Optional
|
3
4
|
|
4
5
|
from pydantic import ValidationError
|
5
6
|
|
@@ -33,9 +34,9 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
|
33
34
|
def infer_symbolic_parameters(
|
34
35
|
self,
|
35
36
|
functions: list[NativeFunctionDefinition],
|
36
|
-
additional_signatures:
|
37
|
-
list[NamedParamsQuantumFunctionDeclaration]
|
38
|
-
|
37
|
+
additional_signatures: Optional[
|
38
|
+
list[NamedParamsQuantumFunctionDeclaration]
|
39
|
+
] = None,
|
39
40
|
) -> None:
|
40
41
|
SymbolicParamInference(functions, additional_signatures).infer()
|
41
42
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from functools import singledispatchmethod
|
2
|
-
from typing import Any
|
2
|
+
from typing import Any, Optional
|
3
3
|
|
4
4
|
import numpy as np
|
5
5
|
from numpy.random import permutation
|
@@ -104,9 +104,9 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
104
104
|
def infer_symbolic_parameters(
|
105
105
|
self,
|
106
106
|
functions: list[NativeFunctionDefinition],
|
107
|
-
additional_signatures:
|
108
|
-
list[NamedParamsQuantumFunctionDeclaration]
|
109
|
-
|
107
|
+
additional_signatures: Optional[
|
108
|
+
list[NamedParamsQuantumFunctionDeclaration]
|
109
|
+
] = None,
|
110
110
|
) -> None:
|
111
111
|
pass
|
112
112
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import TYPE_CHECKING
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
2
2
|
|
3
3
|
import sympy
|
4
4
|
|
@@ -49,7 +49,7 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
49
49
|
self.emit_statement(allocate)
|
50
50
|
return True
|
51
51
|
|
52
|
-
def _get_var_size(self, target: QuantumSymbol, size: Expression
|
52
|
+
def _get_var_size(self, target: QuantumSymbol, size: Optional[Expression]) -> str:
|
53
53
|
if size is None:
|
54
54
|
if not target.quantum_type.is_evaluated:
|
55
55
|
raise ClassiqValueError(
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
1
3
|
from classiq.interface.exceptions import ClassiqExpansionError
|
2
4
|
from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
|
3
5
|
from classiq.interface.generator.expressions.expression import Expression
|
@@ -69,7 +71,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
69
71
|
self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
|
70
72
|
return True
|
71
73
|
|
72
|
-
def _infer_result_type(self, op: ArithmeticOperation) -> QuantumNumeric
|
74
|
+
def _infer_result_type(self, op: ArithmeticOperation) -> Optional[QuantumNumeric]:
|
73
75
|
expr = self._evaluate_expression(op.expression)
|
74
76
|
if len(self._get_classical_vars_in_expression(expr)):
|
75
77
|
return None
|
@@ -4,6 +4,7 @@ from typing import (
|
|
4
4
|
TYPE_CHECKING,
|
5
5
|
Any,
|
6
6
|
Generic,
|
7
|
+
Optional,
|
7
8
|
cast,
|
8
9
|
)
|
9
10
|
from uuid import UUID
|
@@ -62,7 +63,13 @@ from classiq.model_expansions.quantum_operations.emitter import (
|
|
62
63
|
Emitter,
|
63
64
|
QuantumStatementT,
|
64
65
|
)
|
65
|
-
from classiq.model_expansions.scope import
|
66
|
+
from classiq.model_expansions.scope import (
|
67
|
+
Evaluated,
|
68
|
+
QuantumSymbol,
|
69
|
+
QuantumSymbolList,
|
70
|
+
QuantumVariable,
|
71
|
+
Scope,
|
72
|
+
)
|
66
73
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
67
74
|
from classiq.model_expansions.utils.text_utils import are, readable_list, s
|
68
75
|
from classiq.qmod.semantics.validation.signature_validation import (
|
@@ -74,11 +81,14 @@ if TYPE_CHECKING:
|
|
74
81
|
|
75
82
|
|
76
83
|
def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
|
77
|
-
handles =
|
78
|
-
|
84
|
+
handles = chain.from_iterable(
|
85
|
+
(
|
86
|
+
[arg.value.handle]
|
87
|
+
if isinstance(arg.value, QuantumSymbol)
|
88
|
+
else arg.value.handles if isinstance(arg.value, QuantumSymbolList) else []
|
89
|
+
)
|
79
90
|
for arg in evaluated_args
|
80
|
-
|
81
|
-
]
|
91
|
+
)
|
82
92
|
for handle, other_handle in combinations(handles, 2):
|
83
93
|
if handle.overlaps(other_handle):
|
84
94
|
if handle == other_handle:
|
@@ -130,7 +140,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
130
140
|
self,
|
131
141
|
function: FunctionClosure,
|
132
142
|
args: list[ArgValue],
|
133
|
-
propagated_debug_info: FunctionDebugInfo
|
143
|
+
propagated_debug_info: Optional[FunctionDebugInfo],
|
134
144
|
) -> QuantumFunctionCall:
|
135
145
|
call = self._create_quantum_function_call(
|
136
146
|
function, args, propagated_debug_info=propagated_debug_info
|
@@ -140,8 +150,8 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
140
150
|
|
141
151
|
@staticmethod
|
142
152
|
def _get_back_ref(
|
143
|
-
propagated_debug_info: FunctionDebugInfo
|
144
|
-
) -> UUID
|
153
|
+
propagated_debug_info: Optional[FunctionDebugInfo],
|
154
|
+
) -> Optional[UUID]:
|
145
155
|
if propagated_debug_info is None:
|
146
156
|
return None
|
147
157
|
if propagated_debug_info.node is None:
|
@@ -152,7 +162,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
152
162
|
self,
|
153
163
|
function: FunctionClosure,
|
154
164
|
args: list[ArgValue],
|
155
|
-
propagated_debug_info: FunctionDebugInfo
|
165
|
+
propagated_debug_info: Optional[FunctionDebugInfo],
|
156
166
|
) -> QuantumFunctionCall:
|
157
167
|
function = function.clone()
|
158
168
|
function = function.set_depth(self._builder.current_function.depth + 1)
|
@@ -281,7 +291,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
281
291
|
) -> None:
|
282
292
|
for parameter, argument in zip(parameters, arguments):
|
283
293
|
param_handle = HandleBinding(name=parameter.name)
|
284
|
-
if isinstance(argument.value,
|
294
|
+
if isinstance(argument.value, QuantumVariable):
|
285
295
|
assert isinstance(parameter, PortDeclaration)
|
286
296
|
closure.scope[parameter.name] = Evaluated(
|
287
297
|
QuantumSymbol(
|
@@ -314,7 +324,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
314
324
|
positional_args = [
|
315
325
|
arg.emit()
|
316
326
|
for arg in evaluated_args
|
317
|
-
if isinstance(arg.value,
|
327
|
+
if isinstance(arg.value, QuantumVariable) or _is_symbolic(arg.value)
|
318
328
|
]
|
319
329
|
|
320
330
|
return positional_args
|
@@ -181,12 +181,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
181
181
|
self._capture_classical_var(var_name, var_type)
|
182
182
|
|
183
183
|
def _update_captured_vars(self, op: QuantumOperation) -> None:
|
184
|
-
|
185
|
-
[(handle, PortDeclarationDirection.Input) for handle in op.inputs]
|
186
|
-
+ [(handle, PortDeclarationDirection.Output) for handle in op.outputs]
|
187
|
-
+ [(handle, PortDeclarationDirection.Inout) for handle in op.inouts]
|
188
|
-
)
|
189
|
-
for handle, direction in handles:
|
184
|
+
for handle, direction in op.handles_with_directions:
|
190
185
|
self._capture_handle(handle, direction)
|
191
186
|
|
192
187
|
def _capture_handle(
|
@@ -22,7 +22,9 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
|
|
22
22
|
from classiq.interface.generator.functions.type_name import TypeName
|
23
23
|
from classiq.interface.model.handle_binding import (
|
24
24
|
FieldHandleBinding,
|
25
|
+
GeneralHandle,
|
25
26
|
HandleBinding,
|
27
|
+
HandlesList,
|
26
28
|
SlicedHandleBinding,
|
27
29
|
SubscriptHandleBinding,
|
28
30
|
)
|
@@ -39,10 +41,17 @@ T = TypeVar("T")
|
|
39
41
|
|
40
42
|
|
41
43
|
@dataclass(frozen=True)
|
42
|
-
class
|
43
|
-
handle: HandleBinding
|
44
|
+
class QuantumVariable:
|
44
45
|
quantum_type: QuantumType
|
45
46
|
|
47
|
+
def emit(self) -> GeneralHandle:
|
48
|
+
raise NotImplementedError
|
49
|
+
|
50
|
+
|
51
|
+
@dataclass(frozen=True)
|
52
|
+
class QuantumSymbol(QuantumVariable):
|
53
|
+
handle: HandleBinding
|
54
|
+
|
46
55
|
@property
|
47
56
|
def is_subscript(self) -> bool:
|
48
57
|
return isinstance(self.handle, (SubscriptHandleBinding, SlicedHandleBinding))
|
@@ -140,6 +149,47 @@ class QuantumSymbol:
|
|
140
149
|
for field_name, field_type in quantum_type.fields.items()
|
141
150
|
}
|
142
151
|
|
152
|
+
def __str__(self) -> str:
|
153
|
+
return str(self.handle)
|
154
|
+
|
155
|
+
|
156
|
+
@dataclass(frozen=True)
|
157
|
+
class QuantumSymbolList(QuantumVariable):
|
158
|
+
handles: list[HandleBinding]
|
159
|
+
|
160
|
+
@staticmethod
|
161
|
+
def from_symbols(
|
162
|
+
symbols: list[Union[QuantumSymbol, "QuantumSymbolList"]],
|
163
|
+
) -> "QuantumSymbolList":
|
164
|
+
handles = list(
|
165
|
+
itertools.chain.from_iterable(
|
166
|
+
(
|
167
|
+
symbol.handles
|
168
|
+
if isinstance(symbol, QuantumSymbolList)
|
169
|
+
else [symbol.handle]
|
170
|
+
)
|
171
|
+
for symbol in symbols
|
172
|
+
)
|
173
|
+
)
|
174
|
+
if len(handles) == 0:
|
175
|
+
raise ClassiqExpansionError("Empty concatenation expression")
|
176
|
+
length: Optional[Expression]
|
177
|
+
if any(not symbol.quantum_type.has_size_in_bits for symbol in symbols):
|
178
|
+
length = None
|
179
|
+
else:
|
180
|
+
length = Expression(
|
181
|
+
expr=str(sum(symbol.quantum_type.size_in_bits for symbol in symbols))
|
182
|
+
)
|
183
|
+
return QuantumSymbolList(
|
184
|
+
handles=handles, quantum_type=QuantumBitvector(length=length)
|
185
|
+
)
|
186
|
+
|
187
|
+
def emit(self) -> HandlesList:
|
188
|
+
return HandlesList(handles=self.handles)
|
189
|
+
|
190
|
+
def __str__(self) -> str:
|
191
|
+
return str(self.handles)
|
192
|
+
|
143
193
|
|
144
194
|
@singledispatch
|
145
195
|
def evaluated_to_str(value: Any) -> str:
|
@@ -183,7 +233,7 @@ class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
|
|
183
233
|
def emit(self) -> ArgValue:
|
184
234
|
from classiq.model_expansions.closure import FunctionClosure
|
185
235
|
|
186
|
-
if isinstance(self.value, (
|
236
|
+
if isinstance(self.value, (QuantumVariable, FunctionClosure)):
|
187
237
|
return self.value.emit()
|
188
238
|
if isinstance(self.value, list) and all(
|
189
239
|
isinstance(item, FunctionClosure) for item in self.value
|
@@ -33,7 +33,7 @@ def _handle_contains_handle(handle: HandleBinding, other_handle: HandleBinding)
|
|
33
33
|
return 0
|
34
34
|
|
35
35
|
|
36
|
-
class
|
36
|
+
class ExprNormalizer(ast.NodeTransformer):
|
37
37
|
def visit_Call(self, node: ast.Call) -> ast.AST:
|
38
38
|
if not isinstance(node.func, ast.Name):
|
39
39
|
return self.generic_visit(node)
|
@@ -74,7 +74,7 @@ SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
|
|
74
74
|
def _rewrite_expression(
|
75
75
|
symbol_mapping: SymbolRenaming, expression: Expression
|
76
76
|
) -> Expression:
|
77
|
-
normalized_expr =
|
77
|
+
normalized_expr = ExprNormalizer().visit(ast.parse(expression.expr))
|
78
78
|
vrc = VarRefCollector(
|
79
79
|
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
80
80
|
)
|
@@ -62,9 +62,9 @@ class SymbolicParamInference(ModelVisitor):
|
|
62
62
|
def __init__(
|
63
63
|
self,
|
64
64
|
functions: list[NativeFunctionDefinition],
|
65
|
-
additional_signatures:
|
66
|
-
list[NamedParamsQuantumFunctionDeclaration]
|
67
|
-
|
65
|
+
additional_signatures: Optional[
|
66
|
+
list[NamedParamsQuantumFunctionDeclaration]
|
67
|
+
] = None,
|
68
68
|
) -> None:
|
69
69
|
self._functions = nameables_to_dict(functions)
|
70
70
|
self._additional_signatures = (
|
@@ -39,7 +39,7 @@ def span_lookup_table(func: RealFunction, *targets: QNum) -> QNum:
|
|
39
39
|
The quantum result of applying func to targets
|
40
40
|
|
41
41
|
Notes:
|
42
|
-
Must be called inside a generative function (`@qfunc
|
42
|
+
Must be called inside a generative function (`@qfunc`)
|
43
43
|
"""
|
44
44
|
if len(targets) == 0:
|
45
45
|
raise ClassiqValueError("No targets specified")
|
@@ -334,7 +334,7 @@ def _classical_hadamard_transform(arr: list[float]) -> np.ndarray:
|
|
334
334
|
return 1 / np.sqrt(len(arr)) * np.array(sympy.fwht(np.array(arr)))
|
335
335
|
|
336
336
|
|
337
|
-
@qfunc
|
337
|
+
@qfunc
|
338
338
|
def _load_phases(
|
339
339
|
phases: list[float],
|
340
340
|
target: QArray[QBit, Literal["log(get_field(phases, 'len'), 2)"]],
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Optional, Union
|
1
|
+
from typing import Optional, Union, cast
|
2
2
|
|
3
3
|
from classiq.interface.exceptions import ClassiqError
|
4
4
|
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
@@ -7,10 +7,26 @@ from classiq.interface.generator.model.preferences.preferences import Preference
|
|
7
7
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME, SerializedModel
|
8
8
|
|
9
9
|
from classiq.qmod.classical_function import CFunc
|
10
|
-
from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
|
10
|
+
from classiq.qmod.quantum_function import BaseQFunc, GenerativeQFunc, QFunc
|
11
11
|
from classiq.qmod.write_qmod import write_qmod
|
12
12
|
|
13
13
|
|
14
|
+
class _EntryPointWrapper(str):
|
15
|
+
entry_point: BaseQFunc
|
16
|
+
|
17
|
+
|
18
|
+
def add_entry_point(
|
19
|
+
new_model: SerializedModel, old_model: SerializedModel
|
20
|
+
) -> SerializedModel:
|
21
|
+
if not hasattr(old_model, "entry_point"):
|
22
|
+
return new_model
|
23
|
+
new_model_with_entry_point = _EntryPointWrapper(new_model)
|
24
|
+
new_model_with_entry_point.entry_point = cast(
|
25
|
+
_EntryPointWrapper, old_model
|
26
|
+
).entry_point
|
27
|
+
return cast(SerializedModel, new_model_with_entry_point)
|
28
|
+
|
29
|
+
|
14
30
|
def create_model(
|
15
31
|
entry_point: Union[QFunc, GenerativeQFunc],
|
16
32
|
constraints: Optional[Constraints] = None,
|
@@ -48,7 +64,9 @@ def create_model(
|
|
48
64
|
preferences,
|
49
65
|
classical_execution_function,
|
50
66
|
)
|
51
|
-
|
67
|
+
serialized_model = _EntryPointWrapper(model.get_model())
|
68
|
+
serialized_model.entry_point = entry_point
|
69
|
+
result = cast(SerializedModel, serialized_model)
|
52
70
|
|
53
71
|
if out_file is not None:
|
54
72
|
write_qmod(result, out_file)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from collections.abc import Iterator
|
2
|
+
from contextlib import contextmanager
|
3
|
+
|
4
|
+
_DECLARATIVE_SWITCH = False
|
5
|
+
|
6
|
+
|
7
|
+
def get_global_declarative_switch() -> bool:
|
8
|
+
return _DECLARATIVE_SWITCH
|
9
|
+
|
10
|
+
|
11
|
+
@contextmanager
|
12
|
+
def set_global_declarative_switch() -> Iterator[None]:
|
13
|
+
global _DECLARATIVE_SWITCH
|
14
|
+
previous = _DECLARATIVE_SWITCH
|
15
|
+
_DECLARATIVE_SWITCH = True
|
16
|
+
try:
|
17
|
+
yield
|
18
|
+
finally:
|
19
|
+
_DECLARATIVE_SWITCH = previous
|
@@ -35,6 +35,7 @@ from classiq.interface.model.control import Control
|
|
35
35
|
from classiq.interface.model.handle_binding import (
|
36
36
|
FieldHandleBinding,
|
37
37
|
HandleBinding,
|
38
|
+
HandlesList,
|
38
39
|
SlicedHandleBinding,
|
39
40
|
SubscriptHandleBinding,
|
40
41
|
)
|
@@ -395,6 +396,9 @@ class DSLPrettyPrinter(ModelVisitor):
|
|
395
396
|
def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
|
396
397
|
return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
|
397
398
|
|
399
|
+
def visit_HandlesList(self, handles: HandlesList) -> str:
|
400
|
+
return f"{{{', '.join(map(self.visit, handles.handles))}}}"
|
401
|
+
|
398
402
|
def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
|
399
403
|
if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
|
400
404
|
op = "="
|
@@ -37,6 +37,7 @@ from classiq.interface.model.control import Control
|
|
37
37
|
from classiq.interface.model.handle_binding import (
|
38
38
|
FieldHandleBinding,
|
39
39
|
HandleBinding,
|
40
|
+
HandlesList,
|
40
41
|
SlicedHandleBinding,
|
41
42
|
SubscriptHandleBinding,
|
42
43
|
)
|
@@ -518,6 +519,9 @@ class PythonPrettyPrinter(ModelVisitor):
|
|
518
519
|
def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
|
519
520
|
return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
|
520
521
|
|
522
|
+
def visit_HandlesList(self, handles: HandlesList) -> str:
|
523
|
+
return self.visit(handles.handles)
|
524
|
+
|
521
525
|
def visit_ArithmeticOperation(
|
522
526
|
self, arith_op: ArithmeticOperation, in_lambda: bool = False
|
523
527
|
) -> str:
|
classiq/qmod/qfunc.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
|
2
|
-
from contextlib import contextmanager
|
1
|
+
import warnings
|
3
2
|
from typing import Callable, Literal, Optional, Union, overload
|
4
3
|
|
5
|
-
from classiq.interface.exceptions import ClassiqInternalError
|
4
|
+
from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqInternalError
|
6
5
|
|
6
|
+
from classiq.qmod.global_declarative_switch import get_global_declarative_switch
|
7
7
|
from classiq.qmod.quantum_callable import QCallable
|
8
8
|
from classiq.qmod.quantum_function import (
|
9
9
|
BaseQFunc,
|
@@ -12,22 +12,9 @@ from classiq.qmod.quantum_function import (
|
|
12
12
|
QFunc,
|
13
13
|
)
|
14
14
|
|
15
|
-
_GENERATIVE_SWITCH = False
|
16
|
-
|
17
|
-
|
18
|
-
@contextmanager
|
19
|
-
def set_global_generative_switch() -> Iterator[None]:
|
20
|
-
global _GENERATIVE_SWITCH
|
21
|
-
previous = _GENERATIVE_SWITCH
|
22
|
-
_GENERATIVE_SWITCH = True
|
23
|
-
try:
|
24
|
-
yield
|
25
|
-
finally:
|
26
|
-
_GENERATIVE_SWITCH = previous
|
27
|
-
|
28
15
|
|
29
16
|
@overload
|
30
|
-
def qfunc(func: Callable) ->
|
17
|
+
def qfunc(func: Callable) -> GenerativeQFunc: ...
|
31
18
|
|
32
19
|
|
33
20
|
@overload
|
@@ -42,16 +29,16 @@ def qfunc(
|
|
42
29
|
@overload
|
43
30
|
def qfunc(
|
44
31
|
*,
|
45
|
-
generative: Literal[
|
32
|
+
generative: Literal[False],
|
46
33
|
synthesize_separately: bool = False,
|
47
34
|
atomic_qualifiers: Optional[list[str]] = None,
|
48
|
-
) -> Callable[[Callable],
|
35
|
+
) -> Callable[[Callable], QFunc]: ...
|
49
36
|
|
50
37
|
|
51
38
|
@overload
|
52
39
|
def qfunc(
|
53
40
|
*, synthesize_separately: bool, atomic_qualifiers: Optional[list[str]] = None
|
54
|
-
) -> Callable[[Callable],
|
41
|
+
) -> Callable[[Callable], GenerativeQFunc]: ...
|
55
42
|
|
56
43
|
|
57
44
|
@overload
|
@@ -59,17 +46,38 @@ def qfunc(
|
|
59
46
|
*,
|
60
47
|
synthesize_separately: bool = False,
|
61
48
|
atomic_qualifiers: Optional[list[str]] = None,
|
62
|
-
) -> Callable[[Callable],
|
49
|
+
) -> Callable[[Callable], GenerativeQFunc]: ...
|
63
50
|
|
64
51
|
|
65
52
|
def qfunc(
|
66
53
|
func: Optional[Callable] = None,
|
67
54
|
*,
|
68
55
|
external: bool = False,
|
69
|
-
generative: bool =
|
56
|
+
generative: Optional[bool] = None,
|
70
57
|
synthesize_separately: bool = False,
|
71
58
|
atomic_qualifiers: Optional[list[str]] = None,
|
72
59
|
) -> Union[Callable[[Callable], QCallable], QCallable]:
|
60
|
+
if generative is True:
|
61
|
+
warnings.warn(
|
62
|
+
"The use of `generative=True` is no longer required. Note that the "
|
63
|
+
"treatment of parameters of Qmod types will change from Python value to "
|
64
|
+
"symbolic in a near release. Change Qmod types to the corresponding Python "
|
65
|
+
"built-in types in order to use the parameters in Python expression "
|
66
|
+
"contexts.\n"
|
67
|
+
"Recommended changes:\n"
|
68
|
+
"@qfunc(generative=True) -> @qfunc\n"
|
69
|
+
"CInt->int\n"
|
70
|
+
"CReal->float\n"
|
71
|
+
"CArray->list\n\n"
|
72
|
+
"For more information see https://docs.classiq.io/latest/qmod-reference/language-reference/generative-descriptions/",
|
73
|
+
ClassiqDeprecationWarning,
|
74
|
+
stacklevel=2,
|
75
|
+
)
|
76
|
+
elif generative is None:
|
77
|
+
generative = True
|
78
|
+
if get_global_declarative_switch():
|
79
|
+
generative = False
|
80
|
+
|
73
81
|
def wrapper(func: Callable) -> QCallable:
|
74
82
|
qfunc: BaseQFunc
|
75
83
|
|
@@ -77,7 +85,7 @@ def qfunc(
|
|
77
85
|
_validate_directives(synthesize_separately, atomic_qualifiers)
|
78
86
|
return ExternalQFunc(func)
|
79
87
|
|
80
|
-
if generative
|
88
|
+
if generative:
|
81
89
|
qfunc = GenerativeQFunc(func)
|
82
90
|
else:
|
83
91
|
qfunc = QFunc(func)
|