1

The question has arisen from multidimensional arrays comparisons. I will describe those operations using the library NumPy. In case that library is not known by someone, please check this article in the Nature Magazine, which shows the importance of this library for the scientific community: [1]: https://www.nature.com/articles/s41586-020-2649-2

Comparing two multidimensional arrays with the same shape:

x = np.zeros((5,5,5))
y = np.zeros((5,5,5))
result = x == y

The result is an array with three dimensions and all values equals to 'True'

Comparing one multidimensional array with a scalar:

x = np.zeros((5,5,5))
result = x == 0

The result is an array with three dimensions and all values equals to 'True'

Comparing two multidimensional arrays with 'similar' shapes:

x = np.zeros((5,5,5))
y = np.zeros((5,5))
result = x == y

The result is an array with three dimensions and all values equals to 'True'

In this last case, as the array of three dimensions (5,5,5) can be composed of a set of arrays of two dimensions (5,5), the result is the same of the two previous cases. If we try to compare an three dimension array that cannot be 'composed' of two dimension arrays (e.g. (5,5,5) and (4,4)) the result is an error. In that sense, I was wondering if there is any concept that can defines that kind of composition.

  • 1
    "Matrix" refers specifically to a two-dimensional array. By an abuse-of-terminology, one might refer to a multi-dimensional array as a "tensor", but you would be generally frowned upon for doing so, as a tensor is a far-more-rich concept than a multi-dimensional array. – Paul Sinclair May 21 '21 at 20:42
  • "x = np.zeros((5,5,5)) result = x == 5 The result is an array with three dimensions and all values equals to 'True'" - Are you sure about that? Since your array has $0$ for all entries, I would think asking if they are equal to $5$ should return false. – Paul Sinclair May 21 '21 at 20:46
  • @PaulSinclair, you were right about the result of the second example. The result is actually an array with all values equal to 'False'. For the sake easy comparation among all results of the examples, I have change the scalar vaue to zero. – Alex Javarotti May 22 '21 at 11:55

2 Answers2

2

Where you see comparisons working with unequal dimensions in numpy, it is because there is an obvious way to copy the data in the array with lower dimensions to fill out a higher dimensional array. That is, you just extend the lower dimensional array by making it constant with respect to each of the higher dimensional indices.

This is not really a mathematical behavior. Rather it is a design choice by the programmers of Python. They wanted a forgiving language. So if there is some reasonable, predictable, way to not throw an error, they don't throw an error. But where there are multiple ways to avoid the error, and none of them are obviously preferable over all the others. Then you should still throw the error, as the user may have intended a different behavior and not understand that they didn't get it.

Mathematics itself is less forgiving. Comparing a 3D array to a 2D array is not valid. Period. They are two different things. You need to figure out what conversion of the 2D array to 3D makes sense for the problem at hand, make that conversion, and compare the two 3D arrays explicitly.

Paul Sinclair
  • 43,643
0

In my opinion language of tensors is very appropriate in the case presented. And it's entirely possible to express numpy's behaviour in terms of 'mathematical' jargon.

In case of tensors, numpy's len(a.shape) is called tensor rank and tensors are traditionally written as $A_{ijk}$ (or more/less lower indexes) that can take integer values. So numpy a[1,3,2] becomes $A_{132}$ in tensor notation.

(In the following paragraphs I'm following Einsteins convention when writing tensor products.)

Most natural way to make tensors of higher rank from tensors of lower rank is to use tensor product (in case of numpy it corresponds to np.outer) so when you want to compare $A_{ij}$ to $B_{ijk}$ you first have to extend $A$ somehow. Most natural choice is to use vector of ones to define extended variant of $A$, say $\bar{A}$ as

$$ \bar{A} = A_{ij} 1_k$$

This way numpy defines comparison of tensors of different rank as follows:

$$ (A_{ij} =_{np} B_{ijk}) \leftrightarrow_{def} (A_{ij}1_k = B_{ijk}) $$

Which clearly is a rank $3$ tensor of true/false entries (for each $i,j,k$ you plug in you'll get some true/false value).

One issue that someone might have with this language is that $1_k$ is a very specific vector and is dependent on the choice of coordinates, but hey, no one can forbid us from declaring one particular vector as special.

All of the above applies to higher dimensional arrays as well. When you start thinking in those terms, numpys np.einsum, which is exactly tensor contraction, will prove a very powerful tool ;)

Radost
  • 1,802
  • Contravariant indices on tensors are traditionally written as subscripts. However, covariant indices are traditionally written as superscripts. To convert one to the other, you have to contract with the metric. And it isn't just $1_k$ that depends on the coordinates, but so also are $A_{ij}$ and $B_{ijk}$. Just like linear operators can be represented by matrices for specific coordinate systems, but are something deeper, Tensors can be represented by multi-dimensional arrays for specific coordinate systems, but are themselves objects independent of those representations. – Paul Sinclair May 22 '21 at 13:29
  • Complementing what @Radost described, I ended up finding a term that is related to this kind of problem: "Broadcasting" link – Alex Javarotti May 22 '21 at 13:32