test-ffi-blis.scm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. ; -*- mode: scheme; coding: utf-8 -*-
  2. ; Tests for (ffi blis).
  3. ; (c) Daniel Llorens - 2014-2015, 2019-2020
  4. ; This library is free software; you can redistribute it and/or modify it under
  5. ; the terms of the GNU Lesser General Public License as published by the Free
  6. ; Software Foundation; either version 3 of the License, or (at your option) any
  7. ; later version.
  8. (import (ffi blis) (srfi srfi-64) (srfi srfi-1) (ice-9 match) (srfi srfi-26) (ice-9 arrays))
  9. (include "common.scm")
  10. (set! test-log-to-file #f)
  11. (test-begin "ffi-blis")
  12. (define (apply-transpose-flag A flag)
  13. (cond ((or (eq? flag BLIS-NO-TRANSPOSE) (eq? flag BLIS-NO-CONJUGATE)) A)
  14. ((eq? flag BLIS-TRANSPOSE) (transpose-array A 1 0))
  15. ((or (eq? flag BLIS-CONJ-NO-TRANSPOSE) (eq? flag BLIS-CONJUGATE))
  16. (let ((B (array-copy A))) (array-map! B conj A) B))
  17. ((eq? flag BLIS-CONJ-TRANSPOSE)
  18. (let ((B (array-copy A))) (array-map! B conj A) (transpose-array B 1 0)))
  19. (else (throw 'bad-transpose-flag flag))))
  20. ; to be disabled/relaxed for specific tests, see below
  21. (blis-error-checking-level-set! BLIS_FULL_ERROR_CHECKING)
  22. ; ---------------------------------
  23. ; Test types
  24. ; ---------------------------------
  25. (define-syntax for-each-lambda
  26. (lambda (x)
  27. (syntax-case x ()
  28. ((_ ((a b) ...) e0 e ...)
  29. #'(for-each (lambda (a ...) e0 e ...) b ...)))))
  30. (define* (test-approximate-array tag test expected err)
  31. (test-begin tag)
  32. (array-for-each (lambda (test expected) (test-approximate test expected err))
  33. test expected)
  34. (test-end tag))
  35. (define (scalar-cases stype)
  36. (match stype
  37. ((or 'f32 'f64) '(-1 0 2))
  38. ((or 'c32 'c64) '(1-1i 1+1i 0 2))))
  39. ; ---------------------------------
  40. ; ?amaxv
  41. ; ---------------------------------
  42. (test-equal 6 (blis-amaxv #c64(1 2 3 4 2 -1 -8 3+3i)))
  43. (test-equal 7 (blis-amaxv #c64(1 2 3 4 2 -1 -8 5+5i)))
  44. ; ---------------------------------
  45. ; ?setv
  46. ; ---------------------------------
  47. (let* ((X (array-copy #f64(1 2 3 4)))
  48. (Y (blis-dsetv! BLIS-NO-CONJUGATE 3. X)))
  49. (test-eq X Y)
  50. (test-equal X (make-typed-array 'f64 3 4)))
  51. (let* ((X (array-copy #c64(1 2 3 4)))
  52. (Y (blis-zsetv! BLIS-CONJUGATE 3+9i X)))
  53. (test-eq X Y)
  54. (test-equal X (make-typed-array 'c64 3-9i 4)))
  55. (let* ((X (array-copy #c64(1 2 3 4)))
  56. (Y (blis-setv! BLIS-NO-CONJUGATE 3+9i X)))
  57. (test-eq X Y)
  58. (test-equal X (make-typed-array 'c64 3+9i 4)))
  59. ; ---------------------------------
  60. ; ?setm
  61. ; ---------------------------------
  62. (let* ((A (array-copy #2f64((1 2 3) (4 5 6))))
  63. (B (blis-dsetm! BLIS-NO-CONJUGATE 0 BLIS-NONUNIT-DIAG BLIS-DENSE 3. A)))
  64. (test-eq A B)
  65. (test-equal A (make-typed-array 'f64 3. 2 3)))
  66. (let* ((A (array-copy #2c64((1 2 3) (4 5 6))))
  67. (B (blis-setm! BLIS-CONJUGATE 0 BLIS-NONUNIT-DIAG BLIS-DENSE 3+9i A)))
  68. (test-eq A B)
  69. (test-equal A (make-typed-array 'c64 3-9i 2 3)))
  70. ; ---------------------------------
  71. ; ?copyv ?axbyv ?axpbyv
  72. ; ---------------------------------
  73. (define (test-lin type f-name f conj-A alpha make-A beta make-B)
  74. (define (ref conjX alpha X beta Y)
  75. (array-map! Y
  76. (lambda (x y)
  77. (+ (* beta y) (* alpha (if (eqv? conjX BLIS-CONJUGATE) (conj x) x))))
  78. X Y)
  79. Y)
  80. (let* ((tag (format #f "~a:~a" (procedure-name make-A) (procedure-name make-B)))
  81. (case-name (format #f "~a, ~a" (procedure-name f) tag))
  82. (A (fill-A1! (make-A type)))
  83. (B (fill-B1! (make-B type)))
  84. (Aref (array-copy A))
  85. (Bref (array-copy B)))
  86. (test-begin case-name)
  87. (for-each-lambda ((alpha alpha))
  88. (for-each-lambda ((beta beta))
  89. (let ((val-ref (ref conj-A alpha A beta Bref))
  90. (val-f (f conj-A alpha A beta B)))
  91. (test-approximate-array 'source A Aref 0)
  92. (test-approximate-array 'content B Bref 0)
  93. (test-approximate-array 'result val-ref val-f 0))))
  94. (test-end case-name)))
  95. (for-each-lambda ((type '(f32 f64 c32 c64))
  96. (copyv (list blis-scopyv! blis-dcopyv! blis-ccopyv! blis-zcopyv!))
  97. (axpyv (list blis-saxpyv! blis-daxpyv! blis-caxpyv! blis-zaxpyv!))
  98. (axpbyv (list blis-saxpbyv! blis-daxpbyv! blis-caxpbyv! blis-zaxpbyv!)))
  99. (let ((scalar-cases (scalar-cases type)))
  100. (for-each (match-lambda
  101. ((conj-A make-A make-B)
  102. (test-lin type 'copy
  103. (lambda (conj-A alpha make-A beta make-B)
  104. (copyv conj-A make-A make-B))
  105. conj-A '(1) make-A '(0) make-B)
  106. (test-lin type 'axpyv
  107. (lambda (conj-A alpha make-A beta make-B)
  108. (axpyv conj-A alpha make-A make-B))
  109. conj-A scalar-cases make-A '(1) make-B)
  110. (test-lin type 'axpbyv
  111. axpbyv
  112. conj-A scalar-cases make-A scalar-cases make-B)))
  113. (list-product
  114. (list BLIS-CONJUGATE BLIS-NO-CONJUGATE)
  115. (list make-v-compact make-v-offset make-v-strided)
  116. (list make-v-compact make-v-offset make-v-strided)))))
  117. ; ---------------------------------
  118. ; ?swapv
  119. ; ---------------------------------
  120. (let* ((x (array-copy #f64(1 2 3)))
  121. (z (array-copy #f64(7 8 9 10 11 12)))
  122. (y (make-shared-array z (lambda (i) (list (+ 1 (* i 2)))) 3)))
  123. (blis-swapv! x y)
  124. (test-assert (array-equal? x #f64(8 10 12)))
  125. (test-assert (array-equal? z #f64(7 1 9 2 11 3))))
  126. (let* ((A (array-copy #2c64((1 2 3) (4 5 6))))
  127. (B (blis-setm! BLIS-CONJUGATE 0 BLIS-NONUNIT-DIAG BLIS-DENSE 3+9i A)))
  128. (test-eq A B)
  129. (test-equal A (make-typed-array 'c64 3-9i 2 3)))
  130. ; ---------------------------------
  131. ; ?axpbym ?copym FIXME coverage of flags
  132. ; ---------------------------------
  133. (define A (array-copy #2f64((1 2) (3 4))))
  134. (let ((B (array-copy #2f64((9 8) (7 6)))))
  135. (blis-daxpym! 0 BLIS-NONUNIT-DIAG BLIS-DENSE BLIS-NO-TRANSPOSE 3 A B)
  136. (test-equal B #2f64((12. 14.) (16. 18.)))
  137. (blis-daxpym! 0 BLIS-NONUNIT-DIAG BLIS-DENSE BLIS-TRANSPOSE 3 A B)
  138. (test-equal B #2f64((15. 23.) (22. 30.)))
  139. (let ((C (array-copy A)))
  140. (blis-dcopym! 0 BLIS-NONUNIT-DIAG BLIS-DENSE BLIS-TRANSPOSE B C)
  141. (test-equal B #2f64((15. 23.) (22. 30.)))
  142. (test-equal C #2f64((15. 22.) (23. 30.)))))
  143. ; ---------------------------------
  144. ; ?dotv
  145. ; ---------------------------------
  146. (define (test-dotv type f conj-A conj-B make-A make-B)
  147. (define (ref conj-A conj-B A B)
  148. (let ((rho 0))
  149. (array-for-each
  150. (lambda (a b)
  151. (set! rho (+ rho (* (if (eq? conj-A BLIS-CONJUGATE) (conj a) a)
  152. (if (eq? conj-B BLIS-CONJUGATE) (conj b) b)))))
  153. A B)
  154. rho))
  155. (let* ((tag (format #f "~a:~a" (procedure-name make-A) (procedure-name make-B)))
  156. (case-name (format #f "~a, ~a" (procedure-name f) tag))
  157. (A (fill-A1! (make-A type)))
  158. (B (fill-B1! (make-B type))))
  159. (test-begin case-name)
  160. (test-equal (ref conj-A conj-B A B) (f conj-A conj-B A B))
  161. (test-end case-name)))
  162. (for-each-lambda ((type '(f32 f64 c32 c64))
  163. (dotv (list blis-sdotv blis-ddotv blis-cdotv blis-zdotv)))
  164. (for-each (match-lambda
  165. ((conj-A conj-B make-A make-B)
  166. (test-dotv type blis-dotv conj-A conj-B make-A make-B)))
  167. (list-product
  168. (list BLIS-CONJUGATE BLIS-NO-CONJUGATE)
  169. (list BLIS-CONJUGATE BLIS-NO-CONJUGATE)
  170. (list make-v-compact make-v-offset make-v-strided)
  171. (list make-v-compact make-v-offset make-v-strided))))
  172. ; ---------------------------------
  173. ; ?norm1v normfv normiv
  174. ; ---------------------------------
  175. (test-approximate (blis-dnorm1v #f64(1 2 3 4)) 10. 0)
  176. (test-approximate (blis-dnormfv #f64(1 2 3 4)) (sqrt (+ (* 1 1) (* 2 2) (* 3 3) (* 4 4))) 0)
  177. (test-approximate (blis-dnormiv #f64(1 2 3 4)) 4. 0)
  178. (test-approximate (blis-norm1v #c32(0+1i 2 3 0-4i)) 10. 0)
  179. (test-approximate (blis-normfv #c64(0+1i 2 3 0-4i)) (sqrt (+ (* 1 1) (* 2 2) (* 3 3) (* 4 4))) 0)
  180. (test-approximate (blis-normiv #c32(0+1i 2 3 0-4i)) 4. 0)
  181. ; ---------------------------------
  182. ; sgemm dgemm cgemm zgemm
  183. ; ---------------------------------
  184. (define (test-gemm tag gemm! transA transB alpha A B beta C)
  185. ;; alpha * sum_k(A_{ik}*B_{kj}) + beta * C_{ij} -> C_{ij}
  186. (define (ref-gemm! transA transB alpha A B beta C)
  187. (let* ((A (apply-transpose-flag A transA))
  188. (B (apply-transpose-flag B transB))
  189. (M (first (array-dimensions C)))
  190. (N (second (array-dimensions C)))
  191. (K (first (array-dimensions B))))
  192. (do ((i 0 (+ i 1))) ((= i M))
  193. (do ((j 0 (+ j 1))) ((= j N))
  194. (array-set! C (* beta (array-ref C i j)) i j)
  195. (do ((k 0 (+ k 1))) ((= k K))
  196. (array-set! C (+ (array-ref C i j) (* alpha (array-ref A i k) (array-ref B k j))) i j))))))
  197. (let ((C1 (array-copy C))
  198. (C2 (array-copy C))
  199. (AA (array-copy A))
  200. (BB (array-copy B)))
  201. (gemm! transA transB alpha A B beta C1)
  202. (ref-gemm! transA transB alpha A B beta C2)
  203. ;; (test-approximate-array tag C1 C2 1e-15) ; TODO as a single test.
  204. (test-begin tag)
  205. (test-equal C1 C2)
  206. (test-equal AA A)
  207. (test-equal BB B)
  208. (test-end tag)))
  209. (for-each
  210. (match-lambda
  211. ((type gemm!)
  212. ; some extra tests with non-square matrices.
  213. (let ((A (fill-A2! (make-typed-array type *unspecified* 4 3)))
  214. (B (fill-A2! (make-typed-array type *unspecified* 3 5)))
  215. (C (fill-A2! (make-typed-array type *unspecified* 4 5))))
  216. (test-gemm "gemm-1" blis-gemm! BLIS-NO-TRANSPOSE BLIS-NO-TRANSPOSE 1. A B 1. C)
  217. (test-gemm "gemm-2" blis-gemm! BLIS-TRANSPOSE BLIS-NO-TRANSPOSE 1. A C 1. B)
  218. (test-gemm "gemm-3" blis-gemm! BLIS-NO-TRANSPOSE BLIS-TRANSPOSE 1. C B 1. A))
  219. (let ((A (fill-A2! (transpose-array (make-typed-array 'f64 *unspecified* 4 3) 1 0)))
  220. (B (fill-A2! (transpose-array (make-typed-array 'f64 *unspecified* 3 5) 1 0)))
  221. (C (fill-A2! (transpose-array (make-typed-array 'f64 *unspecified* 4 5) 1 0))))
  222. (test-gemm "gemm-4" blis-dgemm! BLIS-TRANSPOSE BLIS-TRANSPOSE 1. A B 1. (transpose-array C 1 0))
  223. (test-gemm "gemm-5" blis-dgemm! BLIS-NO-TRANSPOSE BLIS-TRANSPOSE 1. A C 1. (transpose-array B 1 0))
  224. (test-gemm "gemm-6" blis-dgemm! BLIS-TRANSPOSE BLIS-NO-TRANSPOSE 1. C B 1. (transpose-array A 1 0)))
  225. (define (with-matrix-types types-AB types-C)
  226. (for-each
  227. (match-lambda ((make-A make-B make-C transA transB)
  228. (test-gemm (format #f "gemm:~a:~a:~a:~a:~a:~a" type (procedure-name make-A)
  229. (procedure-name make-B) (procedure-name make-C)
  230. transA transB)
  231. gemm! transA transB 3. (fill-A2! (make-A type))
  232. (fill-A2! (make-B type)) 2. (fill-A2! (make-C type)))))
  233. (apply list-product
  234. (append (list types-AB types-AB types-C)
  235. (make-list 2 (list BLIS-TRANSPOSE BLIS-NO-TRANSPOSE
  236. BLIS-CONJ-NO-TRANSPOSE BLIS-CONJ-TRANSPOSE))))))
  237. (define with-overlap (list make-M-z1 make-M-z1 make-M-z00 make-M-overlap make-M-overlap-reversed))
  238. (define without-overlap (list make-M-c-order make-M-fortran-order make-M-offset
  239. make-M-strided make-M-strided-both make-M-strided-reversed))
  240. (blis-error-checking-level-set! BLIS_NO_ERROR_CHECKING)
  241. (with-matrix-types with-overlap without-overlap)
  242. (blis-error-checking-level-set! BLIS_FULL_ERROR_CHECKING)
  243. (with-matrix-types without-overlap without-overlap)))
  244. `((f32 ,blis-sgemm!)
  245. (f64 ,blis-dgemm!)
  246. (c32 ,blis-cgemm!)
  247. (c64 ,blis-zgemm!)))
  248. ; ---------------------------------
  249. ; ?gemv
  250. ; ---------------------------------
  251. (define (test-gemv tag gemv! transA conjX alpha A X beta Y)
  252. ;; alpha*sum_j(A_{ij} * X_j) + beta*Y_i -> Y_i
  253. (define (ref-gemv! transA conjX alpha A X beta Y)
  254. (let* ((A (apply-transpose-flag A transA))
  255. (X (apply-transpose-flag X conjX)))
  256. (match (array-dimensions A)
  257. ((M N)
  258. (do ((i 0 (+ i 1))) ((= i M))
  259. (array-set! Y (* beta (array-ref Y i)) i)
  260. (do ((j 0 (+ j 1))) ((= j N))
  261. (array-set! Y (+ (array-ref Y i) (* alpha (array-ref A i j) (array-ref X j))) i)))
  262. Y))))
  263. (let ((Y1 (array-copy Y))
  264. (Y2 (array-copy Y))
  265. (AA (array-copy A))
  266. (XX (array-copy X)))
  267. (gemv! transA conjX alpha A X beta Y1)
  268. (ref-gemv! transA conjX alpha A X beta Y2)
  269. ;; (test-approximate-array tag Y1 Y2 1e-15) ; TODO as a single test.
  270. (test-begin tag)
  271. (test-equal Y1 Y2)
  272. (test-equal AA A)
  273. (test-equal XX X)
  274. (test-end tag)))
  275. (for-each
  276. (match-lambda
  277. ((type gemv!)
  278. ; TODO some extra tests with non-square matrices.
  279. (define (with-types M-types v1-types v2-types)
  280. (for-each
  281. (match-lambda ((make-A make-X make-Y transA conjX)
  282. (test-gemv (format #f "gemv:~a:~a:~a:~a:~a:~a" type (procedure-name make-A)
  283. (procedure-name make-X) (procedure-name make-Y)
  284. transA conjX)
  285. gemv! transA conjX 3. (fill-A2! (make-A type))
  286. (fill-A1! (make-X type)) 2. (fill-A1! (make-Y type)))))
  287. (apply list-product
  288. (list M-types v1-types v2-types
  289. (list BLIS-TRANSPOSE BLIS-NO-TRANSPOSE BLIS-CONJ-NO-TRANSPOSE BLIS-CONJ-TRANSPOSE)
  290. (list BLIS-NO-CONJUGATE BLIS-CONJUGATE)))))
  291. (define with-overlap-M (list make-M-z1 make-M-z1 make-M-z00 make-M-overlap make-M-overlap-reversed))
  292. (define with-overlap-v (list make-v-z))
  293. (define without-overlap-v (list make-v-compact make-v-strided make-v-offset make-v-strided-reversed))
  294. (define without-overlap-M (list make-M-c-order make-M-fortran-order make-M-offset
  295. make-M-strided make-M-strided-both make-M-strided-reversed))
  296. (blis-error-checking-level-set! BLIS_FULL_ERROR_CHECKING)
  297. (with-types with-overlap-M without-overlap-v without-overlap-v)
  298. (with-types without-overlap-M with-overlap-v without-overlap-v)
  299. (blis-error-checking-level-set! BLIS_FULL_ERROR_CHECKING)
  300. (with-types without-overlap-M without-overlap-v without-overlap-v)))
  301. `((f32 ,blis-sgemv!)
  302. (f64 ,blis-dgemv!)
  303. (c32 ,blis-cgemv!)
  304. (c64 ,blis-zgemv!)))
  305. ; ---------------------------------
  306. ; ?ger
  307. ; ---------------------------------
  308. (define (test-ger tag ger! conjX conjY alpha X Y A)
  309. ;; alpha*x_i*y_j + A_{i, j} -> A_{i, j}
  310. (define (ref-ger! conjX conjY alpha X Y A)
  311. (let* ((X (apply-transpose-flag X conjX))
  312. (Y (apply-transpose-flag Y conjY))
  313. (M (array-length X))
  314. (N (array-length Y)))
  315. (match (array-dimensions A)
  316. ((M N)
  317. (do ((i 0 (+ i 1))) ((= i M))
  318. (do ((j 0 (+ j 1))) ((= j N))
  319. (array-set! A (+ (array-ref A i j) (* alpha (array-ref X i) (array-ref Y j))) i j)))
  320. Y))))
  321. (let ((A1 (array-copy A))
  322. (A2 (array-copy A)))
  323. (ger! conjX conjY alpha X Y A1)
  324. (ref-ger! conjX conjY alpha X Y A2)
  325. ;; (test-approximate-array tag A1 A2 1e-15) ; TODO as a single test.
  326. (test-begin tag)
  327. (test-equal A1 A2)
  328. (test-end tag)))
  329. (for-each
  330. (match-lambda
  331. ((type ger!)
  332. ; TODO some extra tests with non-square matrices.
  333. (for-each
  334. (match-lambda ((make-X make-Y make-A conjX conjY)
  335. (test-ger (format #f "ger:~a:~a:~a:~a:~a:~a" type (procedure-name make-X)
  336. (procedure-name make-Y) (procedure-name make-A)
  337. conjX conjY)
  338. ger! conjX conjY 3.
  339. (fill-A1! (make-X type))
  340. (fill-A1! (make-Y type))
  341. (fill-A2! (make-A type)))))
  342. (list-product
  343. (list make-v-compact make-v-strided make-v-offset make-v-strided-reversed)
  344. (list make-v-compact make-v-strided make-v-offset make-v-strided-reversed)
  345. (list make-M-c-order make-M-fortran-order make-M-offset
  346. make-M-strided make-M-strided-both make-M-strided-reversed)
  347. (list BLIS-NO-CONJUGATE BLIS-CONJUGATE)
  348. (list BLIS-NO-CONJUGATE BLIS-CONJUGATE)))))
  349. `((f32 ,blis-sger!)
  350. (f64 ,blis-dger!)
  351. (c32 ,blis-cger!)
  352. (c64 ,blis-zger!)))
  353. (define error-count (test-runner-fail-count (test-runner-current)))
  354. (test-end "ffi-blis")
  355. (exit error-count)