Skip to content

Tracker: semantic differences between numpy and pytorch #5

Open
@ev-br

Description

@ev-br

This is to track situations where eponymous functions in numpy and torch have different semantics, and to record decisions about these. Typically, the difference is views vs copies.

  • flip: np.flip creates a view, torch.flip creates a copy.

  • slicing with negative step is not supported:

In [34]: t = torch.arange(12).reshape((3, 4))

In [35]: t[:, ::-1]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [35], in <cell line: 1>()
----> 1 t[:, ::-1]

ValueError: step must be greater than zero

  • strides are value strides, not byte strides

  • if given an out= array of a wrong size, numpy raises on an attempt to broadcast, while pytorch emits a warning and resizes the out array

In [13]: torch.add(torch.ones(2), torch.ones(2), out=torch.empty(3))
<ipython-input-13-991dccd2c5b7>:1: UserWarning: An output with one or more elements was resized since it had shape [3], which does not match the required output shape [2]. This behavior is deprecated, and in a future PyTorch release outputs will not be resized unless they have zero elements. You can explicitly reuse an out tensor t by resizing it, inplace, to zero elements with t.resize_(0). (Triggered internally at /home/conda/feedstock_root/build_artifacts/pytorch-recipe_1670076225403/work/aten/src/ATen/native/Resize.cpp:17.)
  torch.add(torch.ones(2), torch.ones(2), out=torch.empty(3))
Out[13]: tensor([2., 2.])

In [14]: np.add(np.ones(2), np.ones(2), out=np.empty(3))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [14], in <cell line: 1>()
----> 1 np.add(np.ones(2), np.ones(2), out=np.empty(3))

ValueError: operands could not be broadcast together with shapes (2,) (2,) (3,) 

  • squeeze on axes of non-zero length is a noop in torch, while numpy raises
In [86]: t = torch.as_tensor([[1],[2],[3]])

In [87]: t.squeeze(0).shape
Out[87]: torch.Size([3, 1])

In [88]: t.numpy().squeeze(0).shape
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [88], in <cell line: 1>()
----> 1 t.numpy().squeeze(0).shape

ValueError: cannot select an axis to squeeze out which has size not equal to one

  • torch treats one-element arrays as scalars, while numpy requires that ndim == 0:
In [49]: operator.index(_np.array([1]))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [49], in <cell line: 1>()
----> 1 operator.index(_np.array([1]))

TypeError: only integer scalar arrays can be converted to a scalar index

In [50]: operator.index(torch.as_tensor([1]))
Out[50]: 1

In [51]: operator.index(torch.as_tensor([[1]]))
Out[51]: 1


  • Raising integers to negative powers is a ValueError in numpy and an integer zero in pytorch:
In [68]: torch.as_tensor(2)**torch.as_tensor(-1)
Out[68]: tensor(0)

In [69]: np.array(2)**np.array(-1)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [69], in <cell line: 1>()
----> 1 np.array(2)**np.array(-1)

ValueError: Integers to negative integer powers are not allowed.

In some cases NumPy and PyTorch raise different exception classes: numpy raises a ValueError or TypeError, while pytorch defaults to RuntimeError.

For instance (this one is straight from the numpy test suite) subtracting booleans :

In [12]: a = np.ones((), dtype=np.bool_); a - a
Input In [12], in <cell line: 1>()
----> 1 a = np.ones((), dtype=np.bool_); a - a

TypeError: numpy boolean subtract, the `-` operator, is not supported, use the bitwise_xor, the `^` operator, or the logical_xor function instead.

The matching pytorch call raises a RuntmeError.

For the time being, I'm adding a raises((TypeError, RuntimeError)) in the test suite, with an intention to revisit this later, and either translate exceptions everywhere, or just better document the difference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions