83 lines
No EOL
2.9 KiB
Python
83 lines
No EOL
2.9 KiB
Python
import numpy as np
|
|
from itertools import product, combinations
|
|
|
|
I=np.array([[1,0],[0,1]],complex)
|
|
X=np.array([[0,1],[1,0]],complex)
|
|
Y=np.array([[0,-1j],[1j,0]],complex)
|
|
Z=np.array([[1,0],[0,-1]],complex)
|
|
paulis=[I,X,Y,Z]
|
|
labels=['I','X','Y','Z']
|
|
|
|
def kron_all(ops):
|
|
out=ops[0]
|
|
for op in ops[1:]: out=np.kron(out,op)
|
|
return out
|
|
|
|
def graph_state(n, edges):
|
|
psi=np.ones(2**n,complex)/np.sqrt(2**n)
|
|
# basis index bits qubit 0..n-1 as MSB? phase invariant consistent
|
|
for idx in range(2**n):
|
|
bits=[(idx>>(n-1-q))&1 for q in range(n)]
|
|
phase=1
|
|
for a,b in edges:
|
|
if bits[a]*bits[b]: phase*=-1
|
|
psi[idx]*=phase
|
|
return psi
|
|
|
|
def corr_tensor(psi,n):
|
|
coeff={}
|
|
for inds in product(range(4), repeat=n):
|
|
op=kron_all([paulis[i] for i in inds])
|
|
val=np.vdot(psi, op@psi)
|
|
if abs(val)>1e-9:
|
|
coeff[inds]=float(np.real_if_close(val))
|
|
return coeff
|
|
|
|
def matrix_for_cut(coeff,S,n, source_full=False, target_full=False):
|
|
Sc=[i for i in range(n) if i not in S]
|
|
row=[]; col=[]
|
|
for inds in product(range(4), repeat=len(S)):
|
|
if all(i==0 for i in inds): continue
|
|
if source_full and any(i==0 for i in inds): continue
|
|
row.append(inds)
|
|
for inds in product(range(4), repeat=len(Sc)):
|
|
if all(i==0 for i in inds): continue
|
|
if target_full and any(i==0 for i in inds): continue
|
|
col.append(inds)
|
|
M=np.zeros((len(col),len(row))) # target rows, source cols
|
|
for r,tinds in enumerate(col):
|
|
for c,sinds in enumerate(row):
|
|
full=[0]*n
|
|
for q,ind in zip(S,sinds): full[q]=ind
|
|
for q,ind in zip(Sc,tinds): full[q]=ind
|
|
M[r,c]=coeff.get(tuple(full),0.0)
|
|
return M,row,col
|
|
|
|
def partial_rho(psi, keep, n):
|
|
rho=np.outer(psi, psi.conj()).reshape([2]*n*2)
|
|
trace=[i for i in range(n) if i not in keep]
|
|
# trace out from high to low axes
|
|
for q in sorted(trace, reverse=True):
|
|
rho=np.trace(rho, axis1=q, axis2=q+rho.ndim//2)
|
|
d=2**len(keep)
|
|
return rho.reshape(d,d)
|
|
|
|
n=4
|
|
edges=[(0,1),(1,2),(2,3),(3,0)]
|
|
psi=graph_state(n,edges)
|
|
coeff=corr_tensor(psi,n)
|
|
print('nonzero coeffs', len(coeff))
|
|
for cutname,S in [('adjacent12|34',[0,1]),('diagonal13|24',[0,2]),('adjacent14|23',[0,3])]:
|
|
M,_,_=matrix_for_cut(coeff,S,n)
|
|
G,_,_=matrix_for_cut(coeff,S,n,source_full=True,target_full=True)
|
|
norm=np.linalg.svd(M/3, compute_uv=False).sum()
|
|
rawn=np.linalg.svd(M, compute_uv=False).sum()
|
|
gnorm=np.linalg.svd(G/3, compute_uv=False).sum()
|
|
graw=np.linalg.svd(G, compute_uv=False).sum()
|
|
print('\n',cutname)
|
|
print('full normalized',norm,'raw',rawn,'sing',np.linalg.svd(M/3,compute_uv=False))
|
|
print('genuine normalized',gnorm,'raw',graw,'sing',np.linalg.svd(G/3,compute_uv=False))
|
|
rho=partial_rho(psi,S,n)
|
|
print('rho eigen',np.linalg.eigvalsh(rho),'max mixed dist',np.linalg.norm(rho-np.eye(4)/4))
|
|
# list nonzero stabilizers labels
|
|
# print rows/cols maybe |