qilisdk 0.1.6__py3-none-any.whl → 0.1.7__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.
- qilisdk/analog/__init__.py +1 -2
- qilisdk/analog/hamiltonian.py +1 -68
- qilisdk/analog/schedule.py +288 -313
- qilisdk/backends/backend.py +5 -1
- qilisdk/backends/cuda_backend.py +9 -5
- qilisdk/backends/qutip_backend.py +23 -12
- qilisdk/core/__init__.py +4 -0
- qilisdk/core/interpolator.py +406 -0
- qilisdk/core/parameterizable.py +66 -10
- qilisdk/core/variables.py +150 -7
- qilisdk/digital/circuit.py +1 -0
- qilisdk/digital/circuit_transpiler.py +46 -0
- qilisdk/digital/circuit_transpiler_passes/__init__.py +18 -0
- qilisdk/digital/circuit_transpiler_passes/circuit_transpiler_pass.py +36 -0
- qilisdk/digital/circuit_transpiler_passes/decompose_multi_controlled_gates_pass.py +216 -0
- qilisdk/digital/circuit_transpiler_passes/numeric_helpers.py +82 -0
- qilisdk/digital/gates.py +12 -2
- qilisdk/{speqtrum/experiments → experiments}/__init__.py +13 -2
- qilisdk/{speqtrum/experiments → experiments}/experiment_functional.py +90 -2
- qilisdk/{speqtrum/experiments → experiments}/experiment_result.py +16 -0
- qilisdk/functionals/sampling.py +8 -1
- qilisdk/functionals/time_evolution.py +6 -2
- qilisdk/functionals/variational_program.py +58 -0
- qilisdk/speqtrum/speqtrum.py +360 -130
- qilisdk/speqtrum/speqtrum_models.py +108 -19
- qilisdk/utils/openfermion/__init__.py +38 -0
- qilisdk/{core/algorithm.py → utils/openfermion/__init__.pyi} +2 -3
- qilisdk/utils/openfermion/openfermion.py +45 -0
- qilisdk/utils/visualization/schedule_renderers.py +16 -8
- {qilisdk-0.1.6.dist-info → qilisdk-0.1.7.dist-info}/METADATA +74 -24
- {qilisdk-0.1.6.dist-info → qilisdk-0.1.7.dist-info}/RECORD +33 -26
- {qilisdk-0.1.6.dist-info → qilisdk-0.1.7.dist-info}/WHEEL +1 -1
- qilisdk/analog/linear_schedule.py +0 -121
- {qilisdk-0.1.6.dist-info → qilisdk-0.1.7.dist-info}/licenses/LICENCE +0 -0
|
@@ -18,6 +18,16 @@ from typing import Any, Callable, Generic, TypeVar, cast, overload
|
|
|
18
18
|
|
|
19
19
|
from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, field_serializer, field_validator
|
|
20
20
|
|
|
21
|
+
from qilisdk.experiments import (
|
|
22
|
+
RabiExperiment,
|
|
23
|
+
RabiExperimentResult,
|
|
24
|
+
T1Experiment,
|
|
25
|
+
T1ExperimentResult,
|
|
26
|
+
T2Experiment,
|
|
27
|
+
T2ExperimentResult,
|
|
28
|
+
TwoTonesExperiment,
|
|
29
|
+
TwoTonesExperimentResult,
|
|
30
|
+
)
|
|
21
31
|
from qilisdk.functionals import (
|
|
22
32
|
Sampling,
|
|
23
33
|
SamplingResult,
|
|
@@ -27,7 +37,6 @@ from qilisdk.functionals import (
|
|
|
27
37
|
VariationalProgramResult,
|
|
28
38
|
)
|
|
29
39
|
from qilisdk.functionals.functional_result import FunctionalResult
|
|
30
|
-
from qilisdk.speqtrum.experiments import RabiExperiment, RabiExperimentResult, T1Experiment, T1ExperimentResult
|
|
31
40
|
from qilisdk.utils.serialization import deserialize, serialize
|
|
32
41
|
|
|
33
42
|
|
|
@@ -52,7 +61,7 @@ class Token(SpeQtrumModel):
|
|
|
52
61
|
|
|
53
62
|
access_token: str = Field(alias="accessToken")
|
|
54
63
|
expires_in: int = Field(alias="expiresIn")
|
|
55
|
-
issued_at:
|
|
64
|
+
issued_at: int = Field(alias="issuedAt")
|
|
56
65
|
refresh_token: str = Field(alias="refreshToken")
|
|
57
66
|
token_type: str = Field(alias="tokenType")
|
|
58
67
|
|
|
@@ -88,6 +97,8 @@ class ExecuteType(str, Enum):
|
|
|
88
97
|
VARIATIONAL_PROGRAM = "variational_program"
|
|
89
98
|
RABI_EXPERIMENT = "rabi_experiment"
|
|
90
99
|
T1_EXPERIMENT = "t1_experiment"
|
|
100
|
+
T2_EXPERIMENT = "t2_experiment"
|
|
101
|
+
TWO_TONES_EXPERIMENT = "two_tones_experiment"
|
|
91
102
|
|
|
92
103
|
|
|
93
104
|
class SamplingPayload(SpeQtrumModel):
|
|
@@ -160,6 +171,34 @@ class T1ExperimentPayload(SpeQtrumModel):
|
|
|
160
171
|
return v
|
|
161
172
|
|
|
162
173
|
|
|
174
|
+
class T2ExperimentPayload(SpeQtrumModel):
|
|
175
|
+
t2_experiment: T2Experiment = Field(...)
|
|
176
|
+
|
|
177
|
+
@field_serializer("t2_experiment")
|
|
178
|
+
def _serialize_t2_experiment(self, t2_experiment: T2Experiment, _info):
|
|
179
|
+
return serialize(t2_experiment)
|
|
180
|
+
|
|
181
|
+
@field_validator("t2_experiment", mode="before")
|
|
182
|
+
def _load_t2_experiment(cls, v):
|
|
183
|
+
if isinstance(v, str):
|
|
184
|
+
return deserialize(v, T2Experiment)
|
|
185
|
+
return v
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class TwoTonesExperimentPayload(SpeQtrumModel):
|
|
189
|
+
two_tones_experiment: TwoTonesExperiment = Field(...)
|
|
190
|
+
|
|
191
|
+
@field_serializer("two_tones_experiment")
|
|
192
|
+
def _serialize_two_tones_experiment(self, two_tones_experiment: TwoTonesExperiment, _info):
|
|
193
|
+
return serialize(two_tones_experiment)
|
|
194
|
+
|
|
195
|
+
@field_validator("two_tones_experiment", mode="before")
|
|
196
|
+
def _load_two_tones_experiment(cls, v):
|
|
197
|
+
if isinstance(v, str):
|
|
198
|
+
return deserialize(v, TwoTonesExperiment)
|
|
199
|
+
return v
|
|
200
|
+
|
|
201
|
+
|
|
163
202
|
class ExecutePayload(SpeQtrumModel):
|
|
164
203
|
type: ExecuteType = Field(...)
|
|
165
204
|
sampling_payload: SamplingPayload | None = None
|
|
@@ -167,6 +206,8 @@ class ExecutePayload(SpeQtrumModel):
|
|
|
167
206
|
variational_program_payload: VariationalProgramPayload | None = None
|
|
168
207
|
rabi_experiment_payload: RabiExperimentPayload | None = None
|
|
169
208
|
t1_experiment_payload: T1ExperimentPayload | None = None
|
|
209
|
+
t2_experiment_payload: T2ExperimentPayload | None = None
|
|
210
|
+
two_tones_experiment_payload: TwoTonesExperimentPayload | None = None
|
|
170
211
|
|
|
171
212
|
|
|
172
213
|
class ExecuteResult(SpeQtrumModel):
|
|
@@ -176,6 +217,8 @@ class ExecuteResult(SpeQtrumModel):
|
|
|
176
217
|
variational_program_result: VariationalProgramResult | None = None
|
|
177
218
|
rabi_experiment_result: RabiExperimentResult | None = None
|
|
178
219
|
t1_experiment_result: T1ExperimentResult | None = None
|
|
220
|
+
t2_experiment_result: T2ExperimentResult | None = None
|
|
221
|
+
two_tones_experiment_result: TwoTonesExperimentResult | None = None
|
|
179
222
|
|
|
180
223
|
@field_serializer("sampling_result")
|
|
181
224
|
def _serialize_sampling_result(self, sampling_result: SamplingResult, _info):
|
|
@@ -227,12 +270,32 @@ class ExecuteResult(SpeQtrumModel):
|
|
|
227
270
|
return deserialize(v, T1ExperimentResult)
|
|
228
271
|
return v
|
|
229
272
|
|
|
273
|
+
@field_serializer("t2_experiment_result")
|
|
274
|
+
def _serialize_t2_experiment_resultt(self, t2_experiment_result: T2ExperimentResult, _info):
|
|
275
|
+
return serialize(t2_experiment_result) if t2_experiment_result is not None else None
|
|
230
276
|
|
|
231
|
-
|
|
232
|
-
|
|
277
|
+
@field_validator("t2_experiment_result", mode="before")
|
|
278
|
+
def _load_t2_experiment_result(cls, v):
|
|
279
|
+
if isinstance(v, str) and v.startswith("!"):
|
|
280
|
+
return deserialize(v, T2ExperimentResult)
|
|
281
|
+
return v
|
|
233
282
|
|
|
283
|
+
@field_serializer("two_tones_experiment_result")
|
|
284
|
+
def _serialize_two_tones_experiment_result(self, two_tones_experiment_result: TwoTonesExperimentResult, _info):
|
|
285
|
+
return serialize(two_tones_experiment_result) if two_tones_experiment_result is not None else None
|
|
286
|
+
|
|
287
|
+
@field_validator("two_tones_experiment_result", mode="before")
|
|
288
|
+
def _load_two_tones_experiment_result(cls, v):
|
|
289
|
+
if isinstance(v, str) and v.startswith("!"):
|
|
290
|
+
return deserialize(v, TwoTonesExperimentResult)
|
|
291
|
+
return v
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
TFunctionalResult_co = TypeVar("TFunctionalResult_co", bound=FunctionalResult, covariant=True)
|
|
295
|
+
TVariationalInnerResult = TypeVar("TVariationalInnerResult", bound=FunctionalResult)
|
|
234
296
|
|
|
235
|
-
|
|
297
|
+
|
|
298
|
+
ResultExtractor = Callable[[ExecuteResult], TFunctionalResult_co]
|
|
236
299
|
|
|
237
300
|
|
|
238
301
|
# these helpers live outside the models so they can be referenced by default values
|
|
@@ -266,10 +329,24 @@ def _require_t1_experiment_result(result: ExecuteResult) -> T1ExperimentResult:
|
|
|
266
329
|
return result.t1_experiment_result
|
|
267
330
|
|
|
268
331
|
|
|
332
|
+
def _require_t2_experiment_result(result: ExecuteResult) -> T2ExperimentResult:
|
|
333
|
+
if result.t2_experiment_result is None:
|
|
334
|
+
raise RuntimeError("SpeQtrum did not return a t2_experiment_result for a T2 experiment execution.")
|
|
335
|
+
return result.t2_experiment_result
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _require_two_tones_experiment_result(result: ExecuteResult) -> TwoTonesExperimentResult:
|
|
339
|
+
if result.two_tones_experiment_result is None:
|
|
340
|
+
raise RuntimeError(
|
|
341
|
+
"SpeQtrum did not return a two_tones_experiment_result for a Two-Tones experiment execution."
|
|
342
|
+
)
|
|
343
|
+
return result.two_tones_experiment_result
|
|
344
|
+
|
|
345
|
+
|
|
269
346
|
def _require_variational_program_result_typed(
|
|
270
|
-
inner_result_type: type[
|
|
271
|
-
) -> ResultExtractor[VariationalProgramResult[
|
|
272
|
-
def _extractor(result: ExecuteResult) -> VariationalProgramResult[
|
|
347
|
+
inner_result_type: type[TVariationalInnerResult],
|
|
348
|
+
) -> ResultExtractor[VariationalProgramResult[TVariationalInnerResult]]:
|
|
349
|
+
def _extractor(result: ExecuteResult) -> VariationalProgramResult[TVariationalInnerResult]:
|
|
273
350
|
variational_result = _require_variational_program_result(result)
|
|
274
351
|
optimal_results = variational_result.optimal_execution_results
|
|
275
352
|
if not isinstance(optimal_results, inner_result_type):
|
|
@@ -278,17 +355,17 @@ def _require_variational_program_result_typed(
|
|
|
278
355
|
f"({type(optimal_results).__qualname__}) does not match the expected "
|
|
279
356
|
f"{inner_result_type.__qualname__}."
|
|
280
357
|
)
|
|
281
|
-
return cast("VariationalProgramResult[
|
|
358
|
+
return cast("VariationalProgramResult[TVariationalInnerResult]", variational_result)
|
|
282
359
|
|
|
283
360
|
return _extractor
|
|
284
361
|
|
|
285
362
|
|
|
286
|
-
class JobHandle(SpeQtrumModel, Generic[
|
|
363
|
+
class JobHandle(SpeQtrumModel, Generic[TFunctionalResult_co]):
|
|
287
364
|
"""Strongly typed reference to a submitted SpeQtrum job."""
|
|
288
365
|
|
|
289
366
|
id: int
|
|
290
367
|
execute_type: ExecuteType
|
|
291
|
-
extractor: ResultExtractor[
|
|
368
|
+
extractor: ResultExtractor[TFunctionalResult_co] = Field(repr=False, exclude=True)
|
|
292
369
|
|
|
293
370
|
@classmethod
|
|
294
371
|
def sampling(cls, job_id: int) -> "JobHandle[SamplingResult]":
|
|
@@ -305,12 +382,12 @@ class JobHandle(SpeQtrumModel, Generic[ResultT_co]):
|
|
|
305
382
|
@overload
|
|
306
383
|
@classmethod
|
|
307
384
|
def variational_program(
|
|
308
|
-
cls, job_id: int, *, result_type: type[
|
|
309
|
-
) -> "JobHandle[VariationalProgramResult[
|
|
385
|
+
cls, job_id: int, *, result_type: type[TVariationalInnerResult]
|
|
386
|
+
) -> "JobHandle[VariationalProgramResult[TVariationalInnerResult]]": ...
|
|
310
387
|
|
|
311
388
|
@classmethod
|
|
312
389
|
def variational_program(
|
|
313
|
-
cls, job_id: int, *, result_type: type[
|
|
390
|
+
cls, job_id: int, *, result_type: type[TVariationalInnerResult] | None = None
|
|
314
391
|
) -> "JobHandle[Any]":
|
|
315
392
|
"""Create a variational-program handle for an existing job identifier.
|
|
316
393
|
|
|
@@ -335,7 +412,7 @@ class JobHandle(SpeQtrumModel, Generic[ResultT_co]):
|
|
|
335
412
|
|
|
336
413
|
extractor = _require_variational_program_result_typed(result_type)
|
|
337
414
|
handle = cls(id=job_id, execute_type=ExecuteType.VARIATIONAL_PROGRAM, extractor=extractor) # type: ignore[arg-type]
|
|
338
|
-
return cast("JobHandle[VariationalProgramResult[
|
|
415
|
+
return cast("JobHandle[VariationalProgramResult[TVariationalInnerResult]]", handle)
|
|
339
416
|
|
|
340
417
|
@classmethod
|
|
341
418
|
def rabi_experiment(cls, job_id: int) -> "JobHandle[RabiExperimentResult]":
|
|
@@ -345,7 +422,19 @@ class JobHandle(SpeQtrumModel, Generic[ResultT_co]):
|
|
|
345
422
|
def t1_experiment(cls, job_id: int) -> "JobHandle[T1ExperimentResult]":
|
|
346
423
|
return cls(id=job_id, execute_type=ExecuteType.T1_EXPERIMENT, extractor=_require_t1_experiment_result) # type: ignore[return-value, arg-type]
|
|
347
424
|
|
|
348
|
-
|
|
425
|
+
@classmethod
|
|
426
|
+
def t2_experiment(cls, job_id: int) -> "JobHandle[T2ExperimentResult]":
|
|
427
|
+
return cls(id=job_id, execute_type=ExecuteType.T2_EXPERIMENT, extractor=_require_t2_experiment_result) # type: ignore[return-value, arg-type]
|
|
428
|
+
|
|
429
|
+
@classmethod
|
|
430
|
+
def two_tones_experiment(cls, job_id: int) -> "JobHandle[TwoTonesExperimentResult]":
|
|
431
|
+
return cls(
|
|
432
|
+
id=job_id,
|
|
433
|
+
execute_type=ExecuteType.TWO_TONES_EXPERIMENT,
|
|
434
|
+
extractor=_require_two_tones_experiment_result, # type: ignore[return-value, arg-type]
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
def bind(self, detail: "JobDetail") -> "TypedJobDetail[TFunctionalResult_co]":
|
|
349
438
|
"""Attach this handle's typing information to a concrete job detail.
|
|
350
439
|
|
|
351
440
|
Args:
|
|
@@ -438,13 +527,13 @@ class JobDetail(JobInfo):
|
|
|
438
527
|
error_logs: str | None = None
|
|
439
528
|
|
|
440
529
|
|
|
441
|
-
class TypedJobDetail(JobDetail, Generic[
|
|
530
|
+
class TypedJobDetail(JobDetail, Generic[TFunctionalResult_co]):
|
|
442
531
|
"""`JobDetail` subclass that exposes a strongly typed `get_results` method."""
|
|
443
532
|
|
|
444
533
|
expected_type: ExecuteType = Field(repr=False)
|
|
445
|
-
extractor: ResultExtractor[
|
|
534
|
+
extractor: ResultExtractor[TFunctionalResult_co] = Field(repr=False, exclude=True)
|
|
446
535
|
|
|
447
|
-
def get_results(self) ->
|
|
536
|
+
def get_results(self) -> TFunctionalResult_co:
|
|
448
537
|
"""Return the strongly typed execution result.
|
|
449
538
|
|
|
450
539
|
Returns:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
from qilisdk._optionals import ImportedFeature, OptionalFeature, Symbol, import_optional_dependencies
|
|
17
|
+
|
|
18
|
+
__all__ = []
|
|
19
|
+
|
|
20
|
+
OPTIONAL_FEATURES: list[OptionalFeature] = [
|
|
21
|
+
OptionalFeature(
|
|
22
|
+
name="openfermion",
|
|
23
|
+
dependencies=["openfermion"],
|
|
24
|
+
symbols=[
|
|
25
|
+
Symbol(path="qilisdk.utils.openfermion.openfermion", name="openfermion_to_qilisdk"),
|
|
26
|
+
Symbol(path="qilisdk.utils.openfermion.openfermion", name="qilisdk_to_openfermion"),
|
|
27
|
+
],
|
|
28
|
+
),
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
current_module = sys.modules[__name__]
|
|
32
|
+
|
|
33
|
+
# Dynamically import (or stub) each feature's symbols and attach them
|
|
34
|
+
for feature in OPTIONAL_FEATURES:
|
|
35
|
+
imported_feature: ImportedFeature = import_optional_dependencies(feature)
|
|
36
|
+
for symbol_name, symbol_obj in imported_feature.symbols.items():
|
|
37
|
+
setattr(current_module, symbol_name, symbol_obj)
|
|
38
|
+
__all__ += [symbol_name] # noqa: PLE0604
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
from abc import ABC
|
|
15
14
|
|
|
15
|
+
from .openfermion import openfermion_to_qilisdk, qilisdk_to_openfermion
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
"""Abstract base class for SDK algorithms."""
|
|
17
|
+
__all__ = ["openfermion_to_qilisdk", "qilisdk_to_openfermion"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from openfermion import QubitOperator
|
|
16
|
+
|
|
17
|
+
from qilisdk.analog import Hamiltonian, PauliI, PauliX, PauliY, PauliZ
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def openfermion_to_qilisdk(qubit_operator: QubitOperator) -> Hamiltonian:
|
|
21
|
+
pauli_map = {"X": PauliX, "Y": PauliY, "Z": PauliZ}
|
|
22
|
+
|
|
23
|
+
return Hamiltonian(
|
|
24
|
+
{
|
|
25
|
+
(tuple((pauli_map[op](q)) for q, op in term) if len(term) > 0 else (PauliI(0),)): coeff
|
|
26
|
+
for term, coeff in qubit_operator.terms.items()
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def qilisdk_to_openfermion(hamiltonian: Hamiltonian) -> QubitOperator:
|
|
32
|
+
of_ham = QubitOperator()
|
|
33
|
+
|
|
34
|
+
for coeff, terms in hamiltonian:
|
|
35
|
+
of_term = ""
|
|
36
|
+
for t in terms:
|
|
37
|
+
if isinstance(t, PauliI):
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
of_term += str(t).replace("(", "").replace(")", "") + " "
|
|
41
|
+
of_term = of_term.rstrip()
|
|
42
|
+
|
|
43
|
+
of_ham += QubitOperator(of_term, coeff)
|
|
44
|
+
|
|
45
|
+
return of_ham
|
|
@@ -18,6 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
from typing import TYPE_CHECKING
|
|
19
19
|
|
|
20
20
|
import matplotlib.pyplot as plt
|
|
21
|
+
import numpy as np
|
|
21
22
|
|
|
22
23
|
if TYPE_CHECKING:
|
|
23
24
|
from qilisdk.analog.schedule import Schedule
|
|
@@ -29,7 +30,13 @@ from qilisdk.utils.visualization.style import ScheduleStyle
|
|
|
29
30
|
class MatplotlibScheduleRenderer:
|
|
30
31
|
"""Render a Schedule using matplotlib, with theme support."""
|
|
31
32
|
|
|
32
|
-
def __init__(
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
schedule: Schedule,
|
|
36
|
+
ax: plt.Axes | None = None,
|
|
37
|
+
*,
|
|
38
|
+
style: ScheduleStyle | None = None,
|
|
39
|
+
) -> None:
|
|
33
40
|
self.schedule = schedule
|
|
34
41
|
self.style = style or ScheduleStyle()
|
|
35
42
|
self.ax = ax or self._make_axes(self.style.dpi, self.style)
|
|
@@ -57,13 +64,14 @@ class MatplotlibScheduleRenderer:
|
|
|
57
64
|
T = self.schedule.T
|
|
58
65
|
dt = self.schedule.dt
|
|
59
66
|
hamiltonians = self.schedule.hamiltonians
|
|
60
|
-
times = [i * dt for i in range(int(T / dt))]
|
|
67
|
+
times = list(np.linspace(0, T, int(1 / dt), dtype=float)) # [i * dt for i in range(int((T + dt) / dt))]
|
|
68
|
+
for t in self.schedule.tlist:
|
|
69
|
+
if t not in times:
|
|
70
|
+
times.append(t)
|
|
71
|
+
times = sorted(times)
|
|
61
72
|
for h in hamiltonians:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
t = _t * dt
|
|
65
|
-
for h in hamiltonians:
|
|
66
|
-
plots[h].append(self.schedule.get_coefficient(t, h))
|
|
73
|
+
coef = self.schedule.coefficients[h]
|
|
74
|
+
plots[h] = [coef[float(t)] for t in times]
|
|
67
75
|
|
|
68
76
|
# Generate gradient colors between primary and accent
|
|
69
77
|
def hex_to_rgb(hex_color: str) -> tuple[int, ...]:
|
|
@@ -162,5 +170,5 @@ class MatplotlibScheduleRenderer:
|
|
|
162
170
|
Returns:
|
|
163
171
|
A newly created Matplotlib Axes.
|
|
164
172
|
"""
|
|
165
|
-
_, ax = plt.subplots(figsize=style.figsize, dpi=style.dpi, facecolor=style.theme.background)
|
|
173
|
+
_, ax = plt.subplots(figsize=style.figsize, dpi=dpi or style.dpi, facecolor=style.theme.background)
|
|
166
174
|
return ax
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qilisdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: QiliSDK is a Python framework for writing digital and analog quantum algorithms and executing them across multiple quantum backends. Its modular design streamlines the development process and enables easy integration with a variety of quantum platforms.
|
|
5
5
|
Project-URL: Homepage, https://github.com/qilimanjaro-tech/qilisdk
|
|
6
6
|
Project-URL: Documentation, https://qilimanjaro-tech.github.io/qilisdk/
|
|
@@ -10,35 +10,39 @@ Project-URL: Changelog, https://github.com/qilimanjaro-tech/qilisdk/blob/main/CH
|
|
|
10
10
|
Author-email: Qilimanjaro Quantum Tech <info@qilimanjaro.tech>
|
|
11
11
|
License-File: LICENCE
|
|
12
12
|
Keywords: analog quantum computing,digital quantum computing,qilimanjaro,quantum computing
|
|
13
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: Intended Audience :: Science/Research
|
|
17
17
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
18
|
Classifier: Operating System :: POSIX :: Linux
|
|
19
19
|
Classifier: Programming Language :: Python :: 3
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
23
|
Classifier: Topic :: Scientific/Engineering
|
|
24
24
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
25
25
|
Classifier: Topic :: Scientific/Engineering :: Quantum Computing
|
|
26
|
-
Requires-Python: >=3.
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
27
|
Requires-Dist: dill>=0.3.9
|
|
28
28
|
Requires-Dist: loguru>=0.7.3
|
|
29
29
|
Requires-Dist: matplotlib>=3.10.7
|
|
30
|
-
Requires-Dist:
|
|
30
|
+
Requires-Dist: mkl-service>=2.4.2; sys_platform != 'darwin'
|
|
31
|
+
Requires-Dist: numpy>=2.2.0
|
|
31
32
|
Requires-Dist: pydantic-settings>=2.11.0
|
|
32
33
|
Requires-Dist: pydantic>=2.12.3
|
|
33
34
|
Requires-Dist: ruamel-yaml>=0.18.16
|
|
34
|
-
Requires-Dist: scipy
|
|
35
|
+
Requires-Dist: scipy<1.16.3,>=1.15; sys_platform != 'darwin'
|
|
36
|
+
Requires-Dist: scipy>=1.16.3; sys_platform == 'darwin'
|
|
35
37
|
Provides-Extra: cuda
|
|
36
|
-
Requires-Dist: cuda-quantum-cu12==0.
|
|
38
|
+
Requires-Dist: cuda-quantum-cu12==0.13.0; extra == 'cuda'
|
|
39
|
+
Provides-Extra: openfermion
|
|
40
|
+
Requires-Dist: openfermion>=1.7.1; extra == 'openfermion'
|
|
37
41
|
Provides-Extra: qutip
|
|
38
42
|
Requires-Dist: qutip-qip>=0.4.1; extra == 'qutip'
|
|
39
|
-
Requires-Dist: qutip>=5.2.
|
|
43
|
+
Requires-Dist: qutip>=5.2.2; extra == 'qutip'
|
|
40
44
|
Provides-Extra: speqtrum
|
|
41
|
-
Requires-Dist: httpx
|
|
45
|
+
Requires-Dist: httpx==0.28.1; extra == 'speqtrum'
|
|
42
46
|
Requires-Dist: keyring>=25.6.0; extra == 'speqtrum'
|
|
43
47
|
Requires-Dist: keyrings-alt>=5.0.2; extra == 'speqtrum'
|
|
44
48
|
Description-Content-Type: text/markdown
|
|
@@ -48,6 +52,7 @@ Description-Content-Type: text/markdown
|
|
|
48
52
|
[](https://pypi.org/project/qilisdk/)
|
|
49
53
|
[](https://pypi.org/project/qilisdk/)
|
|
50
54
|
[](#license)
|
|
55
|
+
[](https://qilimanjaro-tech.github.io/qilisdk/main/index.html)
|
|
51
56
|
|
|
52
57
|
**QiliSDK** is a Python framework for writing digital and analog quantum algorithms and executing them across multiple quantum backends. Its modular design streamlines the development process and enables easy integration with a variety of quantum platforms.
|
|
53
58
|
|
|
@@ -202,18 +207,27 @@ print("Optimal Parameters:", result.optimal_parameters)
|
|
|
202
207
|
QiliSDK includes a client for interacting with Qilimanjaro's SpeQtrum platform. This module supports secure login and a unified interface for both digital circuits and analog evolutions:
|
|
203
208
|
|
|
204
209
|
```python
|
|
205
|
-
from qilisdk.
|
|
210
|
+
from qilisdk.backends import CudaBackend, CudaSamplingMethod
|
|
211
|
+
from qilisdk.digital import Circuit, H, M
|
|
206
212
|
from qilisdk.functionals import Sampling
|
|
213
|
+
from qilisdk.speqtrum import SpeQtrum
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# Build a single-qubit circuit
|
|
217
|
+
circuit = Circuit(nqubits=1)
|
|
218
|
+
circuit.add(H(0))
|
|
219
|
+
circuit.add(M(0))
|
|
207
220
|
|
|
208
221
|
# Login to QaaSBackend with credentials (or use environment variables)
|
|
209
222
|
# This only needs to be run once.
|
|
210
|
-
SpeQtrum.login(username="
|
|
223
|
+
SpeQtrum.login(username="YOUR_USERNAME", apikey="YOUR_APIKEY")
|
|
211
224
|
|
|
212
225
|
# Instantiate QaaSBackend
|
|
213
226
|
client = SpeQtrum()
|
|
214
227
|
|
|
215
|
-
#
|
|
216
|
-
|
|
228
|
+
# Execute a pre-built circuit (see Digital Quantum Circuits section)
|
|
229
|
+
# make sure to select the device (you can list available devices using ``client.list_devices()``)
|
|
230
|
+
job_id = client.submit(Sampling(circuit, 1000), device="SELECTED_DEVICE")
|
|
217
231
|
print("job id:", job_id)
|
|
218
232
|
print("job status:", client.get_job(job_id).status)
|
|
219
233
|
print("job result:", client.get_job(job_id).result)
|
|
@@ -241,18 +255,17 @@ print("CUDA Backend Results:", results)
|
|
|
241
255
|
|
|
242
256
|
### Time Evolution using Qutip
|
|
243
257
|
|
|
244
|
-
For analog simulations, the
|
|
258
|
+
For analog simulations, the `TimeEvolution` and unified `Schedule` classes allow you to simulate time-dependent quantum dynamics. The following example uses callable coefficients defined over an interval to interpolate between two Hamiltonians on a Qutip backend:
|
|
245
259
|
|
|
246
260
|
```python
|
|
247
|
-
import numpy as np
|
|
248
261
|
from qilisdk.analog import Schedule, X, Z, Y
|
|
249
262
|
from qilisdk.core import ket, tensor_prod
|
|
250
263
|
from qilisdk.backends import QutipBackend
|
|
251
264
|
from qilisdk.functionals import TimeEvolution
|
|
252
265
|
|
|
253
266
|
# Define total time and timestep
|
|
254
|
-
T = 100
|
|
255
|
-
|
|
267
|
+
T = 100.0
|
|
268
|
+
dt = 0.1
|
|
256
269
|
nqubits = 1
|
|
257
270
|
|
|
258
271
|
# Define Hamiltonians
|
|
@@ -260,13 +273,14 @@ Hx = sum(X(i) for i in range(nqubits))
|
|
|
260
273
|
Hz = sum(Z(i) for i in range(nqubits))
|
|
261
274
|
|
|
262
275
|
# Build a time‑dependent schedule
|
|
263
|
-
schedule = Schedule(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
276
|
+
schedule = Schedule(
|
|
277
|
+
hamiltonians={"hx": Hx, "hz": Hz},
|
|
278
|
+
coefficients={
|
|
279
|
+
"hx": {(0.0, T): lambda t: 1 - t / T},
|
|
280
|
+
"hz": {(0.0, T): lambda t: t / T},
|
|
281
|
+
},
|
|
282
|
+
dt=dt,
|
|
283
|
+
)
|
|
270
284
|
|
|
271
285
|
# draw the schedule
|
|
272
286
|
schedule.draw()
|
|
@@ -418,6 +432,42 @@ print("Reconstructed Circuit:")
|
|
|
418
432
|
restored_circuit.draw()
|
|
419
433
|
```
|
|
420
434
|
|
|
435
|
+
### OpenFermion Integration
|
|
436
|
+
|
|
437
|
+
`QiliSDK` can translate ``QubitOperator`` objects from ``OpenFermion`` to ``QiliSDK``'s ``Hamiltonian`` Objects and vice versa.
|
|
438
|
+
|
|
439
|
+
This code is available under an optional dependency that can be installed using ``pip install qilisdk[openfermion]``.
|
|
440
|
+
|
|
441
|
+
here is an example of the usage:
|
|
442
|
+
```python
|
|
443
|
+
from openfermion.hamiltonians import jellium_model
|
|
444
|
+
from openfermion.transforms import fourier_transform, jordan_wigner
|
|
445
|
+
from openfermion.utils import Grid
|
|
446
|
+
|
|
447
|
+
from qilisdk.utils.openfermion import openfermion_to_qilisdk, qilisdk_to_openfermion
|
|
448
|
+
|
|
449
|
+
# Let's look at a very small model of jellium in 1D.
|
|
450
|
+
grid = Grid(dimensions=1, length=3, scale=1.0)
|
|
451
|
+
spinless = True
|
|
452
|
+
|
|
453
|
+
# Get the momentum Hamiltonian.
|
|
454
|
+
momentum_hamiltonian = jellium_model(grid, spinless)
|
|
455
|
+
momentum_qubit_operator = jordan_wigner(momentum_hamiltonian)
|
|
456
|
+
momentum_qubit_operator.compress()
|
|
457
|
+
|
|
458
|
+
# Fourier transform the Hamiltonian to the position basis.
|
|
459
|
+
position_hamiltonian = fourier_transform(momentum_hamiltonian, grid, spinless)
|
|
460
|
+
position_qubit_operator = jordan_wigner(position_hamiltonian)
|
|
461
|
+
position_qubit_operator.compress()
|
|
462
|
+
|
|
463
|
+
qilisdk_ham = openfermion_to_qilisdk(position_qubit_operator)
|
|
464
|
+
openfermion_ham = qilisdk_to_openfermion(qilisdk_ham)
|
|
465
|
+
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
|
421
471
|
---
|
|
422
472
|
|
|
423
473
|
## Development
|