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

Regular grid interpolation #244

Merged
merged 25 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b01db55
added tests for regular grid interpolation
flabowski Jun 16, 2023
b3d3001
added regular grid interpolation module
flabowski Jun 16, 2023
fe25304
added some tests
flabowski Jun 17, 2023
880962b
wrapper for scipy's regular grid interpolation
flabowski Jun 17, 2023
ce6b8e4
removed some comments
flabowski Jun 17, 2023
8df7469
minor code style improvements
flabowski Jun 17, 2023
6977a9e
adjusted docsting
flabowski Jun 18, 2023
24bee31
added pre-processing to map points and values onto regular grid in 2D
flabowski Jun 19, 2023
b5835b8
modified pre-processing to map points and values onto regular grid in nD
flabowski Jun 19, 2023
a9deaa0
modified the regular grid interpolation so it works with the grid poi…
flabowski Jun 19, 2023
98d1425
removed useless fill_value argument
flabowski Jun 19, 2023
761fcde
added a doc file for the automatic doc generation of the new module
flabowski Jun 26, 2023
9326752
improved docstring of get_grid_axes
flabowski Jun 26, 2023
c2692fb
moved ValueError to the right place and adjusted the error message.
flabowski Jun 26, 2023
3f79063
added docstring for predict method
flabowski Jun 26, 2023
eb7f1a3
removed 'dim' variable
flabowski Jun 26, 2023
706bae7
removed calculate_flat_index(...) entirely
flabowski Jun 26, 2023
4148b4f
removed TODO comment
flabowski Jun 26, 2023
4a77030
other cosmetic changes
flabowski Jun 26, 2023
638af8d
some simplifications, no need to operate with the transposed of the v…
flabowski Jun 26, 2023
e49ad18
reverted suggested change, as it changed the behaviour
flabowski Jun 26, 2023
3f77b6a
improved code style
flabowski Jun 27, 2023
48f0c42
updated autogenerated rst and added it to the code documentation
flabowski Jun 27, 2023
1d8b690
added test to make sure exception is raised.
flabowski Jun 28, 2023
2ad23ae
removed 'def' member manually
flabowski Jun 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Code Documentation
ae
podae
reducedordermodel
regular_grid
23 changes: 23 additions & 0 deletions docs/source/regular_grid.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
RegularGrid
=====================

.. currentmodule:: ezyrb.regular_grid

.. automodule:: ezyrb.regular_grid

.. autosummary::
:toctree: _summaries
:nosignatures:

RegularGrid
RegularGrid.def
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not needed. Or at least we don't use it in the other files. I am not sure about the generated outcome...

Copy link
Contributor Author

@flabowski flabowski Jun 28, 2023

Choose a reason for hiding this comment

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

I am not sure either. The file was generated automatically when ran make_rst.sh. Some other things seem to be fishy.

edit: everything seems fine after reverting what make_rst.sh did. @ndem0 maybe some commands need to be updated?

Copy link
Member

Choose a reason for hiding this comment

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

We are not using that script anymore! Sorry, it's not written, I gonna remove it in the next release of EZyRB (#232).
The file now looks correct, thanks!

RegularGrid.get_grid_axes
RegularGrid.fit
RegularGrid.predict

.. autoclass:: RegularGrid
:members:
:private-members:
:undoc-members:
:show-inheritance:
:noindex:
3 changes: 2 additions & 1 deletion ezyrb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
__all__ = [
'Database', 'Reduction', 'POD', 'Approximation', 'RBF', 'Linear', 'GPR',
'ANN', 'KNeighborsRegressor', 'RadiusNeighborsRegressor', 'AE',
'ReducedOrderModel', 'PODAE'
'ReducedOrderModel', 'PODAE', 'RegularGrid'
]

from .meta import *
Expand All @@ -15,6 +15,7 @@
from .approximation import Approximation
from .rbf import RBF
from .linear import Linear
from .regular_grid import RegularGrid
from .gpr import GPR
from .reducedordermodel import ReducedOrderModel
from .ann import ANN
Expand Down
117 changes: 117 additions & 0 deletions ezyrb/regular_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""
Module for higher order interpolation on regular grids
"""
import numpy as np
from scipy.interpolate import RegularGridInterpolator as RGI
from .approximation import Approximation


class RegularGrid(Approximation):
"""
Multidimensional interpolator on regular grids.
See also scipy's RegularGridInterpolator for information on kwargs.

:param array_like points: the coordinates of the points on a regular grid.
:param array_like values: The (vector-) data on the regular grid in
n dimensions.

:Example:

>>> import ezyrb
>>> import numpy as np
>>> def f(x, y, z):
... return 2 * x**3 + 3 * y**2 - z
>>> x = np.linspace(1, 4, 11)
>>> y = np.linspace(4, 7, 22)
>>> z = np.linspace(7, 9, 33)
>>> xg, yg, zg = np.meshgrid(x, y, z, indexing='ij')
>>> points = np.c_[xg.ravel(), yg.ravel(), zg.ravel()]
>>> data_mode_x = f(xg, yg, zg).reshape(-1, 1)
# lets assume we have 2 modes, i.e. a rank 2 model
>>> data = np.concatenate((data_mode_x, data_mode_x/10), axis=1)
>>> rgi = ezyrb.RegularGrid()
>>> rgi.fit(points, data, method="linear")
>>> pts = np.array([[2.1, 6.2, 8.3],
... [3.3, 5.2, 7.1],
... [1., 4., 7.],
... [4., 7., 9.]])
>>> rgi.predict(pts)
array([[125.80469388, 12.58046939],
[146.30069388, 14.63006939],
[ 43. , 4.3 ],
[266. , 26.6 ]])
>>> f(pts[:, 0], pts[:, 1], pts[:, 2])
array([125.542, 145.894, 43. , 266. ])
>>> f(pts[:, 0], pts[:, 1], pts[:, 2])/10
array([12.5542, 14.5894, 4.3 , 26.6 ])
"""

def __init__(self):
self.interpolator = None

def get_grid_axes(self, pts_scrmbld, vals_scrmbld):
"""
Calculates the grid axes from a meshed grid and re-orders the values so
they fit on a mesh generated with
numpy.meshgrid(ax1, ax2, ..., axn, indexing="ij")

flabowski marked this conversation as resolved.
Show resolved Hide resolved
:param array_like pts_scrmbld: the coordinates of the points.
:param array_like vals_scrmbld: the (vector-)values in the points.
:return: The grid axes given as a tuple of ndarray, with shapes
(m1, ), …, (mn, ) and values mapped on the ordered grid.
:rtype: (list, numpy.ndarray)

"""
grid_axes = []
iN = [] # index in dimension N
nN = [] # size of dimension N
dim = pts_scrmbld.shape[1]
for i in range(dim):
xn, unique_inverse_n = np.unique(pts_scrmbld[:, i],
return_inverse=True)
grid_axes.append(xn)
nN.append(len(xn))
iN.append(unique_inverse_n)
if np.prod(nN) != len(vals_scrmbld):
msg = "Values don't match grid. Make sure to pass a list of "\
"points that are on a regular grid! Be aware of floating "\
"point precision."
raise ValueError(msg)
new_row_index = np.ravel_multi_index(iN, nN)
reverse_scrambling = np.argsort(new_row_index)
vals_on_regular_grid = vals_scrmbld[reverse_scrambling]
return grid_axes, vals_on_regular_grid

def fit(self, points, values, **kwargs):
"""
Construct the interpolator given `points` and `values`.
Assumes that the points are on a regular grid, fails when not.
see scipy.interpolate.RegularGridInterpolator

:param array_like points: the coordinates of the points.
:param array_like values: the values in the points.
"""
points = np.asarray(points)
if not np.issubdtype(points.dtype, np.number):
raise ValueError('Invalid format or dimension for the argument'
'`points`.')
if points.ndim == 1:
points = points[:, None]
vals = np.asarray(values)
grid_axes, values_grd = self.get_grid_axes(points, vals)
shape = [len(ax) for ax in grid_axes]
shape.append(-1)
self.interpolator = RGI(grid_axes, values_grd.reshape(shape), **kwargs)

def predict(self, new_point):
flabowski marked this conversation as resolved.
Show resolved Hide resolved
"""
Evaluate interpolator at given `new_point`, can be multiple points.

:param array_like new_point: the coordinates of the given point(s).
:return: the interpolated values.
:rtype: numpy.ndarray
"""
new_point = np.asarray(new_point)
if new_point.ndim == 1:
new_point = new_point[:, None]
return self.interpolator(new_point)
95 changes: 95 additions & 0 deletions tests/test_regular_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import numpy as np
from unittest import TestCase
from ezyrb import RegularGrid, Database, POD, ReducedOrderModel


class TestRegularGrid(TestCase):

def test_1D_1mode(self):
reg = RegularGrid()
reg.fit(np.array([1, 2, 3])[:, None],
np.array([1, 5, 3])[:, None])
assert reg.predict([1]) == 1
assert reg.predict([2]) == 5
assert reg.predict([3]) == 3

def test_predict1d(self):
reg = RegularGrid()
x1 = np.array([[1], [6], [8]])
V = np.array([[1, 0], [20, 5], [8, 6]]) # n, r = 3, 2
reg.fit(x1, V)
result = reg.predict([[1], [8], [6]])
assert (result[0] == [1, 0]).all()
assert (result[1] == [8, 6]).all()
assert (result[2] == [20, 5]).all()

def test_predict1d_2(self):
reg = RegularGrid()
x1 = [1, 2]
reg.fit(x1, [[1, 1], [2, 2]])
result = reg.predict([[1.5]])
assert (result[0] == [[1.5, 1.5]]).all()

def test_predict2d(self):
reg = RegularGrid()
x1 = [1, 2, 3]
x2 = [4, 5, 6, 7]
xx1, xx2 = np.meshgrid(x1, x2, indexing="ij")
points = np.c_[xx1.ravel(), xx2.ravel()]
V = [[1, 21], [2, 22], [3, 23], [4, 24], [5, 25], [6, 26],
[7, 27], [8, 28], [9, 29], [10, 30], [11, 31], [12, 32]]
reg.fit(points, V, method="linear")
result = reg.predict([[1, 4], [1, 5]])
assert (result[0] == [1, 21]).all()
assert (result[1] == [2, 22]).all()

def test_get_grid_axes_2D(self):
x1 = [.1, .2, .3]
x2 = [4, 5, 6, 7]
xx1, xx2 = np.meshgrid(x1, x2, indexing="ij")
V = np.arange(len(x1)*len(x2)*2).reshape(-1, 2)
pts = np.c_[xx1.ravel(), xx2.ravel()]

random_order = np.arange(len(pts))
np.random.shuffle(random_order)
pts_scrmbld = pts[random_order, :]
V_scrmbld = V[random_order, :]

reg = RegularGrid()
grid_axes, V_unscrambled = reg.get_grid_axes(pts_scrmbld, V_scrmbld)

assert np.allclose(V_unscrambled, V)
assert np.allclose(grid_axes[0], x1)
assert np.allclose(grid_axes[1], x2)

def test_get_grid_axes_3D(self):
x1 = [.1, .2, .3]
x2 = [4, 5, 6, 7]
x3 = [.12, .34, .56, .78, .90]
xx1, xx2, xx3 = np.meshgrid(x1, x2, x3, indexing="ij")
V = np.arange(len(x1)*len(x2)*len(x3)*2).reshape(-1, 2)
pts = np.c_[xx1.ravel(), xx2.ravel(), xx3.ravel()]

random_order = np.arange(len(pts))
np.random.shuffle(random_order)
pts_scrmbld = pts[random_order, :]
V_scrmbld = V[random_order, :]

reg = RegularGrid()
grid_axes, V_unscrambled = reg.get_grid_axes(pts_scrmbld, V_scrmbld)

assert np.allclose(V_unscrambled, V)
assert np.allclose(grid_axes[0], x1)
assert np.allclose(grid_axes[1], x2)
assert np.allclose(grid_axes[2], x3)

def test_with_db_predict(self):
reg = RegularGrid()
pod = POD()
db = Database(np.array([1, 2, 3])[:, None],
np.array([1, 5, 3])[:, None])
rom = ReducedOrderModel(db, pod, reg)
rom.fit()
assert rom.predict([1]) == 1
assert rom.predict([2]) == 5
assert rom.predict([3]) == 3
flabowski marked this conversation as resolved.
Show resolved Hide resolved