classiq 0.66.1__py3-none-any.whl → 0.68.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/_internals/api_wrapper.py +5 -1
- classiq/_internals/async_utils.py +1 -1
- classiq/_internals/authentication/password_manager.py +1 -1
- classiq/_internals/client.py +1 -1
- 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/execution/execution_session.py +7 -3
- classiq/executor.py +7 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/ast_node.py +1 -1
- classiq/interface/chemistry/operator.py +1 -1
- classiq/interface/debug_info/debug_info.py +27 -0
- classiq/interface/exceptions.py +2 -5
- classiq/interface/generator/arith/argument_utils.py +1 -1
- classiq/interface/generator/arith/arithmetic.py +99 -2
- classiq/interface/generator/arith/arithmetic_expression_parser.py +1 -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 +38 -3
- 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/quantum_program.py +18 -1
- classiq/interface/generator/reset.py +14 -0
- classiq/interface/generator/types/enum_declaration.py +33 -2
- classiq/interface/generator/ucc.py +1 -1
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/classical_if.py +2 -2
- classiq/interface/model/control.py +2 -2
- classiq/interface/model/invert.py +2 -2
- classiq/interface/model/power.py +2 -2
- classiq/interface/model/quantum_function_call.py +4 -0
- classiq/interface/model/quantum_statement.py +13 -0
- classiq/interface/model/repeat.py +2 -2
- classiq/interface/model/statement_block.py +1 -1
- classiq/interface/model/within_apply_operation.py +2 -2
- 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 +87 -26
- classiq/model_expansions/quantum_operations/allocate.py +9 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -0
- classiq/model_expansions/quantum_operations/bind.py +67 -14
- classiq/model_expansions/quantum_operations/block_evaluator.py +76 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
- classiq/model_expansions/quantum_operations/classicalif.py +10 -6
- classiq/model_expansions/quantum_operations/composite_emitter.py +27 -0
- classiq/model_expansions/quantum_operations/emitter.py +24 -3
- classiq/model_expansions/quantum_operations/expression_evaluator.py +33 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +28 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -2
- classiq/model_expansions/quantum_operations/repeat.py +9 -3
- classiq/model_expansions/quantum_operations/variable_decleration.py +13 -2
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +1 -1
- classiq/open_library/functions/__init__.py +1 -2
- classiq/open_library/functions/amplitude_amplification.py +12 -9
- classiq/open_library/functions/discrete_sine_cosine_transform.py +16 -13
- classiq/open_library/functions/grover.py +11 -15
- classiq/open_library/functions/modular_exponentiation.py +7 -13
- classiq/open_library/functions/state_preparation.py +16 -17
- classiq/open_library/functions/swap_test.py +1 -1
- classiq/open_library/functions/utility_functions.py +10 -2
- classiq/qmod/builtins/functions/__init__.py +3 -0
- classiq/qmod/builtins/functions/chemistry.py +6 -38
- classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
- classiq/qmod/quantum_expandable.py +30 -6
- 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/qmod/semantics/validation/types_validation.py +1 -1
- classiq/qmod/symbolic.py +2 -1
- classiq/qmod/utilities.py +2 -1
- classiq/qmod/write_qmod.py +10 -7
- classiq/synthesis.py +20 -7
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/METADATA +1 -1
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/RECORD +86 -81
- classiq/model_expansions/quantum_operations/shallow_emitter.py +0 -166
- classiq/qmod/semantics/validation/func_call_validation.py +0 -99
- classiq/qmod/semantics/validation/handle_validation.py +0 -85
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
from collections.abc import Sequence
|
2
|
+
from typing import TYPE_CHECKING
|
3
|
+
|
4
|
+
from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
|
5
|
+
|
6
|
+
from classiq.model_expansions.closure import Closure
|
7
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
8
|
+
from classiq.model_expansions.scope import Scope
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
12
|
+
|
13
|
+
|
14
|
+
_BLOCK_RENAMES = {
|
15
|
+
"compute": "within",
|
16
|
+
"action": "apply",
|
17
|
+
}
|
18
|
+
_REVERSE_BLOCK_RENAMES = {rename: name for name, rename in _BLOCK_RENAMES.items()}
|
19
|
+
|
20
|
+
|
21
|
+
class BlockEvaluator(Emitter[QuantumOperation]):
|
22
|
+
def __init__(
|
23
|
+
self, interpreter: "BaseInterpreter", operation_name: str, *block_names: str
|
24
|
+
) -> None:
|
25
|
+
super().__init__(interpreter)
|
26
|
+
self._operation_name = operation_name
|
27
|
+
self._block_names: Sequence[str] = block_names
|
28
|
+
|
29
|
+
def emit(self, op: QuantumOperation, /) -> bool:
|
30
|
+
expanded_blocks: dict[str, list[QuantumStatement]] = {}
|
31
|
+
blocks = [
|
32
|
+
block
|
33
|
+
for block in self._block_names
|
34
|
+
if hasattr(op, block) and getattr(op, block) is not None
|
35
|
+
]
|
36
|
+
|
37
|
+
if len(blocks) > 0:
|
38
|
+
if op.is_generative():
|
39
|
+
expanded_blocks = self.expand_generative_blocks(op)
|
40
|
+
else:
|
41
|
+
expanded_blocks = self.expand_blocks(op, blocks)
|
42
|
+
expanded_blocks.update(expanded_blocks)
|
43
|
+
|
44
|
+
op = op.model_copy(update={**expanded_blocks, "back_ref": op.uuid})
|
45
|
+
self._builder.emit_statement(op)
|
46
|
+
return True
|
47
|
+
|
48
|
+
def expand_blocks(
|
49
|
+
self, op: QuantumOperation, block_names: list[str]
|
50
|
+
) -> dict[str, list[QuantumStatement]]:
|
51
|
+
blocks = {
|
52
|
+
_BLOCK_RENAMES.get(block, block): getattr(op, block)
|
53
|
+
for block in block_names
|
54
|
+
}
|
55
|
+
block_closure = Closure(
|
56
|
+
name=self._operation_name,
|
57
|
+
scope=Scope(parent=self._current_scope),
|
58
|
+
blocks=blocks,
|
59
|
+
)
|
60
|
+
context = self._expand_operation(block_closure)
|
61
|
+
return {
|
62
|
+
block: context.statements(_BLOCK_RENAMES.get(block, block))
|
63
|
+
for block in block_names
|
64
|
+
}
|
65
|
+
|
66
|
+
def expand_generative_blocks(
|
67
|
+
self, op: QuantumOperation
|
68
|
+
) -> dict[str, list[QuantumStatement]]:
|
69
|
+
blocks = [
|
70
|
+
block for block in self._block_names if op.has_generative_block(block)
|
71
|
+
]
|
72
|
+
context = self._expand_generative_context(op, self._operation_name, blocks)
|
73
|
+
return {
|
74
|
+
_REVERSE_BLOCK_RENAMES.get(block, block): context.statements(block)
|
75
|
+
for block in blocks
|
76
|
+
}
|
@@ -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
|
@@ -17,7 +18,7 @@ def _is_all_identity_calls(body: Sequence[QuantumStatement]) -> bool:
|
|
17
18
|
|
18
19
|
|
19
20
|
class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
|
20
|
-
def emit(self, classical_if: ClassicalIf, /) ->
|
21
|
+
def emit(self, classical_if: ClassicalIf, /) -> bool:
|
21
22
|
condition = self._interpreter.evaluate(classical_if.condition).as_type(bool)
|
22
23
|
op_name = "then" if condition else "else"
|
23
24
|
is_generative = classical_if.is_generative()
|
@@ -25,7 +26,7 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
|
|
25
26
|
body: Sequence[QuantumStatement]
|
26
27
|
if is_generative:
|
27
28
|
if not classical_if.has_generative_block(op_name):
|
28
|
-
return
|
29
|
+
return True
|
29
30
|
context = self._expand_generative_context(classical_if, op_name, op_name)
|
30
31
|
context.blocks["body"] = context.blocks[op_name]
|
31
32
|
context.blocks.pop(op_name)
|
@@ -34,7 +35,7 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
|
|
34
35
|
body = classical_if.then if condition else classical_if.else_
|
35
36
|
|
36
37
|
if _is_all_identity_calls(body):
|
37
|
-
return
|
38
|
+
return True
|
38
39
|
|
39
40
|
if is_generative or not self._should_wrap(body):
|
40
41
|
for stmt in body:
|
@@ -42,12 +43,15 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
|
|
42
43
|
self._interpreter._builder.emit_statement(stmt)
|
43
44
|
else:
|
44
45
|
self._interpreter.emit_statement(stmt)
|
45
|
-
return
|
46
|
+
return True
|
46
47
|
|
47
48
|
then_else_func = FunctionClosure.create(
|
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,
|
52
53
|
)
|
53
|
-
self._emit_quantum_function_call(
|
54
|
+
self._emit_quantum_function_call(
|
55
|
+
then_else_func, list(), new_function_debug_info_by_node(classical_if)
|
56
|
+
)
|
57
|
+
return True
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from collections.abc import Sequence
|
2
|
+
from typing import TYPE_CHECKING, Generic
|
3
|
+
|
4
|
+
from classiq.model_expansions.quantum_operations.emitter import (
|
5
|
+
Emitter,
|
6
|
+
QuantumStatementT,
|
7
|
+
)
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
11
|
+
|
12
|
+
|
13
|
+
class CompositeEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT]):
|
14
|
+
def __init__(
|
15
|
+
self,
|
16
|
+
interpreter: "BaseInterpreter",
|
17
|
+
emitters: Sequence[Emitter[QuantumStatementT]],
|
18
|
+
) -> None:
|
19
|
+
super().__init__(interpreter)
|
20
|
+
self._emitters = emitters
|
21
|
+
|
22
|
+
def emit(self, statement: QuantumStatementT, /) -> bool:
|
23
|
+
for emitter in self._emitters:
|
24
|
+
if emitter.emit(statement):
|
25
|
+
return True
|
26
|
+
self._builder.emit_statement(statement)
|
27
|
+
return True
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import ast
|
1
2
|
from abc import ABC, abstractmethod
|
2
3
|
from typing import (
|
3
4
|
TYPE_CHECKING,
|
@@ -9,7 +10,10 @@ from typing import (
|
|
9
10
|
|
10
11
|
import sympy
|
11
12
|
|
12
|
-
from classiq.interface.debug_info.debug_info import
|
13
|
+
from classiq.interface.debug_info.debug_info import (
|
14
|
+
DebugInfoCollection,
|
15
|
+
new_function_debug_info_by_node,
|
16
|
+
)
|
13
17
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
14
18
|
from classiq.interface.generator.expressions.evaluated_expression import (
|
15
19
|
EvaluatedExpression,
|
@@ -36,12 +40,15 @@ from classiq.model_expansions.sympy_conversion.sympy_to_python import (
|
|
36
40
|
translate_sympy_quantum_expression,
|
37
41
|
)
|
38
42
|
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
43
|
+
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
39
44
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
40
45
|
|
41
46
|
if TYPE_CHECKING:
|
42
47
|
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
43
48
|
|
44
|
-
QuantumStatementT = TypeVar(
|
49
|
+
QuantumStatementT = TypeVar(
|
50
|
+
"QuantumStatementT", bound=QuantumStatement, contravariant=True
|
51
|
+
)
|
45
52
|
|
46
53
|
|
47
54
|
class Emitter(Generic[QuantumStatementT], ABC):
|
@@ -57,7 +64,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
57
64
|
)
|
58
65
|
|
59
66
|
@abstractmethod
|
60
|
-
def emit(self, statement: QuantumStatementT, /) ->
|
67
|
+
def emit(self, statement: QuantumStatementT, /) -> bool:
|
61
68
|
pass
|
62
69
|
|
63
70
|
def _expand_operation(self, closure: Closure) -> OperationContext:
|
@@ -140,6 +147,10 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
140
147
|
def emit_statement(self, statement: QuantumStatement) -> None:
|
141
148
|
if isinstance(statement, QuantumOperation):
|
142
149
|
self._update_captured_vars(statement)
|
150
|
+
if statement.uuid not in self._interpreter._model.debug_info:
|
151
|
+
self._interpreter._model.debug_info[statement.uuid] = (
|
152
|
+
new_function_debug_info_by_node(statement) # type:ignore[arg-type]
|
153
|
+
)
|
143
154
|
self._builder.emit_statement(statement)
|
144
155
|
|
145
156
|
def _update_captured_vars(self, op: QuantumOperation) -> None:
|
@@ -166,3 +177,13 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
166
177
|
defining_function=defining_function,
|
167
178
|
direction=direction,
|
168
179
|
)
|
180
|
+
|
181
|
+
def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
|
182
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
183
|
+
vrc.visit(ast.parse(expr.expr))
|
184
|
+
handles = dict.fromkeys(
|
185
|
+
handle
|
186
|
+
for handle in vrc.var_handles
|
187
|
+
if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
|
188
|
+
)
|
189
|
+
return [self._interpreter.evaluate(handle).value for handle in handles]
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.generator.functions.port_declaration import (
|
5
|
+
PortDeclarationDirection,
|
6
|
+
)
|
7
|
+
from classiq.interface.model.quantum_statement import QuantumOperation
|
8
|
+
|
9
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
13
|
+
|
14
|
+
|
15
|
+
class ExpressionEvaluator(Emitter[QuantumOperation]):
|
16
|
+
def __init__(self, interpreter: "BaseInterpreter", expression_name: str) -> None:
|
17
|
+
super().__init__(interpreter)
|
18
|
+
self._expression_name = expression_name
|
19
|
+
|
20
|
+
def emit(self, op: QuantumOperation, /) -> bool:
|
21
|
+
expression = getattr(op, self._expression_name)
|
22
|
+
if not isinstance(expression, Expression) or expression.is_evaluated():
|
23
|
+
return False
|
24
|
+
evaluated_expression = self._evaluate_expression(
|
25
|
+
expression, preserve_bool_ops=True
|
26
|
+
)
|
27
|
+
for symbol in self._get_symbols_in_expression(evaluated_expression):
|
28
|
+
self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
|
29
|
+
op = op.model_copy(
|
30
|
+
update={self._expression_name: evaluated_expression, "back_ref": op.uuid}
|
31
|
+
)
|
32
|
+
self._interpreter.emit(op)
|
33
|
+
return True
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
4
|
+
from classiq.interface.model.quantum_statement import QuantumOperation
|
5
|
+
|
6
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
10
|
+
|
11
|
+
|
12
|
+
class HandleEvaluator(Emitter[QuantumOperation]):
|
13
|
+
def __init__(self, interpreter: "BaseInterpreter", handle_name: str) -> None:
|
14
|
+
super().__init__(interpreter)
|
15
|
+
self._handle_name = handle_name
|
16
|
+
|
17
|
+
def emit(self, op: QuantumOperation, /) -> bool:
|
18
|
+
handle = getattr(op, self._handle_name)
|
19
|
+
if not isinstance(handle, HandleBinding):
|
20
|
+
return False
|
21
|
+
evaluated_handle = self._interpreter.evaluate(handle).value.handle
|
22
|
+
if handle == evaluated_handle:
|
23
|
+
return False
|
24
|
+
op = op.model_copy(
|
25
|
+
update={self._handle_name: evaluated_handle, "back_ref": op.uuid}
|
26
|
+
)
|
27
|
+
self._interpreter.emit(op)
|
28
|
+
return True
|
@@ -28,16 +28,17 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
28
28
|
super().__init__(interpreter)
|
29
29
|
self._model = self._interpreter._model
|
30
30
|
|
31
|
-
def emit(self, call: QuantumFunctionCall, /) ->
|
31
|
+
def emit(self, call: QuantumFunctionCall, /) -> bool:
|
32
32
|
if call.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
|
33
33
|
self._allocate_compatibility(call)
|
34
|
-
return
|
34
|
+
return True
|
35
35
|
function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
|
36
36
|
args = call.positional_args
|
37
37
|
with ErrorManager().call(function.name):
|
38
38
|
self._emit_quantum_function_call(
|
39
39
|
function, args, self._debug_info.get(call.uuid)
|
40
40
|
)
|
41
|
+
return True
|
41
42
|
|
42
43
|
def _allocate_compatibility(self, call: QuantumFunctionCall) -> None:
|
43
44
|
if len(call.positional_args) != 2:
|
@@ -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 (
|
@@ -16,7 +17,7 @@ from classiq.qmod.quantum_function import GenerativeQFunc
|
|
16
17
|
|
17
18
|
|
18
19
|
class RepeatEmitter(CallEmitter[Repeat]):
|
19
|
-
def emit(self, repeat: Repeat, /) ->
|
20
|
+
def emit(self, repeat: Repeat, /) -> bool:
|
20
21
|
count = self._interpreter.evaluate(repeat.count).as_type(int)
|
21
22
|
if count < 0:
|
22
23
|
raise ClassiqExpansionError(
|
@@ -25,6 +26,7 @@ class RepeatEmitter(CallEmitter[Repeat]):
|
|
25
26
|
op_name = self._counted_name_allocator.allocate(REPEAT_OPERATOR_NAME)
|
26
27
|
for i in range(count):
|
27
28
|
self._emit_iteration(repeat, i, op_name)
|
29
|
+
return True
|
28
30
|
|
29
31
|
def _emit_iteration(self, repeat: Repeat, i: int, op_name: str) -> None:
|
30
32
|
closure_constructor: type[FunctionClosure]
|
@@ -50,7 +52,11 @@ class RepeatEmitter(CallEmitter[Repeat]):
|
|
50
52
|
],
|
51
53
|
body=repeat.body,
|
52
54
|
scope=Scope(parent=self._current_scope),
|
53
|
-
|
55
|
+
lambda_external_vars=self._builder.current_block.captured_vars,
|
54
56
|
**extra_args,
|
55
57
|
)
|
56
|
-
self._emit_quantum_function_call(
|
58
|
+
self._emit_quantum_function_call(
|
59
|
+
iteration_function,
|
60
|
+
[Expression(expr=str(i))],
|
61
|
+
new_function_debug_info_by_node(repeat),
|
62
|
+
)
|
@@ -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,
|
@@ -11,9 +12,15 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol
|
|
11
12
|
|
12
13
|
|
13
14
|
class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement]):
|
14
|
-
def emit(self, variable_declaration: VariableDeclarationStatement, /) ->
|
15
|
-
var_decl = variable_declaration.model_copy(
|
15
|
+
def emit(self, variable_declaration: VariableDeclarationStatement, /) -> bool:
|
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,8 @@ 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)
|
39
|
+
return True
|
@@ -174,6 +174,6 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
|
|
174
174
|
def visit_Attribute(self, node: ast.Attribute) -> ast.Call:
|
175
175
|
return ast.Call(
|
176
176
|
func=ast.Name("get_field"),
|
177
|
-
args=[node.value, ast.Constant(value=node.attr)],
|
177
|
+
args=[self.visit(node.value), ast.Constant(value=node.attr)],
|
178
178
|
keywords=[],
|
179
179
|
)
|
@@ -10,7 +10,7 @@ from .hea import *
|
|
10
10
|
from .linear_pauli_rotation import *
|
11
11
|
from .linear_pauli_rotation import _single_pauli
|
12
12
|
from .modular_exponentiation import *
|
13
|
-
from .modular_exponentiation import _check_msb
|
13
|
+
from .modular_exponentiation import _check_msb
|
14
14
|
from .qaoa_penalty import *
|
15
15
|
from .qft_functions import *
|
16
16
|
from .qpe import *
|
@@ -72,7 +72,6 @@ OPEN_LIBRARY_FUNCTIONS = [
|
|
72
72
|
qst_type2,
|
73
73
|
modular_increment,
|
74
74
|
qft,
|
75
|
-
_ctrl_x,
|
76
75
|
_prepare_uniform_trimmed_state_step,
|
77
76
|
_qct_d_operator,
|
78
77
|
_qct_pi_operator,
|
@@ -4,7 +4,7 @@ from classiq.qmod.builtins.operations import (
|
|
4
4
|
allocate,
|
5
5
|
bind,
|
6
6
|
control,
|
7
|
-
|
7
|
+
power,
|
8
8
|
within_apply,
|
9
9
|
)
|
10
10
|
from classiq.qmod.cparam import CInt, CReal
|
@@ -24,7 +24,7 @@ def amplitude_amplification(
|
|
24
24
|
"""
|
25
25
|
[Qmod Classiq-library function]
|
26
26
|
|
27
|
-
Applies the Amplitude Amplification algorithm
|
27
|
+
Applies the Amplitude Amplification algorithm; Prepares a state using the given `space_transform` function, and applies `reps` repetititions
|
28
28
|
of the grover operator, using the given `oracle` functions which marks the "good" states.
|
29
29
|
|
30
30
|
Args:
|
@@ -34,7 +34,10 @@ def amplitude_amplification(
|
|
34
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
35
|
"""
|
36
36
|
space_transform(packed_qvars)
|
37
|
-
|
37
|
+
power(
|
38
|
+
reps,
|
39
|
+
lambda: grover_operator(oracle, space_transform, packed_qvars),
|
40
|
+
)
|
38
41
|
|
39
42
|
|
40
43
|
@qfunc
|
@@ -47,18 +50,18 @@ def exact_amplitude_amplification(
|
|
47
50
|
"""
|
48
51
|
[Qmod Classiq-library function]
|
49
52
|
|
50
|
-
Applies an exact version of the Amplitude Amplification algorithm
|
53
|
+
Applies an exact version of the Amplitude Amplification algorithm, assuming knowledge of the amplitude of the marked state.
|
51
54
|
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
55
|
|
53
56
|
Based on the algorithm in [Quantum state preparation without coherent arithmetic](https://arxiv.org/abs/2210.14892).
|
54
57
|
|
55
|
-
Assuming the `space_transform` creates a state $|\\psi
|
56
|
-
argument, the function will load exactly the state $|\\
|
58
|
+
Assuming the `space_transform` creates a state $|\\psi\\rangle = a|\\psi_{good}\\rangle + \\sqrt{1-a}|\\psi_{bad}\\rangle$, given `a` as the `amplitude`
|
59
|
+
argument, the function will load exactly the state $|\\psi_{good}\\rangle$.
|
57
60
|
|
58
|
-
Note: if the `amplitude` argument is not exact, the resulting state will not be exactly $|\\
|
61
|
+
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
62
|
|
60
63
|
Args:
|
61
|
-
amplitude: The amplitude of the state $|\\
|
64
|
+
amplitude: The amplitude of the state $|\\psi_{good}\\rangle$ with regards to the initial state prepared by `space_transform`.
|
62
65
|
oracle: The oracle operator that marks the "good" states. This operator should flip the sign of the amplitude of the "good" state.
|
63
66
|
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
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.
|
@@ -68,7 +71,7 @@ def exact_amplitude_amplification(
|
|
68
71
|
theta = pi / (4 * k + 2)
|
69
72
|
rot_phase = 2 * acos(sin(theta) / amplitude)
|
70
73
|
|
71
|
-
extended_qvars: QArray = QArray(
|
74
|
+
extended_qvars: QArray = QArray()
|
72
75
|
within_apply(
|
73
76
|
lambda: [ # type:ignore[arg-type]
|
74
77
|
allocate(aux),
|