Cytnx icon indicating copy to clipboard operation
Cytnx copied to clipboard

Incorrect eigenvalues after permute and combineBonds on symmetric cytnx.Unitensor

Open Chan-Sing-Hong opened this issue 5 months ago • 3 comments

Description The attached code (test_permute_combineBonds.zip) demonstrates a problem when diagonalizing a cytnx.UniTensor with Z2 symmetry. Specifically, when applying permute followed by combineBonds on a symmetric tensor, the resulting eigenvalues from cytnx.linalg.Eig is incorrect.

Test Overview The figure below illustrates three tests performed in the demo code for both symmetric and non-symmetric tensors:

  • Test 1: diagonalize directly
  • Test 2: permute → diagonalize
  • Test 3: permutecombineBonds → diagonalize Image

Results All three tests should yield consistent eigenvalues for both symmetric and non-symmetric tensors. However, test 3 produces an incorrect eigenspectrum for symmetric tensors. This suggests that the combination of permute and combineBonds may mishandle the elements or block structure for symmetric UniTensor.

Chan-Sing-Hong avatar Jul 17 '25 08:07 Chan-Sing-Hong

I think the problem is in the UniTensor.combineBonds(). In the following I provide an example: Following two should get identical resuts, but they do not.

  • convert a symmetric tensor into a non-symmetic tensor, then combineBonds.
  • combineBonds for a symmetric tensor, then convert it into a non-symmetric tensor.
import cytnx
import numpy as np

ba = cytnx.Bond(cytnx.BD_IN, [[0],[1],[1],[0]], [1,1,1,1], [cytnx.Symmetry.Zn(2)])
bb = cytnx.Bond(cytnx.BD_OUT, [[0],[1]], [1,1], [cytnx.Symmetry.Zn(2)])
bc = cytnx.Bond(cytnx.BD_OUT, [[0],[1]], [1,1], [cytnx.Symmetry.Zn(2)])
T_sym = cytnx.UniTensor([ba, bb, bc], rowrank=1).relabel(["a","b","c"]).set_name("T_sym")
# T_sym.print_diagram()
T = cytnx.UniTensor.zeros([4,2,2]).relabel(["a","b","c"]).set_name("T")
# T.print_diagram()
value = 1
qa = [0,1,1,0]
qb = [0,1]
qc = [0,1]
for a in range(4):
    for b in range(2):
        for c in range(2):
            if T_sym.at([a,b,c]).exists() == True :
                # print("a={},b={},c={},qa={},qb={},qc={},value={}".format(a,b,c,qa[a],qb[b],qc[c],value))
                T_sym.at([a,b,c]).value = value
                T.at([a,b,c]).value = value
                value+=1
# print(T_sym)
# print(T)
# print(T.reshape(4,4))
T_convert = cytnx.UniTensor.zeros([4,2,2]).relabel(["a","b","c"]).set_name("T_convert")
T_convert.print_diagram()
T_convert.convert_from(T_sym)
# print(T_convert)
# print(T_convert.reshape(4,4))
TT = T.clone().set_name("TT")
TT.combineBonds(["b","c"])
TT.print_diagram()
print(TT)
TT_sym = T_sym.clone().set_name("TT_sym")
TT_sym.combineBonds(["b","c"])
TT_sym.print_diagram()
# print(TT_sym)
TT_sym_convert = cytnx.UniTensor.zeros([4,4]).relabel(["a","b"]).set_name("TT_sym_convert")
TT_sym_convert.convert_from(TT_sym)
TT_sym_convert.print_diagram()
print(TT_sym_convert)

The output looks like

-----------------------
tensor Name : T_convert
tensor Rank : 3
block_form  : False
is_diag     : False
on device   : cytnx device: CPU
          ---------     
         /         \    
   a ____| 4     2 |____ b
         |         |    
         |       2 |____ c
         \         /    
          ---------     
-----------------------
tensor Name : TT
tensor Rank : 2
block_form  : False
is_diag     : False
on device   : cytnx device: CPU
          ---------     
         /         \    
   a ____| 4     4 |____ b
         \         /    
          ---------     
-------- start of print ---------
Tensor name: TT
is_diag    : False
contiguous : True

Total elem: 16
type  : Double (Float64)
cytnx device: CPU
Shape : (4,4)
[[1.00000e+00 0.00000e+00 0.00000e+00 2.00000e+00 ]
 [0.00000e+00 3.00000e+00 4.00000e+00 0.00000e+00 ]
 [0.00000e+00 5.00000e+00 6.00000e+00 0.00000e+00 ]
 [7.00000e+00 0.00000e+00 0.00000e+00 8.00000e+00 ]]




-----------------------
tensor Name : TT_sym
tensor Rank : 2
contiguous  : True
valid blocks : 2
is diag   : False
on device   : cytnx device: CPU
      row           col 
         -----------    
         |         |    
   a  -->| 4     4 |-->  b
         |         |    
         -----------    

-----------------------
tensor Name : TT_sym_convert
tensor Rank : 2
block_form  : False
is_diag     : False
on device   : cytnx device: CPU
          ---------     
         /         \    
   a ____| 4     4 |____ b
         \         /    
          ---------     
-------- start of print ---------
Tensor name: TT_sym_convert
is_diag    : False
contiguous : True

Total elem: 16
type  : Double (Float64)
cytnx device: CPU
Shape : (4,4)
[[1.00000e+00 2.00000e+00 0.00000e+00 0.00000e+00 ]
 [7.00000e+00 8.00000e+00 0.00000e+00 0.00000e+00 ]
 [0.00000e+00 0.00000e+00 3.00000e+00 4.00000e+00 ]
 [0.00000e+00 0.00000e+00 5.00000e+00 6.00000e+00 ]]

pcchen avatar Jul 21 '25 15:07 pcchen

I think in this case one should use is_grp: bool = True when combing bonds.

Then I realize that that cytnx.Bond.combineBonds and cytnx.Bond.combineBonds support is_grp: bool, but cytnx.UniTensor.combineBonds DONOT support is_grp: bool!

pcchen avatar Jul 21 '25 15:07 pcchen

I think we need to implement combiner and splitter to fully fix this issus and related ones.

However, for now can we change the default value of is_grp to false as a work around (for some cases)?

pcchen avatar Sep 24 '25 09:09 pcchen