docstring cleanup, tpyinmg, and start on generics for copy semantics,
[CommonLispStat.git] / matrices.lsp
blob58a42e08c768b4ece8299b4c7a72f1b601188775
1 ;;; -*- mode: lisp -*-
2 ;;;
3 ;;; Copyright (c) 2005--2007, by A.J. Rossini <blindglobe@gmail.com>
4 ;;; See COPYRIGHT file for any additional restrictions (BSD license).
5 ;;; Since 1991, ANSI was finally finished. Modified to match ANSI
6 ;;; Common Lisp.
8 ;;;; matrices -- Basic matrix operations
9 ;;;;
10 ;;;; Copyright (c) 1991, by Luke Tierney. Permission is granted for
11 ;;;; unrestricted use.
14 ;;; Need to extend to use CLEM
16 ;;;
17 ;;; Package Setup
18 ;;;
20 (in-package :cl-user)
22 (defpackage :lisp-stat-matrix
23 (:use :common-lisp
24 :cffi
25 :lisp-stat-compound-data)
26 (:export matrixp num-rows num-cols matmult identity-matrix diagonal
27 row-list column-list inner-product outer-product
28 cross-product transpose bind-columns bind-rows
29 array-data-vector vector-to-array
31 check-matrix check-square-matrix
33 copy-array copy-vector
36 (in-package :lisp-stat-matrix)
38 (deftype matrix () 'array) ;; temp fix
40 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41 ;;;;
42 ;;;; Array to Row-Major Data Vector Conversion Functions
43 ;;;;
44 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
46 (defun array-data-vector (a)
47 "Args: (a)
48 Displaces array A to a vector"
49 (make-array (array-total-size a)
50 :displaced-to a
51 :element-type (array-element-type a)))
53 (defun vector-to-array (v dims)
54 "Args: (v dims)
55 Displaces vector V to array with dimensions DIMS"
56 (make-array dims
57 :displaced-to v
58 :element-type (array-element-type v)))
60 ;;;;
62 (defun check-matrix (a)
63 (if (not (and (typep a' array)
64 (= (array-rank a) 2)))
65 (error "not a matrix - ~s" a)
66 t))
68 (defun check-square-matrix (a)
69 (if (and (check-matrix a)
70 (/= (array-dimension a 0) (array-dimension a 1))
71 (error "matrix not square - ~s" a))
72 t))
74 (defun matrixp (x)
75 "Args: (x)
77 Returns:
78 T if X is a 2-d array (i.e. a matrix),
79 NIL otherwise."
80 (and (typep x 'array)
81 (= (array-rank x) 2)))
83 (defun num-rows (x)
84 "Args: (x)
86 Returns number of rows in X."
87 (if (matrixp x)
88 (array-dimension x 0)
89 (error "only useful for matrices.")))
91 (defun num-cols (x)
92 "Args: (x)
94 Returns number of columns in X."
95 (if (matrixp x)
96 (array-dimension x 1)
97 (error "only useful for matrices.")))
100 ;;; Look at this! Prime target for generic function dispatch!
101 (defun matmult (a b &rest args)
102 "Args: (a b &rest args)
103 Returns the matrix product of matrices a, b, etc. If a is a vector it is
104 treated as a row vector; if b is a vector it is treated as a column vector."
105 ;; fixme: why does SBCL claim this is unreachable?
106 (let ((rtype (cond ((and (typep a 'matrix)
107 (typep b 'matrix)) 'matrix)
108 ((and (typep a 'matrix)
109 (typep b 'sequence)) 'vector)
110 ((and (typep a 'sequence)
111 (typep b 'matrix)) 'vector)
112 ((and (typep a 'sequence)
113 (typep b 'sequence)) 'number)
114 ((typep a 'sequence)
115 (if (consp a) 'list 'vector))
116 ((typep b 'sequence)
117 (if (consp b) 'list 'vector)))))
119 (if (typep a 'sequence)
120 (setf a (vector-to-array (coerce a 'vector) (list 1 (length a)))))
121 (if (typep b 'sequence)
122 (setf b (vector-to-array (coerce b 'vector) (list (length b) 1))))
123 (if (not (= (array-dimension a 1) (array-dimension b 0)))
124 (error "dimensions do not match"))
125 (if args
126 (reduce #'matmult args :initial-value (matmult a b))
127 (let* ((n (array-dimension a 0))
128 (m (array-dimension b 1))
129 (p (array-dimension a 1))
130 (c (make-array (list n m)))
132 (declare (fixnum n m p))
133 (dotimes (i n)
134 (declare (fixnum i))
135 (dotimes (j m)
136 (declare (fixnum j))
137 (setq x 0)
138 (dotimes (k p)
139 (declare (fixnum k))
140 (setq x (+ x
141 (* (aref a i k)
142 (aref b k j)))))
143 (setf (aref c i j) x)))
144 (case rtype
145 (matrix c)
146 (number (aref c 0 0))
147 (t (coerce (compound-data-seq c) rtype)))))))
149 (defun identity-matrix (n)
150 "Args: (n)
151 Returns the identity matrix of rank N."
152 (let ((result (make-array (list n n) :initial-element 0)))
153 (dotimes (i n result)
154 (declare (fixnum i))
155 (setf (aref result i i) 1))))
157 ;; this thing is not very efficient at this point - too much coercing
158 (defun diagonal (x)
159 "Args: (x)
160 If X is a matrix, returns the diagonal of X. If X is a sequence, returns a
161 diagonal matrix of rank (length X) with diagonal elements eq to the elements
162 of X."
163 (cond ((typep x 'matrix)
164 (let* ((n (min (num-rows x) (num-cols x)))
165 (result (make-array n)))
166 (dotimes (i n (coerce result 'list))
167 (setf (aref result i) (aref x i i)))))
168 ((typep x 'sequence)
169 (let* ((x (coerce x 'vector))
170 (n (length x))
171 (result (make-array (list n n) :initial-element 0)))
172 (dotimes (i n result)
173 (setf (aref result i i) (aref x i)))))
174 (t (error "argument must be a matrix or a sequence"))))
176 (defun row-list (x)
177 "Args: (m)
179 Returns a list of the rows of M as vectors"
180 (check-matrix x)
181 (let ((m (num-rows x))
182 (n (num-cols x))
183 (result nil))
184 (declare (fixnum m n))
185 (flet ((get-row (k)
186 (declare (fixnum k))
187 (let ((row (make-array n)))
188 (dotimes (i n row)
189 (declare (fixnum i))
190 (setf (aref row i) (aref x k i))))))
191 (dotimes (i m result)
192 (declare (fixnum i))
193 (setf result (cons (get-row (- m i 1)) result))))))
195 (defun column-list (x)
196 "Args: (m)
198 Returns a list of the columns of M as vectors"
199 (check-matrix x)
200 (let ((m (num-rows x))
201 (n (num-cols x))
202 (result nil))
203 (declare (fixnum m n))
204 (flet ((get-col (k)
205 (declare (fixnum k))
206 (let ((col (make-array m)))
207 (dotimes (i m col)
208 (declare (fixnum i))
209 (setf (aref col i) (aref x i k))))))
210 (dotimes (i n result)
211 (declare (fixnum i))
212 (setf result (cons (get-col (- n i 1)) result))))))
214 (defun inner-product (x y)
215 "Args: (x y)
217 Returns inner product of sequences X and Y."
218 (check-sequence x)
219 (check-sequence y)
220 (let ((n (length x))
221 (cx (make-next-element x))
222 (cy (make-next-element y))
223 (result 0))
224 (declare (fixnum n))
225 (if (/= n (length y)) (error "sequence lengths do not match"))
226 (dotimes (i n result)
227 (declare (fixnum i))
228 (setf result
229 (+ result (* (get-next-element cx i)
230 (get-next-element cy i)))))))
232 (defun outer-product (x y &optional (f #'*))
233 "Args: (x y &optional (fcn #'*))
235 Returns the generalized outer product of x and y, using fcn. Tat is, the result
236 is a matrix of dimension ((length x) (length y)) and the (i j) element of the
237 result is computed as (apply fcn (aref x i) (aref y j))."
239 (let* ((x (coerce x 'vector))
240 (y (coerce y 'vector))
241 (m (length x))
242 (n (length y))
243 (a (make-array (list m n))))
244 (declare (fixnum m n))
245 (dotimes (i m a)
246 (declare (fixnum i))
247 (dotimes (j n)
248 (declare (fixnum j))
249 (setf (aref a i j) (funcall f (aref x i) (aref y j)))))))
251 (defun cross-product (x)
252 "Args: (x)
254 If X is a matrix returns (matmult (transpose X) X). If X is a vector returns
255 (inner-product X X)."
257 (check-matrix x)
258 (let* ((n (num-rows x))
259 (p (num-cols x))
260 (c (make-array (list p p))))
261 (declare (fixnum n p))
262 (dotimes (i p c)
263 (declare (fixnum i))
264 (dotimes (j (+ i 1))
265 (declare (fixnum j))
266 (let ((val 0))
267 (dotimes (k n)
268 (declare (fixnum k))
269 (incf val (* (aref x k i) (aref x k j))))
270 (setf (aref c i j) val)
271 (setf (aref c j i) val))))))
273 (defun transpose-list (x)
274 (let ((m (length (first x))))
275 (dolist (next x)
276 (if (not (consp next)) (error "not a list - ~a" x))
277 (if (/= m (length next)) (error "sublists not the same length")))
278 (do* ((cx (copy-list x))
279 (result (make-list m))
280 (next result (cdr next)))
281 ((null next) result)
282 (setf (first next) (mapcar #'first cx))
283 (do ((next cx (cdr next)))
284 ((null next))
285 (setf (first next) (rest (first next)))))))
287 (defun transpose (x)
288 "Args: (m)
289 Returns the transpose of the matrix M."
290 (cond
291 ((consp x) (transpose-list x))
293 (check-matrix x)
294 (let* ((m (num-rows x))
295 (n (num-cols x))
296 (tx (make-array (list n m))))
297 (declare (fixnum m n))
298 (dotimes (i m tx)
299 (declare (fixnum i))
300 (dotimes (j n)
301 (declare (fixnum j))
302 (setf (aref tx j i) (aref x i j))))))))
304 (defun bind-columns (&rest args)
305 "Args (&rest args)
307 The ARGS can be matrices, vectors, or lists. Arguments are bound into a matrix
308 along their columns. Example:
309 (bind-columns #2a((1 2)(3 4)) #(5 6)) returns #2a((1 2 5)(3 4 6))"
311 (flet ((check-arg (x)
312 (if (not (or (typep x 'sequence) (typep x 'matrix)))
313 (error "bad argument type")))
314 (arg-cols (x) (if (typep x 'sequence) 1 (num-cols x)))
315 (arg-rows (x) (if (typep x 'sequence) (length x) (num-rows x))))
316 (dolist (x args) (check-arg x)) ;; verify data structure conformance.
317 (let ((m (arg-rows (first args)))
318 (n (arg-cols (first args))))
319 (declare (fixnum m n))
320 (dolist (x (rest args))
321 (if (/= m (arg-rows x)) (error "column lengths do not match"))
322 (incf n (arg-cols x)))
323 (do* ((result (make-array (list m n)))
324 (args args (rest args))
325 (firstcol 0)
326 (x (first args) (first args)))
327 ((null args) result)
328 (cond
329 ((typep x 'sequence)
330 (let ((cx (make-next-element x)))
331 (dotimes (i m)
332 (setf (aref result i firstcol) (get-next-element cx i)))))
334 (let ((k (arg-cols x)))
335 (dotimes (i m)
336 (dotimes (j k)
337 (setf (aref result i (+ firstcol j)) (aref x i j)))))))
338 (incf firstcol (arg-cols x))))))
340 (defun bind-rows (&rest args)
341 "Args (&rest args)
343 The ARGS can be matrices, vectors, or lists. Arguments are bound into a matrix
344 along their rows. Example:
345 (bind-rows #2a((1 2)(3 4)) #(5 6)) returns #2a((1 2)(3 4)(5 6))"
347 (flet ((check-arg (x)
348 (if (not (or (typep x 'sequence)
349 (typep x 'matrix)))
350 (error "bad argument type")))
351 (arg-cols (x) (if (typep x 'sequence) (length x) (num-cols x)))
352 (arg-rows (x) (if (typep x 'sequence) 1 (num-rows x))))
353 (dolist (x args) (check-arg x))
354 (let ((m (arg-rows (first args)))
355 (n (arg-cols (first args))))
356 (declare (fixnum m n))
357 (dolist (x (rest args))
358 (if (/= n (arg-cols x)) (error "row lengths do not match"))
359 (incf m (arg-rows x)))
360 (do* ((result (make-array (list m n)))
361 (args args (rest args))
362 (firstrow 0)
363 (x (first args) (first args)))
364 ((null args) result)
365 (cond
366 ((typep x 'sequence)
367 (let ((cx (make-next-element x)))
368 (dotimes (i n)
369 (setf (aref result firstrow i) (get-next-element cx i)))))
371 (let ((k (arg-rows x)))
372 (dotimes (i n)
373 (dotimes (j k)
374 (setf (aref result (+ firstrow j) i) (aref x j i)))))))
375 (incf firstrow (arg-rows x))))))
379 ;;; Copying Functions
382 (defun copy-vector (x)
383 "Args: (x)
385 Returns a copy of the vector X"
386 (copy-seq x))
388 (defun copy-array (a)
389 "Args: (a)
391 Returns a copy of the array A"
392 (vector-to-array (copy-seq (array-data-vector a))
393 (array-dimensions a)))
395 (defgeneric copy (x)
396 (:documentation "methods for copying linaar algebra forms."))
398 (defmethod copy ((x vector)))
400 (defmethod copy ((x matrix)))