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
8 ;;;; matrices -- Basic matrix operations
10 ;;;; Copyright (c) 1991, by Luke Tierney. Permission is granted for
11 ;;;; unrestricted use.
19 (in-package :lisp-stat-basics
)
21 (export '(matrixp num-rows num-cols matmult identity-matrix diagonal
22 row-list column-list inner-product outer-product cross-product
23 transpose bind-columns bind-rows
))
27 Returns T if X is a matrix, NIL otherwise."
28 (and (arrayp x
) (= (array-rank x
) 2)))
32 Returns number of rows in X."
33 (array-dimension x
0))
37 Returns number of columns in X."
38 (array-dimension x
1))
40 (defun matmult (a b
&rest args
)
41 "Args: (a b &rest args)
42 Returns the matrix product of matrices a, b, etc. If a is a vector it is
43 treated as a row vector; if b is a vector it is treated as a column vector."
44 (let ((rtype (cond ((and (matrixp a
) (matrixp b
)) 'matrix
)
45 ((and (sequencep a
) (sequencep b
)) 'number
)
46 ((sequencep a
) (if (consp a
) 'list
'vector
))
47 ((sequencep b
) (if (consp b
) 'list
'vector
)))))
50 (setf a
(vector-to-array (coerce a
'vector
) (list 1 (length a
)))))
52 (setf b
(vector-to-array (coerce b
'vector
) (list (length b
) 1))))
53 (if (not (= (array-dimension a
1) (array-dimension b
0)))
54 (error "dimensions do not match"))
56 (reduce #'matmult args
:initial-value
(matmult a b
))
57 (let* ((n (array-dimension a
0))
58 (m (array-dimension b
1))
59 (p (array-dimension a
1))
60 (c (make-array (list n m
)))
62 (declare (fixnum n m p
))
71 (* (aref a i k
) (aref b k j
)))))
72 (setf (aref c i j
) x
)))
76 (t (coerce (compound-data-seq c
) rtype
)))))))
78 (defun identity-matrix (n)
80 Returns the identity matrix of rank N."
81 (let ((result (make-array (list n n
) :initial-element
0)))
84 (setf (aref result i i
) 1))))
86 ;; this thing is not very efficient at this point - too much coercing
89 If X is a matrix, returns the diagonal of X. If X is a sequence, returns a
90 diagonal matrix of rank (length X) with diagonal elements eq to the elements
93 (let* ((n (min (num-rows x
) (num-cols x
)))
94 (result (make-array n
)))
95 (dotimes (i n
(coerce result
'list
))
96 (setf (aref result i
) (aref x i i
)))))
98 (let* ((x (coerce x
'vector
))
100 (result (make-array (list n n
) :initial-element
0)))
101 (dotimes (i n result
)
102 (setf (aref result i i
) (aref x i
)))))
103 (t (error "argument must be a matrix or a sequence"))))
107 Returns a list of the rows of M as vectors"
109 (let ((m (num-rows x
))
112 (declare (fixnum m n
))
115 (let ((row (make-array n
)))
118 (setf (aref row i
) (aref x k i
))))))
119 (dotimes (i m result
)
121 (setf result
(cons (get-row (- m i
1)) result
))))))
123 (defun column-list (x)
125 Returns a list of the columns of M as vectors"
127 (let ((m (num-rows x
))
130 (declare (fixnum m n
))
133 (let ((col (make-array m
)))
136 (setf (aref col i
) (aref x i k
))))))
137 (dotimes (i n result
)
139 (setf result
(cons (get-col (- n i
1)) result
))))))
141 (defun inner-product (x y
)
143 Returns inner product of sequences X and Y."
147 (cx (make-next-element x
))
148 (cy (make-next-element y
))
151 (if (/= n
(length y
)) (error "sequence lengths do not match"))
152 (dotimes (i n result
)
155 (+ result
(* (get-next-element cx i
) (get-next-element cy i
)))))))
157 (defun outer-product (x y
&optional
(f #'*))
158 "Args: (x y &optional (fcn #'*))
159 Returns the generalized outer product of x and y, using fcn. Tat is, the result
160 is a matrix of dimension ((length x) (length y)) and the (i j) element of the
161 result is computed as (apply fcn (aref x i) (aref y j))."
162 (let* ((x (coerce x
'vector
))
163 (y (coerce y
'vector
))
166 (a (make-array (list m n
))))
167 (declare (fixnum m n
))
172 (setf (aref a i j
) (funcall f
(aref x i
) (aref y j
)))))))
174 (defun cross-product (x)
176 If X is a matrix returns (matmult (transpose X) X). If X is a vector returns
177 (inner-product X X)."
179 (let* ((n (num-rows x
))
181 (c (make-array (list p p
))))
182 (declare (fixnum n p
))
190 (incf val
(* (aref x k i
) (aref x k j
))))
191 (setf (aref c i j
) val
)
192 (setf (aref c j i
) val
))))))
194 (defun transpose-list (x)
195 (let ((m (length (first x
))))
197 (if (not (consp next
)) (error "not a list - ~a" x
))
198 (if (/= m
(length next
)) (error "sublists not the same length")))
199 (do* ((cx (copy-list x
))
200 (result (make-list m
))
201 (next result
(cdr next
)))
203 (setf (first next
) (mapcar #'first cx
))
204 (do ((next cx
(cdr next
)))
206 (setf (first next
) (rest (first next
)))))))
210 Returns the transpose of the matrix M."
212 ((consp x
) (transpose-list x
))
215 (let* ((m (num-rows x
))
217 (tx (make-array (list n m
))))
218 (declare (fixnum m n
))
223 (setf (aref tx j i
) (aref x i j
))))))))
225 (defun bind-columns (&rest args
)
227 The ARGS can be matrices, vectors, or lists. Arguments are bound into a matrix
229 Example: (bind-columns #2a((1 2)(3 4)) #(5 6)) returns #2a((1 2 5)(3 4 6))"
230 (flet ((check-arg (x)
231 (if (not (or (sequencep x
) (matrixp x
)))
232 (error "bad argument type")))
233 (arg-cols (x) (if (sequencep x
) 1 (num-cols x
)))
234 (arg-rows (x) (if (sequencep x
) (length x
) (num-rows x
))))
235 (dolist (x args
) (check-arg x
))
236 (let ((m (arg-rows (first args
)))
237 (n (arg-cols (first args
))))
238 (declare (fixnum m n
))
239 (dolist (x (rest args
))
240 (if (/= m
(arg-rows x
)) (error "column lengths do not match"))
241 (incf n
(arg-cols x
)))
242 (do* ((result (make-array (list m n
)))
243 (args args
(rest args
))
245 (x (first args
) (first args
)))
249 (let ((cx (make-next-element x
)))
251 (setf (aref result i firstcol
) (get-next-element cx i
)))))
253 (let ((k (arg-cols x
)))
256 (setf (aref result i
(+ firstcol j
)) (aref x i j
)))))))
257 (incf firstcol
(arg-cols x
))))))
259 (defun bind-rows (&rest args
)
261 The ARGS can be matrices, vectors, or lists. Arguments are bound into a matrix
263 Example: (bind-rows #2a((1 2)(3 4)) #(5 6)) returns #2a((1 2)(3 4)(5 6))"
264 (flet ((check-arg (x)
265 (if (not (or (sequencep x
) (matrixp x
)))
266 (error "bad argument type")))
267 (arg-cols (x) (if (sequencep x
) (length x
) (num-cols x
)))
268 (arg-rows (x) (if (sequencep x
) 1 (num-rows x
))))
269 (dolist (x args
) (check-arg x
))
270 (let ((m (arg-rows (first args
)))
271 (n (arg-cols (first args
))))
272 (declare (fixnum m n
))
273 (dolist (x (rest args
))
274 (if (/= n
(arg-cols x
)) (error "row lengths do not match"))
275 (incf m
(arg-rows x
)))
276 (do* ((result (make-array (list m n
)))
277 (args args
(rest args
))
279 (x (first args
) (first args
)))
283 (let ((cx (make-next-element x
)))
285 (setf (aref result firstrow i
) (get-next-element cx i
)))))
287 (let ((k (arg-rows x
)))
290 (setf (aref result
(+ firstrow j
) i
) (aref x j i
)))))))
291 (incf firstrow
(arg-rows x
))))))