Skip to content

Conversation

@goldsborough
Copy link
Contributor

PyTorch has torch.tensor, which is a convenient way of creating a tensor from a list of values. This PR adds the equivalent to ATen/C++.

It kind of depends on #7869, but putting it here already. The most important file is aten/src/ATen/ScalarList.h which can already be reviewed.

I've also named it values for now because factory methods still dispatch over Type, and Type::tensor already exists. After the other PR lands I'll rename it tensor.

@colesbury @gchanan @ezyang

Copy link
Member

@colesbury colesbury left a comment

Choose a reason for hiding this comment

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

I'm not a fan of the new ScalarList type, since it forces a copy of it's inputs and it's only use seems to be to construct tensors.

Can you use at::ArrayRef<int64_t> and at::ArrayRef<double> directly instead? These tensor(...) overloads seem like they should be added directly to at:: namespace and not go thorough generated code. I don't think they'll be useful in Python, for tracing/JIT, or for autograd.

@goldsborough
Copy link
Contributor Author

Those are both good ideas

@goldsborough
Copy link
Contributor Author

goldsborough commented Jun 14, 2018

@colesbury I ran into a lot of trouble around ambiguous constructors for ArrayRef. Having two functions with ArrayRef<double> and ArrayRef<int> causes {1, 2, 3} to be ambiguous because it doesn't know whether to call ArrayRef<double>({1, 2, 3}) or ArrayRef<int>({1, 2, 3}). Of course, only one would be preferred during overload resolution, but the compiler can't think that far (the ambiguity comes from knowing whether to invoke the constructor of ArrayRef<double> or ArrayRef<int>.

Instead I've made the methods templated. This way, at least you can write at::tensor<int64_t>({1, 2, 3}). It's still not as nice as ScalarList, which solved the whole ambiguity problem, but I guess it doesn't incur a copy and is less code ...

@goldsborough
Copy link
Contributor Author

@pytorchbot retest this please

This comment was marked as off-topic.

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.

ok

@colesbury
Copy link
Member

colesbury commented Jun 15, 2018

This deserves some tests. Currently, if you call an unsupported method you'll get a linker error which isn't ideal. In my local testing, at::native::tensor({1, 2, 3, 4}) also did not work.

Here are some use cases that I think should work:

std::vector<int32_t> data(...);
at::tensor(data);

at::tensor({1, 2, 3, 4})
at::tensor({1.0, 2.5, 3.5})

I would suggest the following to avoid the ambiguous overloads:

tensor(ArrayRef<int64_t> values)
tensor(ArrayRef<int32_t> values)
tensor(ArrayRef<int16_t> values)
tensor(ArrayRef<int8_t> values)
tensor(ArrayRef<uint8_t> values)
tensor(ArrayRef<float> values)
tensor(ArrayRef<double> values)
tensor(ArrayRef<Half> values)

tensor(initializer_list<int> values)
tensor(initializer_list<float> values)
tensor(initializer_list<double> values)

I would suggest that the ArrayRef<T> overloads preserve the type T of their values if no dtype is given. For example, the int32_t overload should create an int tensor, not a long tensor.

I would suggest that the initializer_list overloads default to kLong for <int> and the default tensor type for float and double. Since the default tensor type isn't in ATen yet, you could just assume kFloat for now.

@goldsborough goldsborough force-pushed the tensor branch 2 times, most recently from 22ef21e to 763eed8 Compare June 18, 2018 23:45
@goldsborough goldsborough requested a review from ebetica as a code owner June 18, 2018 23:45
@goldsborough
Copy link
Contributor Author

@colesbury @ezyang please take another look. I used the FORALL_SCALAR_TYPES macro and it works very well now. I'm omitting half support because it would require a more complicated separate implementation, and we don't need it urgently.

Added lots of tests.

My local test case:

std::vector<int> v = {1, 2, 3, 5, 6};
  std::vector<float> w = {1, 2, 3, 5, 6};
  std::cout << at::tensor(123.456) + 0.5 << std::endl;
  std::cout << at::tensor({1, 2, 3}) + 0.5 << std::endl;
  std::cout << at::tensor(v) + 0.5 << std::endl;
  std::cout << at::tensor(w) + 0.5 << std::endl;
  std::cout << at::tensor({1.0, 2.0, 3.0}) + 0.5 << std::endl;
  std::cout << at::tensor({1.5, 2.6, 3.7}, at::kInt) + 0.5 << std::endl;
  std::cout << at::tensor(123, at::kByte) + 0.5 << std::endl;

prints

 123.9560
[ CPUDoubleTensor{1} ]
 1
 2
 3
[ CPUIntTensor{3} ]
 1
 2
 3
 5
 6
[ CPUIntTensor{5} ]
 1.5000
 2.5000
 3.5000
 5.5000
 6.5000
[ CPUFloatTensor{5} ]
 1.5000
 2.5000
 3.5000
[ CPUDoubleTensor{3} ]
 1
 2
 3
[ CPUIntTensor{3} ]
 123
[ CPUByteTensor{1} ]

@goldsborough
Copy link
Contributor Author

@pytorchbot retest this please

@goldsborough goldsborough force-pushed the tensor branch 3 times, most recently from 07f1e85 to 687824e Compare June 20, 2018 04:29
@ezyang
Copy link
Contributor

ezyang commented Jun 20, 2018

Hey @goldsborough, is there any particular reason you didn't go the "add overloads for initializer list" route?

@goldsborough
Copy link
Contributor Author

Isn't that the effectively what I did? There's one overload of initializer_list per scalar type. This way there is 0 ambiguity and everything works. Or did you mean something else?

@ezyang
Copy link
Contributor

ezyang commented Jun 20, 2018

So, I got deceived by my C++ heuristic that says that if you can do it with overloads, don't use a template; I couldn't actually construct an example where it mattered if tensor was templated or not. So... carry on!

@goldsborough goldsborough merged commit 9335885 into pytorch:master Jun 20, 2018
@goldsborough goldsborough deleted the tensor branch June 20, 2018 18:44
Tensor tensor(ArrayRef<T> values, const TensorOptions& options) {
auto result = at::empty(values.size(), options);
for (size_t i = 0; i < values.size(); ++i) {
result[i] = values[i];

This comment was marked as off-topic.

This comment was marked as off-topic.

TEST_CASE("Tensor/ContainsCorrectValueForSingleValue") {
auto tensor = at::tensor(123);
REQUIRE(tensor.numel() == 1);
REQUIRE(tensor.dtype() == at::kInt);

This comment was marked as off-topic.

}

#define TENSOR(T, _1, _2) \
Tensor tensor(ArrayRef<T> values, const TensorOptions& options) { \

This comment was marked as off-topic.

This comment was marked as off-topic.

petrex pushed a commit to petrex/pytorch that referenced this pull request Jun 20, 2018
* upstream/master: (92 commits)
  more formatting (pytorch#8701)
  Fix pytorch#8692 (pytorch#8699)
  Create captured inputs recursively for loop to resolve loop-carried dependencies across nested blocks (pytorch#8345)
  Shard test_nn to reduce runtime for each test target (pytorch#8678)
  Create at::tensor (pytorch#8475)
  Clarify mp note about sharing a tensor's grad field. (pytorch#8688)
  Add owner rule for cpp_extension.py (pytorch#8700)
  fix formatting in :math: in fold docstring (pytorch#8696)
  Some 0-sized dimension support, port catArray away from resizeLegacy. (pytorch#8666)
  Implement flatten function (pytorch#8578)
  Created Tensor::to functions (pytorch#8643)
  Add a warning in gradcheck if inputs precision < float64 (pytorch#8663)
  Fix parsing of floating point defaults in python_arg_parser (pytorch#8681)
  Export ProcessGroupGloo options to Python (pytorch#8664)
  Fix build error in pybind_state_ideep (pytorch#8684)
  Compatibility: write nDimension/_nDimension corresponding to dim()/_dim(). (pytorch#8676)
  Improve win-build.sh for local build (pytorch#8674)
  don't do unnecessary copies for bernoulli_ (pytorch#8682)
  Use parallel if get_num_threads 0 (pytorch#8677)
  Fix serialization for Parameters (pytorch#8633)
  ...
petrex pushed a commit to petrex/pytorch that referenced this pull request Jun 21, 2018
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