From c48d38b4289c862a4d2adb85adc6e16011567ac0 Mon Sep 17 00:00:00 2001 From: Samuel Date: Fri, 12 Jan 2024 17:28:50 -0500 Subject: [PATCH 1/4] chebyshev_uniform for Generator1D --- neurodiffeq/generators.py | 18 +++++++++++++++--- tests/test_generators.py | 4 ++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/neurodiffeq/generators.py b/neurodiffeq/generators.py index cdad4e8..158fea7 100644 --- a/neurodiffeq/generators.py +++ b/neurodiffeq/generators.py @@ -4,6 +4,12 @@ import numpy as np from typing import List +def _chebyshev_uniform(a, b, n): + unif_sample = torch.linspace(0, np.pi, n) + nodes = torch.cos(unif_sample) + nodes = ((a + b) + (b - a) * nodes) / 2 + nodes.requires_grad_(True) + return nodes def _chebyshev_first(a, b, n): nodes = torch.cos(((torch.arange(n) + 0.5) / n) * np.pi) @@ -11,14 +17,12 @@ def _chebyshev_first(a, b, n): nodes.requires_grad_(True) return nodes - def _chebyshev_second(a, b, n): nodes = torch.cos(torch.arange(n) / float(n - 1) * np.pi) nodes = ((a + b) + (b - a) * nodes) / 2 nodes.requires_grad_(True) return nodes - def _compute_log_negative(t_min, t_max, whence): if t_min <= 0 or t_max <= 0: suggested_t_min = 10 ** t_min @@ -108,6 +112,7 @@ class Generator1D(BaseGenerator): - If set to 'equally-spaced-noisy', a normal noise will be added to the previously mentioned set of points. - If set to 'log-spaced', the points will be fixed to a set of log-spaced points that go from t_min to t_max. - If set to 'log-spaced-noisy', a normal noise will be added to the previously mentioned set of points, + - If set to 'chebyshev_uniform', the points are uniformly sampled between 0 and π, transformed into chebyshev nodes of the first kind, and mapped to (t_min, t_max). - 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]. @@ -148,6 +153,9 @@ def __init__(self, size, t_min=0.0, t_max=1.0, method='uniform', noise_std=None) start, end = _compute_log_negative(t_min, t_max, self.__class__) self.examples = torch.logspace(start, end, self.size, requires_grad=True) self.getter = lambda: torch.normal(mean=self.examples, std=self.noise_std) + elif method in ['chebyshev_uniform']: + self.examples = _chebyshev_uniform(t_min, t_max, size) + self.getter = lambda: self.examples elif method in ['chebyshev', 'chebyshev1']: self.examples = _chebyshev_first(t_min, t_max, size) self.getter = lambda: self.examples @@ -333,13 +341,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 == 'chebyshev_uniform': + x = _chebyshev_uniform(xyz_min[0], xyz_max[0], grid[0]) + y = _chebyshev_uniform(xyz_min[1], xyz_max[1], grid[1]) + z = _chebyshev_uniform(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', 'chebyshev_uniform']: self.getter = lambda: (self.grid_x, self.grid_y, self.grid_z) elif method == 'equally-spaced-noisy': self.noise_xmean = torch.zeros(self.size) diff --git a/tests/test_generators.py b/tests/test_generators.py index abdb7ed..ba98667 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -120,6 +120,10 @@ def test_generator1d(): x = generator.getter() assert _check_shape_and_grad(generator, size, x) + generator = Generator1D(size=size, t_min=0.1, t_max=2.0, method='chebyshev_uniform') + x = generator.getter() + assert _check_shape_and_grad(generator, size, x) + generator = Generator1D(size=size, t_min=0.1, t_max=2.0, method='chebyshev') x = generator.getter() assert _check_shape_and_grad(generator, size, x) From 7ace845e1f2d572841add6f55f476ec24abac40d Mon Sep 17 00:00:00 2001 From: Samuel Date: Fri, 12 Jan 2024 17:33:17 -0500 Subject: [PATCH 2/4] Deleting chebyshev_uniform from other generators --- neurodiffeq/generators.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/neurodiffeq/generators.py b/neurodiffeq/generators.py index 158fea7..00f56d9 100644 --- a/neurodiffeq/generators.py +++ b/neurodiffeq/generators.py @@ -341,17 +341,13 @@ 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 == 'chebyshev_uniform': - x = _chebyshev_uniform(xyz_min[0], xyz_max[0], grid[0]) - y = _chebyshev_uniform(xyz_min[1], xyz_max[1], grid[1]) - z = _chebyshev_uniform(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', 'chebyshev_uniform']: + if method in ['equally-spaced', 'chebyshev', 'chebyshev1', 'chebyshev2']: self.getter = lambda: (self.grid_x, self.grid_y, self.grid_z) elif method == 'equally-spaced-noisy': self.noise_xmean = torch.zeros(self.size) From 121bc452906d39aa926d054b3910bd799da3e014 Mon Sep 17 00:00:00 2001 From: Samuel Date: Fri, 12 Jan 2024 17:47:34 -0500 Subject: [PATCH 3/4] Changing chebyshev_uniform to start by randomly sampling from a uniform distribution. --- neurodiffeq/generators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neurodiffeq/generators.py b/neurodiffeq/generators.py index 00f56d9..610be6c 100644 --- a/neurodiffeq/generators.py +++ b/neurodiffeq/generators.py @@ -5,7 +5,7 @@ from typing import List def _chebyshev_uniform(a, b, n): - unif_sample = torch.linspace(0, np.pi, n) + unif_sample = torch.rand(n) * np.pi nodes = torch.cos(unif_sample) nodes = ((a + b) + (b - a) * nodes) / 2 nodes.requires_grad_(True) @@ -112,7 +112,7 @@ class Generator1D(BaseGenerator): - If set to 'equally-spaced-noisy', a normal noise will be added to the previously mentioned set of points. - If set to 'log-spaced', the points will be fixed to a set of log-spaced points that go from t_min to t_max. - If set to 'log-spaced-noisy', a normal noise will be added to the previously mentioned set of points, - - If set to 'chebyshev_uniform', the points are uniformly sampled between 0 and π, transformed into chebyshev nodes of the first kind, and mapped to (t_min, t_max). + - If set to 'chebyshev_uniform', the points are randomly sampled from an uniform distribution between 0 and π, transformed into chebyshev nodes of the first kind, and mapped to (t_min, t_max). - 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]. From 7e1ab5f3d3782daab3dea1e7803de0e639d32981 Mon Sep 17 00:00:00 2001 From: Samuel Date: Sat, 13 Jan 2024 18:58:29 -0500 Subject: [PATCH 4/4] Adding chebyshev_noisy --- neurodiffeq/generators.py | 13 +++++++++++++ tests/test_generators.py | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/neurodiffeq/generators.py b/neurodiffeq/generators.py index 610be6c..2449380 100644 --- a/neurodiffeq/generators.py +++ b/neurodiffeq/generators.py @@ -4,6 +4,14 @@ import numpy as np from typing import List +def _chebyshev_noisy(a, b, n, std): + sample = torch.linspace(0, np.pi, n) + sample_noisy = sample + torch.normal(mean=0, std=std) + nodes = torch.cos(sample_noisy) + nodes = ((a + b) + (b - a) * nodes) / 2 + nodes.requires_grad_(True) + return nodes + def _chebyshev_uniform(a, b, n): unif_sample = torch.rand(n) * np.pi nodes = torch.cos(unif_sample) @@ -115,6 +123,8 @@ class Generator1D(BaseGenerator): - If set to 'chebyshev_uniform', the points are randomly sampled from an uniform distribution between 0 and π, transformed into chebyshev nodes of the first kind, and mapped to (t_min, t_max). - 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 'chebyshev_noisy', a normal noise will be added to chebyshev nodes of the first kind over (t_min, t_max). + defaults to 'uniform'. :type method: str, optional @@ -162,6 +172,9 @@ 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 == 'chebyshev_noisy': + self.examples = _chebyshev_noisy(t_min, t_max, size, self.noise_std) + self.getter = lambda: self.examples else: raise ValueError(f'Unknown method: {method}') diff --git a/tests/test_generators.py b/tests/test_generators.py index ba98667..21d1df5 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -136,6 +136,11 @@ def test_generator1d(): x = generator.getter() assert _check_shape_and_grad(generator, size, x) + generator = Generator1D(size=size, t_min=0.1, t_max=2.0, method='chebyshev_noisy', + noise_std=0.01) + x = generator.getter() + assert _check_shape_and_grad(generator, size, x) + with raises(ValueError): generator = Generator1D(size=size, t_min=0.0, t_max=2.0, method='magic')