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 aBaseParameters
instance, aBaseParameters.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
andis_active
.Note
The attributes
range
andis_active
are available to both, theParameter
andBaseParameters
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 withself.x = List[float]
.
Works only, iflen(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 withself.is_active = List[bool]
.
Works only, iflen(List[bool]) == len(self)
.
-
range
¶ Returns: A list of parameter ranges. Note: Batch-set all
Parameter
ranges withself.is_active = List[Tuple(float,flaot)]
.
Works only, iflen(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()
andget_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 theactive
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 tolen(self)
orlen(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
andis_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)]
.
-