cat.scm 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. ; (c) Daniel Llorens - 2012-2013, 2015
  2. ; Concatenate arrays.
  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 cat))
  8. (import (srfi srfi-1) (srfi srfi-26) (ploy ploy) (ploy basic) (ploy assert)
  9. (ice-9 match) (ice-9 rdelim))
  10. (define (list-subst l i val)
  11. (let ((u (list-copy l)))
  12. (list-set! u i val)
  13. u))
  14. (define (plain-cat! i dest . xx)
  15. (fold (lambda (x base)
  16. (let ((dc ($. x i)))
  17. (apply amend! dest x
  18. (append (make-list i #t)
  19. (list (J dc base))
  20. (make-list (- (array-rank x) i 1) #t)))
  21. (+ base dc)))
  22. 0
  23. xx)
  24. dest)
  25. (define (cat! ir out . xx)
  26. (let* ((xx (map (lambda (x) (if (array? x) x (make-array x))) xx)))
  27. (if (> 0 ir)
  28. (apply cat! 0 out
  29. (map (lambda (x) (apply make-shared-array
  30. x (lambda i (drop i (- ir)))
  31. (append (make-list (- ir) 1) ($ x))))
  32. xx))
  33. (let* ((x-largest-rank (fold (lambda (x xm) (if (> (array-rank x) (array-rank xm)) x xm)) (car xx) (cdr xx)))
  34. (out-rank (max (+ 1 ir) (array-rank x-largest-rank)))
  35. (dims-to-cat (map (lambda (x)
  36. (if (> (array-rank x) ir)
  37. ($. x ir)
  38. 1))
  39. xx))
  40. (out-shape (list-subst (append ($ x-largest-rank)
  41. (make-list (- out-rank (array-rank x-largest-rank)) 1))
  42. ir (apply + dims-to-cat)))
  43. (o (or out (apply make-typed-array (array-type (car xx)) *unspecified* out-shape)))
  44. (xx (map (lambda (x dc) (extend-right x (list-subst out-shape ir dc)))
  45. xx dims-to-cat)))
  46. (apply plain-cat! ir o xx)))))
  47. (define (icat! ir out . xx)
  48. (let* ((xx (map (lambda (x) (if (array? x) x (make-array x))) xx)))
  49. (if (> 0 ir)
  50. (apply icat! 0 out
  51. (map (lambda (x) (apply make-shared-array
  52. x (lambda i (drop-right i (- ir)))
  53. (append ($ x) (make-list (- ir) 1))))
  54. xx))
  55. (let* ((x-largest-rank (fold (lambda (x xm) (if (> (array-rank x) (array-rank xm)) x xm)) (car xx) (cdr xx)))
  56. (out-rank (max (+ 1 ir) (array-rank x-largest-rank)))
  57. (dims-to-cat (map (lambda (x)
  58. (if (> (array-rank x) ir)
  59. ($. x (- (array-rank x) (+ 1 ir)))
  60. 1))
  61. xx))
  62. (out-shape (list-subst (append (make-list (- out-rank (array-rank x-largest-rank)) 1)
  63. ($ x-largest-rank))
  64. (- out-rank (+ 1 ir)) (apply + dims-to-cat)))
  65. (o (or out (apply make-typed-array (array-type (car xx)) *unspecified* out-shape)))
  66. (xx (map (lambda (x dc) (extend-left x (list-subst out-shape (- out-rank (+ 1 ir)) dc)))
  67. xx dims-to-cat)))
  68. (apply plain-cat! (- out-rank (+ 1 ir)) o xx)))))
  69. (define (cat ir . xx)
  70. "cat i . xx
  71. Concatenate arrays xx ... along axis i. The shapes of xx ... must have
  72. matching prefixes.
  73. The output array will have the rank of the xx with the largest rank, or (+ 1
  74. axis), whichever is larger. If necessary, the xx are broadcast to this
  75. output rank. Where none of the xx provides a dimension, the broadcast
  76. dimension is 1. The dimensions of the xx must match on all axes, except
  77. possibly along the axis of concatenation.
  78. As an extension, if i is negative, the shape of each array xx ... is extended
  79. by (- i) singleton dimensions on the left and the concatenation is carried
  80. out along the leftmost axis.
  81. For example:
  82. (cat 0 (i. 1 2) (i. 2 2)) => #2((0 1) (0 1) (2 3)))
  83. (cat 0 #(1 2) #(3 4 5)) => #(1 2 3 4 5))
  84. (cat -1 #(1 2) #(4 5)) => #2((1 2) (4 5))
  85. (cat 1 #(1 2) #(4 5)) => #2((1 4) (2 5))
  86. (cat 0 a #(0 1)) => #(a 0 1)
  87. (cat 1 a #(0 1)) => #2((a 0) (a 1))
  88. (cat -1 a #(0 1)) => #2((a a) (0 1))
  89. (cat 1 #(a b) #2((0 1) (2 3))) => #2((a 0 1) (b 2 3))
  90. (cat 0 #(a b) #2((0 1) (2 3))) => #2((a a) (b b) (0 1) (2 3))
  91. See also: (cat!), (icat), (extend-right).
  92. "
  93. (apply cat! ir #f xx))
  94. (define (icat ir . xx)
  95. "icat i xx ...
  96. Concatenate items of rank i of arrays xx ... The shapes of xx ... must have
  97. matching suffixes.
  98. The output array will have the rank of the xx with the largest rank, or (+ 1
  99. i), whichever is larger. If necessary, the xx are broadcast to this output
  100. rank. Where none of the xx provides a dimension, the broadcast dimension is
  101. 1. The dimensions of the xx must match on all axes, except possibly along the
  102. axis of concatenation.
  103. As an extension, if ir is negative, the shape of each array xx ... is
  104. extended by (- i) singleton dimensions on the right and the
  105. concatenation is carried out along the rightmost axis.
  106. (icat ...) always creates a new array and not a shared array. 'icat' stands
  107. for 'item-cat'.
  108. For example:
  109. (icat 0 'a 'b 'c) => #(a b c)
  110. (icat 1 'a 'b 'c) => #2((a) (b) (c))
  111. (icat 0 #(1 2 3) 4 #(5 6)) => #(1 2 3 4 5 6)
  112. (icat 0 #2((0 1) (2 3)) #(a b)) => #2((0 1 a b) (2 3 a b)))
  113. (icat 1 #2((0 1) (2 3)) #(a b)) => #2((0 1) (2 3) (a b))
  114. (icat 1 #2((0 1)) #(a)) => error, mismatched dimensions along axis 0
  115. (icat 0 #2((0 1)) #(a)) => #2((0 1 a))
  116. (icat -1 #(1 2 3) #(a b c)) => #2((1 a) (2 b) (3 c))
  117. (icat -1 'a #(x y z)) => #2((a x) (a y) (a z))
  118. See also: (icat!), (cat), (extend-left).
  119. Longer explanation: suppose the shapes of the arguments are
  120. (s5 s4 s3 s2 s1 s0)
  121. (t1 t0)
  122. (r3 r2 r1 r0)
  123. The axes are aligned as shown. The numbers indicate the concatenation axis
  124. for a given value of i. For example, suppose i is 2. Then (s3
  125. r3), (s1 t1 r1) and (s0 t0 r0) must match. The arguments are broadcast to
  126. (s5 s4 s3 s2 s1 s0)
  127. (s5 s4 s3 1 t1 t0)
  128. (s5 s4 r3 r2 r1 r0)
  129. and then concatenated along axis (s2 1 r2). The result has shape
  130. (s5 s4 s3 (+ s2 1 r2) s1 s0).
  131. "
  132. (apply icat! ir #f xx))
  133. (export cat cat! icat! icat)