classiq 0.86.1__py3-none-any.whl → 0.88.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.
Potentially problematic release.
This version of classiq might be problematic. Click here for more details.
- classiq/__init__.py +2 -0
- classiq/applications/__init__.py +1 -2
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
- classiq/applications/combinatorial_helpers/encoding_mapping.py +11 -15
- classiq/applications/combinatorial_helpers/encoding_utils.py +6 -6
- classiq/applications/combinatorial_helpers/memory.py +4 -4
- classiq/applications/combinatorial_helpers/optimization_model.py +5 -5
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +6 -10
- classiq/applications/combinatorial_helpers/pyomo_utils.py +27 -26
- classiq/applications/combinatorial_helpers/sympy_utils.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/encoding.py +4 -6
- classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +4 -4
- classiq/applications/combinatorial_helpers/transformations/ising_converter.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/penalty_support.py +3 -3
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -0
- classiq/applications/hamiltonian/pauli_decomposition.py +34 -2
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +117 -17
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -5
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +66 -16
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +48 -26
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +65 -72
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +13 -6
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +36 -19
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +17 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +24 -2
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +97 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +11 -26
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +56 -0
- classiq/evaluators/qmod_node_evaluators/piecewise_evaluation.py +40 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +3 -4
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +51 -24
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +28 -6
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +330 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/evaluators/type_type_match.py +1 -1
- classiq/execution/execution_session.py +18 -3
- classiq/execution/qnn.py +4 -1
- classiq/execution/user_budgets.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +10 -30
- classiq/interface/backend/quantum_backend_providers.py +63 -52
- classiq/interface/execution/primitives.py +1 -0
- classiq/interface/generator/application_apis/__init__.py +0 -1
- classiq/interface/generator/arith/binary_ops.py +107 -115
- classiq/interface/generator/arith/extremum_operations.py +33 -45
- classiq/interface/generator/arith/number_utils.py +4 -1
- classiq/interface/generator/circuit_code/types_and_constants.py +0 -9
- classiq/interface/generator/compiler_keywords.py +2 -0
- classiq/interface/generator/expressions/atomic_expression_functions.py +0 -2
- classiq/interface/generator/function_param_list.py +129 -5
- classiq/interface/generator/functions/classical_type.py +67 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +12 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- classiq/interface/generator/transpiler_basis_gates.py +5 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +0 -8
- classiq/interface/helpers/model_normalizer.py +2 -2
- classiq/interface/helpers/text_utils.py +7 -2
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/classical_if.py +48 -0
- classiq/interface/model/classical_parameter_declaration.py +4 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/port_declaration.py +12 -0
- classiq/interface/model/quantum_function_declaration.py +12 -0
- classiq/interface/model/quantum_type.py +117 -2
- classiq/interface/pretty_print/expression_to_qmod.py +7 -8
- classiq/interface/pyomo_extension/__init__.py +0 -4
- classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +2 -2
- classiq/model_expansions/arithmetic.py +43 -1
- classiq/model_expansions/arithmetic_compute_result_attrs.py +255 -0
- classiq/model_expansions/capturing/captured_vars.py +2 -5
- classiq/model_expansions/quantum_operations/allocate.py +23 -16
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -71
- classiq/model_expansions/quantum_operations/bind.py +15 -7
- classiq/model_expansions/quantum_operations/call_emitter.py +2 -10
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +2 -8
- classiq/open_library/functions/__init__.py +4 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/open_library/functions/state_preparation.py +47 -5
- classiq/qmod/builtins/__init__.py +0 -3
- classiq/qmod/builtins/classical_functions.py +0 -28
- classiq/qmod/builtins/enums.py +26 -20
- classiq/qmod/builtins/functions/__init__.py +0 -5
- classiq/qmod/builtins/operations.py +142 -0
- classiq/qmod/builtins/structs.py +33 -29
- classiq/qmod/native/pretty_printer.py +1 -1
- classiq/qmod/pretty_print/expression_to_python.py +1 -6
- classiq/qmod/pretty_print/pretty_printer.py +4 -1
- classiq/qmod/qmod_variable.py +94 -2
- classiq/qmod/semantics/annotation/call_annotation.py +4 -2
- classiq/qmod/semantics/annotation/qstruct_annotator.py +20 -5
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/METADATA +5 -5
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/RECORD +106 -124
- classiq/applications/finance/__init__.py +0 -15
- classiq/interface/finance/finance_modelling_params.py +0 -11
- classiq/interface/finance/function_input.py +0 -102
- classiq/interface/finance/gaussian_model_input.py +0 -50
- classiq/interface/finance/log_normal_model_input.py +0 -40
- classiq/interface/finance/model_input.py +0 -22
- classiq/interface/generator/amplitude_estimation.py +0 -34
- classiq/interface/generator/application_apis/finance_declarations.py +0 -108
- classiq/interface/generator/expressions/enums/__init__.py +0 -0
- classiq/interface/generator/expressions/enums/finance_functions.py +0 -12
- classiq/interface/generator/finance.py +0 -107
- classiq/interface/generator/function_param_list_without_self_reference.py +0 -160
- classiq/interface/generator/grover_diffuser.py +0 -93
- classiq/interface/generator/grover_operator.py +0 -106
- classiq/interface/generator/oracles/__init__.py +0 -3
- classiq/interface/generator/oracles/arithmetic_oracle.py +0 -82
- classiq/interface/generator/oracles/custom_oracle.py +0 -65
- classiq/interface/generator/oracles/oracle_abc.py +0 -76
- classiq/interface/generator/oracles/oracle_function_param_list.py +0 -6
- classiq/interface/generator/piecewise_linear_amplitude_loading.py +0 -165
- classiq/interface/generator/qpe.py +0 -169
- classiq/interface/grover/__init__.py +0 -0
- classiq/interface/grover/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- classiq/qmod/builtins/functions/finance.py +0 -34
- /classiq/{interface/finance → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/WHEEL +0 -0
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Union
|
|
|
3
3
|
import sympy
|
|
4
4
|
|
|
5
5
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
|
6
|
-
from classiq.interface.exceptions import ClassiqValueError
|
|
6
|
+
from classiq.interface.exceptions import ClassiqExpansionError, ClassiqValueError
|
|
7
7
|
from classiq.interface.generator.expressions.expression import Expression
|
|
8
8
|
from classiq.interface.model.allocate import Allocate
|
|
9
9
|
from classiq.interface.model.handle_binding import NestedHandleBinding
|
|
@@ -12,7 +12,9 @@ from classiq.interface.model.quantum_type import (
|
|
|
12
12
|
QuantumNumeric,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
from classiq.evaluators.
|
|
15
|
+
from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
|
|
16
|
+
inject_quantum_type_attributes_inplace,
|
|
17
|
+
)
|
|
16
18
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
|
17
19
|
from classiq.model_expansions.scope import QuantumSymbol
|
|
18
20
|
|
|
@@ -76,7 +78,7 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
|
76
78
|
target: QuantumSymbol,
|
|
77
79
|
op_update_dict: dict[str, Expression],
|
|
78
80
|
) -> None:
|
|
79
|
-
if target.quantum_type.
|
|
81
|
+
if target.quantum_type.has_size_in_bits:
|
|
80
82
|
expr = str(target.quantum_type.size_in_bits)
|
|
81
83
|
elif self._allow_symbolic_attrs:
|
|
82
84
|
expr = f"{target.handle}.size"
|
|
@@ -95,11 +97,14 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
|
95
97
|
size_value = self._interpret_size(size, str(target.handle))
|
|
96
98
|
op_update_dict["size"] = Expression(expr=str(size_value))
|
|
97
99
|
|
|
98
|
-
if not isinstance(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
if not isinstance(
|
|
101
|
+
size_value, sympy.Basic
|
|
102
|
+
) and not inject_quantum_type_attributes_inplace(
|
|
103
|
+
QuantumBitvector(length=op_update_dict["size"]), target.quantum_type
|
|
104
|
+
):
|
|
105
|
+
raise ClassiqExpansionError(
|
|
106
|
+
f"Cannot allocate {op_update_dict['size']} qubits for variable "
|
|
107
|
+
f"{str(target)!r} of type {target.quantum_type.qmod_type_name}"
|
|
103
108
|
)
|
|
104
109
|
|
|
105
110
|
def _handle_with_numeric_attrs(
|
|
@@ -127,15 +132,17 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
|
127
132
|
isinstance(size_value, sympy.Basic)
|
|
128
133
|
or isinstance(is_signed_value, sympy.Basic)
|
|
129
134
|
or isinstance(fraction_digits_value, sympy.Basic)
|
|
135
|
+
) and not inject_quantum_type_attributes_inplace(
|
|
136
|
+
QuantumNumeric(
|
|
137
|
+
size=op_update_dict["size"],
|
|
138
|
+
is_signed=op_update_dict["is_signed"],
|
|
139
|
+
fraction_digits=op_update_dict["fraction_digits"],
|
|
140
|
+
),
|
|
141
|
+
target.quantum_type,
|
|
130
142
|
):
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
is_signed=op_update_dict["is_signed"],
|
|
135
|
-
fraction_digits=op_update_dict["fraction_digits"],
|
|
136
|
-
),
|
|
137
|
-
target.quantum_type,
|
|
138
|
-
var_name,
|
|
143
|
+
raise ClassiqExpansionError(
|
|
144
|
+
f"Cannot allocate {op_update_dict['size']} qubits for variable "
|
|
145
|
+
f"{var_name!r} of type {target.quantum_type.qmod_type_name}"
|
|
139
146
|
)
|
|
140
147
|
|
|
141
148
|
def _interpret_size(
|
|
@@ -44,9 +44,7 @@ def convert_inplace_op_bool_expression(
|
|
|
44
44
|
if not is_bool(op.expression.expr):
|
|
45
45
|
return
|
|
46
46
|
_validate_target_type(target, op.expression.expr, op.operation_kind)
|
|
47
|
-
op.expression = op.expression.
|
|
48
|
-
update=dict(expr="1" if op.expression.expr == "True" else "0")
|
|
49
|
-
)
|
|
47
|
+
op.expression = Expression(expr="1" if op.expression.expr == "True" else "0")
|
|
50
48
|
|
|
51
49
|
|
|
52
50
|
def _supported_types() -> tuple[str, ...]:
|
|
@@ -27,7 +27,9 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
|
27
27
|
)
|
|
28
28
|
from classiq.interface.model.within_apply_operation import WithinApply
|
|
29
29
|
|
|
30
|
-
from classiq.evaluators.
|
|
30
|
+
from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
|
|
31
|
+
inject_quantum_type_attributes_inplace,
|
|
32
|
+
)
|
|
31
33
|
from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_expressions import (
|
|
32
34
|
convert_assignment_bool_expression,
|
|
33
35
|
validate_assignment_bool_expression,
|
|
@@ -67,30 +69,38 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
|
67
69
|
)
|
|
68
70
|
convert_assignment_bool_expression(op)
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
if
|
|
72
|
+
expression_type = self._infer_expression_type(op)
|
|
73
|
+
if expression_type is None:
|
|
72
74
|
return False
|
|
73
75
|
|
|
74
|
-
if not
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
if not result_type.is_instantiated:
|
|
77
|
+
if not inject_quantum_type_attributes_inplace(expression_type, result_type):
|
|
78
|
+
raise ClassiqExpansionError(
|
|
79
|
+
f"Cannot assign expression of type "
|
|
80
|
+
f"{expression_type.qmod_type_name} to variable "
|
|
81
|
+
f"{str(result_symbol)!r} of type "
|
|
82
|
+
f"{result_symbol.quantum_type.qmod_type_name}"
|
|
83
|
+
)
|
|
78
84
|
return False
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
if isinstance(result_type, QuantumNumeric) and isinstance(
|
|
87
|
+
expression_type, QuantumNumeric
|
|
88
|
+
):
|
|
89
|
+
result_type.set_bounds(expression_type.get_bounds())
|
|
90
|
+
if self._same_numeric_attributes(result_type, expression_type):
|
|
82
91
|
return False
|
|
83
|
-
|
|
84
92
|
self._validate_declared_attributes(
|
|
85
|
-
result_type,
|
|
93
|
+
result_type, expression_type, str(op.result_var)
|
|
86
94
|
)
|
|
87
|
-
if self._replace_assignment_if_needed:
|
|
88
|
-
self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
|
|
89
|
-
return True
|
|
90
|
-
else:
|
|
95
|
+
if not self._replace_assignment_if_needed:
|
|
91
96
|
return False
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
self._assign_to_inferred_var_and_bind(op, result_type, expression_type)
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
def _infer_expression_type(
|
|
102
|
+
self, op: ArithmeticOperation
|
|
103
|
+
) -> Optional[QuantumNumeric]:
|
|
94
104
|
expr = self._evaluate_expression(op.expression)
|
|
95
105
|
if len(self._get_classical_vars_in_expression(expr)):
|
|
96
106
|
return None
|
|
@@ -114,96 +124,72 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
|
114
124
|
self._machine_precision,
|
|
115
125
|
)
|
|
116
126
|
|
|
117
|
-
@staticmethod
|
|
118
|
-
def _copy_numeric_attributes(
|
|
119
|
-
result_type: QuantumNumeric, inferred_result_type: QuantumNumeric
|
|
120
|
-
) -> None:
|
|
121
|
-
if not result_type.has_size_in_bits:
|
|
122
|
-
result_type.size = Expression(expr=str(inferred_result_type.size_in_bits))
|
|
123
|
-
if not result_type.has_sign:
|
|
124
|
-
result_type.is_signed = Expression(
|
|
125
|
-
expr=str(inferred_result_type.sign_value)
|
|
126
|
-
)
|
|
127
|
-
if not result_type.has_fraction_digits:
|
|
128
|
-
result_type.fraction_digits = Expression(
|
|
129
|
-
expr=str(inferred_result_type.fraction_digits_value)
|
|
130
|
-
)
|
|
131
|
-
result_type.set_bounds(inferred_result_type.get_bounds())
|
|
132
|
-
|
|
133
127
|
@staticmethod
|
|
134
128
|
def _same_numeric_attributes(
|
|
135
|
-
result_type: QuantumNumeric,
|
|
129
|
+
result_type: QuantumNumeric, expression_type: QuantumNumeric
|
|
136
130
|
) -> bool:
|
|
137
131
|
return (
|
|
138
|
-
result_type.size_in_bits ==
|
|
139
|
-
and result_type.sign_value ==
|
|
132
|
+
result_type.size_in_bits == expression_type.size_in_bits
|
|
133
|
+
and result_type.sign_value == expression_type.sign_value
|
|
140
134
|
and result_type.fraction_digits_value
|
|
141
|
-
==
|
|
135
|
+
== expression_type.fraction_digits_value
|
|
142
136
|
)
|
|
143
137
|
|
|
144
138
|
@classmethod
|
|
145
139
|
def _validate_declared_attributes(
|
|
146
|
-
cls, result_type: QuantumNumeric,
|
|
140
|
+
cls, result_type: QuantumNumeric, expression_type: QuantumNumeric, var: str
|
|
147
141
|
) -> None:
|
|
148
142
|
result_size, result_sign, result_fractions = (
|
|
149
143
|
result_type.size_in_bits,
|
|
150
144
|
result_type.sign_value,
|
|
151
145
|
result_type.fraction_digits_value,
|
|
152
146
|
)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
147
|
+
expression_size, expression_sign, expression_fractions = (
|
|
148
|
+
expression_type.size_in_bits,
|
|
149
|
+
expression_type.sign_value,
|
|
150
|
+
expression_type.fraction_digits_value,
|
|
157
151
|
)
|
|
158
152
|
result_integers = result_size - result_fractions
|
|
159
|
-
|
|
153
|
+
expression_integers = expression_size - expression_fractions
|
|
160
154
|
|
|
161
155
|
if (
|
|
162
|
-
(result_integers <
|
|
163
|
-
or (result_fractions <
|
|
164
|
-
or (not result_sign and
|
|
156
|
+
(result_integers < expression_integers)
|
|
157
|
+
or (result_fractions < expression_fractions)
|
|
158
|
+
or (not result_sign and expression_sign)
|
|
165
159
|
or (
|
|
166
160
|
result_sign
|
|
167
|
-
and not
|
|
168
|
-
and result_integers ==
|
|
161
|
+
and not expression_sign
|
|
162
|
+
and result_integers == expression_integers
|
|
169
163
|
)
|
|
170
164
|
):
|
|
171
165
|
if (
|
|
172
166
|
not result_sign
|
|
173
167
|
and result_fractions == 0
|
|
174
|
-
and not
|
|
175
|
-
and
|
|
168
|
+
and not expression_sign
|
|
169
|
+
and expression_fractions == 0
|
|
176
170
|
):
|
|
177
171
|
result_size_str = f"size {result_size}"
|
|
178
|
-
|
|
172
|
+
expression_size_str = f"size {expression_size}"
|
|
179
173
|
hint = f"Hint: increase the size in the declaration of {var!r} or omit it to enable automatic inference."
|
|
180
174
|
else:
|
|
181
175
|
result_size_str = f"size {result_size}, {'signed' if result_sign else 'unsigned'}, and {result_fractions} fraction digits"
|
|
182
|
-
|
|
176
|
+
expression_size_str = f"size {expression_size}, {'signed' if expression_sign else 'unsigned'}, and {expression_fractions} fraction digits"
|
|
183
177
|
hint = f"Hint: omit the numeric attributes from the declaration of {var!r} to enable automatic inference."
|
|
184
178
|
raise ClassiqExpansionError(
|
|
185
|
-
f"Cannot assign an expression with inferred {
|
|
179
|
+
f"Cannot assign an expression with inferred {expression_size_str} to variable {var!r} with declared {result_size_str}. {hint}"
|
|
186
180
|
)
|
|
187
181
|
|
|
188
|
-
@staticmethod
|
|
189
|
-
def _craft_size_string(size: int, is_signed: bool, fraction_digits: int) -> str:
|
|
190
|
-
extra = (
|
|
191
|
-
f", with {fraction_digits} fraction digits" if fraction_digits > 0 else ""
|
|
192
|
-
)
|
|
193
|
-
return f"{size} ({'signed' if is_signed else 'unsigned'}{extra})"
|
|
194
|
-
|
|
195
182
|
def _assign_to_inferred_var_and_bind(
|
|
196
183
|
self,
|
|
197
184
|
op: ArithmeticOperation,
|
|
198
185
|
result_type: QuantumNumeric,
|
|
199
|
-
|
|
186
|
+
expression_type: QuantumNumeric,
|
|
200
187
|
) -> None:
|
|
201
188
|
stmts: StatementBlock = []
|
|
202
189
|
handles: list[HandleBinding] = []
|
|
203
190
|
|
|
204
191
|
extra_fraction_digits = (
|
|
205
|
-
result_type.fraction_digits_value
|
|
206
|
-
- inferred_result_type.fraction_digits_value
|
|
192
|
+
result_type.fraction_digits_value - expression_type.fraction_digits_value
|
|
207
193
|
)
|
|
208
194
|
if extra_fraction_digits > 0:
|
|
209
195
|
handles.append(
|
|
@@ -216,7 +202,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
|
216
202
|
inferred_result_handle = HandleBinding(name=inferred_result_name)
|
|
217
203
|
stmts.append(
|
|
218
204
|
VariableDeclarationStatement(
|
|
219
|
-
name=inferred_result_name, qmod_type=
|
|
205
|
+
name=inferred_result_name, qmod_type=expression_type
|
|
220
206
|
)
|
|
221
207
|
)
|
|
222
208
|
handles.append(inferred_result_handle)
|
|
@@ -228,8 +214,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
|
228
214
|
result_type.size_in_bits - result_type.fraction_digits_value
|
|
229
215
|
)
|
|
230
216
|
inferred_result_integer_size = (
|
|
231
|
-
|
|
232
|
-
- inferred_result_type.fraction_digits_value
|
|
217
|
+
expression_type.size_in_bits - expression_type.fraction_digits_value
|
|
233
218
|
)
|
|
234
219
|
extra_integers = result_integer_size - inferred_result_integer_size
|
|
235
220
|
if extra_integers > 0:
|
|
@@ -239,17 +224,13 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
|
239
224
|
|
|
240
225
|
stmts.append(BindOperation(in_handles=handles, out_handles=[op.result_var]))
|
|
241
226
|
|
|
242
|
-
if
|
|
243
|
-
result_type.sign_value
|
|
244
|
-
and inferred_result_type.sign_value
|
|
245
|
-
and extra_integers > 0
|
|
246
|
-
):
|
|
227
|
+
if result_type.sign_value and expression_type.sign_value and extra_integers > 0:
|
|
247
228
|
sign_idx = result_type.size_in_bits - extra_integers - 1
|
|
248
229
|
self._sign_extension(
|
|
249
230
|
op.result_var, sign_idx, result_type.size_in_bits, stmts
|
|
250
231
|
)
|
|
251
232
|
|
|
252
|
-
if (inferred_bounds :=
|
|
233
|
+
if (inferred_bounds := expression_type.get_bounds()) is not None:
|
|
253
234
|
lower_bound = Expression(expr=str(inferred_bounds[0]))
|
|
254
235
|
upper_bound = Expression(expr=str(inferred_bounds[1]))
|
|
255
236
|
else:
|
|
@@ -5,14 +5,17 @@ from classiq.interface.exceptions import (
|
|
|
5
5
|
ClassiqExpansionError,
|
|
6
6
|
ClassiqInternalExpansionError,
|
|
7
7
|
)
|
|
8
|
+
from classiq.interface.generator.expressions.expression import Expression
|
|
8
9
|
from classiq.interface.model.bind_operation import BindOperation
|
|
9
|
-
from classiq.interface.model.quantum_type import QuantumNumeric
|
|
10
|
+
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
|
|
10
11
|
|
|
11
12
|
from classiq.evaluators.parameter_types import (
|
|
12
13
|
evaluate_types_in_quantum_symbols,
|
|
13
14
|
)
|
|
15
|
+
from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
|
|
16
|
+
inject_quantum_type_attributes_inplace,
|
|
17
|
+
)
|
|
14
18
|
from classiq.evaluators.quantum_type_utils import (
|
|
15
|
-
set_size,
|
|
16
19
|
validate_bind_targets,
|
|
17
20
|
)
|
|
18
21
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
|
@@ -134,11 +137,16 @@ class BindEmitter(Emitter[BindOperation]):
|
|
|
134
137
|
if output.quantum_type.has_size_in_bits
|
|
135
138
|
)
|
|
136
139
|
if len(unsized_outputs) == 1:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
str(
|
|
141
|
-
)
|
|
140
|
+
unsized_type = unsized_outputs[0].quantum_type
|
|
141
|
+
new_size = input_size - output_size
|
|
142
|
+
if not inject_quantum_type_attributes_inplace(
|
|
143
|
+
QuantumBitvector(length=Expression(expr=str(new_size))), unsized_type
|
|
144
|
+
):
|
|
145
|
+
raise ClassiqExpansionError(
|
|
146
|
+
f"Cannot bind {new_size} qubits to variable "
|
|
147
|
+
f"{str(unsized_outputs[0])!r} of type "
|
|
148
|
+
f"{unsized_type.qmod_type_name}"
|
|
149
|
+
)
|
|
142
150
|
elif input_size != output_size:
|
|
143
151
|
raise ClassiqExpansionError(
|
|
144
152
|
f"The total size for the input and output of the bind operation must be the same. The in size is {input_size} and the out size is {output_size}"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from collections.abc import Sequence
|
|
2
2
|
from itertools import chain, combinations
|
|
3
3
|
from typing import (
|
|
4
|
-
TYPE_CHECKING,
|
|
5
4
|
Any,
|
|
6
5
|
Generic,
|
|
7
6
|
Optional,
|
|
@@ -80,18 +79,15 @@ from classiq.model_expansions.scope import (
|
|
|
80
79
|
QuantumVariable,
|
|
81
80
|
Scope,
|
|
82
81
|
)
|
|
82
|
+
from classiq.model_expansions.transformers.model_renamer import ModelRenamer
|
|
83
83
|
from classiq.model_expansions.transformers.type_modifier_inference import (
|
|
84
84
|
TypeModifierValidation,
|
|
85
85
|
)
|
|
86
|
-
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
|
87
86
|
from classiq.qmod.pretty_print.expression_to_python import transform_expression
|
|
88
87
|
from classiq.qmod.semantics.validation.signature_validation import (
|
|
89
88
|
validate_function_signature,
|
|
90
89
|
)
|
|
91
90
|
|
|
92
|
-
if TYPE_CHECKING:
|
|
93
|
-
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
|
94
|
-
|
|
95
91
|
|
|
96
92
|
def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
|
|
97
93
|
handles = chain.from_iterable(
|
|
@@ -145,11 +141,7 @@ def _validate_gen_args(
|
|
|
145
141
|
)
|
|
146
142
|
|
|
147
143
|
|
|
148
|
-
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT],
|
|
149
|
-
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
|
150
|
-
Emitter.__init__(self, interpreter)
|
|
151
|
-
VarSplitter.__init__(self, interpreter._builder.current_scope)
|
|
152
|
-
|
|
144
|
+
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelRenamer):
|
|
153
145
|
@staticmethod
|
|
154
146
|
def _should_wrap(body: Sequence[QuantumStatement]) -> bool:
|
|
155
147
|
# This protects shadowing of captured variables (i.e, bad user code) by wrapping the body in a function
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
|
1
4
|
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
|
2
5
|
ArithmeticOperation,
|
|
3
6
|
)
|
|
@@ -12,5 +15,8 @@ class ClassicalVarEmitter(Emitter[ArithmeticOperation]):
|
|
|
12
15
|
if not isinstance(result_symbol, ClassicalSymbol):
|
|
13
16
|
return False
|
|
14
17
|
op._classical_assignment = True
|
|
18
|
+
match = re.search(r"measure\((.*?)\)", op.expression.expr)
|
|
19
|
+
if match is not None:
|
|
20
|
+
op.set_var_handles([HandleBinding(name=match.group(1))])
|
|
15
21
|
self.emit_statement(op)
|
|
16
22
|
return True
|
|
@@ -19,11 +19,5 @@ class HandleEvaluator(Emitter[QuantumOperation]):
|
|
|
19
19
|
if not isinstance(handle, HandleBinding):
|
|
20
20
|
return False
|
|
21
21
|
evaluated_handle = self._interpreter.evaluate(handle).value.handle.collapse()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
op = op.model_copy(
|
|
25
|
-
update={self._handle_name: evaluated_handle, "back_ref": op.uuid}
|
|
26
|
-
)
|
|
27
|
-
self._interpreter.add_to_debug_info(op)
|
|
28
|
-
self._interpreter.emit(op)
|
|
29
|
-
return True
|
|
22
|
+
setattr(op, self._handle_name, evaluated_handle)
|
|
23
|
+
return False
|
|
@@ -8,6 +8,7 @@ from .discrete_sine_cosine_transform import _qct_d_operator, _qct_pi_operator
|
|
|
8
8
|
from .grover import *
|
|
9
9
|
from .grover import _cond_phase_flip
|
|
10
10
|
from .hea import *
|
|
11
|
+
from .lcu import *
|
|
11
12
|
from .linear_pauli_rotation import *
|
|
12
13
|
from .linear_pauli_rotation import _single_pauli
|
|
13
14
|
from .lookup_table import span_lookup_table
|
|
@@ -100,6 +101,8 @@ __all__ = [
|
|
|
100
101
|
"inplace_c_modular_multiply",
|
|
101
102
|
"inplace_prepare_complex_amplitudes",
|
|
102
103
|
"inplace_prepare_int",
|
|
104
|
+
"lcu",
|
|
105
|
+
"lcu_pauli",
|
|
103
106
|
"linear_pauli_rotations",
|
|
104
107
|
"modular_exp",
|
|
105
108
|
"modular_increment",
|
|
@@ -113,6 +116,7 @@ __all__ = [
|
|
|
113
116
|
"prepare_exponential_state",
|
|
114
117
|
"prepare_ghz_state",
|
|
115
118
|
"prepare_int",
|
|
119
|
+
"prepare_linear_amplitudes",
|
|
116
120
|
"prepare_uniform_interval_state",
|
|
117
121
|
"prepare_uniform_trimmed_state",
|
|
118
122
|
"projector_controlled_double_phase",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from classiq.open_library.functions.state_preparation import (
|
|
6
|
+
apply_phase_table,
|
|
7
|
+
)
|
|
8
|
+
from classiq.open_library.functions.utility_functions import switch
|
|
9
|
+
from classiq.qmod.builtins.functions import IDENTITY, X, Y, Z, inplace_prepare_state
|
|
10
|
+
from classiq.qmod.builtins.operations import (
|
|
11
|
+
control,
|
|
12
|
+
repeat,
|
|
13
|
+
within_apply,
|
|
14
|
+
)
|
|
15
|
+
from classiq.qmod.builtins.structs import IndexedPauli, SparsePauliOp
|
|
16
|
+
from classiq.qmod.cparam import CArray
|
|
17
|
+
from classiq.qmod.qfunc import qfunc
|
|
18
|
+
from classiq.qmod.qmod_variable import QArray, QBit, QNum
|
|
19
|
+
from classiq.qmod.quantum_callable import QCallableList
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@qfunc
|
|
23
|
+
def apply_pauli_term(pauli_string: CArray[IndexedPauli], x: QArray[QBit]) -> None:
|
|
24
|
+
repeat(
|
|
25
|
+
count=pauli_string.len,
|
|
26
|
+
iteration=lambda i: switch(
|
|
27
|
+
pauli_string[i].pauli,
|
|
28
|
+
[
|
|
29
|
+
lambda: IDENTITY(x[pauli_string[i].index]),
|
|
30
|
+
lambda: X(x[pauli_string[i].index]),
|
|
31
|
+
lambda: Y(x[pauli_string[i].index]),
|
|
32
|
+
lambda: Z(x[pauli_string[i].index]),
|
|
33
|
+
],
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@qfunc
|
|
39
|
+
def lcu(
|
|
40
|
+
coefficients: list[float],
|
|
41
|
+
unitaries: QCallableList,
|
|
42
|
+
block: QNum[Literal["max(ceiling(log(coefficients.len, 2)), 1)"]],
|
|
43
|
+
) -> None:
|
|
44
|
+
"""
|
|
45
|
+
[Qmod Classiq-library function]
|
|
46
|
+
|
|
47
|
+
Implements a general linear combination of unitaries (LCU) procedure. The algorithm prepares a superposition
|
|
48
|
+
over the `unitaries` according to `magnitudes`, and then conditionally applies each unitary controlled by the `block`.
|
|
49
|
+
|
|
50
|
+
The operation is of the form:
|
|
51
|
+
|
|
52
|
+
$$\\sum_j \\alpha_j U_j$$
|
|
53
|
+
|
|
54
|
+
where $U_j$ is a unitary operation applied to `data`.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
coefficients: L1-normalized array of $\\{ \\alpha_j \\}$ of the LCU coefficients.
|
|
58
|
+
unitaries: A list of quantum callable functions to be applied conditionally.
|
|
59
|
+
block: Quantum variable that holds the superposition index used for conditional application of each unitary.
|
|
60
|
+
"""
|
|
61
|
+
coefficients = coefficients + [0] * (
|
|
62
|
+
2**block.size - len(coefficients) # type:ignore[operator]
|
|
63
|
+
)
|
|
64
|
+
magnitudes = [np.abs(c) for c in coefficients]
|
|
65
|
+
magnitudes = (np.array(magnitudes) / np.sum(magnitudes)).tolist()
|
|
66
|
+
phases = [np.angle(complex(c)) for c in coefficients]
|
|
67
|
+
|
|
68
|
+
within_apply(
|
|
69
|
+
lambda: inplace_prepare_state(magnitudes, 0, block),
|
|
70
|
+
lambda: [
|
|
71
|
+
repeat(
|
|
72
|
+
count=unitaries.len,
|
|
73
|
+
iteration=lambda i: control(block == i, lambda: unitaries[i]()),
|
|
74
|
+
),
|
|
75
|
+
# TODO: replace to sparse constant phase
|
|
76
|
+
apply_phase_table(phases, block),
|
|
77
|
+
],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@qfunc
|
|
82
|
+
def lcu_pauli(
|
|
83
|
+
operator: SparsePauliOp,
|
|
84
|
+
data: QArray[QBit, Literal["operator.num_qubits"]],
|
|
85
|
+
block: QNum[Literal["max(ceiling(log(operator.terms.len, 2)), 1)"]],
|
|
86
|
+
) -> None:
|
|
87
|
+
"""
|
|
88
|
+
[Qmod Classiq-library function]
|
|
89
|
+
|
|
90
|
+
Applies a linear combination of unitaries (LCU) where each unitary is a Pauli term,
|
|
91
|
+
represented as a tensor product of Pauli operators. The function prepares a superposition
|
|
92
|
+
over the unitaries according to the given magnitudes and phases, and applies the corresponding
|
|
93
|
+
Pauli operators conditionally.
|
|
94
|
+
|
|
95
|
+
This is useful for implementing Hamiltonian terms of the form:
|
|
96
|
+
|
|
97
|
+
$$H=\\sum_j \\alpha_j P_j$$
|
|
98
|
+
|
|
99
|
+
where $P_j$ is a tensor product of Pauli operators.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
operator: Operator consists of pauli strings with their coefficients, represented in a sparse format.
|
|
103
|
+
data: Quantum Variable on which the Pauli operators act. Its size must match the number of qubits required by the Pauli operator.
|
|
104
|
+
block: Quantum variable that holds the superposition index used for conditional application of each term.
|
|
105
|
+
"""
|
|
106
|
+
coefficients = [
|
|
107
|
+
operator.terms[i].coefficient
|
|
108
|
+
for i in range(operator.terms.len) # type:ignore[attr-defined]
|
|
109
|
+
]
|
|
110
|
+
lcu(
|
|
111
|
+
coefficients,
|
|
112
|
+
[
|
|
113
|
+
lambda i=i: apply_pauli_term(operator.terms[i].paulis, data)
|
|
114
|
+
for i in range(operator.terms.len) # type:ignore[attr-defined]
|
|
115
|
+
],
|
|
116
|
+
block,
|
|
117
|
+
)
|
|
@@ -13,8 +13,8 @@ from classiq.open_library.functions.utility_functions import (
|
|
|
13
13
|
from classiq.qmod.builtins.functions import (
|
|
14
14
|
CX,
|
|
15
15
|
IDENTITY,
|
|
16
|
+
PHASE,
|
|
16
17
|
RY,
|
|
17
|
-
RZ,
|
|
18
18
|
H,
|
|
19
19
|
X,
|
|
20
20
|
inplace_prepare_amplitudes,
|
|
@@ -324,7 +324,7 @@ def _classical_hadamard_transform(arr: list[float]) -> np.ndarray:
|
|
|
324
324
|
|
|
325
325
|
|
|
326
326
|
@qfunc
|
|
327
|
-
def
|
|
327
|
+
def apply_phase_table(
|
|
328
328
|
phases: list[float],
|
|
329
329
|
target: QArray[QBit, Literal["log(get_field(phases, 'len'), 2)"]],
|
|
330
330
|
) -> None:
|
|
@@ -333,10 +333,10 @@ def _load_phases(
|
|
|
333
333
|
for i in range(1, len(alphas) - 1):
|
|
334
334
|
gray = _graycode(i)
|
|
335
335
|
next_gray = _graycode(i + 1)
|
|
336
|
-
|
|
336
|
+
PHASE(alphas[gray], target[_msb(gray)])
|
|
337
337
|
CX(target[_control_qubit(i)], target[_msb(next_gray)])
|
|
338
338
|
|
|
339
|
-
|
|
339
|
+
PHASE(alphas[_graycode(len(phases) - 1)], target[target.len - 1])
|
|
340
340
|
|
|
341
341
|
|
|
342
342
|
@qfunc
|
|
@@ -358,7 +358,7 @@ def inplace_prepare_complex_amplitudes(
|
|
|
358
358
|
target: The quantum variable to act upon.
|
|
359
359
|
"""
|
|
360
360
|
inplace_prepare_amplitudes(magnitudes, 0, target)
|
|
361
|
-
|
|
361
|
+
apply_phase_table(phases, target)
|
|
362
362
|
|
|
363
363
|
|
|
364
364
|
@qfunc
|
|
@@ -469,3 +469,45 @@ def prepare_basis_state(state: list[bool], arr: Output[QArray]) -> None:
|
|
|
469
469
|
for idx, value in enumerate(state):
|
|
470
470
|
if value:
|
|
471
471
|
X(arr[idx])
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def linear_hadamard_walsh_coefficients(n: int) -> np.ndarray:
|
|
475
|
+
coeffs = np.zeros(n + 1)
|
|
476
|
+
coeffs[0] = 2 ** (n / 2) * ((2**n - 1) / 2)
|
|
477
|
+
for k in range(1, n + 1):
|
|
478
|
+
coeffs[k] = -(2 ** (k - 1 + n / 2) / 2)
|
|
479
|
+
return coeffs / np.linalg.norm(coeffs)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
@qfunc
|
|
483
|
+
def _zero_ctrl_rot(ctrl: QNum, target: QBit, theta: CReal) -> None:
|
|
484
|
+
control(ctrl == 0, lambda: RY(theta, target))
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
@qfunc
|
|
488
|
+
def prepare_linear_amplitudes(x: QArray) -> None:
|
|
489
|
+
""" "
|
|
490
|
+
[Qmod Classiq-library function]
|
|
491
|
+
|
|
492
|
+
Initializes a quantum variable in a state with linear amplitudes:
|
|
493
|
+
$$|\\psi\rangle = \frac{1}{Z}\\sum_{x=0}^{2^n-1}{x|x\rangle}$$
|
|
494
|
+
Where $Z$ is a normalization constant.
|
|
495
|
+
|
|
496
|
+
Based on the paper https://quantum-journal.org/papers/q-2024-03-21-1297/pdf/
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
x: The quantum register to prepare.
|
|
500
|
+
"""
|
|
501
|
+
coeffs = linear_hadamard_walsh_coefficients(x.size) # type: ignore[arg-type]
|
|
502
|
+
thetas = np.zeros(x.size + 1) # type: ignore[arg-type]
|
|
503
|
+
for i in range(x.size): # type: ignore[arg-type]
|
|
504
|
+
thetas[i] = 2 * np.arcsin(
|
|
505
|
+
coeffs[i + 1] / np.sqrt(1 - np.linalg.norm(coeffs[1 : i + 1]) ** 2)
|
|
506
|
+
)
|
|
507
|
+
for k in range(x.len):
|
|
508
|
+
if k == 0:
|
|
509
|
+
RY(thetas[k], x[k])
|
|
510
|
+
else:
|
|
511
|
+
_zero_ctrl_rot(x[0:k], x[k], thetas[k])
|
|
512
|
+
|
|
513
|
+
hadamard_transform(x)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from classiq.interface.finance.function_input import FinanceFunctionInput
|
|
2
|
-
|
|
3
1
|
from .classical_execution_primitives import * # noqa: F403
|
|
4
2
|
from .classical_execution_primitives import (
|
|
5
3
|
__all__ as _builtin_classical_execution_primitives,
|
|
@@ -17,7 +15,6 @@ from .operations import __all__ as _builtin_operations
|
|
|
17
15
|
from .structs import * # noqa: F403
|
|
18
16
|
from .structs import __all__ as _builtin_structs
|
|
19
17
|
|
|
20
|
-
FinanceFunctionInput.model_rebuild()
|
|
21
18
|
BUILTIN_CONSTANTS = [
|
|
22
19
|
constant._get_constant_node()
|
|
23
20
|
for constant in [
|