slices.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. ; (c) Daniel Llorens 2012-2015
  2. ; Array operations, after J & others.
  3. ; This library is free software; you can redistribute it and/or modify it under
  4. ; the terms of the GNU General Public License as published by the Free
  5. ; Software Foundation; either version 3 of the License, or (at your option) any
  6. ; later version.
  7. ; @todo reductions: look at J/repa/numpy/PDL/SAC?; tensordot/tensorsolve.
  8. (define-module (ploy slices))
  9. (import (ice-9 optargs) (srfi srfi-26) (srfi srfi-1) (srfi srfi-11)
  10. (srfi srfi-9) (srfi srfi-8) (ploy basic) (ploy assert) (ploy ploy)
  11. (ploy as-array) (ploy reduce) (ploy cat) (ice-9 control))
  12. ; from (ploy basic)
  13. (re-export $. $ tally array-dim pk-shape pk=>)
  14. ; from (ploy ploy)
  15. (re-export rank verb w/rank array-atom array-type*
  16. ply/t ply ply! ply-n/o from J range amend! out/t out ravel
  17. reshape reshape-strict rollaxis out/t out
  18. i. iota. linspace. linspace-m.)
  19. ; from (ploy reduce)
  20. (re-export over over/t folda folda/t foldb foldb/t dot cdot)
  21. ; from (ploy cat)
  22. (re-export cat icat)
  23. ; from (ploy as-array)
  24. (re-export arraylike-dimensions as-array)
  25. ; ---------------------------------------------------------------------
  26. ; joining arrays. @todo Put cat in here, or put these in cat.scm.
  27. ; ---------------------------------------------------------------------
  28. ; name is after J monad ; but w/o padding or replication.
  29. (define (raze a)
  30. ; @todo use over + tally, but over must support rank > 1.
  31. (let* ((m (tally a))
  32. (n (folda (verb (lambda (n a) (+ n (tally a))) '() 0 '_) 0 a))
  33. (b (apply make-typed-array (or (zero? m) (array-type (array-ref a 0)))
  34. *unspecified* n (cdr ($ (array-cell-ref a 0))))))
  35. (let loop ((i 0) (st 0))
  36. ; @TODO need amend! or...
  37. (if (= i m)
  38. b
  39. (let ((ai (array-cell-ref a i)))
  40. (amend! b ai (J (tally ai) st))
  41. (loop (+ 1 i) (+ st (tally ai))))))))
  42. (export raze)
  43. ; @todo See how one would do this in J.
  44. (define (tile a . sb)
  45. "tile a . sb
  46. Replicate array a over dimensions sb.
  47. sb indicate axes matching those of a. That is, the first element of sb indicates
  48. how many times to repeat a's first dimension, and so on.
  49. If a has shape [a0 a1 ... a(n-1)] and sb is [b0 b1 ... b(m-1)] with n>=m,
  50. the result has shape [a0*b0 a1*b1 ... a(m-1)*b(m-1) a(m) ... a(n-1)]."
  51. (let ((rb (length sb))
  52. (ra (rank a)))
  53. (assert (<= rb ra) "bad ranks")
  54. (apply reshape
  55. (apply transpose-array
  56. (out (verb (lambda (b a) a) '() 0 0) (apply reshape #f sb) a)
  57. (append (iota rb 0 2) (iota rb 1 2) (iota (- ra rb) (+ rb rb))))
  58. (let ((sa ($ a)))
  59. (append (map * sb sa) (drop sa rb))))))
  60. (export tile)
  61. ; ---------------------------------------------------------------------
  62. ; explode arrays, for when we need to loop using map, etc.
  63. ; ---------------------------------------------------------------------
  64. (define (explode cell-rank A)
  65. (if (or (zero? cell-rank) (= (rank A) cell-rank))
  66. A
  67. (ply/t #t (verb values '() cell-rank) A)))
  68. (export explode)
  69. ; ---------------------------------------------------------------------
  70. ; axis operations.
  71. ; ---------------------------------------------------------------------
  72. (define (squeeze. A)
  73. (apply reshape A (filter (cut not= 1 <>) ($ A))))
  74. (export squeeze.)
  75. ; @todo have this in Guile.
  76. (define* (reverse. A #:optional (i 0))
  77. "reverse. A [i]
  78. Reverse array over axis i, by default the first (i=0)"
  79. (apply make-shared-array
  80. A
  81. (lambda j (append (take j i) (list (- ($. A i) (list-ref j i) 1)) (drop j (+ i 1))))
  82. ($ A)))
  83. (export reverse.)
  84. ; -----------------------------
  85. ; cant
  86. ; -----------------------------
  87. ; #(a b c d ...) -> #2((a b c) (b c d) ...)
  88. ; the name is from one of Iverson's papers (A dict. of APL?)
  89. ; c cannot be 0, but one can use reshape in that case.
  90. ; @todo apply to first axis?
  91. ; @TODO Check c:is_c_order? (ra-large.H:is_c_order) against this.
  92. (define cant
  93. (case-lambda
  94. ((a c n)
  95. (make-shared-array a
  96. (lambda (i j) (list (+ (* i n) (* j 1))))
  97. (+ (floor/ (- (tally a) c) n) 1) c))
  98. ((a c)
  99. (cant a c 1))))
  100. (export cant)
  101. ; ---------------------------------------------------------------------
  102. ; roll
  103. ; ---------------------------------------------------------------------
  104. ; @bug dubious use of from for set!.
  105. ; @todo A case for loop fusion, also w/rank use.
  106. (define (roll n a)
  107. (if (zero? n)
  108. a
  109. (let* ((b (apply make-typed-array (array-type a) *unspecified* ($ a)))
  110. (s (tally a))
  111. (n (modulo n s)))
  112. (array-copy! (from a (J (- s n) 0)) (from b (J (- s n) n)))
  113. (array-copy! (from a (J n (- s n))) (from b (J n 0)))
  114. b)))
  115. (export roll)
  116. ; ---------------------------------------------------------------------
  117. ; family is sort, partial-sort, median, min-by, max-by.
  118. ; ---------------------------------------------------------------------
  119. (define (max-by l <)
  120. (assert (pair? l) "bad args to max-by")
  121. (fold (lambda (s b) (if (< b s) s b)) (car l) (cdr l)))
  122. (define (min-by l <)
  123. (assert (pair? l) "bad args to min-by")
  124. (fold (lambda (s b) (if (< s b) s b)) (car l) (cdr l)))
  125. (export max-by min-by)
  126. ; sort i according to the order of (from b i).
  127. ; this is a shortcut for (sort-by i (from b i) <).
  128. ; @todo There's a (sort-indices-by-norm) in (array symmetrizer). See to replace it.
  129. (define sort-indices-by.
  130. (case-lambda
  131. ((i b) (sort-indices-by. i b <))
  132. ((i b <) (stable-sort* i (lambda (i j) (< (array-cell-ref b i) (array-cell-ref b j)))))))
  133. ; sort over items.
  134. (define sort.
  135. (case-lambda
  136. ((a) (sort. a <))
  137. ((a <) (ply/t (array-type* a) (verb (cut array-cell-ref a <>) (cdr ($ a)) 0)
  138. (sort-indices-by. (i. (tally a)) a <)))))
  139. ; sort a according to the order of the corresponding elements of b.
  140. (define sort-by.
  141. (case-lambda
  142. ((a b) (sort-by. a b <))
  143. ((a b <)
  144. (assert (>= (tally b) (tally a)) "b too small to grade a")
  145. (ply/t (array-type* a) (verb (cut array-cell-ref a <>) (cdr ($ a)) 0)
  146. (sort-indices-by. (i. (tally a)) b <)))))
  147. ; @todo unfortunately stable-sort doesn't work on uniform arrays.
  148. (define stable-sort*
  149. (case-lambda
  150. ((a) (stable-sort* a <))
  151. ((a <)
  152. (if (or (eq? #t (array-type a)) (list? a))
  153. (stable-sort a <)
  154. (let ((A (array-copy #t a)))
  155. (stable-sort! A <)
  156. (array-copy (array-type a) A))))))
  157. (export sort. sort-by. sort-indices-by.)
  158. ; ---------------------------------------------------------------------
  159. ; variant selectors
  160. ; ---------------------------------------------------------------------
  161. ; @todo Use (over) when it supports rank>1.
  162. (define (count. pred? a)
  163. ; (over + (lambda (a) (if (pred? a) 1 0)) a)
  164. (folda (verb (lambda (c a) (+ c (if (pred? a) 1 0))) '() 0 '_) 0 a))
  165. (define copy./t
  166. (case-lambda
  167. ((type w b)
  168. (let* ((o (apply make-typed-array type *unspecified*
  169. (cons (count. values w) (cdr ($ b)))))
  170. (j 0))
  171. (array-slice-for-each 1
  172. (lambda (w b)
  173. (when (array-cell-ref w)
  174. (array-cell-set! o (array-cell-ref b) j)
  175. (set! j (+ 1 j))))
  176. w b)
  177. o))
  178. ((type b)
  179. (array-copy type b))))
  180. (define copy.
  181. (case-lambda
  182. ((w b) (copy./t (array-type* b) w b))
  183. ((b) (copy./t (array-type* b) b))))
  184. ; this is the same as (ply (verb array-cell-ref #f '_ 0) a i), unless there are repeated indices.
  185. (define (copy-i. i a)
  186. (copy. (let ((l (make-array #f (tally a))))
  187. (array-for-each (lambda (i) (array-set! l #t i)) i)
  188. l)
  189. a))
  190. (define (filter. pred? a)
  191. (let ((pred? (if (verb? pred?) pred? (verb pred? '() -1))))
  192. (copy. (ply/t 'b pred? a) a)))
  193. (define (filter-map. f a)
  194. (let ((fa (ply (if (verb? f) f (verb f '() -1)) a)))
  195. (copy. fa fa)))
  196. (export filter. filter-map.)
  197. (define (remove-i. i a)
  198. (if (zero? (tally i))
  199. a
  200. (let ((i (sort. i <))
  201. (b (apply make-typed-array (array-type* a) *unspecified*
  202. (cons (- (tally a) (tally i)) (cdr ($ a))))))
  203. (let loop ((k 0) (a0 0) (b0 0))
  204. (if (= k (tally i))
  205. (let ((ik (tally a)))
  206. (array-copy! (from a (J (- ik a0) a0)) (from b (J (- ik a0) b0)))
  207. b)
  208. (let* ((ik (array-cell-ref i k))
  209. (d (- ik a0)))
  210. (if (zero? d)
  211. (loop (+ 1 k) (+ 1 ik) (+ b0 d))
  212. (begin
  213. (array-copy! (from a (J d a0)) (from b (J d b0)))
  214. (loop (+ 1 k) (+ 1 ik) (+ b0 d))))))))))
  215. (define drop.
  216. (case-lambda
  217. ((a) (drop. a 1))
  218. ((a n) (from a (range n (tally a))))))
  219. (export count. copy. copy-i. remove-i. drop. copy./t)
  220. ; @todo neither over nor fold is a good fit for every. any. index .
  221. ; @todo over should support identity...
  222. (define (every. pred? a)
  223. (reset (over (case-lambda ((c a) a) ((a) a) (() #t))
  224. (lambda (a) (let ((x (pred? a))) (or x (shift k x))))
  225. a)))
  226. ; @todo see every.
  227. (define (any. pred? a)
  228. (reset (over (case-lambda ((c a) a) ((a) a) (() #f))
  229. (lambda (a) (let ((x (pred? a))) (and x (shift k x))))
  230. a)))
  231. ; @todo maybe rename to index-first.
  232. ; @todo foldb carries the index, redundant.
  233. ; @todo pred? what if pred? is a verb.
  234. (define (index pred? a)
  235. (reset (folda (verb (lambda (i a) (or (and (pred? a) (shift k i)) (+ 1 i)))
  236. '() 0 '_)
  237. 0 a)
  238. ; J dyad i. would leave the result of the fold.
  239. #f))
  240. ; @TODO see (index). ply/t carries the index, (i. (tally a)) is redundant.
  241. ; @TODO pred? should be a verb...
  242. ; @TODO a variable rank version, returning multi-indices.
  243. (define (indices pred? . a)
  244. (assert (procedure? pred?) "indices can't take verb, sorry")
  245. (copy. (apply ply/t 'b (apply verb pred? '() (make-list (length a) -1)) a)
  246. (i. (tally (car a)))))
  247. (export every. any. index indices)
  248. ; should be (set! (from y x) (iota (size x))) without having to create i.
  249. ; Maybe i ~ TensorIndex<0>.
  250. ; cf gradeup for inverting permutations.
  251. (define* (invert-index x #:optional (m (+ 1 (over max x))))
  252. "invert-index x #:optional (m 1+(max x))
  253. return y such that y[x[i]] = i. Range of y is 0..(m-1)."
  254. (let ((y (make-vector m #f))
  255. (n (tally x)))
  256. (let loop ((i 0))
  257. (cond ((= i n) y)
  258. (else (array-set! y i (array-ref x i))
  259. (loop (+ 1 i)))))))
  260. (export invert-index)
  261. (define (first. x) (from x 0))
  262. (define (last. x) (from x (- (tally x) 1)))
  263. (export first. last.)
  264. ; ---------------------------------------------------------------------
  265. ; conversion or casting.
  266. ; ---------------------------------------------------------------------
  267. ; @todo f64/c64 uniform vectors are also bytevectors, so try to cast instead.
  268. ; @todo ply with vector constructor is wasteful.
  269. ; @todo maybe keep the last axis, but must fix users.
  270. (define (complex->f64 c)
  271. (apply reshape
  272. (ply/t 'f64 (verb (lambda (x) (vector (real-part x) (imag-part x))) '(2) 0) c)
  273. (append (drop-right ($ c) 1) (list (* 2 (last ($ c)))))))
  274. (define (real->c64 r)
  275. (assert (even? (last ($ r))) "last dimension must be even")
  276. (let ((r2 (apply reshape r (append (drop-right ($ r) 1) (list (/ (last ($ r)) 2) 2)))))
  277. (ply/t 'c64 (verb (lambda (x) (make-rectangular (array-ref x 0) (array-ref x 1))) '() 1) r2)))
  278. (export complex->f64 real->c64)
  279. ; ---------------------------------------------------------------------
  280. ; common verbs, but @todo maybe not the right place.
  281. ; ---------------------------------------------------------------------
  282. (define vnorm. (verb v-norm '() 1))
  283. (define sqrm. (verb sqrm-reduce '() 1))
  284. (export vnorm. sqrm.)
  285. (re-export v-norm sqrm-reduce)