def with_subranges(self, *subranges): """ Basically the same thing as calling `add_subrange()` multiple times, except returns `self` instead of the subrange, allowing for a different syntax (see below) :Examples: >>> IndexRange.reset_ranges() >>> orb = DeclareIndexRange('p,q,r,s', 10, name="Orbital space").with_subranges( ... DeclareIndexRange('i,j,k,l', 0, 3, name="Occupied space").with_subranges( ... DeclareIndexRange("i*,j*,k*,l*", 0, 1, name="Core"), ... DeclareIndexRange("i',j',k',l'", 1, 3) ... ), ... DeclareIndexRange('a,b,c,d', 3, 10, name="Virtual space") ... ) >>> orb <IndexRange named 'Orbital space' represented by indices ['p', 'q', 'r', 's']> >>> len(orb.subranges) 2 >>> len(orb.subranges[0].subranges) 2 """ for subrange in listify_args(subranges): self.add_subrange(subrange) # TODO raise an error if subrange's index_range_set is set to something other than the same thing as ours return self
def __init__(self, comparison_representation, *files): self.started = True self._files = listify_args(*files) self.properties = MoleculeDict(comparison_representation, default=lambda: []) self.properties_for_molecules = defaultdict(lambda: []) for file in files: self._parse_file(file)
def available_pairs(self, *keys): for key in listify_args(keys): if key in self: value = self[key] if isinstance(value, Keyword): if value.value is not None: yield key, str(value.value) else: yield key, value._name else: yield key, str(value)
def __class_getitem__(cls, args): """ When given multiple arguments, this functions as a serogate constructor, allowing Ellipsis literals and slice literals to be given. """ items = listify_args(args) if len(items) == 3 and isinstance(items[2], basestring): name = items.pop() return IndexRange(*items, name=name) else: return IndexRange(*items)
def __attr_init__(self, types, strong, type_error_text): self.types = tuple(listify_args(types)) if type_error_text is None: self.type_error_text = "Attribute '{name}'".format(name=self.__name__) self.type_error_text += " in class '{cls}' " if isinstance(types, Iterable): second_part = "must be an instance of one of the following: ('{types}');".format(types="', '".join(map(classname, types))) else: second_part = "must be an instance of '{types}'".format(types=classname(types)) self.type_error_text += second_part + " (got '{got}')" else: self.type_error_text = type_error_text self.strong = strong
def __attr_init__(self, types, strong, type_error_text): self.types = tuple(listify_args(types)) if type_error_text is None: self.type_error_text = "Attribute '{name}'".format( name=self.__name__) self.type_error_text += " in class '{cls}' " if isinstance(types, Iterable): second_part = "must be an instance of one of the following: ('{types}');".format( types="', '".join(map(classname, types))) else: second_part = "must be an instance of '{types}'".format( types=classname(types)) self.type_error_text += second_part + " (got '{got}')" else: self.type_error_text = type_error_text self.strong = strong
def __new__(cls, argname, arg, *allowed_types, **kwargs): allowed = listify_args(allowed_types) my_caller = get_kwarg(kwargs, 'caller') or (caller() + '()') if len(allowed) == 1: return TypeError('Argument {0} to {1} must be {2} (got {3})'.format( argname, my_caller, classname(allowed[0]), type(arg).__name__ )) else: return TypeError("Argument {0} to {1} must be one of ({2}) (got {3})".format( argname, my_caller, ', '.join(classname(a) for a in allowed), type(arg).__name__ ))
def remove_phase_factor(*args, **kwargs): """ Given any number of `Tensor` objects that are the same up to a phase factor, return a list of `Tensor` objects that have been. Copies are only made if necessary. The chosen phase is the one that makes the first non-zero element (in the `numpy.nditer(tensor)` iterator, using the `cutoff` keyword argument or Tensor.zero_cutoff if one is not given) positive. """ cutoff = kwargs.pop('cutoff', Tensor.same_tensor_cutoff) force_list = kwargs.pop('force_list', False) args = listify_args(*args, ignore=np.ndarray) if len(args) == 0: return [] if sanity_checking_enabled: if not args[0].same_upto_phase_factor(*args[1:], ** {'cutoff': cutoff}): raise ValueError( "can't align phases of tensors that are not the same to a phase factor" ) it = np.nditer(args[0], flags=['multi_index']) idx = None for i in it: if abs(i) < Tensor.zero_cutoff: idx = it.multi_index break if idx is None: # it's the zero vector (and so are all of the rest, assuming sanity # checking is enabled and/or the user is being safe) if len(args) == 1 and not force_list: return args[0] else: return args ret_val = [] for arg in args: if arg[idx] < 0: ret_val.append(-1.0 * arg) else: ret_val.append(arg) if len(ret_val) == 1 and not force_list: return ret_val[0] else: return ret_val
def remove_phase_factor(*args, **kwargs): """ Given any number of `Tensor` objects that are the same up to a phase factor, return a list of `Tensor` objects that have been. Copies are only made if necessary. The chosen phase is the one that makes the first non-zero element (in the `numpy.nditer(tensor)` iterator, using the `cutoff` keyword argument or Tensor.zero_cutoff if one is not given) positive. """ cutoff = kwargs.pop('cutoff', Tensor.same_tensor_cutoff) force_list = kwargs.pop('force_list', False) args = listify_args(*args, ignore=np.ndarray) if len(args) == 0: return [] if sanity_checking_enabled: if not args[0].same_upto_phase_factor(*args[1:], **{'cutoff': cutoff}): raise ValueError("can't align phases of tensors that are not the same to a phase factor") it = np.nditer(args[0], flags=['multi_index']) idx = None for i in it: if abs(i) < Tensor.zero_cutoff: idx = it.multi_index break if idx is None: # it's the zero vector (and so are all of the rest, assuming sanity # checking is enabled and/or the user is being safe) if len(args) == 1 and not force_list: return args[0] else: return args ret_val = [] for arg in args: if arg[idx] < 0: ret_val.append(-1.0 * arg) else: ret_val.append(arg) if len(ret_val) == 1 and not force_list: return ret_val[0] else: return ret_val
def keywordify_if_available(self, format, *keys): """ Mostly for use by templates, this method returns a string of keyword-value pairs in using the given format for each argument in the argument list. `format` should use the str.format() protocol from the python standard library with the named slots `{key}` where the keyword name should go and `{value}` where the value of the detail should go (or, if the value of the detail is a `Keyword` object, the contents of the `value` attribute for that `Keyword`, or, if the `value` attribute for the keyword is not defined, the `_name` attribute for the `Keyword` object in question. Aliased as `keywordify` """ ret_val = "" for key in listify_args(keys): if key in self: value = self[key] if isinstance(value, Keyword): if value.value is not None: ret_val += format.format(key, value.value) + "\n" else: ret_val += format.format(key, value._name) + "\n" else: ret_val += format.format(key, value) + "\n" return ret_val
def __class_getitem__(cls, args): return listify_args(args)
def __init__(self, function, *variables, **kwargs): """ """ if len(FiniteDifferenceDerivative.formulas) == 0: # Load the formulas generated "by hand", which (for now, anyway) require fewer # displacements than the automatically generated formulas if we also need to # compute the lower order derivatives as well, as is the case with the computation # of quartic forcefields. (But not, for instance, the B tensor. So the # FiniteDifferenceDerivative constructor could be optimized to take a parameter # which specifies whether we should choose the formula with the fewest overall # displacements or the fewest "new" displacements not needed for smaller derivatives) load_formulas() #--------------------------------------------------------------------------------# # miscellanea self._target_robustness = kwargs.pop('target_robustness', 2) self._value_function = kwargs.pop('value_function', None) self._delta_function = kwargs.pop('delta_function', None) self._delta = kwargs.pop('delta', None) self._forward = kwargs.pop('forward', False) self._function = function #--------------------------------------------------------------------------------# # type checking if type_checking_enabled: if not all(isinstance(v, FiniteDifferenceVariable) for v in variables): raise TypeError if not isinstance(self.target_robustness, int): raise TypeError #--------------------------------------------------------------------------------# # Get the variables and the orders.... vars = listify_args(*variables) # Determine which formula we need vars = sorted(vars, key=id) # This is nasty, but it works...The zip(*list_of_lists) effectively "unzips" self._orders, self._variables = zip( *sorted( [(len(list(g)), k) for k, g in groupby(vars)], reverse=True) ) #--------------------------------------------------------------------------------# # Determine which formula to use # This gets reused, so define a quicky function... def get_possibilities(formula_list): return [f for f in formula_list if f.orders == list(self.orders) and f.robustness >= self.target_robustness and (f.is_forward() if self._forward else f.is_central()) ] #----------------------------------------# # First, try and get a "hand-generated" formula possibilities = get_possibilities(FiniteDifferenceDerivative.formulas) if len(possibilities) == 0: # We know how to generate single variable formulas to arbitrary order, so let's do it n_derivative_vars = len(self.orders) derivative_order = sum(self.orders) if n_derivative_vars == 1: # This long name is unweildy... gen_dict = FiniteDifferenceDerivative.generated_single_variable_formulas # See if we've already generated it... formula = gen_dict.get( ( derivative_order, self.target_robustness + (1 if not self._forward and self.target_robustness % 2 == 1 else 0), self._forward ), None) if formula is None: # okay, we can generate it. generate_single_variable_formulas( derivative_order, self.target_robustness + (1 if not self._forward and self.target_robustness % 2 == 1 else 0), self._forward) formula = gen_dict[( derivative_order, self.target_robustness + (1 if not self._forward and self.target_robustness % 2 == 1 else 0), self._forward)] possibilities.append(formula) if sanity_checking_enabled: possibilities = get_possibilities(possibilities) else: # we don't know how to generate these...yet...but I'm working on it! raise RuntimeError("Can't find formula for orders {0} and" " robustness {1}".format( self.orders, self.target_robustness)) # Use the minimum robustness for now. Later we can make it use # the best possible without additional calculations. self._formula = sorted(possibilities, key=attrgetter('robustness'))[0]
def __init__(self, function, *variables, **kwargs): """ """ if len(FiniteDifferenceDerivative.formulas) == 0: # Load the formulas generated "by hand", which (for now, anyway) require fewer # displacements than the automatically generated formulas if we also need to # compute the lower order derivatives as well, as is the case with the computation # of quartic forcefields. (But not, for instance, the B tensor. So the # FiniteDifferenceDerivative constructor could be optimized to take a parameter # which specifies whether we should choose the formula with the fewest overall # displacements or the fewest "new" displacements not needed for smaller derivatives) load_formulas() # --------------------------------------------------------------------------------# # miscellanea self._target_robustness = kwargs.pop("target_robustness", 2) self._value_function = kwargs.pop("value_function", None) self._delta_function = kwargs.pop("delta_function", None) self._delta = kwargs.pop("delta", None) self._forward = kwargs.pop("forward", False) self._function = function # --------------------------------------------------------------------------------# # type checking if type_checking_enabled: if not all(isinstance(v, FiniteDifferenceVariable) for v in variables): raise TypeError if not isinstance(self.target_robustness, int): raise TypeError # --------------------------------------------------------------------------------# # Get the variables and the orders.... vars = listify_args(*variables) # Determine which formula we need vars = sorted(vars, key=id) # This is nasty, but it works...The zip(*list_of_lists) effectively "unzips" self._orders, self._variables = zip(*sorted([(len(list(g)), k) for k, g in groupby(vars)], reverse=True)) # --------------------------------------------------------------------------------# # Determine which formula to use # This gets reused, so define a quicky function... def get_possibilities(formula_list): return [ f for f in formula_list if f.orders == list(self.orders) and f.robustness >= self.target_robustness and (f.is_forward() if self._forward else f.is_central()) ] # ----------------------------------------# # First, try and get a "hand-generated" formula possibilities = get_possibilities(FiniteDifferenceDerivative.formulas) if len(possibilities) == 0: # We know how to generate single variable formulas to arbitrary order, so let's do it n_derivative_vars = len(self.orders) derivative_order = sum(self.orders) if n_derivative_vars == 1: # This long name is unweildy... gen_dict = FiniteDifferenceDerivative.generated_single_variable_formulas # See if we've already generated it... formula = gen_dict.get( ( derivative_order, self.target_robustness + (1 if not self._forward and self.target_robustness % 2 == 1 else 0), self._forward, ), None, ) if formula is None: # okay, we can generate it. generate_single_variable_formulas( derivative_order, self.target_robustness + (1 if not self._forward and self.target_robustness % 2 == 1 else 0), self._forward, ) formula = gen_dict[ ( derivative_order, self.target_robustness + (1 if not self._forward and self.target_robustness % 2 == 1 else 0), self._forward, ) ] possibilities.append(formula) if sanity_checking_enabled: possibilities = get_possibilities(possibilities) else: # we don't know how to generate these...yet...but I'm working on it! raise RuntimeError( "Can't find formula for orders {0} and" " robustness {1}".format(self.orders, self.target_robustness) ) # Use the minimum robustness for now. Later we can make it use # the best possible without additional calculations. self._formula = sorted(possibilities, key=attrgetter("robustness"))[0]
def __new__(cls, *args, **kwargs): """ TODO Move this to class level documentation, since it doesn't show up in Sphinx Takes several different forms. Given a number of arguments (possibly nested lists), a Tensor is created as expected. *This is the only form in which arguments may be specified without keywords.* `Tensor` can also be initialized by giving a list of dimensions for the `shape` (aliased as `dimension`) keyword argument, with a possible default value keyword argument `default_val`. Unless otherwise specified via the `dtype` keyword argument, it is assumed that the input data should be cast as a `numpy.float64`. """ # Pop off any special args or kwargs and store their values for later has_indices = False ret_val = None indices = None units = pop_kwarg(kwargs, 'units') name = kwargs.pop('name', None) #--------------------------------------------------------------------------------# # pop off any kwargs that the subclass's __init__ takes... subclass_kwargs = {} for supercls in cls.__mro__: if supercls is Tensor: break if hasattr(supercls, '__tensor_init__') and callable(supercls.__tensor_init__): if hasattr(supercls.__tensor_init__, 'getargspec'): argspec = supercls.__tensor_init__.getargspec() else: argspec = inspect.getargspec(supercls.__tensor_init__) for arg in argspec.args[1:]: subclass_kwargs[arg] = kwargs.pop(arg, None) #--------------------------------------------------------------------------------# # Check for indices... indices_kwarg = kwargs.pop('indices', None) if indices_kwarg is None and len(args) == 1 and isinstance(args[0], basestring): args = list(args) indices_kwarg = args.pop(0) args = tuple(args) index_range_set = pop_multikwarg(kwargs, 'index_range_set', 'in_set', 'set') if indices_kwarg is not None: has_indices = True indices = EinsumTensor.split_indices(indices_kwarg) if index_range_set is None: index_range_set = IndexRange.global_index_range_set shape = [] for idx in indices: if idx not in index_range_set.known_ranges: raise IndexError("unknown index '" + idx + "'") shape.append(index_range_set.known_ranges[idx].size) shape = tuple(shape) if "shape" in kwargs: kwshape = kwargs.pop('shape') if shape != kwshape: raise TypeError("inconsistent shape: indices '{}' indicate a shape of {}, but" " the keyword 'shape' was given with the shape {}".format( ",".join(indices), shape, kwshape )) kwargs['shape'] = shape #--------------------------------------------------------------------------------# # Now create a numpy.ndarray object... def_val = pop_kwarg(kwargs, 'default_val', 'default_value', 'default') or 0.0 # Set the default data type to float, unless the user specifies dtype = get_kwarg(kwargs, 'dtype') or float if not callable(dtype) and grendel.show_warnings: warn("dtype given to {0} constructor is not Callable and thus cannot be used for casting." \ " It is better to use callable types for dtype if possible; e.g. numpy.float64 instead" \ "of numpy.dtype('float64')".format(classname(cls))) kwargs['dtype'] = dtype # Typecast the default value if callable(dtype): def_val = dtype(def_val) # See if we have a shape... shape = pop_kwarg(kwargs, 'shape', 'dimension') # See if we have data... # This allows us to support the form Tensor(1, 2, 3, 4) if len(args) == 1 and isinstance(args[0], np.ndarray): data = args[0] else: data = listify_args(*args) if 'data' in kwargs: if data: raise TypeError("`data` may be specified as a keyword argument or as " \ "the regular arguments to {0} constructor, but not both.".format(classname(cls))) else: data = pop_kwarg('data') if shape and not isinstance(data, np.ndarray): has_content = False if len(args) != 0: has_content = True if not has_content: if def_val == 0.0: try: ret_val = np.zeros(shape=shape, **kwargs) except: raise else: ret_val = (np.ones(shape=shape, **kwargs) * def_val) else: if grendel.sanity_checking_enabled: # Check data length tmp = np.array(data) needed_data_size = 1 for dim in shape: needed_data_size *= dim try: tmp.reshape((needed_data_size,)) except ValueError: raise ValueError("Data provided to {0} constructor is incompatible with the shape {1}".format(classname(cls), shape)) # Check data type ret_val = np.array(data, **kwargs).reshape(shape) else: # Just pass on the data and any surviving kwargs try: if isinstance(data, np.ndarray) and ( len(kwargs) == 0 or (len(kwargs) == 1 and 'dtype' in kwargs and kwargs['dtype'] == data.dtype) ): # Just do a view ret_val = data.view(cls) else: # Otherwise, we need to call the numpy "constructor" (actually a factory function) of ndarray ret_val = np.array(data, **kwargs) except: # debugging breakpoint hook raise if shape and ret_val.shape != shape: raise ValueError("Shape mismatch: data shape {0} does not match specified shape {1}".format( data.shape, shape )) #--------------------------------------------------------------------------------# # View-cast the ret_val to the class in question, but only if we haven't already if not isinstance(ret_val, cls): ret_val = ret_val.view(cls) # Now assign stuff from any special args... if has_indices: ret_val.indices = indices ret_val.index_range_set = index_range_set else: ret_val.indices = None ret_val.units = units ret_val.name = name if name is None: ret_val.name = "(unnamed tensor)" # pass the remaining kwargs to the initializer... ret_val.__tensor_init__(**subclass_kwargs) return ret_val
def __new__(cls, *args, **kwargs): """ TODO Move this to class level documentation, since it doesn't show up in Sphinx Takes several different forms. Given a number of arguments (possibly nested lists), a Tensor is created as expected. *This is the only form in which arguments may be specified without keywords.* `Tensor` can also be initialized by giving a list of dimensions for the `shape` (aliased as `dimension`) keyword argument, with a possible default value keyword argument `default_val`. Unless otherwise specified via the `dtype` keyword argument, it is assumed that the input data should be cast as a `numpy.float64`. """ # Pop off any special args or kwargs and store their values for later has_indices = False ret_val = None indices = None units = pop_kwarg(kwargs, 'units') name = kwargs.pop('name', None) #--------------------------------------------------------------------------------# # pop off any kwargs that the subclass's __init__ takes... subclass_kwargs = {} for supercls in cls.__mro__: if supercls is Tensor: break if hasattr(supercls, '__tensor_init__') and callable( supercls.__tensor_init__): if hasattr(supercls.__tensor_init__, 'getargspec'): argspec = supercls.__tensor_init__.getargspec() else: argspec = inspect.getargspec(supercls.__tensor_init__) for arg in argspec.args[1:]: subclass_kwargs[arg] = kwargs.pop(arg, None) #--------------------------------------------------------------------------------# # Check for indices... indices_kwarg = kwargs.pop('indices', None) if indices_kwarg is None and len(args) == 1 and isinstance( args[0], basestring): args = list(args) indices_kwarg = args.pop(0) args = tuple(args) index_range_set = pop_multikwarg(kwargs, 'index_range_set', 'in_set', 'set') if indices_kwarg is not None: has_indices = True indices = EinsumTensor.split_indices(indices_kwarg) if index_range_set is None: index_range_set = IndexRange.global_index_range_set shape = [] for idx in indices: if idx not in index_range_set.known_ranges: raise IndexError("unknown index '" + idx + "'") shape.append(index_range_set.known_ranges[idx].size) shape = tuple(shape) if "shape" in kwargs: kwshape = kwargs.pop('shape') if shape != kwshape: raise TypeError( "inconsistent shape: indices '{}' indicate a shape of {}, but" " the keyword 'shape' was given with the shape {}". format(",".join(indices), shape, kwshape)) kwargs['shape'] = shape #--------------------------------------------------------------------------------# # Now create a numpy.ndarray object... def_val = pop_kwarg(kwargs, 'default_val', 'default_value', 'default') or 0.0 # Set the default data type to float, unless the user specifies dtype = get_kwarg(kwargs, 'dtype') or float if not callable(dtype) and grendel.show_warnings: warn("dtype given to {0} constructor is not Callable and thus cannot be used for casting." \ " It is better to use callable types for dtype if possible; e.g. numpy.float64 instead" \ "of numpy.dtype('float64')".format(classname(cls))) kwargs['dtype'] = dtype # Typecast the default value if callable(dtype): def_val = dtype(def_val) # See if we have a shape... shape = pop_kwarg(kwargs, 'shape', 'dimension') # See if we have data... # This allows us to support the form Tensor(1, 2, 3, 4) if len(args) == 1 and isinstance(args[0], np.ndarray): data = args[0] else: data = listify_args(*args) if 'data' in kwargs: if data: raise TypeError("`data` may be specified as a keyword argument or as " \ "the regular arguments to {0} constructor, but not both.".format(classname(cls))) else: data = pop_kwarg('data') if shape and not isinstance(data, np.ndarray): has_content = False if len(args) != 0: has_content = True if not has_content: if def_val == 0.0: try: ret_val = np.zeros(shape=shape, **kwargs) except: raise else: ret_val = (np.ones(shape=shape, **kwargs) * def_val) else: if grendel.sanity_checking_enabled: # Check data length tmp = np.array(data) needed_data_size = 1 for dim in shape: needed_data_size *= dim try: tmp.reshape((needed_data_size, )) except ValueError: raise ValueError( "Data provided to {0} constructor is incompatible with the shape {1}" .format(classname(cls), shape)) # Check data type ret_val = np.array(data, **kwargs).reshape(shape) else: # Just pass on the data and any surviving kwargs try: if isinstance(data, np.ndarray) and ( len(kwargs) == 0 or (len(kwargs) == 1 and 'dtype' in kwargs and kwargs['dtype'] == data.dtype)): # Just do a view ret_val = data.view(cls) else: # Otherwise, we need to call the numpy "constructor" (actually a factory function) of ndarray ret_val = np.array(data, **kwargs) except: # debugging breakpoint hook raise if shape and ret_val.shape != shape: raise ValueError( "Shape mismatch: data shape {0} does not match specified shape {1}" .format(data.shape, shape)) #--------------------------------------------------------------------------------# # View-cast the ret_val to the class in question, but only if we haven't already if not isinstance(ret_val, cls): ret_val = ret_val.view(cls) # Now assign stuff from any special args... if has_indices: ret_val.indices = indices ret_val.index_range_set = index_range_set else: ret_val.indices = None ret_val.units = units ret_val.name = name if name is None: ret_val.name = "(unnamed tensor)" # pass the remaining kwargs to the initializer... ret_val.__tensor_init__(**subclass_kwargs) return ret_val