Implications of array broadcasting Bradley Lucier (24 Oct 2024 18:55 UTC)
Re: Implications of array broadcasting Alex Shinn (28 Oct 2024 06:17 UTC)
Re: Implications of array broadcasting Bradley Lucier (30 Oct 2024 23:57 UTC)
Re: Implications of array broadcasting Alex Shinn (31 Oct 2024 13:15 UTC)
Re: Implications of array broadcasting Bradley Lucier (02 Nov 2024 04:24 UTC)
Re: Implications of array broadcasting Alex Shinn (03 Nov 2024 22:21 UTC)
Re: Implications of array broadcasting Bradley Lucier (05 Nov 2024 03:08 UTC)
Re: Implications of array broadcasting Alex Shinn (07 Nov 2024 00:27 UTC)
Re: Implications of array broadcasting Bradley Lucier (07 Nov 2024 20:29 UTC)
Re: Implications of array broadcasting Alex Shinn (08 Nov 2024 00:01 UTC)
Re: Implications of array broadcasting Bradley Lucier (08 Nov 2024 20:51 UTC)
Re: Implications of array broadcasting Alex Shinn (08 Nov 2024 22:54 UTC)

Implications of array broadcasting Bradley Lucier 24 Oct 2024 18:55 UTC
I've been thinking about how array broadcasting in the Python sense
could be added to this library.  This issue clarified some thoughts,
which I'd like to summarize here (mainly so I can stop thinking about
them for a while).

1.  Broadcasting is a non-1-1 transformation on arrays (for nontrivial
broadcasts).  In general, the example of broadcasting shows that non-1-1
transforms can be useful, so this statement in the SRFI:
================
We also require that our affine maps be one-to-one, so that if i⃗ ≠j⃗
then T(i⃗ )≠T(j⃗ ). Without this property, modifying the i⃗ th component
of A would cause the j⃗ th component to change.
================
should be loosened.  Note that the justification of one-to-one maps is
array mutation, so non-1-1 maps should be allowed for immutable arrays.

2.  Because an array's setter is reified, so that it can be stored in
variables, etc., the procedure array-freeze! in general is
meaningless---any existing reference to the setter is unmodified.  So I
think array-freeze! should be removed.

3.  The specification of array->list says
================
It is guaranteed that (array-getter array) is called precisely once for
each multi-index in (array-domain array) in lexicographical order.
================
I specified it this way so that one could rely on array->list,
array-copy, etc., to "do the right thing" on generalized arrays whose
getters have intrinsic side effects.  A big example for me is
generalized arrays that read successive elements from a file when
converted to a list or copied to a specialized array.

However, if such a generalized array is nontrivially broadcast before
being copied, then copying the broadcast result will cause the getter of
the original array to be called multiple times for each original index,
which can obviously cause trouble when reading from a file, etc.

So I think broadcasting should be limited to specialized arrays, in the
same way that "sharing" arrays using an arbitrary affine transform is
limited to specialized arrays.

So it should be specialized-array-broadcast, with nontrivial broadcasts
limited to immutable arrays.

In this way we can implement array broadcasting without array copying,
which is the correct way to do it in my opinion.  In contrast, the NumPy
manual says
===============
However, there are cases when broadcasting uses unnecessarily large
amounts of memory for a particular algorithm.
===============
I don't know whether this means that NumPy sometimes allocates memory
for the results of array broadcasting, but I don't want to do that.

4.  For practical reasons, broadcasting should be limited to arrays with
zero lower bounds.  Chibi has the routine array-to-origin, which
translates a given array to have all zero lower bounds.  It's useful, it
should be added (even though it's "just" another convenience procedure).

5.  If the transform passed to specialized-array-share is not
one-to-one, then one should require that the array argument to
specialized-array-share be immutable.

Over the past few weeks I've been looking for algorithms to determine
whether an affine map on an interval of multi-indices (in the SRFI 231
sense) is one-to-one.

The specified interval is an important part of this question.  For
example, if A is the matrix (10 1) and the interval is
(0,100]\times[0,8), then the mapping x->Ax is one-to-one on that
interval, even though A is not one-to-one on all pairs of integers (or
on $R^2$, either).

I've reduced the problem (perhaps incorrectly) to an integer quadratic
programming problem with linear constraints, which appears in general to
be NP-hard.  The derivation is attached as a PDF file.

So if that's true, it's not something you'd add to error checking in
specialized-array-share.

In the special case of array broadcasting, one can easily determine
whether the broadcast is nontrivial (increasing the width of an interval
axis), so one knows to check whether the associated array argument is
immutable.

But in general it seems difficult to check.  (Although I'd love to be
proved wrong.)

So the specification of specialized-array-share should say something like:
=================
It is an error if the transform argument is not one-to-one on the
new-domain argument.
=================
(I understand that the trend is to say "Program behavior is undefined if
...", but the rest of the document stays "It is an error if ...")

7.  The document discussing how to test whether an integer-valued affine
mapping is one-to-one on a specified interval is attached below.

Brad