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
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
_logger = logging.getLogger(__name__)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _setup_logging() -> None:
|
|
7
|
+
if _logger.handlers:
|
|
8
|
+
return
|
|
9
|
+
|
|
10
|
+
handler = logging.StreamHandler()
|
|
11
|
+
formatter = logging.Formatter("%(message)s")
|
|
12
|
+
handler.setFormatter(formatter)
|
|
13
|
+
_logger.addHandler(handler)
|
|
14
|
+
_logger.setLevel(logging.INFO)
|
|
15
|
+
|
|
16
|
+
_logger.propagate = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_setup_logging()
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from classiq.interface.hardware import Provider
|
|
2
|
+
|
|
3
|
+
_PROVIDER_BACKEND_SEPARATOR = "/"
|
|
4
|
+
|
|
5
|
+
_PROVIDER_TO_CANONICAL_NAME: dict[Provider, str] = {
|
|
6
|
+
Provider.IBM_QUANTUM: "ibm",
|
|
7
|
+
Provider.AZURE_QUANTUM: "azure",
|
|
8
|
+
Provider.AMAZON_BRAKET: "braket",
|
|
9
|
+
Provider.IONQ: "ionq",
|
|
10
|
+
Provider.CLASSIQ: "classiq",
|
|
11
|
+
Provider.GOOGLE: "google",
|
|
12
|
+
Provider.ALICE_AND_BOB: "alice&bob",
|
|
13
|
+
Provider.OQC: "oqc",
|
|
14
|
+
Provider.INTEL: "intel",
|
|
15
|
+
Provider.AQT: "aqt",
|
|
16
|
+
Provider.CINECA: "cineca",
|
|
17
|
+
Provider.SOFTBANK: "softbank",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_CANONICAL_NAMES_TO_PROVIDER: dict[str, Provider] = {
|
|
21
|
+
name: provider for provider, name in _PROVIDER_TO_CANONICAL_NAME.items()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _error_suggested_provider(provider_name: str) -> Provider | None:
|
|
26
|
+
"""
|
|
27
|
+
In the case that receive an incorrect provider name, return a possible suggestion.
|
|
28
|
+
"""
|
|
29
|
+
provider_name = provider_name.strip().lower()
|
|
30
|
+
for canonical_name in _CANONICAL_NAMES_TO_PROVIDER:
|
|
31
|
+
if canonical_name in provider_name:
|
|
32
|
+
return _CANONICAL_NAMES_TO_PROVIDER[canonical_name]
|
|
33
|
+
# Special cases
|
|
34
|
+
if "gcp" in provider_name:
|
|
35
|
+
return Provider.GOOGLE
|
|
36
|
+
if "microsoft" in provider_name:
|
|
37
|
+
return Provider.AZURE_QUANTUM
|
|
38
|
+
if "amazon" in provider_name or "aws" in provider_name:
|
|
39
|
+
return Provider.AMAZON_BRAKET
|
|
40
|
+
if "oxford" in provider_name:
|
|
41
|
+
return Provider.OQC
|
|
42
|
+
if "alice" in provider_name or "bob" in provider_name:
|
|
43
|
+
return Provider.ALICE_AND_BOB
|
|
44
|
+
if "alpine" in provider_name:
|
|
45
|
+
return Provider.AQT
|
|
46
|
+
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _parse_provider_backend(spec: str) -> tuple[Provider, str]:
|
|
51
|
+
"""
|
|
52
|
+
Parse a backend specification into (provider, backend). Provider is case-insensitive.
|
|
53
|
+
Backend must NOT contain the separator. If provider is not specified, it defaults to
|
|
54
|
+
Classiq.
|
|
55
|
+
"""
|
|
56
|
+
if not spec.strip():
|
|
57
|
+
raise ValueError("Backend specification must be a non-empty string")
|
|
58
|
+
|
|
59
|
+
spec = spec.strip()
|
|
60
|
+
|
|
61
|
+
if _PROVIDER_BACKEND_SEPARATOR not in spec:
|
|
62
|
+
return Provider.CLASSIQ, spec
|
|
63
|
+
|
|
64
|
+
provider_raw, backend = spec.split(_PROVIDER_BACKEND_SEPARATOR, 1)
|
|
65
|
+
|
|
66
|
+
if _PROVIDER_BACKEND_SEPARATOR in backend:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"Backend name must not contain '{_PROVIDER_BACKEND_SEPARATOR}': '{backend}'"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
provider_key = provider_raw.strip().lower()
|
|
72
|
+
backend = backend.strip()
|
|
73
|
+
|
|
74
|
+
if not provider_key:
|
|
75
|
+
raise ValueError("Provider name cannot be empty")
|
|
76
|
+
if not backend:
|
|
77
|
+
raise ValueError("Backend name cannot be empty")
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
provider = _CANONICAL_NAMES_TO_PROVIDER[provider_key]
|
|
81
|
+
except KeyError:
|
|
82
|
+
error_message = f"Unrecognized provider '{provider_raw}'."
|
|
83
|
+
suggested_provider = _error_suggested_provider(provider_key)
|
|
84
|
+
if suggested_provider is not None:
|
|
85
|
+
error_message += (
|
|
86
|
+
f" Did you mean '{_PROVIDER_TO_CANONICAL_NAME[suggested_provider]}'?"
|
|
87
|
+
)
|
|
88
|
+
raise ValueError(error_message) from None
|
|
89
|
+
|
|
90
|
+
return provider, backend
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from pandas import DataFrame
|
|
7
|
+
|
|
8
|
+
from classiq.interface.backend.backend_preferences import (
|
|
9
|
+
AliceBobBackendPreferences,
|
|
10
|
+
AQTBackendPreferences,
|
|
11
|
+
AwsBackendPreferences,
|
|
12
|
+
AzureBackendPreferences,
|
|
13
|
+
AzureCredential,
|
|
14
|
+
BackendPreferencesTypes,
|
|
15
|
+
ClassiqBackendPreferences,
|
|
16
|
+
GCPBackendPreferences,
|
|
17
|
+
IBMBackendPreferences,
|
|
18
|
+
IntelBackendPreferences,
|
|
19
|
+
IonqBackendPreferences,
|
|
20
|
+
)
|
|
21
|
+
from classiq.interface.backend.provider_config.provider_config import ProviderConfig
|
|
22
|
+
from classiq.interface.backend.provider_config.providers.alice_bob import AliceBobConfig
|
|
23
|
+
from classiq.interface.backend.provider_config.providers.aqt import AQTConfig
|
|
24
|
+
from classiq.interface.backend.provider_config.providers.azure import AzureConfig
|
|
25
|
+
from classiq.interface.backend.provider_config.providers.braket import BraketConfig
|
|
26
|
+
from classiq.interface.backend.provider_config.providers.ibm import IBMConfig
|
|
27
|
+
from classiq.interface.backend.provider_config.providers.ionq import IonQConfig
|
|
28
|
+
from classiq.interface.backend.quantum_backend_providers import (
|
|
29
|
+
ClassiqNvidiaBackendNames,
|
|
30
|
+
ClassiqSimulatorBackendNames,
|
|
31
|
+
)
|
|
32
|
+
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
|
33
|
+
from classiq.interface.generator.model.preferences import create_random_seed
|
|
34
|
+
from classiq.interface.generator.model.preferences.preferences import (
|
|
35
|
+
TranspilationOption,
|
|
36
|
+
)
|
|
37
|
+
from classiq.interface.hardware import Provider
|
|
38
|
+
|
|
39
|
+
from classiq import (
|
|
40
|
+
ExecutionParams,
|
|
41
|
+
QuantumProgram,
|
|
42
|
+
)
|
|
43
|
+
from classiq.execution import ExecutionSession
|
|
44
|
+
from classiq.execution.functions._logging import _logger
|
|
45
|
+
from classiq.execution.functions.constants import Verbosity
|
|
46
|
+
from classiq.execution.functions.parse_provider_backend import (
|
|
47
|
+
_PROVIDER_TO_CANONICAL_NAME,
|
|
48
|
+
_parse_provider_backend,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class _ProviderConfigToBackendPrefSpec:
|
|
54
|
+
backend_preferences_class: type[BackendPreferencesTypes]
|
|
55
|
+
config_class: type[ProviderConfig] | None = None
|
|
56
|
+
# Maps the config dict (either passed in directly or dumped from config class) to a
|
|
57
|
+
# dict that we can load into the given BackendPreferences class. This is in case
|
|
58
|
+
# we need to rename fields or change structure.
|
|
59
|
+
config_dict_to_backend_preferences_dict: (
|
|
60
|
+
Callable[[dict[str, Any]], dict[str, Any]] | None
|
|
61
|
+
) = None
|
|
62
|
+
# Maps from SDK names to names our backend recognizes, raising a useful error
|
|
63
|
+
# if the name is unrecognized.
|
|
64
|
+
backend_name_mapper: Callable[[str], str] | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _classiq_backend_name_mapper(backend_name: str) -> str:
|
|
68
|
+
backend_name = backend_name.lower()
|
|
69
|
+
if backend_name in [
|
|
70
|
+
ClassiqSimulatorBackendNames.SIMULATOR,
|
|
71
|
+
ClassiqSimulatorBackendNames.SIMULATOR_MATRIX_PRODUCT_STATE,
|
|
72
|
+
ClassiqSimulatorBackendNames.SIMULATOR_DENSITY_MATRIX,
|
|
73
|
+
]:
|
|
74
|
+
return backend_name
|
|
75
|
+
if backend_name == "nvidia_simulator":
|
|
76
|
+
return ClassiqNvidiaBackendNames.SIMULATOR
|
|
77
|
+
if any(keyword in backend_name for keyword in ["gpu", "nvidia"]):
|
|
78
|
+
suggested_backend_name = "nvidia_simulator"
|
|
79
|
+
else:
|
|
80
|
+
suggested_backend_name = "simulator"
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"Unsupported backend name {backend_name}. Did you mean '{suggested_backend_name}'?"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _ibm_backend_name_mapper(backend_name: str) -> str:
|
|
87
|
+
ibm_prefix: Literal["ibm_"] = "ibm_"
|
|
88
|
+
backend_name = backend_name.lower()
|
|
89
|
+
if backend_name.startswith(ibm_prefix):
|
|
90
|
+
backend_name_no_prefix = backend_name.removeprefix(ibm_prefix)
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"IBM backend names shouldn't start with ibm_. Try 'ibm/{backend_name_no_prefix}'."
|
|
93
|
+
)
|
|
94
|
+
return ibm_prefix + backend_name
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _azure_config_dict_to_backend_preferences_dict(
|
|
98
|
+
config_dict: dict[str, Any],
|
|
99
|
+
) -> dict[str, Any]:
|
|
100
|
+
if "location" not in config_dict:
|
|
101
|
+
raise ValueError("Azure config must have 'location' property")
|
|
102
|
+
credentials = None
|
|
103
|
+
if all(
|
|
104
|
+
config_dict.get(key) is not None
|
|
105
|
+
for key in ["tenant_id", "client_id", "client_secret", "resource_id"]
|
|
106
|
+
):
|
|
107
|
+
credentials = AzureCredential.model_validate(
|
|
108
|
+
{
|
|
109
|
+
"tenant_id": config_dict["tenant_id"],
|
|
110
|
+
"client_id": config_dict["client_id"],
|
|
111
|
+
"client_secret": config_dict["client_secret"],
|
|
112
|
+
"resource_id": config_dict["resource_id"],
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
return {
|
|
116
|
+
"location": config_dict["location"],
|
|
117
|
+
"credentials": credentials,
|
|
118
|
+
"ionq_error_mitigation_flag": config_dict.get("ionq_error_mitigation"),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _braket_config_dict_to_backend_preferences_dict(
|
|
123
|
+
config_dict: dict[str, Any],
|
|
124
|
+
) -> dict[str, Any]:
|
|
125
|
+
config_dict["aws_access_key_id"] = config_dict.pop("braket_access_key_id", None)
|
|
126
|
+
config_dict["aws_secret_access_key"] = config_dict.pop(
|
|
127
|
+
"braket_secret_access_key", None
|
|
128
|
+
)
|
|
129
|
+
return config_dict
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
_PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC = {
|
|
133
|
+
Provider.CLASSIQ: _ProviderConfigToBackendPrefSpec(
|
|
134
|
+
backend_preferences_class=ClassiqBackendPreferences,
|
|
135
|
+
backend_name_mapper=_classiq_backend_name_mapper,
|
|
136
|
+
),
|
|
137
|
+
Provider.GOOGLE: _ProviderConfigToBackendPrefSpec(
|
|
138
|
+
backend_preferences_class=GCPBackendPreferences
|
|
139
|
+
),
|
|
140
|
+
Provider.INTEL: _ProviderConfigToBackendPrefSpec(
|
|
141
|
+
backend_preferences_class=IntelBackendPreferences
|
|
142
|
+
),
|
|
143
|
+
Provider.IBM_QUANTUM: _ProviderConfigToBackendPrefSpec(
|
|
144
|
+
backend_preferences_class=IBMBackendPreferences,
|
|
145
|
+
config_class=IBMConfig,
|
|
146
|
+
backend_name_mapper=_ibm_backend_name_mapper,
|
|
147
|
+
),
|
|
148
|
+
Provider.AMAZON_BRAKET: _ProviderConfigToBackendPrefSpec(
|
|
149
|
+
backend_preferences_class=AwsBackendPreferences,
|
|
150
|
+
config_dict_to_backend_preferences_dict=_braket_config_dict_to_backend_preferences_dict,
|
|
151
|
+
config_class=BraketConfig,
|
|
152
|
+
),
|
|
153
|
+
Provider.IONQ: _ProviderConfigToBackendPrefSpec(
|
|
154
|
+
backend_preferences_class=IonqBackendPreferences,
|
|
155
|
+
config_class=IonQConfig,
|
|
156
|
+
),
|
|
157
|
+
Provider.ALICE_AND_BOB: _ProviderConfigToBackendPrefSpec(
|
|
158
|
+
backend_preferences_class=AliceBobBackendPreferences,
|
|
159
|
+
config_class=AliceBobConfig,
|
|
160
|
+
),
|
|
161
|
+
Provider.AQT: _ProviderConfigToBackendPrefSpec(
|
|
162
|
+
backend_preferences_class=AQTBackendPreferences,
|
|
163
|
+
config_class=AQTConfig,
|
|
164
|
+
),
|
|
165
|
+
Provider.AZURE_QUANTUM: _ProviderConfigToBackendPrefSpec(
|
|
166
|
+
backend_preferences_class=AzureBackendPreferences,
|
|
167
|
+
config_dict_to_backend_preferences_dict=_azure_config_dict_to_backend_preferences_dict,
|
|
168
|
+
config_class=AzureConfig,
|
|
169
|
+
),
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _get_backend_preferences_from_specifier(
|
|
174
|
+
backend_spec: str, config: dict[str, Any] | ProviderConfig
|
|
175
|
+
) -> BackendPreferencesTypes:
|
|
176
|
+
provider, backend_name = _parse_provider_backend(backend_spec)
|
|
177
|
+
|
|
178
|
+
if provider not in _PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC:
|
|
179
|
+
raise NotImplementedError(
|
|
180
|
+
f"Unsupported provider '{_PROVIDER_TO_CANONICAL_NAME.get(provider) or provider}'"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
provider_spec = _PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC[provider]
|
|
184
|
+
if isinstance(config, ProviderConfig):
|
|
185
|
+
if provider_spec.config_class is None:
|
|
186
|
+
raise ValueError(
|
|
187
|
+
f"This provider does not support any ProviderConfig classes. Received '{config.__class__.__name__}'"
|
|
188
|
+
)
|
|
189
|
+
if not isinstance(config, provider_spec.config_class):
|
|
190
|
+
raise ValueError(
|
|
191
|
+
f"{_PROVIDER_TO_CANONICAL_NAME[provider]} devices require {provider_spec.config_class.__name__}, got {config.__class__.__name__}"
|
|
192
|
+
)
|
|
193
|
+
config_dict = config.model_dump()
|
|
194
|
+
else:
|
|
195
|
+
config_dict = config
|
|
196
|
+
if provider_spec.backend_name_mapper is not None:
|
|
197
|
+
backend_name = provider_spec.backend_name_mapper(backend_name)
|
|
198
|
+
|
|
199
|
+
if provider_spec.config_dict_to_backend_preferences_dict is not None:
|
|
200
|
+
config_dict = provider_spec.config_dict_to_backend_preferences_dict(config_dict)
|
|
201
|
+
|
|
202
|
+
config_dict["backend_name"] = backend_name
|
|
203
|
+
return provider_spec.backend_preferences_class.model_validate(config_dict)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
_DEFAULT_BACKEND_NAME = "simulator"
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _new_sample(
|
|
210
|
+
qprog: QuantumProgram,
|
|
211
|
+
backend: str | None = None,
|
|
212
|
+
*,
|
|
213
|
+
parameters: ExecutionParams | None = None,
|
|
214
|
+
config: dict[str, Any] | ProviderConfig | None = None,
|
|
215
|
+
num_shots: int | None = None,
|
|
216
|
+
random_seed: int | None = None,
|
|
217
|
+
transpilation_option: TranspilationOption = TranspilationOption.DECOMPOSE,
|
|
218
|
+
verbosity: Verbosity = Verbosity.INFO,
|
|
219
|
+
) -> "DataFrame":
|
|
220
|
+
"""
|
|
221
|
+
Sample a quantum program.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
qprog: The quantum program
|
|
225
|
+
backend: The device (hardware or simulator) on which to run the quantum program. Specified as "provider/device_id"
|
|
226
|
+
parameters: The classical parameters for the quantum program
|
|
227
|
+
config: Provider-specific configuration, such as api keys
|
|
228
|
+
num_shots: The number of times to sample
|
|
229
|
+
random_seed: The random seed used for transpilation and simulation
|
|
230
|
+
transpilation_option: Advanced configuration for hardware-specific transpilation
|
|
231
|
+
verbosity: What level of information should be logged
|
|
232
|
+
|
|
233
|
+
Returns: A dataframe containing the histogram
|
|
234
|
+
"""
|
|
235
|
+
if num_shots is not None and num_shots < 1:
|
|
236
|
+
raise ValueError(f"Argument num_shots must be greater than 0, got {num_shots}")
|
|
237
|
+
if config is None:
|
|
238
|
+
config = {}
|
|
239
|
+
if backend is None:
|
|
240
|
+
backend = _DEFAULT_BACKEND_NAME
|
|
241
|
+
backend_preferences = _get_backend_preferences_from_specifier(backend, config)
|
|
242
|
+
ep = ExecutionPreferences(
|
|
243
|
+
backend_preferences=backend_preferences,
|
|
244
|
+
num_shots=num_shots,
|
|
245
|
+
random_seed=create_random_seed() if random_seed is None else random_seed,
|
|
246
|
+
transpile_to_hardware=transpilation_option,
|
|
247
|
+
)
|
|
248
|
+
if verbosity != Verbosity.QUIET:
|
|
249
|
+
_logger.info(f"Submitting job to {backend}")
|
|
250
|
+
with ExecutionSession(qprog, execution_preferences=ep) as session:
|
|
251
|
+
job = session.submit_sample(parameters)
|
|
252
|
+
if verbosity != Verbosity.QUIET:
|
|
253
|
+
_logger.info(f"Job id: {job.id}")
|
|
254
|
+
result = job.get_sample_result()
|
|
255
|
+
|
|
256
|
+
df = result.dataframe
|
|
257
|
+
return df
|
classiq/execution/jobs.py
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
import webbrowser
|
|
3
|
+
from dataclasses import asdict, dataclass
|
|
3
4
|
from datetime import datetime
|
|
4
|
-
from typing import Any
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
import pandas as pd
|
|
5
9
|
from urllib.parse import urljoin
|
|
6
10
|
|
|
7
11
|
import httpx
|
|
@@ -29,6 +33,10 @@ from classiq._internals.async_utils import syncify_function
|
|
|
29
33
|
from classiq._internals.client import client
|
|
30
34
|
from classiq._internals.jobs import JobID, JobPoller
|
|
31
35
|
|
|
36
|
+
TOTAL_COST = "total_cost"
|
|
37
|
+
CURRENCY_CODE = "currency_code"
|
|
38
|
+
COST = "cost"
|
|
39
|
+
|
|
32
40
|
|
|
33
41
|
class ClassiqExecutionResultError(ClassiqError):
|
|
34
42
|
def __init__(self, primitive: str) -> None:
|
|
@@ -37,6 +45,42 @@ class ClassiqExecutionResultError(ClassiqError):
|
|
|
37
45
|
)
|
|
38
46
|
|
|
39
47
|
|
|
48
|
+
@dataclass
|
|
49
|
+
class ExecutionJobFilters:
|
|
50
|
+
"""
|
|
51
|
+
Filter parameters for querying execution jobs.
|
|
52
|
+
|
|
53
|
+
All filters are combined using AND logic: only jobs matching all specified filters are returned.
|
|
54
|
+
Range filters (with _min/_max suffixes) are inclusive.
|
|
55
|
+
Datetime filters are compared against the job's timestamps.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
id: str | None = None # Exact match on job ID
|
|
59
|
+
session_id: str | None = None # Exact match on session ID
|
|
60
|
+
status: JobStatus | None = (
|
|
61
|
+
None # Exact match on job status (e.g., "COMPLETED", "FAILED")
|
|
62
|
+
)
|
|
63
|
+
name: str | None = None # Exact match on job name
|
|
64
|
+
provider: str | None = None # Exact match on provider name (e.g., "ibm", "aws")
|
|
65
|
+
backend: str | None = None # Exact match on backend name
|
|
66
|
+
program_id: str | None = None # Exact match on program ID
|
|
67
|
+
total_cost_min: float | None = None # Minimum total cost (inclusive)
|
|
68
|
+
total_cost_max: float | None = None # Maximum total cost (inclusive)
|
|
69
|
+
start_time_min: datetime | None = None # Earliest job start time (inclusive)
|
|
70
|
+
start_time_max: datetime | None = None # Latest job start time (inclusive)
|
|
71
|
+
end_time_min: datetime | None = None # Earliest job end time (inclusive)
|
|
72
|
+
end_time_max: datetime | None = None # Latest job end time (inclusive)
|
|
73
|
+
|
|
74
|
+
def format_filters(self) -> dict[str, Any]:
|
|
75
|
+
"""Convert filter fields to API kwargs, excluding None values and converting datetimes."""
|
|
76
|
+
filter_dict = asdict(self)
|
|
77
|
+
return {
|
|
78
|
+
k: (v.isoformat() if isinstance(v, datetime) else v)
|
|
79
|
+
for k, v in filter_dict.items()
|
|
80
|
+
if v is not None
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
40
84
|
class ExecutionJob:
|
|
41
85
|
_details: ExecutionJobDetails
|
|
42
86
|
_result: ResultsCollection | None
|
|
@@ -352,10 +396,83 @@ class ExecutionJob:
|
|
|
352
396
|
|
|
353
397
|
|
|
354
398
|
async def get_execution_jobs_async(
|
|
355
|
-
offset: int = 0,
|
|
399
|
+
offset: int = 0,
|
|
400
|
+
limit: int = 50,
|
|
356
401
|
) -> list[ExecutionJob]:
|
|
357
|
-
|
|
358
|
-
|
|
402
|
+
execution_user_jobs = await ApiWrapper.call_query_execution_jobs(
|
|
403
|
+
offset, limit, http_client=None
|
|
404
|
+
)
|
|
405
|
+
return [ExecutionJob(details) for details in execution_user_jobs.results]
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def get_execution_jobs(
|
|
409
|
+
offset: int = 0,
|
|
410
|
+
limit: int = 50,
|
|
411
|
+
) -> list[ExecutionJob]:
|
|
412
|
+
"""Query execution jobs.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
offset: Number of results to skip (default: 0)
|
|
416
|
+
limit: Maximum number of results to return (default: 50)
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
List of ExecutionJob objects.
|
|
420
|
+
|
|
421
|
+
Examples:
|
|
422
|
+
# Query all jobs:
|
|
423
|
+
jobs = get_execution_jobs(limit=10)
|
|
424
|
+
"""
|
|
425
|
+
return syncify_function(get_execution_jobs_async)(offset, limit)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _flatten_cost_field(action_dict: dict[str, Any]) -> dict[str, Any]:
|
|
429
|
+
cost_obj = action_dict.pop(COST, {}) or {}
|
|
430
|
+
action_dict[TOTAL_COST] = cost_obj.get(TOTAL_COST, 0)
|
|
431
|
+
action_dict[CURRENCY_CODE] = cost_obj.get(CURRENCY_CODE)
|
|
432
|
+
return action_dict
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
async def get_execution_actions_async(
|
|
436
|
+
offset: int = 0,
|
|
437
|
+
limit: int = 50,
|
|
438
|
+
filters: ExecutionJobFilters | None = None,
|
|
439
|
+
) -> "pd.DataFrame":
|
|
440
|
+
import pandas as pd
|
|
441
|
+
|
|
442
|
+
api_kwargs = filters.format_filters() if filters is not None else {}
|
|
443
|
+
execution_user_actions = await ApiWrapper.call_query_execution_jobs(
|
|
444
|
+
offset, limit, http_client=None, **api_kwargs
|
|
445
|
+
)
|
|
446
|
+
execution_actions = execution_user_actions.results
|
|
447
|
+
|
|
448
|
+
if not execution_actions:
|
|
449
|
+
return pd.DataFrame()
|
|
450
|
+
|
|
451
|
+
data = [_flatten_cost_field(action.model_dump()) for action in execution_actions]
|
|
452
|
+
return pd.DataFrame(data)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def get_execution_actions(
|
|
456
|
+
offset: int = 0, limit: int = 50, filters: ExecutionJobFilters | None = None
|
|
457
|
+
) -> "pd.DataFrame":
|
|
458
|
+
"""Query execution jobs with optional filters.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
offset: Number of results to skip (default: 0)
|
|
462
|
+
limit: Maximum number of results to return (default: 50)
|
|
463
|
+
filters: Optional ExecutionJobFilters object containing filter parameters.
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
pandas.DataFrame containing execution job information with columns:
|
|
467
|
+
id, name, start_time, end_time, provider, backend_name, status,
|
|
468
|
+
num_shots, program_id, error, cost.
|
|
359
469
|
|
|
470
|
+
Examples:
|
|
471
|
+
# Query all jobs:
|
|
472
|
+
df = get_execution_actions(limit=10)
|
|
360
473
|
|
|
361
|
-
|
|
474
|
+
# Query with filters:
|
|
475
|
+
filters = ExecutionJobFilters(status="COMPLETED", provider="ibm")
|
|
476
|
+
df = get_execution_actions(filters=filters, limit=10)
|
|
477
|
+
"""
|
|
478
|
+
return syncify_function(get_execution_actions_async)(offset, limit, filters)
|
classiq/interface/_version.py
CHANGED
|
@@ -454,6 +454,20 @@ class CINECABackendPreferences(BackendPreferences):
|
|
|
454
454
|
)
|
|
455
455
|
|
|
456
456
|
|
|
457
|
+
class SoftbankBackendPreferences(BackendPreferences):
|
|
458
|
+
"""
|
|
459
|
+
Represents the backend preferences specific to Softbank.
|
|
460
|
+
"""
|
|
461
|
+
|
|
462
|
+
backend_service_provider: ProviderTypeVendor.SOFTBANK = pydantic.Field(
|
|
463
|
+
default=ProviderVendor.SOFTBANK
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
priority: int | None = pydantic.Field(
|
|
467
|
+
default=None, description="Priority of the job"
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
|
|
457
471
|
def is_exact_simulator(backend_preferences: BackendPreferences) -> bool:
|
|
458
472
|
return backend_preferences.backend_name in EXACT_SIMULATORS
|
|
459
473
|
|
|
@@ -486,6 +500,7 @@ BackendPreferencesTypes = Union[
|
|
|
486
500
|
IntelBackendPreferences,
|
|
487
501
|
AQTBackendPreferences,
|
|
488
502
|
CINECABackendPreferences,
|
|
503
|
+
SoftbankBackendPreferences,
|
|
489
504
|
]
|
|
490
505
|
|
|
491
506
|
__all__ = [
|
|
@@ -12,5 +12,5 @@ class AQTConfig(ProviderConfig):
|
|
|
12
12
|
workspace: The AQT workspace where the simulator/hardware is located.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
api_key: str
|
|
15
|
+
api_key: str = pydantic.Field(description="AQT API key")
|
|
16
16
|
workspace: str = pydantic.Field(description="AQT workspace")
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import pydantic
|
|
2
2
|
|
|
3
|
-
from classiq.interface.backend import pydantic_backend
|
|
4
3
|
from classiq.interface.backend.provider_config.provider_config import ProviderConfig
|
|
5
4
|
|
|
6
5
|
|
|
@@ -26,7 +25,7 @@ class AzureConfig(ProviderConfig):
|
|
|
26
25
|
client_secret: str | None = pydantic.Field(
|
|
27
26
|
default=None, description="Azure Client Secret"
|
|
28
27
|
)
|
|
29
|
-
resource_id:
|
|
28
|
+
resource_id: str | None = pydantic.Field(
|
|
30
29
|
default=None,
|
|
31
30
|
description="Azure Resource ID (including Azure subscription ID, resource "
|
|
32
31
|
"group and workspace), for personal resource",
|
|
@@ -10,7 +10,7 @@ class IBMConfig(ProviderConfig):
|
|
|
10
10
|
Attributes:
|
|
11
11
|
access_token (str | None): The IBM Cloud access token to be used with IBM Quantum hosted backends. Defaults to `None`.
|
|
12
12
|
channel (str): Channel to use for IBM cloud backends. Defaults to `"ibm_cloud"`.
|
|
13
|
-
instance_crn (str): The IBM Cloud instance CRN (Cloud Resource Name) for the IBM Quantum service.
|
|
13
|
+
instance_crn (str | None): The IBM Cloud instance CRN (Cloud Resource Name) for the IBM Quantum service.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
access_token: str | None = pydantic.Field(
|
|
@@ -27,6 +27,7 @@ class ProviderVendor(StrEnum):
|
|
|
27
27
|
INTEL = "Intel"
|
|
28
28
|
AQT = "AQT"
|
|
29
29
|
CINECA = "CINECA"
|
|
30
|
+
SOFTBANK = "Softbank"
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
class ProviderTypeVendor:
|
|
@@ -41,6 +42,7 @@ class ProviderTypeVendor:
|
|
|
41
42
|
INTEL = Literal[ProviderVendor.INTEL]
|
|
42
43
|
AQT = Literal[ProviderVendor.AQT]
|
|
43
44
|
CINECA = Literal[ProviderVendor.CINECA]
|
|
45
|
+
SOFTBANK = Literal[ProviderVendor.SOFTBANK]
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
PROVIDER_NAME_MAPPER = {
|
|
@@ -54,6 +56,7 @@ PROVIDER_NAME_MAPPER = {
|
|
|
54
56
|
ProviderVendor.INTEL: "INTEL",
|
|
55
57
|
ProviderVendor.AQT: "AQT",
|
|
56
58
|
ProviderVendor.CLASSIQ: "CLASSIQ",
|
|
59
|
+
ProviderVendor.SOFTBANK: "SOFTBANK",
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
|