def arg_is_dsl_binop(arg, upd=False): """If arg is dsl_binop, then the dsl_binop should represent the addition or subtraction of an integer literal from a index or auxiliary.""" if arg.op() is op_add or arg.op() is op_sub: if isinstance_list(arg.left(), self._indextypes): if isinstance(arg.right(), int): c = arg.right() a = arg.left() op = arg.op() self.add_index(a, c, op, update=upd) return None else: raise Exception('Only integer literals '\ +'can be added to an index') elif isinstance_list(arg.left(), self._auxtypes): if isinstance(arg.right(), int): c = arg.right() a = arg.left() op = arg.op() self.add_auxiliary(a, c, op, update=upd) return None else: raise Exception('Only integer literals '\ +'can be added to an index') else: raise Exception('binary operation argument must take the form '+\ '[index_object] + [integer] or '+\ '[auxiliary_object] + [integer]') else: raise Exception('Can only accept addition or subtraction '\ +'for increments')
def arg_is_dsl_binop(arg,upd=False): """If arg is dsl_binop, then the dsl_binop should represent the addition or subtraction of an integer literal from a index or auxiliary.""" if arg.op() is op_add or arg.op() is op_sub: if isinstance_list(arg.left(),self._indextypes ): if isinstance(arg.right(),int): c = arg.right() a = arg.left() op = arg.op() self.add_index(a,c,op,update=upd) return None else: raise Exception('Only integer literals '\ +'can be added to an index') elif isinstance_list(arg.left(),self._auxtypes): if isinstance(arg.right(),int): c = arg.right() a = arg.left() op = arg.op() self.add_auxiliary(a,c,op,update=upd) return None else: raise Exception('Only integer literals '\ +'can be added to an index') else: raise Exception('binary operation argument must take the form '+\ '[index_object] + [integer] or '+\ '[auxiliary_object] + [integer]') else: raise Exception('Can only accept addition or subtraction '\ +'for increments')
def add_auxiliary(self, aux,change=0,op=op_add,update=False): """Adds a DSL object (e.g. dsl_integer_index) to a list of integral auxiliary indices and a dictionary of auxiliary indices. These are indices that define integral intermediates, but are not required to define the final end-result class of integrals. If update == True, do not add a new index, but modify and existing dsl_binop in self._index_dict with updated change and op. Implementation notes: * Currently this set of indices must be all of the same type, e.g. all dsl_integer_index objects. In future this may be expanded to allow for integrals with mixed auxiliary index types. * Only a single dsl_integer_index object is currently supported, and this can only be incremented in RR expressions (no decrements). * It is assumed that the auxiliary indices are equal to zero in the final end-result class of integrals. """ assert isinstance_list(aux,self._auxtypes),\ 'aux must be one of the allowed types in self._auxtypes' self._add_obj(aux,self._aux_list,\ dsl_binop(op,aux,change),self._aux_dict,\ update) # self._aux_list.append(aux) # self._aux_dict[ aux.name() ] = dsl_binop(op,aux,change) # [ Current implementation restriction ] assert len( self._aux_list ) <= 1, 'only one auxiliary allowed' assert op == op_add, 'only increments allowed in auxiliary index'
def add_auxiliary(self, aux, change=0, op=op_add, update=False): """Adds a DSL object (e.g. dsl_integer_index) to a list of integral auxiliary indices and a dictionary of auxiliary indices. These are indices that define integral intermediates, but are not required to define the final end-result class of integrals. If update == True, do not add a new index, but modify and existing dsl_binop in self._index_dict with updated change and op. Implementation notes: * Currently this set of indices must be all of the same type, e.g. all dsl_integer_index objects. In future this may be expanded to allow for integrals with mixed auxiliary index types. * Only a single dsl_integer_index object is currently supported, and this can only be incremented in RR expressions (no decrements). * It is assumed that the auxiliary indices are equal to zero in the final end-result class of integrals. """ assert isinstance_list(aux,self._auxtypes),\ 'aux must be one of the allowed types in self._auxtypes' self._add_obj(aux,self._aux_list,\ dsl_binop(op,aux,change),self._aux_dict,\ update) # self._aux_list.append(aux) # self._aux_dict[ aux.name() ] = dsl_binop(op,aux,change) # [ Current implementation restriction ] assert len(self._aux_list) <= 1, 'only one auxiliary allowed' assert op == op_add, 'only increments allowed in auxiliary index'
def add_argument(self, arg, update=False): """Adds a DSL object (e.g. dsl_scalar) to a list and dictionary of arguments required to define the integral class in addition to the indices and auxiliaries. For example, a program for evaluating nuclear attraction integrals ( a | 1/rC | b ) requires an argument specifying the nuclear centre 'C', as well as arguments associated with the two indices 'a' and 'b'. A DSL object added to the list/dictionary of arguments will be appended to the arguments for the subroutine used to generate the integral class and can therefore be passed in by the calling unit. """ assert isinstance_list(arg,self._argtypes),\ 'arg must be one of the allowed types in self._argtypes' self._add_obj(arg,self._arg_list,\ arg,self._arg_dict,\ update)
def add_index(self,index,change=0,op=op_add,update=False): """Adds a DSL object (e.g. dsl_cartesian_gaussian) to a list of integral indices and a dictionary of indices.These are non-auxiliary indices, i.e. they define the desired end-result class of integrals. If update == True, do not add a new index, but modify and existing dsl_binop in self._index_dict with updated change and op. Implementation notes: * Currently this set of indices must be all of the same type, e.g. all dsl_cartesian_gaussian objects. In future this may be expanded to allow for integrals with mixed index types.""" assert isinstance_list(index,self._indextypes),\ 'index must be one of the allowed types in self._indextypes' self._add_obj(index,self._index_list,\ dsl_binop(op,index,change),self._index_dict,\ update)
def add_index(self, index, change=0, op=op_add, update=False): """Adds a DSL object (e.g. dsl_cartesian_gaussian) to a list of integral indices and a dictionary of indices.These are non-auxiliary indices, i.e. they define the desired end-result class of integrals. If update == True, do not add a new index, but modify and existing dsl_binop in self._index_dict with updated change and op. Implementation notes: * Currently this set of indices must be all of the same type, e.g. all dsl_cartesian_gaussian objects. In future this may be expanded to allow for integrals with mixed index types.""" assert isinstance_list(index,self._indextypes),\ 'index must be one of the allowed types in self._indextypes' self._add_obj(index,self._index_list,\ dsl_binop(op,index,change),self._index_dict,\ update)
def parse_args(self,args,copy_from = None): """Goes through a tuple of arguments passed to the __init__() method and updates the corresponding object attributes. If copy_from is set to a dsl_integral object, parse_args additionally checks that the index, auxilary and argument objects in args are in copy_from._index_list, copy_from._aux_list and copy_from._arg_list, i.e. if the new object is being copied from another, no new indexes, auxiliary or arguments may be added. """ def arg_is_dsl_binop(arg,upd=False): """If arg is dsl_binop, then the dsl_binop should represent the addition or subtraction of an integer literal from a index or auxiliary.""" if arg.op() is op_add or arg.op() is op_sub: if isinstance_list(arg.left(),self._indextypes ): if isinstance(arg.right(),int): c = arg.right() a = arg.left() op = arg.op() self.add_index(a,c,op,update=upd) return None else: raise Exception('Only integer literals '\ +'can be added to an index') elif isinstance_list(arg.left(),self._auxtypes): if isinstance(arg.right(),int): c = arg.right() a = arg.left() op = arg.op() self.add_auxiliary(a,c,op,update=upd) return None else: raise Exception('Only integer literals '\ +'can be added to an index') else: raise Exception('binary operation argument must take the form '+\ '[index_object] + [integer] or '+\ '[auxiliary_object] + [integer]') else: raise Exception('Can only accept addition or subtraction '\ +'for increments') def copy_from_check(obj): """Raises and AssertionError if obj is not present in the the _index_list, _aux_list or _arg_list attributes of copy_from.""" all_obj_list = copy_from._index_list + copy_from._aux_list +\ copy_from._arg_list assert obj in all_obj_list,\ obj.name()+' not present in copy_from. If copy_from is set, '+\ 'obj must have been added to the copy_from dsl_integral instance.' if copy_from != None: upd = True else: upd = False i = 1 for arg in args: obj = arg if isinstance(arg,dsl_binop): arg_is_dsl_binop(arg,upd) obj = arg.left() elif isinstance_list(arg,self._indextypes): # No change in a Cartesian Gaussian self.add_index(arg,update=upd) elif isinstance_list(arg,self._auxtypes): # No change in an auxiliary index self.add_auxiliary(arg,update=upd) elif isinstance_list(arg,self._argtypes): self.add_argument(arg,update=upd) else: raise Exception('Argument number '+str(i)+': '+str(arg)+\ ' not understood.') if upd == True: copy_from_check(obj) i +=1 if upd==True: # Additionally ensure that the same number of indexes, auxiliaries, # and arguments are present in a copy and original assert len( copy_from._index_list ) == len( self._index_list ),\ 'cannot change number of indexes in copy' assert len( copy_from._aux_list ) == len( self._aux_list ),\ 'cannot change number of auxiliaries in copy' assert len( copy_from._arg_list ) == len( self._arg_list ),\ 'cannot change number of arguments in copy'
def expr_permute(expr,a,b) : """ Permute indices a and b in dsl_integral, dsl_cartesian_gaussian and dsl_variable objects in an RR expression. The returned expression is a copy of expr, but with the dsl_index objects a and b swapped. Additionally, any dsl_variable (or derived class) objects which are attached to the index objects (i.e. returned by dsl_index.attachment_list) are also swapped in the expression. dsl_variable objects for which expr() returns a DSL expression containing other dsl_variable objects will be replaced with new dsl_variable objects with the expr() permuted in the same way. This is done recursively, so a dsl_variable.expr() is treated in the same way as the DSL expression containing the dsl_variable. If expr contains dsl_integral objects, then these must have both a and b associated with them. For dsl_integral objects, the increments/decrements in a and b are swapped, so if a = ga, b = gb and the expression contains dsl_integral( ga+1, gb-1 ) This dsl_integral object is replaced by a new dsl_integral dsl_integral( ga-1, gb+1 ) """ ### Begin expr_permute code ### assert type(a) == type(b), 'type(a) != type (b)' assert isinstance(a,dsl_index), 'a is not a dsl_index object' assert isinstance(b,dsl_index), 'b is not a dsl_index object' if expr is a: expr = b elif expr is b: expr = a elif isinstance(expr,dsl_binop): expr = dsl_binop( expr.op(), expr_permute( expr.left(),a,b),\ expr_permute( expr.right(),a,b ) ) # print('out:', type(expr.left() ), type(expr.right() ) ) elif isinstance(expr,dsl_unop): expr = dsl_unop( expr.op(), expr_permute( expr.arg(),a,b) ) elif isinstance(expr,dsl_integral): new_int = expr.int() if isinstance_list( a,expr.indextypes() ) and\ isinstance_list( b,expr.indextypes() ): if a in expr.index_list() and b in expr.index_list(): a_binop = expr.index_binop( a ) b_binop = expr.index_binop( b ) new_int.add_index(a,change=b_binop.right(),op=b_binop.op(),update=True) new_int.add_index(b,change=a_binop.right(),op=a_binop.op(),update=True) if a in expr.index_list(): assert b in expr.index_list(), 'dsl_integral instance only has '+\ a.name()+'index. Cannot permute.' elif b in expr.index_list(): assert a in expr.index_list(), 'dsl_integral instance only has '+\ b.name()+'index. Cannot permute.' elif isinstance_list( a,expr.auxtypes() ) and\ isinstance_list( b,expr.auxtypes() ): if a in expr.aux_list() and b in expr.aux_list(): a_binop = expr.aux_binop( a ) b_binop = expr.aux_binop( b ) new_int.add_auxiliary(a,change=b_binop.right(),op=b_binop.op(),\ update=True) new_int.add_auxiliary(b,change=a_binop.right(),op=a_binop.op(),\ update=True) expr = new_int elif isinstance(expr,dsl_variable): # If dsl_variable objects are directly attached to dsl_index objects a,b # then simply swap the objects in the DSL expression. if expr in a.attachment_list() : for k, v in a.attachment_dict().items(): if expr is v: key = k break expr = b.attachment( key ) elif expr in b.attachment_list() : for k, v in b.attachment_dict().items(): if expr is v: key = k break expr = a.attachment( key ) # If dsl_variable objects are not directly attached, then do a recursive # search of the variable dependencies to see if they carry objects attached # to dsl_index object a,b. If they do, create a new dsl_variable object # of the appropriate derived type and permute the expression of that. elif expr.expr() != None: # Permutation symmetry check if expr_basic_symmetry_check( expr.expr(), a, b ) == False: new_variable = expr.__class__(expr = expr_permute( expr.expr(), a, b ), vartype=expr.type() ) expr = new_variable return expr
def parse_args(self, args, copy_from=None): """Goes through a tuple of arguments passed to the __init__() method and updates the corresponding object attributes. If copy_from is set to a dsl_integral object, parse_args additionally checks that the index, auxilary and argument objects in args are in copy_from._index_list, copy_from._aux_list and copy_from._arg_list, i.e. if the new object is being copied from another, no new indexes, auxiliary or arguments may be added. """ def arg_is_dsl_binop(arg, upd=False): """If arg is dsl_binop, then the dsl_binop should represent the addition or subtraction of an integer literal from a index or auxiliary.""" if arg.op() is op_add or arg.op() is op_sub: if isinstance_list(arg.left(), self._indextypes): if isinstance(arg.right(), int): c = arg.right() a = arg.left() op = arg.op() self.add_index(a, c, op, update=upd) return None else: raise Exception('Only integer literals '\ +'can be added to an index') elif isinstance_list(arg.left(), self._auxtypes): if isinstance(arg.right(), int): c = arg.right() a = arg.left() op = arg.op() self.add_auxiliary(a, c, op, update=upd) return None else: raise Exception('Only integer literals '\ +'can be added to an index') else: raise Exception('binary operation argument must take the form '+\ '[index_object] + [integer] or '+\ '[auxiliary_object] + [integer]') else: raise Exception('Can only accept addition or subtraction '\ +'for increments') def copy_from_check(obj): """Raises and AssertionError if obj is not present in the the _index_list, _aux_list or _arg_list attributes of copy_from.""" all_obj_list = copy_from._index_list + copy_from._aux_list +\ copy_from._arg_list assert obj in all_obj_list,\ obj.name()+' not present in copy_from. If copy_from is set, '+\ 'obj must have been added to the copy_from dsl_integral instance.' if copy_from != None: upd = True else: upd = False i = 1 for arg in args: obj = arg if isinstance(arg, dsl_binop): arg_is_dsl_binop(arg, upd) obj = arg.left() elif isinstance_list(arg, self._indextypes): # No change in a Cartesian Gaussian self.add_index(arg, update=upd) elif isinstance_list(arg, self._auxtypes): # No change in an auxiliary index self.add_auxiliary(arg, update=upd) elif isinstance_list(arg, self._argtypes): self.add_argument(arg, update=upd) else: raise Exception('Argument number '+str(i)+': '+str(arg)+\ ' not understood.') if upd == True: copy_from_check(obj) i += 1 if upd == True: # Additionally ensure that the same number of indexes, auxiliaries, # and arguments are present in a copy and original assert len( copy_from._index_list ) == len( self._index_list ),\ 'cannot change number of indexes in copy' assert len( copy_from._aux_list ) == len( self._aux_list ),\ 'cannot change number of auxiliaries in copy' assert len( copy_from._arg_list ) == len( self._arg_list ),\ 'cannot change number of arguments in copy'
def expr_permute(expr, a, b): """ Permute indices a and b in dsl_integral, dsl_cartesian_gaussian and dsl_variable objects in an RR expression. The returned expression is a copy of expr, but with the dsl_index objects a and b swapped. Additionally, any dsl_variable (or derived class) objects which are attached to the index objects (i.e. returned by dsl_index.attachment_list) are also swapped in the expression. dsl_variable objects for which expr() returns a DSL expression containing other dsl_variable objects will be replaced with new dsl_variable objects with the expr() permuted in the same way. This is done recursively, so a dsl_variable.expr() is treated in the same way as the DSL expression containing the dsl_variable. If expr contains dsl_integral objects, then these must have both a and b associated with them. For dsl_integral objects, the increments/decrements in a and b are swapped, so if a = ga, b = gb and the expression contains dsl_integral( ga+1, gb-1 ) This dsl_integral object is replaced by a new dsl_integral dsl_integral( ga-1, gb+1 ) """ ### Begin expr_permute code ### assert type(a) == type(b), 'type(a) != type (b)' assert isinstance(a, dsl_index), 'a is not a dsl_index object' assert isinstance(b, dsl_index), 'b is not a dsl_index object' if expr is a: expr = b elif expr is b: expr = a elif isinstance(expr, dsl_binop): expr = dsl_binop( expr.op(), expr_permute( expr.left(),a,b),\ expr_permute( expr.right(),a,b ) ) # print('out:', type(expr.left() ), type(expr.right() ) ) elif isinstance(expr, dsl_unop): expr = dsl_unop(expr.op(), expr_permute(expr.arg(), a, b)) elif isinstance(expr, dsl_integral): new_int = expr.int() if isinstance_list( a,expr.indextypes() ) and\ isinstance_list( b,expr.indextypes() ): if a in expr.index_list() and b in expr.index_list(): a_binop = expr.index_binop(a) b_binop = expr.index_binop(b) new_int.add_index(a, change=b_binop.right(), op=b_binop.op(), update=True) new_int.add_index(b, change=a_binop.right(), op=a_binop.op(), update=True) if a in expr.index_list(): assert b in expr.index_list(), 'dsl_integral instance only has '+\ a.name()+'index. Cannot permute.' elif b in expr.index_list(): assert a in expr.index_list(), 'dsl_integral instance only has '+\ b.name()+'index. Cannot permute.' elif isinstance_list( a,expr.auxtypes() ) and\ isinstance_list( b,expr.auxtypes() ): if a in expr.aux_list() and b in expr.aux_list(): a_binop = expr.aux_binop(a) b_binop = expr.aux_binop(b) new_int.add_auxiliary(a,change=b_binop.right(),op=b_binop.op(),\ update=True) new_int.add_auxiliary(b,change=a_binop.right(),op=a_binop.op(),\ update=True) expr = new_int elif isinstance(expr, dsl_variable): # If dsl_variable objects are directly attached to dsl_index objects a,b # then simply swap the objects in the DSL expression. if expr in a.attachment_list(): for k, v in a.attachment_dict().items(): if expr is v: key = k break expr = b.attachment(key) elif expr in b.attachment_list(): for k, v in b.attachment_dict().items(): if expr is v: key = k break expr = a.attachment(key) # If dsl_variable objects are not directly attached, then do a recursive # search of the variable dependencies to see if they carry objects attached # to dsl_index object a,b. If they do, create a new dsl_variable object # of the appropriate derived type and permute the expression of that. elif expr.expr() != None: # Permutation symmetry check if expr_basic_symmetry_check(expr.expr(), a, b) == False: new_variable = expr.__class__(expr=expr_permute( expr.expr(), a, b), vartype=expr.type()) expr = new_variable return expr