classiq 0.37.1__py3-none-any.whl → 0.38.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 -2
- classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
- classiq/_analyzer_extras/interactive_hardware.py +3 -3
- classiq/_internals/api_wrapper.py +24 -16
- classiq/_internals/async_utils.py +1 -74
- classiq/_internals/authentication/device.py +9 -4
- classiq/_internals/authentication/password_manager.py +25 -10
- classiq/_internals/authentication/token_manager.py +2 -2
- classiq/_internals/client.py +13 -5
- classiq/_internals/jobs.py +10 -7
- classiq/analyzer/analyzer.py +26 -28
- classiq/analyzer/analyzer_utilities.py +5 -5
- classiq/analyzer/rb.py +4 -5
- classiq/analyzer/show_interactive_hack.py +6 -6
- classiq/applications/benchmarking/mirror_benchmarking.py +9 -6
- classiq/applications/combinatorial_optimization/__init__.py +5 -0
- classiq/applications/qnn/circuit_utils.py +2 -2
- classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
- classiq/applications/qnn/types.py +2 -2
- classiq/applications/qsvm/qsvm.py +4 -7
- classiq/applications/qsvm/qsvm_data_generation.py +2 -5
- classiq/applications_model_constructors/__init__.py +9 -1
- classiq/applications_model_constructors/chemistry_model_constructor.py +9 -16
- classiq/applications_model_constructors/combinatorial_helpers/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/allowed_constraints.py +20 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/isolation.py +42 -0
- classiq/applications_model_constructors/combinatorial_helpers/combinatorial_problem_utils.py +130 -0
- classiq/applications_model_constructors/combinatorial_helpers/encoding_mapping.py +107 -0
- classiq/applications_model_constructors/combinatorial_helpers/encoding_utils.py +122 -0
- classiq/applications_model_constructors/combinatorial_helpers/memory.py +79 -0
- classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +34 -0
- classiq/applications_model_constructors/combinatorial_helpers/optimization_model.py +166 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_utils.py +65 -0
- classiq/applications_model_constructors/combinatorial_helpers/py.typed +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/pyomo_utils.py +243 -0
- classiq/applications_model_constructors/combinatorial_helpers/sympy_utils.py +22 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/encoding.py +194 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/fixed_variables.py +144 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/ising_converter.py +124 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty.py +32 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty_support.py +41 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/sign_seperation.py +75 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/slack_variables.py +90 -0
- classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +48 -91
- classiq/applications_model_constructors/finance_model_constructor.py +4 -17
- classiq/applications_model_constructors/grover_model_constructor.py +20 -91
- classiq/applications_model_constructors/libraries/qmci_library.py +17 -19
- classiq/builtin_functions/standard_gates.py +1 -1
- classiq/exceptions.py +43 -1
- classiq/executor.py +10 -9
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +6 -3
- classiq/interface/analyzer/result.py +12 -4
- classiq/interface/applications/qsvm.py +13 -1
- classiq/interface/backend/backend_preferences.py +4 -2
- classiq/interface/backend/pydantic_backend.py +3 -1
- classiq/interface/backend/quantum_backend_providers.py +1 -0
- classiq/interface/chemistry/fermionic_operator.py +15 -13
- classiq/interface/chemistry/ground_state_problem.py +18 -3
- classiq/interface/chemistry/molecule.py +8 -6
- classiq/interface/chemistry/operator.py +20 -14
- classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
- classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
- classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
- classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
- classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
- classiq/interface/combinatorial_optimization/examples/mht.py +3 -3
- classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
- classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
- classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
- classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
- classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
- classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
- classiq/interface/executor/aws_execution_cost.py +4 -3
- classiq/interface/executor/estimation.py +2 -2
- classiq/interface/executor/execution_preferences.py +5 -34
- classiq/interface/executor/execution_request.py +19 -17
- classiq/interface/executor/optimizer_preferences.py +22 -13
- classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
- classiq/interface/executor/quantum_instruction_set.py +2 -1
- classiq/interface/executor/register_initialization.py +1 -3
- classiq/interface/executor/result.py +41 -10
- classiq/interface/executor/vqe_result.py +1 -1
- classiq/interface/finance/function_input.py +17 -4
- classiq/interface/finance/gaussian_model_input.py +3 -1
- classiq/interface/finance/log_normal_model_input.py +3 -1
- classiq/interface/finance/model_input.py +2 -0
- classiq/interface/generator/amplitude_loading.py +6 -3
- classiq/interface/generator/application_apis/__init__.py +1 -0
- classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
- classiq/interface/generator/arith/argument_utils.py +14 -4
- classiq/interface/generator/arith/arithmetic.py +3 -1
- classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
- classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
- classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
- classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
- classiq/interface/generator/arith/arithmetic_operations.py +5 -10
- classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
- classiq/interface/generator/arith/binary_ops.py +202 -54
- classiq/interface/generator/arith/extremum_operations.py +5 -3
- classiq/interface/generator/arith/logical_ops.py +4 -2
- classiq/interface/generator/arith/machine_precision.py +3 -0
- classiq/interface/generator/arith/number_utils.py +34 -44
- classiq/interface/generator/arith/register_user_input.py +21 -1
- classiq/interface/generator/arith/unary_ops.py +16 -25
- classiq/interface/generator/chemistry_function_params.py +4 -4
- classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
- classiq/interface/generator/compiler_keywords.py +4 -0
- classiq/interface/generator/complex_type.py +3 -10
- classiq/interface/generator/control_state.py +5 -3
- classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
- classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
- classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
- classiq/interface/generator/expressions/evaluated_expression.py +18 -4
- classiq/interface/generator/expressions/expression.py +1 -1
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
- classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
- classiq/interface/generator/finance.py +1 -1
- classiq/interface/generator/function_params.py +7 -6
- classiq/interface/generator/functions/__init__.py +1 -1
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +505 -138
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +25 -99
- classiq/interface/generator/functions/foreign_function_definition.py +12 -4
- classiq/interface/generator/functions/function_implementation.py +8 -4
- classiq/interface/generator/functions/native_function_definition.py +4 -2
- classiq/interface/generator/functions/register.py +4 -2
- classiq/interface/generator/functions/register_mapping_data.py +14 -10
- classiq/interface/generator/generated_circuit_data.py +2 -2
- classiq/interface/generator/grover_operator.py +5 -3
- classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
- classiq/interface/generator/hardware/hardware_data.py +6 -4
- classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
- classiq/interface/generator/hartree_fock.py +3 -1
- classiq/interface/generator/linear_pauli_rotations.py +3 -1
- classiq/interface/generator/mcu.py +5 -3
- classiq/interface/generator/mcx.py +7 -5
- classiq/interface/generator/model/constraints.py +2 -1
- classiq/interface/generator/model/model.py +11 -19
- classiq/interface/generator/model/preferences/preferences.py +4 -3
- classiq/interface/generator/oracles/custom_oracle.py +4 -2
- classiq/interface/generator/oracles/oracle_abc.py +2 -2
- classiq/interface/generator/qpe.py +6 -4
- classiq/interface/generator/qsvm.py +5 -8
- classiq/interface/generator/quantum_function_call.py +21 -16
- classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
- classiq/interface/generator/range_types.py +3 -1
- classiq/interface/generator/slice_parsing_utils.py +8 -3
- classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
- classiq/interface/generator/state_preparation/metrics.py +2 -1
- classiq/interface/generator/state_preparation/state_preparation.py +7 -5
- classiq/interface/generator/state_propagator.py +16 -5
- classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
- classiq/interface/generator/types/struct_declaration.py +8 -3
- classiq/interface/generator/ucc.py +6 -4
- classiq/interface/generator/unitary_gate.py +7 -3
- classiq/interface/generator/validations/flow_graph.py +6 -4
- classiq/interface/generator/validations/validator_functions.py +6 -4
- classiq/interface/hardware.py +2 -2
- classiq/interface/helpers/custom_encoders.py +3 -0
- classiq/interface/helpers/pydantic_model_helpers.py +0 -6
- classiq/interface/helpers/validation_helpers.py +1 -1
- classiq/interface/helpers/versioned_model.py +4 -1
- classiq/interface/ide/show.py +2 -2
- classiq/interface/jobs.py +72 -3
- classiq/interface/model/bind_operation.py +18 -11
- classiq/interface/model/call_synthesis_data.py +68 -0
- classiq/interface/model/inplace_binary_operation.py +2 -2
- classiq/interface/model/model.py +27 -21
- classiq/interface/model/native_function_definition.py +3 -5
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
- classiq/interface/model/quantum_expressions/control_state.py +2 -2
- classiq/interface/model/quantum_function_call.py +25 -139
- classiq/interface/model/quantum_function_declaration.py +8 -0
- classiq/interface/model/quantum_if_operation.py +2 -3
- classiq/interface/model/quantum_lambda_function.py +64 -0
- classiq/interface/model/quantum_type.py +57 -56
- classiq/interface/model/quantum_variable_declaration.py +1 -1
- classiq/interface/model/statement_block.py +32 -0
- classiq/interface/model/validations/handles_validator.py +14 -12
- classiq/interface/model/within_apply_operation.py +11 -0
- classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
- classiq/interface/server/routes.py +5 -0
- classiq/model/function_handler.py +5 -9
- classiq/model/model.py +2 -19
- classiq/qmod/__init__.py +13 -6
- classiq/qmod/builtins/classical_execution_primitives.py +27 -36
- classiq/qmod/builtins/classical_functions.py +24 -14
- classiq/qmod/builtins/functions.py +162 -145
- classiq/qmod/builtins/operations.py +24 -35
- classiq/qmod/builtins/structs.py +15 -15
- classiq/qmod/cfunc.py +42 -0
- classiq/qmod/classical_function.py +6 -14
- classiq/qmod/declaration_inferrer.py +12 -21
- classiq/qmod/expression_query.py +23 -0
- classiq/qmod/model_state_container.py +2 -0
- classiq/qmod/native/__init__.py +0 -0
- classiq/qmod/native/expression_to_qmod.py +189 -0
- classiq/qmod/native/pretty_printer.py +311 -0
- classiq/qmod/qfunc.py +27 -0
- classiq/qmod/qmod_constant.py +76 -0
- classiq/qmod/qmod_parameter.py +34 -12
- classiq/qmod/qmod_struct.py +3 -3
- classiq/qmod/qmod_variable.py +102 -18
- classiq/qmod/quantum_expandable.py +16 -16
- classiq/qmod/quantum_function.py +37 -8
- classiq/qmod/symbolic.py +47 -4
- classiq/qmod/symbolic_expr.py +9 -0
- classiq/qmod/utilities.py +13 -0
- classiq/qmod/write_qmod.py +39 -0
- classiq/quantum_functions/__init__.py +2 -2
- classiq/quantum_functions/annotation_parser.py +9 -11
- classiq/quantum_functions/function_parser.py +1 -1
- classiq/quantum_functions/quantum_function.py +3 -3
- classiq/quantum_register.py +17 -9
- {classiq-0.37.1.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
- {classiq-0.37.1.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
- {classiq-0.37.1.dist-info → classiq-0.38.0.dist-info}/WHEEL +1 -1
- classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
- classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
- classiq/interface/generator/types/combinatorial_problem.py +0 -26
- classiq/interface/model/numeric_reinterpretation.py +0 -25
- classiq/interface/model/operator_synthesis_data.py +0 -48
- classiq/model/function_handler.pyi +0 -152
classiq/qmod/qmod_variable.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
import abc
|
2
|
+
import sys
|
2
3
|
from contextlib import contextmanager
|
4
|
+
from typing import _GenericAlias # type: ignore[attr-defined]
|
3
5
|
from typing import (
|
4
6
|
TYPE_CHECKING,
|
5
7
|
Any,
|
@@ -14,6 +16,7 @@ from typing import (
|
|
14
16
|
Union,
|
15
17
|
get_args,
|
16
18
|
get_origin,
|
19
|
+
overload,
|
17
20
|
)
|
18
21
|
|
19
22
|
from typing_extensions import Annotated, ParamSpec, Self, _AnnotatedAlias
|
@@ -40,11 +43,13 @@ from classiq.interface.model.quantum_type import (
|
|
40
43
|
from classiq.exceptions import ClassiqValueError
|
41
44
|
from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
|
42
45
|
from classiq.qmod.quantum_callable import QCallable
|
43
|
-
from classiq.qmod.symbolic_expr import SymbolicExpr
|
46
|
+
from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
|
44
47
|
from classiq.qmod.symbolic_type import SymbolicTypes
|
48
|
+
from classiq.qmod.utilities import version_portable_get_args
|
45
49
|
|
46
50
|
ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
|
47
51
|
SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
|
52
|
+
UNSUPPORTED_ELEMENT_TYPE = "Only QBit is supported as element type for QArray"
|
48
53
|
|
49
54
|
|
50
55
|
def _is_input_output_typehint(type_hint: Any) -> bool:
|
@@ -183,22 +188,72 @@ class QBit(QScalar):
|
|
183
188
|
return QuantumBit()
|
184
189
|
|
185
190
|
|
186
|
-
|
191
|
+
_P = ParamSpec("_P")
|
187
192
|
|
188
193
|
|
189
|
-
class QNum(Generic[
|
194
|
+
class QNum(Generic[_P], QScalar):
|
190
195
|
QMOD_TYPE = QuantumNumeric
|
191
196
|
|
197
|
+
@overload
|
198
|
+
def __init__(self, name: str):
|
199
|
+
pass
|
200
|
+
|
201
|
+
@overload
|
202
|
+
def __init__(
|
203
|
+
self,
|
204
|
+
name: str,
|
205
|
+
size: Union[int, QParam[int]],
|
206
|
+
is_signed: Union[bool, QParam[bool]],
|
207
|
+
fraction_digits: Union[int, QParam[int]],
|
208
|
+
):
|
209
|
+
pass
|
210
|
+
|
211
|
+
def __init__(
|
212
|
+
self,
|
213
|
+
name: str,
|
214
|
+
size: Union[int, QParam[int], None] = None,
|
215
|
+
is_signed: Union[bool, QParam[bool], None] = None,
|
216
|
+
fraction_digits: Union[int, QParam[int], None] = None,
|
217
|
+
):
|
218
|
+
if (
|
219
|
+
size is None
|
220
|
+
and (is_signed is not None or fraction_digits is not None)
|
221
|
+
or size is not None
|
222
|
+
and (is_signed is None or fraction_digits is None)
|
223
|
+
):
|
224
|
+
raise ClassiqValueError(
|
225
|
+
"Assign none or all of size, is_signed, and fraction_digits"
|
226
|
+
)
|
227
|
+
self._size = None if size is None else Expression(expr=str(size))
|
228
|
+
self._is_signed = None if is_signed is None else Expression(expr=str(is_signed))
|
229
|
+
self._fraction_digits = (
|
230
|
+
None if fraction_digits is None else Expression(expr=str(fraction_digits))
|
231
|
+
)
|
232
|
+
super().__init__(name)
|
233
|
+
|
192
234
|
@classmethod
|
193
235
|
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
194
|
-
|
195
|
-
if
|
196
|
-
|
197
|
-
|
198
|
-
|
236
|
+
type_args = get_args(type_hint)
|
237
|
+
if len(type_args) == 0:
|
238
|
+
return cls.QMOD_TYPE()
|
239
|
+
type_args = type_args[0]
|
240
|
+
if len(type_args) != 3:
|
241
|
+
raise ClassiqValueError(
|
242
|
+
"QNum receives three type arguments: QNum[size: int | QParam[int], "
|
243
|
+
"is_signed: bool | QParam[bool], fraction_digits: int | QParam[int]]"
|
244
|
+
)
|
245
|
+
return cls.QMOD_TYPE(
|
246
|
+
size=Expression(expr=get_type_hint_expr(type_args[0])),
|
247
|
+
is_signed=Expression(expr=get_type_hint_expr(type_args[1])),
|
248
|
+
fraction_digits=Expression(expr=get_type_hint_expr(type_args[2])),
|
249
|
+
)
|
199
250
|
|
200
251
|
def get_qmod_type(self) -> QuantumType:
|
201
|
-
return self.QMOD_TYPE(
|
252
|
+
return self.QMOD_TYPE(
|
253
|
+
size=self._size,
|
254
|
+
is_signed=self._is_signed,
|
255
|
+
fraction_digits=self._fraction_digits,
|
256
|
+
)
|
202
257
|
|
203
258
|
@property
|
204
259
|
def size(self) -> QParamScalar:
|
@@ -212,14 +267,27 @@ class QNum(Generic[_T], QScalar):
|
|
212
267
|
def is_signed(self) -> QParamScalar:
|
213
268
|
return QParamScalar(f"is_signed({self._name})")
|
214
269
|
|
270
|
+
# Support comma-separated generic args in older Python versions
|
271
|
+
if sys.version_info[0:2] < (3, 10):
|
215
272
|
|
216
|
-
|
273
|
+
def __class_getitem__(cls, args) -> _GenericAlias:
|
274
|
+
return _GenericAlias(cls, args)
|
217
275
|
|
218
276
|
|
219
277
|
class QArray(ArrayBase[_P], QVar):
|
220
|
-
def __init__(
|
221
|
-
|
278
|
+
def __init__(
|
279
|
+
self,
|
280
|
+
name: str,
|
281
|
+
element_type: _GenericAlias = QBit,
|
282
|
+
length: Optional[Union[int, QParam[int]]] = None,
|
283
|
+
slice_: Optional[Tuple[int, int]] = None,
|
284
|
+
) -> None:
|
285
|
+
if element_type is not QBit:
|
286
|
+
raise ClassiqValueError(UNSUPPORTED_ELEMENT_TYPE)
|
287
|
+
self._element_type = element_type
|
288
|
+
self._length = length
|
222
289
|
self._slice = slice_
|
290
|
+
super().__init__(name)
|
223
291
|
|
224
292
|
def get_handle_binding(self) -> HandleBinding:
|
225
293
|
if self._slice is None:
|
@@ -234,20 +302,30 @@ class QArray(ArrayBase[_P], QVar):
|
|
234
302
|
offset = self._slice[0] if self._slice is not None else 0
|
235
303
|
if isinstance(key, slice):
|
236
304
|
if key.step is not None:
|
237
|
-
raise
|
305
|
+
raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
|
238
306
|
new_slice = (offset + key.start, offset + key.stop)
|
239
307
|
else:
|
240
308
|
if isinstance(key, QParam) and not isinstance(key, QParamScalar):
|
241
309
|
raise ClassiqValueError("Non-classical parameter for slicing")
|
242
310
|
new_slice = (offset + key, offset + key + 1)
|
243
|
-
if
|
311
|
+
if (
|
312
|
+
self._slice is not None
|
313
|
+
and not isinstance(new_slice[1], Symbolic)
|
314
|
+
and not isinstance(self._slice[1], Symbolic)
|
315
|
+
and new_slice[1] > self._slice[1]
|
316
|
+
) or (
|
317
|
+
self._length is not None
|
318
|
+
and not isinstance(new_slice[1], Symbolic)
|
319
|
+
and not isinstance(self._length, Symbolic)
|
320
|
+
and new_slice[1] > self._length
|
321
|
+
):
|
244
322
|
raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
|
245
323
|
# prevent addition to local handles, since this is used for slicing existing local handles
|
246
324
|
with _no_current_expandable():
|
247
|
-
return QArray(self._name, slice_=new_slice)
|
325
|
+
return QArray(self._name, length=self._length, slice_=new_slice)
|
248
326
|
|
249
327
|
def __len__(self) -> int:
|
250
|
-
raise
|
328
|
+
raise ClassiqValueError(
|
251
329
|
"len(<var>) is not supported for quantum variables - use <var>.len() instead"
|
252
330
|
)
|
253
331
|
|
@@ -258,16 +336,22 @@ class QArray(ArrayBase[_P], QVar):
|
|
258
336
|
else:
|
259
337
|
|
260
338
|
def len(self) -> QParamScalar:
|
339
|
+
if self._length is not None:
|
340
|
+
return QParamScalar(f"{self._length}")
|
261
341
|
return QParamScalar(f"len({self._name})")
|
262
342
|
|
263
343
|
@classmethod
|
264
344
|
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
265
345
|
length_expr: Optional[Expression] = None
|
266
|
-
if len(
|
267
|
-
length_expr = Expression(
|
346
|
+
if len(version_portable_get_args(type_hint)) == 2:
|
347
|
+
length_expr = Expression(
|
348
|
+
expr=get_type_hint_expr(version_portable_get_args(type_hint)[1])
|
349
|
+
)
|
268
350
|
return QuantumBitvector(length=length_expr)
|
269
351
|
|
270
352
|
def get_qmod_type(self) -> QuantumType:
|
353
|
+
if self._length is not None:
|
354
|
+
return QuantumBitvector(length=Expression(expr=str(self._length)))
|
271
355
|
return QuantumBitvector()
|
272
356
|
|
273
357
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import inspect
|
1
2
|
from abc import ABC
|
2
3
|
from types import TracebackType
|
3
4
|
from typing import (
|
@@ -25,14 +26,16 @@ from classiq.interface.model.quantum_function_call import (
|
|
25
26
|
ArgValue,
|
26
27
|
OperandIdentifier,
|
27
28
|
QuantumFunctionCall,
|
28
|
-
QuantumLambdaFunction,
|
29
|
-
QuantumOperand,
|
30
29
|
)
|
31
30
|
from classiq.interface.model.quantum_function_declaration import (
|
32
31
|
PositionalArg,
|
33
32
|
QuantumFunctionDeclaration,
|
34
33
|
QuantumOperandDeclaration,
|
35
34
|
)
|
35
|
+
from classiq.interface.model.quantum_lambda_function import (
|
36
|
+
QuantumLambdaFunction,
|
37
|
+
QuantumOperand,
|
38
|
+
)
|
36
39
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
37
40
|
from classiq.interface.model.quantum_type import QuantumType
|
38
41
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -41,6 +44,7 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
41
44
|
|
42
45
|
from classiq.exceptions import ClassiqValueError
|
43
46
|
from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
|
47
|
+
from classiq.qmod.qmod_constant import QConstant
|
44
48
|
from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
|
45
49
|
from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
|
46
50
|
from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
|
@@ -96,9 +100,7 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
|
|
96
100
|
for arg in self.func_decl.get_positional_arg_decls():
|
97
101
|
if isinstance(arg, ClassicalParameterDeclaration):
|
98
102
|
rename_dict = self.infer_rename_params()
|
99
|
-
actual_name = (
|
100
|
-
rename_dict[arg.name] if arg.name in rename_dict else arg.name
|
101
|
-
)
|
103
|
+
actual_name = rename_dict.get(arg.name, arg.name)
|
102
104
|
result.append(
|
103
105
|
create_param(actual_name, arg.classical_type, self._qmodule)
|
104
106
|
)
|
@@ -126,17 +128,12 @@ class QLambdaFunction(QExpandable):
|
|
126
128
|
return self._decl
|
127
129
|
|
128
130
|
def infer_rename_params(self) -> Dict[str, str]:
|
129
|
-
|
130
|
-
|
131
|
+
py_params = inspect.getfullargspec(self._py_callable)
|
132
|
+
decl_params = self.func_decl.param_decls.keys()
|
131
133
|
return {
|
132
|
-
|
133
|
-
for
|
134
|
-
|
135
|
-
[arg.name for arg in self.func_decl.get_positional_arg_decls()],
|
136
|
-
self._py_callable.__annotations__.keys(),
|
137
|
-
)
|
138
|
-
)
|
139
|
-
if decl_name != actual_name
|
134
|
+
decl_param: py_param
|
135
|
+
for decl_param, py_param in zip(decl_params, py_params.args)
|
136
|
+
if decl_param != py_param
|
140
137
|
}
|
141
138
|
|
142
139
|
|
@@ -163,7 +160,7 @@ class QTerminalCallable(QCallable):
|
|
163
160
|
return QTerminalCallable(self._decl, key)
|
164
161
|
|
165
162
|
def __len__(self) -> int:
|
166
|
-
raise
|
163
|
+
raise ClassiqValueError(
|
167
164
|
"len(<func>) is not supported for quantum callables - use <func>.len() instead (Only if it is an operand list)"
|
168
165
|
)
|
169
166
|
|
@@ -205,6 +202,9 @@ def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue: ...
|
|
205
202
|
|
206
203
|
|
207
204
|
def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
|
205
|
+
if isinstance(val, QConstant):
|
206
|
+
val.add_to_model()
|
207
|
+
return Expression(expr=str(val.name))
|
208
208
|
if isinstance(arg_decl, ClassicalParameterDeclaration):
|
209
209
|
return Expression(expr=str(val))
|
210
210
|
elif isinstance(arg_decl, PortDeclaration):
|
classiq/qmod/quantum_function.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import ast
|
1
2
|
import functools
|
2
3
|
from typing import Any, Callable, Dict, List, Optional, Tuple, get_origin
|
3
4
|
|
@@ -13,6 +14,7 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
13
14
|
from classiq.exceptions import ClassiqError
|
14
15
|
from classiq.qmod.classical_function import CFunc
|
15
16
|
from classiq.qmod.declaration_inferrer import infer_func_decl
|
17
|
+
from classiq.qmod.qmod_constant import QConstant
|
16
18
|
from classiq.qmod.qmod_parameter import QParam
|
17
19
|
from classiq.qmod.qmod_variable import QVar
|
18
20
|
from classiq.qmod.quantum_callable import QCallable, QCallableList
|
@@ -63,21 +65,23 @@ class QFunc(QExpandable):
|
|
63
65
|
) -> Model:
|
64
66
|
self._qmodule.type_decls = dict()
|
65
67
|
self._qmodule.native_defs = dict()
|
68
|
+
self._qmodule.constants = dict()
|
69
|
+
QConstant.set_current_model(self._qmodule)
|
66
70
|
self._add_native_func_def()
|
67
71
|
model_extra_settings: List[Tuple[str, Any]] = [
|
68
72
|
("constraints", constraints),
|
69
73
|
("execution_preferences", execution_preferences),
|
70
74
|
("preferences", preferences),
|
71
75
|
]
|
72
|
-
classical_execution_function
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
76
|
+
if classical_execution_function is not None:
|
77
|
+
self._add_constants_from_classical_code(classical_execution_function)
|
78
|
+
model_extra_settings.append(
|
79
|
+
("classical_execution_code", classical_execution_function.code)
|
80
|
+
)
|
77
81
|
return Model(
|
82
|
+
constants=list(self._qmodule.constants.values()),
|
78
83
|
functions=list(self._qmodule.native_defs.values()),
|
79
84
|
types=list(self._qmodule.type_decls.values()),
|
80
|
-
classical_execution_code=classical_execution_function.code,
|
81
85
|
**{key: value for key, value in model_extra_settings if value},
|
82
86
|
)
|
83
87
|
|
@@ -89,18 +93,43 @@ class QFunc(QExpandable):
|
|
89
93
|
**self.func_decl.__dict__, body=self.body
|
90
94
|
)
|
91
95
|
|
96
|
+
def _add_constants_from_classical_code(
|
97
|
+
self, classical_execution_function: CFunc
|
98
|
+
) -> None:
|
99
|
+
# FIXME: https://classiq.atlassian.net/browse/CAD-18050
|
100
|
+
# We use this visitor to add the constants that were used in the classical
|
101
|
+
# execution code to the model. In the future, if we will have a better notion
|
102
|
+
# of "QModule" and a "QConstant" will be a part of it then we may be able to
|
103
|
+
# remove the handling of the QConstants from this visitor, but I think we will
|
104
|
+
# need similar logic to allow using python variables in the classical execution
|
105
|
+
# code
|
106
|
+
class IdentifierVisitor(ast.NodeVisitor):
|
107
|
+
def visit_Name(self, node: ast.Name) -> None:
|
108
|
+
if (
|
109
|
+
node.id in classical_execution_function._caller_constants
|
110
|
+
and isinstance(
|
111
|
+
classical_execution_function._caller_constants[node.id],
|
112
|
+
QConstant,
|
113
|
+
)
|
114
|
+
):
|
115
|
+
classical_execution_function._caller_constants[
|
116
|
+
node.id
|
117
|
+
].add_to_model()
|
118
|
+
|
119
|
+
IdentifierVisitor().visit(ast.parse(classical_execution_function.code))
|
120
|
+
|
92
121
|
|
93
122
|
class ExternalQFunc(QTerminalCallable):
|
94
123
|
def __init__(self, py_callable: Callable) -> None:
|
95
124
|
decl = _lookup_qfunc(unmangle_keyword(py_callable.__name__))
|
96
125
|
if decl is None:
|
97
|
-
raise
|
126
|
+
raise ClassiqError(f"Definition of {py_callable.__name__!r} not found")
|
98
127
|
|
99
128
|
py_callable.__annotations__.pop("return", None)
|
100
129
|
if py_callable.__annotations__.keys() != {
|
101
130
|
mangle_keyword(arg.name) for arg in decl.get_positional_arg_decls()
|
102
131
|
}:
|
103
|
-
raise
|
132
|
+
raise ClassiqError(
|
104
133
|
f"Parameter type hints for {py_callable.__name__!r} do not match imported declaration"
|
105
134
|
)
|
106
135
|
super().__init__(decl)
|
classiq/qmod/symbolic.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
import sys
|
2
|
-
from typing import Tuple
|
2
|
+
from typing import Optional, Tuple, Type, get_args, get_origin, overload
|
3
3
|
|
4
|
-
from classiq.
|
4
|
+
from classiq.exceptions import ClassiqValueError
|
5
|
+
from classiq.qmod import model_state_container
|
6
|
+
from classiq.qmod.declaration_inferrer import python_type_to_qmod
|
7
|
+
from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
|
5
8
|
from classiq.qmod.symbolic_expr import SymbolicExpr
|
6
9
|
from classiq.qmod.symbolic_type import SymbolicTypes
|
7
10
|
|
@@ -13,9 +16,44 @@ EulerGamma = SymbolicExpr("EulerGamma")
|
|
13
16
|
Catalan = SymbolicExpr("Catalan")
|
14
17
|
|
15
18
|
|
16
|
-
|
19
|
+
@overload
|
20
|
+
def symbolic_function(
|
21
|
+
*args: SymbolicTypes, return_type: None = None
|
22
|
+
) -> QParamScalar: ...
|
23
|
+
|
24
|
+
|
25
|
+
@overload
|
26
|
+
def symbolic_function(*args: SymbolicTypes, return_type: Type[QParam]) -> QParam: ...
|
27
|
+
|
28
|
+
|
29
|
+
def symbolic_function(
|
30
|
+
*args: SymbolicTypes, return_type: Optional[Type[QParam]] = None
|
31
|
+
) -> QParam:
|
32
|
+
qmodule = (
|
33
|
+
model_state_container.QMODULE
|
34
|
+
) # FIXME: https://classiq.atlassian.net/browse/CAD-15126
|
17
35
|
str_args = [str(x) for x in args]
|
18
|
-
|
36
|
+
expr = f"{sys._getframe(1).f_code.co_name}({','.join(str_args)})"
|
37
|
+
|
38
|
+
if return_type is None:
|
39
|
+
return QParamScalar(expr)
|
40
|
+
|
41
|
+
if get_origin(return_type) is not QParam:
|
42
|
+
raise ClassiqValueError(
|
43
|
+
f"Unsupported return type for symbolic function: {return_type}"
|
44
|
+
)
|
45
|
+
|
46
|
+
qmod_type = python_type_to_qmod(get_args(return_type)[0], qmodule=qmodule)
|
47
|
+
if qmod_type is None:
|
48
|
+
raise ClassiqValueError(
|
49
|
+
f"Unsupported return type for symbolic function: {return_type}"
|
50
|
+
)
|
51
|
+
|
52
|
+
return create_param(
|
53
|
+
expr,
|
54
|
+
qmod_type,
|
55
|
+
qmodule,
|
56
|
+
)
|
19
57
|
|
20
58
|
|
21
59
|
def sin(x: SymbolicTypes) -> QParamScalar:
|
@@ -238,6 +276,10 @@ def logical_not(x: SymbolicTypes) -> SymbolicExpr:
|
|
238
276
|
return SymbolicExpr._unary_op(x, "not")
|
239
277
|
|
240
278
|
|
279
|
+
def mod_inverse(a: SymbolicTypes, m: SymbolicTypes) -> QParamScalar:
|
280
|
+
return symbolic_function(a, m)
|
281
|
+
|
282
|
+
|
241
283
|
__all__ = [
|
242
284
|
"pi",
|
243
285
|
"E",
|
@@ -300,6 +342,7 @@ __all__ = [
|
|
300
342
|
"logical_and",
|
301
343
|
"logical_or",
|
302
344
|
"logical_not",
|
345
|
+
"mod_inverse",
|
303
346
|
]
|
304
347
|
|
305
348
|
|
classiq/qmod/symbolic_expr.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import ast
|
3
4
|
from typing import Any
|
4
5
|
|
5
6
|
|
@@ -13,6 +14,14 @@ class Symbolic:
|
|
13
14
|
def __repr__(self) -> str:
|
14
15
|
return self._expr
|
15
16
|
|
17
|
+
def __bool__(self) -> bool:
|
18
|
+
try:
|
19
|
+
return bool(ast.literal_eval(self._expr))
|
20
|
+
except ValueError:
|
21
|
+
raise TypeError(
|
22
|
+
f"Symbolic expression {self._expr!r} cannot be converted to bool"
|
23
|
+
) from None
|
24
|
+
|
16
25
|
|
17
26
|
class SymbolicExpr(Symbolic):
|
18
27
|
def __init__(self, expr: str) -> None:
|
classiq/qmod/utilities.py
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
import keyword
|
2
|
+
import sys
|
3
|
+
from typing import get_args, get_origin
|
4
|
+
|
5
|
+
DEFAULT_DECIMAL_PRECISION = 4
|
2
6
|
|
3
7
|
|
4
8
|
def mangle_keyword(name: str) -> str:
|
@@ -12,3 +16,12 @@ def unmangle_keyword(name: str) -> str:
|
|
12
16
|
if name[-1] == "_" and keyword.iskeyword(name[:-1]):
|
13
17
|
name = name[:-1]
|
14
18
|
return name
|
19
|
+
|
20
|
+
|
21
|
+
def version_portable_get_args(py_type: type) -> tuple:
|
22
|
+
if get_origin(py_type) is None:
|
23
|
+
return tuple()
|
24
|
+
if sys.version_info[0:2] < (3, 10):
|
25
|
+
return get_args(py_type) # The result of __class_getitem__
|
26
|
+
else:
|
27
|
+
return get_args(py_type)[0]
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import json
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from classiq.interface.model.model import Model, SerializedModel
|
6
|
+
|
7
|
+
from classiq.qmod.native.pretty_printer import DSLPrettyPrinter
|
8
|
+
from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
|
9
|
+
|
10
|
+
_QMOD_SUFFIX = "qmod"
|
11
|
+
_SYNTHESIS_OPTIONS_SUFFIX = "synthesis_options.json"
|
12
|
+
|
13
|
+
|
14
|
+
def write_qmod(
|
15
|
+
serialized_model: SerializedModel,
|
16
|
+
name: str,
|
17
|
+
directory: Optional[Path] = None,
|
18
|
+
decimal_precision: int = DEFAULT_DECIMAL_PRECISION,
|
19
|
+
) -> None:
|
20
|
+
model = Model.parse_raw(serialized_model)
|
21
|
+
pretty_printed_model = DSLPrettyPrinter(decimal_precision=decimal_precision).visit(
|
22
|
+
model
|
23
|
+
)
|
24
|
+
|
25
|
+
synthesis_options = model.dict(
|
26
|
+
include={"constraints", "preferences"}, exclude_unset=True
|
27
|
+
)
|
28
|
+
|
29
|
+
synthesis_options_path = Path(f"{name}.{_SYNTHESIS_OPTIONS_SUFFIX}")
|
30
|
+
if directory is not None:
|
31
|
+
synthesis_options_path = directory / synthesis_options_path
|
32
|
+
|
33
|
+
synthesis_options_path.write_text(json.dumps(synthesis_options, indent=2))
|
34
|
+
|
35
|
+
native_qmod_path = Path(f"{name}.{_QMOD_SUFFIX}")
|
36
|
+
if directory is not None:
|
37
|
+
native_qmod_path = directory / native_qmod_path
|
38
|
+
|
39
|
+
native_qmod_path.write_text(pretty_printed_model)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from classiq.quantum_functions.decorators import quantum_function
|
1
|
+
from classiq.quantum_functions.decorators import quantum_function
|
2
2
|
from classiq.quantum_functions.function_library import (
|
3
3
|
QASM3_INTRO,
|
4
4
|
QASM_INTRO,
|
@@ -8,7 +8,7 @@ from classiq.quantum_functions.function_library import (
|
|
8
8
|
)
|
9
9
|
|
10
10
|
__all__ = [
|
11
|
-
"
|
11
|
+
"quantum_function",
|
12
12
|
"QASM_INTRO",
|
13
13
|
"QASM3_INTRO",
|
14
14
|
"FunctionLibrary",
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# type: ignore
|
1
|
+
# type: ignore # noqa: PGH003
|
2
2
|
# We can either ignore each line individually, or ignore the entire file and wait until mypy can ignore
|
3
3
|
# specific errors per-file.
|
4
4
|
import inspect
|
@@ -77,18 +77,16 @@ class AnnotationParser:
|
|
77
77
|
self.input_names, self.input_values, self.output_values
|
78
78
|
):
|
79
79
|
# Is arithmetic QReg
|
80
|
-
if issubclass(input_type.__origin__, QSFixed):
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
)
|
80
|
+
if issubclass(input_type.__origin__, QSFixed) and input_type != output_type:
|
81
|
+
raise ClassiqQFuncError(
|
82
|
+
f"Arithmetic QReg must be of the same type and size in both the input and the output. Got {input_type} and {output_type}"
|
83
|
+
)
|
85
84
|
|
86
85
|
# Is Auxillary QReg
|
87
|
-
if issubclass(input_type.__origin__, AuxQReg):
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
)
|
86
|
+
if issubclass(input_type.__origin__, AuxQReg) and input_type != output_type:
|
87
|
+
raise ClassiqQFuncError(
|
88
|
+
f"Auxillary QReg must be of the same type and size in both the input and the output. Got {input_type} and {output_type}"
|
89
|
+
)
|
92
90
|
|
93
91
|
# Is Zero QReg
|
94
92
|
if input_type.__origin__ is ZeroQReg:
|
@@ -36,7 +36,7 @@ class FunctionParser:
|
|
36
36
|
def _validate_function_output(output: Any) -> None:
|
37
37
|
# Todo: validate QASM
|
38
38
|
|
39
|
-
if
|
39
|
+
if not isinstance(output, str):
|
40
40
|
raise ClassiqQFuncError(
|
41
41
|
"Invalid output. Please return a string of OpenQASM2.0."
|
42
42
|
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import sys
|
2
2
|
from abc import ABC, abstractmethod
|
3
3
|
from types import FunctionType
|
4
|
-
from typing import Callable, Dict, List, Optional, Sequence, Tuple
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple
|
5
5
|
|
6
6
|
from classiq.interface.generator.functions import (
|
7
7
|
FunctionImplementation,
|
@@ -213,7 +213,7 @@ class QuantumFunctionFactory(ABC):
|
|
213
213
|
)
|
214
214
|
return "_".join(str_list)
|
215
215
|
|
216
|
-
def __call__(self, *args, **kwargs):
|
216
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
217
217
|
try:
|
218
218
|
return self._apply_method(str(self), *args, **kwargs)
|
219
219
|
except AttributeError as e:
|
@@ -227,7 +227,7 @@ class QuantumFunctionFactory(ABC):
|
|
227
227
|
"""
|
228
228
|
Abstract method for providing the definition of the user function.
|
229
229
|
The QuantumFunction object may be generated either directly, or using existing
|
230
|
-
helper tools such as the @
|
230
|
+
helper tools such as the @quantum_function decorator.
|
231
231
|
Instance attributes of the QuantumFunctionFactory may be used as parameters for the
|
232
232
|
definition.
|
233
233
|
|