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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

api: revamp custom coefficients API #2434

wants to merge 8 commits into from

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.60140% with 2 lines in your changes missing coverage. Please review.

Project coverage is 86.99%. Comparing base (cec0542) to head (958720a).

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%   86.99%   -0.07%     
==========================================
  Files         238      238              
  Lines       45171    44939     -232     
  Branches     8417     8387      -30     
==========================================
- Hits        39326    39096     -230     
  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
@@ -305,6 283,12 @@ def generate_indices(expr, dim, order, side=None, matvec=None, x0=None):
else:
o_min -= 1

if nweights > 0 and (o_max - o_min 1) != nweights:
assert nweights == (o_max - o_min 1) 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Explanatory comment on this check would probably be worthwhile

@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

@@ -145,9 145,9 @@ def create_gaussian_weights(sigma, lw):
weights = [w/w.sum() for w in (np.exp(-0.5/s**2*(np.linspace(-l, l, 2*l 1))**2)
for s, l in zip(sigma, lw))]
processed = []
for w in weights:
for (w, l) in zip(weights, lw):
Copy link
Contributor

Choose a reason for hiding this comment

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

lw is a pretty cryptic variable name. I would use a more descriptive one or add a comment

temp = list(w)
while len(temp) < 2*max(lw) 1:
while len(temp) < 2*l 1:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this for padding? If so, why not use np.pad? Would be more tidy and robust.

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?

@@ -119,6 121,22 @@ def detect(cls, expr):
OpMin = Operation('min')


identity_mapper = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this mapper be frozen?

@@ -65,14 63,43 @@ class Eq(sympy.Eq, Evaluable):

def __new__(cls, lhs, rhs=0, subdomain=None, coefficients=None, implicit_dims=None,
**kwargs):
if coefficients is not None:
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably only want to have this warning in one place, otherwise it will print two or three warnings rather than just one. We should probably also have a warning saying that coefficients='symbolic' can be removed when instantiating a Function, if there isn't one already.

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"

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