-
Notifications
You must be signed in to change notification settings - Fork 229
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
dsl: Implementation of new interpolation methods #1932
Conversation
…cInterpolator class): - Implementation of _interpolation_indices method - Creation of relative_position_map, in sparse.py - Call to interpolation_indices method inside interpolation's callback
…ng 2D and 3D cubic interpolation
…zation with the sinc interpolator
…ng 2D and 3D sinc interpolation - Add the lookuptable.h file to the operator's default import list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Thanks a lot. I'll have a thorough walk-through.
So SincInterpolation
is not testable, right. It would be good to make it testable in some way. Also, there is a hardcoded path to lookuptable.h
that will break the rest of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok made a first pass. I think it's quite nice and provides some pretty cool features. I left some comments and I'm happy to discuss if not quite clear.
devito/types/sparse.py
Outdated
mathematically equivalent expanded form `coord/spacing - | ||
origin/spacing`. This particular form is problematic when a sparse | ||
point is in close proximity of the grid origin, since due to a larger | ||
machine precision error it may cause a +-1 error in the computation of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That +-1 used to be an error and we made changes to the API to make sure it would not happen anymore. Can you point us to a small MFE with the breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this was my mistake, I took the same comments from position_map function and forgot to update after the rebase. So there is no error in here
devito/types/sparse.py
Outdated
return tuple(product(range(-1, 3), repeat=self.grid.dim)) | ||
elif self.sinc: | ||
r = self.interpolator.r | ||
return tuple(product(range(-r + 1, r+1), repeat=self.grid.dim)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this case true for all r
? I.e for cubic you have r=2
which is range(-1, 3)
and for the linear case r=1
whci his range(0, 2)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right! range(-r + 1, r+1)
works for all r.
I'll change this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in the end we won't have this conditional, I hope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the PR with this fix.
Function, TimeFunction) | ||
|
||
|
||
def unit_box(name='a', shape=(11, 11), grid=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redefinition of existing fixture.
same below
((101, 101), [(3.4, 95.), (3., 92)]), | ||
((101, 101, 101), [(3.4, 95.), (3., 92), (10., 10.)]) | ||
]) | ||
def test_interpolate(shape, coords, npoints=20): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this all be gathered under a common test class?
Also I don't think it needs a new file, test_interpolation.py
exists for this
tests/test_interpolation_cubic.py
Outdated
# print(p.coordinates.data) | ||
op(a=a) | ||
|
||
# Precisão 3D nao bate em 10-6 mas sim em 10-5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is leftover Portugues throughout
devito/operations/interpolators.py
Outdated
|
||
idx_subs = [] | ||
for i, idx in enumerate(index_matrix): | ||
# Introduce ConditionalDimension so that we don't go OOB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only be necessary if r > variables.space_order
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if r <= variables.space_order
there is no need to use ConditionalDimension in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I adjust this part of the code and updated here.
You'll see that I added a conditionalif hasattr(x, 'space_order') else False
. I needed to do this because in some cases variables
does not have the space_order
attribute
lhs = self.sfunction.subs(self_subs) | ||
last = [Inc(lhs, rhs)] if increment else [Eq(lhs, rhs)] | ||
|
||
# Creates the symbolic equation that calls the populate function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To implement the sinc interpolation I used a look up table that holds the values of the coefficients. Before it could be accessed, it was necessary to initialize this table with the values. This symbolic Function populate()
creates a call to the function responsible for initialize the look up table.
This function is defined inside look up table.h, that I apparently forgot to add here. I'm attaching the lookuptable.h in this comment, this way you can see how it works
devito/operations/interpolators.py
Outdated
dim_pos = self.sfunction.grid.dimensions | ||
|
||
# Checks if the data is 3D or 2D | ||
if len(dim_pos) == 3: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be setup at __init_
with self._eqs = self.bicubic if self.sfunction.grid.dim = 2 else ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...also lets try to setup as much at init as possible?
devito/operations/interpolators.py
Outdated
idx_subs, | ||
dim_pos=dim_pos)] | ||
else: | ||
eqs = [np.sum(v) for v in self._bicubic_equations(_expr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why doesn't ..._equations
directly return the sum?
Also why np.sum
? Isn't it still partially symbolic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not returning directly the sum, the return of ..._equations
can be used for both injection and interpolation. In interpolation it sum the values and in injection just assign the values for its respective grid point.
About the use of numpy: Yeah, it is still a symbolic. I wanted to generate a symbolic equation that sum all the individual values, that's why i used np.sum
. Is there some problem in using it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok makes sense for sum. But should not use numpy as it is meant for numerical values. Should use Devito.Add(*v)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this actually work? Numpy summing over sympy symbols?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it works summing over sympy symbols. But I will take a look at Devito.Add(*v) to use it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the code and it is using Add(*v) now
devito/operations/interpolators.py
Outdated
field_offset=field_offset) | ||
|
||
rhs = Symbol(name='sum', dtype=self.sfunction.dtype) | ||
summands = [Eq(rhs, 0., implicit_dims=self.sfunction.dimensions)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
everything till here is the same as in LinearInterpolation
right? and after the bicubic_equation
too. So may be nice to gather those under a PolynomialInterpolation
with order=1
or order=3
that specialize on the equations only since it's the only thing that changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a PolynomialInterpolator class that works as a parent class. It's not exactly what you just said, but I think it solves the code reuse problem
class CubicInterpolator(GenericInterpolator): | ||
""" | ||
Concrete implementation of GenericInterpolator implementing a cubic interpolation | ||
scheme. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blank line
scheme. | ||
Parameters | ||
---------- | ||
sfunction: The SparseFunction that this Interpolator operates on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not legal numpydoc format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed this is not legal, but I guess it is our "fault" that we have not numpy-doc-ized this file.... i.e. look here:
https://github.com/devitocodes/devito/pull/1932/files/c068956a657999c10d1d977347f0f52f40132f1f#diff-2c4e29d657bce68c3dc96bb61bed3d1038b983ab72845761bf2ead45e30e222aR25
|
||
return eqs | ||
|
||
def _interpolation_indices(self, variables, offset=0, field_offset=0): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't there anything we could factorize in a common superclass?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't there anything we could factorize in a common superclass?
Yes, I think it has. I'm working on it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the code by adding the superclass you suggested
devito/operations/interpolators.py
Outdated
|
||
def __init__(self, sfunction): | ||
self.sfunction = sfunction | ||
self.r = 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be a class property
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it a problem to let the user choose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it a problem to let the user choose?
The Bessel function was implemented inside the .h file considering r = 4
. So it would be a problem to allow the user to choose the value of r
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this contribution. Looking great! I just left a bunch of comments
@@ -0,0 +1,269 @@ | |||
import numpy as np |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO this file should be squashed with test_interpolation
, and as much code and existing tests as possible should be reused, through pytest.parametrize
for example
tests/test_interpolation_cubic.py
Outdated
((101, 101, 101), [(3.4, 95.), (3., 92), (10., 10.)]) | ||
]) | ||
def test_interpolate(shape, coords, npoints=20): | ||
"""Test generic point interpolation testing the x-coordinate of an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opening triple quotes want to be on line above for consistency with the rest of the codebase, i.e.
"""
Test generic...
"""
devito/operations/interpolators.py
Outdated
---------- | ||
expr : expr-like | ||
Input expression to interpolate. | ||
position: List |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be list
(all lowercase)
devito/operations/interpolators.py
Outdated
for ii in range(4)] | ||
return eqs | ||
|
||
def _tricubic_equations(self, expr, idx_subs, dim_pos=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this (and _bicubic_equations
) be combined into some _ncubic_equations
for N-dimensional interpolation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it could. I changed the code and made this change you suggested
devito/operations/interpolators.py
Outdated
idx_subs: dict | ||
Structure responsible for mapping the order of substitution of dimensions | ||
in expr. | ||
idx_2d: Integer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be int
I think?
devito/operations/interpolators.py
Outdated
|
||
return eqs | ||
|
||
def _sinc_equations3D(self, expr, idx_subs, dim_pos=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, can these be consolidated into _sinc_equationsND()
? Seems like a decent amount of lines are the same and would be nice to have a generalization to N-D
@@ -0,0 +1,269 @@ | |||
import numpy as np |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be worth having a test to check the convergence of the cubic interpolator? Not sure what convergence would be expected for the sinc one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this seem like a decent idea @mloubout ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't as I pointed out above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm just being stupid and missing it, but where?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry completely missed what you were talking about only saw the numpy
.
Convergence may be tricky, it's usually easier to just do an analytical check where you set the field to polynomial values and check the interpolation is exact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some minor comments.
Also have two higher-level questions:
- Is
SincInterpolator
the best name for what this is? (Sinc is only a part of the interpolation function) - How does this approach compare to using the machinery provided in
PrecomputedInterpolator
, i.e. the Sinc class could inherit from that, precompute the coefficients (which I think you're doing in some way anyway), and hand them off toPrecomputedInterpolator
for the rest?
devito/operations/interpolators.py
Outdated
option = 1, ponto imediatamente vizinho ao ponto de intepolação. | ||
Onde 0 < |arg| < 1 | ||
option = 2, pontos extremos da vizinhança. | ||
Onde 1 < |arg| < 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am I seeing an old version? We probably want our documentation in English :)
devito/operations/interpolators.py
Outdated
dim_pos = self.sfunction.grid.dimensions | ||
|
||
# Checks if the data is 3D or 2D | ||
if len(dim_pos) == 3: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...also lets try to setup as much at init as possible?
devito/operations/interpolators.py
Outdated
idx_subs, | ||
dim_pos=dim_pos)] | ||
else: | ||
eqs = [np.sum(v) for v in self._bicubic_equations(_expr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this actually work? Numpy summing over sympy symbols?
devito/operations/interpolators.py
Outdated
eqs = [v for v in self._tricubic_equations(_expr, | ||
idx_subs, | ||
dim_pos=dim_pos)] | ||
else: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this with inheritance? Or in a way that generalises beyond 2/3 dimensions?
devito/operations/interpolators.py
Outdated
|
||
def __init__(self, sfunction): | ||
self.sfunction = sfunction | ||
self.r = 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it a problem to let the user choose?
devito/operator/operator.py
Outdated
@@ -133,7 +134,8 @@ class Operator(Callable): | |||
""" | |||
|
|||
_default_headers = [('_POSIX_C_SOURCE', '200809L')] | |||
_default_includes = ['stdlib.h', 'math.h', 'sys/time.h'] | |||
_default_includes = ['stdlib.h', 'math.h', 'sys/time.h', | |||
f'{os.path.expanduser("~")}/lookuptable.h'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need help understanding what this header file is doing. Also, is this a static header file that should become a standard part of Devito? Alternately, can this be generated using the JIT infra?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To implement the sinc interpolation I used a look up table that holds the values of the coefficients. So this header contains the look up table and the methods responsible for initialize and get the values from it.
Access to these functions is defined from the definition of symbolic functions during the generation of symbolic equations.
I updated the PR adding the lookuptable.h file, this way you can take a look if you want to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About using JIT to generate the code, I don't know how it could be using JIT but I will take a look.
And yes, the static header file should become a standard part of Devito.
devito/types/sparse.py
Outdated
return tuple(product(range(-1, 3), repeat=self.grid.dim)) | ||
elif self.sinc: | ||
r = self.interpolator.r | ||
return tuple(product(range(-r + 1, r+1), repeat=self.grid.dim)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in the end we won't have this conditional, I hope?
…ay the Interpolator initialization can use it
…r, so it was added a conditional checking this condition
The |
Also, I want to understand if this is significantly different from using PrecomputedInterpolator (i.e. this class could extend and use the functionality there) |
@mloubout Before the decision of create this '.h' file it was made a search for some library that could already has the implementation of the Bessel function that we needed (the zero-order modified Bessel function of the first kind). I found some implementations of the bessel functions, but none of the zero-order modified Bessel function of the first kind. |
@navjotk At the same way that linear interpolation, both cubic and sinc interpolation also can be done by using PrecomputedInterpolator, if the right coefficients are used. So I can say that the main difference is that this new classes removes the responsibility of obtaining the interpolation coefficients from the user. |
@guaacoelho But LinearInterpolator does not do any pre computation. If this class is doing pre computation of coefficients, it could store them in the properties provided by |
…e between the interpolators classes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this contribution!
devito/operations/interpolators.py
Outdated
@@ -1,5 +1,6 @@ | |||
from abc import ABC, abstractmethod | |||
|
|||
# Polly interpolator é a que contem as duas interpolações |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Translate and avoid comment in this area
|
||
def interpolate(self, expr, offset=0, increment=False, self_subs={}): | ||
""" | ||
Generate equations interpolating an arbitrary expression into ``self``. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add line back
variables = list(retrieve_function_carriers(_expr)) | ||
|
||
# Need to get origin of the field in case it is staggered | ||
# TODO: handle each variable staggereing spearately |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
@@ -133,7 +134,8 @@ class Operator(Callable): | |||
""" | |||
|
|||
_default_headers = [('_POSIX_C_SOURCE', '200809L')] | |||
_default_includes = ['stdlib.h', 'math.h', 'sys/time.h'] | |||
_default_includes = ['stdlib.h', 'math.h', 'sys/time.h', | |||
f'{os.path.expanduser("~/devito")}/lookuptable.h'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ι think this .h is not something we encourage
FYI sparse interpolation is beeing revamp in #2128 which should help updae this PR. |
sinc interpolation added in #2342 , closing. Feel free to open a new PR for cubic with the new API. |
Hi Guys @FabioLuporini @mloubout @navjotk @georgebisbas ,
I've been working with @nogueirapeterson to include new interpolation techniques on Devito. We implement the cubic and sinc interpolation methods. Thus, we create two new classes: CubicInterpolator and SincInterpolator, which contains, respectively, the cubic and sinc interpolation methods.
The cubic interpolator was implemented using the cubic convolution technique (10.1109/TASSP.1981.1163711) and the sinc interpolator was implemented using Kaiser windowed sinc functions (https://doi.org/10.1190/1.1451454).
We hope you appreciate this contribution and help us to improve this implementation with your great review!