classiq 0.45.1__py3-none-any.whl → 0.46.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/__init__.py +0 -1
- classiq/_internals/__init__.py +20 -0
- classiq/_internals/authentication/authentication.py +11 -0
- classiq/analyzer/analyzer.py +12 -10
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/libraries/qmci_library.py +4 -9
- classiq/execution/execution_session.py +68 -7
- classiq/executor.py +14 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +189 -0
- classiq/interface/backend/quantum_backend_providers.py +38 -0
- classiq/interface/debug_info/debug_info.py +22 -2
- classiq/interface/exceptions.py +16 -1
- classiq/interface/executor/execution_preferences.py +18 -0
- classiq/interface/generator/application_apis/chemistry_declarations.py +1 -177
- classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +0 -12
- classiq/interface/generator/application_apis/finance_declarations.py +8 -43
- classiq/interface/generator/application_apis/qsvm_declarations.py +0 -78
- classiq/interface/generator/builtin_api_builder.py +0 -3
- classiq/interface/generator/functions/__init__.py +0 -2
- classiq/interface/generator/functions/builtins/__init__.py +0 -15
- classiq/interface/generator/generated_circuit_data.py +2 -0
- classiq/interface/generator/hardware/hardware_data.py +37 -0
- classiq/interface/generator/model/constraints.py +18 -1
- classiq/interface/generator/model/preferences/preferences.py +53 -1
- classiq/interface/generator/model/quantum_register.py +1 -1
- classiq/interface/generator/quantum_program.py +10 -2
- classiq/interface/generator/transpiler_basis_gates.py +4 -0
- classiq/interface/generator/types/builtin_enum_declarations.py +136 -21
- classiq/interface/generator/types/enum_declaration.py +1 -3
- classiq/interface/generator/types/struct_declaration.py +1 -3
- classiq/interface/hardware.py +5 -0
- classiq/interface/ide/visual_model.py +1 -1
- classiq/interface/model/classical_parameter_declaration.py +6 -0
- classiq/interface/model/inplace_binary_operation.py +0 -14
- classiq/interface/model/model.py +1 -18
- classiq/interface/model/port_declaration.py +4 -2
- classiq/interface/model/quantum_function_declaration.py +19 -6
- classiq/interface/model/quantum_lambda_function.py +11 -1
- classiq/interface/model/quantum_variable_declaration.py +1 -1
- classiq/model_expansions/__init__.py +0 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +250 -0
- classiq/model_expansions/capturing/__init__.py +0 -0
- classiq/model_expansions/capturing/captured_var_manager.py +50 -0
- classiq/model_expansions/capturing/mangling_utils.py +17 -0
- classiq/model_expansions/capturing/propagated_var_stack.py +180 -0
- classiq/model_expansions/closure.py +160 -0
- classiq/model_expansions/debug_flag.py +3 -0
- classiq/model_expansions/evaluators/__init__.py +0 -0
- classiq/model_expansions/evaluators/arg_type_match.py +160 -0
- classiq/model_expansions/evaluators/argument_types.py +42 -0
- classiq/model_expansions/evaluators/classical_expression.py +36 -0
- classiq/model_expansions/evaluators/control.py +144 -0
- classiq/model_expansions/evaluators/parameter_types.py +227 -0
- classiq/model_expansions/evaluators/quantum_type_utils.py +235 -0
- classiq/model_expansions/evaluators/type_type_match.py +90 -0
- classiq/model_expansions/expression_evaluator.py +125 -0
- classiq/model_expansions/expression_renamer.py +76 -0
- classiq/model_expansions/function_builder.py +192 -0
- classiq/model_expansions/generative_functions.py +101 -0
- classiq/model_expansions/interpreter.py +365 -0
- classiq/model_expansions/model_tables.py +105 -0
- classiq/model_expansions/quantum_operations/__init__.py +19 -0
- classiq/model_expansions/quantum_operations/bind.py +64 -0
- classiq/model_expansions/quantum_operations/classicalif.py +39 -0
- classiq/model_expansions/quantum_operations/control.py +235 -0
- classiq/model_expansions/quantum_operations/emitter.py +215 -0
- classiq/model_expansions/quantum_operations/expression_operation.py +218 -0
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +250 -0
- classiq/model_expansions/quantum_operations/invert.py +38 -0
- classiq/model_expansions/quantum_operations/power.py +74 -0
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +174 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +15 -0
- classiq/model_expansions/quantum_operations/repeat.py +33 -0
- classiq/model_expansions/quantum_operations/variable_decleration.py +28 -0
- classiq/model_expansions/quantum_operations/within_apply.py +46 -0
- classiq/model_expansions/scope.py +226 -0
- classiq/model_expansions/scope_initialization.py +136 -0
- classiq/model_expansions/sympy_conversion/__init__.py +0 -0
- classiq/model_expansions/sympy_conversion/arithmetics.py +49 -0
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +150 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +113 -0
- classiq/model_expansions/utils/__init__.py +0 -0
- classiq/model_expansions/utils/counted_name_allocator.py +11 -0
- classiq/model_expansions/visitors/__init__.py +0 -0
- classiq/model_expansions/visitors/boolean_expression_transformers.py +214 -0
- classiq/model_expansions/visitors/variable_references.py +115 -0
- classiq/qmod/__init__.py +1 -3
- classiq/qmod/builtins/enums.py +33 -2
- classiq/qmod/builtins/functions/__init__.py +251 -0
- classiq/qmod/builtins/functions/amplitude_estimation.py +26 -0
- classiq/qmod/builtins/functions/arithmetic.py +68 -0
- classiq/qmod/builtins/functions/benchmarking.py +8 -0
- classiq/qmod/builtins/functions/chemistry.py +91 -0
- classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +105 -0
- classiq/qmod/builtins/functions/exponentiation.py +110 -0
- classiq/qmod/builtins/functions/finance.py +34 -0
- classiq/qmod/builtins/functions/grover.py +179 -0
- classiq/qmod/builtins/functions/hea.py +59 -0
- classiq/qmod/builtins/functions/linear_pauli_rotation.py +65 -0
- classiq/qmod/builtins/functions/modular_exponentiation.py +137 -0
- classiq/qmod/builtins/functions/operators.py +22 -0
- classiq/qmod/builtins/functions/qaoa_penalty.py +116 -0
- classiq/qmod/builtins/functions/qft.py +23 -0
- classiq/qmod/builtins/functions/qpe.py +39 -0
- classiq/qmod/builtins/functions/qsvm.py +24 -0
- classiq/qmod/builtins/functions/qsvt.py +136 -0
- classiq/qmod/builtins/functions/standard_gates.py +739 -0
- classiq/qmod/builtins/functions/state_preparation.py +356 -0
- classiq/qmod/builtins/functions/swap_test.py +25 -0
- classiq/qmod/builtins/structs.py +50 -28
- classiq/qmod/cparam.py +64 -0
- classiq/qmod/create_model_function.py +190 -0
- classiq/qmod/declaration_inferrer.py +52 -81
- classiq/qmod/expression_query.py +16 -0
- classiq/qmod/generative.py +48 -0
- classiq/qmod/model_state_container.py +1 -2
- classiq/qmod/native/pretty_printer.py +7 -11
- classiq/qmod/pretty_print/pretty_printer.py +7 -11
- classiq/qmod/python_classical_type.py +67 -0
- classiq/qmod/qfunc.py +19 -4
- classiq/qmod/qmod_parameter.py +15 -64
- classiq/qmod/qmod_variable.py +27 -45
- classiq/qmod/quantum_callable.py +1 -1
- classiq/qmod/quantum_expandable.py +10 -4
- classiq/qmod/quantum_function.py +22 -40
- classiq/qmod/semantics/error_manager.py +22 -10
- classiq/qmod/semantics/static_semantics_visitor.py +10 -12
- classiq/qmod/semantics/validation/types_validation.py +6 -7
- classiq/qmod/utilities.py +2 -2
- classiq/qmod/write_qmod.py +14 -0
- classiq/show.py +10 -0
- classiq/synthesis.py +46 -2
- {classiq-0.45.1.dist-info → classiq-0.46.0.dist-info}/METADATA +1 -1
- {classiq-0.45.1.dist-info → classiq-0.46.0.dist-info}/RECORD +138 -74
- classiq/interface/generator/functions/builtins/core_library/__init__.py +0 -16
- classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +0 -710
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +0 -105
- classiq/interface/generator/functions/builtins/open_lib_functions.py +0 -2489
- classiq/interface/generator/functions/builtins/quantum_operators.py +0 -24
- classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
- classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +0 -21
- classiq/qmod/builtins/functions.py +0 -1029
- {classiq-0.45.1.dist-info → classiq-0.46.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,218 @@
|
|
1
|
+
import ast
|
2
|
+
from abc import abstractmethod
|
3
|
+
from itertools import chain
|
4
|
+
from typing import TYPE_CHECKING, Dict, List, Tuple, TypeVar
|
5
|
+
|
6
|
+
import sympy
|
7
|
+
|
8
|
+
from classiq.interface.exceptions import (
|
9
|
+
ClassiqExpansionError,
|
10
|
+
ClassiqInternalExpansionError,
|
11
|
+
)
|
12
|
+
from classiq.interface.generator.expressions.evaluated_expression import (
|
13
|
+
EvaluatedExpression,
|
14
|
+
)
|
15
|
+
from classiq.interface.generator.expressions.expression import Expression
|
16
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
17
|
+
from classiq.interface.model.bind_operation import BindOperation
|
18
|
+
from classiq.interface.model.handle_binding import (
|
19
|
+
FieldHandleBinding,
|
20
|
+
HandleBinding,
|
21
|
+
SlicedHandleBinding,
|
22
|
+
SubscriptHandleBinding,
|
23
|
+
)
|
24
|
+
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
25
|
+
QuantumExpressionOperation,
|
26
|
+
)
|
27
|
+
from classiq.interface.model.quantum_type import (
|
28
|
+
QuantumBit,
|
29
|
+
QuantumBitvector,
|
30
|
+
QuantumNumeric,
|
31
|
+
)
|
32
|
+
from classiq.interface.model.variable_declaration_statement import (
|
33
|
+
VariableDeclarationStatement,
|
34
|
+
)
|
35
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
36
|
+
|
37
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
38
|
+
from classiq.model_expansions.scope import QuantumSymbol
|
39
|
+
from classiq.model_expansions.sympy_conversion.sympy_to_python import (
|
40
|
+
translate_sympy_quantum_expression,
|
41
|
+
)
|
42
|
+
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
43
|
+
|
44
|
+
ExpressionOperationT = TypeVar("ExpressionOperationT", bound=QuantumExpressionOperation)
|
45
|
+
|
46
|
+
|
47
|
+
class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
|
48
|
+
@abstractmethod
|
49
|
+
def emit(self, op: ExpressionOperationT, /) -> None:
|
50
|
+
pass
|
51
|
+
|
52
|
+
def _emit_with_split(
|
53
|
+
self,
|
54
|
+
op: ExpressionOperationT,
|
55
|
+
expression: Expression,
|
56
|
+
symbols_to_split: List[QuantumSymbol],
|
57
|
+
) -> None:
|
58
|
+
symbols_parts, bind_ops = self._get_bind_ops(symbols_to_split)
|
59
|
+
|
60
|
+
for symbol_parts in symbols_parts:
|
61
|
+
for symbol in symbol_parts:
|
62
|
+
if symbol.handle.identifier not in self._current_scope:
|
63
|
+
self._interpreter.emit_statement(
|
64
|
+
VariableDeclarationStatement(
|
65
|
+
name=symbol.handle.identifier,
|
66
|
+
quantum_type=symbol.quantum_type,
|
67
|
+
)
|
68
|
+
)
|
69
|
+
|
70
|
+
new_expression = self._update_op_expression(
|
71
|
+
{symbol.handle: symbol for symbol in chain.from_iterable(symbols_parts)},
|
72
|
+
expression,
|
73
|
+
)
|
74
|
+
new_op = op.copy(update=dict(expression=new_expression))
|
75
|
+
|
76
|
+
self._interpreter.emit_statement(
|
77
|
+
WithinApply(
|
78
|
+
compute=bind_ops,
|
79
|
+
action=[new_op],
|
80
|
+
source_ref=op.source_ref,
|
81
|
+
)
|
82
|
+
)
|
83
|
+
|
84
|
+
def _update_op_expression(
|
85
|
+
self,
|
86
|
+
symbol_parts: Dict[HandleBinding, QuantumSymbol],
|
87
|
+
expression: Expression,
|
88
|
+
) -> Expression:
|
89
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
90
|
+
vrc.visit(ast.parse(expression.expr))
|
91
|
+
|
92
|
+
new_expr_str = expression.expr
|
93
|
+
for handle in vrc.var_handles:
|
94
|
+
collapsed_handle = handle.collapse()
|
95
|
+
if collapsed_handle in symbol_parts:
|
96
|
+
new_expr_str = new_expr_str.replace(
|
97
|
+
str(handle), symbol_parts[collapsed_handle].handle.identifier
|
98
|
+
)
|
99
|
+
self._check_all_handles_were_replaced(new_expr_str)
|
100
|
+
|
101
|
+
new_expr = Expression(expr=new_expr_str)
|
102
|
+
new_expr._evaluated_expr = EvaluatedExpression(
|
103
|
+
value=self._interpreter.evaluate(new_expr).value
|
104
|
+
)
|
105
|
+
return new_expr
|
106
|
+
|
107
|
+
@staticmethod
|
108
|
+
def _check_all_handles_were_replaced(new_expr_str: str) -> None:
|
109
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
110
|
+
vrc.visit(ast.parse(new_expr_str))
|
111
|
+
for handle in vrc.var_handles:
|
112
|
+
if isinstance(
|
113
|
+
handle,
|
114
|
+
(SubscriptHandleBinding, SlicedHandleBinding, FieldHandleBinding),
|
115
|
+
):
|
116
|
+
raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
|
117
|
+
|
118
|
+
@staticmethod
|
119
|
+
def _get_bind_ops(
|
120
|
+
symbols_to_split: List[QuantumSymbol],
|
121
|
+
) -> Tuple[List[List[QuantumSymbol]], List[BindOperation]]:
|
122
|
+
bind_ops = []
|
123
|
+
symbols_parts = []
|
124
|
+
for symbol in symbols_to_split:
|
125
|
+
symbol_parts = ExpressionOperationEmitter._get_symbol_parts(symbol)
|
126
|
+
symbols_parts.append(symbol_parts)
|
127
|
+
bind_ops.append(
|
128
|
+
BindOperation(
|
129
|
+
in_handles=[symbol.handle],
|
130
|
+
out_handles=[
|
131
|
+
HandleBinding(name=symbol_part.handle.identifier)
|
132
|
+
for symbol_part in symbol_parts
|
133
|
+
],
|
134
|
+
)
|
135
|
+
)
|
136
|
+
return symbols_parts, bind_ops
|
137
|
+
|
138
|
+
@staticmethod
|
139
|
+
def _get_symbol_parts(symbol: QuantumSymbol) -> List[QuantumSymbol]:
|
140
|
+
quantum_type = symbol.quantum_type
|
141
|
+
|
142
|
+
if isinstance(quantum_type, (QuantumBit, QuantumNumeric)):
|
143
|
+
return [symbol]
|
144
|
+
|
145
|
+
if isinstance(quantum_type, QuantumBitvector):
|
146
|
+
if not quantum_type.has_length:
|
147
|
+
raise ClassiqExpansionError(
|
148
|
+
f"Could not determine the length of quantum array "
|
149
|
+
f"{symbol.handle}."
|
150
|
+
)
|
151
|
+
return list(
|
152
|
+
chain.from_iterable(
|
153
|
+
ExpressionOperationEmitter._get_symbol_parts(symbol[idx])
|
154
|
+
for idx in range(quantum_type.length_value)
|
155
|
+
)
|
156
|
+
)
|
157
|
+
|
158
|
+
if TYPE_CHECKING:
|
159
|
+
assert isinstance(quantum_type, TypeName)
|
160
|
+
|
161
|
+
return list(
|
162
|
+
chain.from_iterable(
|
163
|
+
ExpressionOperationEmitter._get_symbol_parts(field_symbol)
|
164
|
+
for field_symbol in symbol.fields.values()
|
165
|
+
)
|
166
|
+
)
|
167
|
+
|
168
|
+
def _get_symbols_to_split(self, expression: Expression) -> List[QuantumSymbol]:
|
169
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
170
|
+
vrc.visit(ast.parse(expression.expr))
|
171
|
+
symbol_names_to_split = dict.fromkeys(
|
172
|
+
handle.name
|
173
|
+
for handle in vrc.var_handles
|
174
|
+
if isinstance(handle, (SubscriptHandleBinding, FieldHandleBinding))
|
175
|
+
)
|
176
|
+
return [
|
177
|
+
symbol
|
178
|
+
for symbol_name in symbol_names_to_split
|
179
|
+
if isinstance(
|
180
|
+
symbol := self._current_scope[symbol_name].value,
|
181
|
+
QuantumSymbol,
|
182
|
+
)
|
183
|
+
]
|
184
|
+
|
185
|
+
def _evaluate_op_expression(self, op: ExpressionOperationT) -> Expression:
|
186
|
+
return self._evaluate_expression(op.expression)
|
187
|
+
|
188
|
+
def _evaluate_expression(self, expression: Expression) -> Expression:
|
189
|
+
evaluated_expression = self._interpreter.evaluate(expression)
|
190
|
+
if isinstance(evaluated_expression.value, sympy.Basic):
|
191
|
+
new_expression = Expression(
|
192
|
+
expr=translate_sympy_quantum_expression(evaluated_expression.value)
|
193
|
+
)
|
194
|
+
else:
|
195
|
+
new_expression = Expression(expr=str(evaluated_expression.value))
|
196
|
+
new_expression._evaluated_expr = EvaluatedExpression(
|
197
|
+
value=evaluated_expression.value
|
198
|
+
)
|
199
|
+
return new_expression
|
200
|
+
|
201
|
+
def _evaluate_types_in_expression(
|
202
|
+
self, op: ExpressionOperationT, expression: Expression
|
203
|
+
) -> ExpressionOperationT:
|
204
|
+
op_with_evaluated_types = op.copy(update={"expression": expression})
|
205
|
+
vrc = VarRefCollector()
|
206
|
+
vrc.visit(ast.parse(op_with_evaluated_types.expression.expr))
|
207
|
+
handles = list(vrc.var_handles)
|
208
|
+
op_with_evaluated_types.set_var_handles(handles)
|
209
|
+
op_with_evaluated_types.initialize_var_types(
|
210
|
+
{
|
211
|
+
handle.name: self._interpreter.evaluate(handle)
|
212
|
+
.as_type(QuantumSymbol)
|
213
|
+
.quantum_type
|
214
|
+
for handle in handles
|
215
|
+
},
|
216
|
+
self._machine_precision,
|
217
|
+
)
|
218
|
+
return op_with_evaluated_types
|
@@ -0,0 +1,250 @@
|
|
1
|
+
from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
4
|
+
from classiq.interface.generator.expressions.expression import Expression
|
5
|
+
from classiq.interface.generator.functions.port_declaration import (
|
6
|
+
PortDeclarationDirection,
|
7
|
+
)
|
8
|
+
from classiq.interface.model.bind_operation import BindOperation
|
9
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
10
|
+
from classiq.interface.model.inplace_binary_operation import (
|
11
|
+
BinaryOperation,
|
12
|
+
InplaceBinaryOperation,
|
13
|
+
)
|
14
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
15
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
16
|
+
from classiq.interface.model.quantum_function_declaration import (
|
17
|
+
NamedParamsQuantumFunctionDeclaration,
|
18
|
+
)
|
19
|
+
from classiq.interface.model.quantum_statement import QuantumStatement
|
20
|
+
from classiq.interface.model.quantum_type import (
|
21
|
+
QuantumBit,
|
22
|
+
QuantumBitvector,
|
23
|
+
QuantumNumeric,
|
24
|
+
)
|
25
|
+
from classiq.interface.model.variable_declaration_statement import (
|
26
|
+
VariableDeclarationStatement,
|
27
|
+
)
|
28
|
+
|
29
|
+
from classiq.model_expansions.closure import FunctionClosure
|
30
|
+
from classiq.model_expansions.evaluators.parameter_types import (
|
31
|
+
evaluate_types_in_quantum_symbols,
|
32
|
+
)
|
33
|
+
from classiq.model_expansions.evaluators.quantum_type_utils import (
|
34
|
+
validate_inplace_binary_op_vars,
|
35
|
+
)
|
36
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
37
|
+
from classiq.model_expansions.scope import QuantumSymbol, Scope
|
38
|
+
from classiq.qmod.builtins.functions import integer_xor, modular_add
|
39
|
+
|
40
|
+
|
41
|
+
def _binary_function_declaration(
|
42
|
+
op: BinaryOperation,
|
43
|
+
) -> NamedParamsQuantumFunctionDeclaration:
|
44
|
+
return {
|
45
|
+
BinaryOperation.Addition: modular_add.func_decl,
|
46
|
+
BinaryOperation.Xor: integer_xor.func_decl,
|
47
|
+
}[op]
|
48
|
+
|
49
|
+
|
50
|
+
class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
|
51
|
+
def emit(self, op: InplaceBinaryOperation, /) -> None:
|
52
|
+
value_var = self._interpreter.evaluate(op.value).as_type(QuantumSymbol)
|
53
|
+
target_var = self._interpreter.evaluate(op.target).as_type(QuantumSymbol)
|
54
|
+
value_var, target_var = evaluate_types_in_quantum_symbols(
|
55
|
+
[value_var, target_var], self._current_scope
|
56
|
+
)
|
57
|
+
validate_inplace_binary_op_vars(value_var, target_var, op.operation.value)
|
58
|
+
if TYPE_CHECKING:
|
59
|
+
assert isinstance(value_var.quantum_type, QuantumNumeric)
|
60
|
+
assert isinstance(target_var.quantum_type, QuantumNumeric)
|
61
|
+
|
62
|
+
sign_diff = int(value_var.quantum_type.sign_value) - int(
|
63
|
+
target_var.quantum_type.sign_value
|
64
|
+
)
|
65
|
+
frac_digits_diff = (
|
66
|
+
value_var.quantum_type.fraction_digits_value
|
67
|
+
- target_var.quantum_type.fraction_digits_value
|
68
|
+
)
|
69
|
+
if (
|
70
|
+
sign_diff + frac_digits_diff == value_var.quantum_type.size_in_bits
|
71
|
+
or -sign_diff - frac_digits_diff == target_var.quantum_type.size_in_bits
|
72
|
+
):
|
73
|
+
with self._propagated_var_stack.capture_variables(op):
|
74
|
+
return
|
75
|
+
|
76
|
+
value_var = QuantumSymbol(
|
77
|
+
handle=HandleBinding(name="value"), quantum_type=value_var.quantum_type
|
78
|
+
)
|
79
|
+
target_var = QuantumSymbol(
|
80
|
+
handle=HandleBinding(name="target"),
|
81
|
+
quantum_type=target_var.quantum_type,
|
82
|
+
)
|
83
|
+
inplace_binary_op_function = FunctionClosure.create(
|
84
|
+
name=op.operation.value,
|
85
|
+
positional_arg_declarations=[
|
86
|
+
PortDeclaration(
|
87
|
+
name=value_var.handle.name,
|
88
|
+
quantum_type=value_var.quantum_type,
|
89
|
+
direction=PortDeclarationDirection.Inout,
|
90
|
+
),
|
91
|
+
PortDeclaration(
|
92
|
+
name=target_var.handle.name,
|
93
|
+
quantum_type=target_var.quantum_type,
|
94
|
+
direction=PortDeclarationDirection.Inout,
|
95
|
+
),
|
96
|
+
],
|
97
|
+
body=_build_inplace_binary_operation(
|
98
|
+
value_var=value_var,
|
99
|
+
target_var=target_var,
|
100
|
+
frac_digits_diff=frac_digits_diff,
|
101
|
+
internal_function_declaration=_binary_function_declaration(
|
102
|
+
op.operation
|
103
|
+
),
|
104
|
+
),
|
105
|
+
scope=Scope(parent=self._current_scope),
|
106
|
+
)
|
107
|
+
with self._propagated_var_stack.capture_variables(op):
|
108
|
+
self._emit_quantum_function_call(
|
109
|
+
inplace_binary_op_function, [op.value, op.target]
|
110
|
+
)
|
111
|
+
|
112
|
+
|
113
|
+
def _build_inplace_binary_operation(
|
114
|
+
value_var: QuantumSymbol,
|
115
|
+
target_var: QuantumSymbol,
|
116
|
+
frac_digits_diff: int,
|
117
|
+
internal_function_declaration: NamedParamsQuantumFunctionDeclaration,
|
118
|
+
) -> List[QuantumStatement]:
|
119
|
+
if TYPE_CHECKING:
|
120
|
+
assert isinstance(value_var.quantum_type, QuantumNumeric)
|
121
|
+
assert isinstance(target_var.quantum_type, QuantumNumeric)
|
122
|
+
|
123
|
+
value_overlap_var, value_sign_var, value_bind_targets = _get_inplace_bind_targets(
|
124
|
+
"value", value_var, frac_digits_diff
|
125
|
+
)
|
126
|
+
target_overlap_var, target_sign_var, target_bind_targets = (
|
127
|
+
_get_inplace_bind_targets("target", target_var, -frac_digits_diff)
|
128
|
+
)
|
129
|
+
|
130
|
+
value_pre_ops, value_post_ops = _get_inplace_pre_post_ops(
|
131
|
+
value_var, value_bind_targets
|
132
|
+
)
|
133
|
+
target_pre_ops, target_post_ops = _get_inplace_pre_post_ops(
|
134
|
+
target_var, target_bind_targets
|
135
|
+
)
|
136
|
+
|
137
|
+
binary_ops = []
|
138
|
+
if value_overlap_var is not None and target_overlap_var is not None:
|
139
|
+
binary_ops.append(
|
140
|
+
_internal_inplace_binary_operation_function_call(
|
141
|
+
internal_function_declaration,
|
142
|
+
value_overlap_var.handle,
|
143
|
+
target_overlap_var.handle,
|
144
|
+
)
|
145
|
+
)
|
146
|
+
if value_sign_var is not None and target_sign_var is not None:
|
147
|
+
binary_ops.append(
|
148
|
+
_internal_inplace_binary_operation_function_call(
|
149
|
+
internal_function_declaration,
|
150
|
+
value_sign_var.handle,
|
151
|
+
target_sign_var.handle,
|
152
|
+
)
|
153
|
+
)
|
154
|
+
if len(binary_ops) == 0:
|
155
|
+
raise ClassiqInternalExpansionError("Bug in unrolling inplace operation")
|
156
|
+
|
157
|
+
return [
|
158
|
+
*value_pre_ops,
|
159
|
+
*target_pre_ops,
|
160
|
+
*binary_ops,
|
161
|
+
*target_post_ops,
|
162
|
+
*value_post_ops,
|
163
|
+
]
|
164
|
+
|
165
|
+
|
166
|
+
def _internal_inplace_binary_operation_function_call(
|
167
|
+
internal_function_declaration: NamedParamsQuantumFunctionDeclaration,
|
168
|
+
value_var: HandleBinding,
|
169
|
+
target_var: HandleBinding,
|
170
|
+
) -> QuantumFunctionCall:
|
171
|
+
internal_function_call = QuantumFunctionCall(
|
172
|
+
function=internal_function_declaration.name,
|
173
|
+
positional_args=[value_var, target_var],
|
174
|
+
)
|
175
|
+
internal_function_call.set_func_decl(internal_function_declaration)
|
176
|
+
return internal_function_call
|
177
|
+
|
178
|
+
|
179
|
+
def _get_inplace_bind_targets(
|
180
|
+
kind: str, var: QuantumSymbol, frac_digits_diff: int
|
181
|
+
) -> Tuple[Optional[QuantumSymbol], Optional[QuantumSymbol], List[QuantumSymbol]]:
|
182
|
+
quantum_type = var.quantum_type
|
183
|
+
if TYPE_CHECKING:
|
184
|
+
assert isinstance(quantum_type, QuantumNumeric)
|
185
|
+
|
186
|
+
if not quantum_type.sign_value and frac_digits_diff <= 0:
|
187
|
+
return var, None, []
|
188
|
+
|
189
|
+
significand_overlap = (
|
190
|
+
quantum_type.size_in_bits
|
191
|
+
- quantum_type.fraction_digits_value
|
192
|
+
- int(quantum_type.sign_value)
|
193
|
+
)
|
194
|
+
fraction_overlap = quantum_type.fraction_digits_value - max(0, frac_digits_diff)
|
195
|
+
if significand_overlap + fraction_overlap == 0 and quantum_type.size_in_bits == 1:
|
196
|
+
assert quantum_type.sign_value
|
197
|
+
return None, var, []
|
198
|
+
|
199
|
+
bind_targets = []
|
200
|
+
|
201
|
+
if frac_digits_diff > 0:
|
202
|
+
bind_targets.append(
|
203
|
+
QuantumSymbol(
|
204
|
+
handle=HandleBinding(name=f"trimmed_{kind}_fraction_digits"),
|
205
|
+
quantum_type=QuantumBitvector(
|
206
|
+
length=Expression(expr=str(frac_digits_diff)),
|
207
|
+
),
|
208
|
+
)
|
209
|
+
)
|
210
|
+
|
211
|
+
overlap_var = None
|
212
|
+
if significand_overlap + fraction_overlap > 0:
|
213
|
+
overlap_var = QuantumSymbol(
|
214
|
+
handle=HandleBinding(name=f"{kind}_overlap"),
|
215
|
+
quantum_type=QuantumNumeric(
|
216
|
+
size=Expression(expr=str(significand_overlap + fraction_overlap)),
|
217
|
+
is_signed=Expression(expr="False"),
|
218
|
+
fraction_digits=Expression(expr=str(fraction_overlap)),
|
219
|
+
),
|
220
|
+
)
|
221
|
+
bind_targets.append(overlap_var)
|
222
|
+
|
223
|
+
sign_var = None
|
224
|
+
if quantum_type.sign_value:
|
225
|
+
sign_var = QuantumSymbol(
|
226
|
+
handle=HandleBinding(name=f"trimmed_{kind}_sign"),
|
227
|
+
quantum_type=QuantumBit(),
|
228
|
+
)
|
229
|
+
bind_targets.append(sign_var)
|
230
|
+
|
231
|
+
return overlap_var, sign_var, bind_targets
|
232
|
+
|
233
|
+
|
234
|
+
def _get_inplace_pre_post_ops(
|
235
|
+
var: QuantumSymbol, bind_targets: List[QuantumSymbol]
|
236
|
+
) -> Tuple[Sequence[QuantumStatement], Sequence[QuantumStatement]]:
|
237
|
+
if len(bind_targets) == 0:
|
238
|
+
return [], []
|
239
|
+
|
240
|
+
value_bind_op = BindOperation(
|
241
|
+
in_handles=[var.handle],
|
242
|
+
out_handles=[var.handle for var in bind_targets],
|
243
|
+
)
|
244
|
+
return [
|
245
|
+
VariableDeclarationStatement(
|
246
|
+
name=var.handle.name,
|
247
|
+
quantum_type=var.quantum_type,
|
248
|
+
)
|
249
|
+
for var in bind_targets
|
250
|
+
] + [value_bind_op], [value_bind_op.reversed()]
|
@@ -0,0 +1,38 @@
|
|
1
|
+
from classiq.interface.generator.functions.builtins.internal_operators import (
|
2
|
+
INVERT_OPERATOR_NAME,
|
3
|
+
)
|
4
|
+
from classiq.interface.model.invert import Invert
|
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
|
+
|
11
|
+
class InvertEmitter(Emitter[Invert]):
|
12
|
+
def emit(self, invert: Invert, /) -> None:
|
13
|
+
if self._should_wrap(invert.body):
|
14
|
+
self._emit_wrapped(invert)
|
15
|
+
return
|
16
|
+
|
17
|
+
self._emit_as_operation(invert)
|
18
|
+
|
19
|
+
def _emit_as_operation(self, invert: Invert) -> None:
|
20
|
+
invert_operation = Closure(
|
21
|
+
name=INVERT_OPERATOR_NAME,
|
22
|
+
blocks={"body": invert.body},
|
23
|
+
scope=Scope(parent=self._current_scope),
|
24
|
+
)
|
25
|
+
with self._propagated_var_stack.capture_variables(invert):
|
26
|
+
context = self._expand_operation(invert_operation)
|
27
|
+
self._builder.emit_statement(
|
28
|
+
Invert(body=context.statements("body"), source_ref=invert.source_ref)
|
29
|
+
)
|
30
|
+
|
31
|
+
def _emit_wrapped(self, invert: Invert) -> None:
|
32
|
+
with self._propagated_var_stack.capture_variables(invert):
|
33
|
+
wrapping_function = self._create_expanded_wrapping_function(
|
34
|
+
INVERT_OPERATOR_NAME, invert.body
|
35
|
+
)
|
36
|
+
self._builder.emit_statement(
|
37
|
+
Invert(body=[wrapping_function], source_ref=invert.source_ref)
|
38
|
+
)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
from typing import Union
|
2
|
+
|
3
|
+
import sympy
|
4
|
+
|
5
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
6
|
+
from classiq.interface.generator.expressions.evaluated_expression import (
|
7
|
+
EvaluatedExpression,
|
8
|
+
)
|
9
|
+
from classiq.interface.generator.expressions.expression import Expression
|
10
|
+
from classiq.interface.generator.functions.builtins.internal_operators import (
|
11
|
+
POWER_OPERATOR_NAME,
|
12
|
+
)
|
13
|
+
from classiq.interface.model.power import Power
|
14
|
+
|
15
|
+
from classiq.model_expansions.closure import Closure
|
16
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
17
|
+
from classiq.model_expansions.scope import Scope
|
18
|
+
|
19
|
+
|
20
|
+
class PowerEmitter(Emitter[Power]):
|
21
|
+
_power_value: Union[int, sympy.Basic]
|
22
|
+
_power_expr: Expression
|
23
|
+
|
24
|
+
def emit(self, power: Power, /) -> None:
|
25
|
+
self._power = power
|
26
|
+
self._power_value = self._get_power_value()
|
27
|
+
self._power_expr = Expression(
|
28
|
+
expr=str(self._power_value), source_ref=power.power.source_ref
|
29
|
+
)
|
30
|
+
self._power_expr._evaluated_expr = EvaluatedExpression(value=self._power_value)
|
31
|
+
|
32
|
+
if len(power.body) > 1 and isinstance(self._power_value, sympy.Basic):
|
33
|
+
self._emit_wrapped()
|
34
|
+
return
|
35
|
+
|
36
|
+
self._emit_as_operation(power)
|
37
|
+
|
38
|
+
def _emit_as_operation(self, power: Power) -> None:
|
39
|
+
power_operation = Closure(
|
40
|
+
name=POWER_OPERATOR_NAME,
|
41
|
+
blocks=dict(body=self._power.body),
|
42
|
+
scope=Scope(parent=self._current_scope),
|
43
|
+
)
|
44
|
+
with self._propagated_var_stack.capture_variables(self._power):
|
45
|
+
context = self._expand_operation(power_operation)
|
46
|
+
self._builder.emit_statement(
|
47
|
+
Power(
|
48
|
+
body=context.statements("body"),
|
49
|
+
power=self._power_expr,
|
50
|
+
source_ref=power.source_ref,
|
51
|
+
)
|
52
|
+
)
|
53
|
+
|
54
|
+
def _emit_wrapped(self) -> None:
|
55
|
+
with self._propagated_var_stack.capture_variables(self._power):
|
56
|
+
wrapping_function = self._create_expanded_wrapping_function(
|
57
|
+
POWER_OPERATOR_NAME, self._power.body
|
58
|
+
)
|
59
|
+
self._builder.emit_statement(
|
60
|
+
Power(
|
61
|
+
body=[wrapping_function],
|
62
|
+
power=self._power_expr,
|
63
|
+
source_ref=self._power.source_ref,
|
64
|
+
)
|
65
|
+
)
|
66
|
+
|
67
|
+
def _get_power_value(self) -> Union[int, sympy.Basic]:
|
68
|
+
power_value = self._interpreter.evaluate(self._power.power).value
|
69
|
+
if not (isinstance(power_value, int) or power_value.is_symbol):
|
70
|
+
raise ClassiqExpansionError(
|
71
|
+
f"`power`'s argument should be an integer or identifier. Complex "
|
72
|
+
f"expressions are not supported. Got {str(power_value)!r}"
|
73
|
+
)
|
74
|
+
return power_value
|