Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 35 additions & 78 deletions devito/finite_differences/differentiable.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import ChainMap
from contextlib import suppress
from functools import cached_property, singledispatch
from itertools import product

Expand Down Expand Up @@ -28,16 +29,8 @@
from devito.types.basic import AbstractFunction, Indexed

__all__ = [
'Conj',
'DiffDerivative',
'Differentiable',
'EvalDerivative',
'Imag',
'IndexDerivative',
'IndexDerivativeProperty',
'LocalSum',
'Real',
'Weights',
'Conj', 'DiffDerivative', 'Differentiable', 'EvalDerivative', 'Imag',
'IndexDerivative', 'IndexDerivativeProperty', 'LocalSum', 'Real', 'Weights',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No

]


Expand Down Expand Up @@ -88,10 +81,7 @@ def grid(self):
grids = {g.root for g in grids}
if len(grids) > 1:
warning("Expression contains multiple grids, returning first found")
try:
return grids.pop()
except KeyError:
return None
return grids.pop() if grids else None

@cached_property
def dtype(self):
Expand Down Expand Up @@ -154,12 +144,10 @@ def _fd(self):
# Filter out all args with fd order too high
fd_args = []
for f in self._args_diff:
try:
with suppress(AttributeError):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yes

if f.space_order <= self.space_order and \
(not f.is_TimeDependent or f.time_order <= self.time_order):
fd_args.append(f)
except AttributeError:
pass
return dict(ChainMap(*[getattr(i, '_fd', {}) for i in fd_args]))

@cached_property
Expand All @@ -168,10 +156,7 @@ def _symbolic_functions(self):

@cached_property
def function(self):
if len(self._functions) == 1:
return set(self._functions).pop()
else:
return None
return set(self._functions).pop() if len(self._functions) == 1 else None

@cached_property
def _uses_symbolic_coefficients(self):
Expand All @@ -198,10 +183,8 @@ def _subs(self, old, new, **hints):
return self
args = list(self.args)
for i, arg in enumerate(args):
try:
with suppress(AttributeError):
args[i] = arg._subs(old, new, **hints)
except AttributeError:
continue
return self.func(*args, evaluate=False)

@property
Expand Down Expand Up @@ -290,10 +273,7 @@ def __rfloordiv__(self, other):
return floor(other / self)

def _inv(self, ref, safe=False):
if safe:
return SafeInv(self, ref or self)
else:
return 1 / self
return SafeInv(self, ref or self) if safe else 1 / self

def __mod__(self, other):
return Mod(self, other)
Expand Down Expand Up @@ -468,9 +448,8 @@ def has(self, *pattern):
"""
for p in pattern:
# Following sympy convention, return True if any is found
if isinstance(p, type) \
and issubclass(p, sympy.Symbol) \
and any(isinstance(i, p) for i in self.free_symbols):
if (isinstance(p, type) and issubclass(p, sympy.Symbol) and

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Todo: revert

any(isinstance(i, p) for i in self.free_symbols)):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is less readable

# Symbols (and subclasses) are the leaves of an expression, and they
# are promptly available via `free_symbols`. So this is super quick
return True
Expand Down Expand Up @@ -500,8 +479,7 @@ def deep_priority(expr):
generic value, so `mu*tau_xx` reports .75 rather than `tau_xx`'s 2.1.
"""
prio = getattr(expr, '_fd_priority', 0)
return max([prio] + [deep_priority(i)
for i in getattr(expr, '_args_diff', ())])
return max([prio] + [deep_priority(i) for i in getattr(expr, '_args_diff', ())])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@FabioLuporini doesn't like getattr, if you're going to touch lines of code you should probably fix them too



def highest_priority(diff_op, candidates=None):
Expand Down Expand Up @@ -656,14 +634,12 @@ def __new__(cls, *args, **kwargs):
# which would destroy `EvalDerivative`s if present. So here we perform
# a similar thing, but cautiously construct an evaluated Add, which
# will preserve the integrity of `EvalDerivative`s, if any
try:
with suppress(AttributeError, ValueError):
a, b = args
if a.is_Rational:
r, b = b.as_coeff_Mul()
if r is sympy.S.One and type(b) is Add:
return Add(*[_keep_coeff(a, bi) for bi in b.args], evaluate=False)
except (AttributeError, ValueError):
pass

return super().__new__(cls, *args, **kwargs)

Expand All @@ -686,9 +662,8 @@ def _gather_for_diff(self):
derivs, other = split(self.args, lambda a: isinstance(a, sympy.Derivative))
if len(derivs) == 0:
return self._eval_at(highest_priority(self))
else:
other = self.func(*other)._eval_at(highest_priority(self))
return self.func(other, *derivs)
other = self.func(*other)._eval_at(highest_priority(self))
return self.func(other, *derivs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I despise multiple returns and consider them the definition of spaghetti code. I'd much rather you consolidate and return once


@classmethod
def _off_func(cls, a, func):
Expand Down Expand Up @@ -777,8 +752,7 @@ def _eval_at(self, func, interp_mode='direct', **kwargs):
if dim in func.indices_ref.getters})
else:
source = a.indices_ref
new_factors.append(interp_at(a, source, block_indices,
self.interp_order))
new_factors.append(interp_at(a, source, block_indices, self.interp_order))

# Final I from block's location to func
return interp_at(self.func(*new_factors), block_indices,
Expand Down Expand Up @@ -836,8 +810,7 @@ class RealComplexPart(ComplexPart):

@cached_property
def dtype(self):
dtype = extract_dtype(self)
return dtype(0).real.__class__
return extract_dtype(self)(0).real.__class__


class Real(RealComplexPart):
Expand Down Expand Up @@ -899,11 +872,8 @@ def __new__(cls, expr, dimensions, **kwargs):
return obj

def __repr__(self):
return "{}({}, ({}))".format(
self.__class__.__name__,
self.expr,
', '.join(d.name for d in self.dimensions)
)
dims = ', '.join(d.name for d in self.dimensions)
return f"{self.__class__.__name__}({self.expr}, ({dims}))"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this actually the correct repr, as in is the second argument actually a tuple?
If yes use {dims !r}, if no you still need the join


__str__ = __repr__

Expand Down Expand Up @@ -1109,17 +1079,14 @@ def _xreplace(self, rule):
return rule[self], True
elif not rule:
return self, False
else:
try:
weights, flags = zip(
*[i._xreplace(rule) for i in self.weights], strict=True
)
if any(flags):
return self.func(initvalue=weights, function=None), True
except AttributeError:
# `float` weights
pass
return super()._xreplace(rule)
try:
weights, flags = zip(*[i._xreplace(rule) for i in self.weights], strict=True)
if any(flags):
return self.func(initvalue=weights, function=None), True
except AttributeError:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should use contextlib.suppress also see comment about multiple returns

# `float` weights
pass
return super()._xreplace(rule)

@cached_property
def _npweights(self):
Expand All @@ -1136,8 +1103,7 @@ def value(self, idx):
v = self._npweights[idx]
if v.is_Number or v.is_Indexed:
return sympy.sympify(v)
else:
return self[idx]
return self[idx]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤮



class IndexDerivativeProperty(Tag):
Expand Down Expand Up @@ -1196,8 +1162,7 @@ def compare(self, other):
return (self.weights.compare(other.weights) or
self.base.compare(other.base) or
(p1 > p2) - (p1 < p2))
else:
return super().compare(other)
return super().compare(other)

@cached_property
def base(self):
Expand Down Expand Up @@ -1250,8 +1215,7 @@ def _subs(self, old, new, **hints):
# may fail to identify the sub-expression to be replaced (note: if
# `a/b/c` are atoms or Indexeds, it's generally fine)

if not old.is_Mul or \
old is not self.base:
if not old.is_Mul or old is not self.base:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You could even use De Morgan's law...

return super()._subs(old, new, **hints)

return self._rebuild(new * self.weights)
Expand Down Expand Up @@ -1300,7 +1264,6 @@ def __new__(cls, *args, base=None, **kwargs):
# story: a zero-order derivative whose weights collapse to one is
# the identity, so it comes back as the sum it was applied to.
assert len(args) <= 1
return obj

return obj

Expand Down Expand Up @@ -1339,8 +1302,7 @@ class diffify:

def __new__(cls, obj):
args = [diffify._doit(i) for i in obj.args]
obj = diffify._doit(obj, args)
return obj
return diffify._doit(obj, args)

def _doit(obj, args=None):
cls = diffify._cls(obj)
Expand Down Expand Up @@ -1401,8 +1363,7 @@ def _diff2sympy(obj):

# Handle special objects
if isinstance(obj, DiffDerivative):
return IndexDerivative(*args, obj.mapper,
deriv_order=obj.deriv_order,
return IndexDerivative(*args, obj.mapper, deriv_order=obj.deriv_order,
properties=obj.properties), True

# Handle generic objects such as arithmetic operations
Expand All @@ -1421,8 +1382,7 @@ def _diff2sympy(obj):
# In case of indices using other Function, evaluate
# may not be a supported argument.
return obj.func(*args), True
else:
return obj, False
return obj, False

return _diff2sympy(expr)[0]

Expand Down Expand Up @@ -1460,8 +1420,7 @@ def _(expr, x0, **kwargs):
def test0(a):
return all(a.indices[d] is i for d, i in x0.items() if d in a.dimensions)

oa, ia = split(expr._args_diff,
lambda a: isinstance(a, sympy.Derivative) or test0(a))
oa, ia = split(expr._args_diff, lambda a: isinstance(a, sympy.Derivative) or test0(a))
oa = oa + tuple(a for a in expr.args if a not in expr._args_diff)

# Interpolate the necessary args
Expand All @@ -1476,8 +1435,7 @@ def test0(a):
def _(expr, x0, **kwargs):
if expr.args:
return expr.func(*[interp_for_fd(i, x0, **kwargs) for i in expr.args])
else:
return expr
return expr
Comment on lines 1436 to +1438

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Okay, now I've seen enough of this!
If you want to reduce lines this is one line:

return expr.func(*[interp_for_fd(i, x0, **kwargs) for i in expr.args]) if expr.args else expr

and removes the multiple returns



@interp_for_fd.register(AbstractFunction)
Expand All @@ -1486,5 +1444,4 @@ def _(expr, x0, **kwargs):
and expr.indices.get(d, v) is not v}
if x0_expr:
return expr.subs({expr.indices[d]: v for d, v in x0_expr.items()})
else:
return expr
return expr
Loading
Loading