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.
Files changed (34) hide show
  1. qilisdk/analog/__init__.py +1 -2
  2. qilisdk/analog/hamiltonian.py +1 -68
  3. qilisdk/analog/schedule.py +288 -313
  4. qilisdk/backends/backend.py +5 -1
  5. qilisdk/backends/cuda_backend.py +9 -5
  6. qilisdk/backends/qutip_backend.py +23 -12
  7. qilisdk/core/__init__.py +4 -0
  8. qilisdk/core/interpolator.py +406 -0
  9. qilisdk/core/parameterizable.py +66 -10
  10. qilisdk/core/variables.py +150 -7
  11. qilisdk/digital/circuit.py +1 -0
  12. qilisdk/digital/circuit_transpiler.py +46 -0
  13. qilisdk/digital/circuit_transpiler_passes/__init__.py +18 -0
  14. qilisdk/digital/circuit_transpiler_passes/circuit_transpiler_pass.py +36 -0
  15. qilisdk/digital/circuit_transpiler_passes/decompose_multi_controlled_gates_pass.py +216 -0
  16. qilisdk/digital/circuit_transpiler_passes/numeric_helpers.py +82 -0
  17. qilisdk/digital/gates.py +12 -2
  18. qilisdk/{speqtrum/experiments → experiments}/__init__.py +13 -2
  19. qilisdk/{speqtrum/experiments → experiments}/experiment_functional.py +90 -2
  20. qilisdk/{speqtrum/experiments → experiments}/experiment_result.py +16 -0
  21. qilisdk/functionals/sampling.py +8 -1
  22. qilisdk/functionals/time_evolution.py +6 -2
  23. qilisdk/functionals/variational_program.py +58 -0
  24. qilisdk/speqtrum/speqtrum.py +360 -130
  25. qilisdk/speqtrum/speqtrum_models.py +108 -19
  26. qilisdk/utils/openfermion/__init__.py +38 -0
  27. qilisdk/{core/algorithm.py → utils/openfermion/__init__.pyi} +2 -3
  28. qilisdk/utils/openfermion/openfermion.py +45 -0
  29. qilisdk/utils/visualization/schedule_renderers.py +16 -8
  30. {qilisdk-0.1.6.dist-info → qilisdk-0.1.7.dist-info}/METADATA +74 -24
  31. {qilisdk-0.1.6.dist-info → qilisdk-0.1.7.dist-info}/RECORD +33 -26
  32. {qilisdk-0.1.6.dist-info → qilisdk-0.1.7.dist-info}/WHEEL +1 -1
  33. qilisdk/analog/linear_schedule.py +0 -121
  34. {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: str = Field(alias="issuedAt")
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
- ResultT_co = TypeVar("ResultT_co", bound=FunctionalResult, covariant=True)
232
- VariationalInnerResultT = TypeVar("VariationalInnerResultT", bound=FunctionalResult)
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
- ResultExtractor = Callable[[ExecuteResult], ResultT_co]
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[VariationalInnerResultT],
271
- ) -> ResultExtractor[VariationalProgramResult[VariationalInnerResultT]]:
272
- def _extractor(result: ExecuteResult) -> VariationalProgramResult[VariationalInnerResultT]:
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[VariationalInnerResultT]", variational_result)
358
+ return cast("VariationalProgramResult[TVariationalInnerResult]", variational_result)
282
359
 
283
360
  return _extractor
284
361
 
285
362
 
286
- class JobHandle(SpeQtrumModel, Generic[ResultT_co]):
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[ResultT_co] = Field(repr=False, exclude=True)
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[VariationalInnerResultT]
309
- ) -> "JobHandle[VariationalProgramResult[VariationalInnerResultT]]": ...
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[VariationalInnerResultT] | None = None
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[VariationalInnerResultT]]", handle)
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
- def bind(self, detail: "JobDetail") -> "TypedJobDetail[ResultT_co]":
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[ResultT_co]):
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[ResultT_co] = Field(repr=False, exclude=True)
534
+ extractor: ResultExtractor[TFunctionalResult_co] = Field(repr=False, exclude=True)
446
535
 
447
- def get_results(self) -> ResultT_co:
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
- class Algorithm(ABC):
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__(self, schedule: Schedule, ax: plt.Axes | None = None, *, style: ScheduleStyle | None = None) -> None:
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
- plots[h] = []
63
- for _t in range(int(T / dt)):
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.6
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 :: 3 - Alpha
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.10
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: numpy>=1.26.4
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>=1.15.3
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.12.0.post1; extra == 'cuda'
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.1; extra == 'qutip'
43
+ Requires-Dist: qutip>=5.2.2; extra == 'qutip'
40
44
  Provides-Extra: speqtrum
41
- Requires-Dist: httpx>=0.28.1; extra == 'speqtrum'
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
  [![Python Versions](https://img.shields.io/pypi/pyversions/qilisdk.svg)](https://pypi.org/project/qilisdk/)
49
53
  [![PyPI Version](https://img.shields.io/pypi/v/qilisdk.svg)](https://pypi.org/project/qilisdk/)
50
54
  [![License](https://img.shields.io/pypi/l/qilisdk.svg)](#license)
55
+ [![Docs](https://img.shields.io/badge/docs-latest-pink.svg)](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.speqtrum import SpeQtrum
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="your_username", apikey="your_apikey")
223
+ SpeQtrum.login(username="YOUR_USERNAME", apikey="YOUR_APIKEY")
211
224
 
212
225
  # Instantiate QaaSBackend
213
226
  client = SpeQtrum()
214
227
 
215
- # # Execute a pre-built circuit (see Digital Quantum Circuits section)
216
- job_id = client.submit(Sampling(circuit, 1000), device="cuda_state_vector")
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 new `TimeEvolution` and `Schedule` classes allow you to simulate time-dependent quantum dynamics. The following example uses a linear schedule to interpolate between two Hamiltonians on a Qutip backend:
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
- steps = np.linspace(0, T, T)
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(T)
264
-
265
- # Add hx with a time‐dependent coefficient function
266
- schedule.add_hamiltonian(label="hx", hamiltonian=Hx, schedule=lambda t: 1 - steps[t] / T)
267
-
268
- # Add hz similarly
269
- schedule.add_hamiltonian(label="hz", hamiltonian=Hz, schedule=lambda t: steps[t] / T)
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