basic.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. ; (c) Daniel Llorens - 2012-2015
  2. ; No local dependencies beyond (assert).
  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. (define-module (ploy basic))
  8. (import (srfi srfi-1) (srfi srfi-8) (ice-9 format) (only (ploy assert) assert))
  9. ; @todo Array gradeup/down and dyadic (grade a by the order of b).
  10. ; also serves as permutation inverse.
  11. ; cf http://www.jsoftware.com/jwiki/Essays/Inverse%20Permutation
  12. (define (gradeup l)
  13. (map cadr (sort (zip l (iota (length l)))
  14. (lambda (a b) (< (car a) (car b))))))
  15. (export gradeup)
  16. ; @todo do not use map.
  17. ; @todo do not run over the full lists for (length=? number . lists).
  18. (define (length=? . a)
  19. (cond ((null? a) #t)
  20. ((number? (car a))
  21. (apply = (car a) (map length (cdr a))))
  22. ((null? (car a))
  23. (every null? (cdr a)))
  24. (else
  25. (and (not (any null? (cdr a)))
  26. (apply length=? (map cdr a))))))
  27. (define (not= x y) (not (= x y)))
  28. (export length=? not=)
  29. (define array-fold
  30. (case-lambda
  31. ((op init)
  32. init)
  33. ((op init a0)
  34. (array-for-each (lambda (x0) (set! init (op x0 init))) a0)
  35. init)
  36. ((op init a0 a1)
  37. (array-for-each (lambda (x0 x1) (set! init (op x0 x1 init))) a0 a1)
  38. init)
  39. ((op init . args)
  40. (apply array-for-each (lambda x (set! init (apply op (append! x (list init))))) args)
  41. init)))
  42. (export array-fold)
  43. (define tally array-length)
  44. (define ($ A) (if (array? A) (array-dimensions A) '()))
  45. (define array-dim (lambda (A i) (list-ref ($ A) i)))
  46. (define $. array-dim)
  47. (define (make-array-of-size c type)
  48. (apply make-typed-array type *unspecified* ($ c)))
  49. (define* (make-random-array dimensions #:key (type 'f64) (f values))
  50. (let ((a (apply make-typed-array type *unspecified* dimensions)))
  51. (array-map! (array-contents a) (lambda x (f (random:uniform))))
  52. a))
  53. (define array-copy
  54. (case-lambda
  55. ((c) (array-copy #f c))
  56. ((type c)
  57. (let ((d (make-array-of-size c (or type (array-type c)))))
  58. (array-copy! c d)
  59. d))))
  60. (define (array-map type op a0 . args)
  61. (if (array? a0)
  62. (let ((d (make-array-of-size a0 (or type (array-type a0)))))
  63. (apply array-map! d op a0 args)
  64. d)
  65. (apply op a0 args)))
  66. (export tally $ array-dim $. make-array-of-size make-random-array
  67. array-copy array-map)
  68. (define (index-rect s i) (fold (lambda (ss ii c) (+ ii (* ss c))) 0 s i))
  69. (define (index-rect-lsd-first s i)
  70. (index-rect (reverse s) (reverse i)))
  71. (export index-rect index-rect-lsd-first)
  72. ; This is used (redefined in tm's files) in box/jast/.geos.
  73. (define (list-product . rest)
  74. "make a list of all lists with 1st element from 1st arg, 2nd element from
  75. 2nd arg, etc.
  76. (list-product '(1 2)) => ((1) (2))
  77. (list-product '(1 2) '(3 4)) => ((1 3) (1 4) (2 3) (2 4))
  78. (list-product '(1 2) '(3 4) '(5 6)) => ((1 3 5) (1 3 6) etc.)"
  79. (cond
  80. ((null? rest)
  81. '())
  82. ((null? (cdr rest))
  83. (map list (car rest)))
  84. (else
  85. (let ((list-product-rest (apply list-product (cdr rest))))
  86. (append-map!
  87. (lambda (p)
  88. (map
  89. (lambda (v) (cons p v))
  90. list-product-rest))
  91. (car rest))))))
  92. (export list-product)
  93. ; @todo Inefficient.
  94. (define (interleave la lb)
  95. "Interleave two lists"
  96. (if (null? la)
  97. '()
  98. (append
  99. (list (car la) (car lb))
  100. (interleave (cdr la) (cdr lb)))))
  101. (export interleave)
  102. (define (list-accumulate* l op)
  103. (if (null? l)
  104. l
  105. (fold (lambda (a new) (cons (+ (op a) (car new)) new))
  106. (list (op (car l)))
  107. (cdr l))))
  108. (define (list-accumulate l) (reverse! (list-accumulate* l values)))
  109. (export list-accumulate* list-accumulate)
  110. (define-syntax time
  111. (syntax-rules ()
  112. ((_ e0 ...)
  113. (let ((start (get-internal-run-time)))
  114. e0 ...
  115. (exact->inexact (/ (- (get-internal-run-time) start)
  116. internal-time-units-per-second))))))
  117. (define-syntax walltime
  118. (syntax-rules ()
  119. ((_ e0 ...)
  120. (let ((start (get-internal-real-time)))
  121. e0 ...
  122. (exact->inexact (/ (- (get-internal-real-time) start)
  123. internal-time-units-per-second))))))
  124. (define (UTC-date-string)
  125. (strftime "%Y-%m-%dT%H:%M:%SZ" (gmtime (current-time))))
  126. (define (UTC-date-string-basic)
  127. (strftime "%Y%m%dT%H%M%SZ" (gmtime (current-time))))
  128. (export time walltime UTC-date-string UTC-date-string-basic)
  129. (define (format! s . args)
  130. (apply format #t (string-append s "\n~!") args))
  131. (export format!)
  132. (define (pk-shape s A) (format! "~a [~a]" s ($ A)) A)
  133. (define (pk=> s f A) (format! "~a [~a]" s (f A)) A)
  134. (export pk-shape pk=>)
  135. (define (conj x) (make-rectangular (real-part x) (- (imag-part x))))
  136. (define-inlinable (sqr x) (* x x))
  137. (define-inlinable (sqrm x) (+ (sqr (real-part x)) (sqr (imag-part x))))
  138. (export conj sqr sqrm)
  139. ; @todo replace with (over) when it's not slower.
  140. (define sqrm-reduce
  141. (case-lambda
  142. ((a)
  143. (let ((end (tally a)))
  144. (let loop ((i 0) (c 0))
  145. (if (< i end)
  146. (loop (+ i 1) (+ c (sqrm (array-ref a i))))
  147. c))))
  148. ((a b)
  149. (let ((end (tally a)))
  150. (let loop ((i 0) (c 0))
  151. (if (< i end)
  152. (loop (+ i 1) (+ c (sqrm (- (array-ref a i) (array-ref b i)))))
  153. c))))))
  154. (define sqrm-reduce*
  155. (case-lambda
  156. ((a)
  157. (array-fold (lambda (a c) (+ c (sqrm a))) 0 a))
  158. ((a b)
  159. (array-fold (lambda (a b c) (+ c (sqrm (- a b)))) 0 a b))))
  160. ;; @TODO sqrm-reduce* shouldn't be slower than sqrm-reduce.
  161. ;; (define A (array-copy 'f64 (i. #e1e7)))
  162. ;; (define B (array-copy #t (array-copy 'f64 (i. #e1e7))))
  163. ;; ,profile (sqrm-reduce A)
  164. (define v-norm
  165. (case-lambda
  166. "v-norm a [b] => (sqrt (sqrm-reduce a [b]))"
  167. ((a) (sqrt (sqrm-reduce a)))
  168. ((a b) (sqrt (sqrm-reduce a b)))))
  169. (export sqrm-reduce v-norm)
  170. (define (rank A)
  171. (if (array? A) (array-rank A) 0))
  172. (export rank)
  173. ; ---------------------------------------------------------------------
  174. ; reference implementations of functions in lloda-array-support branch in Guile.
  175. ; Uncomment if your Guile doesn't have them (untested).
  176. ; ---------------------------------------------------------------------
  177. ;; (define (array-slice a . i)
  178. ;; (let ((li (length i)))
  179. ;; (apply make-shared-array a
  180. ;; (lambda t (append i t))
  181. ;; (drop ($ a) li))))
  182. ;; (define (array-cell-ref a . i)
  183. ;; (let ((li (length i)))
  184. ;; (if (= (rank a) li)
  185. ;; (apply array-ref a i)
  186. ;; (apply make-shared-array a
  187. ;; (lambda t (append i t))
  188. ;; (drop ($ a) li)))))
  189. ;; (define (array-cell-set! a o . i)
  190. ;; (let ((li (length i)))
  191. ;; (if (= (rank a) li)
  192. ;; (apply array-set! a o i)
  193. ;; (array-copy! o (apply array-cell-ref a i)))))
  194. ;; (define (array-slice-for-each frank op . a)
  195. ;; (let ((frame (take ($ (car a)) frank)))
  196. ;; (unless (every (lambda (a) (equal? frame (take ($ a) frank))) (cdr a))
  197. ;; (throw 'mismatched-argument-frames frank (map $ a)))
  198. ;; (array-index-map! (apply make-shared-array (make-array #t) (const '()) frame)
  199. ;; (lambda i (apply op (map (lambda (a) (apply array-slice a i)) a))))))
  200. ;; (export array-slice array-cell-ref array-cell-set! array-slice-for-each)
  201. ; ---------------------------------------------------------------------
  202. ; misc
  203. ; ---------------------------------------------------------------------
  204. ; Slow, since array-index-map! is C and the λ can't be inlined.
  205. (define (i. . args)
  206. (let ((a (apply make-array *unspecified* args)))
  207. (array-index-map! (array-contents a) identity)
  208. a))
  209. (define iota.
  210. (case-lambda
  211. ((n z s)
  212. (if (negative? n)
  213. (error "bad n for iota." n)
  214. (let ((a (make-array *unspecified* n)))
  215. (array-index-map! a (lambda (i) (+ (* i s) z)))
  216. a)))
  217. ((n z) (iota. n z 1))
  218. ((n) (iota. n 0 1))))
  219. (define (linspace. from to n)
  220. "(linspace. from to n) - Return vector of n elements between from and to"
  221. (cond ((<= n 1) (iota. n from))
  222. (else (iota. n from (/ (- to from) (- n 1))))))
  223. (define (linspace-m. from to n)
  224. "(linspace-m. from to n) - Return vector of n elements between from and to"
  225. (cond ((<= n 1) #())
  226. (else (iota. (- n 1) from (/ (- to from) (- n 1))))))
  227. (export i. iota. linspace. linspace-m.)
  228. ; @todo Actual type deduction.
  229. (define (array-type* a)
  230. (cond ((array? a) (array-type a))
  231. ((and (number? a) (inexact? a))
  232. (if (real? a) 'f64 'c64))
  233. (else #t)))
  234. (export array-type*)
  235. ; ---------------------------------------------------------------------
  236. ; reshape
  237. ; ---------------------------------------------------------------------
  238. (define (ravel a)
  239. (or (array-contents a) (array-contents (array-copy (array-type* a) a))))
  240. ; Unravel, C order, repeat or clip, try to avoid copies. Equivalent to J (s $ ,a).
  241. ; Others:
  242. ; PDL reshape (pdl.perl.org/PDLdocs/Core.html#reshape) Unravel, pad with 0. Oddities.
  243. ; Octave reshape. Fortran order, requires size match.
  244. ; J dyad $ (http://www.jsoftware.com/help/jforc/declarations.htm#_Toc191734328). Does not unravel, works with items of a. Repeat or clip.
  245. ; Fortran RESHAPE (http://www.nsc.liu.se/~boein/f77to90/a5.html#section17). Unravel, Fortran order, requires size match unless pad is given, otherwise pad. Optional reorder.
  246. (define (reshape a . s)
  247. "reshape A . s
  248. Reshape C-order ravel of array a to dimensions s, which must be nonnegative
  249. integers. One of the dimensions s may be #t, in which case it is deduced from
  250. the other dimensions and the size of A's ravel.
  251. Examples:
  252. (reshape (i. 2 3) 6) => #(0 1 2 3 4 5)
  253. (reshape (i. 2 3) 5) => #(0 1 2 3 4)
  254. (reshape (i. 2 3) 7) => #(0 1 2 3 4 5 0)
  255. (reshape (i. 2 3) 3 2) => #2((0 1) (2 3) (4 5))
  256. (reshape (i. 2 3) 2 2 2) => #3(((0 1) (2 3)) ((4 5) (0 1)))
  257. (reshape (i. 2 3) 4 2) => #2((0 1) (2 3) (4 5) (0 1))
  258. (reshape (i. 2 3) #t 2) => #2((0 1) (2 3) (4 5))
  259. (reshape (i. 2 3) 2 #t) => #2((0 1 2) (3 4 5))
  260. (reshape (i. 2 3) #t) => #(0 1 2 3 4 5)
  261. (reshape (i. 2 3) 0 3) => #2:0:3()
  262. (reshape (i. 2 3) 0) => #()
  263. (reshape (i. 2 3) 0 0 0) => #3()
  264. (reshape (i. 2 3) 0 #t) => error (can't deduce placeholder)
  265. (reshape (i. 2 3) 4 #t) => error (can't deduce placeholder)
  266. (reshape (i. 2 3)) => 0"
  267. (define mk make-shared-array)
  268. (if (array? a)
  269. (let* ((z ($ a)) (lz (apply * z)))
  270. (let loopb ((ss s) (ls 1))
  271. ; remove placeholder.
  272. (cond ((null? ss))
  273. ((boolean? (car ss))
  274. (let ((quot (apply * ls (cdr ss))))
  275. (when (not (positive? quot)) (throw 'cannot-deduce-dimension-with-empty-result-shape quot))
  276. (let ((pv (/ lz quot)))
  277. (when (not (and (integer? pv) (>= pv 0))) (throw 'bad-placeholder pv))
  278. (set-car! ss pv)
  279. (set! ls lz))))
  280. ((integer? (car ss))
  281. (loopb (cdr ss) (* ls (car ss))))
  282. (else (throw 'bad-dimension-to-reshape (car ss))))
  283. (let loopr ((rz (reverse z)) (rs (reverse s)))
  284. (cond
  285. ; select.
  286. ((null? rs)
  287. (apply array-cell-ref a (make-list (length rz) 0)))
  288. ; tile.
  289. ((null? rz)
  290. (apply mk a (lambda i (drop i (length rs))) s))
  291. ((= (car rz) (car rs))
  292. (loopr (cdr rz) (cdr rs)))
  293. (else
  294. (let ((ls (apply * s))
  295. (a1 (ravel a)))
  296. (if (>= lz ls)
  297. ; select ravel.
  298. (apply mk a1 (lambda i (list (index-rect s i))) s)
  299. (receive (q r) (floor/ ls lz)
  300. ; drop prefix strides.
  301. (or (and (zero? r)
  302. (let loops ((ss s) (p 1) (w 0))
  303. (cond
  304. ((= p q)
  305. (apply mk a1 (lambda i (list (index-rect ss (drop i w)))) s))
  306. ((< p q) (loops (cdr ss) (* p (car ss)) (+ 1 w)))
  307. (else #f))))
  308. ; copy.
  309. (let* ((b (apply make-typed-array (array-type* a) *unspecified* s))
  310. (b1 (array-contents b)))
  311. (array-copy!
  312. (mk a1 (lambda i (cdr i)) q lz)
  313. (mk b1 (lambda (i j) (list (+ j (* lz i)))) q lz))
  314. (array-copy!
  315. (mk a1 list r)
  316. (mk b1 (lambda (j) (list (+ j (* lz q)))) r))
  317. b))))))))))
  318. (apply reshape (make-array a) s)))
  319. (define (reshape-strict A . s)
  320. (unless (= (apply * ($ A)) (apply * s))
  321. (throw 'mismatched-sizes ($ A) s))
  322. (apply reshape A s))
  323. (export ravel reshape reshape-strict)