Skip to content

Commit

Permalink
Added Latin Hypercube sampling (#216)
Browse files Browse the repository at this point in the history
* Added Latin Hypercube sampling

* added chebyshev2-noisy

* changes suggested by shuheng

* fixed latin-hypercube for 2D generator

* skipped defining self.examples for chebychev-noisy and latin-hypercube
  • Loading branch information
Lakshay-13 committed May 23, 2024
1 parent 0ee81fd commit fb91d55
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 1 deletion.
2 changes: 2 additions & 0 deletions neurodiffeq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@

# Turn on future warning by default
warnings.simplefilter('always', FutureWarning)

__version__ = "0.6.3-dev"
48 changes: 47 additions & 1 deletion neurodiffeq/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ def _chebyshev_second(a, b, n):
nodes.requires_grad_(True)
return nodes

def _chebyshev_second_noisy(a, b, n):
nodes = torch.cos((torch.arange(n) + (torch.rand(n) * 2 - 1)) / float(n - 1) * np.pi)
nodes = ((a + b) + (b - a) * nodes) / 2
nodes.requires_grad_(True)
return nodes


def _latin_hypercube(a, b, n):
intervals = torch.linspace(a, b, steps=n + 1)
points = torch.rand(n) * (intervals[1] - intervals[0])
points += intervals[:-1]
points = points[torch.randperm(n)]
points.requires_grad_(True)
return points


def _compute_log_negative(t_min, t_max, whence):
if t_min <= 0 or t_max <= 0:
Expand Down Expand Up @@ -110,6 +125,7 @@ class Generator1D(BaseGenerator):
- If set to 'log-spaced-noisy', a normal noise will be added to the previously mentioned set of points,
- If set to 'chebyshev1' or 'chebyshev', the points are chebyshev nodes of the first kind over (t_min, t_max).
- If set to 'chebyshev2', the points will be chebyshev nodes of the second kind over [t_min, t_max].
- If set to 'latin_hypercube', the points will be generated using Latin Hypercube Sampling from t_min to t_max.
defaults to 'uniform'.
:type method: str, optional
Expand Down Expand Up @@ -154,6 +170,10 @@ def __init__(self, size, t_min=0.0, t_max=1.0, method='uniform', noise_std=None)
elif method == 'chebyshev2':
self.examples = _chebyshev_second(t_min, t_max, size)
self.getter = lambda: self.examples
elif method == 'chebyshev2-noisy':
self.getter = lambda: _chebyshev_second_noisy(t_min, t_max, size)
elif method == 'latin-hypercube':
self.getter = lambda: _latin_hypercube(t_min, t_max, size)
else:
raise ValueError(f'Unknown method: {method}')

Expand Down Expand Up @@ -196,6 +216,7 @@ class Generator2D(BaseGenerator):
- If set to 'equally-spaced-noisy', a normal noise will be added to the previously mentioned set of points.
- If set to 'chebyshev' or 'chebyshev1', the points will be 2-D chebyshev points of the first kind.
- If set to 'chebyshev2', the points will be 2-D chebyshev points of the second kind.
- If set to 'latin_hypercube', the points will be generated using Latin Hypercube Sampling
Defaults to 'equally-spaced-noisy'.
:type method: str, optional
Expand Down Expand Up @@ -255,9 +276,29 @@ def __init__(self, grid=(10, 10), xy_min=(0.0, 0.0), xy_max=(1.0, 1.0), method='
grid_x, grid_y = torch.meshgrid(x, y, indexing='ij')
self.grid_x, self.grid_y = grid_x.flatten(), grid_y.flatten()
self.getter = lambda: (self.grid_x, self.grid_y)
elif method == 'chebyshev2-noisy':
self.getter = self.generate(xy_min, xy_max, grid, method='chebyshev2-noisy')
elif method == 'latin-hypercube':
x = _latin_hypercube(xy_min[0], xy_max[0], grid[0])
y = _latin_hypercube(xy_min[1], xy_max[1], grid[1])
grid_x, grid_y = torch.meshgrid(x, y, indexing='ij')
self.grid_x, self.grid_y = grid_x.flatten(), grid_y.flatten()
self.getter = lambda: (self.grid_x, self.grid_y)
else:
raise ValueError(f'Unknown method: {method}')

def generate(self, xy_min, xy_max, grid, method='chebyshev2-noisy'):
if method == 'chebyshev2-noisy':
x = _chebyshev_second_noisy(xy_min[0], xy_max[0], grid[0])
y = _chebyshev_second_noisy(xy_min[1], xy_max[1], grid[1])
grid_x, grid_y = torch.meshgrid(x, y, indexing='ij')
return (grid_x.flatten(), grid_y.flatten())
elif method == 'latin-hypercube':
x = _latin_hypercube(xy_min[0], xy_max[0], grid[0])
y = _latin_hypercube(xy_min[1], xy_max[1], grid[1])
grid_x, grid_y = torch.meshgrid(x, y, indexing='ij')
return (grid_x.flatten(), grid_y.flatten())

def get_examples(self):
return self.getter()

Expand Down Expand Up @@ -300,6 +341,7 @@ class Generator3D(BaseGenerator):
- If set to 'equally-spaced-noisy', a normal noise will be added to the previously mentioned set of points.
- If set to 'chebyshev' or 'chebyshev1', the points will be 3-D chebyshev points of the first kind.
- If set to 'chebyshev2', the points will be 3-D chebyshev points of the second kind.
- If set to 'latin_hypercube', the points will be generated using Latin Hypercube Sampling
Defaults to 'equally-spaced-noisy'.
:type method: str, optional
Expand Down Expand Up @@ -333,13 +375,17 @@ def __init__(self, grid=(10, 10, 10), xyz_min=(0.0, 0.0, 0.0), xyz_max=(1.0, 1.0
x = _chebyshev_second(xyz_min[0], xyz_max[0], grid[0])
y = _chebyshev_second(xyz_min[1], xyz_max[1], grid[1])
z = _chebyshev_second(xyz_min[2], xyz_max[2], grid[2])
elif method == 'latin-hypercube':
x = _latin_hypercube(xyz_min[0], xyz_max[0], grid[0])
y = _latin_hypercube(xyz_min[1], xyz_max[1], grid[1])
z = _latin_hypercube(xyz_min[2], xyz_max[2], grid[2])
else:
raise ValueError(f"Unknown method: {method}")

grid_x, grid_y, grid_z = torch.meshgrid(x, y, z, indexing='ij')
self.grid_x, self.grid_y, self.grid_z = grid_x.flatten(), grid_y.flatten(), grid_z.flatten()

if method in ['equally-spaced', 'chebyshev', 'chebyshev1', 'chebyshev2']:
if method in ['equally-spaced', 'chebyshev', 'chebyshev1', 'chebyshev2', 'latin-hypercube']:
self.getter = lambda: (self.grid_x, self.grid_y, self.grid_z)
elif method == 'equally-spaced-noisy':
self.noise_xmean = torch.zeros(self.size)
Expand Down

0 comments on commit fb91d55

Please sign in to comment.