//  [python]
def gauge(self,peps_args=cfg.peps_args, global_args=cfg.global_args):
        r"""
        """
        def neg_(dxy): return (-dxy[0],-dxy[1])
        def add_(coord,dxy): return (coord[0]+dxy[0],coord[1]+dxy[1])
        dxy_w_to_ind= dict({(0,-1): 1, (-1,0): 2, (0,1): 3, (1,0): 4})
        expr_ws= {(0,-1): 'um', (-1,0): 'ln', (0,1): 'do', (1,0): 'rp'}

        def _get_dl_gauges(coord,direction,sites,weights):
            coord= self.vertexToSite(coord)
            A= sites[coord]
            ds_to_contract= set(dxy_w_to_ind.keys())-set((direction,))
            
            #
            #         /w^2
            #  ------A^+--w^2
            #    w^2/|/   |
            #  -----|A-----
            #       /
            expr= 'suldr,smnop,'+','.join([expr_ws[d] for d in ds_to_contract])\
                +f'->{expr_ws[direction]}'
            a= torch.einsum(expr,\
                A,A.conj(),*( (weights[(coord,d)]**2)*(1.+0j) if A.is_complex() else weights[(coord,d)]**2 \
                    for d in ds_to_contract) ).contiguous()

            # diagonalize, since a is hermitian and positive. Force ordering in descending magnitude
            # l--    l(0)--U
            #    a =       sqrt(D)^2
            # n--    n(1)--U
            D,U = torch.symeig(-a/a.abs().max(),eigenvectors=True)
            D= -D
            X= U*D.sqrt()
            D_invsqrt= D.rsqrt()
            D_invsqrt[D/D[0]<1.0e-14]=0
            Xinv= (U*D_invsqrt).t().conj()
            return X, Xinv

        def _update_weights_and_sites(sites,weights,Xs):
            # associate pair X,Y to each unique weight and update it
            new_weights, Us= dict(), dict()
            for coord in sites.keys():
                for dxy,ind in dxy_w_to_ind.items():
                    # generate weight_id and reverse weight_id
                    # (coord,dxy) identifies the same weight as (coord+dxy,-dxy) 
                    w_id= (coord, dxy)
                    w_rid= (self.vertexToSite(add_(coord,dxy)), neg_(dxy))

                    if not w_id in new_weights.keys() and not w_rid in new_weights.keys():
                        #   
                        #       |                                              |
                        #   --(0,0)-X^{-1}-X[w_id]-w[w_id]-X'[w_rid]-X'^{-1}-(1,0)--
                        #       |                                              |
                        #   => 
                        #       |                               |
                        #   --(0,0)-X^{-1}--U--S--Vh--X'^{-1}-(1,0)--
                        #       |                               |
                        #
                        U,S,Vh= torch.linalg.svd(Xs[w_id][0].t()@( \
                            weights[w_id]*(1.0+0j) if Xs[w_id][0].is_complex() else weights[w_id] ) @Xs[w_rid][0])
                        new_weights[w_id]= torch.diag(S)#/S[0]
                        new_weights[w_rid]= torch.diag(S)#/S[0]
                        Us[w_id]= U.t()
                        Us[w_rid]= Vh

            
            new_sites={}
            for coord in sites.keys():
                A= sites[self.vertexToSite(coord)]
                expr= 'smnop,'+','.join([expr_ws[d] for d in dxy_w_to_ind.keys()])+'->suldr'
                new_sites[coord]= torch.einsum(expr,A,*(Us[(coord,d)]@Xs[(coord,d)][1]\
                    for d in dxy_w_to_ind.keys())).contiguous()
                # new_sites[coord]= A/A.abs().max()

            return new_sites, new_weights

        dist=[float('inf')]
        n_s, n_w= { c: t/t.abs().max() for c,t in self.sites.items() }, self.weights
        while dist[-1]>peps_args.quasi_gauge_tol and len(dist)<peps_args.quasi_gauge_max_iter:
            # generate X, Xinv for site and bond
            Xs= { (coord,d): _get_dl_gauges(coord,d,n_s,n_w) \
                for  coord in n_s.keys() for d in [(0,-1),(-1,0),(0,1),(1,0)] }

            n_s, n_w1= _update_weights_and_sites(n_s,n_w,Xs)
            dist.append(sum([ torch.dist(n_w1[k].diag(),n_w[k].diag()) \
                for k in n_w.keys() ]).item()/len(n_s))
            n_w= n_w1

        log.info(f"gauge dist_legth: {len(dist)}, dist: {dist}")
        return type(self)(sites=n_s, weights=n_w, vertexToSite=self.vertexToSite, \
            lX=self.lX, lY=self.lY, peps_args=peps_args, global_args=global_args)