3 (defun maxima-matrix-dims (a)
4 (let ((row (second a
)))
5 ;; Should we check that all rows have the same length?
6 (values (length (cdr a
)) (length (cdr row
)))))
8 (defun complex-maxima-matrix-p (a)
10 (dolist (col (cdr row
))
11 (unless (eql ($imagpart col
) 0)
12 (return-from complex-maxima-matrix-p t
))))
15 (defun lapack-lispify-matrix (a nrow ncol
&optional
(assume-complex-maxima-matrix-p nil
))
16 "Convert a Maxima matrix A of dimension NROW and NCOL to Lisp matrix
17 suitable for use with LAPACK"
19 (let* ((array-type (if (or assume-complex-maxima-matrix-p
(complex-maxima-matrix-p a
))
22 (mat (make-array (* nrow ncol
)
23 :element-type array-type
))
24 (mat-2d (make-array (list ncol nrow
)
25 :element-type array-type
30 (dolist (col (cdr row
))
31 ;; Fortran matrices are in column-major order!
32 (setf (aref mat-2d c r
) (if (eql array-type
'flonum
)
34 (coerce (complex ($realpart col
) ($imagpart col
))
41 (defun lapack-maxify-matrix (nrow ncol a
)
42 "Convert an LAPACK matrix of dimensions NROW and NCOL into a Maxima
43 matrix (list of lists)"
44 (let ((2d (make-array (list ncol nrow
) :element-type
(array-element-type a
)
50 ;; Fortran arrays are column-major order!
51 (let ((v (aref 2d c r
)))
52 (push (add (realpart v
) (mul '$%i
(imagpart v
))) row
)))
53 (push `((mlist) ,@(nreverse row
)) res
)))
54 `(($matrix
) ,@(nreverse res
)))))
56 (defun $dgeev
(a &optional right-vec-p left-vec-p
)
58 DGEEV computes for an N-by-N real nonsymmetric matrix A, the
59 eigenvalues and, optionally, the left and/or right eigenvectors.
61 The right eigenvector v(j) of A satisfies
62 A * v(j) = lambda(j) * v(j)
63 where lambda(j) is its eigenvalue.
64 The left eigenvector u(j) of A satisfies
65 u(j)**H * A = lambda(j) * u(j)**H
66 where u(j)**H denotes the conjugate transpose of u(j).
68 The computed eigenvectors are normalized to have Euclidean norm
69 equal to 1 and largest component real.
71 A list of three items is returned. The first item is a list of the
72 eigenvectors. The second item is false or the matrix of right
73 eigenvectors. The last itme is false or the matrix of left
75 (flet ((make-eigval (wr wi
)
76 `((mlist) ,@(map 'list
#'(lambda (r i
)
79 (make-eigvec (n vr wi
)
80 ;; dgeev stores the eigen vectors in a special way. Undo
81 ;; that. For simplicity, we create a 2-D matrix and store
82 ;; the eigenvectors there. Then we convert that matrix
83 ;; into a form that maxima wants. Somewhat inefficient.
84 (let ((evec (make-array (list n n
))))
85 (do ((col 0 (incf col
))
88 (cond ((zerop (aref wi col
))
90 (setf (aref evec row col
) (aref vr posn
))
94 (let* ((next-posn (+ posn n
))
95 (val+ (add (aref vr posn
)
96 (mul '$%i
(aref vr next-posn
))))
97 (val- (sub (aref vr posn
)
98 (mul '$%i
(aref vr next-posn
)))))
99 (setf (aref evec row col
) val
+)
100 (setf (aref evec row
(1+ col
)) val-
)
102 ;; Skip over the next column, which we've already used
105 ;; Now convert this 2-D Lisp matrix into a maxima matrix
110 (push (aref evec r c
) row
))
111 (push `((mlist) ,@(nreverse row
)) res
)))
112 `(($matrix
) ,@(nreverse res
)))
115 (let* ((n (maxima-matrix-dims a
))
116 (a-mat (lapack-lispify-matrix a n n
))
117 (wr (make-array n
:element-type
'flonum
))
118 (wi (make-array n
:element-type
'flonum
))
119 (vl (make-array (if left-vec-p
(* n n
) 0)
120 :element-type
'flonum
))
121 (vr (make-array (if right-vec-p
(* n n
) 0)
122 :element-type
'flonum
)))
123 ;; XXX: FIXME: We need to do more error checking in the calls to
125 (multiple-value-bind (z-jobvl z-jobvr z-n z-a z-lda z-wr z-wi z-vl
126 z-ldvl z-vr z-ldvr z-work z-lwork info
)
127 ;; Figure out how much space we need in the work array.
128 (lapack:dgeev
(if left-vec-p
"V" "N")
129 (if right-vec-p
"V" "N")
130 n a-mat n wr wi vl n vr n wr -
1 0)
131 (declare (ignore z-jobvl z-jobvr z-n z-a z-lda z-wr z-wi z-vl
132 z-ldvl z-vr z-ldvr z-work z-lwork info
))
133 (let* ((opt-lwork (truncate (aref wr
0)))
134 (work (make-array opt-lwork
:element-type
'flonum
)))
135 ;; Now do the work with the optimum size of the work space.
136 (multiple-value-bind (z-jobvl z-jobvr z-n z-a z-lda z-wr z-wi z-vl
137 z-ldvl z-vr z-ldvr z-work z-lwork info
)
138 (lapack:dgeev
(if left-vec-p
"V" "N")
139 (if right-vec-p
"V" "N")
140 n a-mat n wr wi vl n vr n work opt-lwork
0)
141 (declare (ignore z-jobvl z-jobvr z-n z-a z-lda z-wr z-wi z-vl
142 z-ldvl z-vr z-ldvr z-work z-lwork
))
144 (merror "DGEEV: invalid arguments: ~D" info
))
146 (merror "DGEEV: failed to converge: ~D" info
)))
147 ;; Convert wr+%i*wi to maxima form
150 (format t
"info = ~A~%" info
)
151 (format t
"lwork = ~A~%" (aref work
0))
152 (format t
"vr = ~A~%" vr
))
153 (let ((e-val (make-eigval wr wi
))
154 (e-vec-right (if right-vec-p
155 (make-eigvec n vr wi
)
157 (e-vec-left (if left-vec-p
158 (make-eigvec n vl wi
)
160 `((mlist) ,e-val
,e-vec-right
,e-vec-left
))))))))
162 (defun $dgesvd
(a &optional jobu jobvt
)
164 DGESVD computes the singular value decomposition (SVD) of a real
165 M-by-N matrix A, optionally computing the left and/or right singular
166 vectors. The SVD is written
168 A = U * SIGMA * transpose(V)
170 where SIGMA is an M-by-N matrix which is zero except for its
171 min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and
172 V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA
173 are the singular values of A; they are real and non-negative, and
174 are returned in descending order. The first min(m,n) columns of
175 U and V are the left and right singular vectors of A.
177 Note that the routine returns V**T, not V.
179 A list of three items is returned. The first is a list containing the
180 non-zero elements of SIGMA. If jobu is not false, The second element
181 is the matrix U. Otherwise it is false. Similarly, the third element
182 is V**T or false, depending on jobvt."
184 (flet ((maxify-vector (v)
185 `((mlist) ,@(coerce v
'list
)))
187 (if arg
"All columns of U" "No columns of U"))
189 (if arg
"All columns of V^T" "No columns of V^T")))
190 (multiple-value-bind (nrow ncol
)
191 (maxima-matrix-dims a
)
192 (let* ((a-mat (lapack-lispify-matrix a nrow ncol
))
193 (s (make-array (min nrow ncol
) :element-type
'flonum
))
194 (u (make-array (* nrow nrow
) :element-type
'flonum
))
195 (u1 (make-array (list nrow nrow
) :element-type
'flonum
197 (vt (make-array (* ncol ncol
)
198 :element-type
'flonum
))
199 (vt1 (make-array (list ncol ncol
) :element-type
'flonum
201 (wr (make-array 1 :element-type
'flonum
)))
202 ;; XXX: FIXME: We need to do more error checking in the calls to
204 (multiple-value-bind (z-jobu z-jobvt z-m z-n z-a z-lda z-s z-u z-ldu
205 z-vt z-ldvt z-work z-lwork info
)
206 ;; Figure out the optimum size for the work array
207 (lapack::dgesvd
(fixup-jobu jobu
)
214 (declare (ignore z-jobu z-jobvt z-m z-n z-a z-lda z-s z-u z-ldu
215 z-vt z-ldvt z-work z-lwork info
))
216 (let* ((opt-lwork (truncate (aref wr
0)))
217 (work (make-array opt-lwork
:element-type
'flonum
)))
218 ;; Allocate the optimum work array and do the requested
220 (multiple-value-bind (z-jobu z-jobvt z-m z-n z-a z-lda z-s z-u
221 z-ldu z-vt z-ldvt z-work z-lwork info
)
222 (lapack::dgesvd
(fixup-jobu jobu
)
229 (declare (ignore z-jobu z-jobvt z-m z-n z-a z-lda z-s z-u z-ldu
230 z-vt z-ldvt z-work z-lwork
))
232 (merror "DGESVD: invalid arguments: ~D" info
))
234 (merror "DGESVD: failed to converge: ~D" info
)))
235 (let ((u-max (if jobu
236 (lapack-maxify-matrix nrow nrow u1
)
239 (lapack-maxify-matrix ncol ncol vt1
)
241 (s-max (maxify-vector s
)))
242 `((mlist) ,s-max
,u-max
,vt-max
)))))))))
246 (defun $dlange
(norm a
)
248 DLANGE returns the value
250 DLANGE = ( max(abs(A(i,j))), NORM = '$max
252 ( norm1(A), NORM = '$one_norm
254 ( normI(A), NORM = '$inf_norm
256 ( normF(A), NORM = '$frobenius
258 where norm1 denotes the one norm of a matrix (maximum column sum),
259 normI denotes the infinity norm of a matrix (maximum row sum) and
260 normF denotes the Frobenius norm of a matrix (square root of sum of
261 squares). Note that max(abs(A(i,j))) is not a matrix norm."
263 ;; Norm should be '$max, '$one_norm, '$inf_norm, '$frobenius
264 (multiple-value-bind (nrows ncols
)
265 (maxima-matrix-dims a
)
266 (let* ((a-mat (lapack-lispify-matrix a nrows ncols
))
267 (norm-type (ecase norm
272 (work (make-array (if (equal norm-type
"I") nrows
0)
273 :element-type
'flonum
)))
274 (lapack::dlange norm-type nrows ncols a-mat nrows work
))))
277 (defun $zlange
(norm a
)
279 DLANGE returns the value
281 DLANGE = ( max(abs(A(i,j))), NORM = '$max
283 ( norm1(A), NORM = '$one_norm
285 ( normI(A), NORM = '$inf_norm
287 ( normF(A), NORM = '$frobenius
289 where norm1 denotes the one norm of a matrix (maximum column sum),
290 normI denotes the infinity norm of a matrix (maximum row sum) and
291 normF denotes the Frobenius norm of a matrix (square root of sum of
292 squares). Note that max(abs(A(i,j))) is not a matrix norm."
294 ;; Norm should be '$max, '$one_norm, '$inf_norm, '$frobenius
295 (multiple-value-bind (nrows ncols
)
296 (maxima-matrix-dims a
)
297 (let* ((a-mat (lapack-lispify-matrix a nrows ncols
))
298 (norm-type (ecase norm
303 (work (make-array (if (equal norm-type
"I") nrows
0)
304 :element-type
'flonum
)))
305 (lapack::zlange norm-type nrows ncols a-mat nrows work
))))
307 (defun $zgeev
(a &optional right-vec-p left-vec-p
)
309 ZGEEV computes for an N-by-N complex nonsymmetric matrix A, the
310 eigenvalues and, optionally, the left and/or right eigenvectors.
312 The right eigenvector v(j) of A satisfies
313 A * v(j) = lambda(j) * v(j)
314 where lambda(j) is its eigenvalue.
315 The left eigenvector u(j) of A satisfies
316 u(j)**H * A = lambda(j) * u(j)**H
317 where u(j)**H denotes the conjugate transpose of u(j).
319 The computed eigenvectors are normalized to have Euclidean norm
320 equal to 1 and largest component real.
322 A list of three items is returned. The first item is a list of the
323 eigenvectors. The second item is false or the matrix of right
324 eigenvectors. The last itme is false or the matrix of left
326 (flet ((make-eigval (w)
327 `((mlist) ,@(map 'list
#'(lambda (z)
328 (add (realpart z
) (mul '$%i
(imagpart z
))))
331 (let* ((n (maxima-matrix-dims a
))
332 (a-mat (lapack-lispify-matrix a n n t
))
333 (w (make-array n
:element-type
'(complex flonum
)))
334 (vl (make-array (if left-vec-p
(* n n
) 0)
335 :element-type
'(complex flonum
)))
336 (vr (make-array (if right-vec-p
(* n n
) 0)
337 :element-type
'(complex flonum
)))
338 (rwork (make-array 1 :element-type
'flonum
)))
339 ;; XXX: FIXME: We need to do more error checking in the calls to
341 (multiple-value-bind (z-jobvl z-jobvr z-n z-a z-lda z-wr z-wi z-vl
342 z-ldvl z-vr z-ldvr z-work z-lwork info
)
343 ;; Figure out how much space we need in the work array.
344 (lapack:zgeev
(if left-vec-p
"V" "N")
345 (if right-vec-p
"V" "N")
346 n a-mat n w vl n vr n w -
1 rwork
0)
347 (declare (ignore z-jobvl z-jobvr z-n z-a z-lda z-wr z-wi z-vl
348 z-ldvl z-vr z-ldvr z-work z-lwork info
))
349 (let* ((opt-lwork (truncate (realpart (aref w
0))))
350 (work (make-array opt-lwork
:element-type
'(complex flonum
)))
351 (rwork (make-array opt-lwork
:element-type
'flonum
)))
352 ;; Now do the work with the optimum size of the work space.
353 (multiple-value-bind (z-jobvl z-jobvr z-n z-a z-lda z-wr z-wi z-vl
354 z-ldvl z-vr z-ldvr z-work z-lwork info
)
355 (lapack:zgeev
(if left-vec-p
"V" "N")
356 (if right-vec-p
"V" "N")
357 n a-mat n w vl n vr n work opt-lwork rwork
0)
358 (declare (ignore z-jobvl z-jobvr z-n z-a z-lda z-wr z-wi z-vl
359 z-ldvl z-vr z-ldvr z-work z-lwork
))
361 (merror "ZGEEV: invalid arguments: ~D" info
))
363 (merror "ZGEEV: failed to converge: ~D" info
)))
364 (let ((e-val (make-eigval w
))
365 (e-vec-right (if right-vec-p
366 (lapack-maxify-matrix n n vr
)
368 (e-vec-left (if left-vec-p
369 (lapack-maxify-matrix n n vl
)
371 `((mlist) ,e-val
,e-vec-right
,e-vec-left
))))))))
373 (defun $zheev
(a &optional eigen-vector-p
)
374 (flet ((make-eigval (w)
375 `((mlist) ,@(map 'list
#'(lambda (z)
376 (add (realpart z
) (mul '$%i
(imagpart z
))))
379 (let* ((n (maxima-matrix-dims a
))
380 (a-mat (lapack-lispify-matrix a n n t
))
381 (w (make-array n
:element-type
'flonum
))
382 (work (make-array 1 :element-type
'(complex flonum
)))
383 (rwork (make-array (max 1 (- (* 3 n
) 2))
384 :element-type
'flonum
)))
385 ;; XXX: FIXME: We need to do more error checking in the calls to
387 (multiple-value-bind (z-jobz z-uplo z-n z-a z-lda z-w z-work
388 z-lwork z-rwork info
)
389 ;; Figure out how much space we need in the work array.
390 (lapack:zheev
(if eigen-vector-p
"V" "N")
400 (declare (ignore z-jobz z-uplo z-n z-a z-lda z-w z-work
402 (let* ((opt-lwork (truncate (realpart (aref work
0))))
403 (work (make-array opt-lwork
:element-type
'(complex flonum
))))
404 ;; Now do the work with the optimum size of the work space.
405 (multiple-value-bind (z-jobz z-uplo z-n z-a z-lda z-w z-work
406 z-lwork z-rwork info
)
407 (lapack:zheev
(if eigen-vector-p
"V" "N")
417 (declare (ignore z-jobz z-uplo z-n z-a z-lda z-w z-work
420 (merror "ZHEEV: invalid arguments: ~D" info
))
422 (merror "ZHEEV: failed to converge: ~D" info
)))
423 (let ((e-val (make-eigval w
))
424 (e-vec (if eigen-vector-p
425 (lapack-maxify-matrix n n a-mat
)
427 `((mlist) ,e-val
,e-vec
))))))))