qilisdk 0.1.3__py3-none-any.whl → 0.1.4__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/quantum_objects.py +84 -21
- qilisdk/extras/__init__.py +1 -1
- qilisdk/extras/cuda/cuda_backend.py +2 -3
- {qilisdk-0.1.3.dist-info → qilisdk-0.1.4.dist-info}/METADATA +5 -6
- {qilisdk-0.1.3.dist-info → qilisdk-0.1.4.dist-info}/RECORD +7 -7
- {qilisdk-0.1.3.dist-info → qilisdk-0.1.4.dist-info}/WHEEL +0 -0
- {qilisdk-0.1.3.dist-info → qilisdk-0.1.4.dist-info}/licenses/LICENCE +0 -0
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import math
|
|
16
17
|
import string
|
|
17
18
|
from typing import Literal
|
|
18
19
|
|
|
@@ -141,31 +142,95 @@ class QuantumObject:
|
|
|
141
142
|
out = QuantumObject(self._data.conj().T)
|
|
142
143
|
return out
|
|
143
144
|
|
|
144
|
-
def ptrace(self,
|
|
145
|
+
def ptrace(self, keep: list[int], dims: list[int] | None = None) -> "QuantumObject":
|
|
145
146
|
"""
|
|
146
147
|
Compute the partial trace over subsystems not in 'keep'.
|
|
147
148
|
|
|
148
149
|
This method calculates the reduced density matrix by tracing out
|
|
149
150
|
the subsystems that are not specified in the 'keep' parameter.
|
|
150
|
-
The input 'dims' represents the dimensions of each subsystem,
|
|
151
|
-
and 'keep' indicates the indices of
|
|
151
|
+
The input 'dims' represents the dimensions of each subsystem (optional),
|
|
152
|
+
and 'keep' indicates the indices of those subsystems to be retained.
|
|
153
|
+
|
|
154
|
+
If the QuantumObject is a ket or bra, it will first be converted to a density matrix.
|
|
152
155
|
|
|
153
156
|
Args:
|
|
154
|
-
dims (list[int]): A list specifying the dimensions of each subsystem.
|
|
155
157
|
keep (list[int]): A list of indices corresponding to the subsystems to retain.
|
|
158
|
+
The order of the indices in 'keep' is not important, since dimensions will
|
|
159
|
+
be returned in the tensor original order, but the indices must be unique.
|
|
160
|
+
dims (list[int], optional): A list specifying the dimensions of each subsystem.
|
|
161
|
+
If not specified, a density matrix of qubit states is assumed, and the
|
|
162
|
+
dimensions are inferred accordingly (i.e. we split the state in dim 2 states).
|
|
156
163
|
|
|
157
164
|
Raises:
|
|
158
165
|
ValueError: If the product of the dimensions in dims does not match the
|
|
159
|
-
shape of the QuantumObject's dense representation.
|
|
166
|
+
shape of the QuantumObject's dense representation or if any dimension is non-positive.
|
|
167
|
+
ValueError: If the indices in 'keep' are not unique or are out of range.
|
|
168
|
+
ValueError: If the QuantumObject is not a valid density matrix or state vector.
|
|
169
|
+
ValueError: If the number of subsystems exceeds the available ASCII letters.
|
|
160
170
|
|
|
161
171
|
Returns:
|
|
162
172
|
QuantumObject: A new QuantumObject representing the reduced density matrix
|
|
163
173
|
for the subsystems specified in 'keep'.
|
|
164
174
|
"""
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
175
|
+
# 1) Get the density matrix representation:
|
|
176
|
+
rho = self.dense if self.is_operator() else self.to_density_matrix().dense
|
|
177
|
+
|
|
178
|
+
# 2.a) If `dims` is not provided, we assume a density matrix of qubit states (we split in subsystems of dim = 2):
|
|
179
|
+
if dims is None:
|
|
180
|
+
# The to_density_matrix() should check its a square matrix, with size being a power of 2, so we can do:
|
|
181
|
+
number_of_qubits_in_state = int(math.log2(rho.shape[0]))
|
|
182
|
+
dims = [2 for _ in range(number_of_qubits_in_state)]
|
|
183
|
+
# 2.b) If `dims` is provided, we run checks on it:
|
|
184
|
+
else:
|
|
185
|
+
total_dim = int(np.prod(dims))
|
|
186
|
+
if rho.shape != (total_dim, total_dim):
|
|
187
|
+
raise ValueError(
|
|
188
|
+
f"Dimension mismatch: QuantumObject shape {rho.shape} does not match the expected shape ({total_dim}, {total_dim}), given by the product of all passed `dims`: (np.prod(dims), np.prod(dims))."
|
|
189
|
+
)
|
|
190
|
+
if any(d <= 0 for d in dims):
|
|
191
|
+
raise ValueError("All subsystem dimensions must be positive")
|
|
192
|
+
|
|
193
|
+
# 3) Validate & sort `keep`
|
|
194
|
+
keep_set = set(keep)
|
|
195
|
+
if any(i < 0 or i >= len(dims) for i in keep_set):
|
|
196
|
+
raise ValueError("keep indices out of range (0, len(dims))")
|
|
197
|
+
if len(keep_set) != len(keep):
|
|
198
|
+
raise ValueError("duplicate indices in keep")
|
|
199
|
+
|
|
200
|
+
# 4) Trace out the subsystems not in `keep`.
|
|
201
|
+
rho_t = self._compute_traced_tensor_via_einstein_summation(rho, keep_set, dims)
|
|
202
|
+
|
|
203
|
+
# 5) The resulting tensor has separate indices for each subsystem kept.
|
|
204
|
+
# Reshape it into a matrix (i.e. combine the row indices and column indices).
|
|
205
|
+
dims_keep = [dims[i] for i in keep_set]
|
|
206
|
+
new_dim = int(np.prod(dims_keep)) if dims_keep else 1
|
|
207
|
+
|
|
208
|
+
return QuantumObject(rho_t.reshape((new_dim, new_dim)))
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def _compute_traced_tensor_via_einstein_summation(rho: np.ndarray, keep: set[int], dims: list[int]) -> np.ndarray:
|
|
212
|
+
"""Helper function called in `ptrace`, which computes the partial trace over subsystems not in 'keep'.
|
|
213
|
+
|
|
214
|
+
This function generates the appropriate einsum subscript strings for the input tensor
|
|
215
|
+
and performs the summation over the indices corresponding to the subsystems being traced out.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
rho (np.ndarray): The input density matrix to be traced out.
|
|
219
|
+
keep (set[int]): A list of indices corresponding to the subsystems to retain.
|
|
220
|
+
The order of the indices in 'keep' is not important, since dimensions will
|
|
221
|
+
be returned in the tensor original order, but the indices must be unique.
|
|
222
|
+
dims (list[int]): A list specifying the dimensions of each subsystem.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
np.ndarray: The resulting tensor after tracing out the specified subsystems.
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
ValueError: If the number of subsystems exceeds the available ASCII letters.
|
|
229
|
+
"""
|
|
230
|
+
# Check that the number of subsystems is not too large, that we run out of ascii letters.
|
|
231
|
+
needed, MAX_LABELS = len(dims) + len(keep), len(string.ascii_letters)
|
|
232
|
+
if needed > MAX_LABELS:
|
|
233
|
+
raise ValueError(f"Not enough einsum labels (dims + keep): need {needed}, but only {MAX_LABELS} available.")
|
|
169
234
|
|
|
170
235
|
# Use letters from the ASCII alphabet (both cases) for einsum indices.
|
|
171
236
|
# For each subsystem, assign two letters: one for the row index and one for the column index.
|
|
@@ -196,15 +261,7 @@ class QuantumObject:
|
|
|
196
261
|
# Reshape rho into a tensor with shape dims + dims.
|
|
197
262
|
reshaped = rho.reshape(dims + dims)
|
|
198
263
|
# Use einsum to sum over the indices that appear twice (i.e. those being traced out).
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
# The resulting tensor has separate indices for each subsystem kept.
|
|
202
|
-
# Reshape it into a matrix (i.e. combine the row indices and column indices).
|
|
203
|
-
dims_keep = [dims[i] for i in keep]
|
|
204
|
-
new_dim = np.prod(dims_keep)
|
|
205
|
-
reduced_matrix = reduced_tensor.reshape(new_dim, new_dim)
|
|
206
|
-
|
|
207
|
-
return QuantumObject(reduced_matrix)
|
|
264
|
+
return np.einsum(f"{input_subscript}->{output_subscript}", reshaped)
|
|
208
265
|
|
|
209
266
|
def norm(self, order: int | Literal["fro", "tr"] = 1) -> float:
|
|
210
267
|
"""
|
|
@@ -282,6 +339,7 @@ class QuantumObject:
|
|
|
282
339
|
|
|
283
340
|
Raises:
|
|
284
341
|
ValueError: If the QuantumObject is a scalar, as a density matrix cannot be derived.
|
|
342
|
+
ValueError: If the QuantumObject is an operator that is not a density matrix.
|
|
285
343
|
|
|
286
344
|
Returns:
|
|
287
345
|
QuantumObject: A new QuantumObject representing the density matrix.
|
|
@@ -289,15 +347,20 @@ class QuantumObject:
|
|
|
289
347
|
if self.is_scalar():
|
|
290
348
|
raise ValueError("Cannot make a density matrix from scalar.")
|
|
291
349
|
|
|
292
|
-
if self.is_density_matrix():
|
|
293
|
-
return self
|
|
294
|
-
|
|
295
350
|
if self.is_bra():
|
|
296
351
|
return (self.adjoint() @ self).unit()
|
|
297
352
|
|
|
298
353
|
if self.is_ket():
|
|
299
354
|
return (self @ self.adjoint()).unit()
|
|
300
355
|
|
|
356
|
+
if self.is_density_matrix():
|
|
357
|
+
return self
|
|
358
|
+
|
|
359
|
+
if self.is_operator():
|
|
360
|
+
raise ValueError(
|
|
361
|
+
"Cannot make a density matrix from an operator, which is not a density matrix already (trace=1 and hermitian)."
|
|
362
|
+
)
|
|
363
|
+
|
|
301
364
|
raise ValueError(
|
|
302
365
|
"Cannot make a density matrix from this QuantumObject. "
|
|
303
366
|
"It must be either a ket, a bra or already a density matrix."
|
qilisdk/extras/__init__.py
CHANGED
|
@@ -20,7 +20,7 @@ __all__ = []
|
|
|
20
20
|
OPTIONAL_FEATURES: list[OptionalFeature] = [
|
|
21
21
|
OptionalFeature(
|
|
22
22
|
name="cuda",
|
|
23
|
-
dependencies=["
|
|
23
|
+
dependencies=["cuda-quantum-cu12"],
|
|
24
24
|
symbols=[Symbol(path="qilisdk.extras.cuda.cuda_backend", name="CudaBackend")],
|
|
25
25
|
),
|
|
26
26
|
OptionalFeature(
|
|
@@ -17,9 +17,8 @@ from typing import TYPE_CHECKING, Callable, Type, TypeVar
|
|
|
17
17
|
|
|
18
18
|
import cudaq
|
|
19
19
|
import numpy as np
|
|
20
|
-
from cudaq import State
|
|
21
|
-
from cudaq
|
|
22
|
-
from cudaq.operator import Schedule as cuda_schedule
|
|
20
|
+
from cudaq import ElementaryOperator, OperatorSum, ScalarOperator, State, evolve, spin
|
|
21
|
+
from cudaq import Schedule as cuda_schedule
|
|
23
22
|
|
|
24
23
|
from qilisdk.analog.analog_backend import AnalogBackend
|
|
25
24
|
from qilisdk.analog.hamiltonian import Hamiltonian, PauliI, PauliOperator, PauliX, PauliY, PauliZ
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qilisdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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
|
Author-email: Qilimanjaro Quantum Tech <info@qilimanjaro.tech>
|
|
6
6
|
License-File: LICENCE
|
|
@@ -26,7 +26,7 @@ Requires-Dist: pydantic>=2.10.6
|
|
|
26
26
|
Requires-Dist: ruamel-yaml>=0.18.10
|
|
27
27
|
Requires-Dist: scipy>=1.15.1
|
|
28
28
|
Provides-Extra: cuda
|
|
29
|
-
Requires-Dist:
|
|
29
|
+
Requires-Dist: cuda-quantum-cu12; extra == 'cuda'
|
|
30
30
|
Provides-Extra: qaas
|
|
31
31
|
Requires-Dist: httpx>=0.28.1; extra == 'qaas'
|
|
32
32
|
Requires-Dist: keyring>=25.6.0; extra == 'qaas'
|
|
@@ -227,7 +227,7 @@ For analog simulations, the new `TimeEvolution` and `Schedule` classes allow you
|
|
|
227
227
|
|
|
228
228
|
```python
|
|
229
229
|
import numpy as np
|
|
230
|
-
from qilisdk.analog import
|
|
230
|
+
from qilisdk.analog import Schedule, TimeEvolution, ket, X, Z, Y, tensor_prod
|
|
231
231
|
from qilisdk.extras import CudaBackend
|
|
232
232
|
|
|
233
233
|
T = 10 # Total evolution time
|
|
@@ -251,16 +251,15 @@ schedule = Schedule(
|
|
|
251
251
|
)
|
|
252
252
|
|
|
253
253
|
# Prepare an initial state (equal superposition)
|
|
254
|
-
state =
|
|
254
|
+
state = tensor_prod([(ket(0) + ket(1)).unit() for _ in range(nqubits)]).unit()
|
|
255
255
|
|
|
256
256
|
# Perform time evolution on the CUDA backend with observables to monitor
|
|
257
257
|
time_evolution = TimeEvolution(
|
|
258
|
-
backend=CudaBackend(),
|
|
259
258
|
schedule=schedule,
|
|
260
259
|
initial_state=state,
|
|
261
260
|
observables=[Z(0), X(0), Y(0)],
|
|
262
261
|
)
|
|
263
|
-
results = time_evolution.evolve(store_intermediate_results=True)
|
|
262
|
+
results = time_evolution.evolve(backend=CudaBackend(), store_intermediate_results=True)
|
|
264
263
|
print("Time Evolution Results:", results)
|
|
265
264
|
```
|
|
266
265
|
|
|
@@ -9,7 +9,7 @@ qilisdk/analog/analog_backend.py,sha256=GRCqlCslo8F0iB8N6Mn-_0IFJos6xRbadwVvk3lY
|
|
|
9
9
|
qilisdk/analog/analog_result.py,sha256=aaTGQmKm8-cuFvs_lhCAP0JOaUxsmsNcVbn_M5rvkm4,4705
|
|
10
10
|
qilisdk/analog/exceptions.py,sha256=66nF1b3God6LgJ1IQd0thC3iyr6z1iMcZTfmDsSr4LE,686
|
|
11
11
|
qilisdk/analog/hamiltonian.py,sha256=ELQXIauk_foOQbOChTFIV4fYzTwqHhSP1tMw8PwytaQ,25126
|
|
12
|
-
qilisdk/analog/quantum_objects.py,sha256=
|
|
12
|
+
qilisdk/analog/quantum_objects.py,sha256=toYJZmgnAq06vHSpJbyl6bXEC1jRLjBrN9tJ2lV_9bc,24478
|
|
13
13
|
qilisdk/analog/schedule.py,sha256=AHpiBPN1YZb9J2WNw6MyRPjRWAyqLVIiGFj5y_rwDmA,12807
|
|
14
14
|
qilisdk/common/__init__.py,sha256=sB9yxB1512KdJuWGSHx7TWvIxLMFBX1m7zm6NVkWEAA,657
|
|
15
15
|
qilisdk/common/algorithm.py,sha256=_MBdMHIJogTPnR8QRCvpf364lxQNDqGWWyZpsgplcyI,636
|
|
@@ -27,11 +27,11 @@ qilisdk/digital/digital_result.py,sha256=PmqVOp4W1COZOFYcROxuLG7WAYjBAB6EM5BeY0J
|
|
|
27
27
|
qilisdk/digital/exceptions.py,sha256=21RnaSBkzZOuQWcIWVB303F4nyU1ABOykhAYow4m7lA,871
|
|
28
28
|
qilisdk/digital/gates.py,sha256=Ylf9iwDNONaatzIrrPpXi6H59C7bMvrq-tWyIK5ZykU,29614
|
|
29
29
|
qilisdk/digital/vqe.py,sha256=HdxueeBO6MQYKyy8xo2_x50hqHd693RPvXaxEFIcEJI,6648
|
|
30
|
-
qilisdk/extras/__init__.py,sha256=
|
|
30
|
+
qilisdk/extras/__init__.py,sha256=MdNUZ8Fd0hYFcRzUQWrhHGGEhdHlldd61fWL8iIPe_Y,1522
|
|
31
31
|
qilisdk/extras/__init__.pyi,sha256=xQ4zOPeSaNM5zGfb07fK4lcbAu4y95YfgSlHs5TkR3Y,717
|
|
32
32
|
qilisdk/extras/cuda/__init__.py,sha256=LDSTo7NstSbHMvvqvx7ZLxh0HFTEXg1O_B76SyEEHk8,588
|
|
33
33
|
qilisdk/extras/cuda/cuda_analog_result.py,sha256=0IoBZpkPfxS5CDps1Z6uRH1ATWG689klYR1RvGyUhBU,737
|
|
34
|
-
qilisdk/extras/cuda/cuda_backend.py,sha256=
|
|
34
|
+
qilisdk/extras/cuda/cuda_backend.py,sha256=AuoWuwlFQfloEE6krMP_1ZHsQZJyxwcnB7g56YTW1U0,16379
|
|
35
35
|
qilisdk/extras/cuda/cuda_digital_result.py,sha256=bgHCrmcUVCBngo443PhfLkMxAT8Kk24FRubU3bJsdsQ,742
|
|
36
36
|
qilisdk/extras/qaas/__init__.py,sha256=LDSTo7NstSbHMvvqvx7ZLxh0HFTEXg1O_B76SyEEHk8,588
|
|
37
37
|
qilisdk/extras/qaas/keyring.py,sha256=xjJj6wrRALj31foctYTGZ07wgusVyIGuzeKxU-MNAA0,1774
|
|
@@ -45,7 +45,7 @@ qilisdk/extras/qaas/qaas_vqe_result.py,sha256=tQUJ904aW7BzSzd-Dxi8TvL6AIQAnDsiOG
|
|
|
45
45
|
qilisdk/utils/__init__.py,sha256=cFdezrFwesp9azZEBG_CWq3_Qp1yH8do_PyJbIIdSkU,920
|
|
46
46
|
qilisdk/utils/openqasm2.py,sha256=QGQi2rrkYB_cqRgCyp3V3uDyfnVvcdMxykIiK0-sqXM,8316
|
|
47
47
|
qilisdk/utils/serialization.py,sha256=vp-q2SZ9cBq3NX-gfT3ZDt0tzF3KnxkhV0cM7imJ2zo,3870
|
|
48
|
-
qilisdk-0.1.
|
|
49
|
-
qilisdk-0.1.
|
|
50
|
-
qilisdk-0.1.
|
|
51
|
-
qilisdk-0.1.
|
|
48
|
+
qilisdk-0.1.4.dist-info/METADATA,sha256=TyXixdKgm05tUHh5HArhbZh5U2cUE63hGTFPY6msU1M,18016
|
|
49
|
+
qilisdk-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
50
|
+
qilisdk-0.1.4.dist-info/licenses/LICENCE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
51
|
+
qilisdk-0.1.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|