The Broadcasting mechanism in NumPy is usually introduced by explaining the relevant rules.
Another perspective is to look at the topic from a mathematical viewpoint.
The initial example is the addition of a function and a constant.
The authors argue that this is not proper mathematical notation and resolve it by defining a broadcast function for the constant.
The result is the addition of two functions, the sine function and a constant function.
Which could also be written as
It is not quite clear to me what the intention of the authors was to leave out the function parameters. I assume they were omitted to make it clear that what being talked about is the mathematical objects rather than specific numerical outputs.
The question whether a 1×1 matrix should be considered a scalar is a similar topic with regard to notation.
A perspective that tries to bring together rigour and practicality:
A 1×1 matrix is not a scalar–it is an element of a matrix algebra. However, there is sometimes a meaningful way of treating a 1×1 matrix as though it were a scalar, hence in many contexts it is useful to treat such matrices as being "functionally equivalent" to scalars. It might be a little sloppy to do so, but a little bit of sloppiness is forgivable if it introduces no confusion or ambiguity, and if it aids brevity or clarity of exposition.
With the objection:
We can cast any real \(x\) to the constant function on \(\mathbb{R}\) whose value is always \(x\), but are reals and constant functions "functionally equivalent"? Well... no, a function can be evaluated at a point, while a real has no such feature.
And the remark:
Okay I guess the added words "in the right context", in the right context, would evade my objection. Namely, if we interpret it to mean that scalars and 1-by-1 matrices, when used in certain specific ways, can be treated as functionally equivalent, then I agree. =)
Handling of a 1x1 array in NumPy:
>>> from numpy import array, reshape
>>> a = reshape(array(1), (1,1))
>>> type(a)
<class 'numpy.ndarray'>
>>> a.shape
(1, 1)
>>> type(a[0,0])
<class 'numpy.int64'>
>>> type(a.item())
<class 'int'>
Similar for a zero-dimensional array:
>>> a = array(1)
>>> type(a)
<class 'numpy.ndarray'>
>>> a.shape
()
>>> type(a[()])
<class 'numpy.int64'>
>>> type(a.item())
<class 'int'>