3.5. Parameter Interfaces

Any AMS engine that can be parameterized is represented by an Interface class (derived from BaseParameters). This is mostly acting as a translation layer between the Optimizer and AMS. Currently, these engines can be parameterized:

3.5.1. Available Parameter Interfaces

The following examples showcase the use of the parameter interface to ReaxFF, however, the methods discussed are available to all other interfaces as well. The API is defined through the BaseParameters base class.

3.5.2. Parameter Interface Basics

>>> # The initialization parameters might be different for every interface
>>> # Here, we are reading the parameters from a force field file
>>> interface = ReaxParams('my_ffield.ff')

A parameter interface behaves like a list. It stores instances of the Parameter class:

>>> interface[0]
Parameter(VALUE, "NAME", IS_ACTIVE, RANGE)
>>> len(interface) # The total number of parameters in this instance
42
>>> # We can iterate over all stored parameters:
>>> for parameter in interface:
...   do_something(parameter)

Note

Although the underlying data structure of the parameter interface is a list, dict-like look-ups are also supported. Instead of accessing an entry by its list index, it can also be accessed by the parameter’s name string:

>>> interface[121].name               # List-like behaviour, same as interface(121)
"parameter_name"
>>> interface["parameter_name"].name  # Dict-like behaviour
"parameter_name"

This works because name strings in the same interface are required to be unique, but is slower than a genuine dictionary lookup.

3.5.3. Working with Parameters

The user can interact with a single Parameter by its attributes name, value, range, is_active:

>>> interface[0]
Parameter(50, "x_1", True, (10,100))
>>> interface[0].value
50
>>> interface[0].name = 'y_1'
>>> interface[0].name
'y_1'
>>> interface[0].is_active
True
>>> interface[0].range
(10,100)

In addition to single-parameter interactions, the user can interact with all parameters in an interface at once through the attributes names, x, range, is_active:

>>> interface.x           # Returns the `Parameter.value` of every parameter in the interface
>>> [50, 1.23, ..., 321]
>>> interface.names       # Returns the `Parameter.name` of every parameter in the interface
['y_1', 'x_2', ..., 'x_N']
>>> interface.range       # Returns the `Parameter.range` of every parameter in the interface
[(10, 100), ..., (0,1)]
>>> interface.is_active   # Returns the `Parameter.is_active` of every parameter in the interface
[True, False, ..., True]

Note

The attributes range, is_active are present in both, Parameter and BaseParameters classes.

Any of the above attributes can be used as a setter, to change or set multiple parameter values at once:

>>> interface.x
[50, 1.23, ..., 321]
>>> new_x = interface.x
>>> new_x[0] = 1000.
>>> interface.x = new_x
>>> interface.x
[1000., 1.23, ..., 321]
>>>
>>> interface.is_active = len(interface)*[False] # Sets all parameters to is_active=False
>>> not all(interface.is_active)
True

Note

The above setters only work when the value they are being set to is of the same length as len(interface):

>>> len(interface)
100
>>> interface.x = [1,2]
ValueError: Len of passed values is not the same as size of this interface.

3.5.4. The Active Parameters Subset

Important

During an optimization, only the active subset of parameters will be optimized.

The dynamic active attribute returns a subset of all parameters that meet the Parameter.is_active==True comparison:

>>> len(interface)
100
>>> interface.is_active.count(True) # Number of is_active parameters
50
>>> len(interface.active)
50
>>> interface.is_active = len(interface)*[False]
>>> len(interface.active)
0

This subset of a parameter interface behaves just like the main interface:

>>> for active_param in interface.active:
...   do_something(active_param)
>>> interface.active.x     = new_x
>>> interface.active.range = new_ranges
>>> all(interface.active.is_active) # Obviously
True

3.5.5. Storage

Each parameter interface has a write() method which will write the currently stored parameter values to disk in a format native to the associated engine and readable by AMS. For more information check out the respective parameter interface documentation.

3.5.5.1. Lossless Storage

In most cases, storing an interface with the write() method will not preserve any information that might be relevant for future parameterizations. In such cases, the pickle_dump() and pickle_load() methods can be used to store and load the complete instance in binary format. This preserves all additional parameter attributes such as range or is_active:

>>> ljp1 = LennardJonesParams()
>>> ljp1[0].value = 42
>>> ljp1[0].range = (0, 43)
>>> ljp1.pickle_dump('ljp1.pkl')
>>> ljp2 = LennardJonesParams.pickle_load('ljp1.pkl')
>>> ljp1 == ljp2
True

3.5.6. Relation to PLAMS Settings

Each parameter interface can be represented as a PLAMS Settings instance. This happens through the get_engine() method, which returns an intermediary Engine instance:

>>> ljp    = LennardJonesParams()
>>> engine = ljp.get_engine()
>>> print(engine)
AMSInput: |
   Engine lennardjones
     eps 0.0003
     rmin 3.0
   EndEngine

Engine instances are intended to be stored in engine collections whenever data needs to be reproduced or shared. The PLAMS Settings instance associated with each Engine instance can be accessed through the Engine.settings attribute:

>>> print(engine.settings)
input:
      lennardjones:
                   eps:         0.0003
                   rmin:        3.0

3.5.7. Parameter API

class Parameter(value=None, name=None, is_active=True, range=[None, None], id=None)

A helper class representing a single parameter in the BaseParameters.

Note

This class is not public in this module, as it is not possible to create these entries on their own. They can only be managed by an instance of a BaseParameters class.

Attributes:
name : str
Parameter name
value : float
Parameter value
range : Tuple[float, float]

Upper and lower ranges for the parameter value. Anything outside of that range will not be considered by the optimizer.

Note

Providing a range with the same lower and upper values does not automatically set the parameter to is_active=False.

atoms : List[str]
If the parameter is atom-specific, will store a list of all atoms involved in this attribute, empty list otherwise.
is_active : bool
The optimizer will only optimize parameters set to is_active==True. Within a BaseParameters instance, a BaseParameters.active subset is generated based on this attribute.

3.5.8. Interface Base Class API

Base class from which all engine specific parameterizations should derive:

class BaseParameters(names, active, values, ranges)

A class representing the interface to an arbitrary AMS engine. Classes representing specific engines (e.g. ReaxFF) can derive from this abstract base class.

Will generate entries of a Parameter helper class type. Each entry holds the following attributes: value, range, name, atoms and is_active.

Note

The attributes range and is_active are available to both, the Parameter and BaseParameters classes.

>>> # Init BaseParameters()-derived class
>>> type(self[0])
<class 'scm.params.parameterinterfaces.base.Parameter'>
>>> self[0].range       # The attribute of a single entry, returns a single tuple
(x,y)
>>> self[0].is_active   # The attribute of a single entry, returns a single bool
True
>>> self.range          # Attributes of all entries in this class, return a list of tuples
[(x1,y1), ..., (xn,yn)]
>>> self.is_active      # Attributes of all entries in this class., return a list of bools
[True, False ..., True]
__init__(names, active, values, ranges)

Base class constructor to be called from the derived classes. Performs a few basic checks.

Parameters:

names : list or tuple
Data structure holding the unique names of each parameter.
active : list or tuple of bools
Data structure specifying, whether to optimize this parameter (True), or not.
values : list or tuple of floats
Data structure with the initial parameter values.
ranges : list or tuple of (float,float) or [float,float]
Data structure specifying the lower and upper bounds for each parameter.
x
Returns:A list of the current parameter values.

Note: Batch-set all Parameter values with self.x = List[float].
Works only, if len(List[float]) == len(self).

names
Returns:A list of parameter names.
is_active
Returns:A list of bools, marking the parameter active (True) or inactive for an optimization.

Note: Batch-set all Parameter.is_active values with self.is_active = List[bool].
Works only, if len(List[bool]) == len(self).

range
Returns:A list of parameter ranges.

Note: Batch-set all Parameter ranges with self.is_active = List[Tuple(float,flaot)].
Works only, if len(List[Tuple(float,flaot)]) == len(self)

active

Important

This subset contains all parameters that will be optimized.

Given the initial set of all parameters in this class, generates and stores a subset in this attribute, which only contains the parameters marked as is_active == True.

The inherits the same properties and methods as its parent (with the exclusion of write() and get_engine()), e.g.:

>>> self.range          # Returns the range  of ALL parameters in this class
>>> self.active.range   # Returns the range  of the active parameters only
>>> self.active.x       # Returns the values of the active parameters only
>>> self.active[0]
>>> len(self.active)

Setting a Parameter.is_active attribute in the parent will include / exclude it from the active subset:

>>> len(self)
100
>>> len(self.active)
50
>>> self('ParameterX').is_active
False
>>> self('ParameterX').is_active = True
>>> len(self.active)
51
get_engine(parameters=None, *args, **kwargs)

Given a set of parameters returns a ready-to-run Engine.

Note

This abstract method has to be overwritten by a derived class. The parameter length check defined here should be propagated with super().get_engine().

Note

Parameters can be None, in which case an instance holding the current paramers will be returned. len(params) should be equal to len(self) or len(self.active).

Important

Children that inherit from this class should lock this method to make it thread-safe:

with self._Lock: # This variable is generated by the base class
    ...
Returns:an Engine instance for the given parameters.
write(path, parameters=None)

Abstract method. Child class must have a write() method that stores the engine to disk. If parameters is provided, this method should call super().get_engine(parameters) prior to writing. before writing them to disk.

Important

Children that inherit from this class should lock this method to make it thread-safe:

with self._Lock: # This variable is generated by the base class
    ...
pickle_dump(fname)

Stores the parameter interface instance in a binary format to fname. Loading it with pickle_load() will restore values, ranges and active settings. If fname does not end with the .pkl extension, it will be added.

classmethod pickle_load(fname)

Loads the parameter interface from a binary file stored with pickle_dump().

__len__()
Returns:Number of parameters.
__iter__()

Iterates over Parameter entries .

Each entry has the attributes: value, range, name and is_active.

__getitem__(id)

If id is a string, get the Parameter with name id (dict),
if id is an int, get the Parameter at id (list).

__call__(id)

Same as __getitem__()

__eq__(other)

Checks if two interfaces are the same

index(i)

Returns the index of parameter i in the interface, such that i == self[self.index(i)].