lib.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. ; -*- mode: scheme; coding: utf-8 -*-
  2. ; (c) Daniel Llorens - 2018-2019
  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. ;;; Commentary:
  8. ;; Library for Newra - hub, plus old arrays compatiblity & misc.
  9. ;;; Code:
  10. (define-module (newra lib)
  11. #:export (ra-index-map!
  12. make-ra make-typed-ra make-ra-shared ra->list
  13. array->ra ra->array as-ra
  14. ra-i ra-iota
  15. ra-copy ra-map
  16. ra-fold))
  17. (import (only (srfi :1) fold every any iota drop xcons) (srfi :26) (srfi :71)
  18. (ice-9 control) (ice-9 match) (only (rnrs base) vector-map vector-for-each)
  19. (newra base) (newra map) (newra reshape))
  20. ; ----------------
  21. ; fold - probably better ways to do this by fixing or extending (newra map)
  22. ; ----------------
  23. (define-inlinable-case ra-fold
  24. (case-lambda
  25. "
  26. Reduce arrays @var{a} by (... (@var{kons} (@var{cons} @var{knil} @var{a}0 ...)
  27. @var{a}1 ... ) ...) where (@var{a}0 ...), (@var{a}1 ...) is the row-major ravel
  28. of @var{a}...
  29. See also: ra-fold ra-map! ra-for-each ra-slice-for-each
  30. "
  31. ((kons knil)
  32. knil)
  33. ((kons knil a0)
  34. (ra-for-each (lambda (x0) (set! knil (kons knil x0))) a0)
  35. knil)
  36. ((kons knil a0 a1)
  37. (ra-for-each (lambda (x0 x1) (set! knil (kons knil x0 x1))) a0 a1)
  38. knil)
  39. ((kons knil a0 a1 a2)
  40. (ra-for-each (lambda (x0 x1 x2) (set! knil (kons knil x0 x1 x2))) a0 a1 a2)
  41. knil)
  42. ((kons knil . args)
  43. (apply ra-for-each (lambda x (set! knil (apply kons knil x))) args)
  44. knil)))
  45. ; ----------------
  46. ; transition help
  47. ; ----------------
  48. (define (array->ra a)
  49. (let ((dims (list->vector
  50. (map (lambda (b i) (make-dim (- (cadr b) (car b) -1) (car b) i))
  51. (array-shape a)
  52. (shared-array-increments a)))))
  53. (make-ra-root (shared-array-root a) dims
  54. (- (shared-array-offset a) (ra-offset 0 dims)))))
  55. (define (ra->array ra)
  56. (when (eq? 'd (ra-type ra))
  57. (throw 'nonconvertible-type (ra-type ra)))
  58. (apply make-shared-array (ra-root ra)
  59. (lambda i (list (apply ra-pos (ra-zero ra) (ra-dims ra) i)))
  60. (vector->list (vector-map (lambda (dim) (list (dim-lo dim) (dim-hi dim)))
  61. (ra-dims ra)))))
  62. (define (make-typed-ra type value . d)
  63. "
  64. make-typed-ra type value d ...
  65. Return a new ra of type TYPE and shape D. The ra will be initialized with
  66. VALUE. VALUE may be *unspecified*, in which case the ra may be left
  67. uninitialized.
  68. See also: make-ra
  69. "
  70. (make-ra-new type value (apply c-dims d)))
  71. (define (make-ra value . d)
  72. "
  73. make-ra value d ...
  74. Equivalent to (make-typed-ra #t value d ...).
  75. See also: make-typed-ra
  76. "
  77. (make-ra-new #t value (apply c-dims d)))
  78. (define (make-ra-shared oldra mapfunc . d)
  79. "
  80. make-ra-shared oldra mapfunc ... d
  81. Return a shared array with shape D over the root of OLDRA. MAPFUNC is a function
  82. of the indices of the new array and must return a list of indices to
  83. OLDRA.
  84. MAPFUNC is only called the minimum (+ 1 (length D)) times that are needed in
  85. order to determine an affine function from one set of indices to the other.
  86. For example, to displace the bounds of an array without affecting its contents:
  87. guile> (define x (ra-i 3 2))
  88. guile> x
  89. #%2d:3:2((0 1) (2 3) (4 5))
  90. guile> (make-ra-shared x (lambda (i j) (list (- i 2) (+ j 3))) '(2 4) '(-3 -2))
  91. #%2d@2:3@-3:2((0 1) (2 3) (4 5))
  92. See also: make-ra, FIXME ra-affine-map
  93. "
  94. ; get lo & len, won't use step except if the result is empty.
  95. (let* ((oldra (ra-check oldra))
  96. (dims (apply c-dims d))
  97. (newrank (vector-length dims)))
  98. ; if the result *is* empty then it has no valid indices, so we cannot call mapfunc.
  99. (let emptycheck ((k 0))
  100. (if (< k newrank)
  101. (if (positive? (dim-len (vector-ref dims k)))
  102. (emptycheck (+ 1 k))
  103. (make-ra-root (%%ra-root oldra) dims (%%ra-zero oldra)))
  104. (let* ((los (vector->list (vector-map dim-lo dims)))
  105. (ref (apply ra-pos (%%ra-zero oldra) (%%ra-dims oldra) (apply mapfunc los)))
  106. (dims (vector-map
  107. (lambda (dim step) (make-dim (dim-len dim) (dim-lo dim) step))
  108. dims
  109. (let ((steps (make-vector newrank 0)))
  110. (let loop ((k 0))
  111. (cond
  112. ((= k newrank) steps)
  113. (else
  114. (vector-set!
  115. steps k
  116. (if (positive? (dim-len (vector-ref dims k)))
  117. (let ((ii (list-copy los)))
  118. (list-set! ii k (+ 1 (list-ref los k)))
  119. (- (apply ra-pos (%%ra-zero oldra) (%%ra-dims oldra) (apply mapfunc ii)) ref))
  120. 0))
  121. (loop (+ k 1)))))))))
  122. (make-ra-root (%%ra-root oldra) dims (- ref (ra-offset 0 dims))))))))
  123. ; FIXME Depends on traversal order of ra-for-each.
  124. (define (ra->list ra)
  125. "
  126. ra->list ra
  127. Return a nested list of the elements of ra RA. For example, if RA is a 1-rank
  128. ra, the list contains the elements of RA; if RA is a 2-rank ra, the list
  129. contains a list for each of the rows of RA; and so on.
  130. See also: as-ra
  131. "
  132. (let ((ra (ra-check ra))
  133. (rank (%%ra-rank ra)))
  134. (cond
  135. ((zero? rank) (ra-ref ra))
  136. (else
  137. (let ((ra (apply ra-reverse ra (iota rank)))
  138. (dimk (vector-ref (%%ra-dims ra) (- rank 1))))
  139. (let loop-rank ((ra ra))
  140. (cond
  141. ((= 1 (%%ra-rank ra))
  142. (if (> (dim-len dimk) 20)
  143. (ra-fold xcons '() ra)
  144. (let loop-dim ((l '()) (i (dim-lo dimk)))
  145. (if (> i (dim-hi dimk))
  146. l
  147. (loop-dim (cons (ra-ref ra i) l) (+ i 1))))))
  148. (else
  149. (let ((l '()))
  150. (ra-slice-for-each 1 (lambda (x) (set! l (cons (loop-rank x) l))) ra)
  151. l)))))))))
  152. ; Similar to (@ (newra) ra-for-each-slice-1) - since we cannot unroll. It
  153. ; might be cheaper to go Fortran order (building the index lists back to front);
  154. ; should try that. C order and set-cdr! is how oldra does it.
  155. ; This function is provided for compatibility with oldra; generally we shouldn't
  156. ; be building index lists.
  157. (define (ra-index-map! ra op)
  158. "
  159. ra-index-map! ra op -> ra
  160. Apply @var{op} to the indices of each element of @var{ra} in turn, storing the
  161. result in the corresponding element. The order of iteration is unspecified.
  162. This function returns the modified @var{ra}.
  163. For example:
  164. @example
  165. guile> (define x (make-ra 0 2 3))
  166. guile> (ra-index-map! x (lambda x x))
  167. guile> x
  168. #%2:2:3(((0 0) (0 1) (0 2)) ((1 0) (1 1) (1 2)))
  169. @end example
  170. See also: @code{ra-iota} @code{ra-i}
  171. "
  172. (let* ((kk (ra-rank ra))
  173. (ii (make-list kk))
  174. (los lens ((@ (newra map) ra-slice-for-each-check) kk ra)))
  175. (if (= kk 0)
  176. (ra-set! ra (apply op ii))
  177. (let loop-rank ((k 0) (ra ra) (endi ii))
  178. (let* ((lo (vector-ref los k))
  179. (end (+ lo (vector-ref lens k))))
  180. (if (= (+ 1 k) kk)
  181. (let loop-dim ((i lo))
  182. (if (= i end)
  183. (set-car! endi lo)
  184. (begin
  185. (set-car! endi i)
  186. (ra-set! ra (apply op ii) i)
  187. (loop-dim (+ i 1)))))
  188. (let loop-dim ((i lo))
  189. (if (= i end)
  190. (set-car! endi lo)
  191. (begin
  192. (set-car! endi i)
  193. (loop-rank (+ k 1) (ra-slice ra i) (cdr endi))
  194. (loop-dim (+ i 1)))))))))
  195. ra))
  196. ; ----------------
  197. ; functions not found in oldra, or significantly extended
  198. ; ----------------
  199. ; FIXME partial implementation.
  200. (define* (as-ra ra #:key (type (ra-type ra)) (new? #f))
  201. (cond ((and (eq? (ra-type ra) type) (not new?)) ra)
  202. (else (ra-copy! (make-ra-new
  203. type *unspecified*
  204. (apply c-dims (map dim-len (vector->list (ra-dims ra)))))
  205. ra))))
  206. (define (ra-i . i)
  207. (make-ra-root (make-aseq) (apply c-dims i)))
  208. ; FIXME (ra-iota #f ...) is somewhat inconsistent with (ra-i #t). Maybe accept ra-iota #t instead?
  209. (define ra-iota
  210. (case-lambda*
  211. (()
  212. ; lo #f so it matches axes with any lo
  213. (make-ra-root (make-aseq) (vector (make-dim #f #f 1))))
  214. ((len #:optional (lo 0) (step 1))
  215. (make-ra-root (make-aseq lo step) (vector (make-dim len 0 1))))))
  216. ; oldra has array-copy in (ice-9 arrays). Something of the sort.
  217. ; FIXME handling of dead axes could be faster - maybe c-dims should be written differently.
  218. (define ra-copy
  219. (case-lambda
  220. "
  221. @deffn @w{function} ra-copy src
  222. @deffnx @w{function} ra-copy type src
  223. Copy the contents of @var{src} into a new ra of type @var{type} and the same
  224. shape as @var{src}. @var{type} defaults to @code{(ra-type src)} if not given, or
  225. @code{#t} if @code{(ra-type src)} is @code{'d}.
  226. If @var{src} has dead axes, those are preserved in the result.
  227. See also: ra-copy! as-ra
  228. "
  229. ((ra) (ra-copy #f ra))
  230. ((type ra)
  231. (let* ((type (or type (match (ra-type ra) ('d #t) (t t))))
  232. (shape (map (match-lambda
  233. (($ <dim> len lo step)
  234. (let ((lo (or lo 0)))
  235. (list lo
  236. (if len
  237. (+ lo len -1)
  238. (if (zero? step)
  239. lo
  240. (throw 'cannot-copy-infinite-ra ra)))))))
  241. (vector->list (ra-dims ra))))
  242. ; copy destination needs the singletons else the lens don't match.
  243. ; FIXME we could have dead axes match dead axes.
  244. (rb (ra-copy! (make-ra-new type *unspecified* (apply c-dims shape)) ra)))
  245. ; preserve dead axes in the result.
  246. (if (any not (ra-dimensions ra))
  247. (make-ra-root (ra-root rb)
  248. (vector-map (lambda (a b) (if (dim-len a) b a)) (ra-dims ra) (ra-dims rb))
  249. (ra-zero rb))
  250. rb)))))
  251. (define (ra-map type op rx0 . rx)
  252. "
  253. Same as @code{(ra-map! ra rx0 rx ...)}, except that the result is created from
  254. @var{type} and the shapes of @var{rx} ...
  255. If @var{type} is #f, then the type of the result is @code{(ra-type rx0)}, or
  256. @code{#t} if @code{(ra-type rx0)} is @code{'d}.
  257. See also: ra-map!
  258. "
  259. (let* ((k (fold (lambda (a b) (max b (ra-rank a))) (ra-rank rx0) rx))
  260. ; FIXME vector->list is ugly. Need to refine/formalize the 'arguments match' routine
  261. (lo len (apply ra-slice-for-each-check k rx0 rx)))
  262. (apply ra-map! (apply make-typed-ra (or type (match (ra-type rx0) ('d #t) (t t)))
  263. *unspecified*
  264. (map (lambda (lo len) (list lo (+ lo len -1)))
  265. (vector->list lo) (vector->list len)))
  266. op rx0 rx)))