qilisdk 0.1.1__py3-none-any.whl → 0.1.3__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 +4 -4
- qilisdk/analog/quantum_objects.py +135 -88
- qilisdk/digital/ansatz.py +18 -1
- qilisdk/digital/vqe.py +1 -0
- qilisdk/extras/__init__.py +1 -1
- qilisdk/extras/cuda/cuda_backend.py +3 -3
- qilisdk/extras/qaas/models.py +76 -1
- qilisdk/extras/qaas/qaas_backend.py +120 -19
- qilisdk/extras/qaas/qaas_time_evolution_result.py +20 -0
- qilisdk/extras/qaas/qaas_vqe_result.py +20 -0
- qilisdk/yaml.py +34 -2
- {qilisdk-0.1.1.dist-info → qilisdk-0.1.3.dist-info}/METADATA +4 -4
- {qilisdk-0.1.1.dist-info → qilisdk-0.1.3.dist-info}/RECORD +15 -13
- {qilisdk-0.1.1.dist-info → qilisdk-0.1.3.dist-info}/WHEEL +0 -0
- {qilisdk-0.1.1.dist-info → qilisdk-0.1.3.dist-info}/licenses/LICENCE +0 -0
qilisdk/analog/__init__.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
from .algorithms import TimeEvolution
|
|
16
16
|
from .hamiltonian import Hamiltonian, I, X, Y, Z
|
|
17
|
-
from .quantum_objects import QuantumObject,
|
|
17
|
+
from .quantum_objects import QuantumObject, basis_state, bra, expect_val, ket, tensor_prod
|
|
18
18
|
from .schedule import Schedule
|
|
19
19
|
|
|
20
20
|
__all__ = [
|
|
@@ -26,9 +26,9 @@ __all__ = [
|
|
|
26
26
|
"X",
|
|
27
27
|
"Y",
|
|
28
28
|
"Z",
|
|
29
|
-
"
|
|
29
|
+
"basis_state",
|
|
30
30
|
"bra",
|
|
31
|
-
"
|
|
31
|
+
"expect_val",
|
|
32
32
|
"ket",
|
|
33
|
-
"
|
|
33
|
+
"tensor_prod",
|
|
34
34
|
]
|
|
@@ -24,7 +24,11 @@ from scipy.sparse.linalg import norm as scipy_norm
|
|
|
24
24
|
from qilisdk.yaml import yaml
|
|
25
25
|
|
|
26
26
|
Complex = int | float | complex
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
###############################################################################
|
|
30
|
+
# Main Class Definition
|
|
31
|
+
###############################################################################
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
@yaml.register_class
|
|
@@ -39,9 +43,9 @@ class QuantumObject:
|
|
|
39
43
|
|
|
40
44
|
The internal data is stored as a SciPy CSR (Compressed Sparse Row) matrix for
|
|
41
45
|
efficient arithmetic and manipulation. The expected shapes for the data are:
|
|
42
|
-
- (2**N, 2**N) for operators or density matrices,
|
|
46
|
+
- (2**N, 2**N) for operators or density matrices (or scalars),
|
|
43
47
|
- (2**N, 1) for ket states,
|
|
44
|
-
- (1, 2**N) for bra states.
|
|
48
|
+
- (1, 2**N) or (2**N,) for bra states.
|
|
45
49
|
"""
|
|
46
50
|
|
|
47
51
|
def __init__(self, data: np.ndarray | sparray | spmatrix) -> None:
|
|
@@ -50,10 +54,12 @@ class QuantumObject:
|
|
|
50
54
|
|
|
51
55
|
Converts a NumPy array to a CSR matrix if needed and validates the shape of the input.
|
|
52
56
|
The input must represent a valid quantum state or operator with appropriate dimensions.
|
|
57
|
+
Notice that 1D arrays of shape (2N,) are considered/transformed to bras with shape (1, 2N).
|
|
53
58
|
|
|
54
59
|
Args:
|
|
55
60
|
data (np.ndarray | sparray | spmatrix): A dense NumPy array or a SciPy sparse matrix
|
|
56
|
-
representing a quantum state or operator.
|
|
61
|
+
representing a quantum state or operator. Should be of shape: (2**N, 2**N) for operators
|
|
62
|
+
(1, 2**N) for ket states, (2**N, 1) or (2**N,) for bra states, or (1, 1) for scalars.
|
|
57
63
|
|
|
58
64
|
Raises:
|
|
59
65
|
ValueError: If the input data is not a NumPy array or a SciPy sparse matrix,
|
|
@@ -65,21 +71,18 @@ class QuantumObject:
|
|
|
65
71
|
self._data = data.tocsr()
|
|
66
72
|
else:
|
|
67
73
|
raise ValueError("Input must be a NumPy array or a SciPy sparse matrix")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
or (
|
|
74
|
-
self._data.shape[1] == self._data.shape[0] and self._data.shape[0] % 2 != 0 and self._data.shape[0] != 1
|
|
75
|
-
)
|
|
76
|
-
)
|
|
77
|
-
if invalid_shape:
|
|
74
|
+
|
|
75
|
+
# Valid shapes are operators = (2**N, 2**N) (scalars included), bra's = (1, 2**N) / (2**N,), or ket's =(2**N, 1):
|
|
76
|
+
valid_shape = self.is_operator() or self.is_ket() or self.is_bra()
|
|
77
|
+
|
|
78
|
+
if len(self._data.shape) != 2 or not valid_shape: # noqa: PLR2004
|
|
78
79
|
raise ValueError(
|
|
79
80
|
"Dimension of data is wrong. expected data to have shape similar to (2**N, 2**N), (1, 2**N), (2**N, 1)",
|
|
80
81
|
f"but received {self._data.shape}",
|
|
81
82
|
)
|
|
82
83
|
|
|
84
|
+
# ------------- Properties --------------
|
|
85
|
+
|
|
83
86
|
@property
|
|
84
87
|
def data(self) -> csr_matrix:
|
|
85
88
|
"""
|
|
@@ -126,7 +129,9 @@ class QuantumObject:
|
|
|
126
129
|
"""
|
|
127
130
|
return self._data.shape
|
|
128
131
|
|
|
129
|
-
|
|
132
|
+
# ----------- Matrix Logic Operations ------------
|
|
133
|
+
|
|
134
|
+
def adjoint(self) -> QuantumObject:
|
|
130
135
|
"""
|
|
131
136
|
Compute the adjoint (conjugate transpose) of the QuantumObject.
|
|
132
137
|
|
|
@@ -141,9 +146,9 @@ class QuantumObject:
|
|
|
141
146
|
Compute the partial trace over subsystems not in 'keep'.
|
|
142
147
|
|
|
143
148
|
This method calculates the reduced density matrix by tracing out
|
|
144
|
-
the subsystems that are not specified in the 'keep' parameter.
|
|
145
|
-
input 'dims' represents the dimensions of each subsystem,
|
|
146
|
-
indicates the indices of the subsystems to be retained.
|
|
149
|
+
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 the subsystems to be retained.
|
|
147
152
|
|
|
148
153
|
Args:
|
|
149
154
|
dims (list[int]): A list specifying the dimensions of each subsystem.
|
|
@@ -162,26 +167,22 @@ class QuantumObject:
|
|
|
162
167
|
if rho.shape != (total_dim, total_dim):
|
|
163
168
|
raise ValueError("Dimension mismatch between provided dims and QuantumObject shape")
|
|
164
169
|
|
|
165
|
-
n = len(dims)
|
|
166
170
|
# Use letters from the ASCII alphabet (both cases) for einsum indices.
|
|
167
171
|
# For each subsystem, assign two letters: one for the row index and one for the column index.
|
|
168
|
-
row_letters = []
|
|
169
|
-
|
|
170
|
-
out_row = [] # Letters that will remain in the output for the row part.
|
|
171
|
-
out_col = [] # Letters for the column part.
|
|
172
|
+
row_letters, col_letters = [], []
|
|
173
|
+
out_row, out_col = [], [] # Letters that will remain in the output for the row part and for the column part.
|
|
172
174
|
letters = iter(string.ascii_letters)
|
|
173
175
|
|
|
174
|
-
for i in range(
|
|
176
|
+
for i in range(len(dims)):
|
|
175
177
|
if i in keep:
|
|
176
|
-
# For a subsystem we want to keep, use two different letters
|
|
177
|
-
r = next(letters)
|
|
178
|
-
c = next(letters)
|
|
178
|
+
# For a subsystem we want to keep, use two different letters (r, c)
|
|
179
|
+
r, c = next(letters), next(letters)
|
|
179
180
|
row_letters.append(r)
|
|
180
181
|
col_letters.append(c)
|
|
181
182
|
out_row.append(r)
|
|
182
183
|
out_col.append(c)
|
|
183
184
|
else:
|
|
184
|
-
# For subsystems to be traced out, assign the same letter so that those indices are summed.
|
|
185
|
+
# For subsystems to be traced out, assign the same letter (r, r) so that those indices are summed.
|
|
185
186
|
r = next(letters)
|
|
186
187
|
row_letters.append(r)
|
|
187
188
|
col_letters.append(r)
|
|
@@ -209,26 +210,34 @@ class QuantumObject:
|
|
|
209
210
|
"""
|
|
210
211
|
Compute the norm of the QuantumObject.
|
|
211
212
|
|
|
212
|
-
For density matrices, the norm order can be specified. For state vectors,
|
|
213
|
-
the norm is computed accordingly.
|
|
213
|
+
For density matrices, the norm order can be specified. For state vectors, the norm is computed accordingly.
|
|
214
214
|
|
|
215
215
|
Args:
|
|
216
216
|
order (int or {"fro", "tr"}, optional): The order of the norm.
|
|
217
|
-
|
|
218
|
-
the trace norm
|
|
217
|
+
Only applies if the QuantumObject represents a density matrix. Other than all the
|
|
218
|
+
orders accepted by scipy, it also accepts 'tr' for the trace norm. Defaults to 1.
|
|
219
|
+
|
|
220
|
+
Raises:
|
|
221
|
+
ValueError: If the QuantumObject is not a valid density matrix or state vector,
|
|
219
222
|
|
|
220
223
|
Returns:
|
|
221
224
|
float: The computed norm of the QuantumObject.
|
|
222
225
|
"""
|
|
223
226
|
if self.is_scalar():
|
|
224
227
|
return self.dense[0][0]
|
|
225
|
-
|
|
228
|
+
|
|
229
|
+
if self.is_density_matrix() or self.shape[0] == self.shape[1]:
|
|
226
230
|
if order == "tr":
|
|
227
|
-
return
|
|
231
|
+
return np.sum(np.abs(np.linalg.eigvalsh(self.dense)))
|
|
228
232
|
return scipy_norm(self._data, ord=order)
|
|
233
|
+
|
|
229
234
|
if self.is_bra():
|
|
230
235
|
return np.sqrt(self._data @ self._data.conj().T).toarray()[0, 0]
|
|
231
|
-
|
|
236
|
+
|
|
237
|
+
if self.is_ket():
|
|
238
|
+
return np.sqrt(self._data.conj().T @ self._data).toarray()[0, 0]
|
|
239
|
+
|
|
240
|
+
raise ValueError("The QuantumObject is not a valid density matrix or state vector. Cannot compute the norm.")
|
|
232
241
|
|
|
233
242
|
def unit(self, order: int | Literal["fro", "tr"] = "tr") -> QuantumObject:
|
|
234
243
|
"""
|
|
@@ -238,8 +247,8 @@ class QuantumObject:
|
|
|
238
247
|
|
|
239
248
|
Args:
|
|
240
249
|
order (int or {"fro", "tr"}, optional): The order of the norm to use for normalization.
|
|
241
|
-
|
|
242
|
-
the trace norm
|
|
250
|
+
Only applies if the QuantumObject represents a density matrix. Other than all the
|
|
251
|
+
orders accepted by scipy, it also accepts 'tr' for the trace norm. Defaults to "tr".
|
|
243
252
|
|
|
244
253
|
Raises:
|
|
245
254
|
ValueError: If the norm of the QuantumObject is 0, making normalization impossible.
|
|
@@ -250,6 +259,7 @@ class QuantumObject:
|
|
|
250
259
|
norm = self.norm(order=order)
|
|
251
260
|
if norm == 0:
|
|
252
261
|
raise ValueError("Cannot normalize a zero-norm Quantum Object")
|
|
262
|
+
|
|
253
263
|
return QuantumObject(self._data / norm)
|
|
254
264
|
|
|
255
265
|
def expm(self) -> QuantumObject:
|
|
@@ -261,6 +271,40 @@ class QuantumObject:
|
|
|
261
271
|
"""
|
|
262
272
|
return QuantumObject(expm(self._data))
|
|
263
273
|
|
|
274
|
+
def to_density_matrix(self) -> QuantumObject:
|
|
275
|
+
"""
|
|
276
|
+
Convert the QuantumObject to a density matrix.
|
|
277
|
+
|
|
278
|
+
If the QuantumObject represents a state vector (ket or bra), this method
|
|
279
|
+
calculates the corresponding density matrix by taking the outer product.
|
|
280
|
+
If the QuantumObject is already a density matrix, it is returned unchanged.
|
|
281
|
+
The resulting density matrix is normalized.
|
|
282
|
+
|
|
283
|
+
Raises:
|
|
284
|
+
ValueError: If the QuantumObject is a scalar, as a density matrix cannot be derived.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
QuantumObject: A new QuantumObject representing the density matrix.
|
|
288
|
+
"""
|
|
289
|
+
if self.is_scalar():
|
|
290
|
+
raise ValueError("Cannot make a density matrix from scalar.")
|
|
291
|
+
|
|
292
|
+
if self.is_density_matrix():
|
|
293
|
+
return self
|
|
294
|
+
|
|
295
|
+
if self.is_bra():
|
|
296
|
+
return (self.adjoint() @ self).unit()
|
|
297
|
+
|
|
298
|
+
if self.is_ket():
|
|
299
|
+
return (self @ self.adjoint()).unit()
|
|
300
|
+
|
|
301
|
+
raise ValueError(
|
|
302
|
+
"Cannot make a density matrix from this QuantumObject. "
|
|
303
|
+
"It must be either a ket, a bra or already a density matrix."
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# ----------- Checks for Matrices ------------
|
|
307
|
+
|
|
264
308
|
def is_ket(self) -> bool:
|
|
265
309
|
"""
|
|
266
310
|
Check if the QuantumObject represents a ket (column vector) state.
|
|
@@ -268,7 +312,7 @@ class QuantumObject:
|
|
|
268
312
|
Returns:
|
|
269
313
|
bool: True if the QuantumObject is a ket state, False otherwise.
|
|
270
314
|
"""
|
|
271
|
-
return self.shape[
|
|
315
|
+
return self.shape[1] == 1 and self.shape[0].bit_count() == 1
|
|
272
316
|
|
|
273
317
|
def is_bra(self) -> bool:
|
|
274
318
|
"""
|
|
@@ -277,7 +321,7 @@ class QuantumObject:
|
|
|
277
321
|
Returns:
|
|
278
322
|
bool: True if the QuantumObject is a bra state, False otherwise.
|
|
279
323
|
"""
|
|
280
|
-
return self.shape[
|
|
324
|
+
return self.shape[0] == 1 and self.shape[1].bit_count() == 1
|
|
281
325
|
|
|
282
326
|
def is_scalar(self) -> bool:
|
|
283
327
|
"""
|
|
@@ -286,14 +330,22 @@ class QuantumObject:
|
|
|
286
330
|
Returns:
|
|
287
331
|
bool: True if the QuantumObject is a scalar, False otherwise.
|
|
288
332
|
"""
|
|
289
|
-
return self.
|
|
333
|
+
return self.shape == (1, 1)
|
|
334
|
+
|
|
335
|
+
def is_operator(self) -> bool:
|
|
336
|
+
"""
|
|
337
|
+
Check if the QuantumObject is an operator (square matrix).
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
bool: True if the QuantumObject is an operator, False otherwise.
|
|
341
|
+
"""
|
|
342
|
+
return self._data.shape[1] == self._data.shape[0] and self._data.shape[0].bit_count() == 1
|
|
290
343
|
|
|
291
|
-
def
|
|
344
|
+
def is_density_matrix(self, tol: float = 1e-8) -> bool:
|
|
292
345
|
"""
|
|
293
346
|
Determine if the QuantumObject is a valid density matrix.
|
|
294
347
|
|
|
295
|
-
A valid density matrix must be square, Hermitian, positive semi-definite,
|
|
296
|
-
and have a trace equal to 1.
|
|
348
|
+
A valid density matrix must be square, Hermitian, positive semi-definite, and have a trace equal to 1.
|
|
297
349
|
|
|
298
350
|
Args:
|
|
299
351
|
tol (float, optional): The numerical tolerance for verifying Hermiticity,
|
|
@@ -303,11 +355,11 @@ class QuantumObject:
|
|
|
303
355
|
bool: True if the QuantumObject is a valid density matrix, False otherwise.
|
|
304
356
|
"""
|
|
305
357
|
# Check if rho is a square matrix
|
|
306
|
-
if
|
|
358
|
+
if not self.is_operator():
|
|
307
359
|
return False
|
|
308
360
|
|
|
309
361
|
# Check Hermitian condition: rho should be equal to its conjugate transpose
|
|
310
|
-
if not
|
|
362
|
+
if not self.is_hermitian(tol=tol):
|
|
311
363
|
return False
|
|
312
364
|
|
|
313
365
|
# Check if eigenvalues are non-negative (positive semi-definite)
|
|
@@ -318,7 +370,7 @@ class QuantumObject:
|
|
|
318
370
|
# Check if the trace is 1
|
|
319
371
|
return np.isclose(self._data.trace(), 1, atol=tol)
|
|
320
372
|
|
|
321
|
-
def
|
|
373
|
+
def is_hermitian(self, tol: float = 1e-8) -> bool:
|
|
322
374
|
"""
|
|
323
375
|
Check if the QuantumObject is Hermitian.
|
|
324
376
|
|
|
@@ -331,39 +383,20 @@ class QuantumObject:
|
|
|
331
383
|
"""
|
|
332
384
|
return np.allclose(self.dense, self._data.conj().T.toarray(), atol=tol)
|
|
333
385
|
|
|
334
|
-
|
|
335
|
-
"""
|
|
336
|
-
Convert the QuantumObject to a density matrix.
|
|
337
|
-
|
|
338
|
-
If the QuantumObject represents a state vector (ket or bra), this method
|
|
339
|
-
calculates the corresponding density matrix by taking the outer product.
|
|
340
|
-
If the QuantumObject is already a density matrix, it is returned unchanged.
|
|
341
|
-
The resulting density matrix is normalized.
|
|
342
|
-
|
|
343
|
-
Raises:
|
|
344
|
-
ValueError: If the QuantumObject is a scalar, as a density matrix cannot be derived.
|
|
345
|
-
|
|
346
|
-
Returns:
|
|
347
|
-
QuantumObject: A new QuantumObject representing the density matrix.
|
|
348
|
-
"""
|
|
349
|
-
if self.is_scalar():
|
|
350
|
-
raise ValueError("Cannot make a density matrix from scalar.")
|
|
351
|
-
if self.is_dm():
|
|
352
|
-
return self
|
|
353
|
-
if self.is_bra():
|
|
354
|
-
return (self.dag() @ self).unit()
|
|
355
|
-
return (self @ self.dag()).unit()
|
|
386
|
+
# ----------- Basic Arithmetic Operators ------------
|
|
356
387
|
|
|
357
388
|
def __add__(self, other: QuantumObject | Complex) -> QuantumObject:
|
|
358
389
|
if isinstance(other, QuantumObject):
|
|
359
390
|
return QuantumObject(self._data + other._data)
|
|
360
391
|
if isinstance(other, Complex) and other == 0:
|
|
361
392
|
return self
|
|
393
|
+
|
|
362
394
|
raise TypeError("Addition is only supported between QuantumState instances")
|
|
363
395
|
|
|
364
396
|
def __sub__(self, other: QuantumObject) -> QuantumObject:
|
|
365
397
|
if isinstance(other, QuantumObject):
|
|
366
398
|
return QuantumObject(self._data - other._data)
|
|
399
|
+
|
|
367
400
|
raise TypeError("Subtraction is only supported between QuantumState instances")
|
|
368
401
|
|
|
369
402
|
def __mul__(self, other: QuantumObject | Complex) -> QuantumObject:
|
|
@@ -371,11 +404,13 @@ class QuantumObject:
|
|
|
371
404
|
return QuantumObject(self._data * other)
|
|
372
405
|
if isinstance(other, QuantumObject):
|
|
373
406
|
return QuantumObject(self._data * other._data)
|
|
407
|
+
|
|
374
408
|
raise TypeError("Unsupported multiplication type")
|
|
375
409
|
|
|
376
410
|
def __matmul__(self, other: QuantumObject) -> QuantumObject:
|
|
377
411
|
if isinstance(other, QuantumObject):
|
|
378
412
|
return QuantumObject(self._data @ other._data)
|
|
413
|
+
|
|
379
414
|
raise TypeError("Dot product is only supported between QuantumState instances")
|
|
380
415
|
|
|
381
416
|
def __rmul__(self, other: QuantumObject | Complex) -> QuantumObject:
|
|
@@ -385,19 +420,23 @@ class QuantumObject:
|
|
|
385
420
|
return f"{self.dense}"
|
|
386
421
|
|
|
387
422
|
|
|
388
|
-
|
|
423
|
+
###############################################################################
|
|
424
|
+
# Outside class Function Definitions
|
|
425
|
+
###############################################################################
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def basis_state(n: int, N: int) -> QuantumObject:
|
|
389
429
|
"""
|
|
390
|
-
Generate the basis vector representation
|
|
430
|
+
Generate the n'th basis vector representation, on a N-size Hilbert space (N=2**num_qubits).
|
|
391
431
|
|
|
392
|
-
This function creates a column vector (ket) representing the Fock state |n⟩
|
|
393
|
-
in a Hilbert space of dimension N.
|
|
432
|
+
This function creates a column vector (ket) representing the Fock state |n⟩ in a Hilbert space of dimension N.
|
|
394
433
|
|
|
395
434
|
Args:
|
|
396
|
-
|
|
397
|
-
|
|
435
|
+
n (int): The desired number state (from 0 to N-1).
|
|
436
|
+
N (int): The dimension of the Hilbert space, has a value 2**num_qubits.
|
|
398
437
|
|
|
399
438
|
Returns:
|
|
400
|
-
QuantumObject: A QuantumObject representing the
|
|
439
|
+
QuantumObject: A QuantumObject representing the |n⟩'th basis state on a N-size Hilbert space (N=2**num_qubits).
|
|
401
440
|
"""
|
|
402
441
|
return QuantumObject(csc_array(([1], ([n], [0])), shape=(N, 1)))
|
|
403
442
|
|
|
@@ -406,9 +445,8 @@ def ket(*state: int) -> QuantumObject:
|
|
|
406
445
|
"""
|
|
407
446
|
Generate a ket state for a multi-qubit system.
|
|
408
447
|
|
|
409
|
-
This function creates a tensor product of individual qubit states (kets)
|
|
410
|
-
|
|
411
|
-
ket(0, 1) creates a two-qubit ket state |0⟩ ⊗ |1⟩.
|
|
448
|
+
This function creates a tensor product of individual qubit states (kets) based on the input values.
|
|
449
|
+
Each input must be either 0 or 1. For example, ket(0, 1) creates a two-qubit ket state |0⟩ ⊗ |1⟩.
|
|
412
450
|
|
|
413
451
|
Args:
|
|
414
452
|
*state (int): A sequence of integers representing the state of each qubit (0 or 1).
|
|
@@ -420,17 +458,17 @@ def ket(*state: int) -> QuantumObject:
|
|
|
420
458
|
QuantumObject: A QuantumObject representing the multi-qubit ket state.
|
|
421
459
|
"""
|
|
422
460
|
if any(s not in {0, 1} for s in state):
|
|
423
|
-
raise ValueError("the state can only
|
|
424
|
-
|
|
461
|
+
raise ValueError(f"the state can only contain 1s or 0s. But received: {state}")
|
|
462
|
+
|
|
463
|
+
return tensor_prod([QuantumObject(csc_array(([1], ([s], [0])), shape=(2, 1))) for s in state])
|
|
425
464
|
|
|
426
465
|
|
|
427
466
|
def bra(*state: int) -> QuantumObject:
|
|
428
467
|
"""
|
|
429
468
|
Generate a bra state for a multi-qubit system.
|
|
430
469
|
|
|
431
|
-
This function creates a tensor product of individual qubit states (bras)
|
|
432
|
-
|
|
433
|
-
bra(0, 1) creates a two-qubit bra state ⟨0| ⊗ ⟨1|.
|
|
470
|
+
This function creates a tensor product of individual qubit states (bras) based on the input values.
|
|
471
|
+
Each input must be either 0 or 1. For example, bra(0, 1) creates a two-qubit bra state ⟨0| ⊗ ⟨1|.
|
|
434
472
|
|
|
435
473
|
Args:
|
|
436
474
|
*state (int): A sequence of integers representing the state of each qubit (0 or 1).
|
|
@@ -442,11 +480,12 @@ def bra(*state: int) -> QuantumObject:
|
|
|
442
480
|
QuantumObject: A QuantumObject representing the multi-qubit bra state.
|
|
443
481
|
"""
|
|
444
482
|
if any(s not in {0, 1} for s in state):
|
|
445
|
-
raise ValueError("the state can only
|
|
446
|
-
return tensor([QuantumObject(csc_array(([1], ([0], [s])), shape=(1, 2))) for s in state])
|
|
483
|
+
raise ValueError(f"the state can only contain 1s or 0s. But received:: {state}")
|
|
447
484
|
|
|
485
|
+
return tensor_prod([QuantumObject(csc_array(([1], ([0], [s])), shape=(1, 2))) for s in state])
|
|
448
486
|
|
|
449
|
-
|
|
487
|
+
|
|
488
|
+
def tensor_prod(operators: list[QuantumObject]) -> QuantumObject:
|
|
450
489
|
"""
|
|
451
490
|
Calculate the tensor product of a list of QuantumObjects.
|
|
452
491
|
|
|
@@ -463,10 +502,11 @@ def tensor(operators: list[QuantumObject]) -> QuantumObject:
|
|
|
463
502
|
if len(operators) > 1:
|
|
464
503
|
for i in range(1, len(operators)):
|
|
465
504
|
out = kron(out, operators[i].data)
|
|
505
|
+
|
|
466
506
|
return QuantumObject(out)
|
|
467
507
|
|
|
468
508
|
|
|
469
|
-
def
|
|
509
|
+
def expect_val(operator: QuantumObject, state: QuantumObject) -> Complex:
|
|
470
510
|
"""
|
|
471
511
|
Calculate the expectation value of an operator with respect to a quantum state.
|
|
472
512
|
|
|
@@ -477,10 +517,17 @@ def expect(operator: QuantumObject, state: QuantumObject) -> Complex:
|
|
|
477
517
|
operator (QuantumObject): The quantum operator represented as a QuantumObject.
|
|
478
518
|
state (QuantumObject): The quantum state or density matrix represented as a QuantumObject.
|
|
479
519
|
|
|
520
|
+
Raises:
|
|
521
|
+
ValueError: If the operator is not a square matrix.
|
|
522
|
+
|
|
480
523
|
Returns:
|
|
481
524
|
Complex: The expectation value. The result is guaranteed to be real if the operator
|
|
482
525
|
is Hermitian, and may be complex otherwise.
|
|
483
526
|
"""
|
|
527
|
+
if not operator.is_operator():
|
|
528
|
+
raise ValueError("The operator must be a square matrix.")
|
|
529
|
+
|
|
484
530
|
if state.data.shape[1] == state.data.shape[0]:
|
|
485
531
|
return (operator @ state).dense.trace()
|
|
486
|
-
|
|
532
|
+
|
|
533
|
+
return (state.adjoint() @ operator @ state).dense[0, 0]
|
qilisdk/digital/ansatz.py
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
from abc import ABC, abstractmethod
|
|
15
|
-
from typing import ClassVar, Literal, Union
|
|
15
|
+
from typing import Any, ClassVar, Literal, Union
|
|
16
16
|
|
|
17
17
|
from qilisdk.digital.circuit import Circuit
|
|
18
18
|
from qilisdk.digital.gates import CNOT, CZ, U1, U2, U3, M
|
|
@@ -103,6 +103,23 @@ class HardwareEfficientAnsatz(Ansatz):
|
|
|
103
103
|
"grouped": self._construct_layer_grouped,
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
107
|
+
state = self.__dict__.copy()
|
|
108
|
+
# Exclude the mapping that contains bound methods (not serializable).
|
|
109
|
+
state.pop("construct_layer_handlers", None)
|
|
110
|
+
return state
|
|
111
|
+
|
|
112
|
+
def __setstate__(self, state) -> None: # noqa: ANN001
|
|
113
|
+
"""
|
|
114
|
+
Restore the object's state after deserialization and reinitialize any attributes that were omitted.
|
|
115
|
+
"""
|
|
116
|
+
self.__dict__.update(state)
|
|
117
|
+
# Reconstruct the mapping with the proper bound methods.
|
|
118
|
+
self.construct_layer_handlers = {
|
|
119
|
+
"interposed": self._construct_layer_interposed,
|
|
120
|
+
"grouped": self._construct_layer_grouped,
|
|
121
|
+
}
|
|
122
|
+
|
|
106
123
|
@property
|
|
107
124
|
def nparameters(self) -> int:
|
|
108
125
|
"""
|
qilisdk/digital/vqe.py
CHANGED
qilisdk/extras/__init__.py
CHANGED
|
@@ -25,7 +25,7 @@ OPTIONAL_FEATURES: list[OptionalFeature] = [
|
|
|
25
25
|
),
|
|
26
26
|
OptionalFeature(
|
|
27
27
|
name="qaas",
|
|
28
|
-
dependencies=["httpx", "keyring"
|
|
28
|
+
dependencies=["httpx", "keyring"],
|
|
29
29
|
symbols=[Symbol(path="qilisdk.extras.qaas.qaas_backend", name="QaaSBackend")],
|
|
30
30
|
),
|
|
31
31
|
]
|
|
@@ -277,7 +277,7 @@ class CudaBackend(DigitalBackend, AnalogBackend):
|
|
|
277
277
|
|
|
278
278
|
return CudaAnalogResult(
|
|
279
279
|
final_expected_values=np.array(
|
|
280
|
-
[exp_val.expectation() for exp_val in evolution_result.final_expectation_values()]
|
|
280
|
+
[exp_val.expectation() for exp_val in evolution_result.final_expectation_values()[0]]
|
|
281
281
|
),
|
|
282
282
|
expected_values=(
|
|
283
283
|
np.array(
|
|
@@ -287,12 +287,12 @@ class CudaBackend(DigitalBackend, AnalogBackend):
|
|
|
287
287
|
else None
|
|
288
288
|
),
|
|
289
289
|
final_state=(
|
|
290
|
-
QuantumObject(np.array(evolution_result.final_state())).
|
|
290
|
+
QuantumObject(np.array(evolution_result.final_state())).adjoint()
|
|
291
291
|
if evolution_result.final_state() is not None
|
|
292
292
|
else None
|
|
293
293
|
),
|
|
294
294
|
intermediate_states=(
|
|
295
|
-
[QuantumObject(np.array(state)).
|
|
295
|
+
[QuantumObject(np.array(state)).adjoint() for state in evolution_result.intermediate_states()]
|
|
296
296
|
if evolution_result.intermediate_states() is not None
|
|
297
297
|
else None
|
|
298
298
|
),
|
qilisdk/extras/qaas/models.py
CHANGED
|
@@ -16,9 +16,20 @@ from enum import Enum
|
|
|
16
16
|
|
|
17
17
|
from pydantic import BaseModel, ConfigDict, Field
|
|
18
18
|
|
|
19
|
+
from qilisdk.analog import Hamiltonian, QuantumObject, Schedule, TimeEvolution
|
|
20
|
+
from qilisdk.analog.hamiltonian import PauliOperator
|
|
21
|
+
from qilisdk.common.optimizer import Optimizer
|
|
22
|
+
from qilisdk.digital import VQE, Circuit
|
|
23
|
+
from qilisdk.yaml import yaml
|
|
24
|
+
|
|
25
|
+
from .qaas_analog_result import QaaSAnalogResult
|
|
26
|
+
from .qaas_digital_result import QaaSDigitalResult
|
|
27
|
+
from .qaas_time_evolution_result import QaaSTimeEvolutionResult
|
|
28
|
+
from .qaas_vqe_result import QaaSVQEResult
|
|
29
|
+
|
|
19
30
|
|
|
20
31
|
class QaaSModel(BaseModel):
|
|
21
|
-
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
|
|
32
|
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True, arbitrary_types_allowed=True)
|
|
22
33
|
|
|
23
34
|
|
|
24
35
|
class LoginPayload(BaseModel): ...
|
|
@@ -51,7 +62,71 @@ class DeviceStatus(str, Enum):
|
|
|
51
62
|
OFFLINE = "offline"
|
|
52
63
|
|
|
53
64
|
|
|
65
|
+
class DeviceType(str, Enum):
|
|
66
|
+
"""Device type"""
|
|
67
|
+
|
|
68
|
+
QUANTUM_DIGITAL = "quantum_device"
|
|
69
|
+
QUANTUM_ANALOG = "quantum_analog_device"
|
|
70
|
+
SIMULATOR = "simulator_device"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@yaml.register_class
|
|
54
74
|
class Device(QaaSModel):
|
|
55
75
|
id: int = Field(...)
|
|
56
76
|
name: str = Field(...)
|
|
57
77
|
status: DeviceStatus = Field(...)
|
|
78
|
+
type: DeviceType = Field(...)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ExecutePayloadType(str, Enum):
|
|
82
|
+
DIGITAL = "digital"
|
|
83
|
+
ANALOG = "analog"
|
|
84
|
+
VQE = "vqe"
|
|
85
|
+
TIME_EVOLUTION = "time_evolution"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@yaml.register_class
|
|
89
|
+
class DigitalPayload(QaaSModel):
|
|
90
|
+
circuit: Circuit = Field(...)
|
|
91
|
+
nshots: int = Field(...)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@yaml.register_class
|
|
95
|
+
class AnalogPayload(QaaSModel):
|
|
96
|
+
schedule: Schedule = Field(...)
|
|
97
|
+
initial_state: QuantumObject = Field(...)
|
|
98
|
+
observables: list[PauliOperator | Hamiltonian] = Field(...)
|
|
99
|
+
store_intermediate_results: bool = Field(...)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@yaml.register_class
|
|
103
|
+
class VQEPayload(QaaSModel):
|
|
104
|
+
vqe: VQE = Field(...)
|
|
105
|
+
optimizer: Optimizer = Field(...)
|
|
106
|
+
nshots: int = Field(...)
|
|
107
|
+
store_intermediate_results: bool = Field(...)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@yaml.register_class
|
|
111
|
+
class TimeEvolutionPayload(QaaSModel):
|
|
112
|
+
time_evolution: TimeEvolution = Field()
|
|
113
|
+
store_intermediate_results: bool = Field()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@yaml.register_class
|
|
117
|
+
class ExecutePayload(QaaSModel):
|
|
118
|
+
type: ExecutePayloadType = Field(...)
|
|
119
|
+
device_id: int = Field(...)
|
|
120
|
+
digital_payload: DigitalPayload | None = None
|
|
121
|
+
analog_payload: AnalogPayload | None = None
|
|
122
|
+
vqe_payload: VQEPayload | None = None
|
|
123
|
+
time_evolution_payload: TimeEvolutionPayload | None = None
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@yaml.register_class
|
|
127
|
+
class ExecuteResponse(QaaSModel):
|
|
128
|
+
type: ExecutePayloadType = Field(...)
|
|
129
|
+
digital_result: QaaSDigitalResult | None = None
|
|
130
|
+
analog_result: QaaSAnalogResult | None = None
|
|
131
|
+
vqe_result: QaaSVQEResult | None = None
|
|
132
|
+
time_evolution_result: QaaSTimeEvolutionResult | None = None
|
|
@@ -17,27 +17,39 @@ import json
|
|
|
17
17
|
import logging
|
|
18
18
|
from base64 import urlsafe_b64encode
|
|
19
19
|
from datetime import datetime, timezone
|
|
20
|
-
from
|
|
20
|
+
from os import environ
|
|
21
|
+
from typing import TYPE_CHECKING, cast
|
|
21
22
|
|
|
22
23
|
import httpx
|
|
23
|
-
from pydantic import ValidationError
|
|
24
|
+
from pydantic import TypeAdapter, ValidationError
|
|
24
25
|
|
|
25
26
|
from qilisdk.analog.analog_backend import AnalogBackend
|
|
26
27
|
from qilisdk.digital.digital_backend import DigitalBackend
|
|
27
28
|
|
|
28
29
|
from .keyring import delete_credentials, load_credentials, store_credentials
|
|
29
|
-
from .models import
|
|
30
|
+
from .models import (
|
|
31
|
+
AnalogPayload,
|
|
32
|
+
Device,
|
|
33
|
+
DigitalPayload,
|
|
34
|
+
ExecutePayload,
|
|
35
|
+
ExecutePayloadType,
|
|
36
|
+
ExecuteResponse,
|
|
37
|
+
TimeEvolutionPayload,
|
|
38
|
+
Token,
|
|
39
|
+
VQEPayload,
|
|
40
|
+
)
|
|
30
41
|
from .qaas_settings import QaaSSettings
|
|
31
42
|
|
|
32
43
|
if TYPE_CHECKING:
|
|
33
|
-
from qilisdk.analog
|
|
34
|
-
from qilisdk.analog.
|
|
35
|
-
from qilisdk.
|
|
36
|
-
from qilisdk.
|
|
37
|
-
from qilisdk.digital.circuit import Circuit
|
|
44
|
+
from qilisdk.analog import Hamiltonian, QuantumObject, Schedule, TimeEvolution
|
|
45
|
+
from qilisdk.analog.hamiltonian import PauliOperator
|
|
46
|
+
from qilisdk.common.optimizer import Optimizer
|
|
47
|
+
from qilisdk.digital import VQE, Circuit
|
|
38
48
|
|
|
39
49
|
from .qaas_analog_result import QaaSAnalogResult
|
|
40
50
|
from .qaas_digital_result import QaaSDigitalResult
|
|
51
|
+
from .qaas_time_evolution_result import QaaSTimeEvolutionResult
|
|
52
|
+
from .qaas_vqe_result import QaaSVQEResult
|
|
41
53
|
|
|
42
54
|
logging.basicConfig(
|
|
43
55
|
format="%(levelname)s [%(asctime)s] %(name)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.DEBUG
|
|
@@ -54,9 +66,7 @@ class QaaSBackend(DigitalBackend, AnalogBackend):
|
|
|
54
66
|
c) keyring (fallback).
|
|
55
67
|
"""
|
|
56
68
|
|
|
57
|
-
_api_url: str = "https://qilimanjaroqaas.ddns.net:8080/api/v1"
|
|
58
|
-
_authorization_request_url: str = "https://qilimanjaroqaas.ddns.net:8080/api/v1/authorisation-tokens"
|
|
59
|
-
_authorization_refresh_url: str = "https://qilimanjaroqaas.ddns.net:8080/api/v1/authorisation-tokens/refresh"
|
|
69
|
+
_api_url: str = environ.get("PUBLIC_API_URL", "https://qilimanjaroqaas.ddns.net:8080/api/v1")
|
|
60
70
|
|
|
61
71
|
def __init__(self) -> None:
|
|
62
72
|
"""
|
|
@@ -70,6 +80,14 @@ class QaaSBackend(DigitalBackend, AnalogBackend):
|
|
|
70
80
|
"Please call QaaSBackend.login(username, apikey) or ensure environment variables are set."
|
|
71
81
|
)
|
|
72
82
|
self._username, self._token = credentials
|
|
83
|
+
self._selected_device: Device | None = None
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def selected_device(self) -> Device | None:
|
|
87
|
+
return self._selected_device
|
|
88
|
+
|
|
89
|
+
def set_device(self, device: Device) -> None:
|
|
90
|
+
self._selected_device = device
|
|
73
91
|
|
|
74
92
|
@classmethod
|
|
75
93
|
def login(
|
|
@@ -105,7 +123,7 @@ class QaaSBackend(DigitalBackend, AnalogBackend):
|
|
|
105
123
|
encoded_assertion = urlsafe_b64encode(json.dumps(assertion, indent=2).encode("utf-8")).decode("utf-8")
|
|
106
124
|
with httpx.Client(timeout=10.0) as client:
|
|
107
125
|
response = client.post(
|
|
108
|
-
QaaSBackend.
|
|
126
|
+
QaaSBackend._api_url + "/authorisation-tokens",
|
|
109
127
|
json={
|
|
110
128
|
"grantType": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
111
129
|
"assertion": encoded_assertion,
|
|
@@ -134,12 +152,37 @@ class QaaSBackend(DigitalBackend, AnalogBackend):
|
|
|
134
152
|
headers={"X-Client-Version": "0.23.2", "Authorization": f"Bearer {self._token.access_token}"},
|
|
135
153
|
)
|
|
136
154
|
response.raise_for_status()
|
|
137
|
-
|
|
138
|
-
|
|
155
|
+
|
|
156
|
+
devices_list_adapter = TypeAdapter(list[Device])
|
|
157
|
+
devices = devices_list_adapter.validate_python(response.json()["items"])
|
|
158
|
+
|
|
159
|
+
# Previous two lines are the same as doing:
|
|
160
|
+
# response_json = response.json()
|
|
161
|
+
# devices = [Device(**item) for item in response_json["items"]]
|
|
162
|
+
|
|
139
163
|
return devices
|
|
140
164
|
|
|
165
|
+
def _ensure_device_selected(self) -> Device:
|
|
166
|
+
if self._selected_device is None:
|
|
167
|
+
raise ValueError("Device not selected.")
|
|
168
|
+
return self._selected_device
|
|
169
|
+
|
|
141
170
|
def execute(self, circuit: Circuit, nshots: int = 1000) -> QaaSDigitalResult:
|
|
142
|
-
|
|
171
|
+
device = self._ensure_device_selected()
|
|
172
|
+
payload = ExecutePayload(
|
|
173
|
+
type=ExecutePayloadType.DIGITAL,
|
|
174
|
+
device_id=device.id,
|
|
175
|
+
digital_payload=DigitalPayload(circuit=circuit, nshots=nshots),
|
|
176
|
+
)
|
|
177
|
+
with httpx.Client(timeout=20.0) as client:
|
|
178
|
+
response = client.post(
|
|
179
|
+
QaaSBackend._api_url + "/execute",
|
|
180
|
+
headers={"X-Client-Version": "0.23.2", "Authorization": f"Bearer {self._token.access_token}"},
|
|
181
|
+
json=payload.model_dump_json(),
|
|
182
|
+
)
|
|
183
|
+
response.raise_for_status()
|
|
184
|
+
execute_response = ExecuteResponse(**response.json())
|
|
185
|
+
return cast("QaaSDigitalResult", execute_response.digital_result)
|
|
143
186
|
|
|
144
187
|
def evolve(
|
|
145
188
|
self,
|
|
@@ -148,7 +191,65 @@ class QaaSBackend(DigitalBackend, AnalogBackend):
|
|
|
148
191
|
observables: list[PauliOperator | Hamiltonian],
|
|
149
192
|
store_intermediate_results: bool = False,
|
|
150
193
|
) -> QaaSAnalogResult:
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
194
|
+
device = self._ensure_device_selected()
|
|
195
|
+
payload = ExecutePayload(
|
|
196
|
+
type=ExecutePayloadType.ANALOG,
|
|
197
|
+
device_id=device.id,
|
|
198
|
+
analog_payload=AnalogPayload(
|
|
199
|
+
schedule=schedule,
|
|
200
|
+
initial_state=initial_state,
|
|
201
|
+
observables=observables,
|
|
202
|
+
store_intermediate_results=store_intermediate_results,
|
|
203
|
+
),
|
|
204
|
+
)
|
|
205
|
+
with httpx.Client(timeout=20.0) as client:
|
|
206
|
+
response = client.post(
|
|
207
|
+
QaaSBackend._api_url + "/execute",
|
|
208
|
+
headers={"X-Client-Version": "0.23.2", "Authorization": f"Bearer {self._token.access_token}"},
|
|
209
|
+
json=payload.model_dump_json(),
|
|
210
|
+
)
|
|
211
|
+
response.raise_for_status()
|
|
212
|
+
execute_response = ExecuteResponse(**response.json())
|
|
213
|
+
return cast("QaaSAnalogResult", execute_response.analog_result)
|
|
214
|
+
|
|
215
|
+
def run_vqe(
|
|
216
|
+
self, vqe: VQE, optimizer: Optimizer, nshots: int = 1000, store_intermediate_results: bool = False
|
|
217
|
+
) -> QaaSVQEResult:
|
|
218
|
+
device = self._ensure_device_selected()
|
|
219
|
+
payload = ExecutePayload(
|
|
220
|
+
type=ExecutePayloadType.VQE,
|
|
221
|
+
device_id=device.id,
|
|
222
|
+
vqe_payload=VQEPayload(
|
|
223
|
+
vqe=vqe, optimizer=optimizer, nshots=nshots, store_intermediate_results=store_intermediate_results
|
|
224
|
+
),
|
|
225
|
+
)
|
|
226
|
+
with httpx.Client(timeout=20.0) as client:
|
|
227
|
+
response = client.post(
|
|
228
|
+
QaaSBackend._api_url + "/execute",
|
|
229
|
+
headers={"X-Client-Version": "0.23.2", "Authorization": f"Bearer {self._token.access_token}"},
|
|
230
|
+
json=payload.model_dump_json(),
|
|
231
|
+
)
|
|
232
|
+
response.raise_for_status()
|
|
233
|
+
execute_response = ExecuteResponse(**response.json())
|
|
234
|
+
return cast("QaaSVQEResult", execute_response.vqe_result)
|
|
235
|
+
|
|
236
|
+
def run_time_evolution(
|
|
237
|
+
self, time_evolution: TimeEvolution, store_intermediate_results: bool = False
|
|
238
|
+
) -> QaaSTimeEvolutionResult:
|
|
239
|
+
device = self._ensure_device_selected()
|
|
240
|
+
payload = ExecutePayload(
|
|
241
|
+
type=ExecutePayloadType.TIME_EVOLUTION,
|
|
242
|
+
device_id=device.id,
|
|
243
|
+
time_evolution_payload=TimeEvolutionPayload(
|
|
244
|
+
time_evolution=time_evolution, store_intermediate_results=store_intermediate_results
|
|
245
|
+
),
|
|
246
|
+
)
|
|
247
|
+
with httpx.Client(timeout=20.0) as client:
|
|
248
|
+
response = client.post(
|
|
249
|
+
QaaSBackend._api_url + "/execute",
|
|
250
|
+
headers={"X-Client-Version": "0.23.2", "Authorization": f"Bearer {self._token.access_token}"},
|
|
251
|
+
json=payload.model_dump_json(),
|
|
252
|
+
)
|
|
253
|
+
response.raise_for_status()
|
|
254
|
+
execute_response = ExecuteResponse(**response.json())
|
|
255
|
+
return cast("QaaSTimeEvolutionResult", execute_response.time_evolution_result)
|
|
@@ -0,0 +1,20 @@
|
|
|
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 qilisdk.analog.analog_result import AnalogResult
|
|
16
|
+
from qilisdk.yaml import yaml
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@yaml.register_class
|
|
20
|
+
class QaaSTimeEvolutionResult(AnalogResult): ...
|
|
@@ -0,0 +1,20 @@
|
|
|
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 qilisdk.digital.vqe import VQEResult
|
|
16
|
+
from qilisdk.yaml import yaml
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@yaml.register_class
|
|
20
|
+
class QaaSVQEResult(VQEResult): ...
|
qilisdk/yaml.py
CHANGED
|
@@ -19,6 +19,7 @@ import types
|
|
|
19
19
|
|
|
20
20
|
import numpy as np
|
|
21
21
|
from dill import dumps, loads
|
|
22
|
+
from pydantic import BaseModel
|
|
22
23
|
from ruamel.yaml import YAML
|
|
23
24
|
|
|
24
25
|
|
|
@@ -39,7 +40,7 @@ def ndarray_constructor(constructor, node):
|
|
|
39
40
|
|
|
40
41
|
def function_representer(representer, data):
|
|
41
42
|
"""Represent a non-lambda function by serializing it."""
|
|
42
|
-
serialized_function = base64.b64encode(dumps(data)).decode("utf-8")
|
|
43
|
+
serialized_function = base64.b64encode(dumps(data, recurse=True)).decode("utf-8")
|
|
43
44
|
return representer.represent_scalar("!function", serialized_function)
|
|
44
45
|
|
|
45
46
|
|
|
@@ -51,7 +52,7 @@ def function_constructor(constructor, node):
|
|
|
51
52
|
|
|
52
53
|
def lambda_representer(representer, data):
|
|
53
54
|
"""Represent a lambda function by serializing its code."""
|
|
54
|
-
serialized_lambda = base64.b64encode(dumps(data)).decode("utf-8")
|
|
55
|
+
serialized_lambda = base64.b64encode(dumps(data, recurse=True)).decode("utf-8")
|
|
55
56
|
return representer.represent_scalar("!lambda", serialized_lambda)
|
|
56
57
|
|
|
57
58
|
|
|
@@ -62,6 +63,33 @@ def lambda_constructor(constructor, node):
|
|
|
62
63
|
return loads(serialized_lambda) # noqa: S301
|
|
63
64
|
|
|
64
65
|
|
|
66
|
+
def pydantic_model_representer(representer, data):
|
|
67
|
+
"""Representer for Pydantic Models."""
|
|
68
|
+
value = {"type": f"{data.__class__.__module__}.{data.__class__.__name__}", "data": data.model_dump()}
|
|
69
|
+
return representer.represent_mapping("!PydanticModel", value)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def pydantic_model_constructor(constructor, node):
|
|
73
|
+
"""Constructor for Pydantic Models."""
|
|
74
|
+
mapping = constructor.construct_mapping(node, deep=True)
|
|
75
|
+
model_type_str = mapping["type"]
|
|
76
|
+
data = mapping["data"]
|
|
77
|
+
module_name, class_name = model_type_str.rsplit(".", 1)
|
|
78
|
+
mod = __import__(module_name, fromlist=[class_name])
|
|
79
|
+
model_cls = getattr(mod, class_name)
|
|
80
|
+
return model_cls.model_validate(data)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def complex_representer(representer, data: complex):
|
|
84
|
+
value = {"real": data.real, "imag": data.imag}
|
|
85
|
+
return representer.represent_mapping("!complex", value)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def complex_constructor(constructor, node):
|
|
89
|
+
mapping = constructor.construct_mapping(node, deep=True)
|
|
90
|
+
return complex(mapping["real"], mapping["imag"])
|
|
91
|
+
|
|
92
|
+
|
|
65
93
|
yaml = YAML(typ="unsafe")
|
|
66
94
|
yaml.representer.add_representer(np.ndarray, ndarray_representer)
|
|
67
95
|
yaml.constructor.add_constructor("!ndarray", ndarray_constructor)
|
|
@@ -69,3 +97,7 @@ yaml.representer.add_representer(types.FunctionType, function_representer)
|
|
|
69
97
|
yaml.constructor.add_constructor("!function", function_constructor)
|
|
70
98
|
yaml.representer.add_representer(types.LambdaType, lambda_representer)
|
|
71
99
|
yaml.constructor.add_constructor("!lambda", lambda_constructor)
|
|
100
|
+
yaml.representer.add_representer(BaseModel, pydantic_model_representer)
|
|
101
|
+
yaml.constructor.add_constructor("!PydanticModel", pydantic_model_constructor)
|
|
102
|
+
yaml.representer.add_representer(complex, complex_representer)
|
|
103
|
+
yaml.constructor.add_constructor("!complex", complex_constructor)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qilisdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
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
|
|
@@ -21,15 +21,15 @@ Classifier: Topic :: Scientific/Engineering :: Quantum Computing
|
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: dill>=0.3.9
|
|
23
23
|
Requires-Dist: numpy>=2.2.4
|
|
24
|
+
Requires-Dist: pydantic-settings>=2.8.0
|
|
25
|
+
Requires-Dist: pydantic>=2.10.6
|
|
24
26
|
Requires-Dist: ruamel-yaml>=0.18.10
|
|
25
27
|
Requires-Dist: scipy>=1.15.1
|
|
26
28
|
Provides-Extra: cuda
|
|
27
|
-
Requires-Dist: cudaq==0.
|
|
29
|
+
Requires-Dist: cudaq==0.10.0; extra == 'cuda'
|
|
28
30
|
Provides-Extra: qaas
|
|
29
31
|
Requires-Dist: httpx>=0.28.1; extra == 'qaas'
|
|
30
32
|
Requires-Dist: keyring>=25.6.0; extra == 'qaas'
|
|
31
|
-
Requires-Dist: pydantic-settings>=2.8.0; extra == 'qaas'
|
|
32
|
-
Requires-Dist: pydantic>=2.10.6; extra == 'qaas'
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
|
|
35
35
|
# QiliSDK
|
|
@@ -2,14 +2,14 @@ qilisdk/__init__.py,sha256=iwacKJYttlh9DgQgNmnAjkU9CyjDQG42X3gr9Dg1kME,710
|
|
|
2
2
|
qilisdk/__init__.pyi,sha256=iwacKJYttlh9DgQgNmnAjkU9CyjDQG42X3gr9Dg1kME,710
|
|
3
3
|
qilisdk/_optionals.py,sha256=gl6yZ2sROY-USvNkXy6964OBOrDNKJAIyBU8D2H13z4,3594
|
|
4
4
|
qilisdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
qilisdk/yaml.py,sha256=
|
|
6
|
-
qilisdk/analog/__init__.py,sha256=
|
|
5
|
+
qilisdk/yaml.py,sha256=QTDiLB8-3GWOfFKxvKwfEtJjTYXRxGP3ldUAEERB51E,4055
|
|
6
|
+
qilisdk/analog/__init__.py,sha256=0J2XPJKMrvBM8jRa4nhxMKTB0fQ5kt4emb4lp4uv3Qs,1004
|
|
7
7
|
qilisdk/analog/algorithms.py,sha256=OiQma6y5ueNE1buXXbNexme1k1aQaIg1A00C6mX10JQ,4621
|
|
8
8
|
qilisdk/analog/analog_backend.py,sha256=GRCqlCslo8F0iB8N6Mn-_0IFJos6xRbadwVvk3lYilc,1753
|
|
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=jXKaPQV3ZV0so1BzzB9zCCAjOhgSLFCaTQtwGaYwwfU,20641
|
|
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
|
|
@@ -19,31 +19,33 @@ qilisdk/common/optimizer.py,sha256=jIBntTMw5qw03RRp5gzQe4BO9bh_z3Oeh_qfwbot0bI,5
|
|
|
19
19
|
qilisdk/common/optimizer_result.py,sha256=Xqrej4QfuDofya-MpyfXm-FIlJqrCu7worio5oqb3rQ,3990
|
|
20
20
|
qilisdk/common/result.py,sha256=wgNCC_M0eQMArWl5nmDc0-1N5IvW1sxSnIZa7_OllCE,633
|
|
21
21
|
qilisdk/digital/__init__.py,sha256=vAk9BGKUxKc5faOppR64kUnJxV2WKxJK_FGpGyxuheY,1144
|
|
22
|
-
qilisdk/digital/ansatz.py,sha256=
|
|
22
|
+
qilisdk/digital/ansatz.py,sha256=TG-GMDDb8v2WjkynhTRgHp5OUkGborLI4rmPAf33FPs,6546
|
|
23
23
|
qilisdk/digital/circuit.py,sha256=ruE1bJJkJrCKQgbBYU0-sNHWwEI1QKiUbK76owjb32M,3628
|
|
24
24
|
qilisdk/digital/digital_algorithm.py,sha256=ncrmqd_y__1aaOA5ttOLlwCNyIBNdEpBazLapVa54-w,744
|
|
25
25
|
qilisdk/digital/digital_backend.py,sha256=PH514eEEjPAo3R_JnzsG4DVrwssIMzWHJFjcpT440TU,3393
|
|
26
26
|
qilisdk/digital/digital_result.py,sha256=PmqVOp4W1COZOFYcROxuLG7WAYjBAB6EM5BeY0J-eRY,5293
|
|
27
27
|
qilisdk/digital/exceptions.py,sha256=21RnaSBkzZOuQWcIWVB303F4nyU1ABOykhAYow4m7lA,871
|
|
28
28
|
qilisdk/digital/gates.py,sha256=Ylf9iwDNONaatzIrrPpXi6H59C7bMvrq-tWyIK5ZykU,29614
|
|
29
|
-
qilisdk/digital/vqe.py,sha256=
|
|
30
|
-
qilisdk/extras/__init__.py,sha256=
|
|
29
|
+
qilisdk/digital/vqe.py,sha256=HdxueeBO6MQYKyy8xo2_x50hqHd693RPvXaxEFIcEJI,6648
|
|
30
|
+
qilisdk/extras/__init__.py,sha256=jAfA4d8AaTWXqAsQTpv_Rf1tQ0kcfQ_mmxF6rA-Mlro,1510
|
|
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=jnzdMkCUpQQhJ6S9dQgVTOJSciWcl5VhnG84VdZqgYA,16414
|
|
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
|
|
38
|
-
qilisdk/extras/qaas/models.py,sha256=
|
|
38
|
+
qilisdk/extras/qaas/models.py,sha256=Phf_j8LPmBUeUbe_StgM-aSz8o1HecCnfsjFbvMAlTY,3801
|
|
39
39
|
qilisdk/extras/qaas/qaas_analog_result.py,sha256=MLWKtRWLwKZl7Q-tWv615-P_LKFxJIRrK_ni_jQvaP8,738
|
|
40
|
-
qilisdk/extras/qaas/qaas_backend.py,sha256=
|
|
40
|
+
qilisdk/extras/qaas/qaas_backend.py,sha256=2S953of7KUZ_JBNuqPBHCGa3uOu6Jve3CQxoppJvZyc,10030
|
|
41
41
|
qilisdk/extras/qaas/qaas_digital_result.py,sha256=9EaZujlXvdmqdfVD-laDJq_I8YO5bc3XHRvEobOYR1U,743
|
|
42
42
|
qilisdk/extras/qaas/qaas_settings.py,sha256=Vl-OPs0ijht7GqxjztY-Id3nEinfE48j_J-d9mJ4Ctk,935
|
|
43
|
+
qilisdk/extras/qaas/qaas_time_evolution_result.py,sha256=sj9-xe1CbuO-yOkG5ac-f49CpIM4-X6tKnd9RBVfRMU,745
|
|
44
|
+
qilisdk/extras/qaas/qaas_vqe_result.py,sha256=tQUJ904aW7BzSzd-Dxi8TvL6AIQAnDsiOGuMB8fft58,720
|
|
43
45
|
qilisdk/utils/__init__.py,sha256=cFdezrFwesp9azZEBG_CWq3_Qp1yH8do_PyJbIIdSkU,920
|
|
44
46
|
qilisdk/utils/openqasm2.py,sha256=QGQi2rrkYB_cqRgCyp3V3uDyfnVvcdMxykIiK0-sqXM,8316
|
|
45
47
|
qilisdk/utils/serialization.py,sha256=vp-q2SZ9cBq3NX-gfT3ZDt0tzF3KnxkhV0cM7imJ2zo,3870
|
|
46
|
-
qilisdk-0.1.
|
|
47
|
-
qilisdk-0.1.
|
|
48
|
-
qilisdk-0.1.
|
|
49
|
-
qilisdk-0.1.
|
|
48
|
+
qilisdk-0.1.3.dist-info/METADATA,sha256=IwKsI6lbnMmSkdb-zRYDrGQOAwqTktRKOvVcP6N5jTs,18006
|
|
49
|
+
qilisdk-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
50
|
+
qilisdk-0.1.3.dist-info/licenses/LICENCE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
51
|
+
qilisdk-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|