Repository navigation
compiler: Preserve compilation invariants under symbol renaming #3084
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
base: main
Are you sure you want to change the base?
Changes from all commits
d830152
985dc73
75ab325
36f7650
7ac4211
6563ea4
6a6070c
d3626c8
544012f
7a1eb98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -293,15 +293,13 @@ def _toposort(exprs): | |
| if e0.lhs in e1.rhs.free_symbols: | ||
| dag.add_edge(e0, e1, force_add=True) | ||
|
|
||
| ordering = {e: n for n, e in enumerate(exprs)} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this really better than using |
||
|
|
||
| def choose_element(queue, scheduled): | ||
| # Preserve expression order among ready temporaries, independent of names | ||
| tmps = [i for i in queue if isinstance(i.lhs, CTemp)] | ||
| if tmps: | ||
| # Try to honor temporary names as much as possible | ||
| first = sorted(tmps, key=lambda i: i.lhs.name).pop(0) | ||
| queue.remove(first) | ||
| else: | ||
| first = sorted(queue, key=lambda i: exprs.index(i)).pop(0) | ||
| queue.remove(first) | ||
| first = min(tmps or queue, key=ordering.__getitem__) | ||
| queue.remove(first) | ||
| return first | ||
|
|
||
| processed = dag.topological_sort(choose_element) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,7 @@ | |
| EvalDerivative, IndexDerivative, LocalSum | ||
| ) | ||
| from devito.symbolics.extended_dtypes import LONG | ||
| from devito.symbolics.extended_sympy import DefFunction, rfunc | ||
| from devito.symbolics.extended_sympy import CallFromPointer, DefFunction, rfunc | ||
| from devito.symbolics.queries import q_leaf | ||
| from devito.symbolics.search import ( | ||
| retrieve_functions, retrieve_indexed, retrieve_terminals | ||
|
|
@@ -22,11 +22,12 @@ | |
| from devito.symbolics.unevaluation import Pow as UnevalPow | ||
| from devito.symbolics.unevaluation import UnevaluableMixin | ||
| from devito.tools import ( | ||
| EnrichedTuple, as_list, as_tuple, flatten, split, transitive_closure | ||
| EnrichedTuple, as_list, as_tuple, flatten, frozendict, split, transitive_closure | ||
| ) | ||
| from devito.types.array import ComponentAccess | ||
| from devito.types.basic import Basic, Indexed | ||
| from devito.types.equation import Eq | ||
| from devito.types.misc import FIndexed | ||
| from devito.types.relational import Ge, Gt, Le, Lt | ||
|
|
||
| __all__ = [ | ||
|
|
@@ -53,8 +54,9 @@ def uxreplace(expr, rule): | |
| be avoided at all costs (e.g., to prevent SymPy from unpicking Devito | ||
| transformations, such as factorization). | ||
|
|
||
| The canonical ordering of the arguments is however guaranteed; where this is | ||
| not possible, a re-evaluation will be enforced. | ||
| Evaluated expressions retain canonical argument ordering. Lowered, | ||
| unevaluated arithmetic retains its operand order and grouping unless | ||
| substitution makes it completely numeric. | ||
|
|
||
| By avoiding re-evaluations, this function is typically much quicker than | ||
| SymPy's xreplace. | ||
|
|
@@ -117,6 +119,17 @@ def _uxreplace(expr, rule): | |
| return expr, False | ||
|
|
||
|
|
||
| @_uxreplace.register(CallFromPointer) | ||
| def _(expr, rule): | ||
| if expr in rule or expr.__class__ in rule: | ||
| return _uxreplace.dispatch(object)(expr, rule) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this just do the default |
||
|
|
||
| args, changed = _uxreplace_dispatch((expr.pointer, expr.params), rule) | ||
| if changed: | ||
| return expr.func(expr.call, *args), True | ||
| return expr, False | ||
|
|
||
|
|
||
| @_uxreplace.register(LocalSum) | ||
| def _(expr, rule): | ||
| # Only the sum owns these guards; ordinary ConditionalDimensions stay atomic | ||
|
|
@@ -165,6 +178,7 @@ def _(iterable, rule): | |
|
|
||
|
|
||
| @_uxreplace_dispatch.register(dict) | ||
| @_uxreplace_dispatch.register(frozendict) | ||
| def _(mapper, rule): | ||
| ret = {} | ||
| changed = False | ||
|
|
@@ -183,6 +197,12 @@ def _uxreplace_handle(expr, args, kwargs): | |
| return expr.func(*args) | ||
|
|
||
|
|
||
| @_uxreplace_handle.register(CallFromPointer) | ||
| def _(expr, args, kwargs): | ||
| # C member names belong to their compound type, rather than the local scope | ||
| return expr.func(expr.call, *args[1:]) | ||
|
|
||
|
|
||
| @_uxreplace_handle.register(Min) | ||
| @_uxreplace_handle.register(Max) | ||
| @_uxreplace_handle.register(Pow) | ||
|
|
@@ -191,6 +211,15 @@ def _(expr, args, kwargs): | |
| return expr.func(*args, evaluate=evaluate) | ||
|
|
||
|
|
||
| @_uxreplace_handle.register(UnevalAdd) | ||
| @_uxreplace_handle.register(UnevalMul) | ||
| def _(expr, args, kwargs): | ||
| # Lowered arithmetic must survive substitutions without reassociation | ||
| if all(i.is_Number for i in args): | ||
| return expr.func.__base__(*args) | ||
| return expr.func(*args) | ||
|
|
||
|
|
||
| @_uxreplace_handle.register(Add) | ||
| def _(expr, args, kwargs): | ||
| args = [i for i in args if i != 0] | ||
|
|
@@ -252,6 +281,7 @@ def dispatchable(self, obj): | |
| _uxreplace_registry.register(Eq) | ||
| _uxreplace_registry.register(DefFunction) | ||
| _uxreplace_registry.register(ComponentAccess) | ||
| _uxreplace_registry.register(FIndexed) | ||
| _uxreplace_registry.register(LocalSum) | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -198,6 +198,19 @@ def test_jit_compiler_flags(self, envvar, flags, monkeypatch): | |
| assert op0._soname != op1._soname | ||
| assert op1._soname == op2._soname | ||
|
|
||
| @pytest.mark.parametrize('name', ['r0', 'r1']) | ||
| def test_generated_names_avoid_inputs(self, name): | ||
| grid = Grid(shape=(8, 8), extent=(7., 7.)) | ||
| f = Function(name=name, grid=grid, space_order=4) | ||
| u = Function(name='u', grid=grid) | ||
| x, y = np.indices(f.data_with_halo.shape, dtype=np.float32) | ||
| f.data_with_halo[:] = x + 2*y | ||
|
|
||
| op = Operator(Eq(u, f.dx + f.dy), name='names_inputs') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to name this |
||
| op.apply() | ||
|
|
||
| np.testing.assert_allclose(u.data, 3., rtol=1.e-5, atol=1.e-6) | ||
|
|
||
|
|
||
| class TestCodeGen: | ||
|
|
||
|
|
||
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.
mode='unique'