Skip to content

Commit

Permalink
Adding ability to define test space through convex hull (#18)
Browse files Browse the repository at this point in the history
* Track losses during training

* Add convex hull parameter space option

* Documentation in numpy style
  • Loading branch information
andersonw1 authored Nov 13, 2024
1 parent 32f1944 commit 49fe119
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 5 deletions.
21 changes: 21 additions & 0 deletions examples/burgers1d.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ parameter_space:
test_space:
type: grid

## An example if we want to provide training points on exterior
## of region and train in convex hull of training points
# parameter_space:
# parameters:
# - name: a
# min: 0.7
# max: 0.9
# test_space_type: list
# sample_size: 21
# list: [0.70, 0.725, 0.75, 0.800, 0.85, 0.90]
# log_scale: false
# - name: w
# min: 0.9
# max: 1.1
# test_space_type: list
# sample_size: 21
# list: [0.90, 0.970, 1.00, 0.925, 0.98, 1.10]
# log_scale: false
# test_space:
# type: hull

latent_space:
type: ae
ae:
Expand Down
15 changes: 13 additions & 2 deletions src/lasdi/gplasdi.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ def train(self):
n_train = ps.n_train()
ld = self.latent_dynamics

self.training_loss = []
self.ae_loss = []
self.ld_loss = []
self.coef_loss = []

'''
determine number of iterations.
Perform n_iter iterations until overall iterations hit max_iter.
Expand All @@ -184,6 +189,11 @@ def train(self):

loss = loss_ae + self.ld_weight * loss_ld / n_train + self.coef_weight * loss_coef / n_train

self.training_loss.append(loss.item())
self.ae_loss.append(loss_ae.item())
self.ld_loss.append(loss_ld.item())
self.coef_loss.append(loss_coef.item())

loss.backward()
self.optimizer.step()

Expand Down Expand Up @@ -267,7 +277,8 @@ def export(self):
dict_ = {'X_train': self.X_train, 'X_test': self.X_test, 'lr': self.lr, 'n_iter': self.n_iter,
'n_samples' : self.n_samples, 'best_coefs': self.best_coefs, 'max_iter': self.max_iter,
'max_iter': self.max_iter, 'ld_weight': self.ld_weight, 'coef_weight': self.coef_weight,
'restart_iter': self.restart_iter, 'timer': self.timer.export(), 'optimizer': self.optimizer.state_dict()
'restart_iter': self.restart_iter, 'timer': self.timer.export(), 'optimizer': self.optimizer.state_dict(),
'training_loss' : self.training_loss, 'ae_loss' : self.ae_loss, 'ld_loss' : self.ld_loss, 'coeff_loss' : self.coef_loss
}
return dict_

Expand All @@ -280,4 +291,4 @@ def load(self, dict_):
self.optimizer.load_state_dict(dict_['optimizer'])
if (self.device != 'cpu'):
optimizer_to(self.optimizer, self.device)
return
return
111 changes: 108 additions & 3 deletions src/lasdi/param.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
from scipy.spatial import Delaunay
from .inputs import InputParser

def get_1dspace_from_list(config):
Expand Down Expand Up @@ -40,12 +41,18 @@ def __init__(self, config):
for param in self.param_list:
self.param_name += [param['name']]

self.train_space = self.createInitialTrainSpace(self.param_list)
self.n_init = self.train_space.shape[0]

test_space_type = parser.getInput(['test_space', 'type'], datatype=str)
if (test_space_type == 'grid'):
self.train_space = self.createInitialTrainSpace(self.param_list)
self.n_init = self.train_space.shape[0]

self.test_grid_sizes, self.test_meshgrid, self.test_space = self.createTestGridSpace(self.param_list)
if (test_space_type == 'hull'):
assert self.n_param >=2, 'Must have at least 2 parameters if test_space is \'hull\' '
self.train_space = self.createInitialTrainSpaceForHull(self.param_list)
self.n_init = self.train_space.shape[0]

self.test_grid_sizes, self.test_meshgrid, self.test_space = self.createTestHullSpace(self.param_list)

return

Expand All @@ -66,6 +73,38 @@ def createInitialTrainSpace(self, param_list):
mesh_grids = self.createHyperMeshGrid(paramRanges)
return self.createHyperGridSpace(mesh_grids)

def createInitialTrainSpaceForHull(self, param_list):
'''
Concatenates the provided lists of training points into a 2D array.
Arguments
---------
param_list : :obj:`list(dict)`
A list of parameter dictionaries
Returns
-------
mesh_grids : :obj:`numpy.array`
np.array of size [d, k], where d is the number of points provided on the exterior of
the training space and k is the number of parameters (k == len(param_list)).
'''

paramRanges = []

for k, param in enumerate(param_list):

_, paramRange = getParam1DSpace['list'](param)
paramRanges += [paramRange]

if k > 0:
assert (len(paramRanges[k])==len(paramRanges[k - 1])), (f'Training parameters {k} and {k-1} have '
'different lengths. All training parameters '
'must have same length when test_space is \'hull\'.')


mesh_grids = np.vstack((paramRanges)).T
return mesh_grids

def createTestGridSpace(self, param_list):
paramRanges = []
gridSizes = []
Expand All @@ -78,6 +117,72 @@ def createTestGridSpace(self, param_list):
mesh_grids = self.createHyperMeshGrid(paramRanges)
return gridSizes, mesh_grids, self.createHyperGridSpace(mesh_grids)

def createTestGridSpaceForHull(self, param_list):
'''
Builds an initial uniform grid for the testing parameters when the test_space is 'hull'.
Arguments
---------
param_list : :obj:`list(dict)`
A list of parameter dictionaries
Returns
-------
gridSizes : :obj:`list(int)`
A list containing the number of elements on the grid in each parameter.
mesh_grids : :obj:`numpy.array`
tuple of numpy nd arrays, corresponding to each parameter.
Dimension of the array equals to the number of parameters.
param_grid : :obj:`numpy.array`
numpy 2d array of size (grid size x number of parameters).
'''

paramRanges = []
gridSizes = []

for param in param_list:
Nx, paramRange = getParam1DSpace['uniform'](param)
gridSizes += [Nx]
paramRanges += [paramRange]

mesh_grids = self.createHyperMeshGrid(paramRanges)
return gridSizes, mesh_grids, self.createHyperGridSpace(mesh_grids)

def createTestHullSpace(self, param_list):
'''
This function builds an initial uniform grid for the testing parameters, and then
returns any testing points which are within the convex hull of the provided
training parameters.
Arguments
---------
param_list : :obj:`list(dict)`
A list of parameter dictionaries
Returns
-------
gridSizes : :obj:`list(int)`
A list containing the number of elements on the grid in each parameter.
mesh_grids : :obj:`numpy.array`
tuple of numpy nd arrays, corresponding to each parameter.
Dimension of the array equals to the number of parameters.
test_space : :obj:`numpy.array`
numpy 2d array of size [d, k], where d is the number of testing points within
convex hull of the training space and k is the number of parameters (k == len(param_list)).
'''

# Get the initial uniform grid over the training parameters
gridSizes, mesh_grids, test_space = self.createTestGridSpaceForHull(param_list)

# Mesh the training space. This will be slow in higher dimensions
cloud = Delaunay(self.train_space)
# Determine which test points are contained in the convex hull of training points
mask = cloud.find_simplex(test_space)>=0
# Only keep testing points in the convex hull of training points
test_space = test_space[mask]

return gridSizes, mesh_grids, test_space

def getParameter(self, param_vector):
'''
convert numpy array parameter vector to a dict.
Expand Down

0 comments on commit 49fe119

Please sign in to comment.