Incorrect eigenvalues after permute and combineBonds on symmetric cytnx.Unitensor
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:
permute→combineBonds→ diagonalize
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.
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 ]]
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!
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)?