@@ -76,6 +76,77 @@ TEST_F(BlockFermionicUniTensorTest, LinAlgElementwise) {
7676 EXPECT_EQ (AreNearlyEqUniTensor (res.apply_ (), BFUT3.Pow (2 .).apply (), tol), true );
7777}
7878
79+ /* =====test info=====
80+ describe:test fermion_twists behavior on tagged fermionic tensors
81+ ====================*/
82+ TEST_F (BlockFermionicUniTensorTest, FermionTwists) {
83+ UniTensor twisted = BFUT5.fermion_twists ();
84+ UniTensor manual = BFUT5.clone ();
85+ for (cytnx_int64 idx = manual.rowrank (); idx < manual.rank (); idx++) {
86+ if (manual.bonds ()[idx].type () != BD_BRA) manual.twist_ (idx);
87+ }
88+ EXPECT_EQ (twisted.signflip (), manual.signflip ());
89+ EXPECT_TRUE (AreEqUniTensor (twisted.apply_ (), manual.apply_ ()));
90+
91+ // applying fermion_twists_ twice toggles the same set of signs twice
92+ UniTensor twice = BFUT5.clone ();
93+ twice.fermion_twists_ ().fermion_twists_ ();
94+ EXPECT_EQ (twice.signflip (), BFUT5.signflip ());
95+ EXPECT_TRUE (AreEqUniTensor (twice.apply_ (), BFUT5.apply ()));
96+ }
97+
98+ /* =====test info=====
99+ describe:test SVD unitarity and reconstruction for fermionic tensors with mixed in/out legs
100+ ====================*/
101+ TEST_F (BlockFermionicUniTensorTest, SvdUnitaryAndReconstruction) {
102+ const double tol = 1e-10 ;
103+
104+ std::vector<UniTensor> svd_out = linalg::Svd_truncate (BFUT5, 10 , 0 ., true , 0 );
105+ ASSERT_EQ (svd_out.size (), 3 );
106+ UniTensor S = svd_out[0 ].set_name (" S" );
107+ UniTensor U = svd_out[1 ].set_name (" U" );
108+ UniTensor vT = svd_out[2 ].set_name (" vT" );
109+ UniTensor Udag = U.Dagger ().set_name (" Udag" );
110+ Udag.relabel_ (" _aux_L" , " _aux_L_dag" );
111+ UniTensor vTdag = vT.Dagger ().set_name (" vTdag" );
112+ vTdag.relabel_ (" _aux_R" , " _aux_R_dag" );
113+
114+ // Check U^dagger U = I on the auxiliary left space.
115+ UniTensor UdagU = Contract (Udag.fermion_twists (), U);
116+ UniTensor Iu = 0 . * UdagU.clone (); // will be identity matrix
117+ auto shape_u = Iu.shape ();
118+ for (cytnx_uint64 k = 0 ; k < shape_u[0 ]; k++) {
119+ auto proxy = Iu.at ({k, k});
120+ proxy = 1.0 ;
121+ }
122+ EXPECT_TRUE (AreNearlyEqUniTensor (UdagU.apply (), Iu, tol));
123+
124+ // Check vT vT^dagger = I on the auxiliary right space.
125+ UniTensor vTvTdag = Contract (vT.fermion_twists (), vTdag);
126+ UniTensor Iv = 0 . * vTvTdag.clone (); // will be identity matrix
127+ auto shape_v = Iv.shape ();
128+ for (cytnx_uint64 k = 0 ; k < shape_v[0 ]; k++) {
129+ auto proxy = Iv.at ({k, k});
130+ proxy = 1.0 ;
131+ }
132+ EXPECT_TRUE (AreNearlyEqUniTensor (vTvTdag.apply (), Iv, tol));
133+
134+ // Check U^dagger * BFUT5 * V^dagger = S (where V^dagger is returned as vT).
135+ UniTensor Scontr = Contract (Contract (Udag.fermion_twists (), BFUT5.fermion_twists ()), vTdag);
136+ Scontr.relabel_ (" _aux_L_dag" , " _aux_L" );
137+ Scontr.relabel_ (" _aux_R_dag" , " _aux_R" );
138+ Scontr.permute_ (S.labels ());
139+ UniTensor Sref = 0 . * Scontr.clone (); // Creating Sref, a dense version of S
140+ S.apply_ ();
141+ auto shape_s = Sref.shape ();
142+ for (cytnx_uint64 k = 0 ; k < shape_s[0 ]; k++) {
143+ auto pref = Sref.at ({k, k});
144+ auto ps = S.at ({k, k});
145+ if (pref.exists () && ps.exists ()) pref = Scalar (ps);
146+ }
147+ EXPECT_TRUE (AreNearlyEqUniTensor (Scontr.apply (), Sref.apply (), tol));
148+ }
149+
79150TEST_F (BlockFermionicUniTensorTest, group_basis) {
80151 auto out = BFUT4.group_basis ();
81152
0 commit comments