SRFI 231 has array-assign!, which seems similar to math/array's array-indexes-set! and array-slice-set!. I'm wondering whether the specification of array-assign! should be changed to determine the order in which elements are assigned, and I'm studying array-indexes-set! and array-slice-set! for ideas.
Common example algorithms for array libraries are the Jacobi and Gauss-Seidel iterations for approximately solving Laplace's equation, see, e.g., Gauss–Seidel method - Wikipedia . The basic iteration is the same for both:
(define (jacobi-iteration! OUT IN)
;; If IN and OUT are different, this is a Jacobi iteration for Laplace's equation.
;; If IN and OUT are the same, this is a Gauss-Seidel iteration.
(let ((interior (interval-dilate (array-domain IN) '#(1 1) '#(-1 -1))))
(array-assign!
(array-extract OUT interior)
(apply array-map
(lambda (left right up down)
(fl* 0.25 (fl+ left right up down)))
(map (lambda (translation)
(array-extract (array-translate IN translation) interior))
'(#(0 -1) #(0 1) #(-1 0) #(1 0)))))))
For Jacobi's method, the order that elements are computed and assigned to the interior of OUT doesn't matter, because IN and OUT don't share elements, but for the Gauss-Seidel method the ordering makes a difference.
Often, algorithms assume that elements are computed and assigned to the interior of OUT in lexicographical (row-major) order, but that isn't (yet) guaranteed by array-assign!. For example, for so-called symmetric Gauss-Seidel, each iteration reverses the order in which you compute new elements, as in
(define (gauss-seidel-2d X iterations tolerance exact-answer #!optional (symmetric? #t))
(let* ((X_0 (array-copy X)))
(let loop ((n 0)
(X_k X_0))
(jacobi-iteration! X_k X_k) ;; forward gauss-seidel
(if symmetric?
(let ((reversed (array-reverse X_k)))
(jacobi-iteration! reversed reversed))) ;; backward gauss-seidel
(if (and (fxzero? (fxremainder n 10))
(or (fx>= n iterations)
(fl< (frobenius exact-answer X_k) tolerance)))
(begin
(pretty-print (list gauss-seidel-iterations: n error: (frobenius exact-answer X_k) symmetric?: symmetric?))
X_k)
(loop (fx+ n 1) X_k)))))
In searching the math/array documentation, I see for array-indexes-set! and array-slice-set! (which appear to be similar to SRFI 231's array-assign!, please correct me if I'm wrong) the only mention of order of computation/assignment is something like
So I'm wondering whether the order of assignment is specified by the two math/array routines.
As for array-assign! in the SRFI followup, I can see the following considerations:
- Not specifying the order in which elements are assigned to the destination array allows some hypothetical theoretical future implementation to be faster, by exploiting parallelism, the run-time layout of arrays, etc.
- Specifying the order in which elements are assigned to the destination array simplifies the specification of some "bulk" array algorithms, where you don't have to drop down to index loops of the underlying
intervaloperations to achieve your goals.
The sample implementation is slow compared to hand-written loops in C or Fortran, and I think it's unlikely that anyone will write a high-performance implementation in, e.g., the style of PetaLisp GitHub - marcoheisig/Petalisp: Elegant High-Performance Computing (which I admire very much, but it appears that Marco has gone on to other things).
So the question seems to be: Should the library be designed to allow precise specification of algorithms without dropping down into loops (or the low-level interval-for-each interface), limiting future hypothetical high-performance implementations, or should it have imprecise specifications of the order of some computations, which makes specifying some algorithms more difficult, but allows more flexibility in implementations?
Brad