codedistance 0.0.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.
- codedistance/.DS_Store +0 -0
- codedistance/NHow.py +804 -0
- codedistance/__init__.py +5 -0
- codedistance/code_library.py +556 -0
- codedistance/common.py +566 -0
- codedistance/complex_utils.py +117 -0
- codedistance/dem_detector_filtering.py +255 -0
- codedistance/distance.py +2473 -0
- codedistance-0.0.1.dist-info/METADATA +196 -0
- codedistance-0.0.1.dist-info/RECORD +12 -0
- codedistance-0.0.1.dist-info/WHEEL +4 -0
- codedistance-0.0.1.dist-info/licenses/LICENSE +674 -0
codedistance/NHow.py
ADDED
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import numba as nb
|
|
3
|
+
from .common import *
|
|
4
|
+
|
|
5
|
+
#####################################################
|
|
6
|
+
## Ring Functions for Calculating Howell Matrix Form
|
|
7
|
+
#####################################################
|
|
8
|
+
|
|
9
|
+
@nb.jit(nb.int16[:,:](nb.int16,nb.int16,nb.int16))
|
|
10
|
+
def Gcdex_jit(a,b,N):
|
|
11
|
+
'''Extended GCD: Return g,s,t,u,v such that: as + bt = g where g = gcd(a,b); AND au + bv = 0'''
|
|
12
|
+
a = a % N
|
|
13
|
+
b = b % N
|
|
14
|
+
s = nb.int16(0)
|
|
15
|
+
old_s = nb.int16(1)
|
|
16
|
+
t = nb.int16(1)
|
|
17
|
+
old_t = nb.int16(0)
|
|
18
|
+
r = b
|
|
19
|
+
old_r = a
|
|
20
|
+
while r != nb.int16(0):
|
|
21
|
+
quotient = old_r // r
|
|
22
|
+
(old_r, r) = (r, old_r - quotient * r)
|
|
23
|
+
(old_s, s) = (s, old_s - quotient * s)
|
|
24
|
+
(old_t, t) = (t, old_t - quotient * t)
|
|
25
|
+
p = np.sign(t * old_s - s * old_t)
|
|
26
|
+
u = p * s
|
|
27
|
+
v = p * t
|
|
28
|
+
# g = old_r
|
|
29
|
+
s = old_s
|
|
30
|
+
t = old_t
|
|
31
|
+
return np.array([[s,t],[u,v]],dtype=nb.int16)
|
|
32
|
+
|
|
33
|
+
@nb.jit(nb.int16(nb.int16,nb.int16))
|
|
34
|
+
def Ann_jit(a,N):
|
|
35
|
+
'''Annihilator of a modulo N: Return u such that a u mod N = 0. Return 0 if a is a unit. Return 1 if a == 0'''
|
|
36
|
+
a = a % N
|
|
37
|
+
if a == 0:
|
|
38
|
+
return 1
|
|
39
|
+
u = N // np.gcd(a,N)
|
|
40
|
+
return u % N
|
|
41
|
+
|
|
42
|
+
@nb.jit(nb.int16(nb.int16,nb.int16))
|
|
43
|
+
def Split_jit(a,N):
|
|
44
|
+
a = a % N
|
|
45
|
+
if N == nb.int16(0):
|
|
46
|
+
return nb.int16(0)
|
|
47
|
+
if a == nb.int16(0):
|
|
48
|
+
return nb.int16(1)
|
|
49
|
+
r = int(np.ceil(np.log2(np.log2(N)))) if N > 1 else nb.int16(1)
|
|
50
|
+
for i in range(r):
|
|
51
|
+
a = a*a % N
|
|
52
|
+
return N // np.gcd(a,N)
|
|
53
|
+
|
|
54
|
+
@nb.jit(nb.int16(nb.int16,nb.int16,nb.int16))
|
|
55
|
+
def Stab_jit(a,b,N):
|
|
56
|
+
### return c such that GCD(a + bc, N) = GCD(a,b) modulo N
|
|
57
|
+
a = nb.int16(a % N)
|
|
58
|
+
b = nb.int16(b % N)
|
|
59
|
+
g = np.gcd(np.gcd(a,b),N)
|
|
60
|
+
c = Split_jit(a//g,N//g)
|
|
61
|
+
return c % N
|
|
62
|
+
|
|
63
|
+
@nb.jit(nb.int16(nb.int16,nb.int16,nb.int16))
|
|
64
|
+
def Div_jit(a,b,N):
|
|
65
|
+
'''Return c such that bc = a mod N or None if no such c exists'''
|
|
66
|
+
a = a % N
|
|
67
|
+
b = b % N
|
|
68
|
+
if b < 1:
|
|
69
|
+
return nb.int16(-1)
|
|
70
|
+
g = np.gcd(b,N)
|
|
71
|
+
if a % g == nb.int16(0):
|
|
72
|
+
r = a % b
|
|
73
|
+
while r > nb.int16(0):
|
|
74
|
+
a += N
|
|
75
|
+
r = a % b
|
|
76
|
+
return a // b % N
|
|
77
|
+
return nb.int16(-1)
|
|
78
|
+
|
|
79
|
+
@nb.jit(nb.int16(nb.int16,nb.int16))
|
|
80
|
+
def Unit_jit(a,N):
|
|
81
|
+
'''Return a unit c such that ac = gcd(a,N) mod N.'''
|
|
82
|
+
a = a % N
|
|
83
|
+
if a == nb.int16(0):
|
|
84
|
+
return nb.int16(1)
|
|
85
|
+
g = np.gcd(a,N)
|
|
86
|
+
s = Div_jit(g,a,N)
|
|
87
|
+
if g == nb.int16(1):
|
|
88
|
+
return s
|
|
89
|
+
d = Stab_jit(s,N//g,N)
|
|
90
|
+
c = (s + d * N // g) % N
|
|
91
|
+
return c
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
#####################################################
|
|
96
|
+
## RREF Modulo 2
|
|
97
|
+
#####################################################
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@nb.jit(nb.types.Tuple((nb.int8[:,:],nb.int64[:]))(nb.int8[:,:],nb.int64,nb.int64,nb.int64,nb.int64))
|
|
101
|
+
def HowZ2Set(A,tB,nB,nC,r0):
|
|
102
|
+
if np.sum(A) == 0:
|
|
103
|
+
return A,np.arange(0,dtype=nb.int64)
|
|
104
|
+
m,n = A.shape
|
|
105
|
+
pivots = []
|
|
106
|
+
B = []
|
|
107
|
+
# B = [set() for i in range(m)]
|
|
108
|
+
# rowData,colData = np.nonzero(A)
|
|
109
|
+
# for ix in range(len(rowData)):
|
|
110
|
+
# B[rowData[ix]].add(colData[ix])
|
|
111
|
+
for myrow in A:
|
|
112
|
+
s = set()
|
|
113
|
+
for i in range(n):
|
|
114
|
+
if myrow[i]==1:
|
|
115
|
+
s.add(i)
|
|
116
|
+
B.append(s)
|
|
117
|
+
r = r0
|
|
118
|
+
for j in range(nC):
|
|
119
|
+
for t in range(tB):
|
|
120
|
+
## c is the column of B we are currently looking at
|
|
121
|
+
c = j + t * nB
|
|
122
|
+
iList = [i for i in range(r,m) if c in B[i]]
|
|
123
|
+
if len(iList) > 0:
|
|
124
|
+
i = iList.pop(0)
|
|
125
|
+
pivots.append(c)
|
|
126
|
+
## found j: if j > r, swap row j with row r
|
|
127
|
+
if i > r:
|
|
128
|
+
## swap using bitflips - more elegant than array indexing
|
|
129
|
+
B[r] ^= B[i]
|
|
130
|
+
B[i] ^= B[r]
|
|
131
|
+
B[r] ^= B[i]
|
|
132
|
+
## eliminate non-zero entries in column c apart from row r
|
|
133
|
+
for i in [i for i in range(r) if c in B[i] ] + iList:
|
|
134
|
+
B[i] ^= B[r]
|
|
135
|
+
r +=1
|
|
136
|
+
C = np.zeros((m,n),dtype=nb.int8)
|
|
137
|
+
for i in range(m):
|
|
138
|
+
for j in B[i]:
|
|
139
|
+
C[i][j] = 1
|
|
140
|
+
return C,np.array(pivots,dtype=nb.int64)
|
|
141
|
+
|
|
142
|
+
def KerZ2(A,cols,r0,verbose=False):
|
|
143
|
+
'''fast kernel method - test'''
|
|
144
|
+
# if RREF(A) is of form I|D up to col perms then ker(A) = D.T|I
|
|
145
|
+
A = np.array(A,dtype=np.int8)
|
|
146
|
+
B, pivots = RREFZ2(A,cols,r0)
|
|
147
|
+
w = np.sum(B, axis=-1)
|
|
148
|
+
B = B[w > 0]
|
|
149
|
+
m, n = B.shape
|
|
150
|
+
k = n - m
|
|
151
|
+
nonpivots = invRange(n,pivots)
|
|
152
|
+
C = np.zeros((k,n),dtype=np.int8)
|
|
153
|
+
for i in range(k):
|
|
154
|
+
C[i,nonpivots[i]] = 1
|
|
155
|
+
C[:,pivots] = B[:,nonpivots].T
|
|
156
|
+
if verbose:
|
|
157
|
+
return B, pivots, C
|
|
158
|
+
return C
|
|
159
|
+
|
|
160
|
+
@nb.njit(nb.types.Tuple((nb.int8[:,:],nb.int64[:]))(nb.int8[:,:],nb.int64[:],nb.int64))
|
|
161
|
+
def RREFZ2(A,cols,r0):
|
|
162
|
+
'''RREF using cols as indices to perform reductions'''
|
|
163
|
+
pivots = []
|
|
164
|
+
B = A.copy()
|
|
165
|
+
if np.sum(B) > 0:
|
|
166
|
+
m = len(B)
|
|
167
|
+
r = r0
|
|
168
|
+
for c in cols:
|
|
169
|
+
iList = [i for i in range(r,m) if B[i,c] > 0]
|
|
170
|
+
if len(iList) > 0:
|
|
171
|
+
i = iList.pop(0)
|
|
172
|
+
pivots.append(c)
|
|
173
|
+
## found j: if j > r, swap row j with row r
|
|
174
|
+
if i > r:
|
|
175
|
+
## swap using bitflips - more elegant than array indexing
|
|
176
|
+
B[r] ^= B[i]
|
|
177
|
+
B[i] ^= B[r]
|
|
178
|
+
B[r] ^= B[i]
|
|
179
|
+
## eliminate non-zero entries in column c apart from row r
|
|
180
|
+
for i in [i for i in range(r) if B[i,c] > 0] + iList:
|
|
181
|
+
B[i] ^= B[r]
|
|
182
|
+
r += 1
|
|
183
|
+
return B,np.array(pivots,dtype=nb.int64)
|
|
184
|
+
|
|
185
|
+
@nb.jit(nb.types.Tuple((nb.int8[:,:],nb.int64[:]))(nb.int8[:,:],nb.int64,nb.int64,nb.int64,nb.int64))
|
|
186
|
+
def HowZ2(A,tB,nB,nC,r0):
|
|
187
|
+
pivots = []
|
|
188
|
+
B = A.copy()
|
|
189
|
+
if np.sum(A) == 0:
|
|
190
|
+
return B,np.array(pivots,dtype=nb.int64)
|
|
191
|
+
m = len(B)
|
|
192
|
+
r = r0
|
|
193
|
+
|
|
194
|
+
for j in range(nC):
|
|
195
|
+
for t in range(tB):
|
|
196
|
+
## c is the column of B we are currently looking at
|
|
197
|
+
c = j + t * nB
|
|
198
|
+
iList = [i for i in range(r,m) if B[i,c] > 0]
|
|
199
|
+
if len(iList) > 0:
|
|
200
|
+
i = iList.pop(0)
|
|
201
|
+
pivots.append(c)
|
|
202
|
+
## found j: if j > r, swap row j with row r
|
|
203
|
+
if i > r:
|
|
204
|
+
## swap using bitflips - more elegant than array indexing
|
|
205
|
+
# B[r] = B[r] ^ B[i]
|
|
206
|
+
# B[i] = B[r] ^ B[i]
|
|
207
|
+
# B[r] = B[r] ^ B[i]
|
|
208
|
+
B[r] ^= B[i]
|
|
209
|
+
B[i] ^= B[r]
|
|
210
|
+
B[r] ^= B[i]
|
|
211
|
+
## eliminate non-zero entries in column c apart from row r
|
|
212
|
+
for i in [i for i in range(r) if B[i,c] > 0] + iList:
|
|
213
|
+
# B[i] = B[i] ^ B[r]
|
|
214
|
+
B[i] ^= B[r]
|
|
215
|
+
r +=1
|
|
216
|
+
return B,np.array(pivots,dtype=nb.int64)
|
|
217
|
+
|
|
218
|
+
@nb.jit(nb.int8[:,:](nb.int8[:,:]))
|
|
219
|
+
def binSpanNb(A):
|
|
220
|
+
r,n = A.shape
|
|
221
|
+
temp = np.zeros((1 << r, n),dtype=nb.int8)
|
|
222
|
+
j = 1
|
|
223
|
+
for i in range(r):
|
|
224
|
+
k = j << 1
|
|
225
|
+
temp[j:k] = temp[:j] ^ A[i]
|
|
226
|
+
j = k
|
|
227
|
+
return temp
|
|
228
|
+
|
|
229
|
+
def SpanBin(A):
|
|
230
|
+
'''Span of binary matrix A - seems faster than jit version...'''
|
|
231
|
+
r,n = A.shape
|
|
232
|
+
temp = np.zeros((1 << r, n),dtype=np.int8)
|
|
233
|
+
j = 1
|
|
234
|
+
for i in range(r):
|
|
235
|
+
k = j << 1
|
|
236
|
+
temp[j:k] = temp[:j] ^ A[i]
|
|
237
|
+
j = k
|
|
238
|
+
return temp
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
#####################################################
|
|
242
|
+
## Calculate Howell Matrix form modulo N
|
|
243
|
+
#####################################################
|
|
244
|
+
|
|
245
|
+
@nb.jit(nb.types.Tuple((nb.int16[:,:],nb.types.List(nb.int64)))(nb.int16[:,:],nb.int16,nb.int64,nb.int64,nb.int64,nb.int64))
|
|
246
|
+
def HowZN(A,N,tB,nB,nC,r0):
|
|
247
|
+
'''Return Howell basis of A mod N plus row operations to convert to this form'''
|
|
248
|
+
pivots = []
|
|
249
|
+
if np.sum(A) == 0:
|
|
250
|
+
return A,pivots
|
|
251
|
+
m,n = A.shape
|
|
252
|
+
B = [a for a in A]
|
|
253
|
+
N = nb.int16(N)
|
|
254
|
+
r = r0
|
|
255
|
+
|
|
256
|
+
## c is the column of B we are currently looking at
|
|
257
|
+
for mc in range(nC):
|
|
258
|
+
for t in range(tB):
|
|
259
|
+
c = mc + t * nB
|
|
260
|
+
## find j such that B[j][c] > 0
|
|
261
|
+
jList = [j for j in range(r,m) if B[j][c] > 0]
|
|
262
|
+
if len(jList) > 0:
|
|
263
|
+
j = jList.pop(0)
|
|
264
|
+
pivots.append(mc)
|
|
265
|
+
## found j: if j > r, swap row j with row r
|
|
266
|
+
if j > r:
|
|
267
|
+
B[j],B[r] = B[r],B[j]
|
|
268
|
+
## Multiplying by x ensures that B[r][c] is a minimal representative
|
|
269
|
+
b = B[r][c]
|
|
270
|
+
x = Unit_jit(b,N)
|
|
271
|
+
if(x > 1):
|
|
272
|
+
B[r] = np.mod(B[r] * x, N)
|
|
273
|
+
## eliminate entries in column c below row r
|
|
274
|
+
for j in jList:
|
|
275
|
+
a, b = B[r][c],B[j][c]
|
|
276
|
+
C = Gcdex_jit(a,b,N)
|
|
277
|
+
Br = np.mod(B[r] * C[0,0] + B[j] * C[0,1], N)
|
|
278
|
+
Bj = np.mod(B[r] * C[1,0] + B[j] * C[1,1], N)
|
|
279
|
+
B[r] = Br
|
|
280
|
+
B[j] = Bj
|
|
281
|
+
## ensure entries in column c above row r are less than B[r][c]
|
|
282
|
+
b = B[r][c]
|
|
283
|
+
for j in range(r):
|
|
284
|
+
if B[j][c] >= b:
|
|
285
|
+
x = nb.int16(B[j][c] // b)
|
|
286
|
+
B[j] = np.mod(B[j] - B[r] * x,N)
|
|
287
|
+
## Multiplying by x = Ann(b) eliminates b = B[r][c], but rest of the row may be non-zero
|
|
288
|
+
## If x > 0 then b is a zero divisor and we add a row
|
|
289
|
+
## If x == 0, b is a unit and we move to the next value of l
|
|
290
|
+
x = Ann_jit(b,N)
|
|
291
|
+
if x > 0:
|
|
292
|
+
B.append(np.mod(B[r] * x,N))
|
|
293
|
+
m += 1
|
|
294
|
+
r +=1
|
|
295
|
+
temp = np.empty((m,n),dtype=nb.int16)
|
|
296
|
+
for i in range (m):
|
|
297
|
+
temp[i] = B[i]
|
|
298
|
+
return temp,pivots
|
|
299
|
+
|
|
300
|
+
# @nb.jit
|
|
301
|
+
def blockDims(n,nA=0,tB=1,nC=-1):
|
|
302
|
+
nA = min(n,nA)
|
|
303
|
+
nB = (n - nA) // tB
|
|
304
|
+
if nC < 0 or nC > nB:
|
|
305
|
+
nC = nB
|
|
306
|
+
return nA,nB,nC
|
|
307
|
+
|
|
308
|
+
def getH(A,N,nA=0,tB=1,nC=-1,r0=0,retPivots=False):
|
|
309
|
+
'''Return Howell matrix form modulo N:
|
|
310
|
+
A: input matrix
|
|
311
|
+
N: linear algebra modulo N
|
|
312
|
+
nA: number of cols appended to right - not subject to row reduction
|
|
313
|
+
tB: number of blocks in the matrix
|
|
314
|
+
nC: number of columns to reduce
|
|
315
|
+
r0: starting row for reduction'''
|
|
316
|
+
## nB: number of columns in each block
|
|
317
|
+
m,n = A.shape
|
|
318
|
+
nA,nB,nC = blockDims(n,nA,tB,nC)
|
|
319
|
+
if N==2:
|
|
320
|
+
A = np.array(A,dtype=np.int8)
|
|
321
|
+
H,pivots = HowZ2(A,tB,nB,nC,r0)
|
|
322
|
+
# H,pivots = HowZ2Set(A,tB,nB,nC,r0)
|
|
323
|
+
else:
|
|
324
|
+
A = np.array(A,dtype=np.int16)
|
|
325
|
+
H,pivots = HowZN(A,N,tB,nB,nC,r0)
|
|
326
|
+
w = np.sum(H,axis=-1)
|
|
327
|
+
ix = [i for i in range(len(H)) if i < r0 or w[i] >0]
|
|
328
|
+
H = H[ix]
|
|
329
|
+
return (H, list(pivots)) if retPivots else H
|
|
330
|
+
|
|
331
|
+
def getHU(A,N,nA=0,tB=1,nC=-1,r0=0):
|
|
332
|
+
'''Return Howell matrix form modulo N plus transformation matrix U such that H = U @ A mod N'''
|
|
333
|
+
m,n = A.shape
|
|
334
|
+
B = np.hstack([A,ZMatI(m)])
|
|
335
|
+
nA += m
|
|
336
|
+
HU = getH(B,N,nA,tB,nC,r0)
|
|
337
|
+
return HU[:,:n],HU[:,n:]
|
|
338
|
+
|
|
339
|
+
def getK(A,N,nA=0,tB=1,nC=-1,retPivots=False):
|
|
340
|
+
'''Return Kernel K such that K @ A.T = 0 mod N'''
|
|
341
|
+
## Transpose so nA,tB,nC,r0 not applicable
|
|
342
|
+
H, U = getHU(A.T,N)
|
|
343
|
+
ix = np.sum(H,axis=-1) == 0
|
|
344
|
+
K = U[ix,:]
|
|
345
|
+
## r0 not applicable...
|
|
346
|
+
return getH(K,N,nA,tB,nC,retPivots=retPivots)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def HowRes(A,B,N,tB=1):
|
|
350
|
+
'''Return R, V such that B = R + V @ A mod N for A,B matrices, unknowns R, V'''
|
|
351
|
+
return solveH(A,B,N,tB)
|
|
352
|
+
|
|
353
|
+
def HowResU(A,B,N,tB=1):
|
|
354
|
+
'''Return R, V such that B = R + V @ A mod N for A,B matrices, unknowns R, V'''
|
|
355
|
+
R,V,H,U,K = solveHU(A,B,N,tB)
|
|
356
|
+
return R,V
|
|
357
|
+
|
|
358
|
+
def solveHU(A,B,N,tB=1):
|
|
359
|
+
'''Solve B = R + (V + <K>) @ A mod N for A,B matrices, unknowns R, V, K
|
|
360
|
+
A: m x n
|
|
361
|
+
B: r x n
|
|
362
|
+
R: r x n
|
|
363
|
+
V: r x m
|
|
364
|
+
K: k x m'''
|
|
365
|
+
B1D = len(B.shape) == 1
|
|
366
|
+
A, B = ZMat2D(A), ZMat2D(B)
|
|
367
|
+
m,n = A.shape
|
|
368
|
+
r,n1 = B.shape
|
|
369
|
+
if n != n1:
|
|
370
|
+
print(func_name(), 'A,B incompatible shape')
|
|
371
|
+
## Make matrix of form [[I,B,0],[0,A,I]]
|
|
372
|
+
BA = np.hstack([B,ZMatZeros((r,m))])
|
|
373
|
+
BA = ZMatVstack([BA,np.hstack([A,ZMatI(m)])])
|
|
374
|
+
## Howell form - only consider first m2 + n1 columns
|
|
375
|
+
HBA = getH(BA,N,nA=m,tB=tB,r0=r)
|
|
376
|
+
## How results in matrix of form [[I,R,V],[0,H,U],[0,0,K]]
|
|
377
|
+
## R: top middle block of HBA
|
|
378
|
+
R = HBA[:r,:n]
|
|
379
|
+
## if B is 1D, return 1D result
|
|
380
|
+
if B1D:
|
|
381
|
+
R = R[0]
|
|
382
|
+
## V: top right block of HBA - negative to reverse residue calc
|
|
383
|
+
V = np.mod(-HBA[:r,n:],N)
|
|
384
|
+
## H: bottom middle block of HBA
|
|
385
|
+
H = HBA[r:,:n]
|
|
386
|
+
## U: bottom right block of HBA
|
|
387
|
+
U = HBA[r:,n:]
|
|
388
|
+
ix = np.sum(H,axis=-1) > 0
|
|
389
|
+
H,U,K = H[ix,:],U[ix,:],U[np.logical_not(ix), :]
|
|
390
|
+
return R,V,H,U,K
|
|
391
|
+
|
|
392
|
+
def solveH(A,B,N,tB=1):
|
|
393
|
+
'''Solve B = R + V @ A mod N for unknown R, H = getH(A,N)
|
|
394
|
+
A: m x n
|
|
395
|
+
B: r x n
|
|
396
|
+
R: r x n'''
|
|
397
|
+
B1D = len(np.shape(B)) == 1
|
|
398
|
+
A, B = ZMat2D(A), ZMat2D(B)
|
|
399
|
+
m,n = A.shape
|
|
400
|
+
r,n1 = B.shape
|
|
401
|
+
if n != n1:
|
|
402
|
+
print(func_name(), 'A,B incompatible shape')
|
|
403
|
+
## Make matrix of form [[B],[A]]
|
|
404
|
+
BA = ZMatVstack([B,A])
|
|
405
|
+
## Howell form - only consider first m2 + n1 columns
|
|
406
|
+
HBA = getH(BA,N,tB=tB,r0=r)
|
|
407
|
+
## How results in matrix of form [[I,R,V],[0,H,U],[0,0,K]]
|
|
408
|
+
## R: top block
|
|
409
|
+
R = HBA[:r,:]
|
|
410
|
+
## H: lower block
|
|
411
|
+
H = HBA[r:,:]
|
|
412
|
+
## if B is 1D, return 1D result
|
|
413
|
+
if B1D:
|
|
414
|
+
R = R[0]
|
|
415
|
+
return R,H
|
|
416
|
+
|
|
417
|
+
# @nb.jit
|
|
418
|
+
def HowPivots(A,nA=0,tB=1,nC=-1,r0=0):
|
|
419
|
+
'''Leading indices of matrix in Howell/RREF form
|
|
420
|
+
- Examine first nC columns
|
|
421
|
+
- Matrix has nBlocks blocks A = [A_0|A_1|..|A_n-1]'''
|
|
422
|
+
temp = []
|
|
423
|
+
if np.sum(A) == 0:
|
|
424
|
+
return temp
|
|
425
|
+
m,n = A.shape
|
|
426
|
+
nA,nB,nC = blockDims(n,nA,tB,nC)
|
|
427
|
+
r,c = r0,0
|
|
428
|
+
for r in range(m):
|
|
429
|
+
while c < nC and np.all([A[r,c + t * nB] == 0 for t in range(tB)]):
|
|
430
|
+
c+=1
|
|
431
|
+
if c < nC:
|
|
432
|
+
temp.append(c)
|
|
433
|
+
else:
|
|
434
|
+
return temp
|
|
435
|
+
return temp
|
|
436
|
+
|
|
437
|
+
# def ZMatBlockSum(A,nA=0,tB=1,nC=-1,N=2):
|
|
438
|
+
# '''Sum block of Zmat
|
|
439
|
+
# - up to nC
|
|
440
|
+
# - tB - number of blocks
|
|
441
|
+
# - if A = [A_0|A_1|..|A_n] return A_0 + 2 A_1 + ... + 2^n A_n'''
|
|
442
|
+
# nB,nC = blockDims(A,nA,tB,nC)
|
|
443
|
+
# ix = ZMat(range(nC))
|
|
444
|
+
# S = A.take(indices=ix, axis=-1)
|
|
445
|
+
# for t in range(1,tB):
|
|
446
|
+
# ix += nB
|
|
447
|
+
# S += A.take(indices=ix, axis=-1) * (N ** t)
|
|
448
|
+
# return S
|
|
449
|
+
|
|
450
|
+
# @nb.jit
|
|
451
|
+
# @nb.jit(nb.types.Tuple((nb.int8[:,:],nb.int64[:]))(nb.int8[:,:],nb.int64,nb.int64,nb.int64,nb.int64))
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def indepL(L,pList,nA,tB,retWeights=False):
|
|
456
|
+
w = np.sum(L[:,-nA:],axis=-1)
|
|
457
|
+
L = L[w > 0,:]
|
|
458
|
+
probs,weights = ZMatProbW(L,pList=pList,nA=nA,tB=tB,N=2)
|
|
459
|
+
ix = argsort(probs)
|
|
460
|
+
probs = probs[ix]
|
|
461
|
+
weights = weights[ix]
|
|
462
|
+
L = L[ix,:]
|
|
463
|
+
H = L[:,-nA:]
|
|
464
|
+
H,U = getHU(H,2)
|
|
465
|
+
K = U[np.sum(H,axis=-1) == 0,:]
|
|
466
|
+
K,LI = getH(K,2,retPivots=True)
|
|
467
|
+
ix = invRange(len(L),LI)
|
|
468
|
+
L = L[ix,:]
|
|
469
|
+
probs = probs[ix]
|
|
470
|
+
weights = weights[ix]
|
|
471
|
+
if retWeights:
|
|
472
|
+
return L,probs,weights
|
|
473
|
+
else:
|
|
474
|
+
return L,probs
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def uniqL(L,pList,nA,tB,pMax = 1e6):
|
|
478
|
+
H = L[:,-nA:]
|
|
479
|
+
ix = np.sum(H,axis=-1)
|
|
480
|
+
L = L[ix>0]
|
|
481
|
+
# L = ZMat({tuple(x) for x in L})
|
|
482
|
+
L = np.unique(L,axis=0)
|
|
483
|
+
probs = ZMatProb(L,pList,nA,tB)
|
|
484
|
+
if len(probs) > pMax:
|
|
485
|
+
ix = argsort(probs)
|
|
486
|
+
ix = ix[-pMax:]
|
|
487
|
+
L = L[ix]
|
|
488
|
+
probs = probs[ix]
|
|
489
|
+
return L,probs
|
|
490
|
+
|
|
491
|
+
# @nb.jit
|
|
492
|
+
def ZMatTake(A,ix):
|
|
493
|
+
'''Take indices ix form A along axis -1 '''
|
|
494
|
+
B = A[:,ix].copy()
|
|
495
|
+
return B
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def XZhad(E, ix=None):
|
|
499
|
+
'''Apply Hadamard to qubits according to index ix for Pauli operator list E'''
|
|
500
|
+
m,n = E.shape
|
|
501
|
+
nB = n//2
|
|
502
|
+
if ix is None:
|
|
503
|
+
ix = np.arange(nB)
|
|
504
|
+
ixN = ix + nB
|
|
505
|
+
ix1 = np.arange(n)
|
|
506
|
+
ix1[ix] = ixN
|
|
507
|
+
ix1[ixN] = ix
|
|
508
|
+
return ZMatPermuteCols(E,ix1)
|
|
509
|
+
|
|
510
|
+
def XZCX(E, a,b):
|
|
511
|
+
'''Apply CX(a->b) for Pauli operator list E'''
|
|
512
|
+
m,n = E.shape
|
|
513
|
+
nB = n//2
|
|
514
|
+
E1 = E.copy()
|
|
515
|
+
E1[:,b] ^= E[:,a]
|
|
516
|
+
E1[:,a+nB] ^= E[:,b+nB]
|
|
517
|
+
return E1
|
|
518
|
+
|
|
519
|
+
def XZCZ(E, a,b):
|
|
520
|
+
'''Apply CZ(a b) for Pauli operator list E'''
|
|
521
|
+
m,n = E.shape
|
|
522
|
+
nB = n//2
|
|
523
|
+
E1 = E.copy()
|
|
524
|
+
E1[:,a+nB] ^= E[:,b]
|
|
525
|
+
if a != b:
|
|
526
|
+
## if a==b, we apply S; otherwise it's CZ
|
|
527
|
+
E1[:,b+nB] ^= E[:,a]
|
|
528
|
+
return E1
|
|
529
|
+
|
|
530
|
+
# @nb.jit
|
|
531
|
+
def ZMatBlockSum(A,nA=0,tB=1,nC=-1,N=2):
|
|
532
|
+
'''Sum block of Zmat
|
|
533
|
+
- up to nC
|
|
534
|
+
- tB - number of blocks
|
|
535
|
+
- if A = [A_0|A_1|..|A_n] return A_0 + 2 A_1 + ... + 2^n A_n'''
|
|
536
|
+
m,n = A.shape
|
|
537
|
+
# print(len(A))
|
|
538
|
+
nA,nB,nC = blockDims(n,nA,tB,nC)
|
|
539
|
+
ix = np.arange(nC)
|
|
540
|
+
S = ZMatTake(A,ix)
|
|
541
|
+
for t in np.arange(1,tB):
|
|
542
|
+
ix += nB
|
|
543
|
+
S += ZMatTake(A,ix) * (N ** t)
|
|
544
|
+
return S
|
|
545
|
+
|
|
546
|
+
def ZMatBlockSum(A,nA=0,tB=1,nC=-1,N=2):
|
|
547
|
+
'''Sum block of Zmat
|
|
548
|
+
- up to nC
|
|
549
|
+
- tB - number of blocks
|
|
550
|
+
- if A = [A_0|A_1|..|A_n] return A_0 + 2 A_1 + ... + 2^n A_n'''
|
|
551
|
+
m,n = A.shape
|
|
552
|
+
# print(len(A))
|
|
553
|
+
nA,nB,nC = blockDims(n,nA,tB,nC)
|
|
554
|
+
S = np.empty((m,nC),dtype=int)
|
|
555
|
+
c1 = 0
|
|
556
|
+
c2 = c1 + nC
|
|
557
|
+
S[:,:] = A[:,c1:c2]
|
|
558
|
+
c1 = c2
|
|
559
|
+
for t in range(1,tB):
|
|
560
|
+
c2 = c1 + nC
|
|
561
|
+
if N == 2:
|
|
562
|
+
S += (A[:,c1:c2] << t)
|
|
563
|
+
else:
|
|
564
|
+
S += A[:,c1:c2] * (N ** t)
|
|
565
|
+
c1 = c2
|
|
566
|
+
return S
|
|
567
|
+
|
|
568
|
+
def ZMatPermuteCols(A,ix,nA=0,tB=1,nC=-1):
|
|
569
|
+
m,n = A.shape
|
|
570
|
+
nA,nB,nC = blockDims(n,nA=nA,tB=tB,nC=nC)
|
|
571
|
+
ix = list(ix) + invRange(nB,ix)
|
|
572
|
+
ix = ZMat(ix)
|
|
573
|
+
ind = np.arange(n)
|
|
574
|
+
for t in range(tB):
|
|
575
|
+
ind[t*nB:(t+1)*nB] = ix
|
|
576
|
+
ix += nB
|
|
577
|
+
return ZMatTake(A,ind)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
####################################
|
|
584
|
+
## Matrix Multiplication - avoid overflow for large matrices
|
|
585
|
+
####################################
|
|
586
|
+
|
|
587
|
+
# @nb.jit (nb.int16[:,:](nb.int16[:,:],nb.int16[:,:],nb.int64))
|
|
588
|
+
# def matMulZN(A,B,N):
|
|
589
|
+
# m1,n1 = A.shape
|
|
590
|
+
# m2,n2 = B.shape
|
|
591
|
+
# C = np.zeros((m1,n2),dtype=nb.int16)
|
|
592
|
+
# m = min(n1,m2)
|
|
593
|
+
# for i in range(m1):
|
|
594
|
+
# for j in range(n2):
|
|
595
|
+
# temp = nb.int16(0)
|
|
596
|
+
# for k in range(m):
|
|
597
|
+
# temp = np.mod(temp + (A[i,k] * B[k,j]),N)
|
|
598
|
+
# C[i,j] = temp
|
|
599
|
+
# return C
|
|
600
|
+
|
|
601
|
+
def matMulZN(A,B,N):
|
|
602
|
+
return np.mod(A @ B, N)
|
|
603
|
+
|
|
604
|
+
@nb.jit (nb.int8[:,:](nb.int8[:,:],nb.int8[:,:]))
|
|
605
|
+
def matMulZ2(A,B):
|
|
606
|
+
m1,n1 = A.shape
|
|
607
|
+
m2,n2 = B.shape
|
|
608
|
+
C = np.zeros((m1,n2),dtype=nb.int8)
|
|
609
|
+
m = min(n1,m2)
|
|
610
|
+
for i in range(m1):
|
|
611
|
+
for j in range(n2):
|
|
612
|
+
temp = nb.int8(0)
|
|
613
|
+
for k in range(m):
|
|
614
|
+
temp = temp ^ (A[i,k] & B[k,j])
|
|
615
|
+
C[i,j] = temp
|
|
616
|
+
return C
|
|
617
|
+
|
|
618
|
+
def matMul(A,B,N):
|
|
619
|
+
'''Multiply two integer matrices modulo N'''
|
|
620
|
+
A = ZMat2D(A)
|
|
621
|
+
B = ZMat2D(B)
|
|
622
|
+
if N==2:
|
|
623
|
+
A = np.array(A,dtype=np.int8)
|
|
624
|
+
B = np.array(B,dtype=np.int8)
|
|
625
|
+
return matMulZ2(A,B)
|
|
626
|
+
else:
|
|
627
|
+
A = np.array(A,dtype=np.int16)
|
|
628
|
+
B = np.array(B,dtype=np.int16)
|
|
629
|
+
return matMulZN(A,B,N)
|
|
630
|
+
return np.mod(A @ B, N)
|
|
631
|
+
|
|
632
|
+
def mod1(A):
|
|
633
|
+
'''Replace values > 0 in A with 1'''
|
|
634
|
+
return np.clip(A,0,1)
|
|
635
|
+
A = ZMat(A)
|
|
636
|
+
A[A>0] = 1
|
|
637
|
+
return A
|
|
638
|
+
|
|
639
|
+
def RemoveZeroRows(A,N=False):
|
|
640
|
+
'''Remove any zero rows from integer matrix A'''
|
|
641
|
+
# A = ZMat(A)
|
|
642
|
+
w = np.sum(A, axis=-1)
|
|
643
|
+
return A[w > 0]
|
|
644
|
+
|
|
645
|
+
def restoreShape(A,s):
|
|
646
|
+
if s!=A.shape:
|
|
647
|
+
return np.reshape(A,s)
|
|
648
|
+
return A
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def ZMatWeight(A,nA=0,tB=1,nC=-1):
|
|
652
|
+
s = A.shape
|
|
653
|
+
if len(s) != 2:
|
|
654
|
+
return restoreShape(np.sum(mod1(ZMatBlockSum(ZMat2D(A),nA,tB,nC,1)),axis=-1),s[:-1])
|
|
655
|
+
return np.sum(mod1(ZMatBlockSum(A,nA,tB,nC,1)),axis=-1)
|
|
656
|
+
|
|
657
|
+
def pListDefault(tB=1,pI=0.7):
|
|
658
|
+
pLen = (1 << tB) - 1
|
|
659
|
+
p = (1-pI)/pLen
|
|
660
|
+
return np.array([pI] + [p] * pLen)
|
|
661
|
+
|
|
662
|
+
def ZMatProbW(A,pList=None,nA=0,tB=1,nC=-1,N=2):
|
|
663
|
+
'''Return probabilities and weight of each row of A
|
|
664
|
+
'''
|
|
665
|
+
if len(A) == 0:
|
|
666
|
+
return [],[]
|
|
667
|
+
if pList is None:
|
|
668
|
+
pList = pListDefault(tB)
|
|
669
|
+
pList = np.array(pList)
|
|
670
|
+
W = ZMatBlockSum(A,tB=tB,N=2,nA=nA)
|
|
671
|
+
s = np.prod(pList[W],axis=-1)
|
|
672
|
+
W = np.sum(mod1(W),axis=-1)
|
|
673
|
+
return s, W
|
|
674
|
+
|
|
675
|
+
def ZMatProb(A,pList,nA=0,tB=1,nC=-1,N=2):
|
|
676
|
+
s, w = ZMatProbW(A,pList,nA=0,tB=1,nC=-1,N=2)
|
|
677
|
+
return s
|
|
678
|
+
|
|
679
|
+
def lowWeightGens(L,S=None,N=2,tB=1,pList=None,retProb=False):
|
|
680
|
+
'''Get set of lowest weight generators of logicals L
|
|
681
|
+
S is a list of stabilisers
|
|
682
|
+
tB is number of blocks
|
|
683
|
+
N is precision
|
|
684
|
+
pList is list of probabilities'''
|
|
685
|
+
# Default probabilities
|
|
686
|
+
if pList is None:
|
|
687
|
+
pList = pListDefault(tB=tB)
|
|
688
|
+
w = ZMatProb(L,pList,tB=tB,N=N)
|
|
689
|
+
## order L by increasing prob/decreasing weight
|
|
690
|
+
ix = argsort(w)
|
|
691
|
+
L = L[ix]
|
|
692
|
+
w = w[ix]
|
|
693
|
+
LS = L if S is None else ZMatVstack([L,S])
|
|
694
|
+
## RREF plus transformation matrix
|
|
695
|
+
H, U = getHU(LS,N,tB=tB)
|
|
696
|
+
## K is a list of linear combinations of rows which result in zero
|
|
697
|
+
ix = np.sum(H,axis=-1) == 0
|
|
698
|
+
K = U[ix,:]
|
|
699
|
+
## RREF - so combinations of lowest weight rows are to the RHS
|
|
700
|
+
K, LI = getH(K,N,retPivots=True)
|
|
701
|
+
# K = K[:,:len(L)]
|
|
702
|
+
## get leading entries of K - these correspond to rows we should exclude
|
|
703
|
+
## as adding them to the set results in a redundancy
|
|
704
|
+
# LI = HowPivots(K,nC=len(L))
|
|
705
|
+
ix = invRange(len(L),LI)
|
|
706
|
+
L = L[ix,:]
|
|
707
|
+
w = w[ix]
|
|
708
|
+
return (L,w) if retProb else L
|
|
709
|
+
|
|
710
|
+
##################################################
|
|
711
|
+
#### Operations on Spans ####
|
|
712
|
+
##################################################
|
|
713
|
+
|
|
714
|
+
def affineIntersection(A1,o1,A2,o2,N,C=False):
|
|
715
|
+
'''Calculate intersection of two affine spaces
|
|
716
|
+
U1 = o1 + <A1> mod N
|
|
717
|
+
U2 = o2 + <A2> mod N'''
|
|
718
|
+
if C is not False:
|
|
719
|
+
A,o = C
|
|
720
|
+
tocheck = np.mod(o + A,N)
|
|
721
|
+
tocheck = ZMatVstack([[o],tocheck])
|
|
722
|
+
for v in tocheck:
|
|
723
|
+
v1 = np.mod(v - o1,N)
|
|
724
|
+
b,u = HowRes(A1,v1,N)
|
|
725
|
+
if not isZero(b):
|
|
726
|
+
print(v1, 'Not in span of A1')
|
|
727
|
+
return False
|
|
728
|
+
v2 = np.mod(v - o2,N)
|
|
729
|
+
b,u = HowRes(A2,v2,N)
|
|
730
|
+
if not isZero(b):
|
|
731
|
+
print(v2, 'Not in span of A2')
|
|
732
|
+
return False
|
|
733
|
+
return True
|
|
734
|
+
o = affineIntercept(A1,o1,A2,o2,N)
|
|
735
|
+
if o is False:
|
|
736
|
+
return False
|
|
737
|
+
## new affine space is intersection
|
|
738
|
+
A = nsIntersection([A1,A2],N)
|
|
739
|
+
## residue mod A to simplify result
|
|
740
|
+
R, V = HowRes(A, o, N)
|
|
741
|
+
return A,R[0]
|
|
742
|
+
|
|
743
|
+
def affineIntercept(A1,o1,A2,o2,N,C=False):
|
|
744
|
+
'''Calculate intersection of two affine spaces
|
|
745
|
+
U1 = o1 + <A1> mod N
|
|
746
|
+
U2 = o2 + <A2> mod N
|
|
747
|
+
return o which is in both U1 and U2 or False'''
|
|
748
|
+
if C is not False:
|
|
749
|
+
## check if C-o1 is in <A1>
|
|
750
|
+
R,V = HowRes(A1,np.mod(C-o1,N),N)
|
|
751
|
+
if not isZero(R):
|
|
752
|
+
return False
|
|
753
|
+
## check if C-o2 is in <A2>
|
|
754
|
+
R,V = HowRes(A2,np.mod(C-o2,N),N)
|
|
755
|
+
if not isZero(R):
|
|
756
|
+
return False
|
|
757
|
+
return True
|
|
758
|
+
A = ZMatVstack([A1, A2])
|
|
759
|
+
## find solution o1-o2 = (v1|v2) @ (A1//A2) = v1@A1 + v2@A2 mod N <=> o1 - v1@A1 = o2 + v2@A2
|
|
760
|
+
R, v1v2 = HowResU(A, np.mod(o1-o2,N), N)
|
|
761
|
+
if not isZero(R):
|
|
762
|
+
## there is no solution if residue is non-zero
|
|
763
|
+
return False
|
|
764
|
+
## extract v1 from v1v2 - corresponds to first len(A1) entries of the vector
|
|
765
|
+
v1 = v1v2[:,:len(A1)]
|
|
766
|
+
v1A1 = matMul(v1, A1, N)[0]
|
|
767
|
+
## note minus sign
|
|
768
|
+
return np.mod(o1 - v1A1,N)
|
|
769
|
+
|
|
770
|
+
def nsIntersection(Alist,N,C=False):
|
|
771
|
+
'''Intersection of multiple rowspans AList modulo N.'''
|
|
772
|
+
if C is not False:
|
|
773
|
+
for A in Alist:
|
|
774
|
+
## check if C in <A> for each A in AList
|
|
775
|
+
R,V = HowRes(A,C,N)
|
|
776
|
+
if not isZero(R):
|
|
777
|
+
return False
|
|
778
|
+
return True
|
|
779
|
+
if len(Alist) == 0:
|
|
780
|
+
return False
|
|
781
|
+
A = Alist[0]
|
|
782
|
+
for B in Alist[1:]:
|
|
783
|
+
if len(B) < len(A):
|
|
784
|
+
A,B = B,A
|
|
785
|
+
AB = ZMatVstack([A,B])
|
|
786
|
+
Kt = getK(AB.T,N)
|
|
787
|
+
A0 = ZMatVstack([A,ZMatZeros(B.shape)])
|
|
788
|
+
C = matMul(Kt, A0,N)
|
|
789
|
+
A = getH(C,N)
|
|
790
|
+
return A
|
|
791
|
+
|
|
792
|
+
def nsUnion(Alist,N,C=False):
|
|
793
|
+
'''Union of multiple rowspaces Alist modulo N'''
|
|
794
|
+
if C is not False:
|
|
795
|
+
## check if A in C for all A in Alist
|
|
796
|
+
for A in Alist:
|
|
797
|
+
R,V = HowRes(C,A,N)
|
|
798
|
+
if not isZero(R):
|
|
799
|
+
return False
|
|
800
|
+
return True
|
|
801
|
+
if len(Alist) == 0:
|
|
802
|
+
return False
|
|
803
|
+
return getH(ZMatVstack(Alist),N)
|
|
804
|
+
|