ploy.scm 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. ; (c) Daniel Llorens - 2012-2015
  2. ; Array operations.
  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. ; @bug array-copy! is lax wrt copies (dest can be larger) which lets some errors
  8. ; through. Should use something stricter, or fix array-copy!.
  9. (define-module (ploy ploy))
  10. (import (ice-9 optargs) (srfi srfi-26) (srfi srfi-1) (srfi srfi-11) (ice-9 match)
  11. (srfi srfi-9) (srfi srfi-8) (ploy basic) (ploy assert))
  12. (re-export $. $ tally array-dim rank array-type* i. iota. linspace. linspace-m.)
  13. (re-export ravel reshape reshape-strict)
  14. ; Not for users, b/c array-first may return #0(.).
  15. (define (array-first a)
  16. (apply make-shared-array a
  17. (lambda x (cons 0 x))
  18. (cdr ($ a))))
  19. (define (array-rest a)
  20. (apply make-shared-array a
  21. (lambda x (cons (+ (car x) 1) (cdr x)))
  22. (cons (- (tally a) 1) (cdr ($ a)))))
  23. ; -------------------------------------------------------------------
  24. ; Guile array iteration ops
  25. ; -------------------------------------------------------------------
  26. ;
  27. ; write-arg ordered/any-order indices/element
  28. ; array-for-each R O E
  29. ; array-map! W A E
  30. ; array-index-map! W A I
  31. ; array-map-in-order! W O E
  32. ;
  33. ; probably need array-index-map-in-order!, array-for-each-any-order!
  34. ; (see ply-n/o!), something else?
  35. ; -------------------------------------------------------------------
  36. ; declare verb ranks
  37. ; -------------------------------------------------------------------
  38. ; A necessary first step in establishing the use of ranks is to define the ranks
  39. ; of all existing functions. Because of the treatment of degenerate cases, it is
  40. ; possible to do this in a manner compatible with existing definitions by simply
  41. ; assigning infinite ranks to all. However, it is generally more useful to
  42. ; assign the lowest rank which will ensure compatibility with existing
  43. ; definitions, since this leads to simpler definitions of the functions, and
  44. ; greater utilization of the general scheme of extension. In particular, the
  45. ; scalar functions can, and will, be assigned rank 0.
  46. ; ---Iverson 1983, 'Rationalized APL'
  47. ; The emphasis on examples using first axis variants of functions and operators
  48. ; is intentional. Intermediate and last axis definitions fall out logically and
  49. ; simply from use of the Rank Operator in conjunction with first axis variants,
  50. ; while the converse is not true. For example [phi] can be written as [theta]"1,
  51. ; whereas [theta] cannot be expressed in terms of [phi].
  52. ; ---Bernecky1988, 'An introduction to function rank'
  53. (define-record-type <verb>
  54. (make-verb op oshape ri) verb?
  55. (op verb-op)
  56. (oshape verb-oshape)
  57. (ri verb-ri))
  58. (define (valid-ranks? ri)
  59. (every (lambda (ri) (or (integer? ri) (eq? '_ ri))) ri))
  60. ; declare verb ranks.
  61. ; @TODO A way to reshape to match, either with cycle (easier ---that's what
  62. ; reshape does) or filler (??).
  63. (define verb
  64. (case-lambda
  65. "verb {op | op oshape | op oshape . ri}
  66. Construct verb from procedure op taking args of ranks ri.
  67. oshape can be - a function (arg0-shape ...) -> output shape.
  68. - a fixed output shape.
  69. - #f meaning 'arbitrary'."
  70. ((op)
  71. (unless (procedure? op) (throw 'bad-op-0 op))
  72. (make-verb op #f #f))
  73. ((op oshape)
  74. (unless (procedure? op) (throw 'bad-op-1 op))
  75. (make-verb op oshape #f))
  76. ((op oshape . ri)
  77. (unless (procedure? op) (throw 'bad-op-2 op))
  78. (unless (valid-ranks? ri) (throw 'bad-input-ranks ri))
  79. (make-verb op oshape ri))))
  80. ; modify a verb to take other cell ranks; rank conjunction.
  81. ; @TODO Make it easier to change just some ranks of op.
  82. ; @TODO Deduce output rank.
  83. (define (w/rank op . ri)
  84. (make-verb (cond ((procedure? op) (verb op))
  85. ((verb? op) op)
  86. (else (throw 'bad-op-3 op)))
  87. #f
  88. (if (valid-ranks? ri)
  89. ri
  90. (throw 'bad-input-ranks ri))))
  91. (define (verb-actual-ri op . ra)
  92. (match (verb-ri op)
  93. (#f (make-list (length ra) 0))
  94. (ri (unless (= (length ra) (length ri)) (throw 'bad-number-of-args op ri (length ra)))
  95. (map (lambda (ri ra)
  96. (let ((r (cond ((eq? '_ ri) ra)
  97. ((negative? ri) (+ ri ra))
  98. (else ri))))
  99. (unless (<= 0 r ra) (throw 'bad-rank ri ra))
  100. r))
  101. ri ra))))
  102. (define (verb-final? op)
  103. (procedure? (verb-op op)))
  104. (export verb? verb w/rank verb-op verb-ri verb-actual-ri verb-final? verb-oshape)
  105. ; -------------------------------------------------------------------
  106. ; array-map over already matching frames
  107. ; -------------------------------------------------------------------
  108. ; the fundamental array ops.
  109. (define array-map/frame!
  110. (case-lambda
  111. ; optimizations. @TODO wish it was automatic; would apply elsewhere.
  112. ((o f op a0)
  113. (if (= (length f) (rank o) (rank a0))
  114. (array-map! o op a0)
  115. (array-slice-for-each (length f)
  116. (lambda (o a0)
  117. (array-cell-set! o (op (array-cell-ref a0))))
  118. o a0)))
  119. ((o f op a0 a1)
  120. (let ((oo o))
  121. (if (= (length f) (rank o) (rank a0) (rank a1))
  122. (array-map! o op a0 a1)
  123. (array-slice-for-each (length f)
  124. (lambda (o a0 a1)
  125. (array-cell-set! o (op (array-cell-ref a0) (array-cell-ref a1))))
  126. o a0 a1))))
  127. ((o f op . a)
  128. (if (apply = (length f) (rank o) (map rank a))
  129. (apply array-map! o op a)
  130. (apply array-slice-for-each (length f)
  131. (lambda (o . a)
  132. (array-cell-set! o (apply op (map array-cell-ref a))))
  133. o a)))))
  134. (define (array-atom A)
  135. "array-atom A
  136. Return an atom of A. If A is a zero-size slice of a root array, this function
  137. may return a result that is not in A, but is in the root array."
  138. (if (array? A)
  139. (array-ref (shared-array-root A) 0)
  140. A))
  141. ; convert nested array to straight array, first nesting only. We may need this
  142. ; on the output of array-map/frame! if op output shape can't be computed.
  143. (define (collapse-array type a)
  144. (let ((b (and (positive? (apply * ($ a)))
  145. (let ((b (array-atom a)))
  146. (and (array? b) b)))))
  147. (cond (b (let ((o (apply make-typed-array type *unspecified* (append ($ a) ($ b)))))
  148. (array-slice-for-each (rank a)
  149. (lambda (o a) (array-cell-set! o (array-ref a)))
  150. o a)
  151. o))
  152. ((eq? (array-type a) type) a)
  153. (else (array-copy type a)))))
  154. (define (array-map/frame type oshape f op . a)
  155. (cond ((null? f)
  156. (let ((o (apply op a)))
  157. (if (and (array? o) (not (eq? (array-type o) type)))
  158. (array-copy type o)
  159. o)))
  160. ((or (null? oshape) (pair? oshape))
  161. (let ((o (apply make-typed-array type *unspecified* (append f oshape))))
  162. (apply array-map/frame! o f op a)
  163. o))
  164. (else
  165. ; output cell may not be scalar, so can't use uniform type before collapse.
  166. (collapse-array
  167. type
  168. (let ((o (apply make-array *unspecified* f)))
  169. (apply array-map/frame! o f op a)
  170. o)))))
  171. ; without output. @TODO don't need in order traversal, despite for-each.
  172. (define (array-map/frame-n/o f op . a)
  173. (cond
  174. ((null? f)
  175. (apply op a))
  176. ((apply = (length f) (map rank a))
  177. (apply array-for-each op a))
  178. (else
  179. (apply array-slice-for-each (length f) (lambda a (apply op (map array-cell-ref a))) a))))
  180. (export collapse-array array-atom array-map/frame! array-map/frame)
  181. ; -------------------------------------------------------------------
  182. ; match frames
  183. ; -------------------------------------------------------------------
  184. ; Rank-extend A with cell rank r to frame f.
  185. ; [(a skipped k prefix) ( (-r)-frame of a/k... ) ### (a/k r-cell ...)]
  186. ; [(a skipped k prefix) ( f ...................... ) (a/k r-cell ...)]
  187. ; where ### are the extended axes.
  188. ; @todo the scalar cases should be handled specially so that this isn't needed.
  189. (define (match-frame a f r k)
  190. "Rank-extend a with cell rank r to frame f."
  191. (cond ((length=? (- (rank a) k r) f)
  192. a)
  193. ((not (array? a))
  194. (match-frame (make-array a) f r k))
  195. (else
  196. (apply make-shared-array a
  197. (lambda i (append (take i k) (take (drop i k) (- (rank a) k r)) (take-right i r)))
  198. (append (take ($ a) k) f (take-right ($ a) r))))))
  199. (define (prefix-frame a r k)
  200. "Frame common to all arrays a with cell ranks r, ignoring first k axes."
  201. (fold (lambda (a r f)
  202. (let ((fa (drop-right! (drop ($ a) k) r)))
  203. (let loop ((s f) (sa fa))
  204. (cond ((null? sa) f)
  205. ((null? s) fa)
  206. ((= (car s) (car sa)) (loop (cdr s) (cdr sa)))
  207. (else (error "shape clash" ($ a) r f k))))))
  208. '() a r))
  209. (define (nested-op-frames op k . a)
  210. "Match argument frames for all nested w/rank ops."
  211. (let loop ((op op) (ff '()) (a a) (k k))
  212. (let* ((r (apply verb-actual-ri op (map (lambda (a) (- (rank a) k)) a)))
  213. (f (prefix-frame a r k))
  214. (a (map! (cut match-frame <> f <> k) a r)))
  215. (if (verb-final? op)
  216. (values (let ((vo (verb-oshape op)))
  217. (if (procedure? vo)
  218. (apply vo (map (lambda (a r) (take-right ($ a) r)) a r))
  219. vo))
  220. (concatenate! (reverse! (cons f ff)))
  221. (verb-op op)
  222. ; ply doesn't use r, but fold does.
  223. r a)
  224. (loop (verb-op op) (cons f ff) a (+ k (length f)))))))
  225. ; -------------------------------------------------------------------
  226. ; interface
  227. ; -------------------------------------------------------------------
  228. ; @todo reduce scalar arguments with a closure.
  229. (define (ply/t type op . a)
  230. (let ((op (if (verb? op) op (verb op))))
  231. (receive (oshape f op ri a) (apply nested-op-frames op 0 a)
  232. (apply array-map/frame type oshape f op a))))
  233. (define (ply op . a)
  234. (apply ply/t (array-type* (car a)) op a))
  235. (export prefix-frame match-frame nested-op-frames ply/t ply)
  236. ; -------------------------------------------------------------------
  237. ; ply with reference argument.
  238. ; -------------------------------------------------------------------
  239. ; See (ammend!) http://www.jsoftware.com/help/jforc/modifying_an_array_m.htm#_Toc191734471
  240. ; y[m] = x ... x m} y ... where $x must be a suffix of $(m{ y).
  241. ; See amend! below and how it relates to this.
  242. ;; 0 (<1 1)} 4 4 $ 5
  243. ;; 0 1 2 3 (0)} 4 4 $ 5
  244. ;; 0 (0)} 4 4 $ 5
  245. ;; 0 1 (0)} 4 4 $ 5 -> error
  246. ;; 1 2 (1 1;2 2)} 4 4 $ 5
  247. ;; 1 2 (1 1;1 1)} 4 4 $ 5 -> undefined
  248. ; also look at expand-X in /box.
  249. (define (ply! o op . a)
  250. (let ((op (if (verb? op) op (verb op))))
  251. (receive (oshape f op ri a) (apply nested-op-frames op 0 a)
  252. (if oshape
  253. (let ((suffix (take-right ($ o) (length oshape))))
  254. (if (equal? oshape suffix)
  255. (let ((oo (apply reshape o #f suffix)))
  256. (apply array-map/frame! (array-first oo) f op a)
  257. (array-copy! (apply reshape (array-first oo) (- (tally oo) 1) suffix)
  258. (array-rest oo)))
  259. (error "mismatched arguments")))
  260. (let* ((ss (apply array-map/frame (array-type o) oshape f op a))
  261. (oshape ($ ss)))
  262. (let ((suffix (take-right ($ o) (length oshape))))
  263. (if (equal? oshape suffix)
  264. (let ((oo (apply reshape o #f suffix)))
  265. (array-copy! (apply reshape ss (tally oo) suffix)
  266. oo))
  267. (error "mismatched arguments")))))
  268. o)))
  269. (define (ply!! o op . a)
  270. (let ((op (if (verb? op) op (verb op))))
  271. ; @TODO not using oshape, should maybe compute it separately.
  272. (receive (oshape f op ri o-a) (apply nested-op-frames op 0 o a)
  273. (apply array-map/frame! (car o-a) f op (cdr o-a))
  274. (car o-a))))
  275. ; (ply! (make-array 0 10) (verb (const 2) '()))
  276. ; (ply! (make-array 0 10) (const 2))
  277. (define (ply-n/o op . a)
  278. (let ((op (if (verb? op) op (verb op))))
  279. ; @TODO not using oshape, should maybe compute it separately.
  280. (receive (oshape f op ri a) (apply nested-op-frames op 0 a)
  281. (apply array-map/frame-n/o f op a))))
  282. (export ply! ply!! ply-n/o)
  283. ; -------------------------------------------------------------------
  284. ; selection
  285. ; -------------------------------------------------------------------
  286. (define-record-type <J>
  287. (make-J n from step) J?
  288. (n J-n)
  289. (from J-from)
  290. (step J-step))
  291. (define J
  292. (case-lambda (() #f)
  293. ((n) (make-J n 0 1))
  294. ((n from) (make-J n from 1))
  295. ((n from step) (make-J n from step))))
  296. (define (J-index J i)
  297. (+ (J-from J) (* (J-step J) i)))
  298. (define range
  299. (case-lambda ((from til)
  300. (range from til 1))
  301. ((from til step)
  302. (assert (eq? (>= til from) (>= step 0)))
  303. (J (floor/ (- til from) step) from step))))
  304. ; return the numbers in [0 .. k) not in l, assumed sorted. @todo vectors here.
  305. (define (complement-list k l)
  306. (let loop ((i 0) (l l) (r '()))
  307. (cond ((= i k) r)
  308. ((null? l) (append! r (iota (- k i) i)))
  309. ((< i (car l)) (loop (+ (car l) 1) (cdr l) (append! r (iota (- (car l) i) i))))
  310. ((= i (car l)) (loop (+ i 1) (cdr l) r))
  311. (else (error "bad arguments")))))
  312. (define-record-type <parsed-array-arg>
  313. (make-paa axis rank arg) paa?
  314. (axis paa-axis)
  315. (rank paa-rank)
  316. (arg paa-arg))
  317. (define (parse-from-args a . x)
  318. (define (checked-cdr d) (if (pair? d) (cdr d) (throw 'bad-rank)))
  319. (let*-values
  320. ; c is ((axis rank arg) ...) for array args.
  321. ; b is array after applying shortcuts.
  322. ; s is dimensions of b.
  323. (((c s) (let loop ((c '()) (s '()) (i 0) (x x) (d ($ a)))
  324. (match x
  325. (()
  326. (values (reverse! c) (append! (reverse! s) d)))
  327. (((? boolean?) . xtail)
  328. (loop c (cons (car d) s) (1+ i) xtail (checked-cdr d)))
  329. (((? integer?) . xtail)
  330. (loop c s i xtail (checked-cdr d)))
  331. (((? J? x) . xtail)
  332. (loop c (cons (J-n x) s) (1+ i) xtail (checked-cdr d)))
  333. (((? array? x) . xtail)
  334. (loop (cons (make-paa i (rank x) x) c) (cons (car d) s)
  335. (1+ i) xtail (checked-cdr d))))))
  336. ((b) (apply make-shared-array
  337. a
  338. (lambda u
  339. (let loop ((u u) (x x) (r '()))
  340. (match x
  341. (() (append! (reverse! r) u))
  342. (((or (? boolean?) (? array?)) . xtail)
  343. (loop (cdr u) xtail (cons (car u) r)))
  344. (((? integer? x) . xtail)
  345. (loop u xtail (cons x r)))
  346. (((? J? x) . xtail)
  347. (loop (cdr u) xtail (cons (J-index x (car u)) r))))))
  348. s))
  349. ((lc) (length c))
  350. ((lb) (array-rank b)))
  351. (values c s b lc lb)))
  352. (define (forward-backward b c lb lc)
  353. (let* ((cx (map paa-axis c))
  354. (forward (gradeup (append cx (complement-list (array-rank b) cx))))
  355. (backward
  356. (let loop ((cc '()) (ww '()) (c c) (i 0) (j 0))
  357. (cond ((null? c)
  358. (append! cc (reverse! ww) (iota (- lb i) j)))
  359. ((= (paa-axis (car c)) i)
  360. (loop (append! cc (iota (paa-rank (car c)) j))
  361. ww (cdr c) (+ i 1) (+ j (paa-rank (car c)))))
  362. (else
  363. (loop cc (cons j ww) c (+ i 1) (+ j 1)))))))
  364. (values forward backward)))
  365. (define (from a . x)
  366. "from A . index-list
  367. Rectangular selection operator. Each index applies to one axis of the array
  368. A. The indices can be either integers or integer arrays of any rank. The
  369. result will have rank equal to the sum of the ranks of the indices. The
  370. following shortcuts for common rank-1 indices are accepted:
  371. #t - the whole axis, i.e. #(lbnd lbnd+1 .. ubnd-1 ubnd).
  372. (J n [from=0 [step=1]]) - #(from from+step .. from+(n-1)*step)
  373. (range a b [step=1]) - #(a a+step ... [a+floor((b-a)/step)*step])
  374. If every index is either a scalar or one of the shortcuts above and the
  375. result has rank > 0, the result will be a shared array over the same storage
  376. as A.
  377. If index-list does not cover the rank of A, the missing indices are taken to
  378. be #t. It is an error if index-list is longer than the rank of A.
  379. If the result x has rank 0, (from) will return x, never #0(x).
  380. Examples:
  381. (from A) => A, if the rank of A > 0, but x if A is #0(x)
  382. (from A #t) => A, if the rank of A > 0, otherwise error
  383. (apply from A [list of (rank A) scalars]) => (apply array-ref A [list ...])
  384. (from (i. 2 3) 0) => #(0 1 2)
  385. (from (i. 2 3) 0 #t) => #(0 1 2), i.e. row 0
  386. (from (i. 2 3) #t 0) => #(0 3), i.e. column 0
  387. (from #(#(0 1) #(2)) 1) => #(2))
  388. (from #(#(0 1) #(2)) 1 0) => error
  389. (from (i. 10 2) (J 2 2) #t) => #2((4 5) (6 7))
  390. (from #(1 2 3) #2((0 1) (1 2) (2 0))) => #2((1 2) (2 3) (3 1))
  391. (from (i. 10 10) #(3 4) #(7 9 2)) => #2((37 39 32) (47 49 42))"
  392. (receive (c s b lc lb) (apply parse-from-args a x)
  393. ; selection without need of transpose.
  394. (define (prefix-from b)
  395. (let ((oshape (drop ($ b) lc)))
  396. (apply ply/t (array-type* b)
  397. (let loop ((i 0) (cr (map paa-rank c)))
  398. (if (null? cr)
  399. (apply verb (lambda x (apply array-cell-ref b x)) oshape (make-list lc 0))
  400. (apply w/rank
  401. (loop (+ 1 i) (cdr cr))
  402. (append! (make-list (+ 1 i) 0) (cdr cr)))))
  403. (map paa-arg c))))
  404. (cond ((null? s) (array-ref b))
  405. ; optimizations.
  406. ((null? c) b)
  407. ((= lc lb) (prefix-from b))
  408. (else
  409. ; move explicit indexing to the front of the shape of b, so that it can be done w/rank.
  410. (receive (forward backward) (forward-backward b c lb lc)
  411. (apply transpose-array
  412. (prefix-from (apply transpose-array b forward))
  413. backward))))))
  414. (export <J> J? J J-index J-n paa-arg paa-rank paa-axis parse-from-args from range)
  415. (define (extend-left z s)
  416. "extend-left z s
  417. Broadcast array z to shape s. The shape of z must be a suffix of s."
  418. (assert (equal? (take-right s (rank z)) ($ z)) "cannot extend to shape")
  419. (cond ((null? s) (if (array? z) (array-ref z) z))
  420. ((array? z) (apply make-shared-array z (lambda i (take-right i (array-rank z))) s))
  421. (else (apply make-shared-array (make-array z) (const '()) s))))
  422. (define (extend-right z s)
  423. "extend-left z s
  424. Broadcast array z to shape s. The shape of z must be a prefix of s."
  425. (assert (equal? (take s (rank z)) ($ z)) "cannot extend to shape")
  426. (cond ((null? s) (if (array? z) (array-ref z) z))
  427. ((array? z) (apply make-shared-array z (lambda i (take i (array-rank z))) s))
  428. (else (apply make-shared-array (make-array z) (const '()) s))))
  429. ; @TODO Factor out more pieces shared with (from).
  430. ; @TODO Consider in the light of (ply!).
  431. (define (amend! a z . x)
  432. "amend! A z indices ... -> modified A
  433. Rectangular assignment operator.
  434. Think of this as (array-copy! z (from A indices ...)), except that it will
  435. work for any indices that work with (from), even for those that
  436. (from A indices) doesn't return an assignable array for.
  437. ($ z) must be a suffix of ($ (from A indices ...)); if (from A indices ...)
  438. has higher rank than z, each of the (rank z)-cells of (from A indices) will
  439. be assigned z. This behavior is after J amend.
  440. Examples:
  441. (define A (array-copy #(10 20 30 40)))
  442. (amend! A #(a b c) #(3 0 2))
  443. => A: #(b 20 c a)
  444. @TODO others..."
  445. (receive (c s b lc lb) (apply parse-from-args a x)
  446. ; selection without need of transpose.
  447. (define (prefix-amend! b z)
  448. (apply ply-n/o
  449. (let loop ((i 0) (cr (map paa-rank c)))
  450. (if (null? cr)
  451. (apply verb (lambda (z . x) (apply array-cell-set! b z x)) #f '_ (make-list lc 0))
  452. (apply w/rank
  453. (loop (+ 1 i) (cdr cr))
  454. (- (car cr))
  455. (append! (make-list (+ 1 i) 0) (cdr cr)))))
  456. z (map paa-arg c)))
  457. (cond
  458. ; optimization.
  459. ((and (null? c) (not (array? z))) (array-fill! b z))
  460. ((= lc lb)
  461. (prefix-amend! b (extend-left z (concatenate (map (lambda (c) ($ (paa-arg c))) c)))))
  462. (else
  463. ; move explicit indexing to the front of the shape of b, so that it can be done w/rank.
  464. (receive (forward backward) (forward-backward b c lb lc)
  465. (prefix-amend!
  466. (apply transpose-array b forward)
  467. (apply transpose-array
  468. ; expected-z-shape: ---($b[i0])--($b[i1])--- will become ---($i0)--($i1)---.
  469. ; @TODO compute along [backward]. @TODO compute only if required.
  470. (extend-left z
  471. (let ((sz (map list ($ b))))
  472. (for-each (lambda (c) (list-set! sz (paa-axis c) ($ (paa-arg c)))) c)
  473. (concatenate sz)))
  474. (gradeup backward))))))
  475. a))
  476. (export extend-left extend-right amend!)
  477. ; ---------------------------------------------------------------------
  478. ; axis operations
  479. ; ---------------------------------------------------------------------
  480. (define* (rollaxis a s #:optional (d -1))
  481. "rollaxis a s [d: -1]
  482. Transpose axis s of array a to d by shifting the axes in between. Negative
  483. axes count from the end.
  484. ($ (rollaxis (i. 2 3 4 5) 0 -1)) => (3 4 5 2)
  485. ($ (rollaxis (i. 2 3 4 5) -1 0)) => (5 2 3 4)
  486. ($ (rollaxis (i. 2 3 4 5) 0 2)) => (3 4 2 5)
  487. ($ (rollaxis (i. 2 3 4 5) 3 1)) => (2 5 3 4)
  488. "
  489. (let ((s (if (negative? s) (+ s (rank a)) s))
  490. (d (if (negative? d) (+ d (rank a)) d)))
  491. ; transpose-array args are the destination of each source axis.
  492. (cond ((< s d)
  493. (apply transpose-array a (append! (iota s)
  494. (list d)
  495. (iota (- d s) s)
  496. (iota (- (rank a) d 1) (+ d 1)))))
  497. ((> s d)
  498. (apply transpose-array a (append! (iota d)
  499. (iota (- s d) (+ d 1))
  500. (list d)
  501. (iota (- (rank a) s 1) (+ s 1)))))
  502. (else a))))
  503. (export rollaxis)
  504. ; ---------------------------------------------------------------------
  505. ; outer product
  506. ; @todo Define a verb instead of an operation, then do (ply (out ..) a ..) (or ply!).
  507. ; But need input ranks.
  508. ; ---------------------------------------------------------------------
  509. (define (out/t type op . a)
  510. (let* ((op (if (verb? op) op (verb op)))
  511. (ra (map rank a))
  512. (ri (apply verb-actual-ri op ra)))
  513. (let ((n (length a)))
  514. (apply
  515. ply/t type
  516. (let loop ((i 1))
  517. (if (>= i n)
  518. op
  519. (apply w/rank (loop (+ i 1)) (append (take ri i) (drop ra i)))))
  520. a))))
  521. (define (out op . a) (apply out/t (array-type* (car a)) op a))
  522. (export out/t out)