-
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
api: revamp custom coefficients API #2434
Conversation
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
Codecov ReportAttention: Patch coverage is
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. |
c7ae7c4
to
bc057cd
Compare
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 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, ...)
thenobj.dx == f.dx(weights=w)
?
- Is it useless or perhaps difficult to provide, in addition, an API such that if you do
- 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?
Well yes, and no. One thing that will be added to tuto doc is that you can provide your own backend for FD through Not sure about adding additional wrapper object for this i don't think it's make it much cleaner.
Completely fine yes, will add it
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. |
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.
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 Derivative
s with identical weights + spec hash the same. Also tests for pickling and unpickling derivatives with custom coefficients.
devito/types/equation.py
Outdated
@classmethod | ||
def _apply_coeffs(cls, expr, coefficients): | ||
""" | ||
This process legacy API of Substitution/Coefficients applying the weights |
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.
"Processes"
The user can also do something like:
We should probably have a test for this |
We should also check that:
works correctly |
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.
Do the big figure changes need to be committed?
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 you avoid committing them somehow?
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 the image indeed different?
If not, one thing I do is 'git add -p'
https://nuclearsquid.com/writings/git-add/
I would prefer to avoid adding these type of constructions that are not really good or clean |
Notebook needs the text reworking. It still references the old API throughout. |
cacb60d
to
b7e35b5
Compare
b7e35b5
to
c407b03
Compare
67b557c
to
65b69e5
Compare
99f692b
to
58a6278
Compare
devito/ir/clusters/algorithms.py
Outdated
# 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 |
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.
entries
shape = weights.shape | ||
return shape[weights.dimensions.index(wdim)], wdim | ||
else: | ||
return len(list(weights)), 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.
Why do you need the list()
here?
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.
perhaps weights could be a generator?
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 can also be Weights
which behave weirdly with len
devito/types/equation.py
Outdated
kwargs['evaluate'] = False | ||
# Backward compat |
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.
"compatibility"
58a6278
to
c58e310
Compare
c58e310
to
2dd0d4c
Compare
2dd0d4c
to
20313d8
Compare
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" |
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 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" |
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.
same as before
if weights is None: | ||
return 0, None | ||
elif isinstance(weights, Function): | ||
wdim = {d for d in weights.dimensions if d not in expr.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.
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 |
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.
perhaps weights could be a generator?
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) |
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.
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 |
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 this justifies the change in expected norm values 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.
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) |
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.
same here as above
from devito.types.utils import DimensionTuple | ||
|
||
__all__ = ['Derivative'] | ||
|
||
|
||
class Derivative(sympy.Derivative, Differentiable, Reconstructable): | ||
class Derivative(sympy.Derivative, Differentiable, Pickable): |
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 isn't Reconstructable enough ?
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 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
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 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): |
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 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 |
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.
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
Superseeds #1644
Currently implemented with backward compatibility, we can drop it at some point.
Left to do for future iterations:
.dxdy
weights=(indices,weights)
for fully custom stencils