classiq 0.100.0__py3-none-any.whl → 0.104.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 +3 -0
- classiq/_internals/api_wrapper.py +29 -4
- classiq/applications/chemistry/op_utils.py +63 -1
- classiq/applications/chemistry/problems.py +18 -6
- classiq/applications/chemistry/ucc.py +2 -2
- classiq/evaluators/parameter_types.py +1 -4
- classiq/evaluators/qmod_annotated_expression.py +1 -1
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +1 -8
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +1 -1
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +2 -2
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +18 -29
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +1 -6
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -7
- classiq/evaluators/qmod_node_evaluators/utils.py +6 -3
- classiq/evaluators/qmod_type_inference/quantum_type_comparison.py +52 -0
- classiq/execution/__init__.py +11 -1
- classiq/execution/execution_session.py +1 -1
- classiq/execution/functions/__init__.py +3 -0
- classiq/execution/functions/_logging.py +19 -0
- classiq/execution/functions/constants.py +9 -0
- classiq/execution/functions/parse_provider_backend.py +90 -0
- classiq/execution/functions/sample.py +257 -0
- classiq/execution/jobs.py +122 -5
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +15 -0
- classiq/interface/backend/provider_config/providers/aqt.py +1 -1
- classiq/interface/backend/provider_config/providers/azure.py +1 -2
- classiq/interface/backend/provider_config/providers/ibm.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +3 -0
- classiq/interface/exceptions.py +0 -42
- classiq/interface/executor/execution_request.py +1 -0
- classiq/interface/executor/quantum_code.py +0 -6
- classiq/interface/executor/result.py +9 -5
- classiq/interface/generator/arith/binary_ops.py +38 -2
- classiq/interface/generator/function_param_list.py +4 -2
- classiq/interface/generator/functions/builtins/internal_operators.py +5 -9
- classiq/interface/generator/functions/classical_type.py +45 -0
- classiq/interface/generator/functions/type_name.py +23 -0
- classiq/interface/generator/generated_circuit_data.py +0 -2
- classiq/interface/generator/generation_request.py +9 -4
- classiq/interface/generator/quantum_program.py +8 -36
- classiq/interface/generator/types/compilation_metadata.py +9 -0
- classiq/interface/hardware.py +1 -0
- classiq/interface/helpers/model_normalizer.py +62 -2
- classiq/interface/helpers/text_utils.py +17 -6
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/invert.py +15 -0
- classiq/interface/model/model.py +42 -3
- classiq/interface/model/model_visitor.py +4 -2
- classiq/interface/model/quantum_function_call.py +17 -5
- classiq/interface/model/quantum_type.py +21 -0
- classiq/interface/model/statement_block.py +0 -4
- classiq/model_expansions/capturing/captured_vars.py +16 -12
- classiq/model_expansions/function_builder.py +9 -1
- classiq/model_expansions/interpreters/base_interpreter.py +12 -10
- classiq/model_expansions/interpreters/generative_interpreter.py +9 -24
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -0
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +132 -28
- classiq/model_expansions/quantum_operations/bind.py +4 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -35
- classiq/model_expansions/quantum_operations/emitter.py +1 -4
- classiq/model_expansions/quantum_operations/expression_evaluator.py +0 -3
- classiq/model_expansions/visitors/uncomputation_signature_inference.py +15 -47
- classiq/open_library/functions/__init__.py +42 -27
- classiq/open_library/functions/bit_operations.py +30 -0
- classiq/open_library/functions/modular_arithmetics.py +597 -0
- classiq/open_library/functions/qft_space_arithmetics.py +81 -0
- classiq/open_library/functions/state_preparation.py +6 -3
- classiq/open_library/functions/utility_functions.py +22 -3
- classiq/qmod/builtins/functions/__init__.py +9 -0
- classiq/qmod/builtins/functions/arithmetic.py +131 -0
- classiq/qmod/builtins/functions/exponentiation.py +34 -4
- classiq/qmod/builtins/operations.py +30 -41
- classiq/qmod/native/pretty_printer.py +12 -12
- classiq/qmod/pretty_print/pretty_printer.py +11 -15
- classiq/qmod/qmod_parameter.py +4 -0
- classiq/qmod/qmod_variable.py +38 -63
- classiq/qmod/quantum_callable.py +8 -2
- classiq/qmod/quantum_expandable.py +3 -1
- classiq/qmod/quantum_function.py +45 -8
- classiq/qmod/semantics/validation/function_name_collisions_validation.py +7 -4
- classiq/qmod/semantics/validation/model_validation.py +7 -2
- classiq/qmod/symbolic_type.py +4 -2
- classiq/qmod/utilities.py +7 -4
- classiq/synthesis_action/__init__.py +20 -0
- classiq/synthesis_action/actions.py +106 -0
- {classiq-0.100.0.dist-info → classiq-0.104.0.dist-info}/METADATA +1 -1
- {classiq-0.100.0.dist-info → classiq-0.104.0.dist-info}/RECORD +90 -84
- classiq/interface/executor/register_initialization.py +0 -36
- classiq/interface/generator/amplitude_loading.py +0 -103
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +0 -77
- classiq/open_library/functions/modular_exponentiation.py +0 -272
- classiq/open_library/functions/qsvt_temp.py +0 -536
- {classiq-0.100.0.dist-info → classiq-0.104.0.dist-info}/WHEEL +0 -0
- {classiq-0.100.0.dist-info → classiq-0.104.0.dist-info}/licenses/LICENSE.txt +0 -0
classiq/__init__.py
CHANGED
|
@@ -68,6 +68,8 @@ from classiq.synthesis import (
|
|
|
68
68
|
update_execution_preferences,
|
|
69
69
|
update_preferences,
|
|
70
70
|
)
|
|
71
|
+
from classiq.synthesis_action import * # noqa: F403
|
|
72
|
+
from classiq.synthesis_action import __all__ as _synthesis_all
|
|
71
73
|
|
|
72
74
|
_application_constructors_all = [
|
|
73
75
|
"construct_combinatorial_optimization_model",
|
|
@@ -124,6 +126,7 @@ __all__ = (
|
|
|
124
126
|
+ _application_constructors_all
|
|
125
127
|
+ _qmod_all
|
|
126
128
|
+ _execution_all
|
|
129
|
+
+ _synthesis_all
|
|
127
130
|
+ _open_library_all
|
|
128
131
|
+ _be_all
|
|
129
132
|
)
|
|
@@ -20,6 +20,7 @@ from classiq.interface.executor.quantum_program_params import (
|
|
|
20
20
|
)
|
|
21
21
|
from classiq.interface.executor.user_budget import UserBudget
|
|
22
22
|
from classiq.interface.generator import quantum_program as generator_result
|
|
23
|
+
from classiq.interface.generator.generation_request import SynthesisActionsQueryResults
|
|
23
24
|
from classiq.interface.generator.preferences.qasm_to_qmod_params import QasmToQmodParams
|
|
24
25
|
from classiq.interface.hardware import HardwareInformation, Provider
|
|
25
26
|
from classiq.interface.ide.visual_model import ProgramVisualModel
|
|
@@ -321,18 +322,42 @@ class ApiWrapper:
|
|
|
321
322
|
offset: int,
|
|
322
323
|
limit: int,
|
|
323
324
|
http_client: httpx.AsyncClient | None = None,
|
|
325
|
+
**extra_query_params: Any,
|
|
324
326
|
) -> execution_request.ExecutionJobsQueryResults:
|
|
327
|
+
params: dict[str, Any] = {
|
|
328
|
+
"offset": offset,
|
|
329
|
+
"limit": limit,
|
|
330
|
+
}
|
|
331
|
+
params.update(extra_query_params)
|
|
325
332
|
data = await cls._call_task(
|
|
326
333
|
http_method=HTTPMethod.GET,
|
|
327
334
|
url=f"{routes.EXECUTION_JOBS_FULL_PATH}",
|
|
328
|
-
params=
|
|
329
|
-
"offset": offset,
|
|
330
|
-
"limit": limit,
|
|
331
|
-
},
|
|
335
|
+
params=params,
|
|
332
336
|
http_client=http_client,
|
|
333
337
|
)
|
|
334
338
|
return execution_request.ExecutionJobsQueryResults.model_validate(data)
|
|
335
339
|
|
|
340
|
+
@classmethod
|
|
341
|
+
async def call_query_synthesis_actions(
|
|
342
|
+
cls,
|
|
343
|
+
offset: int,
|
|
344
|
+
limit: int,
|
|
345
|
+
http_client: httpx.AsyncClient | None = None,
|
|
346
|
+
**extra_query_params: Any | None,
|
|
347
|
+
) -> SynthesisActionsQueryResults:
|
|
348
|
+
params: dict[str, Any] = {
|
|
349
|
+
"offset": offset,
|
|
350
|
+
"limit": limit,
|
|
351
|
+
}
|
|
352
|
+
params.update(extra_query_params)
|
|
353
|
+
data = await cls._call_task(
|
|
354
|
+
http_method=HTTPMethod.GET,
|
|
355
|
+
url=f"{routes.TASKS_GENERATE_FULL_PATH}",
|
|
356
|
+
params=params,
|
|
357
|
+
http_client=http_client,
|
|
358
|
+
)
|
|
359
|
+
return SynthesisActionsQueryResults.model_validate(data)
|
|
360
|
+
|
|
336
361
|
@classmethod
|
|
337
362
|
async def call_analysis_task(
|
|
338
363
|
cls,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from typing import cast
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
4
5
|
from openfermion.ops.operators.qubit_operator import QubitOperator
|
|
5
6
|
from openfermion.utils.operator_utils import count_qubits
|
|
6
7
|
|
|
7
|
-
from classiq.interface.exceptions import ClassiqValueError
|
|
8
|
+
from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqValueError
|
|
8
9
|
|
|
9
10
|
from classiq.qmod.builtins.enums import Pauli
|
|
10
11
|
from classiq.qmod.builtins.structs import IndexedPauli, SparsePauliOp, SparsePauliTerm
|
|
@@ -25,6 +26,13 @@ def _get_n_qubits(qubit_op: QubitOperator, n_qubits: int | None) -> int:
|
|
|
25
26
|
def qubit_op_to_pauli_terms(
|
|
26
27
|
qubit_op: QubitOperator, n_qubits: int | None = None
|
|
27
28
|
) -> SparsePauliOp:
|
|
29
|
+
warnings.warn(
|
|
30
|
+
"The function 'qubit_op_to_pauli_terms' is deprecated due to incorrect order "
|
|
31
|
+
"of qubits in its result. It will no longer be supported starting on 2026-01-22 "
|
|
32
|
+
"at the earliest. Please use 'qubit_op_to_qmod' instead.",
|
|
33
|
+
ClassiqDeprecationWarning,
|
|
34
|
+
stacklevel=2,
|
|
35
|
+
)
|
|
28
36
|
n_qubits = _get_n_qubits(qubit_op, n_qubits)
|
|
29
37
|
return SparsePauliOp(
|
|
30
38
|
terms=[
|
|
@@ -44,6 +52,60 @@ def qubit_op_to_pauli_terms(
|
|
|
44
52
|
)
|
|
45
53
|
|
|
46
54
|
|
|
55
|
+
def qubit_op_to_qmod(
|
|
56
|
+
qubit_op: QubitOperator, n_qubits: int | None = None
|
|
57
|
+
) -> SparsePauliOp:
|
|
58
|
+
n_qubits = _get_n_qubits(qubit_op, n_qubits)
|
|
59
|
+
return SparsePauliOp(
|
|
60
|
+
terms=[
|
|
61
|
+
SparsePauliTerm(
|
|
62
|
+
paulis=[ # type:ignore[arg-type]
|
|
63
|
+
IndexedPauli(
|
|
64
|
+
pauli=getattr(Pauli, pauli),
|
|
65
|
+
index=qubit,
|
|
66
|
+
)
|
|
67
|
+
for qubit, pauli in term
|
|
68
|
+
],
|
|
69
|
+
coefficient=coeff,
|
|
70
|
+
)
|
|
71
|
+
for term, coeff in qubit_op.terms.items()
|
|
72
|
+
],
|
|
73
|
+
num_qubits=n_qubits,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def qmod_to_qubit_op(operator: SparsePauliOp) -> QubitOperator:
|
|
78
|
+
"""
|
|
79
|
+
Transforms Qmod's SparsePauliOp data structure to OpenFermion's QubitOperator data structure.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
operator (SparsePauliOp): The operator to be transformed
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
QubitOperator: The operator in OpenFermion's data structure
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
# Initiating the QubitOperator as the 0 operator
|
|
89
|
+
qo = QubitOperator()
|
|
90
|
+
for sparse_pauli_term in operator.terms:
|
|
91
|
+
# loop over all the IndexedPaulis
|
|
92
|
+
coeff = sparse_pauli_term.coefficient
|
|
93
|
+
if sparse_pauli_term.paulis:
|
|
94
|
+
qo.terms[
|
|
95
|
+
tuple(
|
|
96
|
+
[
|
|
97
|
+
(p.index, p.pauli.name)
|
|
98
|
+
for p in sparse_pauli_term.paulis # type: ignore[attr-defined]
|
|
99
|
+
if p.pauli is not Pauli.I
|
|
100
|
+
]
|
|
101
|
+
)
|
|
102
|
+
] = coeff
|
|
103
|
+
# Operator is the identity
|
|
104
|
+
else:
|
|
105
|
+
qo.terms[()] = coeff
|
|
106
|
+
return qo
|
|
107
|
+
|
|
108
|
+
|
|
47
109
|
_PAULIS_TO_XZ = {"I": (0, 0), "X": (1, 0), "Z": (0, 1), "Y": (1, 1)}
|
|
48
110
|
_XZ_TO_PAULIS = {(0, 0): "I", (1, 0): "X", (0, 1): "Z", (1, 1): "Y"}
|
|
49
111
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from collections.abc import Sequence
|
|
2
3
|
from typing import cast
|
|
3
4
|
|
|
@@ -9,7 +10,7 @@ from openfermion.transforms import (
|
|
|
9
10
|
)
|
|
10
11
|
from openfermion.utils import count_qubits
|
|
11
12
|
|
|
12
|
-
from classiq.interface.exceptions import ClassiqValueError
|
|
13
|
+
from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqValueError
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class FermionHamiltonianProblem:
|
|
@@ -21,7 +22,7 @@ class FermionHamiltonianProblem:
|
|
|
21
22
|
Attributes:
|
|
22
23
|
fermion_hamiltonian (FermionOperator): The fermionic hamiltonian of the problem.
|
|
23
24
|
Assumed to be in the block-spin labeling.
|
|
24
|
-
n_orbitals (int): Number of spatial
|
|
25
|
+
n_orbitals (int): Number of spatial orbitals.
|
|
25
26
|
n_alpha (int): Number of alpha particles.
|
|
26
27
|
n_beta (int): Number of beta particles.
|
|
27
28
|
n_particles (tuple[int, int]): Number of alpha and beta particles.
|
|
@@ -116,6 +117,7 @@ class FermionHamiltonianProblem:
|
|
|
116
117
|
molecule: MolecularData,
|
|
117
118
|
first_active_index: int = 0,
|
|
118
119
|
remove_orbitlas: Sequence[int] | None = None,
|
|
120
|
+
remove_orbitals: Sequence[int] | None = None,
|
|
119
121
|
op_compression_tol: float = 1e-13,
|
|
120
122
|
) -> "FermionHamiltonianProblem":
|
|
121
123
|
"""
|
|
@@ -125,12 +127,22 @@ class FermionHamiltonianProblem:
|
|
|
125
127
|
molecule (MolecularData): The molecule data.
|
|
126
128
|
first_active_index (int): The first active index, indicates all prior
|
|
127
129
|
indices are freezed.
|
|
128
|
-
|
|
130
|
+
remove_orbitals (Sequence[int], optional): Active indices to be removed.
|
|
129
131
|
op_compression_tol (float): Tolerance for trimming the fermion operator.
|
|
130
132
|
|
|
131
133
|
Returns:
|
|
132
134
|
The fermion hamiltonian problem.
|
|
133
135
|
"""
|
|
136
|
+
if remove_orbitlas is not None:
|
|
137
|
+
warnings.warn(
|
|
138
|
+
"The `remove_orbitlas` parameter is deprecated and will not longer be "
|
|
139
|
+
"supported starting on 2026-01-15 at the earliest. Use the "
|
|
140
|
+
"'remove_orbitals' parameter instead",
|
|
141
|
+
ClassiqDeprecationWarning,
|
|
142
|
+
stacklevel=2,
|
|
143
|
+
)
|
|
144
|
+
remove_orbitals = remove_orbitlas
|
|
145
|
+
|
|
134
146
|
if molecule.n_orbitals is None:
|
|
135
147
|
raise ClassiqValueError(
|
|
136
148
|
"The molecular data is not populated. Hint: call `run_pyscf` with the molecule."
|
|
@@ -145,8 +157,8 @@ class FermionHamiltonianProblem:
|
|
|
145
157
|
|
|
146
158
|
freezed_indices = list(range(first_active_index))
|
|
147
159
|
active_indices = list(range(first_active_index, molecule.n_orbitals))
|
|
148
|
-
if
|
|
149
|
-
active_indices = list(set(active_indices) - set(
|
|
160
|
+
if remove_orbitals:
|
|
161
|
+
active_indices = list(set(active_indices) - set(remove_orbitals))
|
|
150
162
|
|
|
151
163
|
molecular_hamiltonian = molecule.get_molecular_hamiltonian(
|
|
152
164
|
occupied_indices=freezed_indices,
|
|
@@ -167,7 +179,7 @@ class FermionHamiltonianProblem:
|
|
|
167
179
|
f"and {n_particles} electrons. "
|
|
168
180
|
f"This can happen if too many orbitals were frozen."
|
|
169
181
|
f"Before freezing number of particle was ({n_alpha, n_beta})."
|
|
170
|
-
f"Consider adjusting `first_active_index` or `
|
|
182
|
+
f"Consider adjusting `first_active_index` or `remove_orbitals` "
|
|
171
183
|
f"to ensure the active space is non-empty."
|
|
172
184
|
)
|
|
173
185
|
|
|
@@ -6,7 +6,7 @@ from openfermion.ops.operators.fermion_operator import FermionOperator
|
|
|
6
6
|
from openfermion.ops.operators.qubit_operator import QubitOperator
|
|
7
7
|
|
|
8
8
|
from classiq.applications.chemistry.mapping import FermionToQubitMapper
|
|
9
|
-
from classiq.applications.chemistry.op_utils import
|
|
9
|
+
from classiq.applications.chemistry.op_utils import qubit_op_to_qmod
|
|
10
10
|
from classiq.applications.chemistry.problems import FermionHamiltonianProblem
|
|
11
11
|
from classiq.qmod.builtins.structs import (
|
|
12
12
|
SparsePauliOp,
|
|
@@ -44,7 +44,7 @@ def get_ucc_hamiltonians(
|
|
|
44
44
|
|
|
45
45
|
n_qubits = mapper.get_num_qubits(problem)
|
|
46
46
|
return [
|
|
47
|
-
|
|
47
|
+
qubit_op_to_qmod(q_op, n_qubits)
|
|
48
48
|
for f_op in f_ops
|
|
49
49
|
if (q_op := mapper.map(f_op))
|
|
50
50
|
not in (
|
|
@@ -133,9 +133,6 @@ def _evaluate_type_from_arg(
|
|
|
133
133
|
# FIXME: Remove suzuki_trotter overloading (CLS-2912)
|
|
134
134
|
if closure.name == "suzuki_trotter" and parameter.name == "pauli_operator":
|
|
135
135
|
return parameter
|
|
136
|
-
# FIXME: Remove qdrift overloading (CLS-4347)
|
|
137
|
-
if closure.name == "qdrift" and parameter.name == "pauli_operator":
|
|
138
|
-
return parameter
|
|
139
136
|
if isinstance(parameter, ClassicalParameterDeclaration):
|
|
140
137
|
return _evaluate_classical_type_from_arg(parameter, argument, closure)
|
|
141
138
|
if isinstance(parameter, PortDeclaration):
|
|
@@ -257,7 +254,7 @@ def _evaluate_op_type_from_arg(
|
|
|
257
254
|
first_lambda = arg_val[0]
|
|
258
255
|
if isinstance(first_lambda, FunctionClosure):
|
|
259
256
|
_raise_argument_type_error(
|
|
260
|
-
arg_val,
|
|
257
|
+
f"[{', '.join(['<lambda>'] * len(arg_val))}]",
|
|
261
258
|
first_lambda.as_operand_declaration(is_list=True),
|
|
262
259
|
parameter.name,
|
|
263
260
|
parameter,
|
|
@@ -207,7 +207,7 @@ class QmodAnnotatedExpression:
|
|
|
207
207
|
node = id(node)
|
|
208
208
|
return node in self._quantum_subscripts
|
|
209
209
|
|
|
210
|
-
def
|
|
210
|
+
def get_quantum_subscript(
|
|
211
211
|
self, node: ast.AST | QmodExprNodeId
|
|
212
212
|
) -> QuantumSubscriptAnnotation:
|
|
213
213
|
if isinstance(node, ast.AST):
|
|
@@ -77,7 +77,6 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
77
77
|
self,
|
|
78
78
|
expr_val: QmodAnnotatedExpression,
|
|
79
79
|
*,
|
|
80
|
-
treat_qnum_as_float: bool = False,
|
|
81
80
|
machine_precision: int = DEFAULT_MACHINE_PRECISION,
|
|
82
81
|
classical_struct_declarations: Sequence[StructDeclaration] | None = None,
|
|
83
82
|
enum_declarations: Sequence[EnumDeclaration] | None = None,
|
|
@@ -88,7 +87,6 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
88
87
|
scope: Mapping[str, Any] | None = None,
|
|
89
88
|
) -> None:
|
|
90
89
|
self._expr_val = expr_val
|
|
91
|
-
self._treat_qnum_as_float = treat_qnum_as_float
|
|
92
90
|
self._machine_precision = machine_precision
|
|
93
91
|
self._classical_struct_decls = nameables_to_dict(
|
|
94
92
|
classical_struct_declarations or []
|
|
@@ -114,9 +112,7 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
114
112
|
|
|
115
113
|
def visit_BinOp(self, node: ast.BinOp) -> None:
|
|
116
114
|
super().generic_visit(node)
|
|
117
|
-
eval_binary_op(
|
|
118
|
-
self._expr_val, node, self._treat_qnum_as_float, self._machine_precision
|
|
119
|
-
)
|
|
115
|
+
eval_binary_op(self._expr_val, node, self._machine_precision)
|
|
120
116
|
|
|
121
117
|
def visit_UnaryOp(self, node: ast.UnaryOp) -> None:
|
|
122
118
|
super().generic_visit(node)
|
|
@@ -164,7 +160,6 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
164
160
|
self._expr_val,
|
|
165
161
|
node,
|
|
166
162
|
func_name,
|
|
167
|
-
self._treat_qnum_as_float,
|
|
168
163
|
self._machine_precision,
|
|
169
164
|
)
|
|
170
165
|
return
|
|
@@ -256,7 +251,6 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
256
251
|
def evaluate_qmod_expression(
|
|
257
252
|
expr: str,
|
|
258
253
|
*,
|
|
259
|
-
treat_qnum_as_float: bool = False,
|
|
260
254
|
machine_precision: int = DEFAULT_MACHINE_PRECISION,
|
|
261
255
|
classical_struct_declarations: Sequence[StructDeclaration] | None = None,
|
|
262
256
|
enum_declarations: Sequence[EnumDeclaration] | None = None,
|
|
@@ -270,7 +264,6 @@ def evaluate_qmod_expression(
|
|
|
270
264
|
expr_value = QmodAnnotatedExpression(expr_ast)
|
|
271
265
|
QmodExpressionEvaluator(
|
|
272
266
|
expr_value,
|
|
273
|
-
treat_qnum_as_float=treat_qnum_as_float,
|
|
274
267
|
machine_precision=machine_precision,
|
|
275
268
|
classical_struct_declarations=classical_struct_declarations,
|
|
276
269
|
enum_declarations=enum_declarations,
|
|
@@ -122,7 +122,7 @@ class _InverseVarMaskTransformer(OutOfPlaceNodeTransformer):
|
|
|
122
122
|
|
|
123
123
|
class _SympyCompatibilityTransformer(OutOfPlaceNodeTransformer):
|
|
124
124
|
def visit_BoolOp(self, node: ast.BoolOp) -> ast.Call:
|
|
125
|
-
if len(node.values)
|
|
125
|
+
if len(node.values) < 2:
|
|
126
126
|
raise ClassiqInternalExpansionError
|
|
127
127
|
node = cast(ast.BoolOp, self.generic_visit(node))
|
|
128
128
|
if isinstance(node.op, ast.Or):
|
|
@@ -107,7 +107,7 @@ def _eval_type_attribute(
|
|
|
107
107
|
if isinstance(subject_type, QuantumNumeric):
|
|
108
108
|
if attr == "is_signed":
|
|
109
109
|
expr_val.set_type(node, Bool())
|
|
110
|
-
if subject_type.
|
|
110
|
+
if subject_type.has_constant_sign:
|
|
111
111
|
expr_val.set_value(node, subject_type.sign_value)
|
|
112
112
|
_remove_quantum_var(expr_val, subject)
|
|
113
113
|
elif subject_type.has_size_in_bits:
|
|
@@ -120,7 +120,7 @@ def _eval_type_attribute(
|
|
|
120
120
|
return
|
|
121
121
|
if attr == "fraction_digits":
|
|
122
122
|
expr_val.set_type(node, Integer())
|
|
123
|
-
if subject_type.
|
|
123
|
+
if subject_type.has_constant_fraction_digits:
|
|
124
124
|
expr_val.set_value(node, subject_type.fraction_digits_value)
|
|
125
125
|
_remove_quantum_var(expr_val, subject)
|
|
126
126
|
elif subject_type.has_size_in_bits:
|
|
@@ -50,7 +50,6 @@ def _validate_binary_op(
|
|
|
50
50
|
op: ast.AST,
|
|
51
51
|
left_type: QmodType,
|
|
52
52
|
right_type: QmodType,
|
|
53
|
-
treat_qnum_as_float: bool,
|
|
54
53
|
) -> None:
|
|
55
54
|
if not _binary_op_allowed(left_type, right_type, op):
|
|
56
55
|
raise ClassiqExpansionError(
|
|
@@ -76,20 +75,19 @@ def _validate_binary_op(
|
|
|
76
75
|
f"Binary operation {type(op).__name__!r} is not supported"
|
|
77
76
|
)
|
|
78
77
|
|
|
79
|
-
if
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
)
|
|
78
|
+
if isinstance(op, ast.FloorDiv) and (
|
|
79
|
+
not is_classical_type(left_type) or not is_classical_type(right_type)
|
|
80
|
+
):
|
|
81
|
+
raise ClassiqExpansionError(
|
|
82
|
+
f"{type(op).__name__!r} with quantum variables is not supported"
|
|
83
|
+
)
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
if not is_classical_type(right_type) and isinstance(
|
|
86
|
+
op, (ast.Div, ast.Mod, ast.Pow, ast.LShift, ast.RShift)
|
|
87
|
+
):
|
|
88
|
+
raise ClassiqExpansionError(
|
|
89
|
+
f"Right-hand side of binary operation {type(op).__name__!r} must be classical numeric value"
|
|
90
|
+
)
|
|
93
91
|
|
|
94
92
|
|
|
95
93
|
def _infer_binary_op_type(
|
|
@@ -98,7 +96,6 @@ def _infer_binary_op_type(
|
|
|
98
96
|
left_type: QmodType,
|
|
99
97
|
right_type: QmodType,
|
|
100
98
|
machine_precision: int,
|
|
101
|
-
treat_qnum_as_float: bool,
|
|
102
99
|
) -> QmodType:
|
|
103
100
|
op = node.op
|
|
104
101
|
|
|
@@ -113,19 +110,13 @@ def _infer_binary_op_type(
|
|
|
113
110
|
return Integer()
|
|
114
111
|
return Real()
|
|
115
112
|
|
|
116
|
-
left_attrs = get_numeric_attrs(
|
|
117
|
-
|
|
118
|
-
)
|
|
119
|
-
right_attrs = get_numeric_attrs(
|
|
120
|
-
expr_val, node.right, right_type, machine_precision, treat_qnum_as_float
|
|
121
|
-
)
|
|
113
|
+
left_attrs = get_numeric_attrs(expr_val, node.left, left_type, machine_precision)
|
|
114
|
+
right_attrs = get_numeric_attrs(expr_val, node.right, right_type, machine_precision)
|
|
122
115
|
|
|
123
116
|
if left_attrs is None or right_attrs is None:
|
|
124
117
|
return QuantumNumeric()
|
|
125
118
|
|
|
126
|
-
right_value = get_classical_value_for_arithmetic(
|
|
127
|
-
expr_val, node.right, right_type, treat_qnum_as_float
|
|
128
|
-
)
|
|
119
|
+
right_value = get_classical_value_for_arithmetic(expr_val, node.right, right_type)
|
|
129
120
|
|
|
130
121
|
if isinstance(op, ast.Add):
|
|
131
122
|
result_attrs = compute_result_attrs_add(
|
|
@@ -157,14 +148,14 @@ def _infer_binary_op_type(
|
|
|
157
148
|
return QuantumNumeric()
|
|
158
149
|
|
|
159
150
|
elif isinstance(op, ast.Mod):
|
|
160
|
-
if right_value is None
|
|
151
|
+
if right_value is None:
|
|
161
152
|
return QuantumNumeric()
|
|
162
153
|
result_attrs = compute_result_attrs_modulo(
|
|
163
154
|
left_attrs, right_attrs, machine_precision
|
|
164
155
|
)
|
|
165
156
|
|
|
166
157
|
elif isinstance(op, ast.Pow):
|
|
167
|
-
if right_value is None
|
|
158
|
+
if right_value is None:
|
|
168
159
|
return QuantumNumeric()
|
|
169
160
|
result_attrs = compute_result_attrs_power(
|
|
170
161
|
left_attrs, right_attrs, machine_precision
|
|
@@ -252,7 +243,6 @@ def _eval_binary_op_constant(
|
|
|
252
243
|
def eval_binary_op(
|
|
253
244
|
expr_val: QmodAnnotatedExpression,
|
|
254
245
|
node: ast.BinOp,
|
|
255
|
-
treat_qnum_as_float: bool,
|
|
256
246
|
machine_precision: int,
|
|
257
247
|
) -> None:
|
|
258
248
|
left = node.left
|
|
@@ -261,7 +251,7 @@ def eval_binary_op(
|
|
|
261
251
|
|
|
262
252
|
left_type = expr_val.get_type(left)
|
|
263
253
|
right_type = expr_val.get_type(right)
|
|
264
|
-
_validate_binary_op(op, left_type, right_type
|
|
254
|
+
_validate_binary_op(op, left_type, right_type)
|
|
265
255
|
|
|
266
256
|
inferred_type = _infer_binary_op_type(
|
|
267
257
|
expr_val,
|
|
@@ -269,7 +259,6 @@ def eval_binary_op(
|
|
|
269
259
|
left_type,
|
|
270
260
|
right_type,
|
|
271
261
|
machine_precision,
|
|
272
|
-
treat_qnum_as_float,
|
|
273
262
|
)
|
|
274
263
|
expr_val.set_type(node, inferred_type)
|
|
275
264
|
|
|
@@ -31,7 +31,6 @@ def _infer_min_max_op_type(
|
|
|
31
31
|
node: ast.Call,
|
|
32
32
|
func_name: str,
|
|
33
33
|
args_types: list[QmodType],
|
|
34
|
-
treat_qnum_as_float: bool,
|
|
35
34
|
machine_precision: int,
|
|
36
35
|
) -> QmodType:
|
|
37
36
|
if all(is_classical_type(arg_type) for arg_type in args_types):
|
|
@@ -41,9 +40,7 @@ def _infer_min_max_op_type(
|
|
|
41
40
|
|
|
42
41
|
args_attrs: list[NumericAttributes] = []
|
|
43
42
|
for arg, arg_type in zip(node.args, args_types):
|
|
44
|
-
attrs = get_numeric_attrs(
|
|
45
|
-
expr_val, arg, arg_type, machine_precision, treat_qnum_as_float
|
|
46
|
-
)
|
|
43
|
+
attrs = get_numeric_attrs(expr_val, arg, arg_type, machine_precision)
|
|
47
44
|
if attrs is None:
|
|
48
45
|
return QuantumNumeric()
|
|
49
46
|
args_attrs.append(attrs)
|
|
@@ -62,7 +59,6 @@ def eval_min_max_op(
|
|
|
62
59
|
expr_val: QmodAnnotatedExpression,
|
|
63
60
|
node: ast.Call,
|
|
64
61
|
func_name: str,
|
|
65
|
-
treat_qnum_as_float: bool,
|
|
66
62
|
machine_precision: int,
|
|
67
63
|
) -> None:
|
|
68
64
|
if len(node.args) < 1:
|
|
@@ -79,7 +75,6 @@ def eval_min_max_op(
|
|
|
79
75
|
node,
|
|
80
76
|
func_name,
|
|
81
77
|
args_types,
|
|
82
|
-
treat_qnum_as_float,
|
|
83
78
|
machine_precision,
|
|
84
79
|
)
|
|
85
80
|
expr_val.set_type(node, inferred_type)
|
|
@@ -21,14 +21,11 @@ def get_numeric_attrs(
|
|
|
21
21
|
node: ast.AST,
|
|
22
22
|
qmod_type: QmodType,
|
|
23
23
|
machine_precision: int,
|
|
24
|
-
treat_qnum_as_float: bool,
|
|
25
24
|
) -> NumericAttributes | None:
|
|
26
25
|
if isinstance(qmod_type, Bool):
|
|
27
26
|
return NumericAttributes.from_bounds(0, 1, 0, machine_precision)
|
|
28
27
|
if is_classical_type(qmod_type):
|
|
29
|
-
value = get_classical_value_for_arithmetic(
|
|
30
|
-
expr_val, node, qmod_type, treat_qnum_as_float
|
|
31
|
-
)
|
|
28
|
+
value = get_classical_value_for_arithmetic(expr_val, node, qmod_type)
|
|
32
29
|
if value is None:
|
|
33
30
|
return None
|
|
34
31
|
return NumericAttributes.from_constant(value, machine_precision)
|
|
@@ -44,7 +41,6 @@ def get_classical_value_for_arithmetic(
|
|
|
44
41
|
expr_val: QmodAnnotatedExpression,
|
|
45
42
|
node: ast.AST,
|
|
46
43
|
qmod_type: QmodType,
|
|
47
|
-
treat_qnum_as_float: bool,
|
|
48
44
|
) -> float | None:
|
|
49
45
|
if not is_classical_type(qmod_type):
|
|
50
46
|
return None
|
|
@@ -55,8 +51,6 @@ def get_classical_value_for_arithmetic(
|
|
|
55
51
|
if isinstance(value, sympy.Basic):
|
|
56
52
|
value = get_sympy_val(value)
|
|
57
53
|
if not isinstance(value, (int, float)):
|
|
58
|
-
if treat_qnum_as_float and isinstance(value, complex):
|
|
59
|
-
return None
|
|
60
54
|
raise ClassiqExpansionError(
|
|
61
55
|
"Arithmetic of quantum variables and non-real values is not supported"
|
|
62
56
|
)
|
|
@@ -2,7 +2,10 @@ from typing import Union, cast
|
|
|
2
2
|
|
|
3
3
|
import sympy
|
|
4
4
|
|
|
5
|
-
from classiq.interface.exceptions import
|
|
5
|
+
from classiq.interface.exceptions import (
|
|
6
|
+
ClassiqExpansionError,
|
|
7
|
+
ClassiqInternalExpansionError,
|
|
8
|
+
)
|
|
6
9
|
from classiq.interface.generator.functions.classical_type import (
|
|
7
10
|
Bool,
|
|
8
11
|
ClassicalArray,
|
|
@@ -108,7 +111,7 @@ def get_sympy_val(val: sympy.Basic) -> bool | int | float | complex:
|
|
|
108
111
|
hasattr(val, "is_imaginary") and val.is_imaginary
|
|
109
112
|
):
|
|
110
113
|
return complex(val)
|
|
111
|
-
raise
|
|
114
|
+
raise ClassiqExpansionError(f"{str(val)!r} is not a number")
|
|
112
115
|
|
|
113
116
|
|
|
114
117
|
def get_sympy_type(val: sympy.Basic) -> ClassicalType:
|
|
@@ -124,4 +127,4 @@ def get_sympy_type(val: sympy.Basic) -> ClassicalType:
|
|
|
124
127
|
or (hasattr(val, "is_imaginary") and val.is_imaginary)
|
|
125
128
|
):
|
|
126
129
|
return Real()
|
|
127
|
-
raise
|
|
130
|
+
raise ClassiqExpansionError(f"{str(val)!r} is not a number")
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from classiq.interface.exceptions import (
|
|
2
|
+
ClassiqInternalExpansionError,
|
|
3
|
+
)
|
|
4
|
+
from classiq.interface.generator.expressions.expression import Expression
|
|
5
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
|
6
|
+
from classiq.interface.model.quantum_type import (
|
|
7
|
+
QuantumBit,
|
|
8
|
+
QuantumBitvector,
|
|
9
|
+
QuantumNumeric,
|
|
10
|
+
QuantumType,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _compare_expressions(expr1: Expression | None, expr2: Expression | None) -> bool:
|
|
15
|
+
if expr1 is None:
|
|
16
|
+
return expr2 is None
|
|
17
|
+
if expr2 is None:
|
|
18
|
+
return False
|
|
19
|
+
return expr1.expr == expr2.expr
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def compare_quantum_types(type_1: QuantumType, type_2: QuantumType) -> bool:
|
|
23
|
+
for qmod_type in (type_1, type_2):
|
|
24
|
+
if isinstance(qmod_type, TypeName) and not qmod_type.has_fields:
|
|
25
|
+
raise ClassiqInternalExpansionError("Quantum struct expected")
|
|
26
|
+
if isinstance(type_1, QuantumBit):
|
|
27
|
+
return isinstance(type_2, QuantumBit)
|
|
28
|
+
if isinstance(type_1, QuantumNumeric):
|
|
29
|
+
return (
|
|
30
|
+
isinstance(type_2, QuantumNumeric)
|
|
31
|
+
and _compare_expressions(type_1.size, type_2.size)
|
|
32
|
+
and _compare_expressions(type_1.is_signed, type_2.is_signed)
|
|
33
|
+
and _compare_expressions(type_1.fraction_digits, type_2.fraction_digits)
|
|
34
|
+
)
|
|
35
|
+
if isinstance(type_1, QuantumBitvector):
|
|
36
|
+
return (
|
|
37
|
+
isinstance(type_2, QuantumBitvector)
|
|
38
|
+
and _compare_expressions(type_1.length, type_2.length)
|
|
39
|
+
and compare_quantum_types(type_1.element_type, type_2.element_type)
|
|
40
|
+
)
|
|
41
|
+
if isinstance(type_1, TypeName):
|
|
42
|
+
return (
|
|
43
|
+
isinstance(type_2, TypeName)
|
|
44
|
+
and type_1.name == type_2.name
|
|
45
|
+
and all(
|
|
46
|
+
compare_quantum_types(field_type_1, field_type_2)
|
|
47
|
+
for field_type_1, field_type_2 in zip(
|
|
48
|
+
type_1.fields.values(), type_2.fields.values(), strict=True
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
raise ClassiqInternalExpansionError(f"Unexpected type {type(type_1).__name__}")
|
classiq/execution/__init__.py
CHANGED
|
@@ -8,7 +8,14 @@ from ..interface.executor.execution_preferences import __all__ as _ep_all
|
|
|
8
8
|
from ..interface.executor.result import ExecutionDetails
|
|
9
9
|
from ..interface.executor.vqe_result import VQESolverResult
|
|
10
10
|
from .execution_session import ExecutionSession
|
|
11
|
-
from .jobs import
|
|
11
|
+
from .jobs import (
|
|
12
|
+
ExecutionJob,
|
|
13
|
+
ExecutionJobFilters,
|
|
14
|
+
get_execution_actions,
|
|
15
|
+
get_execution_actions_async,
|
|
16
|
+
get_execution_jobs,
|
|
17
|
+
get_execution_jobs_async,
|
|
18
|
+
)
|
|
12
19
|
from .qnn import execute_qnn
|
|
13
20
|
from .user_budgets import (
|
|
14
21
|
clear_budget_limit,
|
|
@@ -28,6 +35,9 @@ __all__ = (
|
|
|
28
35
|
"VQESolverResult",
|
|
29
36
|
"IQAEResult",
|
|
30
37
|
"ExecutionJob",
|
|
38
|
+
"ExecutionJobFilters",
|
|
39
|
+
"get_execution_actions",
|
|
40
|
+
"get_execution_actions_async",
|
|
31
41
|
"get_execution_jobs",
|
|
32
42
|
"get_execution_jobs_async",
|
|
33
43
|
"ExecutionSession",
|
|
@@ -365,7 +365,7 @@ class ExecutionSession:
|
|
|
365
365
|
A list of tuples, each containing the estimated cost and the corresponding parameters for that iteration. `cost` is a float, and `parameters` is a dictionary matching the execution parameter format.
|
|
366
366
|
|
|
367
367
|
See Also:
|
|
368
|
-
The [
|
|
368
|
+
The [Execution Tutorial](https://docs.classiq.io/latest/getting-started/classiq_tutorial/execution_tutorial_part2/) has examples on using this method in variational quantum algorithms.
|
|
369
369
|
More information about [Hamiltonians](https://docs.classiq.io/latest/qmod-reference/language-reference/classical-types/#hamiltonians).
|
|
370
370
|
"""
|
|
371
371
|
_hamiltonian_deprecation_warning(cost_function)
|