classiq 0.80.1__py3-none-any.whl → 0.81.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/interface/_version.py +1 -1
- classiq/interface/debug_info/debug_info.py +0 -1
- classiq/interface/generator/compiler_keywords.py +1 -1
- classiq/interface/generator/expressions/atomic_expression_functions.py +11 -7
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +6 -2
- classiq/interface/generator/function_params.py +1 -1
- classiq/interface/generator/functions/classical_type.py +8 -0
- classiq/interface/generator/generated_circuit_data.py +1 -2
- classiq/interface/generator/types/compilation_metadata.py +4 -1
- classiq/interface/model/handle_binding.py +12 -2
- classiq/interface/model/quantum_type.py +12 -1
- classiq/interface/server/routes.py +0 -1
- classiq/model_expansions/atomic_expression_functions_defs.py +1 -1
- classiq/model_expansions/capturing/captured_vars.py +123 -9
- classiq/model_expansions/closure.py +2 -0
- classiq/model_expansions/evaluators/quantum_type_utils.py +3 -18
- classiq/model_expansions/function_builder.py +1 -17
- classiq/model_expansions/quantum_operations/allocate.py +18 -7
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +4 -0
- classiq/model_expansions/quantum_operations/bind.py +2 -1
- classiq/model_expansions/quantum_operations/call_emitter.py +27 -21
- classiq/model_expansions/quantum_operations/emitter.py +28 -0
- classiq/model_expansions/quantum_operations/function_calls_cache.py +1 -16
- classiq/model_expansions/transformers/type_qualifier_inference.py +72 -19
- classiq/model_expansions/visitors/symbolic_param_inference.py +0 -6
- classiq/open_library/functions/amplitude_amplification.py +3 -5
- classiq/open_library/functions/state_preparation.py +9 -0
- classiq/qmod/builtins/functions/__init__.py +3 -1
- classiq/qmod/builtins/functions/exponentiation.py +41 -3
- classiq/qmod/builtins/operations.py +65 -37
- classiq/qmod/declaration_inferrer.py +2 -1
- classiq/qmod/native/pretty_printer.py +8 -9
- classiq/qmod/pretty_print/pretty_printer.py +9 -9
- classiq/qmod/qfunc.py +11 -11
- classiq/qmod/quantum_expandable.py +4 -0
- {classiq-0.80.1.dist-info → classiq-0.81.0.dist-info}/METADATA +1 -1
- {classiq-0.80.1.dist-info → classiq-0.81.0.dist-info}/RECORD +38 -40
- classiq/interface/execution/resource_estimator.py +0 -7
- classiq/interface/execution/result.py +0 -5
- {classiq-0.80.1.dist-info → classiq-0.81.0.dist-info}/WHEEL +0 -0
@@ -29,6 +29,7 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
29
29
|
PortDeclarationDirection,
|
30
30
|
)
|
31
31
|
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
32
|
+
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
32
33
|
from classiq.interface.helpers.backward_compatibility import zip_strict
|
33
34
|
from classiq.interface.helpers.text_utils import are, readable_list, s
|
34
35
|
from classiq.interface.model.classical_parameter_declaration import (
|
@@ -80,7 +81,7 @@ from classiq.model_expansions.scope import (
|
|
80
81
|
Scope,
|
81
82
|
)
|
82
83
|
from classiq.model_expansions.transformers.type_qualifier_inference import (
|
83
|
-
|
84
|
+
TypeQualifierValidation,
|
84
85
|
)
|
85
86
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
86
87
|
from classiq.qmod.pretty_print.expression_to_python import transform_expression
|
@@ -290,6 +291,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
290
291
|
context = self._expand_operation(function)
|
291
292
|
function_context = cast(FunctionContext, context)
|
292
293
|
function_def = self._create_function_definition(function_context, args)
|
294
|
+
self._validate_type_qualifiers(function_context, function_def)
|
293
295
|
self._expanded_functions[cache_key] = function_def
|
294
296
|
self._top_level_scope[function_def.name] = Evaluated(
|
295
297
|
value=function_context.closure.with_new_declaration(function_def)
|
@@ -320,20 +322,14 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
320
322
|
)
|
321
323
|
captured_ports = captured_vars.get_captured_parameters()
|
322
324
|
if len(captured_ports) == 0:
|
323
|
-
self._override_type_qualifier(function_context, func_def)
|
324
325
|
return func_def
|
325
326
|
func_def.positional_arg_declarations = list(
|
326
327
|
chain.from_iterable((func_def.positional_arg_declarations, captured_ports))
|
327
328
|
)
|
328
329
|
|
329
|
-
rewrite_mapping =
|
330
|
-
if function_context.is_lambda:
|
331
|
-
rewrite_mapping |= captured_vars.get_immediate_captured_mapping()
|
332
|
-
rewrite_mapping |= captured_vars.get_classical_captured_mapping()
|
330
|
+
rewrite_mapping = captured_vars.get_captured_mapping(function_context.is_lambda)
|
333
331
|
func_def.body = self.rewrite(func_def.body, rewrite_mapping)
|
334
332
|
|
335
|
-
self._override_type_qualifier(function_context, func_def)
|
336
|
-
|
337
333
|
return func_def
|
338
334
|
|
339
335
|
@staticmethod
|
@@ -419,9 +415,21 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
419
415
|
if var_state and param.direction == PortDeclarationDirection.Output:
|
420
416
|
raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
|
421
417
|
|
422
|
-
def
|
418
|
+
def _validate_type_qualifiers(
|
423
419
|
self, func_context: FunctionContext, func_def: NativeFunctionDefinition
|
424
420
|
) -> None:
|
421
|
+
if self._should_override_type_qualifiers(func_context):
|
422
|
+
self._override_type_qualifiers(func_def)
|
423
|
+
|
424
|
+
unchecked = self._functions_compilation_metadata.get(
|
425
|
+
func_context.name, CompilationMetadata()
|
426
|
+
).unchecked
|
427
|
+
TypeQualifierValidation().run(
|
428
|
+
func_def.port_declarations, func_def.body, unchecked
|
429
|
+
)
|
430
|
+
|
431
|
+
@staticmethod
|
432
|
+
def _should_override_type_qualifiers(func_context: FunctionContext) -> bool:
|
425
433
|
"""
|
426
434
|
The type qualifier can be changed according to the operand passed to the
|
427
435
|
function. For example,
|
@@ -434,29 +442,27 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
434
442
|
"""
|
435
443
|
|
436
444
|
if func_context.is_lambda:
|
437
|
-
|
438
|
-
return
|
445
|
+
return True
|
439
446
|
|
440
447
|
orig_name = func_context.name
|
441
448
|
if (
|
442
449
|
orig_name == MAIN_FUNCTION_NAME
|
443
450
|
or orig_name not in func_context.closure.scope
|
444
451
|
):
|
445
|
-
return
|
452
|
+
return False
|
446
453
|
|
447
454
|
orig_func = func_context.closure.scope[orig_name].value
|
448
|
-
|
449
|
-
isinstance(
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
+
return not (
|
456
|
+
isinstance(orig_func, Closure)
|
457
|
+
and not any(
|
458
|
+
isinstance(param_decl, QuantumOperandDeclaration)
|
459
|
+
for param_decl in orig_func.positional_arg_declarations
|
460
|
+
)
|
461
|
+
)
|
455
462
|
|
456
463
|
@staticmethod
|
457
|
-
def
|
464
|
+
def _override_type_qualifiers(func_def: NativeFunctionDefinition) -> None:
|
458
465
|
# only override the qualifier if it's unspecified (not QFree or Const)
|
459
466
|
for port in func_def.port_declarations:
|
460
467
|
if port.type_qualifier is TypeQualifier.Quantum:
|
461
468
|
port.type_qualifier = TypeQualifier.Inferred
|
462
|
-
TypeQualifierInference().run(func_def.port_declarations, func_def.body)
|
@@ -184,6 +184,8 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
184
184
|
def _update_captured_classical_vars_in_expression(self, expr: Expression) -> None:
|
185
185
|
for var_name, var_type in self._get_classical_vars_in_expression(expr):
|
186
186
|
self._capture_classical_var(var_name, var_type)
|
187
|
+
for handle in self._get_quantum_type_attributes_in_expression(expr):
|
188
|
+
self._capture_quantum_type_attribute(handle)
|
187
189
|
|
188
190
|
def _update_captured_vars(self, op: QuantumOperation) -> None:
|
189
191
|
for handle, direction in op.handles_with_directions:
|
@@ -223,6 +225,17 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
223
225
|
defining_function=defining_function,
|
224
226
|
)
|
225
227
|
|
228
|
+
def _capture_quantum_type_attribute(self, handle: FieldHandleBinding) -> None:
|
229
|
+
if handle.name not in self._current_scope:
|
230
|
+
return
|
231
|
+
defining_function = self._current_scope[handle.name].defining_function
|
232
|
+
if defining_function is None:
|
233
|
+
raise ClassiqInternalExpansionError
|
234
|
+
self._builder.current_block.captured_vars.capture_quantum_type_attribute(
|
235
|
+
handle=handle,
|
236
|
+
defining_function=defining_function,
|
237
|
+
)
|
238
|
+
|
226
239
|
def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
|
227
240
|
vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
|
228
241
|
vrc.visit(ast.parse(expr.expr))
|
@@ -258,3 +271,18 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
258
271
|
)
|
259
272
|
}.items()
|
260
273
|
)
|
274
|
+
|
275
|
+
def _get_quantum_type_attributes_in_expression(
|
276
|
+
self, expr: Expression
|
277
|
+
) -> list[FieldHandleBinding]:
|
278
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
|
279
|
+
vrc.visit(ast.parse(expr.expr))
|
280
|
+
return list(
|
281
|
+
dict.fromkeys(
|
282
|
+
handle
|
283
|
+
for handle in vrc.var_handles
|
284
|
+
if isinstance(handle, FieldHandleBinding)
|
285
|
+
and handle.field in CLASSICAL_ATTRIBUTES
|
286
|
+
and isinstance(self._current_scope[handle.name].value, QuantumSymbol)
|
287
|
+
)
|
288
|
+
)
|
@@ -11,10 +11,6 @@ from classiq.interface.generator.expressions.proxies.classical.classical_struct_
|
|
11
11
|
from classiq.interface.generator.expressions.proxies.classical.utils import (
|
12
12
|
get_proxy_type,
|
13
13
|
)
|
14
|
-
from classiq.interface.generator.functions.port_declaration import (
|
15
|
-
PortDeclarationDirection,
|
16
|
-
)
|
17
|
-
from classiq.interface.model.port_declaration import PortDeclaration
|
18
14
|
from classiq.interface.model.quantum_function_declaration import (
|
19
15
|
NamedParamsQuantumFunctionDeclaration,
|
20
16
|
)
|
@@ -36,18 +32,7 @@ def get_func_call_cache_key(
|
|
36
32
|
raise ClassiqInternalExpansionError(
|
37
33
|
"Mismatch between number of args to number of arg declarations"
|
38
34
|
)
|
39
|
-
|
40
|
-
# output arguments cannot affect the morphization of the function, as their
|
41
|
-
# attributes are defined locally by the function, according to other arguments
|
42
|
-
non_outputs_args = [
|
43
|
-
arg
|
44
|
-
for arg_decl, arg in zip(decl.positional_arg_declarations, args)
|
45
|
-
if not (
|
46
|
-
isinstance(arg_decl, PortDeclaration)
|
47
|
-
and arg_decl.direction is PortDeclarationDirection.Output
|
48
|
-
)
|
49
|
-
]
|
50
|
-
return f"{decl.name}__{_evaluated_args_to_str(non_outputs_args)}"
|
35
|
+
return f"{decl.name}__{_evaluated_args_to_str(args)}"
|
51
36
|
|
52
37
|
|
53
38
|
def _evaluated_args_to_str(evaluated_args: list[Evaluated]) -> str:
|
@@ -4,7 +4,10 @@ import itertools
|
|
4
4
|
from collections.abc import Collection, Iterator, Sequence
|
5
5
|
from contextlib import contextmanager
|
6
6
|
|
7
|
-
from classiq.interface.exceptions import
|
7
|
+
from classiq.interface.exceptions import (
|
8
|
+
ClassiqExpansionError,
|
9
|
+
ClassiqInternalExpansionError,
|
10
|
+
)
|
8
11
|
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
9
12
|
from classiq.interface.model.allocate import Allocate
|
10
13
|
from classiq.interface.model.bind_operation import BindOperation
|
@@ -30,7 +33,18 @@ from classiq.interface.model.within_apply_operation import WithinApply
|
|
30
33
|
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
31
34
|
|
32
35
|
|
33
|
-
|
36
|
+
def _inconsistent_type_qualifier_error(
|
37
|
+
port_name: str, expected: TypeQualifier, actual: TypeQualifier
|
38
|
+
) -> str:
|
39
|
+
return (
|
40
|
+
f"The type modifier of variable '{port_name}' does not conform to the function signature: "
|
41
|
+
f"expected '{expected.name}', but found '{actual.name}'.\n"
|
42
|
+
f"Tip: If the final role of the variable in the function matches '{expected.name}', "
|
43
|
+
f"you may use the `unchecked` flag to instruct the compiler to disregard individual operations."
|
44
|
+
)
|
45
|
+
|
46
|
+
|
47
|
+
class TypeQualifierValidation(ModelVisitor):
|
34
48
|
"""
|
35
49
|
This class assumes that function calls are topologically sorted, so it traverses
|
36
50
|
the list of function calls and infers the type qualifiers for each function call
|
@@ -41,23 +55,41 @@ class TypeQualifierInference(ModelVisitor):
|
|
41
55
|
def __init__(self, support_unused_ports: bool = True) -> None:
|
42
56
|
self._signature_ports: dict[str, PortDeclaration] = dict()
|
43
57
|
self._inferred_ports: dict[str, PortDeclaration] = dict()
|
58
|
+
self._unchecked: set[str] = set()
|
59
|
+
self._conjugation_context: bool = False
|
44
60
|
self._support_unused_ports = (
|
45
61
|
support_unused_ports # could be turned off for debugging
|
46
62
|
)
|
47
63
|
|
48
64
|
@contextmanager
|
49
|
-
def
|
65
|
+
def validate_ports(
|
66
|
+
self, ports: Collection[PortDeclaration], unchecked: Collection[str]
|
67
|
+
) -> Iterator[bool]:
|
50
68
|
for port in ports:
|
51
69
|
if port.type_qualifier is TypeQualifier.Inferred:
|
52
70
|
self._inferred_ports[port.name] = port
|
53
71
|
else:
|
54
72
|
self._signature_ports[port.name] = port
|
73
|
+
self._unchecked.update(unchecked)
|
55
74
|
|
56
|
-
yield len(self._inferred_ports) > 0
|
75
|
+
yield len(self._inferred_ports) > 0 or any(
|
76
|
+
port.type_qualifier is not TypeQualifier.Quantum
|
77
|
+
for port in self._signature_ports.values()
|
78
|
+
)
|
57
79
|
|
58
80
|
self._set_unused_as_const()
|
59
81
|
self._signature_ports.clear()
|
60
82
|
self._inferred_ports.clear()
|
83
|
+
self._unchecked.clear()
|
84
|
+
|
85
|
+
@contextmanager
|
86
|
+
def conjugation_context(self) -> Iterator[None]:
|
87
|
+
previous_context = self._conjugation_context
|
88
|
+
self._conjugation_context = True
|
89
|
+
try:
|
90
|
+
yield
|
91
|
+
finally:
|
92
|
+
self._conjugation_context = previous_context
|
61
93
|
|
62
94
|
def _set_unused_as_const(self) -> None:
|
63
95
|
unresolved_ports = [
|
@@ -73,30 +105,50 @@ class TypeQualifierInference(ModelVisitor):
|
|
73
105
|
for port in unresolved_ports:
|
74
106
|
port.type_qualifier = TypeQualifier.Const
|
75
107
|
|
76
|
-
def
|
108
|
+
def _validate_qualifier(self, candidate: str, qualifier: TypeQualifier) -> None:
|
109
|
+
if self._conjugation_context and qualifier is TypeQualifier.QFree:
|
110
|
+
qualifier = TypeQualifier.Const
|
111
|
+
|
77
112
|
if candidate in self._inferred_ports:
|
78
113
|
self._inferred_ports[candidate].type_qualifier = TypeQualifier.and_(
|
79
114
|
self._inferred_ports[candidate].type_qualifier, qualifier
|
80
115
|
)
|
116
|
+
return
|
117
|
+
|
118
|
+
if candidate not in self._signature_ports or candidate in self._unchecked:
|
119
|
+
return
|
120
|
+
|
121
|
+
signature_qualifier = self._signature_ports[candidate].type_qualifier
|
122
|
+
if signature_qualifier is not TypeQualifier.and_(
|
123
|
+
signature_qualifier, qualifier
|
124
|
+
):
|
125
|
+
raise ClassiqExpansionError(
|
126
|
+
_inconsistent_type_qualifier_error(
|
127
|
+
candidate, signature_qualifier, qualifier
|
128
|
+
)
|
129
|
+
)
|
81
130
|
|
82
131
|
def run(
|
83
|
-
self,
|
132
|
+
self,
|
133
|
+
ports: Collection[PortDeclaration],
|
134
|
+
body: Sequence[QuantumStatement],
|
135
|
+
unchecked: Collection[str],
|
84
136
|
) -> None:
|
85
|
-
with self.
|
86
|
-
if
|
137
|
+
with self.validate_ports(ports, unchecked) as should_validate:
|
138
|
+
if should_validate:
|
87
139
|
self.visit(body)
|
88
140
|
|
89
141
|
def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
|
90
142
|
for handle, port in call.handles_with_params:
|
91
|
-
self.
|
143
|
+
self._validate_qualifier(handle.name, port.type_qualifier)
|
92
144
|
|
93
145
|
def visit_Allocate(self, alloc: Allocate) -> None:
|
94
|
-
self.
|
146
|
+
self._validate_qualifier(alloc.target.name, TypeQualifier.QFree)
|
95
147
|
|
96
148
|
def visit_BindOperation(self, bind_op: BindOperation) -> None:
|
97
149
|
reduced_qualifier = self._get_reduced_qualifier(bind_op)
|
98
150
|
for handle in itertools.chain(bind_op.in_handles, bind_op.out_handles):
|
99
|
-
self.
|
151
|
+
self._validate_qualifier(handle.name, reduced_qualifier)
|
100
152
|
|
101
153
|
def _get_reduced_qualifier(self, bind_op: BindOperation) -> TypeQualifier:
|
102
154
|
handles = itertools.chain(bind_op.in_handles, bind_op.out_handles)
|
@@ -131,7 +183,7 @@ class TypeQualifierInference(ModelVisitor):
|
|
131
183
|
itertools.chain(signature_ports.values(), known_inferred_ports.values())
|
132
184
|
)
|
133
185
|
if len(known_ports) == 0:
|
134
|
-
return TypeQualifier.
|
186
|
+
return TypeQualifier.Const
|
135
187
|
elif len(known_ports) == 1:
|
136
188
|
return known_ports[0].type_qualifier
|
137
189
|
else:
|
@@ -149,25 +201,25 @@ class TypeQualifierInference(ModelVisitor):
|
|
149
201
|
|
150
202
|
def visit_ArithmeticOperation(self, arith: ArithmeticOperation) -> None:
|
151
203
|
result_var = arith.result_var.name
|
152
|
-
self.
|
204
|
+
self._validate_qualifier(result_var, TypeQualifier.QFree)
|
153
205
|
for expr_var in self._extract_expr_vars(arith):
|
154
|
-
self.
|
206
|
+
self._validate_qualifier(expr_var, TypeQualifier.Const)
|
155
207
|
|
156
208
|
def visit_AmplitudeLoadingOperation(
|
157
209
|
self, amp_load: AmplitudeLoadingOperation
|
158
210
|
) -> None:
|
159
211
|
result_var = amp_load.result_var.name
|
160
|
-
self.
|
212
|
+
self._validate_qualifier(result_var, TypeQualifier.Quantum)
|
161
213
|
for expr_var in self._extract_expr_vars(amp_load):
|
162
|
-
self.
|
214
|
+
self._validate_qualifier(expr_var, TypeQualifier.Const)
|
163
215
|
|
164
216
|
def visit_PhaseOperation(self, phase_op: PhaseOperation) -> None:
|
165
217
|
for expr_var in self._extract_expr_vars(phase_op):
|
166
|
-
self.
|
218
|
+
self._validate_qualifier(expr_var, TypeQualifier.Const)
|
167
219
|
|
168
220
|
def visit_Control(self, control: Control) -> None:
|
169
221
|
for control_var in self._extract_expr_vars(control):
|
170
|
-
self.
|
222
|
+
self._validate_qualifier(control_var, TypeQualifier.Const)
|
171
223
|
self.visit(control.body)
|
172
224
|
if control.else_block is not None:
|
173
225
|
self.visit(control.else_block)
|
@@ -179,5 +231,6 @@ class TypeQualifierInference(ModelVisitor):
|
|
179
231
|
self.visit(power.body)
|
180
232
|
|
181
233
|
def visit_WithinApply(self, within_apply: WithinApply) -> None:
|
182
|
-
self.
|
234
|
+
with self.conjugation_context():
|
235
|
+
self.visit(within_apply.compute)
|
183
236
|
self.visit(within_apply.action)
|
@@ -152,16 +152,10 @@ class SymbolicParamInference(ModelVisitor):
|
|
152
152
|
):
|
153
153
|
self._process_compile_time_expressions(arg)
|
154
154
|
else:
|
155
|
-
if isinstance(arg, Expression):
|
156
|
-
for expr_part in self.get_generative_expression_parts(arg.expr):
|
157
|
-
self._process_compile_time_expression(expr_part)
|
158
155
|
for expr in _get_expressions(arg):
|
159
156
|
self._process_nested_compile_time_expression(expr.expr)
|
160
157
|
self.generic_visit(call)
|
161
158
|
|
162
|
-
def get_generative_expression_parts(self, expr: str) -> list[str]:
|
163
|
-
return []
|
164
|
-
|
165
159
|
def _get_params(self, call: QuantumFunctionCall) -> Sequence[AnonPositionalArg]:
|
166
160
|
name = call.func_name
|
167
161
|
if name in self._scope_operands:
|
@@ -1,5 +1,3 @@
|
|
1
|
-
import numpy as np
|
2
|
-
|
3
1
|
from classiq.open_library.functions.grover import grover_operator
|
4
2
|
from classiq.qmod.builtins.functions.standard_gates import RY
|
5
3
|
from classiq.qmod.builtins.operations import (
|
@@ -13,7 +11,7 @@ from classiq.qmod.cparam import CInt, CReal
|
|
13
11
|
from classiq.qmod.qfunc import qfunc
|
14
12
|
from classiq.qmod.qmod_variable import QArray, QBit
|
15
13
|
from classiq.qmod.quantum_callable import QCallable
|
16
|
-
from classiq.qmod.symbolic import acos, asin, ceiling, sin
|
14
|
+
from classiq.qmod.symbolic import acos, asin, ceiling, pi, sin
|
17
15
|
|
18
16
|
|
19
17
|
@qfunc
|
@@ -69,8 +67,8 @@ def exact_amplitude_amplification(
|
|
69
67
|
packed_vars: The variable that holds the state to be amplified. Assumed to be in the zero state at the beginning of the algorithm.
|
70
68
|
"""
|
71
69
|
aux = QBit()
|
72
|
-
k = ceiling((
|
73
|
-
theta =
|
70
|
+
k = ceiling((pi / (4 * asin(amplitude))) - 0.5)
|
71
|
+
theta = pi / (4 * k + 2)
|
74
72
|
rot_phase = 2 * acos(sin(theta) / amplitude)
|
75
73
|
|
76
74
|
extended_qvars: QArray = QArray()
|
@@ -57,6 +57,10 @@ def allocate_num(
|
|
57
57
|
"""
|
58
58
|
[Qmod Classiq-library function]
|
59
59
|
|
60
|
+
This function is **deprecated** and will no longer be supported starting on
|
61
|
+
16/06/2025 at the earliest. Instead, use `allocate` which supports the same
|
62
|
+
parameters.
|
63
|
+
|
60
64
|
Initializes a quantum number with the given number of qubits, sign, and fractional digits.
|
61
65
|
|
62
66
|
Args:
|
@@ -64,6 +68,11 @@ def allocate_num(
|
|
64
68
|
is_signed: Whether the number is signed or unsigned.
|
65
69
|
fraction_digits: The number of fractional digits.
|
66
70
|
"""
|
71
|
+
warnings.warn(
|
72
|
+
"Function `allocate_num` is deprecated and will no longer be supported starting on 16/06/2025 at the earliest. Instead, use `allocate` which supports the same parameters. ",
|
73
|
+
ClassiqDeprecationWarning,
|
74
|
+
stacklevel=1,
|
75
|
+
)
|
67
76
|
allocate(num_qubits, out)
|
68
77
|
|
69
78
|
|
@@ -70,8 +70,9 @@ CORE_LIB_DECLS = [
|
|
70
70
|
inplace_prepare_amplitudes_approx,
|
71
71
|
single_pauli_exponent,
|
72
72
|
commuting_paulis_exponent,
|
73
|
-
sparse_suzuki_trotter,
|
74
73
|
suzuki_trotter,
|
74
|
+
parametric_suzuki_trotter,
|
75
|
+
sparse_suzuki_trotter,
|
75
76
|
qdrift,
|
76
77
|
exponentiation_with_depth_constraint,
|
77
78
|
RESET,
|
@@ -131,6 +132,7 @@ __all__ = [ # noqa: RUF022
|
|
131
132
|
"molecule_hartree_fock",
|
132
133
|
"molecule_hva",
|
133
134
|
"molecule_ucc",
|
135
|
+
"parametric_suzuki_trotter",
|
134
136
|
"pauli_feature_map",
|
135
137
|
"permute",
|
136
138
|
"prepare_amplitudes",
|
@@ -66,7 +66,9 @@ def suzuki_trotter(
|
|
66
66
|
"""
|
67
67
|
[Qmod core-library function]
|
68
68
|
|
69
|
-
Applies the Suzuki-Trotter decomposition to a Pauli operator.
|
69
|
+
Applies the Suzuki-Trotter decomposition to a Pauli operator.
|
70
|
+
|
71
|
+
The Suzuki-Trotter decomposition is a method for approximating the exponential of a sum of operators by a product of exponentials of each operator.
|
70
72
|
The Suzuki-Trotter decomposition of a given order nullifies the error of the Taylor series expansion of the product of exponentials up to that order.
|
71
73
|
The error of a Suzuki-Trotter decomposition decreases as the order and number of repetitions increase.
|
72
74
|
|
@@ -80,6 +82,37 @@ def suzuki_trotter(
|
|
80
82
|
pass
|
81
83
|
|
82
84
|
|
85
|
+
@qfunc(external=True)
|
86
|
+
def parametric_suzuki_trotter(
|
87
|
+
paulis: CArray[CArray[Pauli]],
|
88
|
+
coefficients: CArray[CReal, Literal["get_field(paulis, 'len')"]],
|
89
|
+
evolution_coefficient: CReal,
|
90
|
+
order: CInt,
|
91
|
+
repetitions: CInt,
|
92
|
+
qbv: QArray[QBit, Literal["get_field(paulis[0], 'len')"]],
|
93
|
+
) -> None:
|
94
|
+
"""
|
95
|
+
[Qmod core-library function]
|
96
|
+
|
97
|
+
Applies the Suzuki-Trotter decomposition to a Pauli operator represented by two
|
98
|
+
separate lists of paulis and coefficients.
|
99
|
+
Supports symbolic coefficients, including execution parameters.
|
100
|
+
|
101
|
+
The Suzuki-Trotter decomposition is a method for approximating the exponential of a sum of operators by a product of exponentials of each operator.
|
102
|
+
The Suzuki-Trotter decomposition of a given order nullifies the error of the Taylor series expansion of the product of exponentials up to that order.
|
103
|
+
The error of a Suzuki-Trotter decomposition decreases as the order and number of repetitions increase.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
paulis: The Paulis of the Pauli operator.
|
107
|
+
coefficients: The coefficients of the Pauli operator.
|
108
|
+
evolution_coefficient: A global evolution coefficient multiplying the Pauli operator.
|
109
|
+
order: The order of the Suzuki-Trotter decomposition.
|
110
|
+
repetitions: The number of repetitions of the Suzuki-Trotter decomposition.
|
111
|
+
qbv: The target quantum variable of the exponentiation.
|
112
|
+
"""
|
113
|
+
pass
|
114
|
+
|
115
|
+
|
83
116
|
@qfunc(external=True)
|
84
117
|
def sparse_suzuki_trotter(
|
85
118
|
pauli_operator: SparsePauliOp,
|
@@ -89,8 +122,13 @@ def sparse_suzuki_trotter(
|
|
89
122
|
qbv: QArray[QBit, Literal["get_field(pauli_operator, 'num_qubits')"]],
|
90
123
|
) -> None:
|
91
124
|
"""
|
92
|
-
|
93
|
-
|
125
|
+
[Qmod core-library function]
|
126
|
+
|
127
|
+
Applies the Suzuki-Trotter decomposition to a sparse Pauli operator.
|
128
|
+
|
129
|
+
The Suzuki-Trotter decomposition is a method for approximating the exponential of a sum of operators by a product of exponentials of each operator.
|
130
|
+
The Suzuki-Trotter decomposition of a given order nullifies the error of the Taylor series expansion of the product of exponentials up to that order.
|
131
|
+
The error of a Suzuki-Trotter decomposition decreases as the order and number of repetitions increase.
|
94
132
|
|
95
133
|
Args:
|
96
134
|
pauli_operator: The Pauli operator to be exponentiated, in sparse representation (See: SparsePauliOp).
|
@@ -6,6 +6,7 @@ from typing import (
|
|
6
6
|
Any,
|
7
7
|
Callable,
|
8
8
|
Final,
|
9
|
+
NoReturn,
|
9
10
|
Union,
|
10
11
|
overload,
|
11
12
|
)
|
@@ -16,6 +17,7 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
|
|
16
17
|
REPEAT_OPERATOR_NAME,
|
17
18
|
)
|
18
19
|
from classiq.interface.generator.functions.classical_type import Integer
|
20
|
+
from classiq.interface.helpers.text_utils import s
|
19
21
|
from classiq.interface.model.allocate import Allocate
|
20
22
|
from classiq.interface.model.bind_operation import BindOperation
|
21
23
|
from classiq.interface.model.classical_if import ClassicalIf
|
@@ -322,7 +324,7 @@ def within_apply(
|
|
322
324
|
def repeat(
|
323
325
|
count: Union[SymbolicExpr, int], iteration: Callable[[int], Statements]
|
324
326
|
) -> None:
|
325
|
-
_validate_operand(iteration)
|
327
|
+
_validate_operand(iteration, num_params=1)
|
326
328
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
327
329
|
source_ref = get_source_ref(sys._getframe(1))
|
328
330
|
iteration_operand = prepare_arg(
|
@@ -396,54 +398,80 @@ def phase(expr: SymbolicExpr, theta: float = 1.0) -> None:
|
|
396
398
|
)
|
397
399
|
|
398
400
|
|
399
|
-
def _validate_operand(stmt_block: Any) -> None:
|
400
|
-
if stmt_block is
|
401
|
+
def _validate_operand(stmt_block: Any, num_params: int = 0) -> None:
|
402
|
+
if stmt_block is None:
|
403
|
+
_raise_operand_error(
|
404
|
+
lambda operation_name, operand_arg_name: (
|
405
|
+
f"{operation_name!r} is missing required argument for "
|
406
|
+
f"parameter {operand_arg_name!r}"
|
407
|
+
),
|
408
|
+
num_params,
|
409
|
+
)
|
410
|
+
if isinstance(stmt_block, QCallable):
|
401
411
|
return
|
402
|
-
|
412
|
+
op_spec = inspect.getfullargspec(stmt_block)
|
413
|
+
params = op_spec.args[: len(op_spec.args) - len(op_spec.defaults or ())]
|
414
|
+
if len(params) > num_params or (
|
415
|
+
len(params) < num_params and op_spec.varargs is None
|
416
|
+
):
|
417
|
+
_raise_operand_error(
|
418
|
+
lambda operation_name, operand_arg_name: (
|
419
|
+
f"{operation_name!r} argument for {operand_arg_name!r} has "
|
420
|
+
f"{len(params)} parameter{s(params)} but {num_params} expected"
|
421
|
+
),
|
422
|
+
num_params,
|
423
|
+
)
|
424
|
+
|
425
|
+
|
426
|
+
def _raise_operand_error(
|
427
|
+
error_template: Callable[[str, str], str], num_params: int
|
428
|
+
) -> NoReturn:
|
429
|
+
currentframe: FrameType = inspect.currentframe().f_back # type: ignore[assignment,union-attr]
|
403
430
|
operation_frame: FrameType = currentframe.f_back # type: ignore[assignment]
|
404
431
|
operation_frame_info: inspect.Traceback = inspect.getframeinfo(operation_frame)
|
405
432
|
operation_name: str = operation_frame_info.function
|
406
|
-
|
407
433
|
context = operation_frame_info.code_context
|
408
434
|
assert context is not None
|
409
|
-
operand_arg_name =
|
410
|
-
|
411
|
-
error_message = (
|
412
|
-
f"{operation_name!r} is missing required argument for {operand_arg_name!r}."
|
435
|
+
operand_arg_name = (
|
436
|
+
context[0].split("_validate_operand(")[1].split(")")[0].split(",")[0]
|
413
437
|
)
|
414
|
-
|
415
|
-
operation_name
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
)
|
425
|
-
return ", ".join(
|
426
|
-
[
|
427
|
-
(
|
428
|
-
f"{param.name}={operand_value}"
|
429
|
-
if param.name == operand_arg_name
|
430
|
-
else f"{param.name}=..."
|
431
|
-
)
|
432
|
-
for param in params.values()
|
433
|
-
if param.name != "operand" # FIXME: Remove compatibility (CAD-21932)
|
434
|
-
]
|
438
|
+
operation_parameters = inspect.signature(
|
439
|
+
operation_frame.f_globals[operation_name]
|
440
|
+
).parameters
|
441
|
+
raise ClassiqValueError(
|
442
|
+
error_template(operation_name, operand_arg_name)
|
443
|
+
+ _get_operand_hint(
|
444
|
+
operation_name=operation_name,
|
445
|
+
operand_arg_name=operand_arg_name,
|
446
|
+
params=operation_parameters,
|
447
|
+
num_params=num_params,
|
448
|
+
)
|
435
449
|
)
|
436
450
|
|
437
451
|
|
438
452
|
def _get_operand_hint(
|
439
|
-
operation_name: str,
|
453
|
+
operation_name: str,
|
454
|
+
operand_arg_name: str,
|
455
|
+
params: Mapping[str, inspect.Parameter],
|
456
|
+
num_params: int,
|
440
457
|
) -> str:
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
458
|
+
if operation_name == "repeat":
|
459
|
+
operand_params = " i"
|
460
|
+
else:
|
461
|
+
operand_params = (
|
462
|
+
""
|
463
|
+
if num_params == 0
|
464
|
+
else f" {', '.join([f'p{i}' for i in range(num_params)])}"
|
465
|
+
)
|
466
|
+
args = [
|
467
|
+
(
|
468
|
+
f"{param.name}=lambda{operand_params}: ..."
|
469
|
+
if param.name == operand_arg_name
|
470
|
+
else f"{param.name}=..."
|
471
|
+
)
|
472
|
+
for param in params.values()
|
473
|
+
]
|
474
|
+
return f"\nHint: Write '{operation_name}({', '.join(args)})'"
|
447
475
|
|
448
476
|
|
449
477
|
def _operand_to_body(
|
@@ -175,7 +175,8 @@ def _get_param_name(py_type_args: Any) -> Optional[str]:
|
|
175
175
|
def _validate_annotations(py_type_args: Any, py_type: Any) -> None:
|
176
176
|
for arg in py_type_args[1:-1]:
|
177
177
|
if (
|
178
|
-
isinstance(arg, str)
|
178
|
+
isinstance(arg, str)
|
179
|
+
and not isinstance(arg, (PortDeclarationDirection, TypeQualifier))
|
179
180
|
) or arg is Literal:
|
180
181
|
raise ClassiqValueError(
|
181
182
|
f"Operand parameter declaration must be of the form <param-type> or "
|