classiq 0.66.0__py3-none-any.whl → 0.67.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/finance/finance_model_constructor.py +9 -0
- classiq/applications/grover/grover_model_constructor.py +10 -0
- classiq/applications/qnn/qlayer.py +8 -2
- classiq/applications/qsvm/qsvm_model_constructor.py +9 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/debug_info/debug_info.py +12 -0
- classiq/interface/exceptions.py +2 -5
- classiq/interface/generator/arith/argument_utils.py +1 -1
- classiq/interface/generator/arith/arithmetic.py +3 -1
- classiq/interface/generator/arith/binary_ops.py +3 -0
- classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
- classiq/interface/generator/functions/type_name.py +2 -2
- classiq/interface/generator/generated_circuit_data.py +34 -1
- classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
- classiq/interface/generator/hva.py +1 -1
- classiq/interface/generator/model/preferences/preferences.py +8 -1
- classiq/interface/generator/reset.py +14 -0
- classiq/interface/generator/ucc.py +1 -1
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/quantum_statement.py +13 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +2 -1
- classiq/model_expansions/capturing/captured_vars.py +184 -54
- classiq/model_expansions/closure.py +6 -3
- classiq/model_expansions/evaluators/control.py +14 -38
- classiq/model_expansions/function_builder.py +19 -14
- classiq/model_expansions/generative_functions.py +7 -11
- classiq/model_expansions/interpreters/base_interpreter.py +14 -5
- classiq/model_expansions/interpreters/generative_interpreter.py +9 -8
- classiq/model_expansions/quantum_operations/allocate.py +6 -2
- classiq/model_expansions/quantum_operations/bind.py +65 -13
- classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
- classiq/model_expansions/quantum_operations/classicalif.py +5 -2
- classiq/model_expansions/quantum_operations/emitter.py +8 -1
- classiq/model_expansions/quantum_operations/repeat.py +7 -2
- classiq/model_expansions/quantum_operations/shallow_emitter.py +1 -1
- classiq/model_expansions/quantum_operations/variable_decleration.py +11 -1
- classiq/open_library/functions/__init__.py +8 -0
- classiq/open_library/functions/amplitude_amplification.py +92 -0
- classiq/open_library/functions/grover.py +5 -5
- classiq/qmod/builtins/functions/__init__.py +3 -0
- classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
- classiq/qmod/quantum_function.py +4 -0
- classiq/qmod/semantics/annotation/call_annotation.py +8 -2
- classiq/qmod/semantics/annotation/model_annotation.py +9 -0
- classiq/qmod/semantics/error_manager.py +0 -6
- classiq/qmod/semantics/static_semantics_visitor.py +0 -347
- {classiq-0.66.0.dist-info → classiq-0.67.0.dist-info}/METADATA +1 -1
- {classiq-0.66.0.dist-info → classiq-0.67.0.dist-info}/RECORD +49 -47
- classiq/qmod/semantics/validation/func_call_validation.py +0 -99
- classiq/qmod/semantics/validation/handle_validation.py +0 -85
- {classiq-0.66.0.dist-info → classiq-0.67.0.dist-info}/WHEEL +0 -0
@@ -1,4 +1,7 @@
|
|
1
|
-
from classiq.interface.exceptions import
|
1
|
+
from classiq.interface.exceptions import (
|
2
|
+
ClassiqExpansionError,
|
3
|
+
ClassiqInternalExpansionError,
|
4
|
+
)
|
2
5
|
from classiq.interface.model.bind_operation import BindOperation
|
3
6
|
|
4
7
|
from classiq.model_expansions.evaluators.parameter_types import (
|
@@ -9,21 +12,12 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
|
|
9
12
|
validate_bind_targets,
|
10
13
|
)
|
11
14
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
12
|
-
from classiq.model_expansions.scope import QuantumSymbol
|
15
|
+
from classiq.model_expansions.scope import Evaluated, QuantumSymbol
|
13
16
|
|
14
17
|
|
15
18
|
class BindEmitter(Emitter[BindOperation]):
|
16
19
|
def emit(self, bind: BindOperation, /) -> None:
|
17
|
-
inputs
|
18
|
-
self._interpreter.evaluate(arg).as_type(QuantumSymbol)
|
19
|
-
for arg in bind.in_handles
|
20
|
-
]
|
21
|
-
outputs: list[QuantumSymbol] = [
|
22
|
-
self._interpreter.evaluate(arg).as_type(QuantumSymbol)
|
23
|
-
for arg in bind.out_handles
|
24
|
-
]
|
25
|
-
inputs = evaluate_types_in_quantum_symbols(inputs, self._current_scope)
|
26
|
-
outputs = evaluate_types_in_quantum_symbols(outputs, self._current_scope)
|
20
|
+
inputs, outputs = self._get_inputs_outputs(bind)
|
27
21
|
validate_bind_targets(bind, self._current_scope)
|
28
22
|
unsized_outputs = [
|
29
23
|
output for output in outputs if not output.quantum_type.has_size_in_bits
|
@@ -56,5 +50,63 @@ class BindEmitter(Emitter[BindOperation]):
|
|
56
50
|
)
|
57
51
|
|
58
52
|
self.emit_statement(
|
59
|
-
BindOperation(
|
53
|
+
BindOperation(
|
54
|
+
in_handles=bind.in_handles,
|
55
|
+
out_handles=bind.out_handles,
|
56
|
+
back_ref=bind.uuid,
|
57
|
+
)
|
60
58
|
)
|
59
|
+
|
60
|
+
def _get_inputs_outputs(
|
61
|
+
self, bind: BindOperation
|
62
|
+
) -> tuple[list[QuantumSymbol], list[QuantumSymbol]]:
|
63
|
+
evaluated_inputs: list[Evaluated] = [
|
64
|
+
self._interpreter.evaluate(arg) for arg in bind.in_handles
|
65
|
+
]
|
66
|
+
evaluated_outputs: list[Evaluated] = [
|
67
|
+
self._interpreter.evaluate(arg) for arg in bind.out_handles
|
68
|
+
]
|
69
|
+
self._validate_handle_states(evaluated_inputs, evaluated_outputs)
|
70
|
+
inputs: list[QuantumSymbol] = [
|
71
|
+
input.as_type(QuantumSymbol) for input in evaluated_inputs
|
72
|
+
]
|
73
|
+
outputs: list[QuantumSymbol] = [
|
74
|
+
output.as_type(QuantumSymbol) for output in evaluated_outputs
|
75
|
+
]
|
76
|
+
inputs = evaluate_types_in_quantum_symbols(inputs, self._current_scope)
|
77
|
+
outputs = evaluate_types_in_quantum_symbols(outputs, self._current_scope)
|
78
|
+
return inputs, outputs
|
79
|
+
|
80
|
+
def _validate_handle_states(
|
81
|
+
self, inputs: list[Evaluated], outputs: list[Evaluated]
|
82
|
+
) -> None:
|
83
|
+
input_var_names: set[str] = set()
|
84
|
+
for inp in inputs:
|
85
|
+
if inp.defining_function is None:
|
86
|
+
raise ClassiqInternalExpansionError
|
87
|
+
var_name = inp.value.handle.name
|
88
|
+
input_var_names.add(var_name)
|
89
|
+
state = self._builder.current_block.captured_vars.get_state(
|
90
|
+
var_name, inp.defining_function
|
91
|
+
)
|
92
|
+
if not state:
|
93
|
+
raise ClassiqExpansionError(
|
94
|
+
f"Cannot use uninitialized quantum variable "
|
95
|
+
f"{inp.value.handle.name!r} on the left-hand side of a bind "
|
96
|
+
f"statement"
|
97
|
+
)
|
98
|
+
for out in outputs:
|
99
|
+
if out.defining_function is None:
|
100
|
+
raise ClassiqInternalExpansionError
|
101
|
+
var_name = out.value.handle.name
|
102
|
+
if var_name in input_var_names:
|
103
|
+
continue
|
104
|
+
state = self._builder.current_block.captured_vars.get_state(
|
105
|
+
var_name, out.defining_function
|
106
|
+
)
|
107
|
+
if state:
|
108
|
+
raise ClassiqExpansionError(
|
109
|
+
f"Cannot use initialized quantum variable "
|
110
|
+
f"{out.value.handle.name!r} on the right-hand side of a bind "
|
111
|
+
f"statement"
|
112
|
+
)
|
@@ -1,13 +1,17 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
|
-
from itertools import chain
|
2
|
+
from itertools import chain, combinations
|
3
3
|
from typing import (
|
4
4
|
TYPE_CHECKING,
|
5
5
|
Generic,
|
6
|
-
Optional,
|
7
6
|
cast,
|
8
7
|
)
|
8
|
+
from uuid import UUID
|
9
9
|
|
10
10
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
11
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
12
|
+
from classiq.interface.generator.functions.port_declaration import (
|
13
|
+
PortDeclarationDirection,
|
14
|
+
)
|
11
15
|
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
12
16
|
from classiq.interface.model.classical_parameter_declaration import (
|
13
17
|
ClassicalParameterDeclaration,
|
@@ -26,6 +30,8 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
26
30
|
)
|
27
31
|
|
28
32
|
from classiq.model_expansions.capturing.captured_vars import (
|
33
|
+
INITIALIZED_VAR_MESSAGE,
|
34
|
+
UNINITIALIZED_VAR_MESSAGE,
|
29
35
|
validate_args_are_not_propagated,
|
30
36
|
)
|
31
37
|
from classiq.model_expansions.closure import FunctionClosure
|
@@ -53,6 +59,25 @@ if TYPE_CHECKING:
|
|
53
59
|
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
54
60
|
|
55
61
|
|
62
|
+
def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
|
63
|
+
handles = [
|
64
|
+
arg.value.handle
|
65
|
+
for arg in evaluated_args
|
66
|
+
if isinstance(arg.value, QuantumSymbol)
|
67
|
+
]
|
68
|
+
for handle, other_handle in combinations(handles, 2):
|
69
|
+
if handle.overlaps(other_handle):
|
70
|
+
if handle == other_handle:
|
71
|
+
raise ClassiqExpansionError(
|
72
|
+
f"Quantum cloning violation: Argument {str(handle)!r} is "
|
73
|
+
f"duplicated"
|
74
|
+
)
|
75
|
+
raise ClassiqExpansionError(
|
76
|
+
f"Quantum cloning violation: Arguments {str(handle)!r} and "
|
77
|
+
f"{str(other_handle)!r} overlap"
|
78
|
+
)
|
79
|
+
|
80
|
+
|
56
81
|
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
|
57
82
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
58
83
|
Emitter.__init__(self, interpreter)
|
@@ -71,15 +96,15 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
71
96
|
name=self._counted_name_allocator.allocate(name),
|
72
97
|
body=body,
|
73
98
|
scope=Scope(parent=self._current_scope),
|
74
|
-
|
99
|
+
lambda_external_vars=self._builder.current_block.captured_vars,
|
75
100
|
)
|
76
|
-
return self._create_quantum_function_call(wrapping_function, list())
|
101
|
+
return self._create_quantum_function_call(wrapping_function, list(), None)
|
77
102
|
|
78
103
|
def _emit_quantum_function_call(
|
79
104
|
self,
|
80
105
|
function: FunctionClosure,
|
81
106
|
args: list[ArgValue],
|
82
|
-
propagated_debug_info:
|
107
|
+
propagated_debug_info: FunctionDebugInfo | None,
|
83
108
|
) -> QuantumFunctionCall:
|
84
109
|
call = self._create_quantum_function_call(
|
85
110
|
function, args, propagated_debug_info=propagated_debug_info
|
@@ -87,15 +112,27 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
87
112
|
self.emit_statement(call)
|
88
113
|
return call
|
89
114
|
|
115
|
+
@staticmethod
|
116
|
+
def _get_back_ref(
|
117
|
+
propagated_debug_info: FunctionDebugInfo | None,
|
118
|
+
) -> UUID | None:
|
119
|
+
if propagated_debug_info is None:
|
120
|
+
return None
|
121
|
+
if propagated_debug_info.node is None:
|
122
|
+
return None
|
123
|
+
return propagated_debug_info.node.uuid
|
124
|
+
|
90
125
|
def _create_quantum_function_call(
|
91
126
|
self,
|
92
127
|
function: FunctionClosure,
|
93
128
|
args: list[ArgValue],
|
94
|
-
propagated_debug_info:
|
129
|
+
propagated_debug_info: FunctionDebugInfo | None,
|
95
130
|
) -> QuantumFunctionCall:
|
96
131
|
function = function.clone()
|
97
132
|
function = function.set_depth(self._builder.current_function.depth + 1)
|
133
|
+
self._validate_call_args(function.positional_arg_declarations, args)
|
98
134
|
evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
|
135
|
+
_validate_cloning(evaluated_args)
|
99
136
|
new_declaration = self._prepare_fully_typed_declaration(
|
100
137
|
function, evaluated_args
|
101
138
|
)
|
@@ -114,13 +151,15 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
114
151
|
new_positional_args = self._get_new_positional_args(
|
115
152
|
evaluated_args, is_atomic, new_positional_arg_decls
|
116
153
|
)
|
117
|
-
captured_args = function.captured_vars.get_captured_args(
|
154
|
+
captured_args = function.captured_vars.filter_vars(function).get_captured_args(
|
118
155
|
self._builder.current_function
|
119
156
|
)
|
120
157
|
validate_args_are_not_propagated(new_positional_args, captured_args)
|
121
158
|
new_positional_args.extend(captured_args)
|
122
159
|
new_call = QuantumFunctionCall(
|
123
|
-
function=new_declaration.name,
|
160
|
+
function=new_declaration.name,
|
161
|
+
positional_args=new_positional_args,
|
162
|
+
back_ref=self._get_back_ref(propagated_debug_info),
|
124
163
|
)
|
125
164
|
is_allocate_or_free = new_call.func_name == free.func_decl.name
|
126
165
|
parameters = {
|
@@ -144,6 +183,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
144
183
|
),
|
145
184
|
is_allocate_or_free=is_allocate_or_free,
|
146
185
|
port_to_passed_variable_map=port_to_passed_variable_map,
|
186
|
+
node=new_call._as_back_ref(),
|
147
187
|
)
|
148
188
|
new_call.set_func_decl(new_declaration)
|
149
189
|
return new_call
|
@@ -187,7 +227,10 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
187
227
|
) -> NativeFunctionDefinition:
|
188
228
|
func_def = self._builder.create_definition(function_context)
|
189
229
|
|
190
|
-
|
230
|
+
captured_vars = function_context.closure.captured_vars.filter_vars(
|
231
|
+
function_context.closure
|
232
|
+
)
|
233
|
+
captured_ports = captured_vars.get_captured_ports()
|
191
234
|
if len(captured_ports) == 0:
|
192
235
|
return func_def
|
193
236
|
func_def.positional_arg_declarations = list(
|
@@ -197,7 +240,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
197
240
|
if not function_context.is_lambda:
|
198
241
|
return func_def
|
199
242
|
func_def.body = self.rewrite(
|
200
|
-
func_def.body,
|
243
|
+
func_def.body, captured_vars.get_captured_mapping()
|
201
244
|
)
|
202
245
|
|
203
246
|
return func_def
|
@@ -265,3 +308,29 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
265
308
|
evaluated_args,
|
266
309
|
),
|
267
310
|
)
|
311
|
+
|
312
|
+
def _validate_call_args(
|
313
|
+
self, params: Sequence[PositionalArg], args: list[ArgValue]
|
314
|
+
) -> None:
|
315
|
+
for param, arg in zip(params, args):
|
316
|
+
if not isinstance(param, PortDeclaration) or not isinstance(
|
317
|
+
arg, HandleBinding
|
318
|
+
):
|
319
|
+
continue
|
320
|
+
var_name = arg.name
|
321
|
+
symbol = self._interpreter.evaluate(var_name)
|
322
|
+
if (
|
323
|
+
not isinstance(symbol.value, QuantumSymbol)
|
324
|
+
or symbol.defining_function is None
|
325
|
+
):
|
326
|
+
continue
|
327
|
+
var_state = self._builder.current_block.captured_vars.get_state(
|
328
|
+
var_name, symbol.defining_function
|
329
|
+
)
|
330
|
+
if not var_state and param.direction in (
|
331
|
+
PortDeclarationDirection.Inout,
|
332
|
+
PortDeclarationDirection.Input,
|
333
|
+
):
|
334
|
+
raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
|
335
|
+
if var_state and param.direction == PortDeclarationDirection.Output:
|
336
|
+
raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
2
|
|
3
|
+
from classiq.interface.debug_info.debug_info import new_function_debug_info_by_node
|
3
4
|
from classiq.interface.model.classical_if import ClassicalIf
|
4
5
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
5
6
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
@@ -48,6 +49,8 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
|
|
48
49
|
name=self._counted_name_allocator.allocate("then" if condition else "else"),
|
49
50
|
body=body,
|
50
51
|
scope=Scope(parent=self._current_scope),
|
51
|
-
|
52
|
+
lambda_external_vars=self._builder.current_block.captured_vars,
|
53
|
+
)
|
54
|
+
self._emit_quantum_function_call(
|
55
|
+
then_else_func, list(), new_function_debug_info_by_node(classical_if)
|
52
56
|
)
|
53
|
-
self._emit_quantum_function_call(then_else_func, list())
|
@@ -9,7 +9,10 @@ from typing import (
|
|
9
9
|
|
10
10
|
import sympy
|
11
11
|
|
12
|
-
from classiq.interface.debug_info.debug_info import
|
12
|
+
from classiq.interface.debug_info.debug_info import (
|
13
|
+
DebugInfoCollection,
|
14
|
+
new_function_debug_info_by_node,
|
15
|
+
)
|
13
16
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
14
17
|
from classiq.interface.generator.expressions.evaluated_expression import (
|
15
18
|
EvaluatedExpression,
|
@@ -140,6 +143,10 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
140
143
|
def emit_statement(self, statement: QuantumStatement) -> None:
|
141
144
|
if isinstance(statement, QuantumOperation):
|
142
145
|
self._update_captured_vars(statement)
|
146
|
+
if statement.uuid not in self._interpreter._model.debug_info:
|
147
|
+
self._interpreter._model.debug_info[statement.uuid] = (
|
148
|
+
new_function_debug_info_by_node(statement) # type:ignore[arg-type]
|
149
|
+
)
|
143
150
|
self._builder.emit_statement(statement)
|
144
151
|
|
145
152
|
def _update_captured_vars(self, op: QuantumOperation) -> None:
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from classiq.interface.debug_info.debug_info import new_function_debug_info_by_node
|
1
2
|
from classiq.interface.exceptions import ClassiqExpansionError
|
2
3
|
from classiq.interface.generator.expressions.expression import Expression
|
3
4
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
@@ -50,7 +51,11 @@ class RepeatEmitter(CallEmitter[Repeat]):
|
|
50
51
|
],
|
51
52
|
body=repeat.body,
|
52
53
|
scope=Scope(parent=self._current_scope),
|
53
|
-
|
54
|
+
lambda_external_vars=self._builder.current_block.captured_vars,
|
54
55
|
**extra_args,
|
55
56
|
)
|
56
|
-
self._emit_quantum_function_call(
|
57
|
+
self._emit_quantum_function_call(
|
58
|
+
iteration_function,
|
59
|
+
[Expression(expr=str(i))],
|
60
|
+
new_function_debug_info_by_node(repeat),
|
61
|
+
)
|
@@ -70,7 +70,7 @@ class ShallowEmitter(Emitter[QuantumOperation]):
|
|
70
70
|
expanded_components[handle_name] = self._interpreter.evaluate(
|
71
71
|
handle
|
72
72
|
).value.handle
|
73
|
-
|
73
|
+
expanded_components["back_ref"] = op.uuid
|
74
74
|
op = op.model_copy(update=expanded_components)
|
75
75
|
if isinstance(op, QuantumAssignmentOperation):
|
76
76
|
self._post_process_assignment(op)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
1
2
|
from classiq.interface.model.handle_binding import HandleBinding
|
2
3
|
from classiq.interface.model.variable_declaration_statement import (
|
3
4
|
VariableDeclarationStatement,
|
@@ -12,8 +13,14 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol
|
|
12
13
|
|
13
14
|
class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement]):
|
14
15
|
def emit(self, variable_declaration: VariableDeclarationStatement, /) -> None:
|
15
|
-
var_decl = variable_declaration.model_copy(
|
16
|
+
var_decl = variable_declaration.model_copy(
|
17
|
+
update=dict(back_ref=variable_declaration.uuid)
|
18
|
+
)
|
16
19
|
var_decl.quantum_type = variable_declaration.quantum_type.model_copy()
|
20
|
+
if variable_declaration.name in self._current_scope:
|
21
|
+
raise ClassiqExpansionError(
|
22
|
+
f"Variable {variable_declaration.name!r} is already defined"
|
23
|
+
)
|
17
24
|
self._current_scope[variable_declaration.name] = Evaluated(
|
18
25
|
value=QuantumSymbol(
|
19
26
|
handle=HandleBinding(name=var_decl.name),
|
@@ -25,4 +32,7 @@ class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement])
|
|
25
32
|
),
|
26
33
|
defining_function=self._builder.current_function,
|
27
34
|
)
|
35
|
+
self._builder.current_block.captured_vars.init_var(
|
36
|
+
var_decl.name, self._builder.current_function
|
37
|
+
)
|
28
38
|
self.emit_statement(var_decl)
|
@@ -1,3 +1,7 @@
|
|
1
|
+
from .amplitude_amplification import (
|
2
|
+
amplitude_amplification,
|
3
|
+
exact_amplitude_amplification,
|
4
|
+
)
|
1
5
|
from .amplitude_estimation import *
|
2
6
|
from .discrete_sine_cosine_transform import *
|
3
7
|
from .discrete_sine_cosine_transform import _qct_d_operator, _qct_pi_operator
|
@@ -23,6 +27,8 @@ OPEN_LIBRARY_FUNCTIONS = [
|
|
23
27
|
_single_pauli,
|
24
28
|
linear_pauli_rotations,
|
25
29
|
amplitude_estimation,
|
30
|
+
amplitude_amplification,
|
31
|
+
exact_amplitude_amplification,
|
26
32
|
phase_oracle,
|
27
33
|
reflect_about_zero,
|
28
34
|
grover_diffuser,
|
@@ -78,12 +84,14 @@ OPEN_LIBRARY_FUNCTIONS = [
|
|
78
84
|
__all__ = [
|
79
85
|
"_single_pauli",
|
80
86
|
"allocate_num",
|
87
|
+
"amplitude_amplification",
|
81
88
|
"amplitude_estimation",
|
82
89
|
"apply_to_all",
|
83
90
|
"c_modular_multiply",
|
84
91
|
"cc_modular_add",
|
85
92
|
"encode_in_angle",
|
86
93
|
"encode_on_bloch",
|
94
|
+
"exact_amplitude_amplification",
|
87
95
|
"full_hea",
|
88
96
|
"grover_diffuser",
|
89
97
|
"grover_operator",
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from classiq.open_library.functions.grover import grover_operator
|
2
|
+
from classiq.qmod.builtins.functions.standard_gates import RY
|
3
|
+
from classiq.qmod.builtins.operations import (
|
4
|
+
allocate,
|
5
|
+
bind,
|
6
|
+
control,
|
7
|
+
repeat,
|
8
|
+
within_apply,
|
9
|
+
)
|
10
|
+
from classiq.qmod.cparam import CInt, CReal
|
11
|
+
from classiq.qmod.qfunc import qfunc
|
12
|
+
from classiq.qmod.qmod_variable import QArray, QBit
|
13
|
+
from classiq.qmod.quantum_callable import QCallable
|
14
|
+
from classiq.qmod.symbolic import acos, asin, ceiling, pi, sin
|
15
|
+
|
16
|
+
|
17
|
+
@qfunc
|
18
|
+
def amplitude_amplification(
|
19
|
+
reps: CInt,
|
20
|
+
oracle: QCallable[QArray[QBit]],
|
21
|
+
space_transform: QCallable[QArray[QBit]],
|
22
|
+
packed_qvars: QArray[QBit],
|
23
|
+
) -> None:
|
24
|
+
"""
|
25
|
+
[Qmod Classiq-library function]
|
26
|
+
|
27
|
+
Applies the Amplitude Amplification algorithm (QAE); Prepares a state using the given `space_transform` function, and applies `reps` repetititions
|
28
|
+
of the grover operator, using the given `oracle` functions which marks the "good" states.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
reps: Number of repetitions to apply the grover operator on the initial state. Should be determined by the user, according to the calculated amplification.
|
32
|
+
oracle: The oracle operator that marks the "good" states. This operator should flip the sign of the amplitude of the "good" state.
|
33
|
+
space_transform: The space transform operator (which is known also the state preparation operator). First applied to prepare the state before the amplification, then used inside the Grover operator.
|
34
|
+
packed_vars: The variable that holds the state to be amplified. Assumed to be in the zero state at the beginning of the algorithm.
|
35
|
+
"""
|
36
|
+
space_transform(packed_qvars)
|
37
|
+
repeat(reps, lambda index: grover_operator(oracle, space_transform, packed_qvars))
|
38
|
+
|
39
|
+
|
40
|
+
@qfunc
|
41
|
+
def exact_amplitude_amplification(
|
42
|
+
amplitude: CReal,
|
43
|
+
oracle: QCallable[QArray[QBit]],
|
44
|
+
space_transform: QCallable[QArray[QBit]],
|
45
|
+
packed_qvars: QArray[QBit],
|
46
|
+
) -> None:
|
47
|
+
"""
|
48
|
+
[Qmod Classiq-library function]
|
49
|
+
|
50
|
+
Applies an exact version of the Amplitude Amplification algorithm (QAE), assuming knowledge of the amplitude of the marked state.
|
51
|
+
The function should be applied on the zero state, and it takes care for preparing the initial state before amplification using the `space_transform`.
|
52
|
+
|
53
|
+
Based on the algorithm in [Quantum state preparation without coherent arithmetic](https://arxiv.org/abs/2210.14892).
|
54
|
+
|
55
|
+
Assuming the `space_transform` creates a state $|\\psi\\rangle = a|\\psi_{good}\\rangle + \\sqrt{1-a}|\\psi_{bad}\\rangle$, given `a` as the `amplitude`
|
56
|
+
argument, the function will load exactly the state $|\\psi_{good}\\rangle$.
|
57
|
+
|
58
|
+
Note: if the `amplitude` argument is not exact, the resulting state will not be exactly $|\\psi_{good}\\rangle$, and there will be additional internal auxilliary of the function that is not released correctly.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
amplitude: The amplitude of the state $|\\psi_{good}\\rangle$ with regards to the initial state prepared by `space_transform`.
|
62
|
+
oracle: The oracle operator that marks the "good" states. This operator should flip the sign of the amplitude of the "good" state.
|
63
|
+
space_transform: The space transform operator (which is known also the state preparation operator). First applied to prepare the state before the amplification, then used inside the Grover operator.
|
64
|
+
packed_vars: The variable that holds the state to be amplified. Assumed to be in the zero state at the beginning of the algorithm.
|
65
|
+
"""
|
66
|
+
aux = QBit()
|
67
|
+
k = ceiling((pi / (4 * asin(amplitude))) - 0.5)
|
68
|
+
theta = pi / (4 * k + 2)
|
69
|
+
rot_phase = 2 * acos(sin(theta) / amplitude)
|
70
|
+
|
71
|
+
extended_qvars: QArray = QArray("extended_qvars")
|
72
|
+
within_apply(
|
73
|
+
lambda: [ # type:ignore[arg-type]
|
74
|
+
allocate(aux),
|
75
|
+
bind(
|
76
|
+
[aux, packed_qvars], extended_qvars
|
77
|
+
), # type:ignore[func-returns-value]
|
78
|
+
],
|
79
|
+
lambda: amplitude_amplification(
|
80
|
+
k,
|
81
|
+
lambda qvars_: control(
|
82
|
+
qvars_[0] == 0, lambda: oracle(qvars_[1 : qvars_.size])
|
83
|
+
),
|
84
|
+
lambda qvars_: [
|
85
|
+
space_transform( # type:ignore[func-returns-value]
|
86
|
+
qvars_[1 : qvars_.size]
|
87
|
+
),
|
88
|
+
RY(rot_phase, qvars_[0]),
|
89
|
+
],
|
90
|
+
extended_qvars,
|
91
|
+
),
|
92
|
+
)
|
@@ -56,8 +56,8 @@ def reflect_about_zero(packed_vars: QArray[QBit]) -> None:
|
|
56
56
|
besides the |0> state). Implements the operator $S_0$:
|
57
57
|
|
58
58
|
$$
|
59
|
-
|
60
|
-
S_0|{x}
|
59
|
+
\\begin{equation}
|
60
|
+
S_0|{x}\\rangle = (-1)^{(x\\ne0)}|{x}\\rangle= (2|{0}\\rangle\\langle{0}|-I)|{x}\\rangle
|
61
61
|
\\end{equation}
|
62
62
|
$$
|
63
63
|
|
@@ -85,7 +85,7 @@ def grover_diffuser(
|
|
85
85
|
is the `space_transform` parameter. It is defined as:
|
86
86
|
|
87
87
|
$$
|
88
|
-
|
88
|
+
\\begin{equation}
|
89
89
|
D = A S_0 A^{\\dagger}
|
90
90
|
\\end{equation}
|
91
91
|
$$
|
@@ -118,11 +118,11 @@ def grover_operator(
|
|
118
118
|
$$
|
119
119
|
|
120
120
|
where $S_{\\psi_1}$ is a reflection about marked states, and $S_{\\psi_0}$ is a reflection
|
121
|
-
about a given state defined by $|\\psi_0
|
121
|
+
about a given state defined by $|\\psi_0\\rangle = A|0\\rangle$.
|
122
122
|
|
123
123
|
Args:
|
124
124
|
oracle: A unitary operator which adds a phase of (-1) to marked states.
|
125
|
-
space_transform: The operator which creates $|\\psi_0
|
125
|
+
space_transform: The operator which creates $|\\psi_0\\rangle$, the initial state, used by the diffuser to reflect about it.
|
126
126
|
packed_vars: The state to which to apply the grover operator.
|
127
127
|
"""
|
128
128
|
oracle(packed_vars)
|
@@ -4,6 +4,7 @@ from .benchmarking import *
|
|
4
4
|
from .chemistry import *
|
5
5
|
from .exponentiation import *
|
6
6
|
from .finance import *
|
7
|
+
from .mid_circuit_measurement import *
|
7
8
|
from .operators import *
|
8
9
|
from .qsvm import *
|
9
10
|
from .standard_gates import *
|
@@ -66,6 +67,7 @@ CORE_LIB_DECLS = [
|
|
66
67
|
suzuki_trotter,
|
67
68
|
qdrift,
|
68
69
|
exponentiation_with_depth_constraint,
|
70
|
+
RESET,
|
69
71
|
)
|
70
72
|
]
|
71
73
|
|
@@ -129,6 +131,7 @@ __all__ = [ # noqa: RUF022
|
|
129
131
|
"single_pauli_exponent",
|
130
132
|
"suzuki_trotter",
|
131
133
|
"unitary",
|
134
|
+
"RESET",
|
132
135
|
]
|
133
136
|
BUILTIN_FUNCTION_DECLARATIONS = {
|
134
137
|
func_decl.name: func_decl for func_decl in STD_QMOD_OPERATORS + CORE_LIB_DECLS
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from classiq.qmod.qfunc import qfunc
|
2
|
+
from classiq.qmod.qmod_variable import QBit
|
3
|
+
|
4
|
+
|
5
|
+
@qfunc(external=True)
|
6
|
+
def RESET(target: QBit) -> None:
|
7
|
+
"""
|
8
|
+
Resets the target qubit to the |0> state.
|
9
|
+
|
10
|
+
Performed by measuring the qubit and applying an X gate if necessary.
|
11
|
+
|
12
|
+
Args:
|
13
|
+
target: the qubit to reset
|
14
|
+
"""
|
15
|
+
pass
|
classiq/qmod/quantum_function.py
CHANGED
@@ -31,6 +31,7 @@ from classiq.qmod.qmod_parameter import CArray, CParam
|
|
31
31
|
from classiq.qmod.qmod_variable import QVar
|
32
32
|
from classiq.qmod.quantum_callable import QCallable, QCallableList
|
33
33
|
from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
|
34
|
+
from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
|
34
35
|
from classiq.qmod.utilities import mangle_keyword
|
35
36
|
|
36
37
|
|
@@ -161,6 +162,9 @@ class QFunc(BaseQFunc):
|
|
161
162
|
)
|
162
163
|
|
163
164
|
generative_functions = list(self._qmodule.generative_functions.values())
|
165
|
+
QStructAnnotator().visit(model_stub)
|
166
|
+
for gen_func in generative_functions:
|
167
|
+
QStructAnnotator().visit(gen_func.func_decl)
|
164
168
|
resolve_function_calls(
|
165
169
|
model_stub,
|
166
170
|
dict(model_stub.function_dict)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from collections.abc import Iterator, Mapping
|
2
2
|
from contextlib import contextmanager
|
3
|
-
from typing import Any
|
3
|
+
from typing import Any, Optional
|
4
4
|
|
5
5
|
from classiq.interface.exceptions import ClassiqError
|
6
6
|
from classiq.interface.generator.expressions.expression import Expression
|
@@ -11,6 +11,7 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
11
11
|
from classiq.interface.model.classical_parameter_declaration import (
|
12
12
|
ClassicalParameterDeclaration,
|
13
13
|
)
|
14
|
+
from classiq.interface.model.model import Model
|
14
15
|
from classiq.interface.model.model_visitor import ModelVisitor
|
15
16
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
16
17
|
from classiq.interface.model.port_declaration import PortDeclaration
|
@@ -108,8 +109,13 @@ class _CallLambdaAnnotator(ModelVisitor):
|
|
108
109
|
|
109
110
|
def resolve_function_calls(
|
110
111
|
root: Any,
|
111
|
-
quantum_function_dict: Mapping[str, QuantumFunctionDeclaration],
|
112
|
+
quantum_function_dict: Optional[Mapping[str, QuantumFunctionDeclaration]] = None,
|
112
113
|
) -> None:
|
114
|
+
if quantum_function_dict is None:
|
115
|
+
quantum_function_dict = {}
|
116
|
+
quantum_function_dict = dict(quantum_function_dict)
|
117
|
+
if isinstance(root, Model):
|
118
|
+
quantum_function_dict |= root.function_dict
|
113
119
|
all_functions: Mapping[str, QuantumFunctionDeclaration] = {
|
114
120
|
**BUILTIN_FUNCTION_DECLARATIONS,
|
115
121
|
**quantum_function_dict,
|
@@ -0,0 +1,9 @@
|
|
1
|
+
from classiq.interface.model.model import Model
|
2
|
+
|
3
|
+
from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
|
4
|
+
from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
|
5
|
+
|
6
|
+
|
7
|
+
def annotate_model(model: Model) -> None:
|
8
|
+
QStructAnnotator().visit(model)
|
9
|
+
resolve_function_calls(model)
|