Skip to content

Features/914 adv indexing#923

Closed
ben-bou wants to merge 3 commits into
features/914_distributed-non-ordered-indexingfrom
features/914_adv-indexing
Closed

Features/914 adv indexing#923
ben-bou wants to merge 3 commits into
features/914_distributed-non-ordered-indexingfrom
features/914_adv-indexing

Conversation

@ben-bou

@ben-bou ben-bou commented Feb 22, 2022

Copy link
Copy Markdown
Collaborator

Description

Changes proposed:

  • preprocess key to find Advanced indexing, newaxis indexing and ellipsis
  • change torch_proxy By adding empty dimensions where Advanced indexing should be. In Order to reconstruct the correct shape, Put the shape in the dimensions name.
  • simplify slice indexing by slicing a range_proxy.

Notes

  • DNDarray is Not an instance of Iterable
  • iter(dndarray) works anyway
  • iter(0-dim dndarray) also works, but shouldn‘t

skip ci

@mtar

mtar commented Feb 22, 2022

Copy link
Copy Markdown
Collaborator

GPU cluster tests are currently disabled on this Pull Request.

Comment thread heat/core/dndarray.py
"""
advanced_indexing = False
if isinstance(key, DNDarray):
# DNDARRAY CURRENTLY DOES NOT IMPLEMENT the Iterable interface, need to define __iter__()

@ClaudiaComito ClaudiaComito Feb 23, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We could implement the Iterable interface for non-distributed DNDarrays, but we need to give some thought to what to fall back to when split is not None.

@ben-bou ben-bou Feb 23, 2022

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Well, iter(...) is just an Iterator over the first dimension, right? So I think

def __iter__(self):
    if self.split == 0:
        warnings.warn("Iterating over the split dimension is strongly discouraged")
    for i in range(len(self)):
        yield self[i]

would work as expected

Comment thread heat/core/dndarray.py
for i, k in enumerate(key):
if isinstance(k, Iterable) or isinstance(key, DNDarray):
advanced_indexing = True
key[i] = factories.array(key[i])

@ClaudiaComito ClaudiaComito Feb 23, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

key[i] might be distributed along self.split (or actually newsplit, whatever that is)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

What would newsplit be?

@ClaudiaComito ClaudiaComito Feb 23, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We don't know yet because we need a sanitized key first. I guess what I'm saying is, we need to be agnostic about k.split at this point. How about: never mind

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh no I'm taking this back. tensors and ndarrays have .splitas well

Comment thread heat/core/dndarray.py
expand_key[ellipsis_index - len(key) :] = key[ellipsis_index + 1 :]
key = expand_key
if add_dims:
for i, k in reversed(enumerate(key)):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TypeError: 'enumerate' object is not reversible...

@ClaudiaComito ClaudiaComito left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi @ben-bou thanks a lot for this! I've reviewed the new private functions, I'll get to the rest later.

Comment thread heat/core/dndarray.py
for i, k in reversed(enumerate(key)):
if k is None:
key[i] = slice(None)
arr = arr.expand_dims(i - add_dims + 1) # is the -1 correct?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think __process_key() shouldn't modify arr. torch will take care of creating the extra dimension when we finally index, right?

@ben-bou ben-bou Feb 23, 2022

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

You're right, it will. The difference is where the split axis is in the key. key[split] would not be the index for the split axis if there is a newaxis before it. expand_dims is a convenient way to handle this because it's an in-place operation with correct split semantics.

@ben-bou ben-bou Feb 23, 2022

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Another thing to consider is that named tensors are not compatible with newaxis indexing. So if we are to use torch.names to find the output-split, we have to process the newaxis beforehand.

Comment thread heat/core/dndarray.py
Comment on lines +701 to +706
if k is None:
key[i] = slice(None)
arr = arr.expand_dims(i - add_dims + 1) # is the -1 correct?
add_dims -= 1
# expand key to match the number of dimensions of the DNDarray
key = tuple(key + [slice(None)] * (arr.ndim - len(key)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Really I'm not sure we need to do anything about a None key element at all.

Comment thread heat/core/dndarray.py
"""
Private method for processing keys for indexing. Returns wether advanced indexing is used as well as a processed key and self.
A processed key:
- doesn't cotain any ellipses or newaxis

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this is potentially not correct, see below (i.e. why not leave newaxis in?)

Comment thread heat/core/dndarray.py
Comment on lines +727 to +728
local_inds = range_proxy[start - offset : stop - offset] # only works if stop - offset > 0
local_inds = local_inds[max(offset - start, 0) % step :: step]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How about:

local_start = max(start-offset, 0)
local_stop = max(min(stop-offset, range_proxy.stop), 0)
local_inds = range_proxy[local_start:local_stop]
local_inds = local_inds[max(offset - start, 0) % step :: step]

?

@ben-bou ben-bou Feb 23, 2022

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good point.
So what you're doing by handling the negative values by hand is actually everything indexing the range_proxy would do, right? So there is no reason to sidestep into using a range and we can directly use

local_slice = slice(local_start + max(offset - start, 0) % step, local_stop, step)

Comment thread heat/core/dndarray.py
range_proxy = range(self.lshape[split])
local_inds = range_proxy[start - offset : stop - offset] # only works if stop - offset > 0
local_inds = local_inds[max(offset - start, 0) % step :: step]
if len(local_inds) and stop > offset:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If I got local_stop and local_start right, we probably don't need the stop > offset check any more

@ben-bou ben-bou Feb 23, 2022

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we also don't need the local_inds anymore. Checking if the local slice is empty can be done with
if local_stop > 0:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The condition for empty slice is local_slice.start > local_slice.stop I think.

@ben-bou ben-bou Feb 23, 2022

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

One second, is checking for an empty slice needed at all?

In [90]: torch.ones((3, 4, 5))[:, 5:3, :].shape                                                          
Out[90]: torch.Size([3, 0, 5])
In [91]: torch.ones((3, 4, 5))[:, 0:0, :].shape                                                          
Out[91]: torch.Size([3, 0, 5])

This is exactly what the larray of the empty rank should be.
So if that works, is knowing the output_shape beforehand still needed?
At least in the case where key[split] is a slice we can even construct the resulting lshape_map without communication by calculating local_start and local_stop for every offset.

@ben-bou ben-bou Feb 23, 2022

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

And if key[split] is a scalar, the split dimension is collapsed and we also know the output-shape and all the lshapes trivially.

Lastly, if advanced indexing is used, the output shape and lshapes are dependent on the key, at least if it's a dndarray.

So if I'm not mistaken, this allows us to get rid of the torch_proxy.

Comment thread heat/core/dndarray.py
proxy_key = list(key)
if advanced_indexing:
proxy_key = list(key)
for i, k in reversed(enumerate(key)):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

reversed(enumerate) returns TypeError

@ClaudiaComito

Copy link
Copy Markdown
Member

Superseded by #938 , thanks @ben-bou for your amazing work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants