This question deals with somewhat uncommon usage of the np_conserved module. I would greatly appreciate help, as I suspect there may be a bug with the npc.detect_legcharge function.
I am using the np_conserved module to create my own MPO. The purpose is to simulate a layer of two qubit gates. Suppose I have a 2 qubit unitary \(G\) acting on qubits q1 and q2. I SVD \(G = U\Lambda V\), then define \(A = U\Lambda \) and \(B = V\). Then I can define an MPO for the 2 qubit gate as
Python: Select all
A-I-I-I-I-..B
More generally, I can apply a layer of 2 qubit gates simultaneously in one MPO as long as the supports of the identity strings in between A and B do not overlap as:
Python: Select all
A1-I-I...-B1-i-i-A2-I-I-,,,-B2..-i-An ... -I-Bn
I know that the two qubit gates G I am using preserve particle number, so I would like the MPO tensors to respect a U(1) charge conservation. In general, each of the sets of As and Bs could have different dimension after the SVD, so I do not want to pre-define leg charges for each tensor, and instead want to use
Python: Select all
npc.detect_legcharge()
The following code shows how I do this for a single two qubit gate U acting on sites (ind1, ind2) of the MPO/MPS.
Python: Select all
def two_q_gate_MPO(sites, U, ind1, ind2):
chinfo = npc.ChargeInfo([1], ['2*Sz'])
leg_charge_p1 = npc.LegCharge.from_qflat(chinfo, [[-1],[1]], qconj=1,)
leg_charge_p2 = npc.LegCharge.from_qflat(chinfo, [[-1], [1]], qconj=-1)
leg_charge_left = npc.LegCharge.from_qflat(chinfo, [[0]], qconj=1) #free to choose wL charge of left-most tensor
L = len(sites)
Ws = [None for _ in range(L)]
Id = np.eye(2)
for i in range(ind1):
if i == 0:
charge_idL = npc.detect_legcharge(Id[None,None,:,:],chinfo,[leg_charge_left, None, leg_charge_p1, leg_charge_p2], qconj=-1) #use left-most charge freedom
else:
charge_idL = npc.detect_legcharge(Id[None,None,:,:],chinfo,[Ws[i-1].legs[1].conj(), None, leg_charge_p1, leg_charge_p2], qconj=-1) #detect charge of wR using p, p* known charges and wR.conj() charge of previous tensor
Ws[i] = npc.Array.from_ndarray(Id[None,None,:,:], charge_idL, labels=['wL', 'wR', 'p', 'p*'])
U = np.reshape(U,(2,2,2,2))
U = np.reshape(np.transpose(U,[0,2,1,3]), (4,4)) #reshape to wL,p,wR,p* then split for SVD
u,s,vh = sp.linalg.svd(U, full_matrices=False)
sprime = np.trim_zeros(s)
uprime = u[:,:len(sprime)]
vhprime = vh[:len(sprime),:]
A = np.reshape(uprime@np.diag(sprime), (2,2,len(sprime)))
A = np.transpose(A, [2,0,1])
A = A[None, :, : , :]
B = np.reshape(vhprime, (len(sprime),2,2))
B = B[:,None,:,:]
assert A.shape[1] == B.shape[0]
bond_dim = A.shape[1]
I_tensor = (np.kron(np.eye(bond_dim), np.eye(2))).reshape(bond_dim,2,bond_dim,2).swapaxes(1,2) #kronecker delta on both physical and bond
#charge for A
if ind1 == 0:
charge1 = npc.detect_legcharge(A,chinfo,[leg_charge_left, None, leg_charge_p1, leg_charge_p2], qconj=-1) #use detect leg charge with left_charge
else:
charge1 = npc.detect_legcharge(A,chinfo,[Ws[ind1-1].legs[1].conj(), None, leg_charge_p1, leg_charge_p2], qconj=-1)
Ws[ind1] = npc.Array.from_ndarray(A, charge1, labels=['wL', 'wR', 'p', 'p*'])
#put I_tensor in between A and B
for i in range(ind1+1, ind2):
charge_Itensor = npc.detect_legcharge(I_tensor,chinfo,[Ws[i-1].legs[1].conj(), None, leg_charge_p1, leg_charge_p2], qconj=-1)
Ws[i] = npc.Array.from_ndarray(I_tensor, charge_Itensor, labels=['wL', 'wR', 'p', 'p*'])
#charge for B
charge2 = npc.detect_legcharge(B,chinfo,[Ws[ind2-1].legs[1].conj(), None, leg_charge_p1, leg_charge_p2], qconj=-1)
Ws[ind2] = npc.Array.from_ndarray(B, charge2, labels=['wL', 'wR', 'p', 'p*'])
#identity on last sites from B to end
for i in range(ind2+1, len(sites)):
charge_Idlast = npc.detect_legcharge(Id[None,None,:,:],chinfo,[Ws[i-1].legs[1].conj(), None, leg_charge_p1, leg_charge_p2], qconj=-1)
Ws[i] = npc.Array.from_ndarray(Id[None,None,:,:], charge_Idlast, labels=['wL', 'wR', 'p', 'p*'])
return MPO(sites, Ws, 'finite', 0, -1)
Python: Select all
Ws[ind1] = npc.Array.from_ndarray(A, charge1, labels=['wL', 'wR', 'p', 'p*'])
Python: Select all
ValueError: wrong sector with non-zero entries