qec 0.0.11__py3-none-any.whl → 0.2.1__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/__init__.py +0 -0
- qec/quantum_codes/__init__.py +2 -0
- qec/quantum_codes/codetables_de.py +93 -0
- qec/quantum_codes/five_qubit_code.py +67 -0
- qec/stabilizer_code/__init__.py +1 -0
- qec/stabilizer_code/css_code.py +609 -0
- qec/stabilizer_code/stabilizer_code.py +591 -0
- qec/utils/__init__.py +0 -0
- qec/utils/binary_pauli_utils.py +403 -0
- qec/utils/codetables_de_utils.py +274 -0
- qec/utils/sparse_binary_utils.py +64 -0
- qec-0.2.1.dist-info/LICENSE +21 -0
- qec-0.2.1.dist-info/METADATA +82 -0
- qec-0.2.1.dist-info/RECORD +16 -0
- {qec-0.0.11.dist-info → qec-0.2.1.dist-info}/WHEEL +1 -1
- qec/css.py +0 -164
- qec/hgp.py +0 -75
- qec/lifted_hgp.py +0 -79
- qec/protograph.py +0 -150
- qec/quantum_codes.py +0 -185
- qec/stab.py +0 -119
- qec/xzzx_codes.py +0 -333
- qec-0.0.11.dist-info/METADATA +0 -18
- qec-0.0.11.dist-info/RECORD +0 -11
- {qec-0.0.11.dist-info → qec-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import scipy.sparse
|
3
|
+
|
4
|
+
|
5
|
+
def convert_to_binary_scipy_sparse(
|
6
|
+
matrix: np.typing.ArrayLike,
|
7
|
+
) -> scipy.sparse.csr_matrix:
|
8
|
+
"""
|
9
|
+
Convert and validate a matrix as a sparse binary matrix in CSR format.
|
10
|
+
|
11
|
+
This function checks whether all elements of the input matrix are binary (0 or 1).
|
12
|
+
If the input is not already a sparse matrix, it converts it to a CSR (Compressed Sparse Row) matrix.
|
13
|
+
|
14
|
+
Parameters
|
15
|
+
----------
|
16
|
+
matrix : array-like
|
17
|
+
Input matrix of shape (M, N). Can be a dense array-like or any SciPy sparse matrix format.
|
18
|
+
|
19
|
+
Returns
|
20
|
+
-------
|
21
|
+
scipy.sparse.csr_matrix
|
22
|
+
Binary sparse matrix in CSR format, of shape (M, N).
|
23
|
+
|
24
|
+
Raises
|
25
|
+
------
|
26
|
+
ValueError
|
27
|
+
If the input matrix has elements outside {0, 1}.
|
28
|
+
TypeError
|
29
|
+
If the input is not array-like.
|
30
|
+
|
31
|
+
Examples
|
32
|
+
--------
|
33
|
+
>>> import numpy as np
|
34
|
+
>>> from scipy.sparse import csr_matrix
|
35
|
+
>>> from qec.utils.sparse_binary_utils import convert_to_binary_scipy_sparse
|
36
|
+
>>> mat = np.array([[0, 1], [1, 0]])
|
37
|
+
>>> convert_to_binary_scipy_sparse(mat).toarray()
|
38
|
+
array([[0, 1],
|
39
|
+
[1, 0]])
|
40
|
+
|
41
|
+
>>> mat = csr_matrix([[0, 1], [1, 0]])
|
42
|
+
>>> convert_to_binary_scipy_sparse(mat).toarray()
|
43
|
+
array([[0, 1],
|
44
|
+
[1, 0]])
|
45
|
+
|
46
|
+
>>> mat = np.array([[0, 2], [1, 0]])
|
47
|
+
>>> convert_to_binary_scipy_sparse(mat)
|
48
|
+
Traceback (most recent call last):
|
49
|
+
...
|
50
|
+
ValueError: All elements of the input matrix must be binary.
|
51
|
+
"""
|
52
|
+
if not isinstance(matrix, (np.ndarray, list, scipy.sparse.spmatrix)):
|
53
|
+
raise TypeError("Input must be array-like.")
|
54
|
+
|
55
|
+
if not isinstance(matrix, scipy.sparse.spmatrix):
|
56
|
+
matrix = scipy.sparse.csr_matrix(matrix, dtype=np.uint8)
|
57
|
+
|
58
|
+
if not matrix.dtype == np.uint8:
|
59
|
+
matrix = matrix.astype(np.uint8)
|
60
|
+
|
61
|
+
if not np.all(np.isin(matrix.data, [0, 1])):
|
62
|
+
raise ValueError("All elements of the input matrix must be binary.")
|
63
|
+
|
64
|
+
return matrix
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 qec.Codes
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,82 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: qec
|
3
|
+
Version: 0.2.1
|
4
|
+
Summary: Python Tools for Quantum Error Correction
|
5
|
+
Author-email: Joschka Roffe <joschka@roffe.eu>
|
6
|
+
License: MIT License
|
7
|
+
|
8
|
+
Copyright (c) 2023 qec.Codes
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
18
|
+
copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
SOFTWARE.
|
27
|
+
|
28
|
+
Classifier: Development Status :: 4 - Beta
|
29
|
+
Requires-Python: >=3.8
|
30
|
+
Description-Content-Type: text/markdown
|
31
|
+
License-File: LICENSE
|
32
|
+
Requires-Dist: ldpc
|
33
|
+
Requires-Dist: numpy>=1.24.0
|
34
|
+
Requires-Dist: scipy>=1.9.3
|
35
|
+
Requires-Dist: requests
|
36
|
+
Requires-Dist: beautifulsoup4
|
37
|
+
|
38
|
+
<h1 align="center">QEC </h1>
|
39
|
+
<h3 align="center"> Python Tools for Quantum Error Correction </h3>
|
40
|
+
|
41
|
+
|
42
|
+
- Website: https://qec.codes/
|
43
|
+
|
44
|
+
# Table of contents
|
45
|
+
1. [Features](#features)
|
46
|
+
2. [Installation](#installation)
|
47
|
+
3. [Getting started](#getting-started)
|
48
|
+
|
49
|
+
|
50
|
+
# Features
|
51
|
+
|
52
|
+
Here will go the description of all our fancy features!
|
53
|
+
|
54
|
+
# Installation
|
55
|
+
|
56
|
+
Obtain a local copy of the package by cloning it, then navigate into the created folder:
|
57
|
+
|
58
|
+
```bash
|
59
|
+
git clone git@github.com:qec-codes/qec.git
|
60
|
+
cd qec
|
61
|
+
```
|
62
|
+
|
63
|
+
Finally, install the package:
|
64
|
+
|
65
|
+
```bash
|
66
|
+
pip install -e .
|
67
|
+
```
|
68
|
+
|
69
|
+
You are all set! To import the package use:
|
70
|
+
|
71
|
+
```python
|
72
|
+
In [1]: import qec
|
73
|
+
|
74
|
+
In [2]: qec.__version__
|
75
|
+
Out [2]: '0.1.0'
|
76
|
+
|
77
|
+
```
|
78
|
+
|
79
|
+
# Getting started
|
80
|
+
|
81
|
+
A few simple examples
|
82
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
qec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
qec/quantum_codes/__init__.py,sha256=DQ1ztrq-vBpTyoehaMWOhals46tRj553Jmkq68bDk-E,117
|
3
|
+
qec/quantum_codes/codetables_de.py,sha256=loBDBOK2cbDJ5moKmIx2MXg6e30XEPrEYau19bbDgac,3623
|
4
|
+
qec/quantum_codes/five_qubit_code.py,sha256=0zrGLyIpfyKwYG7uL00yMcM5PdhQGF17_MiI2qTMhOk,2190
|
5
|
+
qec/stabilizer_code/__init__.py,sha256=L5UMjHBlvfQBhkNlEZYSkyaHvNOcDHjc3oxYibMYHRk,63
|
6
|
+
qec/stabilizer_code/css_code.py,sha256=JhNiBHqfwu4OgMVUsXl6yJ4L5KNW4Dn2Sf0beBdAl2s,24763
|
7
|
+
qec/stabilizer_code/stabilizer_code.py,sha256=I5u8JKZu88ioC4E2nBJ-00xCmnL8nU6kdAvwYOfmNRk,22138
|
8
|
+
qec/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
qec/utils/binary_pauli_utils.py,sha256=BSlngYDdRICu0aVu4u_m0bvLicohORyGxfk5eRER7TQ,13245
|
10
|
+
qec/utils/codetables_de_utils.py,sha256=S1wcVGJkkASQQ5s71QAsYBmpyE-3xTb6UsvgMfQtuiw,9469
|
11
|
+
qec/utils/sparse_binary_utils.py,sha256=Y9xfGKzOGFiVTyhb6iF6N7-5oMY6Ah9oLrnv8HhSBHA,1965
|
12
|
+
qec-0.2.1.dist-info/LICENSE,sha256=1b_xwNz1znYBfEaCL6pN2gNBAn8pQIjDRs_UhDp1EJI,1066
|
13
|
+
qec-0.2.1.dist-info/METADATA,sha256=AbWaMM6fYb65-0lUw6qWuywZigdtHseO-6QAbNZK0QM,2367
|
14
|
+
qec-0.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
15
|
+
qec-0.2.1.dist-info/top_level.txt,sha256=d8l_7pJ5u9uWdviNp0FUK-j8VPZqywkDek7qa4NDank,4
|
16
|
+
qec-0.2.1.dist-info/RECORD,,
|
qec/css.py
DELETED
@@ -1,164 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
from ldpc import mod2
|
3
|
-
from ldpc.code_util import compute_code_distance
|
4
|
-
|
5
|
-
class css_code():
|
6
|
-
|
7
|
-
def __init__(self,hx=np.array([[]]),hz=np.array([[]]), name="<Unnamed CSS code>"):
|
8
|
-
|
9
|
-
self.hx=hx #hx pcm
|
10
|
-
self.hz=hz #hz pcm
|
11
|
-
|
12
|
-
self.lx=np.array([[]]) #x logicals
|
13
|
-
self.lz=np.array([[]]) #z logicals
|
14
|
-
|
15
|
-
self.N=np.nan #block length
|
16
|
-
self.K=np.nan #code dimension
|
17
|
-
self.D=np.nan #code distance
|
18
|
-
self.L=np.nan #max column weight
|
19
|
-
self.Q=np.nan #max row weight
|
20
|
-
|
21
|
-
_,nx=self.hx.shape
|
22
|
-
_,nz=self.hz.shape
|
23
|
-
try:
|
24
|
-
assert nx==nz
|
25
|
-
except AssertionError:
|
26
|
-
raise Exception("Error: hx and hz matrices must have equal numbers of columns!")
|
27
|
-
|
28
|
-
if nx!=0:
|
29
|
-
self.compute_dimension()
|
30
|
-
self.compute_ldpc_params()
|
31
|
-
self.compute_logicals()
|
32
|
-
|
33
|
-
self.name=name
|
34
|
-
|
35
|
-
def compute_dimension(self):
|
36
|
-
|
37
|
-
self.N=self.hx.shape[1]
|
38
|
-
assert self.N == self.hz.shape[1], "Code block length (N) inconsistent!"
|
39
|
-
|
40
|
-
self.K=self.N-mod2.rank(self.hx)-mod2.rank(self.hz)
|
41
|
-
return self.K
|
42
|
-
|
43
|
-
def compute_ldpc_params(self):
|
44
|
-
|
45
|
-
#column weights
|
46
|
-
hx_l=np.max(np.sum(self.hx,axis=0))
|
47
|
-
hz_l=np.max(np.sum(self.hz,axis=0))
|
48
|
-
self.L=np.max([hx_l,hz_l]).astype(int)
|
49
|
-
|
50
|
-
#row weights
|
51
|
-
hx_q=np.max(np.sum(self.hx,axis=1))
|
52
|
-
hz_q=np.max(np.sum(self.hz,axis=1))
|
53
|
-
self.Q=np.max([hx_q,hz_q]).astype(int)
|
54
|
-
|
55
|
-
|
56
|
-
def compute_logicals(self):
|
57
|
-
|
58
|
-
def compute_lz(hx,hz):
|
59
|
-
#lz logical operators
|
60
|
-
#lz\in ker{hx} AND \notin Im(Hz.T)
|
61
|
-
|
62
|
-
ker_hx=mod2.nullspace(hx) #compute the kernel basis of hx
|
63
|
-
im_hzT=mod2.row_basis(hz) #compute the image basis of hz.T
|
64
|
-
|
65
|
-
#in the below we row reduce to find vectors in kx that are not in the image of hz.T.
|
66
|
-
log_stack=np.vstack([im_hzT,ker_hx])
|
67
|
-
pivots=mod2.row_echelon(log_stack.T)[3]
|
68
|
-
log_op_indices=[i for i in range(im_hzT.shape[0],log_stack.shape[0]) if i in pivots]
|
69
|
-
log_ops=log_stack[log_op_indices]
|
70
|
-
return log_ops
|
71
|
-
|
72
|
-
if self.K==np.nan: self.compute_dimension()
|
73
|
-
self.lx=compute_lz(self.hz,self.hx)
|
74
|
-
self.lz=compute_lz(self.hx,self.hz)
|
75
|
-
|
76
|
-
return self.lx,self.lz
|
77
|
-
|
78
|
-
@property
|
79
|
-
def code_params(self):
|
80
|
-
try: self.N
|
81
|
-
except AttributeError: self.N=np.nan
|
82
|
-
try: self.K
|
83
|
-
except AttributeError: self.K=np.nan
|
84
|
-
try: self.D
|
85
|
-
except AttributeError: self.D=np.nan
|
86
|
-
try: self.L
|
87
|
-
except AttributeError: self.L=np.nan
|
88
|
-
try: self.Q
|
89
|
-
except AttributeError: self.Q=np.nan
|
90
|
-
|
91
|
-
return f"({self.L},{self.Q})-[[{self.N},{self.K},{self.D}]]"
|
92
|
-
|
93
|
-
def compute_code_distance(self):
|
94
|
-
|
95
|
-
dx=compute_code_distance(self.hx)
|
96
|
-
dz=compute_code_distance(self.hz)
|
97
|
-
dxt=compute_code_distance(self.hx.T)
|
98
|
-
dzt=compute_code_distance(self.hz.T)
|
99
|
-
self.D=np.min([dx,dz,dxt,dzt]).astype(int)
|
100
|
-
|
101
|
-
def test(self,show_tests=True):
|
102
|
-
valid_code=True
|
103
|
-
|
104
|
-
if self.K==np.nan: self.compute_dimension()
|
105
|
-
self.compute_ldpc_params()
|
106
|
-
|
107
|
-
code_label=f"{self.code_params}"
|
108
|
-
|
109
|
-
if show_tests: print(f"{self.name}, {code_label}")
|
110
|
-
|
111
|
-
try:
|
112
|
-
assert self.N==self.hz.shape[1]==self.lz.shape[1]==self.lx.shape[1]
|
113
|
-
assert self.K==self.lz.shape[0]==self.lx.shape[0]
|
114
|
-
if show_tests: print(" -Block dimensions: Pass")
|
115
|
-
except AssertionError:
|
116
|
-
valid_code=False
|
117
|
-
print(" -Block dimensions incorrect")
|
118
|
-
|
119
|
-
try:
|
120
|
-
assert not (self.hz@self.hx.T %2).any()
|
121
|
-
if show_tests: print(" -PCMs commute hz@hx.T==0: Pass")
|
122
|
-
except AssertionError:
|
123
|
-
valid_code=False
|
124
|
-
print(" -PCMs commute hz@hx.T==0: Fail")
|
125
|
-
|
126
|
-
try:
|
127
|
-
assert not (self.hx@self.hz.T %2).any()
|
128
|
-
if show_tests: print(" -PCMs commute hx@hz.T==0: Pass")
|
129
|
-
except AssertionError:
|
130
|
-
valid_code=False
|
131
|
-
print(" -PCMs commute hx@hz.T==0: Fail")
|
132
|
-
|
133
|
-
# if show_tests and valid_code: print("\t-PCMs commute hx@hz.T == hz@hx.T ==0: Pass")
|
134
|
-
|
135
|
-
try:
|
136
|
-
assert not (self.hz@self.lx.T %2).any()
|
137
|
-
except AssertionError:
|
138
|
-
valid_code=False
|
139
|
-
print(" -lx \in ker{hz} AND lz \in ker{hx}: Fail")
|
140
|
-
|
141
|
-
|
142
|
-
try:
|
143
|
-
assert not (self.hx@self.lz.T %2).any()
|
144
|
-
if show_tests: print(" -lx \in ker{hz} AND lz \in ker{hx}: Pass")
|
145
|
-
except AssertionError:
|
146
|
-
valid_code=False
|
147
|
-
print(" -lx \in ker{hz} AND lz \in ker{hx}: Fail")
|
148
|
-
|
149
|
-
|
150
|
-
# if show_tests and valid_code: print("\t-lx \in ker{hz} AND lz \in ker{hx}: Pass")
|
151
|
-
|
152
|
-
try:
|
153
|
-
assert mod2.rank(self.lx@self.lz.T %2)==self.K
|
154
|
-
if show_tests: print(" -lx and lz anticommute: Pass")
|
155
|
-
except AssertionError:
|
156
|
-
valid_code=False
|
157
|
-
print(" -lx and lz anitcommute: Fail")
|
158
|
-
|
159
|
-
# if show_tests and valid_code: print("\t- lx and lz anitcommute: Pass")
|
160
|
-
|
161
|
-
if show_tests and valid_code: print(f" -{self.name} is a valid CSS code w/ params {code_label}")
|
162
|
-
|
163
|
-
return valid_code
|
164
|
-
|
qec/hgp.py
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
from ldpc.mod2 import rank
|
3
|
-
from ldpc.code_util import compute_code_distance
|
4
|
-
from qec.css import css_code
|
5
|
-
|
6
|
-
class hgp(css_code):
|
7
|
-
def __init__(self,h1,h2=None,compute_distance=False):
|
8
|
-
|
9
|
-
super().__init__()
|
10
|
-
#default to symmetric HGP if
|
11
|
-
|
12
|
-
if type(h2)!=np.ndarray: h2=np.copy(h1)
|
13
|
-
|
14
|
-
self.h1=h1
|
15
|
-
self.h2=h2
|
16
|
-
|
17
|
-
#setting up base codes
|
18
|
-
self.m1,self.n1=np.shape(h1)
|
19
|
-
i_m1=np.identity(self.m1,dtype=int)
|
20
|
-
i_n1=np.identity(self.n1,dtype=int)
|
21
|
-
self.r1=rank(self.h1)
|
22
|
-
self.k1=self.n1-self.r1
|
23
|
-
self.k1t=self.m1-self.r1
|
24
|
-
|
25
|
-
self.m2,self.n2=np.shape(h2)
|
26
|
-
i_m2=np.identity(self.m2,dtype=int)
|
27
|
-
i_n2=np.identity(self.n2,dtype=int)
|
28
|
-
self.r2=rank(self.h2)
|
29
|
-
self.k2=self.n2-self.r2
|
30
|
-
self.k2t=self.m2-self.r2
|
31
|
-
|
32
|
-
#hgp code params
|
33
|
-
self.N=self.n1*self.n2 + self.m1*self.m2
|
34
|
-
self.K=self.k1*self.k2+self.k1t*self.k2t
|
35
|
-
self.D=None
|
36
|
-
|
37
|
-
#construct hx and hz
|
38
|
-
self.hx1=np.kron(self.h1,i_n2)
|
39
|
-
self.hx2=np.kron(i_m1,self.h2.T)
|
40
|
-
self.hx = np.hstack([ self.hx1, self.hx2 ])
|
41
|
-
|
42
|
-
self.hz1=np.kron(i_n1,self.h2)
|
43
|
-
self.hz2=np.kron(self.h1.T,i_m2)
|
44
|
-
self.hz = np.hstack([ self.hz1, self.hz2 ])
|
45
|
-
|
46
|
-
#construct the hgp logicals
|
47
|
-
self.compute_logicals()
|
48
|
-
|
49
|
-
##compute code distance if the base codes are small enough for it to be tractable
|
50
|
-
if(compute_distance==True):
|
51
|
-
self.d1=compute_code_distance(self.h1)
|
52
|
-
self.d1t=compute_code_distance(self.h1.T)
|
53
|
-
self.d2=compute_code_distance(self.h2)
|
54
|
-
self.d2t=compute_code_distance(self.h2.T)
|
55
|
-
self.D=np.min([self.d1,self.d1t,self.d2,self.d2t]).astype(int)
|
56
|
-
else: self.D=None
|
57
|
-
|
58
|
-
def print_code_parameters(self):
|
59
|
-
|
60
|
-
if self.D==None: print( f"[[{self.N},{self.K},d]]")
|
61
|
-
else: print( f"[[{self.N},{self.K},{self.D}]]")
|
62
|
-
|
63
|
-
|
64
|
-
class hgp_single(hgp):
|
65
|
-
def __init__(self,h1,compute_distance=False):
|
66
|
-
super().__init__(h1,compute_distance=compute_distance)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
qec/lifted_hgp.py
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
from qec.protograph import protograph_to_qc_code,protograph_transpose,kron_mat_proto,kron_proto_mat
|
3
|
-
from qec.css import css_code
|
4
|
-
|
5
|
-
def I(n):
|
6
|
-
'''
|
7
|
-
Returns an identity matrix of size n
|
8
|
-
'''
|
9
|
-
return np.identity(n).astype(int)
|
10
|
-
|
11
|
-
class lifted_hgp(css_code):
|
12
|
-
|
13
|
-
def __init__(self,lift_parameter,a,b=None):
|
14
|
-
|
15
|
-
'''
|
16
|
-
Generates the lifted hypergraph product of the protographs a and b
|
17
|
-
'''
|
18
|
-
self.a=a
|
19
|
-
|
20
|
-
self.a_m,self.a_n=self.a.shape
|
21
|
-
self.a_t=protograph_transpose(self.a)
|
22
|
-
|
23
|
-
if b is None:
|
24
|
-
self.b=np.copy(self.a)
|
25
|
-
else:
|
26
|
-
self.b=b
|
27
|
-
|
28
|
-
self.b_t=protograph_transpose(self.b)
|
29
|
-
self.b_m,self.b_n=self.b.shape
|
30
|
-
|
31
|
-
self.hx1_proto=kron_proto_mat(self.a,I(self.b_n))
|
32
|
-
self.hx2_proto=kron_mat_proto(I(self.a_m),self.b_t)
|
33
|
-
self.hx_proto=np.hstack([self.hx1_proto,self.hx2_proto])
|
34
|
-
|
35
|
-
self.hx1 = protograph_to_qc_code(lift_parameter,self.hx1_proto)
|
36
|
-
self.hx2 = protograph_to_qc_code(lift_parameter,self.hx2_proto)
|
37
|
-
self.hx=np.hstack([self.hx1,self.hx2]).astype(int)
|
38
|
-
|
39
|
-
self.hz1_proto=kron_mat_proto(I(self.a_n),self.b)
|
40
|
-
self.hz2_proto=kron_proto_mat(self.a_t,I(self.b_m))
|
41
|
-
self.hz_proto=np.hstack([self.hz1_proto,self.hz2_proto])
|
42
|
-
|
43
|
-
self.hz1=protograph_to_qc_code(lift_parameter,self.hz1_proto)
|
44
|
-
self.hz2= protograph_to_qc_code(lift_parameter,self.hz2_proto)
|
45
|
-
self.hz=np.hstack( [self.hz1, self.hz2] ).astype(int)
|
46
|
-
|
47
|
-
super().__init__(self.hx,self.hz)
|
48
|
-
|
49
|
-
class lifted_bicycle(css_code):
|
50
|
-
|
51
|
-
def __init__(self,lift_parameter,a,b=None):
|
52
|
-
|
53
|
-
'''
|
54
|
-
Generates the lifted hypergraph product of the protographs a and b
|
55
|
-
'''
|
56
|
-
|
57
|
-
a_m,a_n=a.shape
|
58
|
-
a_T=protograph_transpose(a)
|
59
|
-
|
60
|
-
if b is None:
|
61
|
-
b=np.copy(a)
|
62
|
-
|
63
|
-
b_T=protograph_transpose(b)
|
64
|
-
b_m,b_n=b.shape
|
65
|
-
|
66
|
-
self.hx1 = protograph_to_qc_code(lift_parameter, a)
|
67
|
-
self.hx2 = protograph_to_qc_code(lift_parameter, b)
|
68
|
-
self.hx=np.hstack([self.hx1,self.hx2]).astype(int)
|
69
|
-
|
70
|
-
self.hz1=protograph_to_qc_code(lift_parameter, protograph_transpose(b))
|
71
|
-
self.hz2= protograph_to_qc_code(lift_parameter, protograph_transpose(a))
|
72
|
-
self.hz=np.hstack( [self.hz1, self.hz2] ).astype(int)
|
73
|
-
|
74
|
-
super().__init__(self.hx,self.hz)
|
75
|
-
|
76
|
-
self.lx1=self.lx[:,0:self.hx1.shape[1]]
|
77
|
-
self.lx2=self.lx[:,self.hx1.shape[1]:]
|
78
|
-
self.lz1=self.lz[:,0:self.hz1.shape[1]]
|
79
|
-
self.lz2=self.lz[:,self.hz1.shape[1]:]
|
qec/protograph.py
DELETED
@@ -1,150 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
|
3
|
-
def permutation_matrix(n,shift):
|
4
|
-
'''
|
5
|
-
Outputs a size-n permutation matrix.
|
6
|
-
|
7
|
-
|
8
|
-
Inputs:
|
9
|
-
n: matrix dimension
|
10
|
-
shift: the shift parameter
|
11
|
-
Returns:
|
12
|
-
mat: nxn matrix shifted by `shift' columns to the left
|
13
|
-
'''
|
14
|
-
return np.roll(np.identity(n),1*shift,axis=1).astype(int)
|
15
|
-
|
16
|
-
def polynomial_to_circulant_matrix(n,non_zero_coefficients):
|
17
|
-
'''
|
18
|
-
Converts a polynomial into a circulant matrix
|
19
|
-
|
20
|
-
Inputs:
|
21
|
-
n: int, matrix dimension
|
22
|
-
non_zero_coefficieents: list of ints (can be np.ndarray), list of the non-zero coefficients of the polynomial
|
23
|
-
Returns:
|
24
|
-
mat: an nxn circulant matrix corresponding to the inputted polynomial
|
25
|
-
'''
|
26
|
-
mat=np.zeros((n,n)).astype(int)
|
27
|
-
for shift in non_zero_coefficients:
|
28
|
-
mat+=permutation_matrix(n,shift)
|
29
|
-
return mat % 2
|
30
|
-
|
31
|
-
# def polynomial_transpose(lift_parameter, polynomial):
|
32
|
-
|
33
|
-
# polynomial_transpose=set()
|
34
|
-
# for coefficient in polynomial:
|
35
|
-
# polynomial_transpose.add((lift_parameter-coefficient)%lift_parameter)
|
36
|
-
|
37
|
-
# return polynomial_transpose
|
38
|
-
|
39
|
-
def polynomial_transpose(polynomial):
|
40
|
-
polynomial_transpose=set()
|
41
|
-
for coefficient in polynomial:
|
42
|
-
polynomial_transpose.add(-1*coefficient)
|
43
|
-
return polynomial_transpose
|
44
|
-
|
45
|
-
def empty_protograph(shape):
|
46
|
-
'''
|
47
|
-
Returns an empty protograh
|
48
|
-
'''
|
49
|
-
m,n=shape
|
50
|
-
row=np.array([{}]*n)
|
51
|
-
return np.vstack([row]*m)
|
52
|
-
|
53
|
-
def kron_mat_proto(mat,proto):
|
54
|
-
'''
|
55
|
-
Kronecker product of a numpy matrix and a protograph
|
56
|
-
'''
|
57
|
-
mat_m,mat_n=mat.shape
|
58
|
-
zero_proto=empty_protograph(proto.shape)
|
59
|
-
|
60
|
-
# print(zero_proto)
|
61
|
-
|
62
|
-
out=[]
|
63
|
-
for i in range(mat_m):
|
64
|
-
row=[]
|
65
|
-
for j in range(mat_n):
|
66
|
-
if mat[i,j]:
|
67
|
-
row.append(proto)
|
68
|
-
else:
|
69
|
-
row.append(zero_proto)
|
70
|
-
out.append(np.hstack(row))
|
71
|
-
|
72
|
-
return np.vstack(out)
|
73
|
-
|
74
|
-
def kron_proto_mat(proto,mat):
|
75
|
-
|
76
|
-
'''
|
77
|
-
Tensor product of a protograph and numpy matrix
|
78
|
-
'''
|
79
|
-
|
80
|
-
proto_m,proto_n=proto.shape
|
81
|
-
mat_m,mat_n=mat.shape
|
82
|
-
zero_proto=empty_protograph(mat.shape)
|
83
|
-
|
84
|
-
out=[]
|
85
|
-
for i in range(proto_m):
|
86
|
-
row=[]
|
87
|
-
for j in range(proto_n):
|
88
|
-
if len(proto[i,j])==0:
|
89
|
-
row.append(zero_proto)
|
90
|
-
else:
|
91
|
-
temp=np.copy(zero_proto)
|
92
|
-
for k in range(temp.shape[0]):
|
93
|
-
for l in range(temp.shape[1]):
|
94
|
-
if mat[k,l]==1:
|
95
|
-
temp[k,l]=proto[i,j]
|
96
|
-
else:
|
97
|
-
temp[k,l]={}
|
98
|
-
|
99
|
-
row.append(temp)
|
100
|
-
|
101
|
-
out.append(np.hstack(row))
|
102
|
-
|
103
|
-
return np.vstack(out)
|
104
|
-
|
105
|
-
def protograph_transpose(protograph):
|
106
|
-
'''
|
107
|
-
Returns the transpose of a protograph.
|
108
|
-
|
109
|
-
Input:
|
110
|
-
lift_paramter: int, the lift parameter for the protograph
|
111
|
-
protograph: np.ndarray, the protograph
|
112
|
-
Return:
|
113
|
-
protograph_transpose: np.ndarray, the transpose of the inputted protograph for the given lift parameter
|
114
|
-
'''
|
115
|
-
|
116
|
-
m,n=protograph.shape
|
117
|
-
protograph_transpose=empty_protograph((n,m))
|
118
|
-
|
119
|
-
for i in range(m):
|
120
|
-
for j in range(n):
|
121
|
-
protograph_transpose[j,i]=polynomial_transpose(protograph[i,j])
|
122
|
-
|
123
|
-
return protograph_transpose
|
124
|
-
|
125
|
-
|
126
|
-
def protograph_to_qc_code(n,protograph):
|
127
|
-
|
128
|
-
'''
|
129
|
-
Generates the parity check matrix of a quasicyclic code from a matrix of polynomials.
|
130
|
-
|
131
|
-
Inputs:
|
132
|
-
n: int, lift parameter
|
133
|
-
protograph: np.ndarray, polynomial matrix
|
134
|
-
Returns:
|
135
|
-
qc_matrix: np.ndarray, quasi-cyclic code corresponding to the inputted polynomial matrix
|
136
|
-
'''
|
137
|
-
|
138
|
-
qc_matrix=[]
|
139
|
-
|
140
|
-
for row in protograph:
|
141
|
-
qc_row=[]
|
142
|
-
for polynomial in row:
|
143
|
-
qc_row.append( polynomial_to_circulant_matrix(n,polynomial) )
|
144
|
-
qc_row=np.hstack(qc_row)
|
145
|
-
qc_matrix.append(qc_row)
|
146
|
-
|
147
|
-
qc_matrix=np.vstack(qc_matrix)
|
148
|
-
return qc_matrix
|
149
|
-
|
150
|
-
|