6.3 Broadcasting
It is often useful to apply a pointwise operation to two or more arrays in a many-to-one manner. Library support for this, which math/array provides, is called broadcasting.
> (define diag (diagonal-array 2 6 1 0))
> (array-shape diag) - : Indexes
'#(6 6)
> diag - : (Array (U Zero One))
(array
#[#[1 0 0 0 0 0]
#[0 1 0 0 0 0]
#[0 0 1 0 0 0]
#[0 0 0 1 0 0]
#[0 0 0 0 1 0]
#[0 0 0 0 0 1]])
> (array-shape (array 10)) - : Indexes
'#()
> (array* diag (array 10)) - : (Array Index)
(array
#[#[10 0 0 0 0 0]
#[0 10 0 0 0 0]
#[0 0 10 0 0 0]
#[0 0 0 10 0 0]
#[0 0 0 0 10 0]
#[0 0 0 0 0 10]])
> (array+ (array* diag (array 10)) (array #[0 1 2 3 4 5])) - : (Array Nonnegative-Fixnum)
(array
#[#[10 1 2 3 4 5]
#[0 11 2 3 4 5]
#[0 1 12 3 4 5]
#[0 1 2 13 4 5]
#[0 1 2 3 14 5]
#[0 1 2 3 4 15]])
6.3.1 Broadcasting Rules
The shorter shape is padded on the left with 1 until it is the same length as the longer shape.
For each axis k, dk and ek are compared. If dk = ek, the result axis is dk; if one axis is length 1, the result axis is the length of the other; otherwise fail.
Both arrays’ axes are stretched by (conceptually) copying the rows of axes with length 1.
es is padded to get #(1 3 3).
The result axis is derived from #(4 1 3) and #(1 3 3) to get #(4 3 3).
drr’s second axis is stretched to length 3, and err’s new first axis (which is length 1 by rule 1) is stretched to length 4.
> (define drr (array #[#[#["00" "01" "02"]] #[#["10" "11" "12"]] #[#["20" "21" "22"]] #[#["30" "31" "32"]]]))
> (array-shape drr) - : Indexes
'#(4 1 3)
> (define err (array #[#["aa" "ab" "ac"] #["ba" "bb" "bc"] #["ca" "cb" "cc"]]))
> (array-shape err) - : Indexes
'#(3 3)
> (define drr+err (array-map string-append drr err))
> (array-shape drr+err) - : Indexes
'#(4 3 3)
> drr+err - : (Array String)
(array
#[#[#["00aa" "01ab" "02ac"]
#["00ba" "01bb" "02bc"]
#["00ca" "01cb" "02cc"]]
#[#["10aa" "11ab" "12ac"]
#["10ba" "11bb" "12bc"]
#["10ca" "11cb" "12cc"]]
#[#["20aa" "21ab" "22ac"]
#["20ba" "21bb" "22bc"]
#["20ca" "21cb" "22cc"]]
#[#["30aa" "31ab" "32ac"]
#["30ba" "31bb" "32bc"]
#["30ca" "31cb" "32cc"]]])
> (define ds (array-shape-broadcast (list (array-shape drr) (array-shape err))))
> ds - : Indexes
'#(4 3 3)
> (array-broadcast drr ds) - : (Array String)
(array
#[#[#["00" "01" "02"]
#["00" "01" "02"]
#["00" "01" "02"]]
#[#["10" "11" "12"]
#["10" "11" "12"]
#["10" "11" "12"]]
#[#["20" "21" "22"]
#["20" "21" "22"]
#["20" "21" "22"]]
#[#["30" "31" "32"]
#["30" "31" "32"]
#["30" "31" "32"]]])
> (array-broadcast err ds) - : (Array String)
(array
#[#[#["aa" "ab" "ac"]
#["ba" "bb" "bc"]
#["ca" "cb" "cc"]]
#[#["aa" "ab" "ac"]
#["ba" "bb" "bc"]
#["ca" "cb" "cc"]]
#[#["aa" "ab" "ac"]
#["ba" "bb" "bc"]
#["ca" "cb" "cc"]]
#[#["aa" "ab" "ac"]
#["ba" "bb" "bc"]
#["ca" "cb" "cc"]]])
6.3.2 Broadcasting Control
> (parameterize ([array-broadcasting #f]) (array* (index-array #(3 3)) (array 10))) array-shape-broadcast: incompatible array shapes
(array-broadcasting #f): '#(3 3), '#()
> (define arr10 (array-map number->string (index-array #(10))))
> (define arr3 (array-map number->string (index-array #(3))))
> arr10 - : (Array String)
(array #["0" "1" "2" "3" "4" "5" "6" "7" "8" "9"])
> arr3 - : (Array String)
(array #["0" "1" "2"])
> (array-map string-append arr10 (array #["+" "-"]) arr3) array-shape-broadcast: incompatible array shapes
(array-broadcasting #t): '#(10), '#(2), '#(3)
> (parameterize ([array-broadcasting 'permissive]) (array-map string-append arr10 (array #["+" "-"]) arr3)) - : (Array String)
(array #["0+0" "1-1" "2+2" "3-0" "4+1" "5-2" "6+0" "7-1" "8+2" "9-0"])