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

api: revamp custom coefficients API #2434

Merged
merged 9 commits into from
Sep 24, 2024
Merged

api: revamp custom coefficients API #2434

merged 9 commits into from
Sep 24, 2024

Conversation

mloubout
Copy link
Contributor

@mloubout mloubout commented Jul 31, 2024

Superseeds #1644

Currently implemented with backward compatibility, we can drop it at some point.

Left to do for future iterations:

  • weights for cross-derivs .dxdy
  • weights with indice, i.e weights=(indices,weights) for fully custom stencils
  • ?

@mloubout mloubout added API api (symbolics, types, ...) feature-request labels Jul 31, 2024
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

Copy link

codecov bot commented Jul 31, 2024

Codecov Report

Attention: Patch coverage is 98.67550% with 2 lines in your changes missing coverage. Please review.

Project coverage is 87.00%. Comparing base (cec0542) to head (77e0021).
Report is 10 commits behind head on master.

Files with missing lines Patch % Lines
devito/finite_differences/tools.py 89.47% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2434      +/-   ##
==========================================
- Coverage   87.06%   87.00%   -0.07%     
==========================================
  Files         238      239       +1     
  Lines       45171    44947     -224     
  Branches     8417     8388      -29     
==========================================
- Hits        39326    39104     -222     
+ Misses       5112     5111       -1     
+ Partials      733      732       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@mloubout mloubout force-pushed the coeffs-revamp branch 2 times, most recently from c7ae7c4 to bc057cd Compare July 31, 2024 16:53
Copy link
Contributor

@FabioLuporini FabioLuporini left a comment

Choose a reason for hiding this comment

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

This is great, thanks!

Couple of questions, mostly related to the aesthetics of the API

  • do we now risk a proliferation of f.d(weights=weights, ...) in the PDE specification? things might get a little noisy
    • Is it useless or perhaps difficult to provide, in addition, an API such that if you do obj = Space(f, w, ...) then obj.dx == f.dx(weights=w) ?
  • I think, now that we're revamping the API, we need to add an alias such that f.dx(weights=w0) == f.dx(w=w0) , again to minimize verbosity
  • EvalDerivatives and IndexDerivatives are all constructed automatically and seamlessly regardless of whether taylor or custom coefficients are use, right?

@mloubout
Copy link
Contributor Author

mloubout commented Aug 1, 2024

do we now risk a proliferation of f.d(weights=weights, ...) in the PDE specification?

Well yes, and no. One thing that will be added to tuto doc is that you can provide your own backend for FD through fd_weights_registry so if you gonna have your own coefficients everywhere you can create your own my_custom_weight and then add it to fd_weights_registry and then use it.

Not sure about adding additional wrapper object for this i don't think it's make it much cleaner.

we need to add an alias such that f.dx(weights=w0) == f.dx(w=w0)

Completely fine yes, will add it

EvalDerivatives and IndexDerivative

Yes, It actually makes everything quite simpler for the compiler, everything now goes through standard FD and creates EvalDerivatives and IndexDerivative. There is no more intricated post-process replacement pass after evaluation.

Copy link
Contributor

@EdCaunt EdCaunt left a comment

Choose a reason for hiding this comment

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

Looks like a pretty big cleanup, much needed.

There should be a test to make sure that changing the weights of a Derivative changes the hash, and that two Derivatives with identical weights + spec hash the same. Also tests for pickling and unpickling derivatives with custom coefficients.

devito/finite_differences/derivative.py Show resolved Hide resolved
devito/finite_differences/tools.py Show resolved Hide resolved
@classmethod
def _apply_coeffs(cls, expr, coefficients):
"""
This process legacy API of Substitution/Coefficients applying the weights
Copy link
Contributor

Choose a reason for hiding this comment

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

"Processes"

devito/types/equation.py Show resolved Hide resolved
@EdCaunt
Copy link
Contributor

EdCaunt commented Aug 2, 2024

do we now risk a proliferation of f.d(weights=weights, ...) in the PDE specification?

The user can also do something like:

subs = {f.dx: f.dx(weights=weights0),
        f.dy: f.dy(weights=weights0),
        f.dx2: f.dx2(weights=weights1)}
eq0 = Eq(f, f.dx + f.dx2)
eq1 = Eq(f, f.dx + f.dy +1)
eqs = [eq.subs(subs) for eq in (eq0, eq1)]

We should probably have a test for this

@EdCaunt
Copy link
Contributor

EdCaunt commented Aug 2, 2024

We should also check that:

solve(f.dx(weights=weights), f.forward)

works correctly

Copy link
Contributor

Choose a reason for hiding this comment

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

Do the big figure changes need to be committed?

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you avoid committing them somehow?

Copy link
Contributor

Choose a reason for hiding this comment

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

is the image indeed different?
If not, one thing I do is 'git add -p'
https://nuclearsquid.com/writings/git-add/

@mloubout
Copy link
Contributor Author

mloubout commented Aug 7, 2024

eqs = [eq.subs(subs) for eq in (eq0, eq1)]

I would prefer to avoid adding these type of constructions that are not really good or clean

@EdCaunt
Copy link
Contributor

EdCaunt commented Aug 12, 2024

Notebook needs the text reworking. It still references the old API throughout.

# Populate the Array (the "map" part)
processed.append(e.func(a.indexify(), rhs, operation=None))

# Set all untouched entried to the identity value if necessary
Copy link
Contributor

Choose a reason for hiding this comment

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

entries

devito/builtins/initializers.py Outdated Show resolved Hide resolved
devito/builtins/initializers.py Outdated Show resolved Hide resolved
shape = weights.shape
return shape[weights.dimensions.index(wdim)], wdim
else:
return len(list(weights)), None
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do you need the list() here?

Copy link
Contributor

Choose a reason for hiding this comment

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

perhaps weights could be a generator?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can also be Weights which behave weirdly with len

devito/ir/equations/equation.py Outdated Show resolved Hide resolved
devito/types/equation.py Show resolved Hide resolved
kwargs['evaluate'] = False
# Backward compat
Copy link
Contributor

Choose a reason for hiding this comment

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

"compatibility"

raise ValueError("Number of FD weights provided does not "
"match the functions space_order")

warn("The Coefficient API is deprecated and will be removed, coefficients should"
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 creating one warning per Coefficient, another option is to register a callback to call at atexit(), or more simply use a global file-level flag to emit the warning only once ?

def __init__(self, *args):

warn("The Coefficient API is deprecated and will be removed, coefficients should"
Copy link
Contributor

Choose a reason for hiding this comment

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

same as before

devito/finite_differences/tools.py Show resolved Hide resolved
if weights is None:
return 0, None
elif isinstance(weights, Function):
wdim = {d for d in weights.dimensions if d not in expr.dimensions}
Copy link
Contributor

Choose a reason for hiding this comment

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

potential nitpicking and therefore ignorable:

is it worth introducing a SymbolicWeights subclass (can't call it just Weights because it'd be the same name as finite_differences/differentiable.py::Weights) and add a few properties such as .dimension ? or is this impossible because you need expr?

shape = weights.shape
return shape[weights.dimensions.index(wdim)], wdim
else:
return len(list(weights)), None
Copy link
Contributor

Choose a reason for hiding this comment

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

perhaps weights could be a generator?

devito/types/equation.py Show resolved Hide resolved
kwargs['evaluate'] = False
# Backward compat
rhs = cls._apply_coeffs(rhs, coefficients)
lhs = cls._apply_coeffs(lhs, coefficients)
obj = sympy.Eq.__new__(cls, lhs, rhs, **kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpicking: blank lines above and below?

@@ -356,7 +356,7 @@ def _cfl_coeff(self):
if 'lam' in self._physical_parameters or 'vs' in self._physical_parameters:
coeffs = fd_w(1, range(-self.space_order//2+1, self.space_order//2+1), .5)
c_fd = sum(np.abs(coeffs[-1][-1])) / 2
return np.sqrt(self.dim) / self.dim / c_fd
return .95 * np.sqrt(self.dim) / self.dim / c_fd
Copy link
Contributor

Choose a reason for hiding this comment

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

so this justifies the change in expected norm values above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. The CFL bound is extremely tight without it so for some complicated models it can become unstable, this is safer

@@ -41,8 +41,8 @@ def run(shape=(50, 50), spacing=(20.0, 20.0), tn=1000.0,
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
def test_viscoelastic(dtype):
_, _, _, [rec1, rec2, v, tau] = run(dtype=dtype)
assert np.isclose(norm(rec1), 12.30114, atol=1e-3, rtol=0)
assert np.isclose(norm(rec2), 0.312462, atol=1e-3, rtol=0)
assert np.isclose(norm(rec1), 12.62339, atol=1e-3, rtol=0)
Copy link
Contributor

Choose a reason for hiding this comment

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

same here as above

from devito.types.utils import DimensionTuple

__all__ = ['Derivative']


class Derivative(sympy.Derivative, Differentiable, Reconstructable):
class Derivative(sympy.Derivative, Differentiable, Pickable):
Copy link
Contributor

Choose a reason for hiding this comment

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

why isn't Reconstructable enough ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You only get sympy's rkwargs at pickle if you only use Reconstructable so you would loose all the x0/weights/side/transpose at pickle, quite weird it didn't trigger a bug before

Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder whether it's due to this , which we might want to lift in Reconstructable. Because as for the rest a Pickable should reconstruct like a Reconstructable...

anyway, no need to investigate now

from devito.types.utils import DimensionTuple

__all__ = ['Derivative']


class Derivative(sympy.Derivative, Differentiable, Reconstructable):
class Derivative(sympy.Derivative, Differentiable, Pickable):
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder whether it's due to this , which we might want to lift in Reconstructable. Because as for the rest a Pickable should reconstruct like a Reconstructable...

anyway, no need to investigate now

raise ValueError("Number of FD weights provided does not "
"match the functions space_order")

deprecations.coeff_warn
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpicking: perhaps a memoized_meth would be neater because you're performing an action rather than retrieving a property

but really, this is super-nitpicking. Can revisit another day

@mloubout mloubout merged commit 4c8be71 into master Sep 24, 2024
30 of 31 checks passed
@mloubout mloubout deleted the coeffs-revamp branch September 24, 2024 11:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API api (symbolics, types, ...) feature-request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants