From e60c5d90acf14c5456ffa37d83ac6e3b07af8080 Mon Sep 17 00:00:00 2001 From: bussilabbot Date: Tue, 19 Nov 2024 08:20:53 +0000 Subject: [PATCH] Update to bussilab/MDRefine@7dce8f9 --- .nojekyll | 0 MDRefine.pdf | Bin 0 -> 93383 bytes MDRefine/MDRefinement.html | 132 + MDRefine/data_loading.html | 602 + MDRefine/hyperminimizer.html | 265 + MDRefine/index.html | 104 + MDRefine/loss_and_minimizer.html | 660 + README.md | 13 + examples/Tutorial.html | 20296 +++++++++++++++++++++++++++++ examples/Tutorial_2.html | 9984 ++++++++++++++ examples/Tutorial_3.html | 13437 +++++++++++++++++++ examples/index.html | 42 + examples/load_data.html | 9596 ++++++++++++++ index.html | 10 + 14 files changed, 55141 insertions(+) create mode 100644 .nojekyll create mode 100644 MDRefine.pdf create mode 100644 MDRefine/MDRefinement.html create mode 100644 MDRefine/data_loading.html create mode 100644 MDRefine/hyperminimizer.html create mode 100644 MDRefine/index.html create mode 100644 MDRefine/loss_and_minimizer.html create mode 100644 README.md create mode 100644 examples/Tutorial.html create mode 100644 examples/Tutorial_2.html create mode 100644 examples/Tutorial_3.html create mode 100644 examples/index.html create mode 100644 examples/load_data.html create mode 100644 index.html diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/MDRefine.pdf b/MDRefine.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e768673ac9278722582fbcf3130c769cf0e580d2 GIT binary patch literal 93383 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 + + + + + +MDRefine.MDRefinement API documentation + + + + + + + + + + + +
+
+
+

Module MDRefine.MDRefinement

+
+
+

Main tool: MDRefinement(). +It refines MD-generated trajectories with customizable refinement.

+
+
+
+
+
+
+

Functions

+
+
+def MDRefinement(infos: dict, *, regularization: dict = None, stride: int = 1, starting_alpha: float = inf, starting_beta: float = inf, starting_gamma: float = inf, random_states=5, which_set: str = 'validation', gtol: float = 0.5, ftol: float = 0.05, results_folder_name: str = 'results', n_parallel_jobs: int = None) +
+
+

This is the main tool of the package: it loads data, searches for the optimal hyperparameters and minimizes the loss function on the whole data set +by using the opimized hyperparameters. The output variables are then saved in a folder; they include input values, min_lambdas (optimal lambda coefficients for Ensemble Refinement, when performed), +result, hyper_search (steps in the search for optimal hyperparameters) (.csv files) and the .npy arrays with the new weights determined in the refinement.

+

Parameters

+
+
infos : dict
+
A dictionary of information used to load data with load_data (see in the Examples directory).
+
regularization : dict
+
A dictionary which can include two keys: force_field_reg and forward_model_reg, to specify the regularizations to the force-field correction and the forward model, respectively; +the first key is either a string (among plain l2, constraint 1, constraint 2, KL divergence) or a user-defined +function which takes as input pars_ff and returns the regularization term to be multiplied by the hyperparameter beta; +the second key is a user-defined function which takes as input pars_fm and forward_coeffs_0 (current and refined forward-model coefficients) and +returns the regularization term to be multiplied by the hyperparameter gamma.
+
stride : int
+
The stride of the frames used to load data employed in search for optimal hyperparameters +(in order to reduce the computational cost, at the price of a lower representativeness of the ensembles).
+
starting_alpha, starting_beta, starting_gamma : floats
+
Starting values of the hyperparameters (np.inf by default, namely no refinement in that direction).
+
random_states : int or list of integers
+
Random states (i.e., seeds) used to split the data set in cross validation (if integer, then random_states = np.arange(random_states).
+
which_set : str
+
String chosen among 'training', 'validation' or 'test', which specifies how to determine optimal hyperparameters: +if minimizing the (average) chi2 on the training set for 'training', on training observables and test frames for 'validation', +on test observables for 'test'.
+
gtol : float
+
Tolerance gtol (on the gradient) of scipy.optimize.minimize (0.5 by default).
+
ftol : float
+
Tolerance ftol of scipy.optimize.minimize (0.05 by default).
+
results_folder_name : str
+
String for the prefix of the folder where to save results; the complete folder name is results_folder_name + '_' + time where time is the current time +when the algorithm has finished, in order to uniquely identify the folder with the results.
+
n_parallel_jobs : int
+
How many jobs are run in parallel (None by default).
+
+
+
+def save_txt(input_values, Result, coeff_names, folder_name='Result') +
+
+

This is an internal tool of MDRefinement() used to save input_values and output Result as csv and npy files in a folder whose name is +folder_name + '_' + date where date is the current time when the computation ended (it uses date_time +to generate unique file name, on the assumption of a single folder name at given time).

+

Parameters

+
+
input_values : dict
+
Dictionary with input values of the refinement, such as stride, starting values of the hyperparameters, random_states, which_set, tolerances (see MDRefinement()).
+
Result : class instance
+
Class instance with the results of minimizer and the search for the optimal hyperparameters.
+
coeff_names : list
+
List with the names of the coefficients (force-field and forward-model corrections).
+
folder_name : str
+
String for the prefix of the folder name (by default, 'Result').
+
+
+
+def unwrap_2dict(my_2dict) +
+
+

Tool to unwrap a 2-layer dictionary my_2dict into list of values and list of keys.

+
+
+
+
+
+
+ +
+ + + diff --git a/MDRefine/data_loading.html b/MDRefine/data_loading.html new file mode 100644 index 0000000..9077378 --- /dev/null +++ b/MDRefine/data_loading.html @@ -0,0 +1,602 @@ + + + + + + +MDRefine.data_loading API documentation + + + + + + + + + + + +
+
+
+

Module MDRefine.data_loading

+
+
+

Tools n. 1: data_loading. +It loads data into the data object.

+
+
+
+
+
+
+

Functions

+
+
+def check_and_skip(data, *, stride=1) +
+
+

This function is an internal tool used in load_data() to modify input data:

+
    +
  • +

    weights are normalized;

    +
  • +
  • +

    it appends observables computed through forward models (if any) to data.mol[name_sys].g;

    +
  • +
  • +

    if hasattr(data.mol[name_sys], 'selected_obs'): it removes non-selected observables from data.mol[name_sys].forward_qs;

    +
  • +
  • +

    select frames with given stride;

    +
  • +
  • +

    count n. experiments and n. frames (data.mol[name_sys].n_frames and data.mol[name_sys].n_experiments) +and check corresponding matching.

    +
  • +
+
+
+def load_data(infos, *, stride=1) +
+
+

This tool loads data from specified directory as indicated by the user in infos +to a dictionary data of classes, which includes data.properties (global properties) and data[system_name]; +for alchemical calculations, there is also data[cycle_name].

+
+
+
+
+

Classes

+
+
+class data_class +(info, path_directory, name_sys) +
+
+

Data object of a molecular system.

+

Parameters

+
+
info : dict
+
Dictionary for the information about the data of name_sys molecular system in path_directory.
+
path_directory : str
+
String for the path of the directory with data of the molecular system name_sys.
+
name_sys : str
+
Name of the molecular system taken into account.
+
+
+

Returns

+
+
temperature : float
+
Value for the temperature at which the trajectory is simulated.
+
gexp : dict
+
Dictionary of Numpy 2-dimensional arrays (N x 2); gexp[j,0] is the experimental value of the j-th observable, gexp[j,1] is the corresponding uncertainty; +the size N depends on the type of observable.
+
names : dict
+
Dictionary of Numpy 1-dimensional arrays of length N with the names of the observables of each type.
+
ref : dict
+
Dictionary of strings with signs `'=', '>', '<', '><' used to define the chi2 to compute, +depending on the observable type.
+
g : dict
+
Dictionary of Numpy 2-dimensional arrays (M x N), where g[name][i,j] is the j-th observable of that type computed in the i-th frame.
+
forward_qs : dict
+
Dictionary of Numpy 2-dimensional arrays (M x N) with the quantities required for the forward model.
+
forward_model : function
+
Function for the forward model, whose input variables are the forward-model coefficients fm_coeffs and the forward_qs dictionary; +a third optional argument is the selected_obs (dictionary with indices of selected observables).
+
weights : array_like
+
Numpy 1-dimensional array of length M with the weights (not required to be normalized).
+
f : array_like
+
Numpy 2-dimensional array (M x P) of terms required to compute the force-field correction, +where P is the n. of parameters pars and M is the n. of frames.
+
ff_correction : function
+
Function for the force-field correction, whose input variables are the force-field correction parameters pars and the f array (sorted consistently with each other).
+
+
+ +Expand source code + +
class data_class:
+    """
+    Data object of a molecular system.
+
+    Parameters
+    ----------
+    info: dict
+        Dictionary for the information about the data of `name_sys` molecular system in `path_directory`. 
+
+    path_directory: str
+        String for the path of the directory with data of the molecular system `name_sys`.
+
+    name_sys: str
+        Name of the molecular system taken into account.
+    
+    --------
+
+    Returns
+    --------
+    temperature : float
+        Value for the temperature at which the trajectory is simulated.
+    
+    gexp : dict
+        Dictionary of Numpy 2-dimensional arrays (N x 2); `gexp[j,0]` is the experimental value of the j-th observable, `gexp[j,1]` is the corresponding uncertainty;
+        the size N depends on the type of observable.
+    
+    names : dict
+        Dictionary of Numpy 1-dimensional arrays of length N with the names of the observables of each type.
+    
+    ref : dict
+        Dictionary of strings with signs `'=', '>', '<', '><' used to define the chi2 to compute,
+        depending on the observable type.
+    
+    g : dict
+        Dictionary of Numpy 2-dimensional arrays (M x N), where `g[name][i,j]` is the j-th observable of that type computed in the i-th frame.
+    
+    forward_qs : dict
+        Dictionary of Numpy 2-dimensional arrays (M x N) with the quantities required for the forward model.
+    
+    forward_model: function
+        Function for the forward model, whose input variables are the forward-model coefficients `fm_coeffs` and the `forward_qs` dictionary;
+        a third optional argument is the `selected_obs` (dictionary with indices of selected observables).
+    
+    weights: array_like
+        Numpy 1-dimensional array of length M with the weights (not required to be normalized).
+    
+    f: array_like
+        Numpy 2-dimensional array (M x P) of terms required to compute the force-field correction,
+        where P is the n. of parameters `pars` and M is the n. of frames.
+    
+    ff_correction: function
+        Function for the force-field correction, whose input variables are the force-field correction parameters `pars` and the `f` array (sorted consistently with each other).
+    """
+    def __init__(self, info, path_directory, name_sys):
+
+        # 0. temperature
+
+        if 'temperature' in info.keys():
+            self.temperature = info['temperature']
+            """`float` value for the temperature"""
+        else:
+            self.temperature = 1.0
+
+        # 1. gexp (experimental values) and names of the observables
+
+        if 'g_exp' in info.keys():
+
+            self.gexp = {}
+            """dictionary of `numpy.ndarray` containing gexp values and uncertainties"""
+            self.names = {}
+            """dictionary of `numpy.ndarray` containing names of experimental observables"""
+            self.ref = {}  # if data.gexp are boundary or puntual values
+            """dictionary of `numpy.ndarray` containing references"""
+
+            if info['g_exp'] is None:
+                if info['DDGs']['if_DDGs'] is False:
+                    print('error, some experimental data is missing')
+            else:
+                if info['g_exp'] == []:
+                    info['g_exp'] = [f[:-4] for f in os.listdir(path_directory+'%s/g_exp' % name_sys)]
+
+                for name in info['g_exp']:
+                    if type(name) is tuple:
+                        if len(name) == 5:
+                            for i in range(2):
+                                if name[2*i+2] == '>':
+                                    s = ' LOWER'
+                                elif name[2*i+2] == '<':
+                                    s = ' UPPER'
+                                else:
+                                    print('error in the sign of gexp')
+                                    return
+
+                                if os.path.isfile(path_directory+'%s/g_exp/%s%s.npy' % (name_sys, name[0], name[2*i+1])):
+                                    self.gexp[name[0]+s] = np.load(
+                                        path_directory+'%s/g_exp/%s%s.npy' % (name_sys, name[0], name[2*i+1]))
+                                elif os.path.isfile(path_directory+'%s/g_exp/%s%s' % (name_sys, name[0], name[2*i+1])):
+                                    self.gexp[name[0]+s] = numpy.loadtxt(
+                                        path_directory+'%s/g_exp/%s%s' % (name_sys, name[0], name[2*i+1]))
+
+                            self.ref[name[0]] = '><'
+
+                        elif name[1] == '=' or name[1] == '>' or name[1] == '<':
+                            if os.path.isfile(path_directory+'%s/g_exp/%s.npy' % (name_sys, name[0])):
+                                self.gexp[name[0]] = np.load(path_directory+'%s/g_exp/%s.npy' % (name_sys, name[0]))
+                            elif os.path.isfile(path_directory+'%s/g_exp/%s' % (name_sys, name[0])):
+                                self.gexp[name[0]] = numpy.loadtxt(path_directory+'%s/g_exp/%s' % (name_sys, name[0]))
+                            self.ref[name[0]] = name[1]
+
+                        else:
+                            print('error on specified sign of gexp')
+                            return
+
+                    else:
+                        if os.path.isfile(path_directory+'%s/g_exp/%s.npy' % (name_sys, name)):
+                            self.gexp[name] = np.load(path_directory+'%s/g_exp/%s.npy' % (name_sys, name))
+                        elif os.path.isfile(path_directory+'%s/g_exp/%s' % (name_sys, name)):
+                            self.gexp[name] = numpy.loadtxt(path_directory+'%s/g_exp/%s' % (name_sys, name))
+                        self.ref[name] = '='
+
+                    if type(name) is tuple:
+                        name = name[0]
+                    if os.path.isfile(path_directory+'%s/names/%s.npy' % (name_sys, name)):
+                        self.names[name] = np.load(path_directory+'%s/names/%s.npy' % (name_sys, name))
+                    elif os.path.isfile(path_directory+'%s/names/%s' % (name_sys, name)):
+                        self.names[name] = numpy.loadtxt(path_directory+'%s/names/%s' % (name_sys, name))
+
+        # 2. g (observables)
+
+        if 'obs' in info.keys():
+
+            self.g = {}
+
+            if info['obs'] is not None:
+                if info['obs'] == []:
+                    info['obs'] = [f[:-4] for f in os.listdir(path_directory+'%s/observables' % name_sys)]
+                for name in info['obs']:
+                    if os.path.isfile(path_directory+'%s/observables/%s.npy' % (name_sys, name)):
+                        self.g[name] = np.load(path_directory+'%s/observables/%s.npy' % (name_sys, name), mmap_mode='r')
+                    elif os.path.isfile(path_directory+'%s/observables/%s' % (name_sys, name)):
+                        self.g[name] = numpy.loadtxt(path_directory+'%s/observables/%s' % (name_sys, name))
+
+        # 3. forward_qs (quantities for the forward model) and forward_model
+
+        if 'forward_qs' in info.keys():
+
+            # in this way, you can define forward model either with or without selected_obs (c)
+            def my_forward_model(a, b, c=None):
+                try:
+                    out = info['forward_model'](a, b, c)
+                    for s in c.keys():
+                        if c[s] == []:
+                            del out[s]
+                except:
+                    assert c is None, 'you have selected_obs but the forward model is not suitably defined!'
+                    out = info['forward_model'](a, b)
+                return out
+
+            self.forward_model = my_forward_model  # info['forward_model']
+
+            self.forward_qs = {}
+
+            for name in info['forward_qs']:
+                if info['forward_qs'] is not None:
+                    if info['forward_qs'] == []:
+                        info['forward_qs'] = [f[:-4] for f in os.listdir(path_directory+'%s/forward_qs' % name_sys)]
+                    for name in info['forward_qs']:
+                        if os.path.isfile(path_directory+'%s/forward_qs/%s.npy' % (name_sys, name)):
+                            self.forward_qs[name] = np.load(
+                                path_directory+'%s/forward_qs/%s.npy' % (name_sys, name), mmap_mode='r')
+                        elif os.path.isfile(path_directory+'%s/forward_qs/%s' % (name_sys, name)):
+                            self.forward_qs[name] = numpy.loadtxt(path_directory+'%s/forward_qs/%s' % (name_sys, name))
+
+        # 4. weights (normalized)
+
+        if os.path.isfile(path_directory+'%s/weights.npy' % name_sys):
+            self.weights = np.load(path_directory+'%s/weights.npy' % name_sys)
+        elif os.path.isfile(path_directory+'%s/weights' % name_sys):
+            self.weights = numpy.loadtxt(path_directory+'%s/weights' % name_sys)
+        else:
+            if ('obs' in info.keys()) and not (info['obs'] is None):
+                name = list(self.g.keys())[0]
+                self.weights = np.ones(len(self.g[name]))
+            elif ('forward_qs' in info.keys()) and not (info['forward_qs'] is None):
+                name = list(self.forward_qs.keys())[0]
+                self.weights = np.ones(len(self.forward_qs[info['forward_qs'][0]]))
+            else:
+                print('error: missing MD data for %s!' % name_sys)
+
+        self.weights = self.weights/np.sum(self.weights)
+
+        # 5. f (force field correction terms) and function
+
+        if ('ff_correction' in info.keys()) and (info['ff_correction'] is not None):
+
+            if info['ff_correction'] == 'linear':
+                self.ff_correction = lambda pars, f: np.matmul(f, pars)
+            else:
+                self.ff_correction = info['ff_correction']
+
+            ff_path = path_directory + '%s/ff_terms' % name_sys
+            self.f = np.load(ff_path + '.npy')
+
+
+
+class data_cycle_class +(cycle_name, DDGs_exp, info) +
+
+

Data object of a thermodynamic cycle.

+

Parameters

+
+
cycle_name : str
+
String with the name of the thermodynamic cycle taken into account.
+
DDGs_exp : pandas.DataFrame
+
Pandas.DataFrame with the experimental values and uncertainties of Delta Delta G in labelled thermodynamic cycles.
+
info : dict
+
Dictionary for the information about the temperature of cycle_name thermodynamic cycle.
+
+
+

Returns

+
+
gexp_DDG : list
+
List of two elements: the experimental value and uncertainty of the Delta Delta G.
+
temperature : float
+
Value of temperature.
+
+
+ +Expand source code + +
class data_cycle_class:
+    """
+    Data object of a thermodynamic cycle.
+    
+    Parameters
+    ----------
+    cycle_name : str
+        String with the name of the thermodynamic cycle taken into account.
+    
+    DDGs_exp : pandas.DataFrame
+        Pandas.DataFrame with the experimental values and uncertainties of Delta Delta G in labelled thermodynamic cycles.
+
+    info: dict
+        Dictionary for the information about the temperature of `cycle_name` thermodynamic cycle. 
+
+    --------
+    Returns
+    --------
+    gexp_DDG : list
+        List of two elements: the experimental value and uncertainty of the Delta Delta G.
+    
+    temperature : float
+        Value of temperature.
+    """
+    def __init__(self, cycle_name, DDGs_exp, info):
+
+        self.gexp_DDG = [DDGs_exp.loc[:, cycle_name].iloc[0], DDGs_exp.loc[:, cycle_name].iloc[1]]
+
+        if 'temperature' in info.keys():
+            self.temperature = info['temperature']
+            """Temperature."""
+        else:
+            self.temperature = 1.0
+            """Temperature"""
+
+
+
+class datapropertiesclass +(info_global, path_directory) +
+
+

Global data, common to all the investigated molecular systems.

+

Parameters

+
+
info_global : dict
+
Dictionary with global information: +info_global['system_names'] with list of names of the molecular systems; +info_global['cycle_names'] with list of names of the thermodynamic cycles; +info_global['forward_coeffs'] with string for the file name of forward coefficients; +info_global['names_ff_pars'] with list of names of the force-field correction coefficients.
+
path_directory : str
+
String with the path of the directory with input files.
+
+
+

Returns

+
+
system_names : list
+
List of names of the investigated molecular systems.
+
forward_coeffs_0 : list
+
List of the forward-model coefficients.
+
names_ff_pars : list
+
List of names of the force-field correction parameters.
+
cycle_names : list
+
List of names of the investigated thermodynamic cycles.
+
+
+ +Expand source code + +
class datapropertiesclass:
+    """Global data, common to all the investigated molecular systems.
+    
+    Parameters
+    ----------
+
+    info_global: dict
+        Dictionary with global information:
+        `info_global['system_names']` with list of names of the molecular systems;
+        `info_global['cycle_names']` with list of names of the thermodynamic cycles;
+        `info_global['forward_coeffs']` with string for the file name of forward coefficients;
+        `info_global['names_ff_pars']` with list of names of the force-field correction coefficients.
+
+    path_directory: str
+        String with the path of the directory with input files.
+
+    --------
+    Returns
+    --------
+    system_names : list
+        List of names of the investigated molecular systems.
+    
+    forward_coeffs_0 : list
+        List of the forward-model coefficients.
+    
+    names_ff_pars : list
+        List of names of the force-field correction parameters.
+
+    cycle_names : list
+        List of names of the investigated thermodynamic cycles.
+    """
+    def __init__(self, info_global, path_directory):
+
+        self.system_names = info_global['system_names']
+
+        if 'forward_coeffs' in info_global.keys():
+            temp = pandas.read_csv(path_directory + info_global['forward_coeffs'], header=None)
+            temp.index = temp.iloc[:, 0]
+            self.forward_coeffs_0 = temp.iloc[:, 1]
+
+            # temp = pandas.read_csv(path_directory+'%s' % info_global['forward_coeffs'], index_col=0)
+            # if temp.shape[0] == 1:
+            #     self.forward_coeffs_0 = temp.iloc[:, 0]
+            # else:
+            #     self.forward_coeffs_0 = temp.squeeze()
+
+        if 'names_ff_pars' in info_global.keys():
+            self.names_ff_pars = info_global['names_ff_pars']
+        
+        if 'cycle_names' in info_global.keys():
+            self.cycle_names = info_global['cycle_names']
+
+    def tot_n_experiments(self, data):
+        """This method computes the total n. of experiments."""
+        
+        tot = 0
+
+        for k in self.system_names:
+            for item in data.mol[k].n_experiments.values():
+                tot += item
+        return tot
+
+

Methods

+
+
+def tot_n_experiments(self, data) +
+
+

This method computes the total n. of experiments.

+
+
+
+
+class my_data +(infos) +
+
+
+
+ +Expand source code + +
class my_data:
+    def __init__(self, infos):
+        
+        system_names = infos['global']['system_names']
+        path_directory = infos['global']['path_directory']
+        if not path_directory[-1] == '/': path_directory += '/'
+        
+        # global data
+        self.properties = datapropertiesclass(infos['global'], path_directory)
+
+        # data for each molecular system
+
+        self.mol = {}
+
+        for name_sys in system_names:
+
+            print('loading ', name_sys)
+            
+            if name_sys in infos.keys():
+                info = {**infos[name_sys], **infos['global']}
+            else:
+                info = infos['global']
+    
+            self.mol[name_sys] = data_class(info, path_directory, name_sys)
+
+        # data for thermodynamic cycles (alchemical calculations)
+
+        if 'cycle_names' in infos['global'].keys():
+
+            logZs = pandas.read_csv(path_directory + 'alchemical/logZs', index_col=0, header=None)
+
+            for name in infos['global']['cycle_names']:
+                for s in ['MD', 'MS', 'AD', 'AS']:
+                    key = name + '_' + s
+                    if key in logZs.index:
+                        self.mol[key].logZ = logZs.loc[key][1]
+                    else:
+                        self.mol[key].logZ = 0.0
+
+            self.cycle = {}
+
+            DDGs_exp = pandas.read_csv(path_directory + 'alchemical/DDGs', index_col=0)
+
+            for name in infos['global']['cycle_names']:
+                if name in infos.keys():
+                    info = {**infos[name], **infos['global']}
+                else:
+                    info = infos['global']
+
+                self.cycle[name] = data_cycle_class(name, DDGs_exp, info)
+
+
+
+
+
+ +
+ + + diff --git a/MDRefine/hyperminimizer.html b/MDRefine/hyperminimizer.html new file mode 100644 index 0000000..f9040aa --- /dev/null +++ b/MDRefine/hyperminimizer.html @@ -0,0 +1,265 @@ + + + + + + +MDRefine.hyperminimizer API documentation + + + + + + + + + + + +
+
+
+

Module MDRefine.hyperminimizer

+
+
+

Tools n. 3: hyperminimizer. +It performs the automatic search for the optimal hyperparameters.

+
+
+
+
+
+
+

Functions

+
+
+def compute_chi2_tot(pars_ff_fm, lambdas, data, regularization, alpha, beta, gamma, which_set) +
+
+

This function is an internal tool used in compute_hypergradient() and hyper_minimizer() +to compute the total chi2 (float variable) for the training or test data set and its derivatives +(with respect to pars_ff_fm and lambdas). The choice of the data set is indicated by which_set +(which_set = 'training' for chi2 on the training set, 'validation' for chi2 on training observables and test frames, +'test' for chi2 on test observables and test frames, through validation function).

+

Parameters

+
+
pars_ff_fm, lambdas : array_like
+
Numpy arrays for (force-field + forward-model) parameters and lambdas parameters, respectively.
+
data : dict
+
Dictionary of data set object.
+
regularization : dict
+
Specified regularizations of force-field and forward-model corrections (see in MDRefinement).
+
alpha, beta, gamma : float
+
Values of the hyperparameters.
+
which_set : str
+
String variable, chosen among 'training', 'validation' or 'test' as explained above.
+
+
+
+def compute_hyperderivatives(pars_ff_fm, lambdas, data, regularization, derivatives_funs, log10_alpha=inf, log10_beta=inf, log10_gamma=inf) +
+
+

This is an internal tool of compute_hypergradient() which computes the derivatives of parameters with respect to hyperparameters, +which are going to be used later to compute the derivatives of chi2 w.r.t. hyperparameters. +It returns an instance of the class derivatives, which includes as attributes the numerical values of +the derivatives dlambdas_dlogalpha, dlambdas_dpars, dpars_dlogalpha, dpars_dlogbeta, dpars_dloggamma.

+

Parameters

+
+
pars_ff_fm : array_like
+
Numpy array for force-field and forward-model coefficients.
+
lambdas : array_like
+
Numpy array for lambdas coefficients (those for ensemble refinement).
+
data : dict
+
The data object.
+
regularization : dict
+
The regularization of force-field and forward-model corrections (see in MDRefinement).
+
derivatives_funs : class instance
+
Instance of the derivatives_funs_class class of derivatives functions computed by Jax.
+
log10_alpha, log10_beta, log10_gamma : floats
+
Logarithms (in base 10) of the corresponding hyperparameters alpha, beta, gamma (np.inf by default).
+
+
+
+def compute_hypergradient(pars_ff_fm, lambdas, log10_alpha, log10_beta, log10_gamma, data_train, regularization, which_set, data_test, derivatives_funs) +
+
+

This is an internal tool of mini_and_chi2_and_grad(), which employs previously defined functions (compute_hyperderivatives(), compute_chi2_tot(), +put_together()) to return selected chi2 and its gradient w.r.t hyperparameters.

+

Parameters

+
+
pars_ff_fm : array_like
+
Numpy array of (force-field and forward-model) parameters.
+
lambdas : dict
+
Dictionary of dictionaries with lambda coefficients (corresponding to Ensemble Refinement).
+
log10_alpha, log10_beta, log10_gamma : floats
+
Logarithms (in base 10) of the hyperparameters alpha, beta, gamma.
+
data_train : class instance
+
The training data set object, which is anyway required to compute the derivatives of parameters w.r.t. hyper-parameters.
+
regularization : dict
+
Specified regularizations (see in MDRefinement).
+
which_set : str
+
String indicating which set defines the chi2 to minimize in order to get the optimal hyperparameters (see in compute_chi2_tot()).
+
data_test : class instance
+
The test data set object, which is required to compute the chi2 on the test set (when which_set == 'validation' or 'test'; +otherwise, if which_set = 'training', it is useless, so it can be set to None).
+
derivatives_funs : class instance
+
Instance of the derivatives_funs_class class of derivatives functions computed by Jax Autodiff (they include those employed in compute_hyperderivatives() +and dchi2_dpars and/or dchi2_dlambdas).
+
+
+
+def hyper_function(log10_hyperpars, map_hyperpars, data, regularization, test_obs, test_frames, which_set, derivatives_funs, starting_pars, n_parallel_jobs) +
+
+

This function is an internal tool of hyper_minimizer() which determines the optimal parameters by minimizing the loss function at given hyperparameters; +then, it computes chi2 and its gradient w.r.t hyperparameters (for the optimal parameters).

+

Parameters

+
+
log10_hyperpars : array_like
+
Numpy array for log10 hyperparameters alpha, beta, gamma (in this order, when present).
+
map_hyperpars : list
+
Legend for log10_hyperpars (they refer to alpha, beta, gamma in this order, +but some of them may not be present, if fixed to +np.inf).
+
data : class instance
+
Class instance for data object.
+
regularization : dict
+
Dictionaries for regularization object.
+
test_obs, test_frames : dicts
+
Dictionaries for test observables and test frames, indicized by seeds.
+
which_set : str
+
String, see for compute_chi2_tot().
+
derivatives_funs : class instance
+
Derivative functions computed by Jax and employed in compute_hypergradient().
+
starting_pars : float
+
Starting values of the parameters, if user-defined; None otherwise.
+
n_parallel_jobs : int
+
Number of parallel jobs.
+
+
+

Returns

+
+
tot_chi2 : float
+
Float value of total chi2.
+
tot_gradient : array_like
+
Numpy array for gradient of total chi2 with respect to the hyperparameters.
+
Results : class instance
+
Results given by minimizer.
+
+
+

Global variable: hyper_intermediate, in order to follow steps of minimization.

+
+
+def hyper_minimizer(data, starting_alpha=inf, starting_beta=inf, starting_gamma=inf, regularization=None, random_states=1, replica_infos=None, which_set='validation', gtol=0.5, ftol=0.05, starting_pars=None, n_parallel_jobs=None) +
+
+

This tool optimizes the hyperparameters by minimizing the selected chi2 (training, validation or test) +over several (randomly) splits of the full data set into training/test set.

+

Parameters

+
+
data : class instance
+
Object data, with the full data set previously loaded.
+
starting_alpha, starting_beta, starting_gamma : floats
+
Starting points of the hyperparameters (+np.inf by default, namely no refinement in that direction).
+
regularization : dict
+
Dictionary for the defined regularizations of force-field and forward-model corrections (None by default); see for MDRefinement.
+
replica_infos : dict
+
Dictionary with information required to split frames following continuous trajectories in replica exchange ("demuxing"); see select_traintest for further details.
+
random_states : int or list
+
Random states (i.e., seeds) used in select_traintest to split the data set into training and test set (see MDRefinement); 1 by default.
+
which_set : str
+
String choosen among 'training', 'validation', 'test' (see in MDRefinement); validation by default.
+
gtol : float
+
Tolerance gtol of scipy.optimize.minimize (0.5 by default).
+
ftol : float
+
Tolerance ftol of scipy.optimize.minimize (0.05 by default).
+
starting_pars : array_like
+
Numpy array of starting values for the minimization of parameters pars_ff_fm (None by default).
+
n_parallel_jobs : int
+
Number of jobs run in parallel (None by default).
+
+
+
+def mini_and_chi2_and_grad(data, test_frames, test_obs, regularization, alpha, beta, gamma, starting_pars, which_set, derivatives_funs) +
+
+

This is an internal tool of hyper_function() which minimizes the loss function at given hyperparameters, computes the chi2 and +its gradient w.r.t. the hyperparameters.

+

Parameters

+
+
data : class instance
+
Class instance which constitutes the data object.
+
test_frames, test_obs : dicts
+
Dictionaries for test frames and test observables (for a given random_state).
+
regularization : dict
+
Dictionary for the regularizations (see in MDRefinement).
+
alpha, beta, gamma : floats
+
Values of the hyperparameters.
+
starting_pars : array_like
+
Numpy 1-dimensional array for starting values of the coefficients in minimizer.
+
which_set : str
+
String among 'training', 'validation' or 'test' (see in MDRefinement).
+
derivatives_funs : class instance
+
Instance of the derivatives_funs_class class of derivatives functions computed by Jax Autodiff.
+
+
+
+def put_together(dchi2_dpars, dchi2_dlambdas, derivatives) +
+
+

This is an internal tool of compute_hypergradient() which applies the chain rule in order to get the derivatives of chi2 w.r.t hyperparameters from +derivatives of chi2 w.r.t. parameters and derivatives of parameters w.r.t. hyperparameters.

+

Parameters

+
+
dchi2_dpars : array-like
+
Numpy 1-dimensional array with derivatives of chi2 w.r.t. pars_ff_fm (force-field and forward-model parameters).
+
dchi2_dlambdas : array-like
+
Numpy 1-dimensional array with derivatives of chi2 w.r.t. lambdas (same order of lambdas in dchi2_dlambdas and in derivatives).
+
derivatives : class instance
+
Class instance with derivatives of pars_ff_fm and lambdas w.r.t. hyperparameters (determined in compute_hyperderivatives()).
+
+
+

Returns

+
+
out : class instance
+
Class instance whose attributes can include dchi2_dlogalpha, dchi2_dlogbeta, dchi2_dloggamma,
+
+

depending on which hyperparameters are not fixed to +np.inf.

+
+
+
+
+
+
+ +
+ + + diff --git a/MDRefine/index.html b/MDRefine/index.html new file mode 100644 index 0000000..4d46e62 --- /dev/null +++ b/MDRefine/index.html @@ -0,0 +1,104 @@ + + + + + + +MDRefine API documentation + + + + + + + + + + + +
+
+
+

Package MDRefine

+
+
+

A package to perform refinement of MD simulation trajectories.

+

Source code is on GitHub. +A test pdf manual is available here.

+

Examples

+

In the examples directory you can find a number of notebooks +that can be used as a source of inspiration.

+
+
+

Sub-modules

+
+
MDRefine.MDRefinement
+
+

Main tool: MDRefine.MDRefinement. +It refines MD-generated trajectories with customizable refinement.

+
+
MDRefine.data_loading
+
+

Tools n. 1: MDRefine.data_loading. +It loads data into the data object.

+
+
MDRefine.hyperminimizer
+
+

Tools n. 3: MDRefine.hyperminimizer. +It performs the automatic search for the optimal hyperparameters.

+
+
MDRefine.loss_and_minimizer
+
+

Tools n. 2: MDRefine.loss_and_minimizer. +It defines the loss functions and minimizes it. +It includes also select_traintest and validation.

+
+
+
+
+
+
+

Functions

+
+
+def get_version() +
+
+
+
+
+
+
+
+
+ +
+ + + diff --git a/MDRefine/loss_and_minimizer.html b/MDRefine/loss_and_minimizer.html new file mode 100644 index 0000000..3095086 --- /dev/null +++ b/MDRefine/loss_and_minimizer.html @@ -0,0 +1,660 @@ + + + + + + +MDRefine.loss_and_minimizer API documentation + + + + + + + + + + + +
+
+
+

Module MDRefine.loss_and_minimizer

+
+
+

Tools n. 2: loss_and_minimizer. +It defines the loss functions and minimizes it. +It includes also select_traintest() and validation().

+
+
+
+
+
+
+

Functions

+
+
+def compute_D_KL(weights_P: numpy.ndarray, correction_ff: numpy.ndarray, temperature: float, logZ_P: float) +
+
+

This tool computes the Kullback-Leibler divergence of P(x) = 1/Z P_0 (x) e^(-V(x)/T) +with respect to P_0 as av(V)/T + log Z where av(V) is the average value of the potential V(x) over P(x).

+

Parameters

+
+
weights_P : array_like
+
Numpy 1-dimensional array for the normalized weights P(x).
+
correction_ff : array_like
+
Numpy 1-dimensional array for the reweighting potential V(x).
+
temperature : float
+
The value of temperature T, in measure units consistently with V(x), namely, such that V(x)/T is adimensional.
+
logZ_P : float
+
The value of log Z.
+
+
+
+def compute_DeltaDeltaG_terms(data, logZ_P) +
+
+

This tool computes the chi2 for Delta Delta G (free-energy differences from thermodynamic cycles), +contributing to the loss function with alchemical calculations.

+

Parameters

+
+
data : class instance
+
Object data; here, data.properties has the attribute cycle_names (list of names of the thermodynamic cycles); +for s in data.properties.cycle_names: data.cycle[s] has attributes temperature (of the cycle) and gexp_DDG; +for s in my_list (where my_list is the list of system names associated to a thermodynamic cycle +my_list = [x2 for x in list(data.properties.cycle_names.values()) for x2 in x]): +data.mol[s] has attributes temperature (of the system) and logZ.
+
logZ_P : dict
+
+

Dictionary for logarithm of the partition function Z_P, namely, average value of exp(-V_phi(x)/temperature)

+

over the original ensemble; its keys are the selected system_names.

+
+
+

Returns

+
+
new_av_DG : dict
+
Dictionary of reweighted averages of Delta G.
+
chi2 : dict
+
Dictionary of chi2 (one for each thermodynamic cycle).
+
loss : float
+
Total contribution to the loss function from free-energy differences Delta Delta G, +given by 1/2 of the total chi2.
+
+
+
+def compute_chi2(ref, weights, g, gexp, if_separate=False) +
+
+

This tool computes the chi2 (for a given molecular system: +the input dictionaries are structured as the attributes of data.mol[mol_name]).

+

Parameters

+
+
ref : dict
+
Dictionary for references (=, >, <, ><) used to compute the appropriate chi2.
+
weights : array_like
+
Numpy 1-dimensional array of weights.
+
g : dict
+
Dictionary of observables specific for the given molecular system.
+
gexp : dict
+
Dictionary of experimental values specific for the given molecular system (coherently with g).
+
if_separate : bool
+
+

Boolean variable, True if you are distinguishing between LOWER and UPPER bounds (name_type + ' LOWER'

+

or name_type + ' UPPER'), needed for minimizations with double bounds.

+
+
+

Returns

+
+
This tool returns 4 variables: 3 dictionaries (with keys running over different kinds of observables) and 1 float:
+
 
+
av_g : dict
+
Dictionary of average values of the observables g.
+
chi2 : dict
+
Dictionary of chi2.
+
rel_diffs : dict
+
Dicionary of relative differences.
+
tot_chi2 : float
+
Total chi2 for the given molecular system.
+
+
+
+def compute_details_ER(weights_P, g, data, lambdas, alpha) +
+
+

This is an internal tool of loss_function() which computes explicitely the contribution to the loss function due to Ensemble Refinement +(namely, 1/2 chi2 + alpha D_KL) and compare this value with -alpha*Gamma (they are equal in the minimum: check). +It cycles over different systems. It acts after the minimization of the loss function inside loss_function() (not for the minimization +itself, since we exploit the Gamma function).

+

Be careful to use either: normalized values for lambdas and g (if hasattr(data.mol[name_mol],'normg_mean')) or non-normalized ones +(if not hasattr(data.mol[name_mol],'normg_mean')).

+

Parameters

+
+
weights_P : dict
+
Dictionary of Numpy arrays, namely, the weights on which Ensemble Refinement acts (those with force-field correction +in the fully combined refinement).
+
g : dict
+
Dictionary of dictionaries, like for data.mol[name_mol].g, corresponding to the observables (computed with updated forward-model coefficients).
+
data : dict
+
The original data object.
+
lambdas : dict
+
Dictionary of Numpy arrays, corresponding to the coefficients for Ensemble Refinement.
+
alpha : float
+
The alpha hyperparameter, for Ensemble Refinement.
+
+
+
+def compute_js(n_experiments) +
+
+

This tool computes the indices js (defined by cumulative sums) for lambdas corresponding to different molecular systems and +types of observables. Be careful to follow always the same order: let's choose it as that of data.n_experiments, +which is a dictionary n_experiments[name_mol][name].

+
+
+def compute_new_weights(weights: numpy.ndarray, correction: numpy.ndarray) +
+
+

This tool computes the new weights as weights*exp(-correction). +It modifies Parameters weights are normalized and correction is shifted by correction -= shift, where shift = np.min(correction). +It returns two variables: a Numpy array new_weights and a float logZ.

+
+
+def deconvolve_lambdas(data, lambdas: numpy.ndarray, if_denormalize: bool = True) +
+
+

This tool deconvolves lambdas from Numpy array to dictionary of dictionaries (corresponding to data.mol[name_mol].g); +if if_denormalize, then lambdas has been computed with normalized data, so use data.mol[name_mol].normg_std and data.mol[name_mol].normg_mean +in order to go back to corresponding lambdas for non-normalized data. The order of lambdas is the one described in compute_js().

+
+
+def gamma_function(lambdas: numpy.ndarray, g: numpy.ndarray, gexp: numpy.ndarray, weights: numpy.ndarray, alpha: float, if_gradient: bool = False) +
+
+

This tool computes gamma function and (if if_gradient) its derivatives and the average values of the observables av_g. +Make sure that lambdas follow the same order as g, gexp (let's use that of data.n_experiments).

+

Parameters

+
+
lambdas : array_like
+
Numpy 1-dimensional array of length N, where lambdas[j] is the lambda value for the j-th observable.
+
g : array_like
+
Numpy 2-dimensional array (M x N); g[i,j] is the j-th observable computed in the i-th frame.
+
gexp : array_like
+
Numpy 2-dimensional array (N x 2); gexp[j,0] is the experimental value of the j-th observable, gexp[j,1] is the associated experimental uncertainty.
+
weights : array_like
+
Numpy 1-dimensional array of length M; w[i] is the weight of the i-th frame (possibly non-normalized).
+
alpha : float
+
The value of the alpha hyperparameter.
+
if_gradient : bool
+
If true, return also the gradient of the gamma function.
+
+
+
+def l2_regularization(pars: numpy.ndarray, choice: str = 'plain l2') +
+
+

This tool computes the L2 regularization for the force-field correction coefficients pars as specified by choice. It includes:

+
    +
  • +

    'plain l2' (plain L2 regularization of pars);

    +
  • +
  • +

    L2 regularization for alchemical calculations with charges (as described by Valerio Piomponi et al., see main paper): +pars[:-1] are the charges and pars[-1] is V_eta; there is the constraint on the total charge, and there are 3 pars[4] charges in the molecule; +so, 'constraint 1' is the L2 regularization on charges, while 'constraint 2' is the L2 regularization on charges and on V_eta.

    +
  • +
+

Output values: lossf_reg and gradient (floats).

+
+
+def loss_function(pars_ff_fm: numpy.ndarray, data: dict, regularization: dict, alpha: float = inf, beta: float = inf, gamma: float = inf, fixed_lambdas: numpy.ndarray = None, gtol_inn: float = 0.001, if_save: bool = False, bounds: dict = None) +
+
+

This tool computes the fully-combined loss function (to minimize), taking advantage of the inner minimization with Gamma function.

+

If not np.isinf(alpha):

+
    +
  • +

    if fixed_lambdas == None, then do the inner minimization of Gamma (in this case, you have the global variable lambdas, +corresponding to the starting point of the minimization; it is a Numpy array sorted as in compute_js()).

    +
  • +
  • +

    else: lambdas is fixed (fixed_lambdas is not None) and the Gamma function is evaluated at this value of lambda, which must +correspond to its point of minimum, otherwise there is a mismatch between the Gamma function and the Ensemble Refinement loss.

    +
  • +
+

The order followed for lambdas is the one of compute_js(), which is not modified in any step.

+

If if_save: loss_function() returns Details class instance with the detailed results; otherwise, it returns just the loss value.

+

The input data are not modified by loss_function() (neither explicitely by loss_function() nor by its inner functions): +for forward-model updating, loss_function() defines a new variable g (through copy.deepcopy).

+

Parameters

+
+
pars_ff_fm : array_like
+
Numpy 1-dimensional array with parameters for force-field corrections and/or forward models. +These parameters are sorted as: first force-field correction (ff), then forward model (fm); +order for ff: names_ff_pars = []; for k in system_names: [names_ff_pars.append(x) for x in data[k].f.keys() if x not in names_ff_pars]; +order for fm: the same as data.forward_coeffs_0.
+
data : dict
+
Dictionary of class instances as organised in load_data, which constitutes the data object.
+
regularization : dict
+
Dictionary for the force-field and forward-model correction regularizations (see MDRefinement).
+
alpha, beta, gamma : floats
+
The hyperparameters of the three refinements (respectively, to: the ensemble, the force-field, the forward-model); +(+np.inf by default, namely no refinement in that direction).
+
fixed_lambdas : array_like
+
Numpy 1-dimensional array of fixed values of lambdas (coefficients for Ensemble Refinement, organized as in compute_js()). +(None by default).
+
gtol_inn : float
+
Tolerance gtol for the inner minimization of Gamma function (1e-3 by default).
+
if_save : bool
+
Boolean variable (False by default).
+
bounds : dict
+
Dictionary of boundaries for the inner minimization (None by default).
+
+
+
+def loss_function_and_grad(pars: numpy.ndarray, data: dict, regularization: dict, alpha: float, beta: float, gamma: float, gtol_inn: float, boundaries: dict, gradient_fun) +
+
+

This tool returns loss_function() and its gradient; the gradient function, which is going to be evaluated, is computed by Jax and passed as input variable gradient_fun. +If not np.isinf(alpha), it appends also loss and lambdas to intermediates.loss and intermediates.lambdas, respectively.

+

Global variable: intermediates (intermediate values during the minimization steps of loss_function()).

+

Parameters

+
+
pars : array_like
+
Numpy array of parameters for force-field correction and forward model, respectively.
+
data, regularization : dicts
+
Dictionaries for data object and regularizations (see in MDRefinement).
+
alpha, beta, gamma : floats
+
Values of the hyperparameters.
+
gtol_inn : float
+
Tolerance gtol for the inner minimization in loss_function().
+
boundaries : dict
+
Dictionary of boundaries for the inner minimization in loss_function().
+
gradient_fun : function
+
Gradient function of loss_function(), computed by Jax.
+
+
+
+def minimizer(original_data, *, regularization: dict = None, alpha: float = inf, beta: float = inf, gamma: float = inf, gtol: float = 0.001, gtol_inn: float = 0.001, data_test: dict = None, starting_pars: numpy.ndarray = None) +
+
+

This tool minimizes loss_function on original_data and do validation() on data_test (if not None), at given hyperparameters.

+

Parameters

+
+
original_data : dict
+
Dictionary for data-like object employed for the minimization of loss_function().
+
regularization : dict
+
Dictionary for the regularizations (see in MDRefinement).
+
alpha, beta, gamma : floats
+
Values of the hyperparameters for combined refinement (+np.inf by default: no refinement in that direction).
+
gtol, gtol_inn : floats
+
Tolerances gtol for the minimizations of loss_function() and inner gamma_function(), respectively.
+
data_test : dict
+
Dictionary for data-like object employed as test set (None by default, namely no validation, just minimization).
+
starting_pars : array_like
+
Numpy 1-dimensional array for pre-defined starting point of loss_function() minimization (None by default).
+
+
+
+def normalize_observables(gexp, g, weights=None) +
+
+

This tool normalizes g and gexp. Since experimental observables have different units, it is better to normalize them, in order that +varying any lambda coefficient by the same value epsilon would result in comparable effects to the ensemble. +This results to be useful in the minimization of gamma_function().

+

Parameters

+
+
gexp, g : dicts
+
Dictionaries corresponding to data.mol[name_mol].gexp and data.mol[name_mol].g.
+
weights : array-like
+
+

 

+

Numpy 1-dimensional array, by default None (namely, equal weight for each frame).

+
+
+

Returns

+
+
norm_g, norm_gexp : dict
+
Dictionaries for normalized g and gexp.
+
norm_gmean, norm_gstd : dict
+
Dictionaries for the reference values for normalization (average and standard deviation).
+
+
+
+def select_traintest(data, *, test_frames_size: float = 0.2, test_obs_size: float = 0.2, random_state: int = None, test_frames: dict = None, test_obs: dict = None, if_all_frames: bool = False, replica_infos: dict = None) +
+
+

This tool splits the data set into training and test set. You can either randomly select the frames and/or the observables (accordingly to test_frames_size, test_obs_size, random_state) or pass the dictionaries test_obs and/or test_frames.

+

Parameters

+
+
data : class instance
+
Class instance for the data object.
+
test_frames_size, test_obs_size : float
+
Values for the fractions of frames and observables for the test set, respectively. Each of them is a number in (0,1) (same fraction for every system), +by default 0.2.
+
random_state : int
+
The random state (or seed), used to make the same choice for different hyperparameters; if None, +it is randomly taken.
+
test_frames, test_obs : dicts
+
Dictionaries for the test frames and observables.
+
if_all_frames : bool
+
Boolean variable, False by default; if True, then use all the frames for the test observables in the test set, +otherwise just the test frames.
+
replica_infos : dict
+
+

Dictionary of information used to select frames based on continuous trajectories ("demuxing"), by default None (just randomly select frames). +It includes: n_temp_replica, path_directory, stride. If not None, select_traintest() will read replica_temp.npy files +with shape (n_frames, n_replicas) containing numbers from 0 to n_replicas - 1 which indicate corresponding temperatures (for each replica

+

index in axis=1).

+
+
+

Returns

+
+
data_train, data_test : class instances
+
Class instances for training and test data; data_test includes: +trained observables and non-trained (test) frames (where it is not specified new); +non-trained (test) observables and non-trained/all (accordingly to if_all_frames) frames (where specified new).
+
test_obs, test_frames : dicts
+
Dictionaries for the observables and frames selected for the test set.
+
+
+
+def validation(pars_ff_fm, lambdas, data_test, *, regularization=None, alpha=inf, beta=inf, gamma=inf, data_train=None, which_return='details') +
+
+

This tool evaluates loss_function() in detail over the test set; then,

+
    +
  • +

    if which_return == 'chi2 validation', it returns the total chi2 on the 'validation' data set (training observables, test frames); +this is required to compute the derivatives of the chi2 in 'validation' with Jax;

    +
  • +
  • +

    elif which_return == 'chi2 test', it returns the total chi2 on the 'test' data set (test observables, test frames +or all frames if data_train is not None); this is required to compute the derivatives of the chi2 in 'test' with Jax;

    +
  • +
  • +

    else, it returns Validation_values class instance, with all the computed values (both chi2 and regularizations).

    +
  • +
+

Parameters

+
+
pars_ff_fm : array_like
+
Numpy 1-dimensional array for the force-field and forward-model coefficients.
+
lambdas : array_like
+
Numpy 1-dimensional array of lambdas coefficients (those for ensemble refinement).
+
data_test : dict
+
Dictionary for the test data set, data-like object, as returned by select_traintest().
+
regularization : dict
+
Dictionary for the regularizations (see in MDRefinement), by default, None.
+
alpha, beta, gamma : floats
+
Values for the hyperparameters (by default, +np.inf, namely, no refinement).
+
data_train : dict
+
Dictionary for the training data set, data-like object, as returned by select_traintest() (None by default, +namely use only test frames for new observables).
+
which_return : str
+
String described above (by default 'details').
+
+
+
+
+
+

Classes

+
+
+class class_test +(data_mol, test_frames_mol, test_obs_mol, if_all_frames, data_train_mol) +
+
+

Class for test data set, with similar structure as data_class.

+
+ +Expand source code + +
class class_test:
+    """
+    Class for test data set, with similar structure as `data_class`.
+    """
+    def __init__(self, data_mol, test_frames_mol, test_obs_mol, if_all_frames, data_train_mol):
+
+        # A. split weights
+        try:
+            w = data_mol.weights[tuple(test_frames_mol)]
+        except:
+            try:
+                w = data_mol.weights[test_frames_mol]
+            except:
+                w = data_mol.weights[list(test_frames_mol)]
+        
+        self.logZ = np.log(np.sum(w))
+        self.weights = w/np.sum(w)
+        self.n_frames = np.shape(w)[0]
+
+        # B. split force-field terms
+        if hasattr(data_mol, 'f'):
+            self.ff_correction = data_mol.ff_correction
+            try:
+                self.f = data_mol.f[test_frames_mol, :]
+            except:
+                self.f = data_mol.f[list(test_frames_mol), :]
+
+        # C. split experimental values gexp, normg_mean and normg_std, observables g
+
+        if hasattr(data_mol, 'gexp'):
+            self.gexp_new = {}
+            self.n_experiments_new = {}
+
+            for name_type in data_mol.gexp.keys():
+
+                try:
+                    self.gexp_new[name_type] = data_mol.gexp[name_type][list(test_obs_mol[name_type])]
+                except:
+                    self.gexp_new[name_type] = data_mol.gexp[name_type][test_obs_mol[name_type]]
+
+                self.n_experiments_new[name_type] = len(test_obs_mol[name_type])
+
+        if hasattr(data_mol, 'names'):
+
+            self.names_new = {}
+
+            for name_type in data_mol.names.keys():
+                self.names_new[name_type] = data_mol.names[name_type][list(test_obs_mol[name_type])]
+
+        if hasattr(data_mol, 'g'):
+
+            self.g_new = {}
+            if if_all_frames:
+                self.g_new_old = {}
+            self.g = {}
+
+            for name_type in data_mol.g.keys():
+
+                # split g into: train, test1 (non-trained obs, all frames or only non-used ones),
+                # test2 (trained obs, non-used frames)
+                # if not test_obs[name_mol][name_type] == []:
+                self.g_new[name_type] = (data_mol.g[name_type][test_frames_mol, :].T)[test_obs_mol[name_type], :].T
+
+                if if_all_frames:  # new observables on trained frames
+                    self.g_new_old[name_type] = np.delete(
+                        data_mol.g[name_type], test_frames_mol, axis=0)[:, list(test_obs_mol[name_type])]
+
+                g3 = np.delete(data_mol.g[name_type], test_obs_mol[name_type], axis=1)
+                self.g[name_type] = g3[test_frames_mol, :]
+
+        if hasattr(data_mol, 'forward_qs'):
+
+            self.forward_qs = {}
+
+            for name_type in data_mol.forward_qs.keys():
+                self.forward_qs[name_type] = data_mol.forward_qs[name_type][list(test_frames_mol), :]
+
+            if if_all_frames:
+                self.forward_qs_trained = copy.deepcopy(data_train_mol.forward_qs)
+
+        if hasattr(data_mol, 'forward_model'):
+            self.forward_model = data_mol.forward_model
+
+        self.ref = copy.deepcopy(data_mol.ref)
+        self.ref_all = copy.deepcopy(data_mol.ref)
+        self.selected_obs = copy.deepcopy(data_train_mol.selected_obs)  # same observables as in training
+        self.selected_obs_new = test_obs_mol
+
+        self.gexp = copy.deepcopy(data_train_mol).gexp
+        self.n_experiments = copy.deepcopy(data_train_mol).n_experiments
+        self.temperature = data_mol.temperature
+
+
+
+class class_train +(data_mol, test_frames_mol, test_obs_mol) +
+
+

Class for training data set, with similar structure as data_class.

+
+ +Expand source code + +
class class_train:
+    """
+    Class for training data set, with similar structure as `data_class`.
+    """
+    def __init__(self, data_mol, test_frames_mol, test_obs_mol):
+
+        # training observables
+        train_obs = {}
+        for s in data_mol.n_experiments.keys():
+            train_obs[s] = [i for i in range(data_mol.n_experiments[s]) if i not in test_obs_mol[s]]
+        self.selected_obs = train_obs
+
+        # A. split weights
+        w = np.delete(data_mol.weights, test_frames_mol)
+        self.logZ = np.log(np.sum(w))
+        self.weights = w/np.sum(w)
+        self.n_frames = np.shape(w)[0]
+
+        # B. split force-field terms
+
+        if hasattr(data_mol, 'f'):
+            self.ff_correction = data_mol.ff_correction
+            self.f = np.delete(data_mol.f, test_frames_mol, axis=0)
+
+        # C. split experimental values gexp, normg_mean and normg_std, observables g
+
+        if hasattr(data_mol, 'gexp'):
+
+            self.gexp = {}
+            self.n_experiments = {}
+
+            for name_type in data_mol.gexp.keys():
+                self.gexp[name_type] = np.delete(data_mol.gexp[name_type], test_obs_mol[name_type], axis=0)
+                self.n_experiments[name_type] = np.shape(self.gexp[name_type])[0]
+
+        if hasattr(data_mol, 'names'):
+
+            self.names = {}
+
+            for name_type in data_mol.names.keys():
+                self.names[name_type] = data_mol.names[name_type][train_obs[name_type]]
+
+        if hasattr(data_mol, 'g'):
+
+            self.g = {}
+
+            for name_type in data_mol.g.keys():
+                train_g = np.delete(data_mol.g[name_type], test_frames_mol, axis=0)
+                self.g[name_type] = np.delete(train_g, test_obs_mol[name_type], axis=1)
+
+        if hasattr(data_mol, 'forward_qs'):
+
+            self.forward_qs = {}
+
+            for name_type in data_mol.forward_qs.keys():
+                self.forward_qs[name_type] = np.delete(data_mol.forward_qs[name_type], test_frames_mol, axis=0)
+
+        if hasattr(data_mol, 'forward_model'):
+            self.forward_model = data_mol.forward_model
+
+        self.ref = data_mol.ref
+
+        self.temperature = data_mol.temperature
+
+
+
+class intermediates_class +(alpha) +
+
+

Class for the intermediate steps of the minimization of the loss function.

+
+ +Expand source code + +
class intermediates_class:
+    """Class for the intermediate steps of the minimization of the loss function."""
+    def __init__(self, alpha):
+        
+        self.loss = []
+        self.pars = []
+
+        if not np.isinf(alpha):
+            self.lambdas = []
+            self.minis = []
+
+
+
+
+
+ +
+ + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d67d33 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +Precompiled manual for MDRefine +------------------------------- + +This repository hosts a precompiled manual for MDRefine +git revision [7dce8f9](https://github.com/bussilab/MDRefine/commit/7dce8f9). + +To browse the manual you should go [here](http://bussilab.github.io/doc-MDRefine). + +You can also download a full copy of the manual for offline access +at [this link](http://github.com/bussilab/doc-MDRefine/archive/master.zip). + +This manual has been compiled on [GitHub Actions](https://github.com/bussilab/MDRefine/actions) on Tue Nov 19 08:20:53 UTC 2024. + diff --git a/examples/Tutorial.html b/examples/Tutorial.html new file mode 100644 index 0000000..47d0e97 --- /dev/null +++ b/examples/Tutorial.html @@ -0,0 +1,20296 @@ + + + + + +Tutorial + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Results = {} + +for beta in betas: + print('beta: ', beta) + + Results[beta] = minimizer(data, regularization=regularization, beta=beta) + + clear_output() + + +!mkdir ../../Figures +!mkdir ../../Results + +Results = Parallel(n_jobs=10, verbose=1)(delayed(minimizer)( + data, regularization=regularization, beta=beta) for beta in betas) + +df.to_csv('Results/alchemical_calculations/values_L2charges') +df2.to_csv('Results/alchemical_calculations/values_DKL') + + + + +
+ + diff --git a/examples/Tutorial_2.html b/examples/Tutorial_2.html new file mode 100644 index 0000000..dd245b0 --- /dev/null +++ b/examples/Tutorial_2.html @@ -0,0 +1,9984 @@ + + + + + +Tutorial_2 + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + diff --git a/examples/Tutorial_3.html b/examples/Tutorial_3.html new file mode 100644 index 0000000..5418002 --- /dev/null +++ b/examples/Tutorial_3.html @@ -0,0 +1,13437 @@ + + + + + +Tutorial_3 + + + + + + + + + + + + +
+ + + +def forward_model_fun(fm_coeffs, forward_qs): + + # 1. compute the cosine (which is the quantity you need in the forward model; + # you could do this just once before loading data) + forward_qs_cos = {} + + for type_name in forward_qs.keys(): + forward_qs_cos[type_name] = jnp.cos(forward_qs[type_name]) + + # # if you have selected_obs, compute only the corresponding observables + # if selected_obs is not None: + # for type_name in forward_qs.keys(): + # forward_qs_cos[type_name] = forward_qs_cos[type_name][:,selected_obs[type_name+'_3J']] + + # 2. compute observables (forward_qs_out) through forward model + forward_qs_out = { + 'backbone1_gamma_3J': fm_coeffs[0]*forward_qs_cos['backbone1_gamma']**2 + fm_coeffs[1]*forward_qs_cos['backbone1_gamma'] + fm_coeffs[2], + 'backbone2_beta_epsilon_3J': fm_coeffs[3]*forward_qs_cos['backbone2_beta_epsilon']**2 + fm_coeffs[4]*forward_qs_cos['backbone2_beta_epsilon'] + fm_coeffs[5], + 'sugar_3J': fm_coeffs[6]*forward_qs_cos['sugar']**2 + fm_coeffs[7]*forward_qs_cos['sugar'] + fm_coeffs[8] } + + return forward_qs_out +infos['global']['names_ff_pars'] = ['sin alpha', 'cos alpha', 'sin zeta', 'cos zeta'] + +def ff_correction(pars, f): + out = jnp.matmul(pars, (f[:,[0,6,3,9]]+f[:,[1,7,4,10]]+f[:,[2,8,5,11]]).T) + return out + +def ff_correction_hexamers(pars, f): + out = jnp.matmul(pars, (f[:,[0,10,5,15]]+f[:,[1,11,6,16]]+f[:,[2,12,7,17]]+f[:,[3,13,8,18]]+f[:,[4,14,9,19]]).T) + return out + +infos['global']['ff_correction'] = ff_correction +infos['UCAAUC'] = {'ff_correction': ff_correction_hexamers} + + + + +{'original': 0 +A_gamma 9.70 +B_gamma -1.80 +C_gamma 0.00 +A_beta 15.30 +B_beta -6.10 +C_beta 1.60 +A_sugar 9.67 +B_sugar -2.03 +C_sugar 0.00 +Name: 1, dtype: float64, 'Thorben': 0 +A_gamma 10.07 +B_gamma -1.87 +C_gamma -0.13 +A_beta 18.34 +B_beta -5.39 +C_beta 0.11 +A_sugar 7.81 +B_sugar -2.05 +C_sugar 0.25 +Name: 1, dtype: float64} + + + + + + + + + + + + + + + + + + +
+ + diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..79d773f --- /dev/null +++ b/examples/index.html @@ -0,0 +1,42 @@ + + + + + + + Directory Tree + + + +

Directory Tree

+ .
+ ├── Tutorial.html
+ ├── Tutorial_2.html
+ ├── Tutorial_3.html
+ ├── index.html
+ └── load_data.html
+


+

+ tree v2.0.2 © 1996 - 2022 by Steve Baker and Thomas Moore
+ HTML output hacked and copyleft © 1998 by Francesc Rocher
+ JSON output hacked and copyleft © 2014 by Florian Sesser
+ Charsets / OS/2 support © 2001 by Kyosuke Tokoro +

+ + diff --git a/examples/load_data.html b/examples/load_data.html new file mode 100644 index 0000000..49746aa --- /dev/null +++ b/examples/load_data.html @@ -0,0 +1,9596 @@ + + + + + +load_data + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..1fc106a --- /dev/null +++ b/index.html @@ -0,0 +1,10 @@ + + + +Page Auto Redirect + + + +This is an auto redirect page. + +