classiq 0.45.1__py3-none-any.whl → 0.46.1__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 +27 -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 +111 -0
- classiq/qmod/builtins/functions/finance.py +34 -0
- classiq/qmod/builtins/functions/grover.py +178 -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 +357 -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.1.dist-info}/METADATA +1 -1
- {classiq-0.45.1.dist-info → classiq-0.46.1.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.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,235 @@
|
|
1
|
+
from typing import Mapping, Optional
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import (
|
4
|
+
ClassiqExpansionError,
|
5
|
+
ClassiqInternalExpansionError,
|
6
|
+
)
|
7
|
+
from classiq.interface.generator.expressions.expression import Expression
|
8
|
+
from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
|
9
|
+
from classiq.interface.generator.functions.type_name import (
|
10
|
+
TypeName,
|
11
|
+
)
|
12
|
+
from classiq.interface.model.bind_operation import BindOperation
|
13
|
+
from classiq.interface.model.inplace_binary_operation import BinaryOperation
|
14
|
+
from classiq.interface.model.quantum_type import (
|
15
|
+
QuantumBit,
|
16
|
+
QuantumBitvector,
|
17
|
+
QuantumNumeric,
|
18
|
+
QuantumType,
|
19
|
+
)
|
20
|
+
|
21
|
+
from classiq.model_expansions.scope import QuantumSymbol, Scope
|
22
|
+
|
23
|
+
|
24
|
+
def copy_type_information(
|
25
|
+
from_type: QuantumType,
|
26
|
+
to_type: QuantumType,
|
27
|
+
to_param_name: str,
|
28
|
+
) -> None:
|
29
|
+
if isinstance(to_type, QuantumBit):
|
30
|
+
set_size(to_type, from_type.size_in_bits, to_param_name)
|
31
|
+
elif isinstance(to_type, QuantumNumeric):
|
32
|
+
set_is_signed(to_type, getattr(from_type, "sign_value", None), to_param_name)
|
33
|
+
set_fraction_digits(
|
34
|
+
to_type,
|
35
|
+
getattr(from_type, "fraction_digits_value", None),
|
36
|
+
to_param_name,
|
37
|
+
)
|
38
|
+
set_size(to_type, from_type.size_in_bits, to_param_name)
|
39
|
+
elif isinstance(to_type, QuantumBitvector):
|
40
|
+
if isinstance(from_type, QuantumBitvector) and type( # noqa: E721
|
41
|
+
from_type.element_type
|
42
|
+
) == type(to_type.element_type):
|
43
|
+
copy_type_information(
|
44
|
+
from_type.element_type, to_type.element_type, to_param_name
|
45
|
+
)
|
46
|
+
set_size(to_type, from_type.size_in_bits, to_param_name)
|
47
|
+
elif isinstance(to_type, TypeName):
|
48
|
+
if isinstance(from_type, TypeName) and from_type.name == to_type.name:
|
49
|
+
for field in from_type.fields:
|
50
|
+
copy_type_information(
|
51
|
+
from_type.fields[field], to_type.fields[field], to_param_name
|
52
|
+
)
|
53
|
+
set_size(to_type, from_type.size_in_bits, to_param_name)
|
54
|
+
else:
|
55
|
+
raise ClassiqInternalExpansionError
|
56
|
+
|
57
|
+
|
58
|
+
def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
|
59
|
+
if size <= 0:
|
60
|
+
raise ClassiqExpansionError(
|
61
|
+
f"Size for {param_name!r} was deduced to be non-positive: {size!r}"
|
62
|
+
)
|
63
|
+
|
64
|
+
if quantum_type.has_size_in_bits and quantum_type.size_in_bits != size:
|
65
|
+
raise ClassiqExpansionError(
|
66
|
+
f"Size mismatch for port {param_name!r} between declared size {quantum_type.size_in_bits} and assigned size {size}"
|
67
|
+
)
|
68
|
+
|
69
|
+
if isinstance(quantum_type, QuantumNumeric):
|
70
|
+
quantum_type.size = Expression(expr=str(size))
|
71
|
+
if not quantum_type.has_sign or not quantum_type.has_fraction_digits:
|
72
|
+
set_is_signed(quantum_type, False, param_name)
|
73
|
+
set_fraction_digits(quantum_type, 0, param_name)
|
74
|
+
elif isinstance(quantum_type, QuantumBitvector):
|
75
|
+
if quantum_type.has_length:
|
76
|
+
if size % quantum_type.length_value != 0:
|
77
|
+
raise ClassiqExpansionError(
|
78
|
+
f"Size mismatch for port {param_name!r}. Cannot fit {size} "
|
79
|
+
f"qubits into an array of {quantum_type.length_value} elements."
|
80
|
+
)
|
81
|
+
set_size(
|
82
|
+
quantum_type.element_type,
|
83
|
+
size // quantum_type.length_value,
|
84
|
+
param_name,
|
85
|
+
)
|
86
|
+
set_length_by_size(quantum_type, size, param_name)
|
87
|
+
elif isinstance(quantum_type, TypeName):
|
88
|
+
fields_without_size = [
|
89
|
+
field_type
|
90
|
+
for field_type in quantum_type.fields.values()
|
91
|
+
if not field_type.has_size_in_bits
|
92
|
+
]
|
93
|
+
if len(fields_without_size) > 1:
|
94
|
+
raise ClassiqInternalExpansionError(
|
95
|
+
f"QuantumStruct should have at most one field without "
|
96
|
+
f"predetermined size. Found {fields_without_size}."
|
97
|
+
)
|
98
|
+
if len(fields_without_size) == 1:
|
99
|
+
predetermined_size_part = sum(
|
100
|
+
field_type.size_in_bits if field_type.has_size_in_bits else 0
|
101
|
+
for field_type in quantum_type.fields.values()
|
102
|
+
)
|
103
|
+
set_size(fields_without_size[0], size - predetermined_size_part, param_name)
|
104
|
+
|
105
|
+
|
106
|
+
def set_fraction_digits(
|
107
|
+
quantum_numeric: QuantumNumeric, fraction_digits: Optional[int], param_name: str
|
108
|
+
) -> None:
|
109
|
+
if fraction_digits is not None and fraction_digits < 0:
|
110
|
+
raise ClassiqExpansionError(
|
111
|
+
f"Number of fraction digits for {param_name!r} was deduced to be negative: "
|
112
|
+
f"{fraction_digits!r}"
|
113
|
+
)
|
114
|
+
|
115
|
+
if (
|
116
|
+
fraction_digits is not None
|
117
|
+
and quantum_numeric.fraction_digits is not None
|
118
|
+
and quantum_numeric.fraction_digits.is_evaluated()
|
119
|
+
and quantum_numeric.fraction_digits_value != fraction_digits
|
120
|
+
):
|
121
|
+
raise ClassiqExpansionError(
|
122
|
+
f"Fraction digits mismatch for port {param_name!r} between declared "
|
123
|
+
f"fraction digits {quantum_numeric.fraction_digits_value!r} and assigned fraction "
|
124
|
+
f"digits {fraction_digits!r}"
|
125
|
+
)
|
126
|
+
|
127
|
+
if not (
|
128
|
+
quantum_numeric.fraction_digits is not None
|
129
|
+
and quantum_numeric.fraction_digits.is_evaluated()
|
130
|
+
):
|
131
|
+
quantum_numeric.fraction_digits = Expression(expr=str(fraction_digits or 0))
|
132
|
+
|
133
|
+
|
134
|
+
def set_is_signed(
|
135
|
+
quantum_numeric: QuantumNumeric, is_signed: Optional[bool], param_name: str
|
136
|
+
) -> None:
|
137
|
+
if (
|
138
|
+
is_signed is not None
|
139
|
+
and quantum_numeric.is_signed is not None
|
140
|
+
and quantum_numeric.is_signed.is_evaluated()
|
141
|
+
and quantum_numeric.sign_value != is_signed
|
142
|
+
):
|
143
|
+
raise ClassiqExpansionError(
|
144
|
+
f"Sign mismatch for port {param_name!r} between declared sign {quantum_numeric.sign_value!r} and assigned sign {is_signed!r}"
|
145
|
+
)
|
146
|
+
|
147
|
+
if not (
|
148
|
+
quantum_numeric.is_signed is not None
|
149
|
+
and quantum_numeric.is_signed.is_evaluated()
|
150
|
+
):
|
151
|
+
quantum_numeric.is_signed = Expression(expr=str(is_signed or False))
|
152
|
+
|
153
|
+
|
154
|
+
def set_element_type(
|
155
|
+
quantum_array: QuantumBitvector, element_type: ConcreteQuantumType
|
156
|
+
) -> None:
|
157
|
+
quantum_array.element_type = element_type
|
158
|
+
|
159
|
+
|
160
|
+
def set_struct_fields(
|
161
|
+
quantum_struct: TypeName, fields: Mapping[str, ConcreteQuantumType]
|
162
|
+
) -> None:
|
163
|
+
quantum_struct._set_fields(fields)
|
164
|
+
|
165
|
+
|
166
|
+
def set_length(quantum_array: QuantumBitvector, length: int) -> None:
|
167
|
+
quantum_array.length = Expression(expr=str(length))
|
168
|
+
|
169
|
+
|
170
|
+
def set_length_by_size(
|
171
|
+
quantum_array: QuantumBitvector, size: int, param_name: str
|
172
|
+
) -> None:
|
173
|
+
if size <= 0:
|
174
|
+
raise ClassiqExpansionError(
|
175
|
+
f"Size for {param_name!r} was deduced to be non-positive: {size!r}"
|
176
|
+
)
|
177
|
+
|
178
|
+
if quantum_array.has_size_in_bits and quantum_array.size_in_bits != size:
|
179
|
+
raise ClassiqExpansionError(
|
180
|
+
f"Size mismatch for port {param_name!r} between declared size "
|
181
|
+
f"{quantum_array.size_in_bits} ({quantum_array.length_value} elements of "
|
182
|
+
f"size {quantum_array.element_type.size_in_bits}) and assigned size {size}."
|
183
|
+
)
|
184
|
+
|
185
|
+
if not quantum_array.element_type.has_size_in_bits:
|
186
|
+
raise ClassiqExpansionError(
|
187
|
+
f"Could not infer element size for array {param_name!r}."
|
188
|
+
)
|
189
|
+
element_size = quantum_array.element_type.size_in_bits
|
190
|
+
|
191
|
+
if size % element_size != 0:
|
192
|
+
raise ClassiqExpansionError(
|
193
|
+
f"Size mismatch for port {param_name!r}. Cannot fit elements of type "
|
194
|
+
f"{quantum_array.element_type.qmod_type_name} (size {element_size}) into "
|
195
|
+
f"{size} qubits."
|
196
|
+
)
|
197
|
+
|
198
|
+
quantum_array.length = Expression(expr=str(size // element_size))
|
199
|
+
|
200
|
+
|
201
|
+
def validate_bind_targets(bind: BindOperation, scope: Scope) -> None:
|
202
|
+
illegal_qnum_bind_targets = []
|
203
|
+
for out_handle in bind.out_handles:
|
204
|
+
out_var = scope[out_handle.name].as_type(QuantumSymbol)
|
205
|
+
out_var_type = out_var.quantum_type
|
206
|
+
if not isinstance(out_var_type, QuantumNumeric):
|
207
|
+
continue
|
208
|
+
if not out_var_type.has_size_in_bits:
|
209
|
+
illegal_qnum_bind_targets.append(str(out_var.handle))
|
210
|
+
elif not out_var_type.has_sign:
|
211
|
+
assert not out_var_type.has_fraction_digits
|
212
|
+
illegal_qnum_bind_targets.append(str(out_var.handle))
|
213
|
+
if len(illegal_qnum_bind_targets) > 0:
|
214
|
+
raise ClassiqExpansionError(
|
215
|
+
f"QNum bind targets {illegal_qnum_bind_targets!r} must be declared or initialized with size, sign, and fraction digits"
|
216
|
+
)
|
217
|
+
|
218
|
+
|
219
|
+
def validate_inplace_binary_op_vars(
|
220
|
+
value_var: QuantumSymbol, target_var: QuantumSymbol, operation_name: str
|
221
|
+
) -> None:
|
222
|
+
if not isinstance(value_var.quantum_type, QuantumNumeric):
|
223
|
+
raise ClassiqExpansionError(
|
224
|
+
f"Cannot perform `{operation_name}` operation with non numeric value {value_var.handle}"
|
225
|
+
)
|
226
|
+
if not isinstance(target_var.quantum_type, QuantumNumeric):
|
227
|
+
raise ClassiqExpansionError(
|
228
|
+
f"Cannot perform `{operation_name}` operation with non numeric target {target_var.handle}"
|
229
|
+
)
|
230
|
+
if operation_name != BinaryOperation.Xor.value and (
|
231
|
+
value_var.quantum_type.sign_value or target_var.quantum_type.sign_value
|
232
|
+
):
|
233
|
+
raise ClassiqExpansionError(
|
234
|
+
f"Cannot perform `{operation_name}` operation with signed variables"
|
235
|
+
)
|
@@ -0,0 +1,90 @@
|
|
1
|
+
from typing import Sequence
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import (
|
4
|
+
ClassiqExpansionError,
|
5
|
+
ClassiqInternalExpansionError,
|
6
|
+
)
|
7
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
8
|
+
AnonClassicalParameterDeclaration,
|
9
|
+
)
|
10
|
+
from classiq.interface.model.port_declaration import AnonPortDeclaration
|
11
|
+
from classiq.interface.model.quantum_function_declaration import (
|
12
|
+
AnonPositionalArg,
|
13
|
+
AnonQuantumOperandDeclaration,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
def check_signature_match(
|
18
|
+
decl_params: Sequence[AnonPositionalArg],
|
19
|
+
op_params: Sequence[AnonPositionalArg],
|
20
|
+
location_str: str,
|
21
|
+
) -> None:
|
22
|
+
if len(decl_params) != len(op_params):
|
23
|
+
raise ClassiqExpansionError(
|
24
|
+
f"{location_str} should have {len(decl_params)} parameters, "
|
25
|
+
f"not {len(op_params)}"
|
26
|
+
)
|
27
|
+
for idx, (decl_param, op_param) in enumerate(zip(decl_params, op_params)):
|
28
|
+
message_prefix = f"Argument {idx} in {location_str} has incompatible type; "
|
29
|
+
_check_type_match(op_param, decl_param, location_str, message_prefix)
|
30
|
+
|
31
|
+
|
32
|
+
def _check_type_match(
|
33
|
+
op_param: AnonPositionalArg,
|
34
|
+
decl_param: AnonPositionalArg,
|
35
|
+
location_str: str,
|
36
|
+
message_prefix: str,
|
37
|
+
) -> None:
|
38
|
+
if isinstance(decl_param, AnonPortDeclaration):
|
39
|
+
error_message = message_prefix + "expected quantum parameter"
|
40
|
+
_check_qvar_type_match(op_param, error_message)
|
41
|
+
elif isinstance(decl_param, AnonQuantumOperandDeclaration):
|
42
|
+
if decl_param.is_list:
|
43
|
+
error_message = message_prefix + "expected operand list parameter"
|
44
|
+
else:
|
45
|
+
error_message = message_prefix + "expected operand parameter"
|
46
|
+
_check_operand_type_match(op_param, decl_param, location_str, error_message)
|
47
|
+
elif isinstance(decl_param, AnonClassicalParameterDeclaration):
|
48
|
+
error_message = (
|
49
|
+
message_prefix + f"expected classical {decl_param.classical_type} parameter"
|
50
|
+
)
|
51
|
+
_check_classical_type_match(op_param, decl_param, error_message)
|
52
|
+
else:
|
53
|
+
raise ClassiqInternalExpansionError(
|
54
|
+
f"unexpected parameter declaration type: {type(decl_param).__name__}"
|
55
|
+
)
|
56
|
+
|
57
|
+
|
58
|
+
def _check_qvar_type_match(op_param: AnonPositionalArg, error_message: str) -> None:
|
59
|
+
if not isinstance(op_param, AnonPortDeclaration):
|
60
|
+
raise ClassiqExpansionError(error_message)
|
61
|
+
|
62
|
+
|
63
|
+
def _check_operand_type_match(
|
64
|
+
op_param: AnonPositionalArg,
|
65
|
+
decl_param: AnonQuantumOperandDeclaration,
|
66
|
+
location_str: str,
|
67
|
+
error_message: str,
|
68
|
+
) -> None:
|
69
|
+
if (
|
70
|
+
not isinstance(op_param, AnonQuantumOperandDeclaration)
|
71
|
+
or decl_param.is_list ^ op_param.is_list
|
72
|
+
):
|
73
|
+
raise ClassiqExpansionError(error_message)
|
74
|
+
check_signature_match(
|
75
|
+
decl_param.positional_arg_declarations,
|
76
|
+
op_param.positional_arg_declarations,
|
77
|
+
f"operand {decl_param.name} in {location_str}",
|
78
|
+
)
|
79
|
+
|
80
|
+
|
81
|
+
def _check_classical_type_match(
|
82
|
+
op_param: AnonPositionalArg,
|
83
|
+
decl_param: AnonClassicalParameterDeclaration,
|
84
|
+
error_message: str,
|
85
|
+
) -> None:
|
86
|
+
if (
|
87
|
+
not isinstance(op_param, AnonClassicalParameterDeclaration)
|
88
|
+
or decl_param.classical_type != op_param.classical_type
|
89
|
+
):
|
90
|
+
raise ClassiqExpansionError(error_message)
|
@@ -0,0 +1,125 @@
|
|
1
|
+
import ast
|
2
|
+
from enum import EnumMeta
|
3
|
+
from typing import Any, Dict, List, Mapping, Optional, Set
|
4
|
+
|
5
|
+
from sympy import sympify
|
6
|
+
|
7
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
8
|
+
from classiq.interface.generator.constant import Constant
|
9
|
+
from classiq.interface.generator.expressions.evaluated_expression import (
|
10
|
+
EvaluatedExpression,
|
11
|
+
)
|
12
|
+
from classiq.interface.generator.expressions.expression import Expression
|
13
|
+
from classiq.interface.generator.expressions.expression_constants import (
|
14
|
+
CPARAM_EXECUTION_SUFFIX,
|
15
|
+
)
|
16
|
+
from classiq.interface.generator.expressions.expression_types import ExpressionValue
|
17
|
+
from classiq.interface.generator.expressions.sympy_supported_expressions import (
|
18
|
+
SYMPY_SUPPORTED_EXPRESSIONS,
|
19
|
+
)
|
20
|
+
|
21
|
+
from classiq.model_expansions.atomic_expression_functions_defs import (
|
22
|
+
ATOMIC_EXPRESSION_FUNCTIONS,
|
23
|
+
qmod_val_to_python,
|
24
|
+
)
|
25
|
+
from classiq.model_expansions.model_tables import SymbolTable
|
26
|
+
from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
|
27
|
+
translate_to_sympy,
|
28
|
+
)
|
29
|
+
from classiq.model_expansions.sympy_conversion.sympy_to_python import sympy_to_python
|
30
|
+
from classiq.qmod import symbolic
|
31
|
+
|
32
|
+
|
33
|
+
def evaluate_constants(constants: List[Constant]) -> Dict[str, EvaluatedExpression]:
|
34
|
+
result: Dict[str, EvaluatedExpression] = {}
|
35
|
+
for constant in constants:
|
36
|
+
result[constant.name] = evaluate(constant.value, result)
|
37
|
+
return result
|
38
|
+
|
39
|
+
|
40
|
+
def evaluate_constants_as_python(constants: List[Constant]) -> Dict[str, Any]:
|
41
|
+
evaluated = evaluate_constants(constants)
|
42
|
+
return {
|
43
|
+
constant.name: qmod_val_to_python(
|
44
|
+
evaluated[constant.name].value, constant.const_type
|
45
|
+
)
|
46
|
+
for constant in constants
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
def evaluate(
|
51
|
+
expr: Expression,
|
52
|
+
locals_dict: Mapping[str, EvaluatedExpression],
|
53
|
+
uninitialized_locals: Optional[Set[str]] = None,
|
54
|
+
) -> EvaluatedExpression:
|
55
|
+
model_locals: Dict[str, ExpressionValue] = {}
|
56
|
+
model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
|
57
|
+
model_locals.update(SymbolTable.enum_table.enums)
|
58
|
+
# locals override builtin-functions
|
59
|
+
model_locals.update({name: expr.value for name, expr in locals_dict.items()})
|
60
|
+
uninitialized_locals = uninitialized_locals or set()
|
61
|
+
|
62
|
+
_validate_undefined_vars(expr.expr, model_locals, uninitialized_locals)
|
63
|
+
|
64
|
+
sympy_expr = translate_to_sympy(expr.expr)
|
65
|
+
try:
|
66
|
+
sympify_result = sympify(sympy_expr, locals=model_locals)
|
67
|
+
except (TypeError, IndexError) as e:
|
68
|
+
raise ClassiqExpansionError(str(e)) from None
|
69
|
+
except AttributeError as e:
|
70
|
+
if isinstance(e.obj, EnumMeta):
|
71
|
+
raise ClassiqExpansionError(
|
72
|
+
f"Enum {e.obj.__name__} has no member {e.name!r}. Available members: "
|
73
|
+
f"{', '.join(e.obj.__members__)}"
|
74
|
+
) from e
|
75
|
+
raise
|
76
|
+
|
77
|
+
return EvaluatedExpression(
|
78
|
+
value=sympy_to_python(sympify_result, locals=model_locals)
|
79
|
+
)
|
80
|
+
|
81
|
+
|
82
|
+
def _validate_undefined_vars(
|
83
|
+
expr: str,
|
84
|
+
model_locals: Dict[str, ExpressionValue],
|
85
|
+
uninitialized_locals: Optional[Set[str]],
|
86
|
+
) -> None:
|
87
|
+
uninitialized_locals = uninitialized_locals or set()
|
88
|
+
id_visitor = _VarsCollector()
|
89
|
+
id_visitor.visit(ast.parse(expr))
|
90
|
+
identifiers = id_visitor.vars
|
91
|
+
undefined_vars = (
|
92
|
+
identifiers
|
93
|
+
- model_locals.keys()
|
94
|
+
- set(SYMPY_SUPPORTED_EXPRESSIONS)
|
95
|
+
- set(symbolic.__all__)
|
96
|
+
- uninitialized_locals
|
97
|
+
)
|
98
|
+
|
99
|
+
# Ignore expanded execution parameters
|
100
|
+
undefined_vars = {
|
101
|
+
var for var in undefined_vars if not var.endswith(CPARAM_EXECUTION_SUFFIX)
|
102
|
+
}
|
103
|
+
|
104
|
+
if len(undefined_vars) == 1:
|
105
|
+
undefined_var = undefined_vars.__iter__().__next__()
|
106
|
+
raise ClassiqExpansionError(f"Variable {undefined_var!r} is undefined")
|
107
|
+
elif len(undefined_vars) > 1:
|
108
|
+
raise ClassiqExpansionError(f"Variables {list(undefined_vars)} are undefined")
|
109
|
+
|
110
|
+
|
111
|
+
class _VarsCollector(ast.NodeTransformer):
|
112
|
+
def __init__(self) -> None:
|
113
|
+
self.vars: Set[str] = set()
|
114
|
+
|
115
|
+
def visit_Name(self, node: ast.Name) -> None:
|
116
|
+
self.vars.add(node.id)
|
117
|
+
|
118
|
+
def visit_Call(self, node: ast.Call) -> None:
|
119
|
+
func = node.func
|
120
|
+
self.visit(func)
|
121
|
+
if not isinstance(func, ast.Name) or func.id != "struct_literal":
|
122
|
+
for arg in node.args:
|
123
|
+
self.visit(arg)
|
124
|
+
for kw in node.keywords:
|
125
|
+
self.visit(kw)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Dict, Sequence
|
3
|
+
|
4
|
+
from classiq.interface.generator.expressions.expression import Expression
|
5
|
+
from classiq.interface.generator.expressions.expression_constants import (
|
6
|
+
RESERVED_EXPRESSIONS,
|
7
|
+
)
|
8
|
+
from classiq.interface.generator.visitor import Transformer
|
9
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
10
|
+
from classiq.interface.model.quantum_function_declaration import (
|
11
|
+
ClassicalParameterDeclaration,
|
12
|
+
PositionalArg,
|
13
|
+
)
|
14
|
+
|
15
|
+
from classiq.model_expansions.visitors.variable_references import VarRefTransformer
|
16
|
+
|
17
|
+
|
18
|
+
class ExpressionRenamer(Transformer):
|
19
|
+
|
20
|
+
def __init__(self, var_mapping: Dict[str, str]) -> None:
|
21
|
+
self.var_ref_transformer = VarRefTransformer(var_mapping)
|
22
|
+
|
23
|
+
@property
|
24
|
+
def var_mapping(self) -> Dict[str, str]:
|
25
|
+
return self.var_ref_transformer.var_mapping
|
26
|
+
|
27
|
+
def visit_QuantumFunctionCall(
|
28
|
+
self, node: QuantumFunctionCall
|
29
|
+
) -> QuantumFunctionCall:
|
30
|
+
update_dict = dict(
|
31
|
+
positional_args=[self.visit(arg) for arg in node.positional_args],
|
32
|
+
)
|
33
|
+
return node.copy(update=update_dict)
|
34
|
+
|
35
|
+
def visit_Expression(self, expression: Expression) -> Expression:
|
36
|
+
parsed_expr = ast.parse(expression.expr)
|
37
|
+
renamed_expr = self.var_ref_transformer.visit(parsed_expr)
|
38
|
+
return Expression(expr=ast.unparse(renamed_expr))
|
39
|
+
|
40
|
+
def rename_string(self, name: str) -> str:
|
41
|
+
return self.var_mapping.get(name, name)
|
42
|
+
|
43
|
+
def rename_expression_dict(
|
44
|
+
self, expr_dict: Dict[str, Expression]
|
45
|
+
) -> Dict[str, Expression]:
|
46
|
+
return {
|
47
|
+
self.rename_string(key): self.visit(val) for key, val in expr_dict.items()
|
48
|
+
}
|
49
|
+
|
50
|
+
@classmethod
|
51
|
+
def from_positional_arg_declarations(
|
52
|
+
cls, positional_arg_declarations: Sequence[PositionalArg], suffix: str
|
53
|
+
) -> "ExpressionRenamer":
|
54
|
+
var_mapping: Dict[str, str] = dict()
|
55
|
+
for arg in positional_arg_declarations:
|
56
|
+
if not isinstance(arg, ClassicalParameterDeclaration):
|
57
|
+
continue
|
58
|
+
if arg.name in RESERVED_EXPRESSIONS:
|
59
|
+
continue
|
60
|
+
new_name = arg.name + suffix
|
61
|
+
var_mapping[arg.name] = new_name
|
62
|
+
|
63
|
+
return cls(var_mapping)
|
64
|
+
|
65
|
+
def rename_positional_arg_declarations(
|
66
|
+
self, positional_arg_declarations: Sequence[PositionalArg]
|
67
|
+
) -> Sequence[PositionalArg]:
|
68
|
+
return tuple(
|
69
|
+
(
|
70
|
+
arg.rename(self.var_mapping[arg.name])
|
71
|
+
if isinstance(arg, ClassicalParameterDeclaration)
|
72
|
+
and arg.name in self.var_mapping
|
73
|
+
else arg
|
74
|
+
)
|
75
|
+
for arg in positional_arg_declarations
|
76
|
+
)
|