Skip to content

Conversation

@theweiho
Copy link
Contributor

@theweiho theweiho commented Mar 1, 2018

Questions/possible future works:

  • How to template-ize to extend support beyond LongTensor?
  • How to check if autograd works (and if not, how to add explicit gradient)?
  • CUDA support?

Testing command:
DEBUG=1 NO_CUDA=1 MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py build && DEBUG=1 NO_CUDA=1 MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py develop && python3 test/test_torch.py

Commands to preview generated documentations:
cd docs
pip install -r requirements.txt
make html

@theweiho
Copy link
Contributor Author

theweiho commented Mar 1, 2018

@gchanan
Copy link
Contributor

gchanan commented Mar 1, 2018

On your questions:

  1. Look at the dispatch functions in Dispatch.h
  2. You can write a test in test/test_autograd.py; if you need an explicit gradient you can add it in derivatives.yaml
  3. You can use a dispatch: declaration in native_functions.yaml; see CUDA: examples in that file.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

@goldsborough
Copy link
Contributor

goldsborough commented Mar 1, 2018

Look great, had mostly nits.
For generic types, the way you'll want to do this is move all your logic into a separate template function (e.g. in an anonymous namespace above):

template<typename scalar_t>
void unique_op(at::Tensor ...) {
// use scalar_t instead of int64_t here
}

then in the current unique function, something like:

AT_DISPATCH_ALL_TYPES(self.type(), "unique", [&] {
  unique_op<scalar_>t(tensors...);
});

See here for an example.

EDIT: This is a better example for CPU: https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/native/TensorCompare.cpp

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

Copy link
Collaborator

@ssnl ssnl left a comment

Choose a reason for hiding this comment

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

This at its current state still needs more work:

  1. Documentation at _torch_docs.py and _tensor_docs.py
  2. Dispatch to other types can be in a later PR, but this will fail with cuda long tensor (probably a segfault). Can you add a cuda version as well?

This comment was marked as off-topic.

This comment was marked as off-topic.

@goldsborough
Copy link
Contributor

I think you accidentally merged instead of rebasing

@theweiho
Copy link
Contributor Author

theweiho commented Mar 2, 2018

Fixed merge. Documentations added and exposed unique as function as well. In order to do so, it turns out using IndexTensor in native_functions.yaml makes it so that the fn is not exposed as torch.unique(), but only as torch.autograd.Variable.unique() (thanks @gchanan!) - so changed to generic Tensor and also added dispatch to other types.

On further thought, realized that there's not really a sensible way to do gradients for this, so explicitly declared it as not implemented.

Regarding CUDA support, @ssnl - I pinged you, but it seems you're off the grid? In any case, I'd probably prefer to defer that - so is there a way to explicitly warn/catch CUDA usage for more graceful failure than segfault?

#include <unordered_map>
#include <unordered_set>

#include <iostream>

This comment was marked as off-topic.

This comment was marked as off-topic.

if (sorted) {
std::vector<scalar_t> vec(set.begin(), set.end());
std::sort(vec.begin(), vec.end());
std::copy(vec.begin(), vec.end(), output->data<scalar_t>());

This comment was marked as off-topic.

This comment was marked as off-topic.

self.assertEqual(empty_inverse, x_inverse)

x_unique, x_inverse = torch.autograd.Variable.unique(
x, sorted=True, return_inverse=True)

This comment was marked as off-topic.

This comment was marked as off-topic.

return_inverse=True,
)
self.assertEqual(torch.ByteTensor([7, 42, 128, 133]), byte_unique)
self.assertEqual(torch.LongTensor([3, 0, 0, 0, 1, 2]), byte_inverse)

This comment was marked as off-topic.

This comment was marked as off-topic.

- **output** (*Tensor*): the list of unique scalar elements
- **inverse_indices** (*Tensor*): the indices (same shape as input)
for where elements in the original input map to in the output
if ``return_inverse`` is ``True``; otherwise, an empty tensor.

This comment was marked as off-topic.

This comment was marked as off-topic.

@fmassa
Copy link
Member

fmassa commented Mar 2, 2018

@theweiho about gradients for unique, can't we consider it's gradient as similar to indexing only the first unique elements of a tensor?

@theweiho
Copy link
Contributor Author

theweiho commented Mar 2, 2018

@ssnl - is there some way to use Travis CI (or something else) to test the CUDA part of the code you suggested?

@fmassa - that was the way we were considering doing, but not sure that totally makes sense. Attributing the gradient to the first unique element seems a bit arbitrary? (Considering all occurrences of that element "contributes" to the unique equally) I also wasn't sure what a use case for the unique gradient would be, so figured it may make sense to defer it until someone has concrete requirements.

const bool return_inverse,
Tensor* output,
Tensor* inverse_indices) {
set_type<scalar_t> set(

This comment was marked as off-topic.

This comment was marked as off-topic.


if (sorted) {
AT_DISPATCH_ALL_TYPES(self.type(), "unique", [&] {
_unique_cpu_template<std::set, scalar_t>(

This comment was marked as off-topic.

This comment was marked as off-topic.

throw std::runtime_error(
"unique is currently CPU-only, and lacks CUDA support. "
"Pull requests welcome!");
return std::make_tuple(self.type().tensor({0}), self.type().tensor({0}));

This comment was marked as off-topic.

This comment was marked as off-topic.

@theweiho theweiho changed the title Add 1-D unique op for LongTensor Add per-element unique op Mar 2, 2018
@theweiho theweiho changed the title Add per-element unique op Add per-element unique op for CPU Mar 2, 2018
}
for (int i = 0; i < input.numel(); ++i) {
inverse_indices.data<int64_t>()[i] =
inverse_map[input.data<scalar_t>()[i]];

This comment was marked as off-topic.

This comment was marked as off-topic.

const Tensor& input = self.contiguous();
set_type<scalar_t> set(
input.data<scalar_t>(), input.data<scalar_t>() + input.numel());
Tensor output = input.type().tensor({static_cast<long long>(set.size())});

This comment was marked as off-topic.

This comment was marked as off-topic.

expected_unique.tolist(), sorted(x_unique.tolist()))
self.assertEqual(empty_inverse, x_inverse)

x_unique, x_inverse = x.unique(return_inverse=True)

This comment was marked as off-topic.

This comment was marked as off-topic.

#include "ATen/ATen.h"

#include <tuple>
A

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

@colesbury
Copy link
Member

@pytorchbot add to whitelist

@ezyang
Copy link
Contributor

ezyang commented Mar 6, 2018

@pytorchbot retest this please

2 similar comments
@ezyang
Copy link
Contributor

ezyang commented Mar 6, 2018

@pytorchbot retest this please

@ezyang
Copy link
Contributor

ezyang commented Mar 6, 2018

@pytorchbot retest this please

x_unique, x_inverse = x.unique(return_inverse=True)
self.assertEqual(
expected_unique.tolist(), sorted(x_unique.tolist()))
self.assertEqual(expected_inverse.numel(), x_inverse.numel())

This comment was marked as off-topic.

This comment was marked as off-topic.

ezyang
ezyang previously requested changes Mar 7, 2018
Copy link
Contributor

@ezyang ezyang left a comment

Choose a reason for hiding this comment

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

I don't think the tests in the hashing case are correct.

@ezyang ezyang dismissed their stale review March 7, 2018 23:08

it's testing numel, not the array

@ezyang ezyang merged commit c2721ab into pytorch:master Mar 7, 2018
@theweiho theweiho deleted the unique-op branch March 7, 2018 23:20
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.

8 participants