qec 0.2.7__py3-none-any.whl → 0.2.9__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.
- qec/code_instances/saved_codes/steane.json +34 -0
- qec/code_instances/saved_codes/test.json +1 -0
- {qec-0.2.7.dist-info → qec-0.2.9.dist-info}/METADATA +1 -1
- qec-0.2.9.dist-info/RECORD +8 -0
- qec/code_constructions/__init__.py +0 -3
- qec/code_constructions/css_code.py +0 -773
- qec/code_constructions/hgp_code.py +0 -308
- qec/code_constructions/stabilizer_code.py +0 -591
- qec/code_instances/__init__.py +0 -1
- qec/code_instances/five_qubit_code.py +0 -67
- qec/codetables_de/__init__.py +0 -1
- qec/codetables_de/codetables_de.py +0 -93
- qec/utils/__init__.py +0 -0
- qec/utils/binary_pauli_utils.py +0 -403
- qec/utils/codetables_de_utils.py +0 -274
- qec/utils/sparse_binary_utils.py +0 -64
- qec-0.2.7.dist-info/RECORD +0 -18
- {qec-0.2.7.dist-info → qec-0.2.9.dist-info}/LICENSE +0 -0
- {qec-0.2.7.dist-info → qec-0.2.9.dist-info}/WHEEL +0 -0
- {qec-0.2.7.dist-info → qec-0.2.9.dist-info}/top_level.txt +0 -0
@@ -1,308 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
import scipy
|
3
|
-
from typing import Union, Tuple
|
4
|
-
import ldpc.mod2
|
5
|
-
import time
|
6
|
-
|
7
|
-
from qec.code_constructions import CSSCode
|
8
|
-
from qec.utils.sparse_binary_utils import convert_to_binary_scipy_sparse
|
9
|
-
|
10
|
-
|
11
|
-
class HypergraphProductCode(CSSCode):
|
12
|
-
"""
|
13
|
-
Implements a Hypergraph Product (HGP) code - derived from two classical linear binary codes.
|
14
|
-
|
15
|
-
Parameters
|
16
|
-
----------
|
17
|
-
seed_matrix_1 :
|
18
|
-
A classical linear binary code used as a "seed" in the HGP construction method.
|
19
|
-
seed_matrix_2 :
|
20
|
-
A classical linear binary code used as a "seed" in the HGP construction method.
|
21
|
-
name : str, default = None
|
22
|
-
The name of the code. If None, the name is set to: "Hypergraph product code"
|
23
|
-
|
24
|
-
Attributes
|
25
|
-
----------
|
26
|
-
seed_matrix_1 : scipy.sparse.spmatrix
|
27
|
-
The input seed_matrix_1 stored as a scipy sparse matrix.
|
28
|
-
seed_matrix_2 : scipy.sparse.spmatrix
|
29
|
-
The input seed_matrix_2 stored as a scipy sparse matrix.
|
30
|
-
_n1 : int
|
31
|
-
Number of columns in seed_matrix_1
|
32
|
-
_n2 : int
|
33
|
-
Number of columns in seed_matrix_2
|
34
|
-
_m1 : int
|
35
|
-
Number of rows in seed_matrix_1 (the number of columns of it's transpose)
|
36
|
-
_m2 : int
|
37
|
-
Number of rows in seed_matrix_2 (the number of columns of it's transpose)
|
38
|
-
|
39
|
-
Notes
|
40
|
-
-----
|
41
|
-
|
42
|
-
The X and Z stabilizer matrices are given by [1]_:
|
43
|
-
|
44
|
-
.. math::
|
45
|
-
|
46
|
-
\begin{align}
|
47
|
-
H_{X} &= \begin{pmatrix}
|
48
|
-
H_{1}\otimes I_{n_{2}} & \,\,I_{r_{1}}\otimes H_{2}^{T}
|
49
|
-
\end{pmatrix}\tag*{(1)}\\
|
50
|
-
H_{Z} &= \begin{pmatrix}
|
51
|
-
I_{n_{1}}\otimes H_{2} & \,\,H_{1}^{T}\otimes I_{r_{2}}
|
52
|
-
\end{pmatrix}~, \tag*{(2)}
|
53
|
-
\end{align}
|
54
|
-
|
55
|
-
where :math:`H_1` and :math:`H_2` correspond to the parity check matrix of the first and second "seed" codes.
|
56
|
-
|
57
|
-
|
58
|
-
.. [1] J.-P. Tillich and G. Zemor, “Quantum LDPC Codes With Positive Rate and Minimum Distance Proportional to the Square Root of the Blocklength”, IEEE Transactions on Information Theory 60, 1193 (2014)
|
59
|
-
"""
|
60
|
-
|
61
|
-
def __init__(
|
62
|
-
self,
|
63
|
-
seed_matrix_1: Union[np.ndarray, scipy.sparse.spmatrix],
|
64
|
-
seed_matrix_2: Union[np.ndarray, scipy.sparse.spmatrix],
|
65
|
-
name: str = None,
|
66
|
-
):
|
67
|
-
self.name = name if name else "Hypergraph product code"
|
68
|
-
|
69
|
-
if not all(
|
70
|
-
isinstance(seed_m, (np.ndarray, scipy.sparse.spmatrix))
|
71
|
-
for seed_m in (seed_matrix_1, seed_matrix_2)
|
72
|
-
):
|
73
|
-
raise TypeError(
|
74
|
-
"The seed matrices must be either numpy arrays or scipy sparse matrices."
|
75
|
-
)
|
76
|
-
|
77
|
-
self.seed_matrix_1 = convert_to_binary_scipy_sparse(seed_matrix_1)
|
78
|
-
self.seed_matrix_2 = convert_to_binary_scipy_sparse(seed_matrix_2)
|
79
|
-
|
80
|
-
# maybe move the below to a private _construct_stabilizer_matrices function?
|
81
|
-
# --------------------------------------------------------------------------
|
82
|
-
self._n1 = seed_matrix_1.shape[1]
|
83
|
-
self._n2 = seed_matrix_2.shape[1]
|
84
|
-
|
85
|
-
self._m1 = seed_matrix_1.shape[0]
|
86
|
-
self._m2 = seed_matrix_2.shape[0]
|
87
|
-
|
88
|
-
x_left = scipy.sparse.kron(seed_matrix_1, scipy.sparse.eye(self._n2))
|
89
|
-
x_right = scipy.sparse.kron(scipy.sparse.eye(self._m1), seed_matrix_2.T)
|
90
|
-
self.x_stabilizer_matrix = scipy.sparse.hstack([x_left, x_right])
|
91
|
-
|
92
|
-
z_left = scipy.sparse.kron(scipy.sparse.eye(self._n1), seed_matrix_2)
|
93
|
-
z_right = scipy.sparse.kron(seed_matrix_1.T, scipy.sparse.eye(self._m2))
|
94
|
-
self.z_stabilizer_matrix = scipy.sparse.hstack([z_left, z_right])
|
95
|
-
# --------------------------------------------------------------------------
|
96
|
-
|
97
|
-
super().__init__(self.x_stabilizer_matrix, self.z_stabilizer_matrix, self.name)
|
98
|
-
|
99
|
-
def compute_exact_code_distance(self) -> int:
|
100
|
-
"""
|
101
|
-
Computes the exact code distance of the HGP code.
|
102
|
-
|
103
|
-
Returns
|
104
|
-
-------
|
105
|
-
int
|
106
|
-
The distance of the code.
|
107
|
-
|
108
|
-
Notes
|
109
|
-
-----
|
110
|
-
The distance of a HGP code is given as:
|
111
|
-
|
112
|
-
.. math::
|
113
|
-
|
114
|
-
\min(d_1, d_2, d_1^T, d_2^T)
|
115
|
-
|
116
|
-
corresponding to the distance of the seed codes and the distance of their transposes.
|
117
|
-
"""
|
118
|
-
|
119
|
-
rank_seed_m1 = ldpc.mod2.rank(self.seed_matrix_1)
|
120
|
-
rank_seed_m2 = ldpc.mod2.rank(self.seed_matrix_2)
|
121
|
-
|
122
|
-
if self.seed_matrix_1.shape[1] != rank_seed_m1:
|
123
|
-
self.d1 = ldpc.mod2.compute_exact_code_distance(self.seed_matrix_1)
|
124
|
-
else:
|
125
|
-
self.d1 = np.inf
|
126
|
-
|
127
|
-
if self.seed_matrix_2.shape[1] != rank_seed_m2:
|
128
|
-
self.d2 = ldpc.mod2.compute_exact_code_distance(self.seed_matrix_2)
|
129
|
-
else:
|
130
|
-
self.d2 = np.inf
|
131
|
-
|
132
|
-
# note: rank(A) = rank(A^T):
|
133
|
-
if self.seed_matrix_1.shape[0] != rank_seed_m1:
|
134
|
-
self.d1T = ldpc.mod2.compute_exact_code_distance(self.seed_matrix_1.T)
|
135
|
-
else:
|
136
|
-
self.d1T = np.inf
|
137
|
-
|
138
|
-
if self.seed_matrix_2.shape[0] != rank_seed_m2:
|
139
|
-
self.d2T = ldpc.mod2.compute_exact_code_distance(self.seed_matrix_2.T)
|
140
|
-
else:
|
141
|
-
self.d2T = np.inf
|
142
|
-
|
143
|
-
self.x_code_distance = min(self.d1T, self.d2)
|
144
|
-
self.z_code_distance = min(self.d1, self.d2T)
|
145
|
-
self.code_distance = min(self.x_code_distance, self.z_code_distance)
|
146
|
-
|
147
|
-
return self.code_distance
|
148
|
-
|
149
|
-
def estimate_min_distance(self, timeout_seconds: float = 0.025) -> int:
|
150
|
-
"""
|
151
|
-
Estimate the minimum X and Z distance of the HGP code. Parameters
|
152
|
-
----------
|
153
|
-
timeout_seconds : float, optional
|
154
|
-
Time limit in seconds for the full search. Default: 0.25
|
155
|
-
|
156
|
-
Returns
|
157
|
-
-------
|
158
|
-
int
|
159
|
-
Best estimate of the (overall) code distance found within time limit.
|
160
|
-
|
161
|
-
"""
|
162
|
-
|
163
|
-
rank_seed_m1 = ldpc.mod2.rank(self.seed_matrix_1)
|
164
|
-
rank_seed_m2 = ldpc.mod2.rank(self.seed_matrix_2)
|
165
|
-
|
166
|
-
d1_timeout_seconds = timeout_seconds / 4
|
167
|
-
if self.seed_matrix_1.shape[1] != rank_seed_m1:
|
168
|
-
d1_start_time = time.time()
|
169
|
-
d1_min_estimate, _, _ = ldpc.mod2.estimate_code_distance(
|
170
|
-
self.seed_matrix_1, d1_timeout_seconds, 0
|
171
|
-
)
|
172
|
-
d1_run_time = time.time() - d1_start_time
|
173
|
-
else:
|
174
|
-
d1_min_estimate = np.inf
|
175
|
-
d1_run_time = 0
|
176
|
-
|
177
|
-
d1T_timeout_seconds = (
|
178
|
-
(d1_timeout_seconds * 4 - d1_run_time) / 3
|
179
|
-
if d1_run_time <= d1_timeout_seconds
|
180
|
-
else timeout_seconds / 4
|
181
|
-
)
|
182
|
-
if self.seed_matrix_1.shape[0] != rank_seed_m1:
|
183
|
-
d1T_start_time = time.time()
|
184
|
-
d1T_min_estimate, _, _ = ldpc.mod2.estimate_code_distance(
|
185
|
-
self.seed_matrix_1.T, d1T_timeout_seconds, 0
|
186
|
-
)
|
187
|
-
d1T_run_time = time.time() - d1T_start_time
|
188
|
-
else:
|
189
|
-
d1T_min_estimate = np.inf
|
190
|
-
d1T_run_time = 0
|
191
|
-
|
192
|
-
d2_timeout_seconds = (
|
193
|
-
(d1T_timeout_seconds * 3 - d1T_run_time) / 2
|
194
|
-
if d1T_run_time <= d1T_timeout_seconds
|
195
|
-
else timeout_seconds / 4
|
196
|
-
)
|
197
|
-
if self.seed_matrix_2.shape[1] != rank_seed_m2:
|
198
|
-
d2_start_time = time.time()
|
199
|
-
d2_min_estimate, _, _ = ldpc.mod2.estimate_code_distance(
|
200
|
-
self.seed_matrix_2, d2_timeout_seconds, 0
|
201
|
-
)
|
202
|
-
d2_run_time = time.time() - d2_start_time
|
203
|
-
else:
|
204
|
-
d2_min_estimate = np.inf
|
205
|
-
d2_run_time = 0
|
206
|
-
|
207
|
-
d2T_timeout_seconds = (
|
208
|
-
(d2_timeout_seconds * 2 - d2_run_time)
|
209
|
-
if d2_run_time <= d2_timeout_seconds
|
210
|
-
else timeout_seconds / 4
|
211
|
-
)
|
212
|
-
if self.seed_matrix_2.shape[0] != rank_seed_m2:
|
213
|
-
d2T_min_estimate, _, _ = ldpc.mod2.estimate_code_distance(
|
214
|
-
self.seed_matrix_2.T, d2T_timeout_seconds, 0
|
215
|
-
)
|
216
|
-
else:
|
217
|
-
d2T_min_estimate = np.inf
|
218
|
-
|
219
|
-
self.x_code_distance = min(d1T_min_estimate, d2_min_estimate)
|
220
|
-
self.z_code_distance = min(d1_min_estimate, d2T_min_estimate)
|
221
|
-
self.code_distance = min(self.x_code_distance, self.z_code_distance)
|
222
|
-
|
223
|
-
return self.code_distance
|
224
|
-
|
225
|
-
def compute_logical_basis(
|
226
|
-
self,
|
227
|
-
) -> Tuple[scipy.sparse.spmatrix, scipy.sparse.spmatrix]:
|
228
|
-
"""
|
229
|
-
Compute the logical operator basis for the given HGP code.
|
230
|
-
|
231
|
-
Returns
|
232
|
-
-------
|
233
|
-
Tuple[scipy.sparse.spmatrix, scipy.sparse.spmatrix]
|
234
|
-
Logical X and Z operator bases (lx, lz).
|
235
|
-
"""
|
236
|
-
|
237
|
-
ker_h1 = ldpc.mod2.kernel(self.seed_matrix_1)
|
238
|
-
ker_h2 = ldpc.mod2.kernel(self.seed_matrix_2)
|
239
|
-
ker_h1T = ldpc.mod2.kernel(self.seed_matrix_1.T)
|
240
|
-
ker_h2T = ldpc.mod2.kernel(self.seed_matrix_2.T)
|
241
|
-
|
242
|
-
row_comp_h1 = ldpc.mod2.row_complement_basis(self.seed_matrix_1)
|
243
|
-
row_comp_h2 = ldpc.mod2.row_complement_basis(self.seed_matrix_2)
|
244
|
-
row_comp_h1T = ldpc.mod2.row_complement_basis(self.seed_matrix_1.T)
|
245
|
-
row_comp_h2T = ldpc.mod2.row_complement_basis(self.seed_matrix_2.T)
|
246
|
-
|
247
|
-
temp = scipy.sparse.kron(ker_h1, row_comp_h2)
|
248
|
-
lz1 = scipy.sparse.hstack(
|
249
|
-
[
|
250
|
-
temp,
|
251
|
-
scipy.sparse.csr_matrix(
|
252
|
-
(temp.shape[0], self._m1 * self._m2), dtype=np.uint8
|
253
|
-
),
|
254
|
-
]
|
255
|
-
)
|
256
|
-
|
257
|
-
temp = scipy.sparse.kron(row_comp_h1T, ker_h2T)
|
258
|
-
lz2 = scipy.sparse.hstack(
|
259
|
-
[
|
260
|
-
scipy.sparse.csr_matrix(
|
261
|
-
(temp.shape[0], self._n1 * self._n2), dtype=np.uint8
|
262
|
-
),
|
263
|
-
temp,
|
264
|
-
]
|
265
|
-
)
|
266
|
-
|
267
|
-
self.z_logical_operator_basis = scipy.sparse.vstack([lz1, lz2], dtype=np.uint8)
|
268
|
-
|
269
|
-
temp = scipy.sparse.kron(row_comp_h1, ker_h2)
|
270
|
-
lx1 = scipy.sparse.hstack(
|
271
|
-
[
|
272
|
-
temp,
|
273
|
-
scipy.sparse.csr_matrix(
|
274
|
-
(temp.shape[0], self._m1 * self._m2), dtype=np.uint8
|
275
|
-
),
|
276
|
-
]
|
277
|
-
)
|
278
|
-
|
279
|
-
temp = scipy.sparse.kron(ker_h1T, row_comp_h2T)
|
280
|
-
lx2 = scipy.sparse.hstack(
|
281
|
-
[
|
282
|
-
scipy.sparse.csr_matrix(
|
283
|
-
(temp.shape[0], self._n1 * self._n2), dtype=np.uint8
|
284
|
-
),
|
285
|
-
temp,
|
286
|
-
]
|
287
|
-
)
|
288
|
-
|
289
|
-
self.x_logical_operator_basis = scipy.sparse.vstack([lx1, lx2], dtype=np.uint8)
|
290
|
-
|
291
|
-
# Follows the way it is done in CSSCode -> move it into __init__?
|
292
|
-
# ----------------------------------------------------------------
|
293
|
-
self.logical_qubit_count = self.x_logical_operator_basis.shape[0]
|
294
|
-
# ----------------------------------------------------------------
|
295
|
-
|
296
|
-
return (self.x_logical_operator_basis, self.z_logical_operator_basis)
|
297
|
-
|
298
|
-
def __str__(self):
|
299
|
-
"""
|
300
|
-
String representation of the HGP code. Includes the name and [[n, k, d]] properties of the code.
|
301
|
-
|
302
|
-
Returns
|
303
|
-
-------
|
304
|
-
str
|
305
|
-
String representation of the HGP code.
|
306
|
-
"""
|
307
|
-
|
308
|
-
return f"{self.name} Hypergraphproduct Code: [[N={self.physical_qubit_count}, K={self.logical_qubit_count}, dx={self.x_code_distance}, dz={self.z_code_distance}]]"
|