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

types: Remove increments from SubDimension names #2257

Closed
wants to merge 27 commits into from

Conversation

EdCaunt
Copy link
Contributor

@EdCaunt EdCaunt commented Nov 3, 2023

Requires a compiler pass to resolve clashing dimension names, which in turn requires additions to uxreplace so it can be used on ConditionalDimensions.

Copy link

codecov bot commented Nov 3, 2023

Codecov Report

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

Comparison is base (dd1b154) 86.90% compared to head (6eeb17b) 86.96%.
Report is 74 commits behind head on master.

Files Patch % Lines
devito/symbolics/manipulation.py 94.33% 3 Missing ⚠️
devito/ir/equations/algorithms.py 96.55% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2257      +/-   ##
==========================================
+ Coverage   86.90%   86.96%   +0.06%     
==========================================
  Files         229      229              
  Lines       41545    42262     +717     
  Branches     7660     7809     +149     
==========================================
+ Hits        36105    36755     +650     
- Misses       4818     4860      +42     
- Partials      622      647      +25     

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

dims = set().union(*tuple(set(i.function.dimensions)
for i in retrieve_indexed(e)))
# Dimensions in conditions and ConditionalDimension parents
dims = dims.union(*tuple(pull_dims(d.condition, flag=False)
Copy link
Contributor

Choose a reason for hiding this comment

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

you probably want to unclutter here and move most of this logic into pull_dims

ideally, all you need to do here would be:

dims = pull_dims(e, flag=...)

# Sort for groupby
dims = sorted(dims, key=lambda x: x.name)

because here you're performing a search which, in theory, is just one, atomic step of separate_dimensions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IIrc, what's required here is more picky than pull_dims (pull dims selects more dimensions than are needed). It just wants dimensions in the indices, conditional parent, and conditional condition

self.append(cls)
_uxreplace_handle.register(cls, _uxreplace_handle_reconstructable)

for kls, callback in (rkwargs_callback_mapper or {}).items():
_uxreplace_dispatch.register(kls, callback)

def dispatchable(self, obj):
return isinstance(obj, tuple(self))
def dispatchable(self, obj, deep=False):
Copy link
Contributor

Choose a reason for hiding this comment

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

So, I'm not sure I fully understand...

For ConditionalDimension:

  • sometimes you want it deep
  • but some other times, you don't ?

is this correct? in particular, you want it deep only when you calol it inside separate_dimensions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Inside separate_dimensions, uxreplace wants to recurse into ConditionalDimensions and update all their args, kwargs, etc as necessary. However, elsewhere in the code, in particular here when called from here, this behaviour may not be desired. In the above case, the ConditionalDimension may have already been indexified and shifted during the lowering of the expressions if it contains terms present in the Eq (in the case that ConditionalDimension is always affected by uxreplace), causing shifting to get applied twice. Hence, the need to be able to have some kind of control over the classes touched by uxreplace on a call-by-call basis

@@ -200,6 +200,10 @@ def is_const(self):
def root(self):
return self

@property
def is_Root(self):
return self == self.root
Copy link
Contributor

Choose a reason for hiding this comment

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

is self.root

# _uxreplace_registry.register(ConditionalDimension, deep=True)

# Registry for dimension uxreplace
_dxreplace_registry = UxreplaceRegistry()
Copy link
Contributor

Choose a reason for hiding this comment

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

rename as ReplaceRegistry ?

for e in expressions:
# Just want dimensions which appear in the expression
# Dimensions in indices
dims = set().union(*tuple(set(i.function.dimensions)
Copy link
Contributor

Choose a reason for hiding this comment

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

Un-necessary extra "set" work, just set().union(*[i.function.dimensions ...])

dims = set().union(*tuple(set(i.function.dimensions)
for i in retrieve_indexed(e)))
# Dimensions in conditions and ConditionalDimension parents
dims = dims.union(*tuple(pull_dims(d.condition, flag=False)
Copy link
Contributor

Choose a reason for hiding this comment

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

same, no need for tuple

dims = dims.union(*tuple(pull_dims(d.condition, flag=False)
for d in dims if d.is_Conditional
and d.condition is not None),
set(d.parent for d in dims if d.is_Conditional))
Copy link
Contributor

Choose a reason for hiding this comment

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

the whole thing seems to be a bit intricated retrieve_dimensions(e, deep=True) + e.implicit_dims should give you all dimensions.
Also why are we ignoring the conditionals with a factor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The issue is that retrieve_dimensions collects too many things, not too few (even with flag=False). It wants to retrieve only indices in the equation, conditional dimension parent, and dimensions in the conditional dimension condition. Good point on the factor, that should probably be added in.

Fabio pointed out that separate_equations probably wants to be after creation of LoweredEQ, so may need to rethink this anyway.

Copy link
Contributor

Choose a reason for hiding this comment

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

It wants to retrieve only indices in the equation

Why would a dimsion use as a free symbol rather than an index be ignored? It doesn't change the issue it's gonna be the wrong value if not changed. Any time both the conditional and the parent are used together irrespective of how is a clash.


# Group dimensions by matching names
groups = tuple(tuple(g) for n, g in groupby(dims, key=lambda x: x.name))
clashes = tuple(g for g in groups if len(g) > 1)
Copy link
Contributor

Choose a reason for hiding this comment

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

you only need generator no need for tuple

rdims = tuple(d for d in c if d.is_Root)
ddims = tuple(d for d in c if not d.is_Root)

for d in (ddims + rdims)[:-1]:
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is safe, a root dimension should never need to have its name change so this should only loop through ddims and raise an error if there is still clashes

subs[d] = resolutions[d]
else:
try:
subs[d] = d._rebuild(d.name+str(count[d.name]))
Copy link
Contributor

Choose a reason for hiding this comment

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

devito standard is formating '%s%s' % (d.name, count[d.name]) not string concatenation

subs[d] = d._rebuild(d.name+str(count[d.name]))
count[d.name] += 1
except KeyError:
subs[d] = d._rebuild(d.name+'0')
Copy link
Contributor

Choose a reason for hiding this comment

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

same, '%s0' % d.name

ddims = tuple(d for d in c if not d.is_Root)

for d in (ddims + rdims)[:-1]:
if d in resolutions.keys():
Copy link
Contributor

Choose a reason for hiding this comment

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

and resolutions[d] not in clashes

Need to make sure you don't replace a clash by another but that case gets tricky because you can't set resoltuion[d] anymore or it would replace the existing one by a different one, not sure what the best way is for this very corner case.

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 could always check that the incremented dimension name isn't in dims and incrementing until it is not?

Copy link
Contributor

Choose a reason for hiding this comment

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

also no need for .keys()

# the object rather than the property, as the latter can
# sometimes have default values which do not match the
# default for the class
v = {i: getattr(expr, "_"+i) for i in expr.__rkwargs__}
Copy link
Contributor

Choose a reason for hiding this comment

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

'_%s' % i

What if there isn't one and there is only the self.rkwargs one directly .function for any AbstractFunction

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 quite sure I understand?

Copy link
Contributor

Choose a reason for hiding this comment

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

Some type have attribute such as self.function that are in rkwargs but self._function doesn't exist

Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

subs[d] = resolutions[d]
else:
try:
subs[d] = d._rebuild('%s%s' % (d.name, count[d.name]))
Copy link
Contributor

Choose a reason for hiding this comment

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

use the sregistry instead of count

You can plumb it down the compiler pass from the caller site -- it's one of the kwargs

@@ -220,8 +221,11 @@ def writes(self):
def xreplace(self, rules):
return LoweredEq(self.lhs.xreplace(rules), self.rhs.xreplace(rules), **self.state)

def func(self, *args):
return self._rebuild(*args, evaluate=False)
def func(self, *args, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

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

since it's a Reconstructable, and in particular a Pickable, I think that the following should suffice:

func = Pickable._rebuild



@_uxreplace_dispatch.register(IterationSpace)
def _(expr, rule, mode='ux'):
Copy link
Contributor

Choose a reason for hiding this comment

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

expr should be called ispace

Copy link
Contributor

Choose a reason for hiding this comment

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

but most importantly, see comments on slack

ddims = tuple(d for d in c if not d.is_Root)

for d in (ddims + rdims)[:-1]:
if d in resolutions.keys():
Copy link
Contributor

Choose a reason for hiding this comment

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

also no need for .keys()

@EdCaunt
Copy link
Contributor Author

EdCaunt commented Dec 1, 2023

Superseded by #2269

@EdCaunt EdCaunt closed this Dec 1, 2023
@EdCaunt EdCaunt deleted the dim-naming-unclash branch July 9, 2024 09:08
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