qilisdk 0.1.3__py3-none-any.whl → 0.1.5__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 (83) hide show
  1. qilisdk/__init__.py +11 -2
  2. qilisdk/__init__.pyi +2 -3
  3. qilisdk/_logging.py +135 -0
  4. qilisdk/_optionals.py +5 -7
  5. qilisdk/analog/__init__.py +3 -18
  6. qilisdk/analog/exceptions.py +2 -4
  7. qilisdk/analog/hamiltonian.py +455 -110
  8. qilisdk/analog/linear_schedule.py +118 -0
  9. qilisdk/analog/schedule.py +272 -79
  10. qilisdk/backends/__init__.py +45 -0
  11. qilisdk/{digital/digital_algorithm.py → backends/__init__.pyi} +3 -5
  12. qilisdk/backends/backend.py +117 -0
  13. qilisdk/{extras/cuda → backends}/cuda_backend.py +153 -161
  14. qilisdk/backends/qutip_backend.py +492 -0
  15. qilisdk/common/__init__.py +48 -2
  16. qilisdk/common/algorithm.py +2 -1
  17. qilisdk/{extras/qaas/qaas_settings.py → common/exceptions.py} +12 -6
  18. qilisdk/common/model.py +1019 -1
  19. qilisdk/common/parameterizable.py +75 -0
  20. qilisdk/common/qtensor.py +666 -0
  21. qilisdk/common/result.py +2 -1
  22. qilisdk/common/variables.py +1931 -0
  23. qilisdk/{extras/cuda/cuda_analog_result.py → cost_functions/__init__.py} +3 -4
  24. qilisdk/cost_functions/cost_function.py +77 -0
  25. qilisdk/cost_functions/model_cost_function.py +145 -0
  26. qilisdk/cost_functions/observable_cost_function.py +109 -0
  27. qilisdk/digital/__init__.py +3 -22
  28. qilisdk/digital/ansatz.py +203 -160
  29. qilisdk/digital/circuit.py +81 -9
  30. qilisdk/digital/exceptions.py +12 -6
  31. qilisdk/digital/gates.py +228 -85
  32. qilisdk/{extras/qaas/qaas_analog_result.py → functionals/__init__.py} +14 -5
  33. qilisdk/functionals/functional.py +39 -0
  34. qilisdk/{extras/cuda/cuda_digital_result.py → functionals/functional_result.py} +3 -4
  35. qilisdk/functionals/sampling.py +81 -0
  36. qilisdk/functionals/sampling_result.py +92 -0
  37. qilisdk/functionals/time_evolution.py +98 -0
  38. qilisdk/functionals/time_evolution_result.py +84 -0
  39. qilisdk/functionals/variational_program.py +80 -0
  40. qilisdk/functionals/variational_program_result.py +69 -0
  41. qilisdk/logging_config.yaml +16 -0
  42. qilisdk/{common/backend.py → optimizers/__init__.py} +2 -1
  43. qilisdk/optimizers/optimizer.py +39 -0
  44. qilisdk/{common → optimizers}/optimizer_result.py +3 -12
  45. qilisdk/{common/optimizer.py → optimizers/scipy_optimizer.py} +10 -28
  46. qilisdk/settings.py +78 -0
  47. qilisdk/{extras → speqtrum}/__init__.py +7 -8
  48. qilisdk/{extras → speqtrum}/__init__.pyi +3 -3
  49. qilisdk/speqtrum/experiments/__init__.py +25 -0
  50. qilisdk/speqtrum/experiments/experiment_functional.py +124 -0
  51. qilisdk/speqtrum/experiments/experiment_result.py +231 -0
  52. qilisdk/{extras/qaas → speqtrum}/keyring.py +8 -4
  53. qilisdk/speqtrum/speqtrum.py +432 -0
  54. qilisdk/speqtrum/speqtrum_models.py +300 -0
  55. qilisdk/utils/__init__.py +0 -14
  56. qilisdk/utils/openqasm2.py +1 -1
  57. qilisdk/utils/serialization.py +1 -1
  58. qilisdk/utils/visualization/PlusJakartaSans-SemiBold.ttf +0 -0
  59. qilisdk/utils/visualization/__init__.py +24 -0
  60. qilisdk/utils/visualization/circuit_renderers.py +781 -0
  61. qilisdk/utils/visualization/schedule_renderers.py +161 -0
  62. qilisdk/utils/visualization/style.py +154 -0
  63. qilisdk/utils/visualization/themes.py +76 -0
  64. qilisdk/yaml.py +126 -0
  65. {qilisdk-0.1.3.dist-info → qilisdk-0.1.5.dist-info}/METADATA +180 -135
  66. qilisdk-0.1.5.dist-info/RECORD +69 -0
  67. qilisdk/analog/algorithms.py +0 -111
  68. qilisdk/analog/analog_backend.py +0 -43
  69. qilisdk/analog/analog_result.py +0 -114
  70. qilisdk/analog/quantum_objects.py +0 -533
  71. qilisdk/digital/digital_backend.py +0 -90
  72. qilisdk/digital/digital_result.py +0 -145
  73. qilisdk/digital/vqe.py +0 -166
  74. qilisdk/extras/cuda/__init__.py +0 -13
  75. qilisdk/extras/qaas/__init__.py +0 -13
  76. qilisdk/extras/qaas/models.py +0 -132
  77. qilisdk/extras/qaas/qaas_backend.py +0 -255
  78. qilisdk/extras/qaas/qaas_digital_result.py +0 -20
  79. qilisdk/extras/qaas/qaas_time_evolution_result.py +0 -20
  80. qilisdk/extras/qaas/qaas_vqe_result.py +0 -20
  81. qilisdk-0.1.3.dist-info/RECORD +0 -51
  82. {qilisdk-0.1.3.dist-info → qilisdk-0.1.5.dist-info}/WHEEL +0 -0
  83. {qilisdk-0.1.3.dist-info → qilisdk-0.1.5.dist-info}/licenses/LICENCE +0 -0
@@ -11,9 +11,8 @@
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 qilisdk.analog.analog_result import AnalogResult
15
- from qilisdk.yaml import yaml
16
14
 
15
+ from .model_cost_function import ModelCostFunction
16
+ from .observable_cost_function import ObservableCostFunction
17
17
 
18
- @yaml.register_class
19
- class CudaAnalogResult(AnalogResult): ...
18
+ __all__ = ["ModelCostFunction", "ObservableCostFunction"]
@@ -0,0 +1,77 @@
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
+ from __future__ import annotations
15
+
16
+ from abc import ABC
17
+ from typing import TYPE_CHECKING, Callable, TypeVar, cast, overload
18
+
19
+ from qilisdk.functionals.functional_result import FunctionalResult
20
+ from qilisdk.functionals.sampling_result import SamplingResult
21
+ from qilisdk.functionals.time_evolution_result import TimeEvolutionResult
22
+
23
+ if TYPE_CHECKING:
24
+ from qilisdk.common.variables import Number
25
+
26
+ TResult = TypeVar("TResult", bound=FunctionalResult)
27
+
28
+
29
+ class CostFunction(ABC):
30
+ """
31
+ Base class that maps functional results into scalar costs.
32
+ """
33
+
34
+ def __init__(self) -> None:
35
+ self._handlers: dict[type[FunctionalResult], Callable[[FunctionalResult], Number]] = {
36
+ SamplingResult: lambda f: self._compute_cost_sampling(cast("SamplingResult", f)),
37
+ TimeEvolutionResult: lambda f: self._compute_cost_time_evolution(cast("TimeEvolutionResult", f)),
38
+ }
39
+
40
+ @overload
41
+ def compute_cost(self, results: SamplingResult) -> Number: ...
42
+
43
+ @overload
44
+ def compute_cost(self, results: TimeEvolutionResult) -> Number: ...
45
+
46
+ @overload
47
+ def compute_cost(self, results: FunctionalResult) -> Number: ...
48
+
49
+ def compute_cost(self, results: TResult) -> Number:
50
+ """
51
+ Dispatch to the appropriate cost implementation based on the result type.
52
+
53
+ Args:
54
+ results (FunctionalResult): Output of a functional execution.
55
+
56
+ Returns:
57
+ Number: Scalar cost extracted from the results.
58
+
59
+ Raises:
60
+ NotImplementedError: If the concrete cost function does not support the given result type.
61
+ """
62
+ try:
63
+ handler = self._handlers[type(results)]
64
+ except KeyError as exc:
65
+ raise NotImplementedError(
66
+ f"{type(self).__qualname__} does not support {type(results).__qualname__}"
67
+ ) from exc
68
+
69
+ return handler(results)
70
+
71
+ def _compute_cost_sampling(self, results: SamplingResult) -> Number:
72
+ """Compute the cost associated with a :class:`SamplingResult`."""
73
+ raise NotImplementedError
74
+
75
+ def _compute_cost_time_evolution(self, results: TimeEvolutionResult) -> Number:
76
+ """Compute the cost associated with a :class:`TimeEvolutionResult`."""
77
+ raise NotImplementedError
@@ -0,0 +1,145 @@
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
+ from __future__ import annotations
15
+
16
+ from typing import TYPE_CHECKING
17
+
18
+ import numpy as np
19
+
20
+ from qilisdk.common.model import QUBO, Model
21
+ from qilisdk.common.qtensor import QTensor, expect_val, ket
22
+ from qilisdk.cost_functions.cost_function import CostFunction
23
+
24
+ if TYPE_CHECKING:
25
+ from qilisdk.common.variables import Number
26
+ from qilisdk.functionals.sampling_result import SamplingResult
27
+ from qilisdk.functionals.time_evolution_result import TimeEvolutionResult
28
+
29
+
30
+ class ModelCostFunction(CostFunction):
31
+ """
32
+ Evaluate the cost of functional results with respect to a :class:`~qilisdk.common.model.Model`.
33
+
34
+ Example:
35
+ .. code-block:: python
36
+
37
+ from qilisdk.common import BinaryVariable, Model, LEQ
38
+ from qilisdk.cost_functions import ModelCostFunction
39
+
40
+ model = Model("demo")
41
+ x0, x1 = BinaryVariable("x0"), BinaryVariable("x1")
42
+ model.set_objective(x0 + x1)
43
+ model.add_constraint("limit", LEQ(x0 + x1, 1))
44
+ cost_fn = ModelCostFunction(model)
45
+ """
46
+
47
+ def __init__(self, model: Model) -> None:
48
+ """
49
+ Args:
50
+ model (Model): Classical model describing objective and constraints.
51
+ """
52
+ super().__init__()
53
+ self._model = model
54
+
55
+ @property
56
+ def model(self) -> Model:
57
+ """Return the underlying optimisation model."""
58
+ return self._model
59
+
60
+ def _compute_cost_time_evolution(self, results: TimeEvolutionResult) -> Number:
61
+ """
62
+ Compute the expectation value of the model objective using a time-evolution result.
63
+
64
+ Evaluates the model on each computational basis state with probability extracted from the final state.
65
+
66
+ Returns:
67
+ Number: Expectation value of the model objective.
68
+
69
+ Raises:
70
+ ValueError: If the final state is not provided in the results.
71
+ """
72
+ if results.final_state is None:
73
+ raise ValueError(
74
+ "can't compute cost using Models from time evolution results when the state is not provided."
75
+ )
76
+
77
+ if isinstance(self.model, QUBO):
78
+ ham = self.model.to_hamiltonian()
79
+ total_cost = complex(np.real_if_close(expect_val(QTensor(ham.to_matrix()), results.final_state)))
80
+ if total_cost.imag == 0:
81
+ return total_cost.real
82
+ return total_cost
83
+
84
+ total_cost = complex(0.0)
85
+
86
+ if results.final_state.is_density_matrix(tol=1e-5):
87
+ rho = results.final_state.dense
88
+ n = results.final_state.nqubits
89
+ for i in range(rho.shape[0]):
90
+ state = [int(b) for b in f"{i:0{n}b}"]
91
+ _ket_state = ket(*state)
92
+ _prob = complex(np.real_if_close(np.trace((_ket_state @ _ket_state.adjoint()).dense @ rho)))
93
+ variable_map = {v: int(state[i]) for i, v in enumerate(self.model.variables())}
94
+ evaluate_results = self.model.evaluate(variable_map)
95
+ total_cost += sum(v for v in evaluate_results.values()) * _prob
96
+ if total_cost.imag == 0:
97
+ return total_cost.real
98
+ return total_cost
99
+
100
+ dense_state = None
101
+ if results.final_state.is_ket():
102
+ dense_state = results.final_state.dense.T[0]
103
+ elif results.final_state.is_bra():
104
+ dense_state = results.final_state.dense[0]
105
+
106
+ if dense_state is None:
107
+ raise ValueError("The final state is invalid.")
108
+
109
+ n = len(self.model.variables())
110
+
111
+ for i, prob in enumerate(dense_state):
112
+ state = [int(b) for b in f"{i:0{n}b}"]
113
+ variable_map = {v: state[i] for i, v in enumerate(self.model.variables())}
114
+ evaluate_results = self.model.evaluate(variable_map)
115
+ total_cost += sum(v for v in evaluate_results.values()) * np.abs(prob**2)
116
+
117
+ total_cost = complex(np.real_if_close(total_cost, tol=1e-12))
118
+ if total_cost.imag == 0:
119
+ return total_cost.real
120
+ return total_cost
121
+
122
+ def _compute_cost_sampling(self, results: SamplingResult) -> Number:
123
+ """
124
+ Compute the model cost by averaging over sampled bitstrings.
125
+
126
+ Each sample is mapped onto the model variables and evaluated using ``model.evaluate``.
127
+
128
+ Returns:
129
+ Number: Average cost of the model objective over all samples.
130
+
131
+ Raises:
132
+ ValueError: If the number of model variables does not match the sample size.
133
+ """
134
+ total_cost = complex(0.0)
135
+ for sample, prob in results.get_probabilities():
136
+ bit_configuration = [int(i) for i in sample]
137
+ if len(self.model.variables()) != len(bit_configuration):
138
+ raise ValueError("Mapping samples to the model's variables is ambiguous.")
139
+ variable_map = {v: bit_configuration[i] for i, v in enumerate(self.model.variables())}
140
+ evaluate_results = self.model.evaluate(variable_map)
141
+ total_cost += sum(v for v in evaluate_results.values()) * prob
142
+
143
+ if total_cost.imag == 0:
144
+ return total_cost.real
145
+ return total_cost
@@ -0,0 +1,109 @@
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
+ from __future__ import annotations
15
+
16
+ from typing import TYPE_CHECKING
17
+
18
+ import numpy as np
19
+
20
+ from qilisdk.analog.hamiltonian import Hamiltonian, PauliOperator
21
+ from qilisdk.common.qtensor import QTensor, expect_val, ket, tensor_prod
22
+ from qilisdk.cost_functions.cost_function import CostFunction
23
+
24
+ if TYPE_CHECKING:
25
+ from qilisdk.common.variables import Number
26
+ from qilisdk.functionals.sampling_result import SamplingResult
27
+ from qilisdk.functionals.time_evolution_result import TimeEvolutionResult
28
+
29
+
30
+ class ObservableCostFunction(CostFunction):
31
+ """
32
+ Compute costs by taking expectation values of observables.
33
+
34
+ Example:
35
+ .. code-block:: python
36
+
37
+ from qilisdk.analog.hamiltonian import Z
38
+ from qilisdk.cost_functions import ObservableCostFunction
39
+
40
+ cost_fn = ObservableCostFunction(Z(0))
41
+ """
42
+
43
+ def __init__(self, observable: QTensor | Hamiltonian | PauliOperator) -> None:
44
+ """
45
+ Args:
46
+ observable (QTensor | Hamiltonian | PauliOperator): Quantum observable whose expectation value defines the cost.
47
+
48
+ Raises:
49
+ ValueError: If the provided observable type is unsupported.
50
+ """
51
+ super().__init__()
52
+ if isinstance(observable, QTensor):
53
+ self._observable = observable
54
+ elif isinstance(observable, Hamiltonian):
55
+ self._observable = QTensor(observable.to_matrix())
56
+ elif isinstance(observable, PauliOperator):
57
+ self._observable = QTensor(observable.matrix)
58
+ else:
59
+ raise ValueError(
60
+ f"Observable needs to be of type QTensor, Hamiltonian, or PauliOperator but {type(observable)} was provided"
61
+ )
62
+
63
+ @property
64
+ def observable(self) -> QTensor:
65
+ """Return the observable in ``QTensor`` form."""
66
+ return self._observable
67
+
68
+ def _compute_cost_time_evolution(self, results: TimeEvolutionResult) -> Number:
69
+ """
70
+ Returns:
71
+ Number: the expectation value of ``observable`` given a final quantum state.
72
+
73
+ Raises:
74
+ ValueError: If the final state is not provided in the results.
75
+
76
+ """
77
+ if results.final_state is None:
78
+ raise ValueError(
79
+ "can't compute cost using Observables from time evolution results when the state is not provided."
80
+ )
81
+ total_cost = complex(np.real_if_close(expect_val(self._observable, results.final_state)))
82
+ if total_cost.imag == 0:
83
+ return total_cost.real
84
+ return total_cost
85
+
86
+ def _compute_cost_sampling(self, results: SamplingResult) -> Number:
87
+ """
88
+ Estimate the observable expectation value using measured bitstring probabilities.
89
+
90
+ Returns:
91
+ Number: the expectation value of ``observable`` estimated from sampled bitstrings.
92
+
93
+ Raises:
94
+ ValueError: If the number of qubits in the observable does not match the sample size
95
+ """
96
+ total_cost = complex(0.0)
97
+ nqubits = self._observable.nqubits
98
+ for sample, prob in results.get_probabilities():
99
+ state = tensor_prod([ket(int(i)) for i in sample])
100
+ if nqubits != state.nqubits:
101
+ raise ValueError(
102
+ f"The samples provided have {state.nqubits} qubits but the observable has {nqubits} qubits"
103
+ )
104
+ evaluate_results = complex(np.real_if_close(expect_val(self._observable, state)))
105
+ total_cost += evaluate_results * prob
106
+
107
+ if total_cost.imag == 0:
108
+ return total_cost.real
109
+ return total_cost
@@ -14,26 +14,7 @@
14
14
 
15
15
  from .ansatz import HardwareEfficientAnsatz
16
16
  from .circuit import Circuit
17
- from .digital_backend import DigitalSimulationMethod
18
- from .gates import (
19
- CNOT,
20
- CZ,
21
- RX,
22
- RY,
23
- RZ,
24
- U1,
25
- U2,
26
- U3,
27
- Gate,
28
- H,
29
- M,
30
- S,
31
- T,
32
- X,
33
- Y,
34
- Z,
35
- )
36
- from .vqe import VQE
17
+ from .gates import CNOT, CZ, RX, RY, RZ, SWAP, U1, U2, U3, Gate, H, I, M, S, T, X, Y, Z
37
18
 
38
19
  __all__ = [
39
20
  "CNOT",
@@ -41,15 +22,15 @@ __all__ = [
41
22
  "RX",
42
23
  "RY",
43
24
  "RZ",
25
+ "SWAP",
44
26
  "U1",
45
27
  "U2",
46
28
  "U3",
47
- "VQE",
48
29
  "Circuit",
49
- "DigitalSimulationMethod",
50
30
  "Gate",
51
31
  "H",
52
32
  "HardwareEfficientAnsatz",
33
+ "I",
53
34
  "M",
54
35
  "S",
55
36
  "T",