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

compiler: Revamp lowering of IndexDerivatives #2310

Merged
merged 10 commits into from
Feb 22, 2024
Merged

Conversation

FabioLuporini
Copy link
Contributor

This paves the way for external customisation, e.g.

  • w[i]*u[x + i] vs w[i]*(u[x + i] - u[x - i])
  • loops vs unroll
  • etc

@@ -324,10 +324,12 @@ def test_redundant_derivatives(self):
assert len(get_arrays(op)) == 0
assert op._profiler._sections['section0'].sops == 74
exprs = FindNodes(Expression).visit(op)
assert len(exprs) == 6
assert len(exprs) == 5
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link

codecov bot commented Feb 12, 2024

Codecov Report

Attention: 8 lines in your changes are missing coverage. Please review.

Comparison is base (c755cd0) 86.67% compared to head (8f22f90) 86.67%.
Report is 2 commits behind head on master.

Files Patch % Lines
devito/core/operator.py 42.85% 2 Missing and 2 partials ⚠️
devito/passes/clusters/derivatives.py 93.10% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2310   +/-   ##
=======================================
  Coverage   86.67%   86.67%           
=======================================
  Files         229      229           
  Lines       43107    43150   +43     
  Branches     7996     8001    +5     
=======================================
+ Hits        37363    37401   +38     
- Misses       5050     5053    +3     
- Partials      694      696    +2     

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

@@ -803,9 +815,13 @@ def _evaluate(self, **kwargs):
return EvalDerivative(expr, base=self.base)


class DiffDerivative(IndexDerivative, DifferentiableOp):
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not make IndexDerivative directlu DifferentiableOp

Copy link
Contributor Author

@FabioLuporini FabioLuporini Feb 12, 2024

Choose a reason for hiding this comment

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

I tried all sort of ways; remember we discussed a sympy issue a few days ago, and eventually I landed here...

So, IndexDerivative was (master branch) a DifferentiableOp, since IndexSum was (master branch) a DifferentiableOp. The heart of the problem is: if an object is non differentiable but one of its arguments is, then a reconstruction will make the parent object differentiable as well

This new structuring distinguishes between DiffDerivative (differentiable) and the lower-level IndexDerivative (non-differentiable). This ensures that, once inside the compiler, everything will have been lowered such that no Differentiable objects can ever be (re)constructed, which in essence will make compilation behave as expected, having ruled out by construction any funky-ness due to the way SymPy handles subclasses and reconstruction

Does it make sense or does it sound like a ranting? I can elaborate or explain in a call if necessary


@property
def scaling(self):
return Mul(*[d.spacing**self.order for d in self.mapper])
Copy link
Contributor

Choose a reason for hiding this comment

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

While yes we wanna go there, this doesn't belong here, this belongs in Derivatives so we can progressively make the eval_fd dimension-indep fir easier compiler pass irrespective of expand/no-expand

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I can drop it, will check

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed.

@@ -247,7 +247,7 @@ def make_derivative(expr, dim, fd_order, deriv_order, side, matvec, x0, symbolic
# Pure number
pass

deriv = IndexDerivative(expr*weights, {dim: indices.free_dim})
deriv = DiffDerivative(expr*weights, {dim: indices.free_dim}, deriv_order)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should drop the if exand/else since can just do this part and return deriv.evaluate if expand=true since IndexDerivative are properly evaluable since a few PR ago

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I worry it opens a Pandora's box that I wouldn't be very willing to deal with at this stage, but I can try

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm now opening a different PR for this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How do u suggest we handle the second part of the if:

if not expand and indices.expr is not None:

IIRC indices.expr may be None for some low order derivatives.
I think we would first have to normalizate behavior at the top (always produce indices.expr) then we could do what you suggested

Copy link
Contributor

Choose a reason for hiding this comment

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

Hum right, didn't think that indices.expr would be None I guess we can park it for now

@@ -755,11 +758,12 @@ def __new__(cls, expr, mapper, **kwargs):
obj = super().__new__(cls, expr, dimensions)
obj._weights = weights
obj._mapper = frozendict(mapper)
obj._order = order
Copy link
Contributor

Choose a reason for hiding this comment

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

comment on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can actually probably drop it, it's now unused. It's the derivative order

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed


def _lower_exprs(expressions, subs):
Copy link
Contributor

Choose a reason for hiding this comment

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

Note for later: subs should not be added to mapper as it would make compiler pass easier (pure sympy) and do the subs at the tail end of iet pass

e, clusters = _core(a, c, weights, reusables, mapper, sregistry)
args.append(e)
processed.extend(clusters)
if not isinstance(expr, IndexDerivative):
Copy link
Contributor

Choose a reason for hiding this comment

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

single dispatch on expr?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not gonna work unless we handle it here , but don't remember the details, will look again

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah no sorry, ignore the comment above, I had totally misunderstood it

Yes, I like the idea, will change tomorrow

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done single-dispatch

@@ -63,9 +63,13 @@ def _normalize_kwargs(cls, **kwargs):
# Distributed parallelism
o['dist-drop-unwritten'] = oo.pop('dist-drop-unwritten', cls.DIST_DROP_UNWRITTEN)

# Misc
# Code generation options for derivatives
Copy link
Contributor

@EdCaunt EdCaunt Feb 14, 2024

Choose a reason for hiding this comment

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

Could this be packed up into a function in a new file like devito.core.utils.py? It's exactly repeated in both cpu.py and gpu.py. Something like parse_cgen_options(o, oo)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

probably yes, but you'll see there are a lot of small differences between the two

Not happening before Rice anyway, but I'll take note

@FabioLuporini FabioLuporini merged commit ccfb823 into master Feb 22, 2024
31 checks passed
@FabioLuporini FabioLuporini deleted the optimize-coeffs branch February 22, 2024 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants