Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify how to adjust GP hyp masks and cutoffs #200

Merged
merged 15 commits into from
Jul 13, 2020
48 changes: 44 additions & 4 deletions flare/gp.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ class GaussianProcess:
(pg. 19) of "Gaussian Processes for Machine Learning" by Rasmussen and
Williams.

Methods within GaussianProcess allow you to make predictions on
AtomicEnvironment objects (see env.py) generated from
FLARE Structures (see struc.py), and after data points are added,
optimize hyperparameters based on available training data (train method).

Args:
kernels (list, optional): Determine the type of kernels. Example:
['2', '3'], ['2', '3', 'mb'], ['2']. Defaults to ['2', '3']
Expand All @@ -57,7 +62,8 @@ class GaussianProcess:
during optimization. Defaults to None.
hyps_mask (dict, optional): hyps_mask can set up which hyper parameter
is used for what interaction. Details see kernels/mc_sephyps.py
name (str, optional): Name for the GP instance.
name (str, optional): Name for the GP instance which dictates global
memory access.
"""

def __init__(self, kernels: List[str] = ['two', 'three'],
Expand Down Expand Up @@ -213,7 +219,9 @@ def check_instantiation(self):

self.bounds = deepcopy(self.hyps_mask.get('bounds', None))

def update_kernel(self, kernels, component="mc", hyps_mask=None):
def update_kernel(self, kernels: List[str], component: str = "mc",
hyps=None, cutoffs: dict = None,
hyps_mask: dict=None):
kernel, grad, ek, efk, _, _, _ = str_to_kernel_set(
kernels, component, hyps_mask)
self.kernel = kernel
Expand All @@ -222,6 +230,24 @@ def update_kernel(self, kernels, component="mc", hyps_mask=None):
self.energy_kernel = ek
self.kernels = kernel_str_to_array(kernel.__name__)

if hyps_mask is not None:
self.hyps_mask = hyps_mask
# Cutoffs argument will override hyps mask's cutoffs key, if present
if isinstance(hyps_mask, dict) and cutoffs is None:
cutoffs = hyps_mask.get('cutoffs', None)

if cutoffs is not None:
if self.cutoffs != cutoffs:
self.adjust_cutoffs(cutoffs, train=False,
new_hyps_mask=hyps_mask)
self.cutoffs = cutoffs

if isinstance(hyps_mask, dict) and hyps is None:
hyps = hyps_mask.get('hyps', None)

if hyps is not None:
self.hyps = hyps

def update_db(self, struc: Structure, forces: List,
custom_range: List[int] = (), energy: float = None):
"""Given a structure and forces, add local environments from the
Expand Down Expand Up @@ -770,7 +796,7 @@ def compute_matrices(self):
self.alpha = np.matmul(self.ky_mat_inv, self.all_labels)


def adjust_cutoffs(self, new_cutoffs: Union[list, tuple, 'np.ndarray'],
def adjust_cutoffs(self, new_cutoffs: Union[list, tuple, 'np.ndarray'] = None,
reset_L_alpha=True, train=True, new_hyps_mask=None):
"""
Loop through atomic environment objects stored in the training data,
Expand All @@ -780,15 +806,29 @@ def adjust_cutoffs(self, new_cutoffs: Union[list, tuple, 'np.ndarray'],
it is **highly** suggested that you call set_L_alpha and
re-optimize your hyperparameters afterwards as is default here.

A helpful way to update the cutoffs and kernel for an extant
GP is to perform the following commands:
>> hyps_mask = pm.as_dict()
>> hyps = hyps_mask['hyps']
>> cutoffs = hyps_mask['cutoffs']
>> kernels = hyps_mask['kernels']
>> gp_model.update_kernel(kernels, 'mc', hyps, cutoffs, hyps_mask)

:param new_cutoffs:
:return:
"""

if (new_hyps_mask is not None):
if new_hyps_mask is not None:
hm = new_hyps_mask
self.hyps_mask = new_hyps_mask
else:
hm = self.hyps_mask
if new_cutoffs is None:
try:
new_cutoffs = hm['cutoffs']
except KeyError:
raise KeyError("New cutoffs not found in the hyps_mask"
"dictionary via call to 'cutoffs' key.")

# update environment
nenv = len(self.training_data)
Expand Down
15 changes: 11 additions & 4 deletions flare/utils/parameter_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@
... 'threebody0':[1, 0.5], 'threebody1':[2, 0.2],
... 'cutoff_threebody':1},
... constraints={'twobody0':[False, True]})
>>> hm = pm.hyps_mask
>>> hyps = hm['hyps']
>>> cutoffs = hm['cutoffs']
>>> hm = pm.as_dict()
>>> kernels = hm['kernels']
>>> gp_model = GaussianProcess(kernels=kernels, cutoffs=cutoffs,
>>> gp_model = GaussianProcess(kernels=kernels,
... hyps=hyps, hyps_mask=hm)

In this example, four atomic species are involved. There are many kinds
Expand Down Expand Up @@ -93,6 +91,15 @@

See more examples in functions ``ParameterHelper.define_group`` , ``ParameterHelper.set_parameters``,
and in the tests ``tests/test_parameters.py``

If you want to add in a new hyperparameter set to an already-existing GP, you can perform the
following steps:

>> hyps_mask = pm.as_dict()
>> hyps = hyps_mask['hyps']
>> kernels = hyps_mask['kernels']
>> gp_model.update_kernel(kernels, 'mc', hyps_mask)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be revised accordingly.

>> gp_model.hyps = hyps
"""

import inspect
Expand Down
5 changes: 2 additions & 3 deletions tests/test_gp.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,7 @@ def test_constrained_optimization_simple(self, all_gps):

test_gp.hyps_mask = hm
test_gp.hyp_labels = hm['hyp_labels']
test_gp.hyps = hyps
test_gp.update_kernel(hm['kernel_name'], "mc", hm)
test_gp.update_kernel(hm['kernel_name'], "mc", hyps, cutoffs, hm)
test_gp.set_L_alpha()

hyp = list(test_gp.hyps)
Expand Down Expand Up @@ -268,7 +267,7 @@ def test_update_L_alpha(self, all_gps, params, par, n_cpus, multihyps):

test_structure, forces = \
get_random_structure(params['cell'], params['unique_species'], 2)
energy = 3.14
energy = 3.14
test_gp.check_L_alpha()
test_gp.update_db(test_structure, forces, energy=energy)
test_gp.update_L_alpha()
Expand Down