qilisdk 0.1.1__tar.gz → 0.1.3__tar.gz
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-0.1.1 → qilisdk-0.1.3}/.github/workflows/code_quality.yml +3 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/.github/workflows/tests.yml +3 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/CHANGELOG.md +38 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/PKG-INFO +4 -4
- {qilisdk-0.1.1 → qilisdk-0.1.3}/pyproject.toml +4 -4
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/analog/__init__.py +4 -4
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/analog/quantum_objects.py +135 -88
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/ansatz.py +18 -1
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/vqe.py +1 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/__init__.py +1 -1
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/cuda/cuda_backend.py +3 -3
- qilisdk-0.1.3/src/qilisdk/extras/qaas/models.py +132 -0
- qilisdk-0.1.3/src/qilisdk/extras/qaas/qaas_backend.py +255 -0
- qilisdk-0.1.3/src/qilisdk/extras/qaas/qaas_time_evolution_result.py +20 -0
- qilisdk-0.1.3/src/qilisdk/extras/qaas/qaas_vqe_result.py +20 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/yaml.py +34 -2
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/analog/test_quantum_objects.py +11 -11
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/utils/test_serialization.py +2 -2
- {qilisdk-0.1.1 → qilisdk-0.1.3}/uv.lock +763 -1067
- qilisdk-0.1.1/src/qilisdk/extras/qaas/models.py +0 -57
- qilisdk-0.1.1/src/qilisdk/extras/qaas/qaas_backend.py +0 -154
- {qilisdk-0.1.1 → qilisdk-0.1.3}/.github/workflows/publish.yml +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/.gitignore +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/.pre-commit-config.yaml +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/.python-version +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/LICENCE +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/README.md +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/__init__.pyi +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/_optionals.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/analog/algorithms.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/analog/analog_backend.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/analog/analog_result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/analog/exceptions.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/analog/hamiltonian.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/analog/schedule.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/common/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/common/algorithm.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/common/backend.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/common/model.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/common/optimizer.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/common/optimizer_result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/common/result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/circuit.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/digital_algorithm.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/digital_backend.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/digital_result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/exceptions.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/digital/gates.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/__init__.pyi +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/cuda/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/cuda/cuda_analog_result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/cuda/cuda_digital_result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/qaas/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/qaas/keyring.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/qaas/qaas_analog_result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/qaas/qaas_digital_result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/extras/qaas/qaas_settings.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/py.typed +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/utils/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/utils/openqasm2.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/src/qilisdk/utils/serialization.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/analog/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/analog/test_analog_result.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/analog/test_hamiltionian.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/analog/test_schedule.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/analog/test_time_evolution.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/common/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/common/test_scipy_optimizer.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/digital/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/digital/test_ansatz.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/digital/test_circuit.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/digital/test_gates.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/digital/test_vqe.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/extras/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/extras/test_cuda_backend.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/test_placeholder.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/utils/__init__.py +0 -0
- {qilisdk-0.1.1 → qilisdk-0.1.3}/tests/utils/test_openqasm2.py +0 -0
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
# Qilisdk 0.1.3 (2025-05-07)
|
|
2
|
+
|
|
3
|
+
### Bugfixes
|
|
4
|
+
|
|
5
|
+
- Made `pydantic` pass to be a mandatory requirement, and not only for qaas as before. Solving a problem with installation overseen in previous PRs.
|
|
6
|
+
|
|
7
|
+
([PR #29](https://github.com/qilimanjaro-tech/qilisdk/pulls/29))
|
|
8
|
+
|
|
9
|
+
- Made several small changes to the `QuantumObject` class and logic. The two main changes are:
|
|
10
|
+
- The first concerns the trace norm which was incorrectly implemented before.
|
|
11
|
+
- The second concerns changing the modulus of 2 check for the Hilbert Space size, to a a pow(2) check.
|
|
12
|
+
|
|
13
|
+
([PR #30](https://github.com/qilimanjaro-tech/qilisdk/pulls/30))
|
|
14
|
+
|
|
15
|
+
- Solved problems with ``Cudaq`` backend:
|
|
16
|
+
- Updated ``Cudaq`` to version 0.10.0 to fix issues encountered in version 0.9.1
|
|
17
|
+
- Migrated ``CudaBackend`` to use the new version of ``Cudaq``
|
|
18
|
+
|
|
19
|
+
([PR #31](https://github.com/qilimanjaro-tech/qilisdk/pulls/31))
|
|
20
|
+
|
|
21
|
+
### Misc
|
|
22
|
+
|
|
23
|
+
- Transformed hardcoded `PUBLIC URL` into an environment variable lookup that defaults to the hardcoded value
|
|
24
|
+
|
|
25
|
+
([PR #32](https://github.com/qilimanjaro-tech/qilisdk/pulls/32))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Qilisdk 0.1.2 (2025-04-22)
|
|
29
|
+
|
|
30
|
+
### Misc
|
|
31
|
+
|
|
32
|
+
- Improved `QaaSBacked` functionality to include methods for executing digital and analog algorithms.
|
|
33
|
+
|
|
34
|
+
[PR #27](https://github.com/qilimanjaro-tech/qilisdk/pulls/27)
|
|
35
|
+
|
|
36
|
+
|
|
1
37
|
# Qilisdk 0.1.1 (2025-04-11)
|
|
2
38
|
|
|
3
39
|
### Misc
|
|
@@ -57,6 +93,7 @@
|
|
|
57
93
|
```
|
|
58
94
|
|
|
59
95
|
([PR #2](https://github.com/qilimanjaro-tech/qilisdk/pulls/2))
|
|
96
|
+
|
|
60
97
|
- Introduces the `Hamiltonian` class as a central component for Pauli-based operator arithmetic, with a flyweight pattern for single-qubit operators. Internally stores terms as a dictionary mapping tuples of `PauliOperator` objects to complex coefficients.
|
|
61
98
|
|
|
62
99
|
**Key Features**
|
|
@@ -121,6 +158,7 @@
|
|
|
121
158
|
This release provides a robust framework for Pauli-operator arithmetic, scalar integration, and canonical simplification, forming a foundation for higher-level quantum analog functionality.
|
|
122
159
|
|
|
123
160
|
([PR #3](https://github.com/qilimanjaro-tech/qilisdk/pulls/3))
|
|
161
|
+
|
|
124
162
|
- Added the `Optimizer` abstract base class and its concrete subclass `SciPyOptimizer`. The `SciPyOptimizer` class wraps `scipy.optimize.minimize` to optimize cost functions while supporting extra keyword arguments such as Jacobian, bounds, etc. This implementation provides a structured way to perform optimization and access optimal parameters via the `optimal_parameters` property.
|
|
125
163
|
|
|
126
164
|
### Code Example
|
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "qilisdk"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.3"
|
|
4
4
|
description = "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
|
readme = "README.md"
|
|
6
6
|
authors = [{name = "Qilimanjaro Quantum Tech", email = "info@qilimanjaro.tech"}]
|
|
@@ -26,17 +26,17 @@ dependencies = [
|
|
|
26
26
|
"numpy>=2.2.4",
|
|
27
27
|
"ruamel-yaml>=0.18.10",
|
|
28
28
|
"scipy>=1.15.1",
|
|
29
|
+
"pydantic>=2.10.6",
|
|
30
|
+
"pydantic-settings>=2.8.0",
|
|
29
31
|
]
|
|
30
32
|
|
|
31
33
|
[project.optional-dependencies]
|
|
32
34
|
cuda = [
|
|
33
|
-
"cudaq==0.
|
|
35
|
+
"cudaq==0.10.0",
|
|
34
36
|
]
|
|
35
37
|
qaas = [
|
|
36
38
|
"httpx>=0.28.1",
|
|
37
39
|
"keyring>=25.6.0",
|
|
38
|
-
"pydantic>=2.10.6",
|
|
39
|
-
"pydantic-settings>=2.8.0",
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
[dependency-groups]
|
|
@@ -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]
|
|
@@ -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
|
"""
|
|
@@ -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
|
]
|