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/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
+