classiq 0.33.0__py3-none-any.whl → 0.34.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/_internals/api_wrapper.py +9 -20
- classiq/_internals/jobs.py +9 -2
- classiq/executor.py +3 -10
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +17 -0
- classiq/interface/backend/pydantic_backend.py +8 -0
- classiq/interface/backend/quantum_backend_providers.py +13 -1
- classiq/interface/chemistry/ground_state_problem.py +1 -1
- classiq/interface/chemistry/operator.py +198 -0
- classiq/interface/executor/execution_request.py +2 -12
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/atomic_quantum_functions.py +2 -2
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +20 -110
- classiq/interface/generator/generated_circuit.py +8 -44
- classiq/interface/generator/generated_circuit_data.py +2 -11
- classiq/interface/generator/model/preferences/preferences.py +2 -2
- classiq/interface/generator/quantum_function_call.py +1 -1
- classiq/interface/hardware.py +1 -0
- classiq/interface/ide/show.py +1 -14
- classiq/interface/model/quantum_function_call.py +9 -339
- classiq/interface/model/quantum_statement.py +3 -2
- classiq/interface/server/routes.py +5 -6
- classiq/model/function_handler.pyi +88 -88
- classiq/qmod/declaration_inferrer.py +34 -17
- classiq/qmod/model_state_container.py +6 -3
- classiq/qmod/qmod_builtins.py +892 -4
- classiq/qmod/qmod_parameter.py +24 -8
- classiq/qmod/quantum_expandable.py +6 -2
- classiq/qmod/quantum_function.py +9 -9
- {classiq-0.33.0.dist-info → classiq-0.34.0.dist-info}/METADATA +1 -1
- {classiq-0.33.0.dist-info → classiq-0.34.0.dist-info}/RECORD +31 -35
- classiq/interface/model/clients/__init__.py +0 -0
- classiq/interface/model/clients/qmod/__init__.py +0 -0
- classiq/interface/model/clients/qmod/qmod_builtins.py +0 -905
- classiq/interface/model/semantics.py +0 -15
- {classiq-0.33.0.dist-info → classiq-0.34.0.dist-info}/WHEEL +0 -0
@@ -1,8 +1,7 @@
|
|
1
1
|
import json
|
2
|
-
from typing import
|
2
|
+
from typing import Dict, Optional, Protocol, Type, TypeVar
|
3
3
|
|
4
4
|
import pydantic
|
5
|
-
from pydantic import parse_obj_as
|
6
5
|
|
7
6
|
import classiq.interface.pyomo_extension # noqa: F401 - patches pyomo to add few features
|
8
7
|
from classiq.interface.analyzer import analysis_params, result as analysis_result
|
@@ -19,10 +18,7 @@ from classiq._internals.enum_utils import StrEnum
|
|
19
18
|
from classiq._internals.jobs import JobPoller
|
20
19
|
from classiq.exceptions import ClassiqAPIError, ClassiqValueError
|
21
20
|
|
22
|
-
_FAIL_FAST_INDICATOR = "{"
|
23
21
|
ResultType = TypeVar("ResultType", bound=pydantic.BaseModel)
|
24
|
-
OtherResultType = TypeVar("OtherResultType", bound=pydantic.BaseModel)
|
25
|
-
_Circuit = Union[generator_result.GeneratedCircuit, generator_result.ExecutionCircuit]
|
26
22
|
|
27
23
|
|
28
24
|
class HTTPMethod(StrEnum):
|
@@ -45,16 +41,6 @@ def _parse_job_response(
|
|
45
41
|
return output_type.parse_obj(description)
|
46
42
|
|
47
43
|
|
48
|
-
def _parse_job_response_multiple_outputs(
|
49
|
-
job_result: JobDescription[JSONObject],
|
50
|
-
output_type: Any, # UnionType in Python 3.10,
|
51
|
-
) -> Union[ResultType, OtherResultType]:
|
52
|
-
description = job_result.description
|
53
|
-
if job_result.status != JobStatus.COMPLETED:
|
54
|
-
raise ClassiqAPIError(description["details"])
|
55
|
-
return parse_obj_as(output_type, description)
|
56
|
-
|
57
|
-
|
58
44
|
class ApiWrapper:
|
59
45
|
@classmethod
|
60
46
|
async def _call_task_pydantic(
|
@@ -82,15 +68,16 @@ class ApiWrapper:
|
|
82
68
|
return res
|
83
69
|
|
84
70
|
@classmethod
|
85
|
-
async def call_generation_task(
|
71
|
+
async def call_generation_task(
|
72
|
+
cls, model: ModelInput
|
73
|
+
) -> generator_result.GeneratedCircuit:
|
86
74
|
poller = JobPoller(base_url=routes.TASKS_GENERATE_FULL_PATH)
|
87
75
|
result = await poller.run_pydantic(model, timeout_sec=None)
|
88
|
-
return
|
76
|
+
return _parse_job_response(result, generator_result.GeneratedCircuit)
|
89
77
|
|
90
78
|
@classmethod
|
91
79
|
async def call_execute_generated_circuit(
|
92
|
-
cls,
|
93
|
-
circuit: _Circuit,
|
80
|
+
cls, circuit: generator_result.GeneratedCircuit
|
94
81
|
) -> execution_request.ExecutionJobDetails:
|
95
82
|
data = await cls._call_task_pydantic(
|
96
83
|
http_method=HTTPMethod.POST,
|
@@ -231,7 +218,9 @@ class ApiWrapper:
|
|
231
218
|
async def call_generate_hamiltonian_task(
|
232
219
|
cls, problem: ground_state_problem.CHEMISTRY_PROBLEMS_TYPE
|
233
220
|
) -> operator.PauliOperator:
|
234
|
-
poller = JobPoller(
|
221
|
+
poller = JobPoller(
|
222
|
+
base_url=routes.GENERATE_HAMILTONIAN_FULL_PATH, use_versioned_url=False
|
223
|
+
)
|
235
224
|
result = await poller.run_pydantic(problem, timeout_sec=None)
|
236
225
|
return _parse_job_response(result, operator.PauliOperator)
|
237
226
|
|
classiq/_internals/jobs.py
CHANGED
@@ -52,11 +52,18 @@ class JobPoller:
|
|
52
52
|
DEV_INTERVAL = 0.05
|
53
53
|
|
54
54
|
def __init__(
|
55
|
-
self,
|
55
|
+
self,
|
56
|
+
base_url: str,
|
57
|
+
required_headers: Optional[Set[str]] = None,
|
58
|
+
use_versioned_url: bool = True,
|
56
59
|
) -> None:
|
57
60
|
self._required_headers = required_headers or set()
|
58
61
|
client_instance = client()
|
59
|
-
self._base_url =
|
62
|
+
self._base_url = (
|
63
|
+
client_instance.make_versioned_url(base_url)
|
64
|
+
if use_versioned_url
|
65
|
+
else base_url
|
66
|
+
)
|
60
67
|
self._async_client = client_instance.async_client()
|
61
68
|
self._mode = client_instance.config.mode
|
62
69
|
|
classiq/executor.py
CHANGED
@@ -3,7 +3,6 @@ import functools
|
|
3
3
|
from typing import Optional, Tuple, Union
|
4
4
|
|
5
5
|
import more_itertools
|
6
|
-
from pydantic import parse_raw_as
|
7
6
|
from typing_extensions import TypeAlias
|
8
7
|
|
9
8
|
from classiq.interface.backend.backend_preferences import BackendPreferencesTypes
|
@@ -19,10 +18,7 @@ from classiq.interface.executor.execution_request import (
|
|
19
18
|
from classiq.interface.executor.quantum_instruction_set import QuantumInstructionSet
|
20
19
|
from classiq.interface.executor.quantum_program import MultipleArguments, QuantumProgram
|
21
20
|
from classiq.interface.executor.result import ExecutionDetails
|
22
|
-
from classiq.interface.generator.generated_circuit import
|
23
|
-
ExecutionCircuit,
|
24
|
-
GeneratedCircuit,
|
25
|
-
)
|
21
|
+
from classiq.interface.generator.generated_circuit import GeneratedCircuit
|
26
22
|
|
27
23
|
from classiq._internals.api_wrapper import ApiWrapper
|
28
24
|
from classiq._internals.async_utils import syncify_function
|
@@ -40,11 +36,8 @@ _MAX_ARGUMENTS_SIZE = 1024
|
|
40
36
|
|
41
37
|
def _parse_serialized_qprog(
|
42
38
|
quantum_program: SerializedQuantumProgram,
|
43
|
-
) ->
|
44
|
-
return
|
45
|
-
Union[GeneratedCircuit, ExecutionCircuit], # type:ignore[arg-type]
|
46
|
-
quantum_program,
|
47
|
-
)
|
39
|
+
) -> GeneratedCircuit:
|
40
|
+
return GeneratedCircuit.parse_raw(quantum_program)
|
48
41
|
|
49
42
|
|
50
43
|
async def execute_async(quantum_program: SerializedQuantumProgram) -> ExecutionJob:
|
classiq/interface/_version.py
CHANGED
@@ -9,6 +9,7 @@ from pydantic import BaseModel, validator
|
|
9
9
|
from classiq.interface.backend import pydantic_backend
|
10
10
|
from classiq.interface.backend.quantum_backend_providers import (
|
11
11
|
EXACT_SIMULATORS,
|
12
|
+
AliceBobBackendNames,
|
12
13
|
AmazonBraketBackendNames,
|
13
14
|
AzureQuantumBackendNames,
|
14
15
|
ClassiqAerBackendNames,
|
@@ -55,6 +56,19 @@ class BackendPreferences(BaseModel):
|
|
55
56
|
AWS_DEFAULT_JOB_TIMEOUT_SECONDS = int(timedelta(minutes=5).total_seconds())
|
56
57
|
|
57
58
|
|
59
|
+
class AliceBobBackendPreferences(BackendPreferences):
|
60
|
+
backend_service_provider: ProviderTypeVendor.ALICE_BOB
|
61
|
+
api_key: pydantic_backend.PydanticAliceBobApiKeyType = pydantic.Field(
|
62
|
+
..., description="AliceBob API key"
|
63
|
+
)
|
64
|
+
|
65
|
+
@pydantic.root_validator(pre=True)
|
66
|
+
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
67
|
+
return values_with_discriminator(
|
68
|
+
values, "backend_service_provider", ProviderVendor.ALICE_AND_BOB
|
69
|
+
)
|
70
|
+
|
71
|
+
|
58
72
|
class ClassiqBackendPreferences(BackendPreferences):
|
59
73
|
backend_service_provider: ProviderTypeVendor.CLASSIQ
|
60
74
|
|
@@ -214,6 +228,7 @@ BackendPreferencesTypes = Union[
|
|
214
228
|
AwsBackendPreferences,
|
215
229
|
IonqBackendPreferences,
|
216
230
|
GCPBackendPreferences,
|
231
|
+
AliceBobBackendPreferences,
|
217
232
|
]
|
218
233
|
|
219
234
|
__all__ = [
|
@@ -230,6 +245,8 @@ __all__ = [
|
|
230
245
|
"IonqBackendNames",
|
231
246
|
"ClassiqNvidiaBackendNames",
|
232
247
|
"GCPBackendPreferences",
|
248
|
+
"AliceBobBackendPreferences",
|
249
|
+
"AliceBobBackendNames",
|
233
250
|
]
|
234
251
|
|
235
252
|
|
@@ -6,7 +6,9 @@ import pydantic
|
|
6
6
|
AZURE_QUANTUM_RESOURCE_ID_REGEX = r"^/subscriptions/([a-fA-F0-9-]*)/resourceGroups/([^\s/]*)/providers/Microsoft\.Quantum/Workspaces/([^\s/]*)$"
|
7
7
|
|
8
8
|
_IONQ_API_KEY_LENGTH: int = 32
|
9
|
+
_ALICE_BOB_API_KEY_LENGTH: int = 72
|
9
10
|
INVALID_API_KEY: str = _IONQ_API_KEY_LENGTH * "a"
|
11
|
+
INVALID_API_KEY_ALICE_BOB: str = _ALICE_BOB_API_KEY_LENGTH * "a"
|
10
12
|
MAX_EXECUTION_TIMEOUT_SECONDS = timedelta(hours=4).total_seconds()
|
11
13
|
|
12
14
|
if TYPE_CHECKING:
|
@@ -18,6 +20,7 @@ if TYPE_CHECKING:
|
|
18
20
|
PydanticIonQApiKeyType = str
|
19
21
|
PydanticArgumentNameType = str
|
20
22
|
PydanticExecutionParameter = str
|
23
|
+
PydanticAliceBobApiKeyType = str
|
21
24
|
else:
|
22
25
|
# TODO Simplify regular expressions in this file
|
23
26
|
|
@@ -32,6 +35,11 @@ else:
|
|
32
35
|
PydanticIonQApiKeyType = pydantic.constr(
|
33
36
|
regex=f"[A-Za-z0-9]{{{_IONQ_API_KEY_LENGTH}}}"
|
34
37
|
)
|
38
|
+
|
39
|
+
PydanticAliceBobApiKeyType = pydantic.constr(
|
40
|
+
regex=f"[A-Za-z0-9]{{{_ALICE_BOB_API_KEY_LENGTH}}}"
|
41
|
+
)
|
42
|
+
|
35
43
|
PydanticExecutionTimeout = pydantic.conint(gt=0, le=MAX_EXECUTION_TIMEOUT_SECONDS)
|
36
44
|
|
37
45
|
PydanticArgumentNameType = pydantic.constr(regex="[_a-zA-Z][_a-zA-Z0-9]*")
|
@@ -16,6 +16,7 @@ class ProviderVendor(StrEnum):
|
|
16
16
|
AMAZON_BRAKET = "Amazon Braket"
|
17
17
|
IONQ = "IonQ"
|
18
18
|
GOOGLE = "Google"
|
19
|
+
ALICE_AND_BOB = "Alice and Bob"
|
19
20
|
|
20
21
|
|
21
22
|
class ProviderTypeVendor:
|
@@ -25,6 +26,7 @@ class ProviderTypeVendor:
|
|
25
26
|
AMAZON_BRAKET = Literal[ProviderVendor.AMAZON_BRAKET]
|
26
27
|
IONQ = Literal[ProviderVendor.IONQ]
|
27
28
|
GOOGLE = Literal[ProviderVendor.GOOGLE]
|
29
|
+
ALICE_BOB = Literal[ProviderVendor.ALICE_AND_BOB]
|
28
30
|
|
29
31
|
|
30
32
|
class ClassiqAerBackendNames(StrEnum):
|
@@ -143,6 +145,17 @@ class GoogleNvidiaBackendNames(StrEnum):
|
|
143
145
|
CUQUANTUM = "cuquantum"
|
144
146
|
|
145
147
|
|
148
|
+
class AliceBobBackendNames(StrEnum):
|
149
|
+
PHYSICAL_CATS_40 = "EMU:40Q:PHYSICAL_CATS"
|
150
|
+
PERFECT_QUBITS = "EMU:20Q:PERFECT_QUBITS"
|
151
|
+
LOGICAL_TARGET = "EMU:40Q:LOGICAL_TARGET"
|
152
|
+
PHYSICAL_CATS_6 = "EMU:6Q:PHYSICAL_CATS"
|
153
|
+
LOGICAL_EARLY = "EMU:15Q:LOGICAL_EARLY"
|
154
|
+
LESCANNE = "EMU:1Q:LESCANNE_2020"
|
155
|
+
TRANSMONS = "EMU:7Q:TRANSMONS"
|
156
|
+
FATCAT = "QPU:1Q:FATCAT_0"
|
157
|
+
|
158
|
+
|
146
159
|
EXACT_SIMULATORS = {
|
147
160
|
IonqBackendNames.SIMULATOR,
|
148
161
|
AzureQuantumBackendNames.IONQ_SIMULATOR,
|
@@ -156,7 +169,6 @@ EXACT_SIMULATORS = {
|
|
156
169
|
|
157
170
|
AllIBMQBackendNames = IBMQHardwareNames
|
158
171
|
|
159
|
-
|
160
172
|
AllBackendsNameByVendor = Union[
|
161
173
|
AllIBMQBackendNames,
|
162
174
|
AzureQuantumBackendNames,
|
@@ -64,7 +64,7 @@ class GroundStateProblem(HashablePydanticBaseModel):
|
|
64
64
|
|
65
65
|
|
66
66
|
class MoleculeProblem(GroundStateProblem):
|
67
|
-
molecule: Molecule
|
67
|
+
molecule: Molecule
|
68
68
|
basis: str = pydantic.Field(default="sto3g", description="Molecular basis set")
|
69
69
|
freeze_core: bool = pydantic.Field(default=False)
|
70
70
|
remove_orbitals: List[int] = pydantic.Field(
|
@@ -224,6 +224,204 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
|
|
224
224
|
frozen = True
|
225
225
|
|
226
226
|
|
227
|
+
class PauliOperatorV1(HashablePydanticBaseModel):
|
228
|
+
"""
|
229
|
+
Specification of a Pauli sum operator.
|
230
|
+
"""
|
231
|
+
|
232
|
+
pauli_list: PydanticPauliList = pydantic.Field(
|
233
|
+
description="A list of tuples each containing a pauli string comprised of I,X,Y,Z characters and a complex coefficient; for example [('IZ', 0.1), ('XY', 0.2)].",
|
234
|
+
)
|
235
|
+
is_hermitian: bool = pydantic.Field(default=False)
|
236
|
+
has_complex_coefficients: bool = pydantic.Field(default=True)
|
237
|
+
|
238
|
+
def show(self) -> str:
|
239
|
+
if self.is_hermitian:
|
240
|
+
# If the operator is hermitian then the coefficients must be numeric
|
241
|
+
return "\n".join(
|
242
|
+
f"{summand[1].real:+.3f} * {summand[0]}" for summand in self.pauli_list # type: ignore[union-attr]
|
243
|
+
)
|
244
|
+
return "\n".join(
|
245
|
+
f"+({summand[1]:+.3f}) * {summand[0]}" for summand in self.pauli_list
|
246
|
+
)
|
247
|
+
|
248
|
+
@pydantic.validator("pauli_list", each_item=True, pre=True)
|
249
|
+
def _validate_pauli_monomials(
|
250
|
+
cls, monomial: Tuple[PydanticPauliMonomialStr, ParameterComplexType]
|
251
|
+
) -> Tuple[PydanticPauliMonomialStr, ParameterComplexType]:
|
252
|
+
_PauliMonomialLengthValidator( # type: ignore[call-arg]
|
253
|
+
monomial=monomial
|
254
|
+
) # Validate the length of the monomial.
|
255
|
+
coeff = cls._validate_monomial_coefficient(monomial[1])
|
256
|
+
parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff) # type: ignore[call-arg]
|
257
|
+
return (parsed_monomial.string, parsed_monomial.coeff)
|
258
|
+
|
259
|
+
@staticmethod
|
260
|
+
def _validate_monomial_coefficient(
|
261
|
+
coeff: Union[sympy.Expr, ParameterComplexType]
|
262
|
+
) -> ParameterComplexType:
|
263
|
+
if isinstance(coeff, str):
|
264
|
+
validate_expression_str(coeff)
|
265
|
+
elif isinstance(coeff, sympy.Expr):
|
266
|
+
coeff = str(coeff)
|
267
|
+
return coeff
|
268
|
+
|
269
|
+
@pydantic.validator("pauli_list")
|
270
|
+
def _validate_pauli_list(cls, pauli_list: PydanticPauliList) -> PydanticPauliList:
|
271
|
+
if not all_equal(len(summand[0]) for summand in pauli_list):
|
272
|
+
raise ValueError("Pauli strings have incompatible lengths.")
|
273
|
+
return pauli_list
|
274
|
+
|
275
|
+
@pydantic.root_validator
|
276
|
+
def _validate_hermitianity(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
277
|
+
pauli_list = values.get("pauli_list", [])
|
278
|
+
if all(isinstance(summand[1], complex) for summand in pauli_list):
|
279
|
+
values["is_hermitian"] = all(
|
280
|
+
np.isclose(complex(summand[1]).real, summand[1])
|
281
|
+
for summand in pauli_list
|
282
|
+
)
|
283
|
+
if values.get("is_hermitian", False):
|
284
|
+
values["has_complex_coefficients"] = False
|
285
|
+
values["pauli_list"] = [
|
286
|
+
(summand[0], complex(summand[1].real)) for summand in pauli_list
|
287
|
+
]
|
288
|
+
else:
|
289
|
+
values["has_complex_coefficients"] = not all(
|
290
|
+
np.isclose(complex(summand[1]).real, summand[1])
|
291
|
+
for summand in pauli_list
|
292
|
+
if isinstance(summand[1], complex)
|
293
|
+
)
|
294
|
+
return values
|
295
|
+
|
296
|
+
def __mul__(self, coefficient: complex) -> "PauliOperatorV1":
|
297
|
+
multiplied_ising = [
|
298
|
+
(monomial[0], self._multiply_monomial_coefficient(monomial[1], coefficient))
|
299
|
+
for monomial in self.pauli_list
|
300
|
+
]
|
301
|
+
return self.__class__(pauli_list=multiplied_ising)
|
302
|
+
|
303
|
+
@staticmethod
|
304
|
+
def _multiply_monomial_coefficient(
|
305
|
+
monomial_coefficient: ParameterComplexType, coefficient: complex
|
306
|
+
) -> ParameterComplexType:
|
307
|
+
if isinstance(monomial_coefficient, ParameterType):
|
308
|
+
return str(sympy.sympify(monomial_coefficient) * coefficient)
|
309
|
+
return monomial_coefficient * coefficient
|
310
|
+
|
311
|
+
@property
|
312
|
+
def is_commutative(self) -> bool:
|
313
|
+
return all(
|
314
|
+
self._is_sub_pauli_commutative(
|
315
|
+
[summand[0][qubit_num] for summand in self.pauli_list]
|
316
|
+
)
|
317
|
+
for qubit_num in range(self.num_qubits)
|
318
|
+
)
|
319
|
+
|
320
|
+
@staticmethod
|
321
|
+
def _is_sub_pauli_commutative(qubit_pauli_string: Union[List[str], str]) -> bool:
|
322
|
+
unique_paulis = set(qubit_pauli_string) - {"I"}
|
323
|
+
return len(unique_paulis) <= 1
|
324
|
+
|
325
|
+
@property
|
326
|
+
def num_qubits(self) -> int:
|
327
|
+
return len(self.pauli_list[0][0])
|
328
|
+
|
329
|
+
def to_matrix(self) -> np.ndarray:
|
330
|
+
if not all(isinstance(summand[1], complex) for summand in self.pauli_list):
|
331
|
+
raise ClassiqValueError(
|
332
|
+
"Supporting only Hamiltonian with numeric coefficients."
|
333
|
+
)
|
334
|
+
return sum(
|
335
|
+
cast(complex, summand[1]) * to_pauli_matrix(summand[0])
|
336
|
+
for summand in self.pauli_list
|
337
|
+
) # type: ignore[return-value]
|
338
|
+
|
339
|
+
@staticmethod
|
340
|
+
def _extend_pauli_string(
|
341
|
+
pauli_string: PydanticPauliMonomialStr, num_extra_qubits: int
|
342
|
+
) -> PydanticPauliMonomialStr:
|
343
|
+
return "I" * num_extra_qubits + pauli_string
|
344
|
+
|
345
|
+
def extend(self, num_extra_qubits: int) -> "PauliOperatorV1":
|
346
|
+
new_pauli_list = [
|
347
|
+
(self._extend_pauli_string(pauli_string, num_extra_qubits), coeff)
|
348
|
+
for (pauli_string, coeff) in self.pauli_list
|
349
|
+
]
|
350
|
+
return self.copy(update={"pauli_list": new_pauli_list}, deep=True)
|
351
|
+
|
352
|
+
@staticmethod
|
353
|
+
def _reorder_pauli_string(
|
354
|
+
pauli_string: PydanticPauliMonomialStr,
|
355
|
+
order: Collection[int],
|
356
|
+
new_num_qubits: int,
|
357
|
+
) -> PydanticPauliMonomialStr:
|
358
|
+
reversed_pauli_string = pauli_string[::-1]
|
359
|
+
reversed_new_pauli_string = ["I"] * new_num_qubits
|
360
|
+
|
361
|
+
for logical_pos, actual_pos in enumerate(order):
|
362
|
+
reversed_new_pauli_string[actual_pos] = reversed_pauli_string[logical_pos]
|
363
|
+
|
364
|
+
return "".join(reversed(reversed_new_pauli_string))
|
365
|
+
|
366
|
+
@staticmethod
|
367
|
+
def _validate_reorder(
|
368
|
+
order: Collection[int],
|
369
|
+
num_qubits: int,
|
370
|
+
num_extra_qubits: int,
|
371
|
+
) -> None:
|
372
|
+
if num_extra_qubits < 0:
|
373
|
+
raise ValueError("Number of extra qubits cannot be negative")
|
374
|
+
|
375
|
+
if len(order) != num_qubits:
|
376
|
+
raise ValueError("The qubits order doesn't match the Pauli operator")
|
377
|
+
|
378
|
+
if len(order) != len(set(order)):
|
379
|
+
raise ValueError("The qubits order is not one-to-one")
|
380
|
+
|
381
|
+
if not all(pos < num_qubits + num_extra_qubits for pos in order):
|
382
|
+
raise ValueError("The qubits order contains qubits which do no exist")
|
383
|
+
|
384
|
+
@classmethod
|
385
|
+
def reorder(
|
386
|
+
cls,
|
387
|
+
operator: "PauliOperatorV1",
|
388
|
+
order: Collection[int],
|
389
|
+
num_extra_qubits: int = 0,
|
390
|
+
) -> "PauliOperatorV1":
|
391
|
+
cls._validate_reorder(order, operator.num_qubits, num_extra_qubits)
|
392
|
+
|
393
|
+
new_num_qubits = operator.num_qubits + num_extra_qubits
|
394
|
+
new_pauli_list = [
|
395
|
+
(cls._reorder_pauli_string(pauli_string, order, new_num_qubits), coeff)
|
396
|
+
for pauli_string, coeff in operator.pauli_list
|
397
|
+
]
|
398
|
+
return cls(pauli_list=new_pauli_list)
|
399
|
+
|
400
|
+
@classmethod
|
401
|
+
def from_unzipped_lists(
|
402
|
+
cls,
|
403
|
+
operators: List[List[Pauli]],
|
404
|
+
coefficients: Optional[List[complex]] = None,
|
405
|
+
) -> "PauliOperatorV1":
|
406
|
+
if coefficients is None:
|
407
|
+
coefficients = [1] * len(operators)
|
408
|
+
|
409
|
+
if len(operators) != len(coefficients):
|
410
|
+
raise ValueError(
|
411
|
+
f"The number of coefficients ({len(coefficients)}) must be equal to the number of pauli operators ({len(operators)})"
|
412
|
+
)
|
413
|
+
|
414
|
+
return cls(
|
415
|
+
pauli_list=[
|
416
|
+
(pauli_integers_to_str(op), coeff)
|
417
|
+
for op, coeff in zip(operators, coefficients)
|
418
|
+
]
|
419
|
+
)
|
420
|
+
|
421
|
+
class Config:
|
422
|
+
frozen = True
|
423
|
+
|
424
|
+
|
227
425
|
# This class validates the length of a monomial.
|
228
426
|
@pydantic.dataclasses.dataclass
|
229
427
|
class _PauliMonomialLengthValidator:
|
@@ -15,10 +15,7 @@ from classiq.interface.executor.quantum_program import (
|
|
15
15
|
)
|
16
16
|
from classiq.interface.executor.result import EstimationResult, ExecutionDetails
|
17
17
|
from classiq.interface.executor.vqe_result import VQESolverResult
|
18
|
-
from classiq.interface.generator.generated_circuit import
|
19
|
-
ExecutionCircuit,
|
20
|
-
GeneratedCircuit,
|
21
|
-
)
|
18
|
+
from classiq.interface.generator.generated_circuit import GeneratedCircuit
|
22
19
|
from classiq.interface.helpers.versioned_model import VersionedModel
|
23
20
|
from classiq.interface.jobs import JobStatus
|
24
21
|
|
@@ -29,10 +26,6 @@ class GeneratedCircuitExecution(GeneratedCircuit):
|
|
29
26
|
execution_type: Literal["generated_circuit"] = "generated_circuit"
|
30
27
|
|
31
28
|
|
32
|
-
class BareGeneratedCircuitExecution(ExecutionCircuit):
|
33
|
-
execution_type: Literal["bare_generated_circuit"] = "bare_generated_circuit"
|
34
|
-
|
35
|
-
|
36
29
|
class QuantumProgramExecution(QuantumProgram):
|
37
30
|
execution_type: Literal["quantum_program"] = "quantum_program"
|
38
31
|
|
@@ -42,10 +35,7 @@ class EstimateOperatorsExecution(OperatorsEstimation):
|
|
42
35
|
|
43
36
|
|
44
37
|
ExecutionPayloads = Union[
|
45
|
-
GeneratedCircuitExecution,
|
46
|
-
BareGeneratedCircuitExecution,
|
47
|
-
QuantumProgramExecution,
|
48
|
-
EstimateOperatorsExecution,
|
38
|
+
GeneratedCircuitExecution, QuantumProgramExecution, EstimateOperatorsExecution
|
49
39
|
]
|
50
40
|
|
51
41
|
|
@@ -441,11 +441,11 @@ ADD_FUNCTION = QuantumFunctionDeclaration(
|
|
441
441
|
port_declarations={
|
442
442
|
"left": PortDeclaration(
|
443
443
|
name="left",
|
444
|
-
direction=PortDeclarationDirection.
|
444
|
+
direction=PortDeclarationDirection.Inout,
|
445
445
|
),
|
446
446
|
"right": PortDeclaration(
|
447
447
|
name="right",
|
448
|
-
direction=PortDeclarationDirection.
|
448
|
+
direction=PortDeclarationDirection.Inout,
|
449
449
|
),
|
450
450
|
"result": PortDeclaration(
|
451
451
|
name="result",
|