Source code for pytriqs.gf.block_gf

################################################################################
#
# TRIQS: a Toolbox for Research in Interacting Quantum Systems
#
# Copyright (C) 2011 by M. Ferrero, O. Parcollet
#
# TRIQS is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# TRIQS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from itertools import izip
from gf import Gf
import operator
import warnings
import numpy as np

def call_factory_from_dict(cl,name, dic):
    """Given a class cl and a dict dic, it calls cl.__factory_from_dict__(dic)"""
    return cl.__factory_from_dict__(name, dic)

[docs]class BlockGf(object): """ Generic Green's Function by Block. """ # not implemented as a dictionary since we want to be sure of the order !). def __init__(self, **kwargs): """ * There are several possible constructors, which accept only keyword arguments. * BlockGf(name_list = list of names, block_list = list of blocks, make_copies = False, name = '') * ``name_list``: list of the names of the blocks, e.g. ["up","down"]. * ``block_list``: list of blocks of Green's functions. * ``make_copies``: If True, it makes a copy of the blocks and build the Green's function from these copies. * BlockGf(mesh = mesh, gf_struct = block structure, target_rank = rank of target space, name = '') * ``mesh``: The mesh used to construct each block * ``target_rank``: The rank of the target space of each block (default: 2) * ``gf_struct``: List of pairs [ [str, [str,...]], ... ] providing the block name and indices, e.g. [ ['up', ['0']], ['dn', ['0']] ] * BlockGf(name_block_generator, make_copies = False, name = '') * ``name_block_generator``: a generator of (name, block) * ``make_copies``: If True, it makes a copy of the blocks and build the Green's function from these copies. """ # first extract the optional name argument self.name = kwargs.pop('name','G') self.note = kwargs.pop('note','') self._rename_gf = kwargs.pop('rename_gf',True) # Default arguments if set(kwargs.keys()) == set(['mesh','gf_struct']): kwargs['target_rank'] = 2 if 'block_list' in kwargs.keys() and 'make_copies' not in kwargs.keys(): kwargs['make_copies'] = False if set(kwargs.keys()) == set(['block_list','make_copies']): kwargs['name_list'] = [str(i) for i in range(len(kwargs['block_list']))] if set(kwargs.keys()) == set(['name_list','block_list','make_copies']): BlockNameList, GFlist = kwargs['name_list'],kwargs['block_list'] assert all([isinstance(name,str) for name in BlockNameList]), "Error in BlockGf Construction: Block-Names must be Strings" elif set(kwargs.keys()) == set(['mesh','gf_struct','target_rank']): BlockNameList = [] GFlist = [] for bl, idx_lst in kwargs['gf_struct']: BlockNameList.append(bl) if len(idx_lst) > 0 and kwargs['target_rank'] > 0: GFlist.append(Gf(mesh=kwargs['mesh'], target_shape=[len(idx_lst)]*kwargs['target_rank'], name='G_%s'%bl, indices=[idx_lst]*kwargs['target_rank'])) else: GFlist.append(Gf(mesh=kwargs['mesh'], target_shape=[], name='G_%s'%bl)) elif set(kwargs.keys()) == set(['name_block_generator','make_copies']): BlockNameList,GFlist = zip(* kwargs['name_block_generator']) else: raise RuntimeError, "BlockGf construction: error in parameters, see the documentation" if kwargs.get('make_copies', False): GFlist = [g.copy() for g in GFlist] # First a few checks assert GFlist !=[], "Empty list of blocks !" for ind in BlockNameList: assert str(ind)[0:2] !='__', "indices should not start with __" assert len(set(BlockNameList)) == len(BlockNameList),"Block indices of the Green Function are not unique" assert len(BlockNameList) == len(GFlist), "Number of indices and of Green Function Blocks differ" assert 'block_names' not in BlockNameList, "'block_names' is a reserved keyword. It is not authorized as a block name ! " # All blocks are compatible for binary operation # --> correction: All blocks have the same type #if not reduce (operator.and_,[ GFlist[0]._is_compatible_for_ops(x) for x in GFlist[1:] ] , True): # raise RuntimeError, "The blocks are not compatible for binary operations: not the same type, same temperature, etc..." if len(set([ type(g) for g in GFlist])) != 1: raise RuntimeError, "BlockGf: All block must have the same type %s"%GFlist # init self.__indices,self.__GFlist = BlockNameList,GFlist try: self.__me_as_dict = dict(self) except TypeError: raise TypeError, "indices are not of the correct type" self.__BlockIndexNumberTable = dict( (i,n) for n,i in enumerate(self.__indices) ) # a dict: index -> number of its order # Add the name to the G self.note = '' if self._rename_gf: for i,g in self: g.name = "%s_%s"%(str(self.name),i) if self.name else '%s'%(i,) del self._rename_gf #------------ copy and construction -----------------------------------------------
[docs] def copy(self,*args): """Returns a (deep) copy of self (i.e. new independent Blocks initialised with self) """ return self.__class__ (name_list = self.__indices[:], block_list = [ g.copy(*args) for g in self.__GFlist],make_copies=False)
def view_selected_blocks(self, selected_blocks): """Returns a VIEW of the selected blocks of self, in the same order as self.""" for b in selected_blocks: assert b in self.__indices,"Selected Blocks must be existing blocks" return self.__class__ ( name_block_generator = [(n,g) for n,g in self if n in selected_blocks ],make_copies=False) def copy_selected_blocks(self, selected_blocks): """Returns a COPY of the selected blocks of self, in the same order as self.""" for b in selected_blocks: assert b in self.__indices,"Selected Blocks must be existing blocks" return self.__class__ ( name_block_generator = [(n,g) for n,g in self if n in selected_blocks ],make_copies=True)
[docs] def copy_from(self, G2): """Copy the Green's function from G2: G2 MUST have the same structure!""" assert isinstance(G2, BlockGf) for (i,g),(i2,g2) in itertools.izip(self,G2): if (g.target_shape[0],g.target_shape[1]) != (g2.target_shape[0],g2.target_shape[1]): raise RuntimeError, "Blocks %s and %s of the Green Function do have the same dimension"%(i1,i2) for (i,g),(i2,g2) in itertools.izip(self,G2): g.copy_from(g2)
#-------------- Iterators ------------------------- def __iter__(self): return izip(self.__indices,self.__GFlist) #--------------------------------------------------------------------------------- def _first(self): return self.__GFlist[0] def __len__(self): return len(self.__GFlist) #---------------- Properties ------------------------------------------- # Deprecated. Left for backward compatibility ?? but # it would not work for Legendre @property def mesh(self): """Deprecated: backward compatibility only""" return self._first().mesh @property def beta(self): """ Inverse Temperature""" return self._first().beta @property def indices(self): """A generator of the block indices""" for ind in self.__indices: yield ind @property def all_indices(self): """ An Iterator on BlockGf indices and indices of the blocs of the form: block_index,n1,n2, where n1,n2 are indices of the block""" for sig,g in self: val = g.indices for x in val: for y in val: yield (sig,x,y) @property def n_blocks(self): """ Number of blocks""" return len(self.__GFlist) @property def real(self): """A Gf with only the real part of data.""" return BlockGf(name_list = self.__indices, block_list = [g.real for g in self.__GFlist], name = ("Re " + self.name) if self.name else '') @property def imag(self): """A Gf with only the imag part of data.""" return BlockGf(name_list = self.__indices, block_list = [g.imag for g in self.__GFlist], name = ("Im " + self.name) if self.name else '') #---------------------- IO ------------------------------------- def __mymakestring(self,x): return str(x).replace(' ','').replace('(','').replace(')','').replace("'",'').replace(',','-') def save(self, filename, accumulate=False): """ Save the Green function in i omega_n (as 2 columns). - accumulate: """ for i,g in self: g.save( "%s_%s"%(filename, self.__mymakestring(i)), accumulate) def load(self, filename, no_exception = False): """ adjust_temperature: if true, """ for i,g in self: try: g.load( "%s_%s"%(filename,self.__mymakestring(i))) except: if not(no_exception): raise def __reduce__(self): return call_factory_from_dict, (self.__class__,self.name, self.__reduce_to_dict__()) def __reduce_to_dict__(self): d = dict(self) d['block_names'] = np.array(list(self.indices)) return d @classmethod def __factory_from_dict__(cls, name, d): # indices : for backward compatibility. indices is str repr of the # indices list and we need to drop name and note ... # block_names in str-mapped just to make sure that the key are python str (they could be numpy.string_, see __reduce_to_dict__) keys = map(str,d.pop('block_names')) if 'block_names' in d else eval(d.pop('indices')) assert (sorted(keys) == sorted(d.keys())) or (sorted(keys + ['note', 'name']) == sorted(d.keys())), "Reload mismatch: the indices and the group names do not corresponds" res = cls(name_list = keys, block_list = [d[k] for k in keys], make_copies=False, name=name) return res #-------------- Pretty print ------------------------- def __repr__(self): s = "Green Function %s composed of %d blocks: \n"%(self.name,self.n_blocks) #s = "Green Function %s composed of %d blocks at inverse temperature beta = %s: \n"%(self.name,self.n_blocks,self.beta) for i,g in self: s += " %s \n"%repr(g) #" Block %s: indices %s \n"%(i,self[i].indices) if self.note: s += "NB: %s\n"%self.note return s def __str__ (self): return self.name if self.name else repr(self) #-------------- Bracket operator [] ------------------------- def __getitem__(self,key): try: g = self.__me_as_dict[key] except KeyError: raise IndexError, "bloc index '" + repr(key) + "' incorrect. Possible indices are: "+ repr(self.__indices) return g def __setitem__(self,key,val): if key == slice(None, None, None): # G[:] = XXX is the same as G << XXX self << val return try: g = self.__me_as_dict[key] except KeyError: raise IndexError, "bloc index '" + repr(key) + "' incorrect. Possible indices are: "+ repr(self.__indices) g << val # -------------- Various operations ------------------------------------- def __le__(self, other): raise RuntimeError, " Operator <= not defined " def __lshift__(self, A): """ A can be 2 things: * G << any_init will init all the BlockGf with the initializer * G << g2 where g2 is a BlockGf will copy g2 into self """ if isinstance(A, self.__class__): for (i,g) in self: g.copy_from(A[i]) elif hasattr(A, "is_block_descriptor") and A.is_block_descriptor(): for g,a in zip(self.__GFlist, A): g << a else: for g in self.__GFlist: g << A return self def __ilshift__(self, A): """ A can be 2 things: * G << any_init will init all the BlockGf with the initializer * G << g2 where g2 is a BlockGf will copy g2 into self """ if isinstance(A, self.__class__): for (i,g) in self: g.copy_from(A[i]) else: try: for i,g in self: g << A except: return NotImplemented return self def __iadd__(self,arg): if isinstance(arg, self.__class__): for (n,g) in self: self[n] += arg[n] elif operator.isSequenceType(arg): assert len(arg) == len(self.__GFlist), "list of incorrect length" for l,g in izip(arg,self.__GFlist): g +=l else: for i,g in self: g += arg return self def __add__(self,y): c = self.copy() c += y return c def __radd__(self,y): return self.__add__(y) def __isub__(self,arg): if isinstance(arg, self.__class__): for (n,g) in self: self[n] -= arg[n] elif operator.isSequenceType(arg): assert len(arg) == len(self.__GFlist) , "list of incorrect length" for l,g in izip(arg,self.__GFlist): g -=l else: for i,g in self: g -= arg return self def __sub__(self,y): c = self.copy() c -= y return c def __rsub__(self,y): c = (-1)*self.copy() c += y return c def __imul__(self,arg): if isinstance(arg, BlockGf): for (n,g) in self: self[n] *= arg[n] else: for i,g in self: g *= arg return self def __mul__(self,y): c = self.copy() c *= y return c def __rmul__(self,x): return self.__mul__(x) def __idiv__(self,arg): if operator.isSequenceType(arg): assert len(arg) == len(self.__GFlist) , "list of incorrect length" for l,g in izip(arg,self.__GFlist): g /=l else: for i,g in self: self[i] /= arg return self def __div__(self,y): c = self.copy() c /= y return c def __neg__(self): c = self.copy() c *= -1 return c #-----------------------------plot protocol ----------------------------------- def _plot_(self, opt_dict): """ Implement the plot protocol""" r = [] for sig,g in self: initial_dict = opt_dict.copy() r += g._plot_(initial_dict) self.name, name_kept = self.name, opt_dict.pop('name', self.name) first_g_name = self.__GFlist[0].name ylabel = r[0]['ylabel'].replace(first_g_name, self.name) if first_g_name else self.name for dic in r: dic['ylabel'] = ylabel # replace the ylabel of the elements to a single ylabel self.name = name_kept return r #-------------------------------------------------------------------------- def zero(self): for i,g in self: g.zero() def density(self, *args, **kwargs): """Returns the density as a dict of density of the blocks""" return dict( (s,g.density(*args, **kwargs)) for s,g in self ) def total_density(self, *args, **kwargs): """ Total density of G """ return sum([ g.total_density(*args, **kwargs) for i,g in self ]) def __check_attr(self,ATTR): if not hasattr(self._first(), ATTR ): raise RuntimeError, "The blocks of this Green Function do not possess the %s method"%ATTR def invert(self): """Inverse all the blocks inplace""" self.__check_attr("invert") for i,g in self: g.invert() def inverse(self): """Return inverse of the BlockGf""" self.__check_attr("inverse") return self.__class__( name_block_generator = [ (n, g.inverse()) for n,g in self], make_copies=False) def transpose(self): """Return transpose of the BlockGf""" self.__check_attr("transpose") return self.__class__( name_block_generator = [ (n, g.transpose()) for n,g in self], make_copies=False) def conjugate(self): """Return conjugate of the BlockGf""" self.__check_attr("conjugate") return self.__class__( name_block_generator = [ (n, g.conjugate()) for n,g in self], make_copies=False) # def make_real_in_tau(self): # """ """ # self.__check_attr("make_real_in_tau") # return self.__class__( name_block_generator = [ (n, g.make_real_in_tau()) for n,g in self], make_copies=False) #-------------- Fourier Backward Compatibility. --------------------------- def set_from_inverse_fourier(self, *args) : warnings.warn("set_from_inverse_fourier is deprecated and should be replaced with set_from_fourier") self.set_from_fourier(*args)
#--------------------------------------------------------- from pytriqs.archive.hdf_archive_schemes import register_class register_class (BlockGf)