Summary:
`torch.linspace(0, 1, 1)` fails with `RuntimeError: invalid argument 3: invalid number of points at ../aten/src/TH/generic/THTensorMoreMath.cpp:2119`, while `np.linspace(0, 1, 1)` works fine.
Looking at the code, there is even a comment by gchanan asking: "NumPy allows you to pass different points even if n <= 1 -- should we?"
I would say "yes". Currently, I would need to handle the case of `steps == 1` or `steps == 0` separately, making sure to change the `end` when calling `torch.linspace`. This is impractical. If we support `start != end`, there are two possibilities for the result: Either we ensure the first value in the resulting sequence always equals `start`, or we ensure the last value in the resulting sequence always equals `end`. Numpy chose the former, which also allows it to support a boolean `endpoint` flag. I'd say we should follow numpy.
This PR adapts `linspace` and `logspace` to mimic the behavior of numpy, adapts the tests accordingly, and extends the docstrings to make clear what happens when passing `steps=1`.
If you decide against this PR, the error message should become explicit about what I did wrong, and the documentation should be extended to mention this restriction.
Pull Request resolved: https://github.com/pytorch/pytorch/pull/14748
Differential Revision:
D13356136
Pulled By: ezyang
fbshipit-source-id:
db85b8f0a98a5e24b3acd766132ab71c91794a82
{
scalar_t i = 0;
- // NumPy allows you to pass different points even if n <= 1 -- should we?
- THArgCheck(n > 1 || ((n == 0 || n == 1) && (a == b)), 3, "invalid number of points");
+ THArgCheck((n >= 0), 3, "number of points must be non-negative");
if (THTensor_(nElement)(r_) != n) {
THTensor_(resize1d)(r_, n);
{
scalar_t i = 0;
- // NumPy allows you to pass different points even if n <= 1 -- should we?
- THArgCheck(n > 1 || ((n == 0 || n == 1) && (a == b)), 3, "invalid number of points");
+ THArgCheck((n >= 0), 3, "number of points must be non-negative");
if (THTensor_(nElement)(r_) != n) {
THTensor_(resize1d)(r_, n);
void THCTensor_(linspace)(THCState *state, THCTensor *r_, scalar_t a, scalar_t b, int64_t n) {
THCAssertSameGPU(THCTensor_(checkGPU)(state, 1, r_));
- // NumPy allows you to pass different points even if n <= 1 -- should we?
- THArgCheck(n > 1 || ((n == 0 || n == 1) && (a == b)), 3, "invalid number of points");
+ THArgCheck((n >= 0), 3, "number of points must be non-negative");
if (THCTensor_(nElement)(state, r_) != n) THCTensor_(resize1d)(state, r_, n);
if (n == 0) {
// skip
void THCTensor_(logspace)(THCState *state, THCTensor *r_, scalar_t a, scalar_t b, int64_t n) {
THCAssertSameGPU(THCTensor_(checkGPU)(state, 1, r_));
- // NumPy allows you to pass different points even if n <= 1 -- should we?
- THArgCheck(n > 1 || ((n == 0 || n == 1) && (a == b)), 3, "invalid number of points");
+ THArgCheck((n >= 0), 3, "number of points must be non-negative");
if (THCTensor_(nElement)(state, r_) != n) THCTensor_(resize1d)(state, r_, n);
if (n == 0) {
// skip
res2 = torch.Tensor()
torch.linspace(_from, to, 137, out=res2)
self.assertEqual(res1, res2, 0)
- self.assertRaises(RuntimeError, lambda: torch.linspace(0, 1, 1))
- self.assertEqual(torch.linspace(0, 0, 1), torch.zeros(1), 0)
+ self.assertRaises(RuntimeError, lambda: torch.linspace(0, 1, -1))
+ self.assertEqual(torch.linspace(0, 1, 1), torch.zeros(1), 0)
# Check linspace for generating with start > end.
self.assertEqual(torch.linspace(2, 0, 3), torch.Tensor((2, 1, 0)), 0)
res2 = torch.Tensor()
torch.logspace(_from, to, 137, out=res2)
self.assertEqual(res1, res2, 0)
- self.assertRaises(RuntimeError, lambda: torch.logspace(0, 1, 1))
- self.assertEqual(torch.logspace(0, 0, 1), torch.ones(1), 0)
+ self.assertRaises(RuntimeError, lambda: torch.logspace(0, 1, -1))
+ self.assertEqual(torch.logspace(0, 1, 1), torch.ones(1), 0)
# Check logspace_ for generating with start > end.
self.assertEqual(torch.logspace(1, 0, 2), torch.Tensor((10, 1)), 0)
tensor([-10., -5., 0., 5., 10.])
>>> torch.linspace(start=-10, end=10, steps=5)
tensor([-10., -5., 0., 5., 10.])
+ >>> torch.linspace(start=-10, end=10, steps=1)
+ tensor([-10.])
""".format(**factory_common_args))
add_docstr(torch.log,
tensor([ 1.0000e-10, 1.0000e-05, 1.0000e+00, 1.0000e+05, 1.0000e+10])
>>> torch.logspace(start=0.1, end=1.0, steps=5)
tensor([ 1.2589, 2.1135, 3.5481, 5.9566, 10.0000])
+ >>> torch.logspace(start=0.1, end=1.0, steps=1)
+ tensor([1.2589])
""".format(**factory_common_args))
add_docstr(torch.logsumexp,