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.
@@ -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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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
-