qec 0.0.11__py3-none-any.whl → 0.2.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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 +6 -0
- qec/stabilizer_code/stabilizer_code.py +609 -0
- qec/utils/__init__.py +0 -0
- qec/utils/binary_pauli_utils.py +401 -0
- qec/utils/codetables_de_utils.py +272 -0
- qec/utils/sparse_binary_utils.py +64 -0
- qec-0.2.0.dist-info/LICENSE +21 -0
- qec-0.2.0.dist-info/METADATA +82 -0
- qec-0.2.0.dist-info/RECORD +16 -0
- {qec-0.0.11.dist-info → qec-0.2.0.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.0.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.0
|
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=8BotcCuWrbnxnbZ1ZIJDI1jgr6-ohq-haPolc59TcWw,127
|
7
|
+
qec/stabilizer_code/stabilizer_code.py,sha256=_3oQwq2UNkPmP2R2qcsKTzYO4CLDvQdaiGxsN4_4r0I,22804
|
8
|
+
qec/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
qec/utils/binary_pauli_utils.py,sha256=FKxOMyEgUfSL1DF--8GUf4Nl6ytbK8Slyw7x2evhAac,13231
|
10
|
+
qec/utils/codetables_de_utils.py,sha256=soCf3u2v-C5EYYMiL8Ta4H6UF8KhRCEkjxLd6qBJai4,9467
|
11
|
+
qec/utils/sparse_binary_utils.py,sha256=Y9xfGKzOGFiVTyhb6iF6N7-5oMY6Ah9oLrnv8HhSBHA,1965
|
12
|
+
qec-0.2.0.dist-info/LICENSE,sha256=1b_xwNz1znYBfEaCL6pN2gNBAn8pQIjDRs_UhDp1EJI,1066
|
13
|
+
qec-0.2.0.dist-info/METADATA,sha256=DisbbTcVUey4dp5WelBc4aZeFcUkkwpsxRzMd44QncU,2367
|
14
|
+
qec-0.2.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
15
|
+
qec-0.2.0.dist-info/top_level.txt,sha256=d8l_7pJ5u9uWdviNp0FUK-j8VPZqywkDek7qa4NDank,4
|
16
|
+
qec-0.2.0.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
|
-
|