Documented `math/array' folds

Refactored many of the fold functions (e.g. `array-axis-andmap' is gone,
replaced by short-cutting `array-axis-and', which is sufficient because the
result of `array-map' is non-strict; added `array-count', `array-all-fold';
removed `array-all=' and friends)

Turned common folds into macros (preserves return types better, speeds up
compilation time)

Exposed a safe variant of `unsafe-array-axis-reduce'
This commit is contained in:
Neil Toronto 2012-11-28 14:13:29 -07:00
parent 9249929c45
commit cd002d5830
7 changed files with 381 additions and 182 deletions

View File

@ -8,6 +8,7 @@
"private/array/array-transform.rkt" "private/array/array-transform.rkt"
"private/array/array-convert.rkt" "private/array/array-convert.rkt"
"private/array/array-fold.rkt" "private/array/array-fold.rkt"
"private/array/array-special-folds.rkt"
"private/array/array-print.rkt" "private/array/array-print.rkt"
"private/array/array-fft.rkt" "private/array/array-fft.rkt"
"private/array/array-syntax.rkt" "private/array/array-syntax.rkt"
@ -34,6 +35,7 @@
"private/array/array-transform.rkt" "private/array/array-transform.rkt"
"private/array/array-convert.rkt" "private/array/array-convert.rkt"
"private/array/array-fold.rkt" "private/array/array-fold.rkt"
"private/array/array-special-folds.rkt"
"private/array/array-print.rkt" "private/array/array-print.rkt"
"private/array/array-syntax.rkt" "private/array/array-syntax.rkt"
"private/array/array-fft.rkt" "private/array/array-fft.rkt"

View File

@ -1,35 +1,32 @@
#lang racket/base #lang racket/base
(require typed/untyped-utils (require (for-syntax racket/base)
(except-in "typed-array-fold.rkt" "typed-array-fold.rkt")
array-axis-sum
array-axis-prod
array-axis-min
array-axis-max
array-all-sum
array-all-prod
array-all-min
array-all-max))
(require/untyped-contract ;; ===================================================================================================
(begin (require "array-struct.rkt")) ;; Standard folds
"typed-array-fold.rkt"
[array-axis-sum (case-> ((Array Number) Integer -> (Array Number)) (define-syntax-rule (define-axis-fold name f)
((Array Number) Integer Number -> (Array Number)))] (define-syntax (name stx)
[array-axis-prod (case-> ((Array Number) Integer -> (Array Number)) (syntax-case stx ()
((Array Number) Integer Number -> (Array Number)))] [(_ arr k) (syntax/loc stx (array-axis-fold arr k f))]
[array-axis-min (case-> ((Array Real) Integer -> (Array Real)) [(_ arr k init) (syntax/loc stx (array-axis-fold arr k f init))])))
((Array Real) Integer Real -> (Array Real)))]
[array-axis-max (case-> ((Array Real) Integer -> (Array Real)) (define-syntax-rule (define-all-fold name f)
((Array Real) Integer Real -> (Array Real)))] (define-syntax (name stx)
[array-all-sum (case-> ((Array Number) -> Number) (syntax-case stx ()
((Array Number) Number -> Number))] [(_ arr) (syntax/loc stx (array-all-fold arr f))]
[array-all-prod (case-> ((Array Number) -> Number) [(_ arr init) (syntax/loc stx (array-all-fold arr f init))])))
((Array Number) Number -> Number))]
[array-all-min (case-> ((Array Real) -> Real) (define-axis-fold array-axis-sum +)
((Array Real) Real -> Real))] (define-axis-fold array-axis-prod *)
[array-all-max (case-> ((Array Real) -> Real) (define-axis-fold array-axis-min min)
((Array Real) Real -> Real))]) (define-axis-fold array-axis-max max)
(define-all-fold array-all-sum +)
(define-all-fold array-all-prod *)
(define-all-fold array-all-min min)
(define-all-fold array-all-max max)
(provide array-axis-fold (provide array-axis-fold
array-axis-sum array-axis-sum
@ -37,17 +34,15 @@
array-axis-min array-axis-min
array-axis-max array-axis-max
array-axis-count array-axis-count
array-axis-andmap array-axis-and
array-axis-ormap array-axis-or
array-fold array-fold
array-all-fold
array-all-sum array-all-sum
array-all-prod array-all-prod
array-all-min array-all-min
array-all-max array-all-max
array-all-count array-all-and
array-all-andmap array-all-or
array-all-ormap array-axis-reduce
array-all-equal? unsafe-array-axis-reduce)
array-all-eqv?
array-all-eq?
array-all=)

View File

@ -18,7 +18,10 @@
(: print-array (All (A) ((Array A) Symbol Output-Port (U #t #f 0 1) -> Any))) (: print-array (All (A) ((Array A) Symbol Output-Port (U #t #f 0 1) -> Any)))
;; The logic in `print-array' causes the REPL printer to try printing an array in each layout, and ;; The logic in `print-array' causes the REPL printer to try printing an array in each layout, and
;; keep the first successful one. An overflowing line means failure. ;; keep the first successful one. An overflowing line means failure.
(define (print-array arr name port mode) (define (print-array orig-arr name port mode)
;; Try to compute each element only once
(define arr (array-lazy orig-arr))
;; Called to print array elements; may recur (e.g. printing arrays of arrays) ;; Called to print array elements; may recur (e.g. printing arrays of arrays)
;; We never have to consider the `mode' argument again after defining `recur-print' ;; We never have to consider the `mode' argument again after defining `recur-print'
(define recur-print (define recur-print

View File

@ -0,0 +1,26 @@
#lang typed/racket/base
(require "array-struct.rkt"
"array-fold.rkt"
"array-pointwise.rkt")
(provide array-count)
(: array-count
(All (A B T ...)
(case-> ((A -> Any) (Array A) -> Index)
((A B T ... T -> Any) (Array A) (Array B) (Array T) ... T -> Index))))
(define array-count
(case-lambda:
[([f : (A -> Any)] [arr0 : (Array A)])
(assert (array-all-sum (inline-array-map (λ: ([a : A]) (if (f a) 1 0)) arr0)) index?)]
[([f : (A B -> Any)] [arr0 : (Array A)] [arr1 : (Array B)])
(assert
(array-all-sum (inline-array-map (λ: ([a : A] [b : B]) (if (f a b) 1 0)) arr0 arr1))
index?)]
[([f : (A B T ... T -> Any)] [arr0 : (Array A)] [arr1 : (Array B)] . [arrs : (Array T) ... T])
(assert
(array-all-sum (apply array-map
(λ: ([a : A] [b : B] . [ts : T ... T]) (if (apply f a b ts) 1 0))
arr0 arr1 arrs))
index?)]))

View File

@ -1,11 +1,11 @@
#lang typed/racket/base #lang typed/racket/base
(require racket/performance-hint (require racket/performance-hint
racket/fixnum
"../unsafe.rkt" "../unsafe.rkt"
"array-struct.rkt" "array-struct.rkt"
"array-indexing.rkt" "array-indexing.rkt"
"utils.rkt" "utils.rkt")
"for-each.rkt")
(provide (all-defined-out)) (provide (all-defined-out))
@ -16,16 +16,32 @@
(define (check-array-axis name arr k) (define (check-array-axis name arr k)
(define dims (array-dims arr)) (define dims (array-dims arr))
(cond (cond
[(= dims 0) (raise-argument-error 'name "Array with at least one axis" 0 arr k)] [(fx= dims 0) (raise-argument-error name "Array with at least one axis" 0 arr k)]
[(or (0 . > . k) (k . >= . dims)) [(or (0 . > . k) (k . >= . dims))
(raise-argument-error 'name (format "Index < ~a" dims) 1 arr k)] (raise-argument-error name (format "Index < ~a" dims) 1 arr k)]
[else k])) [else k]))
(: array-axis-reduce (All (A B) ((Array A) Integer (Index (Integer -> A) -> B) -> (Array B))))
(define (array-axis-reduce arr k f)
(let ([k (check-array-axis 'array-axis-reduce arr k)])
(define ds (array-shape arr))
(define dk (unsafe-vector-ref ds k))
(define new-ds (unsafe-vector-remove ds k))
(define proc (unsafe-array-proc arr))
(unsafe-build-array
new-ds (λ: ([js : Indexes])
(define old-js (unsafe-vector-insert js k 0))
(f dk (λ: ([jk : Integer])
(cond [(or (jk . < . 0) (jk . >= . dk))
(raise-argument-error 'array-axis-reduce (format "Index < ~a" dk) jk)]
[else
(unsafe-vector-set! old-js k jk)
(proc (vector-copy-all old-js))])))))))
(: unsafe-array-axis-reduce (All (A B) ((Array A) Index (Index (Index -> A) -> B) -> (Array B)))) (: unsafe-array-axis-reduce (All (A B) ((Array A) Index (Index (Index -> A) -> B) -> (Array B))))
(begin-encourage-inline (begin-encourage-inline
(define (unsafe-array-axis-reduce arr k f) (define (unsafe-array-axis-reduce arr k f)
(define ds (array-shape arr)) (define ds (array-shape arr))
(define dims (vector-length ds))
(define dk (unsafe-vector-ref ds k)) (define dk (unsafe-vector-ref ds k))
(define new-ds (unsafe-vector-remove ds k)) (define new-ds (unsafe-vector-remove ds k))
(define proc (unsafe-array-proc arr)) (define proc (unsafe-array-proc arr))
@ -42,18 +58,18 @@
(unsafe-array-axis-reduce (unsafe-array-axis-reduce
arr k (λ: ([dk : Index] [proc : (Index -> A)]) arr k (λ: ([dk : Index] [proc : (Index -> A)])
(let: loop : B ([jk : Nonnegative-Fixnum 0] [acc : B init]) (let: loop : B ([jk : Nonnegative-Fixnum 0] [acc : B init])
(cond [(jk . < . dk) (loop (+ jk 1) (f (proc jk) acc))] (cond [(jk . fx< . dk) (loop (fx+ jk 1) (f (proc jk) acc))]
[else acc])))))) [else acc]))))))
(: array-axis-fold/no-init (All (A) ((Array A) Integer (A A -> A) -> (Array A)))) (: array-axis-fold/no-init (All (A) ((Array A) Integer (A A -> A) -> (Array A))))
(define (array-axis-fold/no-init arr k f) (define (array-axis-fold/no-init arr k f)
(let ([k (check-array-axis 'array-axis-fold arr k)]) (let ([k (check-array-axis 'array-axis-fold arr k)])
(when (= (unsafe-vector-ref (array-shape arr) k) 0) (when (fx= (unsafe-vector-ref (array-shape arr) k) 0)
(raise-argument-error 'array-axis-fold "nonzero axis" 0 arr k)) (raise-argument-error 'array-axis-fold "nonzero axis" 0 arr k))
(unsafe-array-axis-reduce (unsafe-array-axis-reduce
arr k (λ: ([dk : Index] [proc : (Index -> A)]) arr k (λ: ([dk : Index] [proc : (Index -> A)])
(let: loop : A ([jk : Nonnegative-Fixnum 1] [acc : A (proc 0)]) (let: loop : A ([jk : Nonnegative-Fixnum 1] [acc : A (proc 0)])
(cond [(jk . < . dk) (loop (+ jk 1) (f (proc jk) acc))] (cond [(jk . fx< . dk) (loop (fx+ jk 1) (f (proc jk) acc))]
[else acc])))))) [else acc]))))))
(: array-axis-fold (All (A B) (case-> ((Array A) Integer (A A -> A) -> (Array A)) (: array-axis-fold (All (A B) (case-> ((Array A) Integer (A A -> A) -> (Array A))
@ -63,57 +79,33 @@
[(arr k f) (array-axis-fold/no-init arr k f)] [(arr k f) (array-axis-fold/no-init arr k f)]
[(arr k f init) (array-axis-fold/init arr k f init)])) [(arr k f init) (array-axis-fold/init arr k f init)]))
;; --------------------------------------------------------------------------------------------------- ;; ===================================================================================================
;; Whole-array fold ;; Whole-array folds
(: array-fold (All (A) ((Array A) ((Array A) Index -> (Array A)) -> (Array A))))
(begin-encourage-inline (begin-encourage-inline
(: array-fold (All (A) ((Array A) ((Array A) Index -> (Array A)) -> (Array A))))
(define (array-fold arr f) (define (array-fold arr f)
(define dims (array-dims arr)) (define dims (array-dims arr))
(let loop ([#{k : Index} dims] [arr arr]) (let loop ([#{k : Index} dims] [arr arr])
(cond [(zero? k) arr] (cond [(fx= k 0) arr]
[else (let ([k (sub1 k)]) [else (let ([k (fx- k 1)])
(loop k (f arr k)))])))) (loop k (f arr k)))])))
;; =================================================================================================== (: array-all-fold (All (A) (case-> ((Array A) (A A -> A) -> A)
;; Standard axis folds ((Array A) (A A -> A) A -> A))))
(define array-all-fold
(define-syntax-rule (define-axis-fold name op T ...) (case-lambda
(begin-encourage-inline [(arr f)
(: name (case-> ((Array T) Integer -> (Array T)) ... (array-ref (array-fold arr (λ: ([arr : (Array A)] [k : Index])
((Array T) Integer T -> (Array T)) ...)) (array-axis-fold arr k f)))
(define name #())]
(case-lambda [(arr f init)
[(arr k) (array-axis-fold arr k op)] (array-ref (array-fold arr (λ: ([arr : (Array A)] [k : Index])
[(arr k init) (array-axis-fold arr k op init)])))) (array-axis-fold arr k f init)))
#())]))
(define-axis-fold array-axis-sum + Float Real Float-Complex Number)
(define-axis-fold array-axis-prod * Float Real Float-Complex Number) ) ; begin-encourage-inline
(define-axis-fold array-axis-min min Float Real)
(define-axis-fold array-axis-max max Float Real)
;; ===================================================================================================
;; Standard whole-array folds
(define-syntax-rule (define-fold name array-axis-op T ...)
(begin-encourage-inline
(: name (case-> ((Array T) -> T) ...
((Array T) T -> T) ...))
(define name
(case-lambda
[(arr) (array-ref (array-fold arr array-axis-op) #())]
[(arr init)
(plet: (A) ([arr : (Array A) arr]
[array-axis-op : ((Array A) Index A -> (Array A)) array-axis-op]
[init : A init])
(array-ref (array-fold arr (λ: ([arr : (Array A)] [k : Index])
(array-axis-op arr k init)))
#()))]))))
(define-fold array-all-sum array-axis-sum Float Real Float-Complex Number)
(define-fold array-all-prod array-axis-prod Float Real Float-Complex Number)
(define-fold array-all-min array-axis-min Float Real)
(define-fold array-all-max array-axis-max Float Real)
;; =================================================================================================== ;; ===================================================================================================
;; Count ;; Count
@ -124,69 +116,38 @@
(unsafe-array-axis-reduce (unsafe-array-axis-reduce
arr k (λ: ([dk : Index] [proc : (Index -> A)]) arr k (λ: ([dk : Index] [proc : (Index -> A)])
(let: loop : Index ([jk : Nonnegative-Fixnum 0] [acc : Nonnegative-Fixnum 0]) (let: loop : Index ([jk : Nonnegative-Fixnum 0] [acc : Nonnegative-Fixnum 0])
(if (jk . < . dk) (if (jk . fx< . dk)
(cond [(pred? (proc jk)) (loop (+ jk 1) (unsafe-fx+ acc 1))] (cond [(pred? (proc jk)) (loop (fx+ jk 1) (unsafe-fx+ acc 1))]
[else (loop (+ jk 1) acc)]) [else (loop (fx+ jk 1) acc)])
(assert acc index?))))))) (assert acc index?)))))))
(: array-all-count (All (A) ((Array A) (A -> Any) -> Index)))
(define (array-all-count arr pred?)
(define: i : (Boxof Nonnegative-Fixnum) (box 0))
(define proc (unsafe-array-proc arr))
(define ds (array-shape arr))
(for-each-array-index ds (λ (js) (when (pred? (proc js)) (set-box! i (unsafe-fx+ (unbox i) 1)))))
(assert (unbox i) index?))
;; =================================================================================================== ;; ===================================================================================================
;; Short-cutting andmap ;; Short-cutting axis folds
(: array-axis-andmap (All (A) ((Array A) Integer (A -> Any) -> (Array Boolean)))) (: array-axis-and (All (A) ((Array A) Integer -> (Array (U A Boolean)))))
(define (array-axis-andmap arr k pred?) (define (array-axis-and arr k)
(let ([k (check-array-axis 'array-axis-andmap arr k)]) (let ([k (check-array-axis 'array-axis-and arr k)])
(unsafe-array-axis-reduce (unsafe-array-axis-reduce
arr k (λ: ([dk : Index] [proc : (Index -> A)]) arr k (λ: ([dk : Index] [proc : (Index -> A)])
(let: loop : Boolean ([jk : Nonnegative-Fixnum 0]) (let: loop : (U A Boolean) ([jk : Nonnegative-Fixnum 0] [acc : (U A Boolean) #t])
(cond [(jk . < . dk) (if (pred? (proc jk)) (loop (+ jk 1)) #f)] (cond [(jk . fx< . dk) (define v (and acc (proc jk)))
[else #t])))))) (if v (loop (fx+ jk 1) v) v)]
[else acc]))))))
(: array-all-andmap (All (A) ((Array A) (A -> Any) -> Boolean))) (: array-axis-or (All (A) ((Array A) Integer -> (Array (U A #f)))))
(define (array-all-andmap arr pred?) (define (array-axis-or arr k)
(let/ec: return : Boolean (let ([k (check-array-axis 'array-axis-or arr k)])
(define proc (unsafe-array-proc arr))
(define ds (array-shape arr))
(for-each-array-index ds (λ (js) (unless (pred? (proc js)) (return #f))))
#t))
;; ===================================================================================================
;; Short-cutting ormap
(: array-axis-ormap (All (A) ((Array A) Integer (A -> Any) -> (Array Boolean))))
(define (array-axis-ormap arr k pred?)
(let ([k (check-array-axis 'array-axis-ormap arr k)])
(unsafe-array-axis-reduce (unsafe-array-axis-reduce
arr k (λ: ([dk : Index] [proc : (Index -> A)]) arr k (λ: ([dk : Index] [proc : (Index -> A)])
(let: loop : Boolean ([jk : Nonnegative-Fixnum 0]) (let: loop : (U A #f) ([jk : Nonnegative-Fixnum 0] [acc : (U A #f) #f])
(cond [(jk . < . dk) (if (pred? (proc jk)) #t (loop (+ jk 1)))] (cond [(jk . fx< . dk) (define v (or acc (proc jk)))
[else #f])))))) (if v v (loop (fx+ jk 1) v))]
[else acc]))))))
(: array-all-ormap (All (A) ((Array A) (A -> Any) -> Boolean))) (: array-all-and (All (A B) ((Array A) -> (U A Boolean))))
(define (array-all-ormap arr pred?) (define (array-all-and arr)
(let/ec: return : Boolean (array-ref ((inst array-fold (U A Boolean)) arr array-axis-and) #()))
(define f (unsafe-array-proc arr))
(define ds (array-shape arr))
(for-each-array-index ds (λ (js) (when (pred? (f js)) (return #t))))
#f))
;; =================================================================================================== (: array-all-or (All (A B) ((Array A) -> (U A #f))))
(define (array-all-or arr)
(: array-all-equal? ((Array Any) (Array Any) -> Boolean)) (array-ref ((inst array-fold (U A #f)) arr array-axis-or) #()))
(define array-all-equal? equal?)
(: array-all-eqv? ((Array Any) (Array Any) -> Boolean))
(define array-all-eqv? (array-lift-comparison eqv?))
(: array-all-eq? ((Array Any) (Array Any) -> Boolean))
(define array-all-eq? (array-lift-comparison eq?))
(: array-all= ((Array Number) (Array Number) -> Boolean))
(define array-all= (array-lift-comparison =))

View File

@ -37,7 +37,8 @@
(vector-ref (array-shape a) 0)) (vector-ref (array-shape a) 0))
(: matrix-all= : (Matrix Number) (Matrix Number) -> Boolean) (: matrix-all= : (Matrix Number) (Matrix Number) -> Boolean)
(define matrix-all= array-all=) (define (matrix-all= arr0 arr1)
(array-all-and (array= arr0 arr1)))
(: matrix-dimensions : (Matrix Number) -> (Values Index Index)) (: matrix-dimensions : (Matrix Number) -> (Values Index Index))
(define (matrix-dimensions a) (define (matrix-dimensions a)

View File

@ -5,9 +5,9 @@
(for-label racket/base racket/vector racket/match racket/unsafe/ops racket/string (for-label racket/base racket/vector racket/match racket/unsafe/ops racket/string
math plot math plot
(only-in typed/racket/base (only-in typed/racket/base
ann inst : λ: define: make-predicate ann inst : λ: define: make-predicate ->
Flonum Real Boolean Any Integer Index Natural Exact-Positive-Integer Flonum Real Boolean Any Integer Index Natural Exact-Positive-Integer
Nonnegative-Real Sequenceof Fixnum Values Nonnegative-Real Sequenceof Fixnum Values Number
All U List Vector Listof Vectorof Struct)) All U List Vector Listof Vectorof Struct))
"utils.rkt") "utils.rkt")
@ -16,7 +16,8 @@
(require racket/match (require racket/match
racket/vector racket/vector
racket/string racket/string
racket/sequence)] racket/sequence
racket/list)]
@title[#:tag "arrays" #:style 'toc]{Arrays} @title[#:tag "arrays" #:style 'toc]{Arrays}
@(author-neil) @(author-neil)
@ -52,6 +53,10 @@ applied to indexes to retrieve elements.
@local-table-of-contents[] @local-table-of-contents[]
@;{==================================================================================================}
@section{Preliminaries} @section{Preliminaries}
@subsection{Definitions} @subsection{Definitions}
@ -296,6 +301,10 @@ of stretching just singleton axes:
Notice that @racket[(array #["+" "-"])] was repeated five times, and that Notice that @racket[(array #["+" "-"])] was repeated five times, and that
@racket[arr3] was repeated three full times and once partially. @racket[arr3] was repeated three full times and once partially.
@;{==================================================================================================}
@section{Types, Predicates and Accessors} @section{Types, Predicates and Accessors}
@defform[(Array A)]{ @defform[(Array A)]{
@ -433,6 +442,10 @@ Returns the number of @racket[arr]'s dimensions. Equivalent to
Returns the vector of data that @racket[arr] contains. Returns the vector of data that @racket[arr] contains.
} }
@;{==================================================================================================}
@section{Construction} @section{Construction}
@defform[(array #[#[...] ...])]{ @defform[(array #[#[...] ...])]{
@ -614,6 +627,10 @@ have the value @racket[on-value]; the rest have the value @racket[off-value].
(diagonal-array 2 7 1 0)] (diagonal-array 2 7 1 0)]
} }
@;{==================================================================================================}
@section{Conversion} @section{Conversion}
@deftogether[(@defproc[(list->array [lst (Listof A)]) (Array A)] @deftogether[(@defproc[(list->array [lst (Listof A)]) (Array A)]
@ -732,6 +749,10 @@ They call the current @racket[array-custom-printer]:
#t)] #t)]
} }
@;{==================================================================================================}
@section{Comprehensions and Sequences} @section{Comprehensions and Sequences}
Sometimes sequential processing is unavoidable, so @racket[math/array] provides loops and sequences. Sometimes sequential processing is unavoidable, so @racket[math/array] provides loops and sequences.
@ -805,6 +826,10 @@ Returns a sequence of indexes for shape @racket[ds], in row-major order.
(indexes-array #(3 3))] (indexes-array #(3 3))]
} }
@;{==================================================================================================}
@section{Pointwise Operations} @section{Pointwise Operations}
Most of the operations documented in this section are simple macros that apply @racket[array-map] Most of the operations documented in this section are simple macros that apply @racket[array-map]
@ -992,6 +1017,10 @@ This is used for both @racket[(array-broadcasting #t)] and @racket[(array-broadc
(array-broadcast (array #[0 1]) ((inst vector Index) 5))] (array-broadcast (array #[0 1]) ((inst vector Index) 5))]
} }
@;{==================================================================================================}
@section{Indexing and Slicing} @section{Indexing and Slicing}
@defproc[(array-ref [arr (Array A)] [js In-Indexes]) A]{ @defproc[(array-ref [arr (Array A)] [js In-Indexes]) A]{
@ -1236,6 +1265,10 @@ Given a slice @racket[s] and an axis length @racket[dk], returns the arguments t
that would produce an equivalent slice specification. that would produce an equivalent slice specification.
} }
@;{==================================================================================================}
@section{Transformations} @section{Transformations}
@defproc[(array-transform [arr (Array A)] [ds In-Indexes] [proc (Indexes -> In-Indexes)]) @defproc[(array-transform [arr (Array A)] [ds In-Indexes] [proc (Indexes -> In-Indexes)])
@ -1257,8 +1290,8 @@ Double an array in every dimension by duplicating elements:
(vector-map (λ: ([d : Index]) (* d 2)) (array-shape arr)) (vector-map (λ: ([d : Index]) (* d 2)) (array-shape arr))
(λ: ([js : Indexes]) (λ: ([js : Indexes])
(vector-map (λ: ([j : Index]) (quotient j 2)) js)))] (vector-map (λ: ([j : Index]) (quotient j 2)) js)))]
Recall that, because @racket[array-transform] returns @tech{non-strict} arrays, the above Because @racket[array-transform] returns @tech{non-strict} arrays, the above result takes
result takes little more space than the original array. little more space than the original array.
Almost all array transformations, including those effected by @secref{array:slice-specs}, are Almost all array transformations, including those effected by @secref{array:slice-specs}, are
implemented using @racket[array-transform] or its unsafe counterpart. implemented using @racket[array-transform] or its unsafe counterpart.
@ -1314,7 +1347,7 @@ Returns an array like @racket[arr], but with axes permuted according to @racket[
The list @racket[perm] represents a mapping from source axis numbers to destination The list @racket[perm] represents a mapping from source axis numbers to destination
axis numbers: the source is the list position, the destination is the list element. axis numbers: the source is the list position, the destination is the list element.
For example, the permutation @racket['(0 1)] is the identity permutation for two-dimensional For example, the permutation @racket['(0 1 2)] is the identity permutation for three-dimensional
arrays, @racket['(1 0)] swaps axes @racket[0] and @racket[1], and @racket['(3 1 2 0)] swaps arrays, @racket['(1 0)] swaps axes @racket[0] and @racket[1], and @racket['(3 1 2 0)] swaps
axes @racket[0] and @racket[3]. axes @racket[0] and @racket[3].
@ -1344,36 +1377,209 @@ Returns an array with shape @racket[(vector (array-size arr))], with the element
(array-flatten (array #[#[0 1] #[2 3]]))] (array-flatten (array #[#[0 1] #[2 3]]))]
} }
@section{Folds}
@;{ @;{==================================================================================================}
array-axis-fold
array-fold
array-all-sum @section{Folds and Other Axis Reductions}
array-all-prod
array-all-min @defproc*[([(array-axis-fold [arr (Array A)] [k Integer] [f (A A -> A)]) (Array A)]
array-all-max [(array-axis-fold [arr (Array A)] [k Integer] [f (A B -> B)] [init B]) (Array B)])]{
array-axis-count Folds a binary function @racket[f] over axis @racket[k] of @racket[arr]. The result has the
array-all-count shape of @racket[arr] but with axis @racket[k] removed.
array-axis-andmap
array-all-andmap The three-argument variant uses the first value of each row in axis @racket[k] as @racket[init].
array-axis-ormap It therefore requires axis @racket[k] to have positive length.
array-all-ormap
array-all-eq? @examples[#:eval typed-eval
array-all-equal? (define arr (index-array #(3 4)))
array-all-eqv? arr
array-all= (array-axis-fold arr 0 +)
array-axis-sum (array-axis-fold arr 1 (inst cons Index (Listof Index)) empty)]
array-axis-prod Notice that the second example returns an array of reversed lists.
array-axis-min This is therefore a left fold; see @racket[foldl].
array-axis-max }
array-lift-comparison
array-axis-fft @deftogether[(@defform*[((array-axis-sum arr k)
array-fft (array-axis-sum arr k init))]
@defform*[((array-axis-prod arr k)
(array-axis-prod arr k init))
#:contracts ([arr (Array Number)]
[k Integer]
[init Number])])]{
}
@deftogether[(@defform*[((array-axis-min arr k)
(array-axis-min arr k init))]
@defform*[((array-axis-max arr k)
(array-axis-max arr k init))
#:contracts ([arr (Array Real)]
[k Integer]
[init Real])])]{
Some standard per-axis folds, defined in terms of @racket[array-axis-fold]. The two-argument
variants require axis @racket[k] to have positive length.
@examples[#:eval typed-eval
(define arr (index-array #(3 4)))
arr
(array-axis-fold arr 1 +)
(array-axis-sum arr 1)
(array-axis-sum arr 0 0.0)]
}
@defproc[(array-fold [arr (Array A)] [g ((Array A) Index -> (Array A))]) (Array A)]{
Folds @racket[g] over @italic{each axis} of @racket[arr], in reverse order. The arguments
to @racket[g] are an array (initially @racket[arr]) and the current axis.
It should return an array with one fewer dimension than the array given, but does not have to.
@examples[#:eval typed-eval
(define arr (index-array #(3 4)))
arr
(array-fold arr (λ: ([arr : (Array Integer)] [k : Index])
(array-axis-sum arr k)))
(+ 0 1 2 3 4 5 6 7 8 9 10 11)
(array-fold
arr
(λ: ([arr : (Array (Listof* Index))] [k : Index])
(array-map
(inst reverse (Listof* Index))
(array-axis-fold arr k
(inst cons (Listof* Index) (Listof (Listof* Index)))
empty))))]
}
@defproc*[([(array-all-fold [arr (Array A)] [f (A A -> A)]) A]
[(array-all-fold [arr (Array A)] [f (A A -> A)] [init A]) A])]{
Folds @racket[f] over every element of @racket[arr] by folding @racket[f] over each axis in
reverse order. The two-argument variant is equivalent to
@racketblock[(array-ref (array-fold arr (λ: ([arr : (Array A)] [k : Index])
(array-axis-fold arr k f)))
#())]
and the three-argument variant is similar. The two-argument variant requires every axis to have
positive length.
@examples[#:eval typed-eval
(define arr (index-array #(3 4)))
arr
(array-all-fold arr +)
(array-all-fold (array #[]) + 0.0)]
}
@deftogether[(@defform*[((array-all-sum arr)
(array-all-sum arr init))]
@defform*[((array-all-prod arr)
(array-all-prod arr init))
#:contracts ([arr (Array Number)]
[init Number])])]{
}
@deftogether[(@defform*[((array-all-min arr)
(array-all-min arr init))]
@defform*[((array-all-max arr)
(array-all-max arr init))
#:contracts ([arr (Array Real)]
[init Real])])]{
Some standard whole-array folds, defined in terms of @racket[array-all-fold]. The one-argument
variants require each axis in @racket[arr] to have positive length.
@examples[#:eval typed-eval
(define arr (index-array #(3 4)))
arr
(array-all-fold arr +)
(array-all-sum arr)
(array-all-sum arr 0.0)]
}
@defproc[(array-axis-count [arr (Array A)] [k Integer] [pred? (A -> Any)]) (Array Index)]{
Counts the elements @racket[x] in rows of axis @racket[k] for which @racket[(pred? x)] is true.
@examples[#:eval typed-eval
(define arr (index-array #(3 3)))
arr
(array-axis-count arr 1 odd?)]
}
@defproc*[([(array-count [pred? (A -> Any)] [arr0 (Array A)]) Index]
[(array-count [pred? (A B Ts ... -> Any)]
[arr0 (Array A)]
[arr1 (Array B)]
[arrs (Array Ts)] ...)
Index])]{
The two-argument variant returns the number of elements @racket[x] in @racket[arr] for which
@racket[(pred? x)] is true. The other variant does the same with the corresponding elements from
any number of arrays. If the arrays' shapes are not the same, they are @tech{broadcast} first.
@examples[#:eval typed-eval
(array-count zero? (array #[#[0 1 0 2] #[0 3 -1 4]]))
(array-count equal?
(array #[#[0 1] #[2 3] #[0 1] #[2 3]])
(array #[0 1]))]
}
@deftogether[(@defproc[(array-axis-and [arr (Array A)] [k Integer]) (Array (U A Boolean))]
@defproc[(array-axis-or [arr (Array A)] [k Integer]) (Array (U A #f))])]{
Apply @racket[and] or @racket[or] to each row in axis @racket[k] of array @racket[arr], using
short-cut evaluation.
Consider the following array, whose second element takes 10 seconds to compute:
@interaction[#:eval typed-eval
(define arr (build-array #(2) (λ: ([js : Indexes])
(cond [(zero? (vector-ref js 0)) #f]
[else (sleep 10)
#t]))))]
Printing it takes over 10 seconds, but this returns immediately:
@interaction[#:eval typed-eval
(array-axis-and arr 0)]
}
@deftogether[(@defproc[(array-all-and [arr (Array A)]) (U A Boolean)]
@defproc[(array-all-or [arr (Array A)]) (U A #f)])]{
Apply @racket[and] or @racket[or] to each element in @racket[arr] using short-cut evaluation.
@racket[(array-all-and arr)] is defined as
@racketblock[(array-ref (array-fold arr array-axis-and) #())]
and @racket[array-all-or] is defined similarly.
@examples[#:eval typed-eval
(define arr (index-array #(3 3)))
(array-all-and (array= arr arr))
(define brr (array+ arr (array 1)))
(array-all-and (array= arr brr))
(array-all-or (array= arr (array 0)))]
}
@defproc[(array-axis-reduce [arr (Array A)] [k Integer] [h (Index (Integer -> A) -> B)]) (Array B)]{
Like @racket[array-axis-fold], but allows evaluation control (such as short-cutting @racket[and] and
@racket[or]) by moving the loop into @racket[h]. The result has the shape of @racket[arr], but with
axis @racket[k] removed.
The arguments to @racket[h] are the length of axis @racket[k] and a procedure that retrieves
elements from that axis's rows by their indexes in axis @racket[k]. It should return the elements
of the resulting array.
For example, summing the squares of the rows in axis @racket[1]:
@interaction[#:eval typed-eval
(define arr (index-array #(3 3)))
arr
(array-axis-reduce
arr 1
(λ: ([dk : Index] [proc : (Integer -> Real)])
(for/fold: ([s : Real 0]) ([jk (in-range dk)])
(+ s (sqr (proc jk))))))
(array-axis-sum (array-map sqr arr) 1)]
Every fold, including @racket[array-axis-fold], is ultimately defined using
@racket[array-axis-reduce] or its unsafe counterpart.
}
@;{==================================================================================================}
@section{Other Array Operations}
@;{array-fft
array-inverse-fft array-inverse-fft
array-axis-fft
array-axis-inverse-fft array-axis-inverse-fft
} }
@;{==================================================================================================}
@section{Subtypes} @section{Subtypes}
@subsection{Flonum Arrays} @subsection{Flonum Arrays}
@ -1382,7 +1588,7 @@ array-axis-inverse-fft
} }
@defproc[(array->flarray [arr (Array Real)]) FlArray]{ @defproc[(array->flarray [arr (Array Real)]) FlArray]{
Returns an flarray that has approximately the same elements as @racket[arr]. Returns an flonum array that has approximately the same elements as @racket[arr].
The elements may lose precision during the conversion. The elements may lose precision during the conversion.
} }
@ -1429,7 +1635,7 @@ flarray>=
} }
@defproc[(array->fcarray [arr (Array Number)]) FCArray]{ @defproc[(array->fcarray [arr (Array Number)]) FCArray]{
Returns an fcarray that has approximately the same elements as @racket[arr]. Returns a float-complex array that has approximately the same elements as @racket[arr].
The elements may lose precision during the conversion. The elements may lose precision during the conversion.
} }
@ -1466,6 +1672,10 @@ fcarray-asin
fcarray-atan fcarray-atan
} }
@;{==================================================================================================}
@section{Unsafe Operations} @section{Unsafe Operations}
@;{ @;{
@ -1481,6 +1691,7 @@ unsafe-build-array
unsafe-mutable-array unsafe-mutable-array
unsafe-flarray unsafe-flarray
unsafe-array-transform unsafe-array-transform
unsafe-array-axis-reduce
} }
@;{ @;{