classiq 0.80.0__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 +18 -13
- 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.0.dist-info → classiq-0.81.0.dist-info}/METADATA +1 -1
- {classiq-0.80.0.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.0.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
|
|
@@ -429,19 +438,14 @@ def _dicke_split_cycle_shift(k: int, qvar: QArray[QBit]) -> None:
|
|
429
438
|
|
430
439
|
@qfunc
|
431
440
|
def prepare_dicke_state_unary_input(max_k: int, qvar: QArray[QBit]) -> None:
|
432
|
-
"""
|
433
|
-
assumes the input is encoded in qvar in unary encoding. should work for every value
|
434
|
-
smaller than max_k
|
435
|
-
"""
|
436
441
|
"""
|
437
442
|
[Qmod Classiq-library function]
|
438
443
|
|
439
444
|
Prepares a Dicke state with a variable number of excitations based on an unary-encoded input.
|
440
445
|
|
441
446
|
The Dicke state is defined to be:
|
442
|
-
|
443
|
-
|
444
|
-
$$
|
447
|
+
|
448
|
+
$$\\mathrm{Dicke}(n, k) = \\frac{1}{\\sqrt{\\binom{n}{k}}} \\sum_{x \\in \\{0,1\\}^n,\\, |x| = k} |x\\rangle$$
|
445
449
|
|
446
450
|
The input register `qvar` is expected to already be initialized in a unary encoding:
|
447
451
|
the value k is represented by a string of k ones followed by zeros, e.g., k = 3 -> |11100...0>.
|
@@ -465,14 +469,15 @@ def prepare_dicke_state(k: int, qvar: QArray[QBit]) -> None:
|
|
465
469
|
Prepares a Dicke state with k excitations over the provided quantum register.
|
466
470
|
|
467
471
|
A Dicke state of n qubits with k excitations is an equal superposition of all basis states
|
468
|
-
with exactly k qubits in the
|
469
|
-
For example, Dicke(2, 1) = (|01
|
470
|
-
|
471
|
-
|
472
|
-
|
472
|
+
with exactly k qubits in the $|1\\rangle$ state and $(n - k)$ qubits in the $|0\\rangle$ state.
|
473
|
+
For example, $\\mathrm{Dicke}(2, 1) = (|01\\rangle + |10\\rangle) / \\sqrt(2)$.
|
474
|
+
|
475
|
+
In the general case it is defined to be:
|
476
|
+
|
477
|
+
$$\\mathrm{Dicke}(n, k) = \\frac{1}{\\sqrt{\\binom{n}{k}}} \\sum_{x \\in \\{0,1\\}^n,\\, |x| = k} |x\\rangle$$
|
473
478
|
|
474
479
|
Args:
|
475
|
-
k: The number of excitations (i.e., number of qubits in state
|
480
|
+
k: The number of excitations (i.e., number of qubits in state $|1\\rangle$).
|
476
481
|
qvar: The quantum register (array of qubits) to initialize. Must be uninitialized and have length >= k.
|
477
482
|
"""
|
478
483
|
if k > 0:
|
@@ -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).
|