Skip to content

Conversation

@yf225
Copy link
Contributor

@yf225 yf225 commented May 9, 2019

After the Variable/Tensor merge, there is no guarantee that indices and values passed into the sparse tensor constructor don't contain AutogradMeta. However, we want to maintain the existing invariant that indices_ and values_ of a sparse tensor don't contain AutogradMeta, and to achieve this we need do shallow-copy in the sparse tensor constructor.

Note that this is BC-breaking for code that changes the sizes / strides of the indices or values tensor after it's used to create a sparse tensor. In current master, such changes will be reflected in the sparse tensor and break sparse tensor invariants. After this PR, those changes will not be reflected in the sparse tensor, and thus the sparse tensor invariants are always preserved. Specifically, running in-place size/stride-changing ops such as resize_ / resize_as_ / as_strided_ / set_ / transpose_ on the original values tensor will not update the sparse tensor's values_. For example:

# Calling resize_ on non-requires-grad value tensor
i2 = torch.zeros([1, 1])
v2 = torch.ones([1, 2, 3])
t2 = torch.sparse_coo_tensor(i2, v2, torch.Size([2, 2, 3]))
v2.resize_(4, 5)
t2.coalesce().values().size()
# On current master, this throws "indices and values must have same nnz, but got nnz from indices: 1, nnz from values: 4", because resizing the original value tensor affects `values_` of the sparse tensor.
# After this PR, this prints "torch.Size([1, 2, 3])", which means resizing the original value tensor doesn't affect `values_` of the sparse tensor.

@pytorchbot pytorchbot added module: operators module: sparse Related to torch.sparse labels May 9, 2019
@yf225 yf225 removed module: operators module: sparse Related to torch.sparse labels May 9, 2019
@yf225 yf225 force-pushed the sparse_shallow_copy branch from 397985c to d7a0f9f Compare May 9, 2019 18:49
@yf225
Copy link
Contributor Author

yf225 commented May 9, 2019

@pytorchbot rebase this please

@yf225 yf225 changed the title [DO NOT MERGE] Test if we can shallow-copy indices and values in sparse tensor ctor Shallow-copy indices and values in sparse tensor ctor May 10, 2019
@yf225 yf225 requested a review from gchanan May 10, 2019 18:07
@gchanan
Copy link
Contributor

gchanan commented May 10, 2019

This is BC breaking and it's never mentioned!

@yf225 yf225 changed the title Shallow-copy indices and values in sparse tensor ctor [BC-breaking] Shallow-copy indices and values in sparse tensor ctor May 10, 2019
@yf225 yf225 changed the title [BC-breaking] Shallow-copy indices and values in sparse tensor ctor Shallow-copy indices and values in sparse tensor ctor May 10, 2019
@yf225 yf225 changed the title Shallow-copy indices and values in sparse tensor ctor [BC-breaking] Shallow-copy indices and values in sparse tensor ctor May 10, 2019
@yf225 yf225 mentioned this pull request May 11, 2019
22 tasks
@yf225
Copy link
Contributor Author

yf225 commented May 11, 2019

@pytorchbot rebase this please

@pytorchbot pytorchbot added module: operators module: sparse Related to torch.sparse labels May 11, 2019
@yf225
Copy link
Contributor Author

yf225 commented May 11, 2019

@pytorchbot rebase this please

Copy link
Contributor

Choose a reason for hiding this comment

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

please comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

what about the version counter? Can we please take a non-default argument to shallow_copy_and_detach for what to do with the version counter? It's troublesome that this API doesn't expose a crucial thing that needs to be considered.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@yf225 yf225 added the module: bc-breaking Related to a BC-breaking change label May 14, 2019
@yf225 yf225 force-pushed the sparse_shallow_copy branch from 811d7fa to 084cfe2 Compare May 16, 2019 04:07
/*allow_tensor_metadata_change=*/true));
auto values_shallow_copy = Tensor(values.unsafeGetTensorImpl()->shallow_copy_and_detach(
/*version_counter=*/values.unsafeGetTensorImpl()->version_counter(),
/*allow_tensor_metadata_change=*/true));
Copy link
Contributor Author

@yf225 yf225 May 16, 2019

Choose a reason for hiding this comment

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

We don't set allow_tensor_metadata_change to false here, because we need to be able to resize the sparse tensor in subsequent operations, e.g. in https://github.com/pytorch/pytorch/blob/master/test/test_sparse.py#L1852.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@yf225 has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@gchanan gchanan left a comment

Choose a reason for hiding this comment

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

can you add a test for the new behavior, please?

// NOTE: There is no guarantee that `indices` and `values` don't contain AutogradMeta. However,
// we want to maintain the invariant that `indices_` and `values_` of a sparse tensor don't
// contain AutogradMeta, and to achieve that we shallow-copy `indices` and `values` here.
auto indices_shallow_copy = LongTensor(indices.unsafeGetTensorImpl()->shallow_copy_and_detach(
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a little confused about what is going on with the version counter.

Given this change, I'd expect that when I call SparseTensor._values() I get a tensor that shares the version counter with the values that were originally passed in. But that doesn't seem to be the case, either in this PR or previously -- do you know why this is?

Example:

>>> ind=torch.tensor([[0],[1]]).add_(1).sub_(1)
>>> values = torch.tensor([1.]).add_(1).add_(1).sub_(1).sub_(1)
>>> c=torch.sparse_coo_tensor(ind, values)
>>> c._values()._version
0
>>> c._indices()._version
0
>>> c=torch.sparse_coo_tensor(ind, values).coalesce()
>>> c.values()._version
0
>>> c.indices()._version
0

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 current implementation of VariableType::_values() makes it so that c._values() shares the same version counter as c. I think this is the right semantics because updating c._values() should also bump the version counter of c.

A related issue is we need to make sure in-place update on the original value tensor values also updates the version counter of the sparse tensor's c's values_ tensor, and it should throw version mismatch error in the backward() operation if the original value tensor is changed (this requires saving the value tensor for backward in the sparse constructor). I added the task in #13638.

v = torch.ones([1, 2, 3])
t = torch.sparse_coo_tensor(i, v, torch.Size([2, 2, 3]))
v.transpose_(0, 1)
self.assertEqual(list(t.coalesce().values().size()), [1, 2, 3])
Copy link
Contributor

Choose a reason for hiding this comment

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

what about tests for changing 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.

Added tests for changing indices.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@yf225 has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@yf225 yf225 reopened this May 16, 2019
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@yf225 has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@yf225 has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@yf225 yf225 removed the merged label May 16, 2019
@pytorch pytorch deleted a comment from facebook-github-bot May 16, 2019
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@yf225 has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@yf225 yf225 reopened this May 16, 2019
@yf225 yf225 closed this May 16, 2019
facebook-github-bot pushed a commit that referenced this pull request May 17, 2019
Summary:
(Reopens #20330 and fixes test error.)

After the Variable/Tensor merge, there is no guarantee that `indices` and `values` passed into the sparse tensor constructor don't contain AutogradMeta. However, we want to maintain the existing invariant that `indices_` and `values_` of a sparse tensor don't contain AutogradMeta, and to achieve this we need do shallow-copy in the sparse tensor constructor.

Note that this is BC-breaking for code that changes the sizes / strides of the indices or values tensor after it's used to create a sparse tensor. In current master, such changes will be reflected in the sparse tensor and break sparse tensor invariants. After this PR, those changes will not be reflected in the sparse tensor, and thus the sparse tensor invariants are always preserved. Specifically, running in-place size/stride-changing ops such as `resize_` / `resize_as_` / `as_strided_` / `set_` / `transpose_` on the original values tensor will not update the sparse tensor's `values_`. For example:
```python
# Calling resize_ on non-requires-grad value tensor
i2 = torch.zeros([1, 1])
v2 = torch.ones([1, 2, 3])
t2 = torch.sparse_coo_tensor(i2, v2, torch.Size([2, 2, 3]))
v2.resize_(4, 5)
t2.coalesce().values().size()
# On current master, this throws "indices and values must have same nnz, but got nnz from indices: 1, nnz from values: 4", because resizing the original value tensor affects `values_` of the sparse tensor.
# After this PR, this prints "torch.Size([1, 2, 3])", which means resizing the original value tensor doesn't affect `values_` of the sparse tensor.
```
Pull Request resolved: #20614

Differential Revision: D15385811

Pulled By: yf225

fbshipit-source-id: e963fcf5e4097f8c881b56145f408565d97cf5c1
zdevito pushed a commit to zdevito/ATen that referenced this pull request May 17, 2019
Summary:
(Reopens pytorch/pytorch#20330 and fixes test error.)

After the Variable/Tensor merge, there is no guarantee that `indices` and `values` passed into the sparse tensor constructor don't contain AutogradMeta. However, we want to maintain the existing invariant that `indices_` and `values_` of a sparse tensor don't contain AutogradMeta, and to achieve this we need do shallow-copy in the sparse tensor constructor.

Note that this is BC-breaking for code that changes the sizes / strides of the indices or values tensor after it's used to create a sparse tensor. In current master, such changes will be reflected in the sparse tensor and break sparse tensor invariants. After this PR, those changes will not be reflected in the sparse tensor, and thus the sparse tensor invariants are always preserved. Specifically, running in-place size/stride-changing ops such as `resize_` / `resize_as_` / `as_strided_` / `set_` / `transpose_` on the original values tensor will not update the sparse tensor's `values_`. For example:
```python
# Calling resize_ on non-requires-grad value tensor
i2 = torch.zeros([1, 1])
v2 = torch.ones([1, 2, 3])
t2 = torch.sparse_coo_tensor(i2, v2, torch.Size([2, 2, 3]))
v2.resize_(4, 5)
t2.coalesce().values().size()
# On current master, this throws "indices and values must have same nnz, but got nnz from indices: 1, nnz from values: 4", because resizing the original value tensor affects `values_` of the sparse tensor.
# After this PR, this prints "torch.Size([1, 2, 3])", which means resizing the original value tensor doesn't affect `values_` of the sparse tensor.
```
Pull Request resolved: pytorch/pytorch#20614

Differential Revision: D15385811

Pulled By: yf225

fbshipit-source-id: e963fcf5e4097f8c881b56145f408565d97cf5c1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module: bc-breaking Related to a BC-breaking change module: sparse Related to torch.sparse

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants