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: Add pass to abridge SubDimension names where possible #2269

Merged
merged 1 commit into from
Dec 5, 2023

Conversation

EdCaunt
Copy link
Contributor

@EdCaunt EdCaunt commented Nov 17, 2023

Making the PR to run it through CI and get some opinions. Still needs tests and probably the option to pass a keyword argument to Operator along the lines of shorten_subdims=False (lmk if anyone has a snappier name).

Copy link

codecov bot commented Nov 17, 2023

Codecov Report

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

Comparison is base (1b5f8c3) 86.82% compared to head (0d00ecd) 86.83%.

Files Patch % Lines
devito/types/grid.py 87.50% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2269      +/-   ##
==========================================
+ Coverage   86.82%   86.83%   +0.01%     
==========================================
  Files         229      229              
  Lines       42387    42435      +48     
  Branches     7845     7868      +23     
==========================================
+ Hits        36803    36850      +47     
- Misses       4924     4926       +2     
+ Partials      660      659       -1     

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

Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@@ -762,6 +762,7 @@ def optimize_schedule_rotations(schedule, sregistry):

try:
candidate = k[ridx]
print(candidate, type(candidate))
Copy link
Contributor

Choose a reason for hiding this comment

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

leftover

@@ -135,6 +135,12 @@ def __init__(self, a, b, c=4):
args += tuple(getattr(self, i[1:]))
else:
args += (getattr(self, i),)

args = list(args)
for k in kwargs.copy():
Copy link
Contributor

Choose a reason for hiding this comment

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

do u actually need to copy here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought you don't want to actually?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you don't copy, the dict length changes within the loop and it causes a RunTimeError. The copy is only used so it iterates over a consistent dictionary. It still edits the original kwargs

Copy link
Contributor

Choose a reason for hiding this comment

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

ah ok:

It still edits the original kwargs

I had missed this one

I typically do list(kwargs) but it doesn't really matter I guess

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this a preference for consistency?

Copy link
Contributor

Choose a reason for hiding this comment

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

good question... I think list(...) is more broadly used

umapper.update({i: replace_parent(i, mapper) for i in uinds})

for it in tree:
mdims = [d for d in FindSymbols('dimensions').visit(it)]
Copy link
Contributor

Choose a reason for hiding this comment

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

aka mdims = FindSymbols('dimensions').visit(it)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yeah, oops

Copy link
Contributor

@mloubout mloubout left a comment

Choose a reason for hiding this comment

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

some minor comments, missed a bit of discussion

symbolic_size=n)
dsi = ModuloDimension('%si' % ds, cd, cd + ds - iis, n)
ii = ModuloDimension('%sii' % d.root, ds, iis, incr=iib)
cd = CustomDimension(name='%s%s' % (d.root, d.root), symbolic_min=ii,
Copy link
Contributor

Choose a reason for hiding this comment

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

d.root.name, try to avoid "%s" % obj and rather use "%" % obj.name in general can be slow sometimes to convert to string

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair, I was going off what was previously there. Will edit

ii = ModuloDimension('%sii' % d.root, ds, iis, incr=iib)
cd = CustomDimension(name='%s%s' % (d.root, d.root), symbolic_min=ii,
symbolic_max=iib, symbolic_size=n)
dsi = ModuloDimension('%si' % ds.root, cd, cd + ds - iis, n)
Copy link
Contributor

Choose a reason for hiding this comment

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

same



def abridge_dim_names(iet):
def replace_parent(dim, mapper):
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure really need a local function for that

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 true, it was originally much bulkier, but I think it can probably be removed

for it in tree:
uinds = set().union(*[i.uindices for i in it])
uinds = [i for i in uinds if i.is_Incr]
uinds = [i for i in uinds if i.parent in mapper]
Copy link
Contributor

Choose a reason for hiding this comment

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

one line if i.is_Incr and i.parent in mapper

for it in tree:
mdims = FindSymbols('dimensions').visit(it)
mdims = [d for d in mdims if d.is_Modulo]
mdims = [d for d in mdims if d.parent in {**mapper, **umapper}]
Copy link
Contributor

Choose a reason for hiding this comment

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

d.parent in mapper or d.parent in umapper , {**mapper, **umapper} creates a new dictionary everytime

mdims = FindSymbols('dimensions').visit(it)
mdims = [d for d in mdims if d.is_Modulo]
mdims = [d for d in mdims if d.parent in {**mapper, **umapper}]
mmapper = {d: replace_parent(d, {**mapper, **umapper}) for d in mdims}
Copy link
Contributor

Choose a reason for hiding this comment

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

either create that combined dictioanry once or call replace_parent separately for each one

mdims = [d for d in mdims if d.parent in {**mapper, **umapper}]
mmapper = {d: replace_parent(d, {**mapper, **umapper}) for d in mdims}
subs.update({i: Uxreplace({**mapper, **umapper, **mmapper}).visit(i)
for i in it})
Copy link
Contributor

Choose a reason for hiding this comment

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

do you actually need three separate dictionaries if they end up merged for usage anyway?

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.

nitpicks left

mapper.update({i: i._rebuild(i.root.name) for i in inds
if i.root not in it.dimensions and names.count(i.root.name) < 2})

if mapper:
Copy link
Contributor

Choose a reason for hiding this comment

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

if you have to open up a scope this big, it's preferable to do the opposite:

if not mapper:
    return iet

<de-indented code>

it's also a useful mental exercise to understand what would happen if you didn't have this if in place. Would the code still work and just behave unoptimally by having to go through useless extra processing? or would it break? if the former, all good. If the latter, sometimes it's an indication of a flaw in the implementation. What case is yours ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would just do a bunch of unnecessary stuff to generate an empty dict and eventually return an unaltered iet. It's just there to avoid inefficiency. Will reverse the return like you said though

mapper.update(umapper)

for it in tree:
mdims = FindSymbols('dimensions').visit(it)
Copy link
Contributor

Choose a reason for hiding this comment

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

lhs name is wrong. It should be

dims = FindSymbols('dimensions').visit(it)

for it in tree:
mdims = FindSymbols('dimensions').visit(it)
mdims = [d for d in mdims if d.is_Modulo]
mdims = [d for d in mdims if d.parent in mapper]
Copy link
Contributor

Choose a reason for hiding this comment

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

these last two lines can (should) be a single line

mdims = [d for d in mdims if d.is_Modulo and d.parent in mapper]



class TestMultiSubDimension:
def test_rebuild(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

blank line after "class ..." line

if not mapper:
return iet

umapper = {}
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 umapper and not just update mapper directly

for it in tree:
dims = FindSymbols('dimensions').visit(it)
dims = [d for d in dims if d.is_Modulo and d.parent in mapper]
mmapper = {d: d._rebuild(parent=mapper[d.parent]) for d in dims}
Copy link
Contributor

Choose a reason for hiding this comment

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

same, why not just update mapper ?

try:
((lst, _), (rst, _)) = thickness
except ValueError:
raise ValueError("Invalid thickness specification: %s" % thickness)
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe bit more explicit message, like "expects : ... "?

@FabioLuporini FabioLuporini changed the title compiler: Added pass to abridge SubDimension names where possible compiler: Add pass to abridge SubDimension names where possible Dec 5, 2023
subs = {}
mapper = {}
tree = retrieve_iteration_tree(iet)
for it in tree:
Copy link
Contributor

Choose a reason for hiding this comment

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

also, tiny comment -- what is this block supposed to do?

Copy link
Contributor

Choose a reason for hiding this comment

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

or perhaps instead of "what", "why" ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It gets all the indices which are SubDimensions or SubDimension-derived appearing in the expression in the innermost loop nests to assemble substitutions for them. It then checks if it can rename these dimensions to the name of their root without introducing a clash within the inner loop.

However, due to loop structures like

for x in range(x_M):
    for yi in range(yi_M):
        ...
    for yj in range(yj_M):
        ....

you need to create a global mapper rather than building and applying it on a tree-by-tree basis. Since the mapper isn't complete until the end of the loop, you need to finish the loop before moving onto updating the parents of the unbound indices.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, nvm, seems to have been left over from previous iterations. Has now been condensed down

tree = retrieve_iteration_tree(iet)
for it in tree:
indexeds = FindSymbols('indexeds').visit(it.inner)
inds = set().union(*[pull_dims(i, flag=False) for i in indexeds])
Copy link
Contributor

Choose a reason for hiding this comment

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

these are dims. Why do you call them inds?

indexeds = FindSymbols('indexeds').visit(it.inner)
inds = set().union(*[pull_dims(i, flag=False) for i in indexeds])
inds = [i for i in inds if any([d.is_Sub for d in i._defines])]
inds = [i for i in inds if not i.is_SubIterator]
Copy link
Contributor

Choose a reason for hiding this comment

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

when we're sure they're dimensions, we typically use the letter d for iteration

I admit this is loosely done, though

devito/passes/iet/misc.py Show resolved Hide resolved
if not mapper:
return iet

for it in tree:
Copy link
Contributor

Choose a reason for hiding this comment

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

some comments here would help

def abridge_dim_names(iet):
subs = {}
mapper = {}
tree = retrieve_iteration_tree(iet)
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be called trees, not tree

it should also be moved at the top of the function since it will be reused by other code blocks later on

so

def ...
     trees = ...
     <blank line>
     subs = {}
     ...

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.

I think this is now GTG

@mloubout mloubout merged commit 1bf0991 into master Dec 5, 2023
31 checks passed
@mloubout mloubout deleted the abridged_names branch December 5, 2023 13:00
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