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