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.
@@ -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,,
@@ -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
-