Observables
A key feature of this framework is its ability to compute the gradients of physical properties with respect to the force field parameters used to estimate them. This requires the framework be able to, internally, be able to not only track the gradients of all quantities which combine to yield the final observable of interest, but to also be able to propagate the gradients of those composite quantities through to the final value.
The framework offers three such objects to this end (Observable
, ObservableArray
and ObservableFrame
objects)
which will be covered in this document.
Note
In future versions of the framework the objects described here will likely be at least in part deprecated in favour of using full automatic differentiation libraries such as jax. Supporting these libraries will take a large re-write of the framework however, as well as full support between differentiable simulation engines like timemachine and the OpenFF toolkit. As such, these objects are implemented as stepping stones which can be gently phased out while working towards that larger, more modern goal.
Observable Objects
The base object used to track observables is the Observable
object. It stores the average value, the standard error
in the value and the gradient of the value with respect to force field parameters of interest.
Currently the value and error are internally stored in a composite Measurement
object, which themselves wrap around
the uncertainties package. This allows uncertainties to be automatically
propagated through operations without the need for user intervention.
Note
Although uncertainties are automatically propagated, it is still up to property estimation workflow authors to ensure that such propagation (assuming a Gaussian error model) is appropriate. An alternative, which is employed throughout the framework is to make use of the bootstrapping technique.
Gradients are stored in a list as ParameterGradient
gradient objects, which store both the floating value of the
gradient alongside an identifying ParameterGradientKey
.
Supported Operations
+ and -:
Observable
objects can be summed with and subtracted from otherObservable
objects,Quantity
objects, floats or integers. When twoObservable
objects are summed / subtracted, their gradients are combined by summing / subtracting also. When anObservable
is summed / subtracted with aQuantity
,float
orint
object it is assumed that these objects do not depend on any force field parameters.*:
Observable
objects may be multiplied by otherObservable
objects,Quantity
objects, andfloat
orint
objects. When twoObservable
objects are multiplied their gradients are propagated using the product rule. When anObservable
is multiplied by aQuantity
,float
orint
object it is assumed that these objects do not depend on any force field parameters./:
Observable
objects may be divided by otherObservable
objects,Quantity
objects, andfloat
orint
objects. Gradients are propagated through the division using the quotient rule. When anObservable
is divided by aQuantity
,float
orint
object (or when these objects are divided by anObservable
object) it is assumed that these objects do not depend on any force field parameters.
In all cases two Observable
objects can only be operated on provided the contain gradient information with respect
to the same set of force field parameters.
Observable Arrays
An extension of the Observable
object is the ObservableArray
object. Unlike an Observable
, an ObservableArray
object does not contain error information, but rather the value it stores and the gradients of that value should be a
numpy array with shape=(n_data_points, n_dimensions)
. It is designed to store information such as the potential
energy evaluated at each configuration sampled during a simulation, as well as the gradient of the potential, which can
then be ensemble averaged using a fluctuation formula to propagate the gradients through to the average.
Like with Observable
objects, gradients are stored in a list as ParameterGradient
gradient objects. The length
of the gradients is required to match the length of the value array.
ObservableArray
objects may be concatenated together using their join()
method or sub-sampled using
their subset()
method.
Supported Operations
The ObservableArray
object supports the same operations as the Observable
object, whereby all operations are
applied elementwise to the stored arrays.
Observable Frames
An ObservableFrame
is a wrapper around a collection of ObservableArray
which contain the types of observable
specified by the ObservableType
enum. It behaves as a dictionary which can take either an ObservableType
or
a string value of an ObservableType
as an index.
Like an ObservableArray
, observable frames may be concatenated together using their join()
method
or sub-sampled using their subset()
method.
Supported Operations
No operations are supported between observable frames.