classiq 0.86.0__py3-none-any.whl → 0.87.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 +2 -0
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- 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 +33 -1
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +88 -11
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +54 -11
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +40 -25
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +29 -59
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +12 -5
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +21 -14
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +9 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +20 -1
- 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 +2 -3
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +48 -21
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +27 -5
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +292 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/execution/execution_session.py +1 -1
- 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/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/function_param_list.py +133 -5
- classiq/interface/generator/functions/classical_type.py +59 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +6 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- 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 +40 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/quantum_type.py +61 -2
- classiq/interface/pretty_print/expression_to_qmod.py +24 -11
- 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 +22 -15
- 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/open_library/functions/__init__.py +3 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/qmod/builtins/enums.py +2 -2
- classiq/qmod/builtins/structs.py +33 -18
- classiq/qmod/pretty_print/expression_to_python.py +7 -9
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/METADATA +3 -3
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/RECORD +83 -89
- classiq/interface/generator/amplitude_estimation.py +0 -34
- 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/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- /classiq/{interface/grover → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/WHEEL +0 -0
@@ -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
|
@@ -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",
|
@@ -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
|
+
_load_phases,
|
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
|
+
_load_phases(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
|
+
)
|
classiq/qmod/builtins/enums.py
CHANGED
@@ -188,7 +188,7 @@ class Pauli(IntEnum):
|
|
188
188
|
)
|
189
189
|
|
190
190
|
return SparsePauliOp(
|
191
|
-
terms=[
|
191
|
+
terms=[
|
192
192
|
SparsePauliTerm(
|
193
193
|
paulis=[ # type:ignore[arg-type]
|
194
194
|
IndexedPauli(pauli=self, index=index) # type:ignore[arg-type]
|
@@ -196,7 +196,7 @@ class Pauli(IntEnum):
|
|
196
196
|
coefficient=1.0, # type:ignore[arg-type]
|
197
197
|
)
|
198
198
|
],
|
199
|
-
num_qubits=index + 1,
|
199
|
+
num_qubits=index + 1,
|
200
200
|
)
|
201
201
|
|
202
202
|
|
classiq/qmod/builtins/structs.py
CHANGED
@@ -63,27 +63,31 @@ class SparsePauliOp:
|
|
63
63
|
num_qubits (CInt): The number of qubits in the Hamiltonian.
|
64
64
|
"""
|
65
65
|
|
66
|
-
terms:
|
67
|
-
num_qubits:
|
66
|
+
terms: list[SparsePauliTerm]
|
67
|
+
num_qubits: int
|
68
68
|
|
69
69
|
def __mul__(self, obj: Union[float, "SparsePauliOp"]) -> "SparsePauliOp":
|
70
70
|
if isinstance(obj, (int, float, complex)):
|
71
71
|
return SparsePauliOp(
|
72
|
-
terms=[
|
72
|
+
terms=[
|
73
73
|
SparsePauliTerm(
|
74
74
|
paulis=term.paulis,
|
75
|
-
coefficient=obj * term.coefficient,
|
75
|
+
coefficient=obj * term.coefficient, # type:ignore[arg-type]
|
76
76
|
)
|
77
|
-
for term in self.terms
|
77
|
+
for term in self.terms
|
78
78
|
],
|
79
79
|
num_qubits=self.num_qubits,
|
80
80
|
)
|
81
|
-
if len(self.terms) != 1 or len(obj.terms) != 1:
|
81
|
+
if len(self.terms) != 1 or len(obj.terms) != 1:
|
82
82
|
raise ClassiqValueError("Cannot attach a pauli to multiple pauli terms")
|
83
83
|
existing_indices = {
|
84
|
-
indexed_pauli.index
|
84
|
+
indexed_pauli.index
|
85
|
+
for indexed_pauli in self.terms[0].paulis # type:ignore[attr-defined]
|
86
|
+
}
|
87
|
+
added_indices = {
|
88
|
+
indexed_pauli.index
|
89
|
+
for indexed_pauli in obj.terms[0].paulis # type:ignore[attr-defined]
|
85
90
|
}
|
86
|
-
added_indices = {indexed_pauli.index for indexed_pauli in obj.terms[0].paulis}
|
87
91
|
overlapping_indices = sorted(existing_indices.intersection(added_indices))
|
88
92
|
if len(overlapping_indices):
|
89
93
|
raise ClassiqValueError(
|
@@ -93,15 +97,15 @@ class SparsePauliOp:
|
|
93
97
|
f"already assigned"
|
94
98
|
)
|
95
99
|
return SparsePauliOp(
|
96
|
-
terms=[
|
100
|
+
terms=[
|
97
101
|
SparsePauliTerm(
|
98
|
-
paulis=self.terms[0].paulis
|
99
|
-
|
102
|
+
paulis=self.terms[0].paulis
|
103
|
+
+ obj.terms[0].paulis, # type:ignore[arg-type]
|
104
|
+
coefficient=self.terms[0].coefficient
|
105
|
+
* obj.terms[0].coefficient, # type:ignore[arg-type]
|
100
106
|
)
|
101
107
|
],
|
102
|
-
num_qubits=max(
|
103
|
-
self.num_qubits, obj.num_qubits # type:ignore[call-overload]
|
104
|
-
),
|
108
|
+
num_qubits=max(self.num_qubits, obj.num_qubits),
|
105
109
|
)
|
106
110
|
|
107
111
|
def __rmul__(self, obj: Union[float, "SparsePauliOp"]) -> "SparsePauliOp":
|
@@ -109,10 +113,21 @@ class SparsePauliOp:
|
|
109
113
|
|
110
114
|
def __add__(self, other: "SparsePauliOp") -> "SparsePauliOp":
|
111
115
|
return SparsePauliOp(
|
112
|
-
terms=self.terms + other.terms,
|
113
|
-
num_qubits=max(
|
114
|
-
|
115
|
-
|
116
|
+
terms=self.terms + other.terms,
|
117
|
+
num_qubits=max(self.num_qubits, other.num_qubits),
|
118
|
+
)
|
119
|
+
|
120
|
+
def __sub__(self, other: "SparsePauliOp") -> "SparsePauliOp":
|
121
|
+
return self + -1.0 * other
|
122
|
+
|
123
|
+
def __str__(self) -> str:
|
124
|
+
return " + ".join(
|
125
|
+
(f"{term.coefficient}*" if term.coefficient != 1 else "")
|
126
|
+
+ "*".join(
|
127
|
+
f"Pauli.{indexed_pauli.pauli.name}({indexed_pauli.index})"
|
128
|
+
for indexed_pauli in term.paulis # type:ignore[attr-defined]
|
129
|
+
)
|
130
|
+
for term in self.terms
|
116
131
|
)
|
117
132
|
|
118
133
|
|
@@ -71,12 +71,7 @@ class ASTToQMODCode(ast.NodeVisitor):
|
|
71
71
|
return self.indent.join(self.visit(child) for child in node.body)
|
72
72
|
|
73
73
|
def visit_Attribute(self, node: ast.Attribute) -> str:
|
74
|
-
|
75
|
-
raise AssertionError("Error parsing enum attribute access")
|
76
|
-
if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
|
77
|
-
raise AssertionError("Error parsing enum attribute access")
|
78
|
-
self._handle_imports(node.value.id)
|
79
|
-
return f"{node.value.id!s}.{node.attr!s}"
|
74
|
+
return f"{self.visit(node.value)}.{node.attr}"
|
80
75
|
|
81
76
|
def visit_Name(self, node: ast.Name) -> str:
|
82
77
|
self._handle_imports(node.id, True)
|
@@ -159,9 +154,12 @@ class ASTToQMODCode(ast.NodeVisitor):
|
|
159
154
|
)
|
160
155
|
return f"{self.visit(node.args[0])}({initializer_list})"
|
161
156
|
else:
|
162
|
-
|
163
|
-
|
164
|
-
|
157
|
+
args = [self._cleaned_ast_to_code(arg) for arg in node.args]
|
158
|
+
kwargs = [
|
159
|
+
f"{kwarg.arg}={self._cleaned_ast_to_code(kwarg.value)}"
|
160
|
+
for kwarg in node.keywords
|
161
|
+
]
|
162
|
+
return "{}({})".format(func, ", ".join(args + kwargs))
|
165
163
|
|
166
164
|
def visit_Expr(self, node: ast.Expr) -> str:
|
167
165
|
return self._cleaned_ast_to_code(node.value)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: classiq
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.87.0
|
4
4
|
Summary: Classiq's Python SDK for quantum computing
|
5
5
|
License: Proprietary
|
6
6
|
Keywords: quantum computing,quantum circuits,quantum algorithms,QAD,QDL
|
@@ -30,14 +30,14 @@ Provides-Extra: analyzer-sdk
|
|
30
30
|
Provides-Extra: chemistry
|
31
31
|
Provides-Extra: qml
|
32
32
|
Requires-Dist: ConfigArgParse (>=1.5.3,<2.0.0)
|
33
|
-
Requires-Dist: Pyomo (>=6.
|
33
|
+
Requires-Dist: Pyomo (>=6.9,<6.10)
|
34
34
|
Requires-Dist: black (>=24.0,<25.0)
|
35
35
|
Requires-Dist: httpx (>=0.23.0,<1)
|
36
36
|
Requires-Dist: ipywidgets ; extra == "analyzer-sdk"
|
37
37
|
Requires-Dist: jupyterlab ; extra == "analyzer-sdk"
|
38
38
|
Requires-Dist: keyring (>=23.5.0,<24.0.0)
|
39
39
|
Requires-Dist: matplotlib (>=3.4.3,<4.0.0)
|
40
|
-
Requires-Dist: networkx (>=2.
|
40
|
+
Requires-Dist: networkx (>=3.2.0,<4.0.0)
|
41
41
|
Requires-Dist: notebook ; extra == "analyzer-sdk"
|
42
42
|
Requires-Dist: numexpr (>=2.7.3,<3.0.0)
|
43
43
|
Requires-Dist: numpy (>=1.20.1,<2.0.0) ; python_version < "3.12"
|