home *** CD-ROM | disk | FTP | other *** search
Text File | 1989-04-27 | 8.8 KB | 286 lines | [TEXT/????] |
- ;;; $Header: linear.scm,v 1.2 87/08/28 00:55:51 GMT gjs Exp $
- ;;;; 6.003 Linear System Solver(s)
-
- ;;; This file contains the following definitions:
- ;;;
- ;;; (solve-linear-system a-matrix b-vector) => x-vector
- ;;;
- ;;; (lu-backsubstitute lu-matrix lu-permutation b-vector) => x-vector
- ;;;
- ;;; (lu-null-vector <lu-matrix> <index>) ==> <vector>
- ;;;
- ;;; (lu-decompose matrix [barf-if-zero-pivot]) =>
- ;;; (lu-matrix lu-permutation sign)
- ;;;
- ;;; (lu-rank lu-matrix) => non-negative-integer
- ;;; (lu-nullity lu-matrix) => non-negative-integer
- ;;;
- ;;; (sign-of-permutation lu-permutation) => {+1, -1}
- ;;;
-
- (if-mit
- (declare (usual-integrations = + - * /
- zero? 1+ -1+
- ;; truncate round floor ceiling
- sqrt exp log sin cos)))
-
- ;;; Solves an inhomogeneous system of linear equations, A*X=B, returning
- ;;; the vector X.
-
- (define (solve-linear-system a b)
- (let ((lu (lu-decompose a)))
- (let ((lumatrix (car lu))
- (lupermutations (cadr lu)))
- (lu-backsubstitute lumatrix lupermutations b))))
-
-
- ;;; THE NEXT PROCEDURE ASSUMES THAT THE MATRIX IS IN LU FORM
- ;;; The rank is just the number of non-zero diagonal elements.
-
- (define (lu-nullity m)
- (count-elements heuristically-zero?
- (diagonal m)))
-
- (define (lu-rank m)
- (let ((n (lu-nullity m)))
- (- (num-rows m) n)))
-
- (define (heuristically-zero? z)
- (< (magnitude z) heuristic-zero-test-bugger-factor))
-
- ;;; The following number was assigned by HAL. -- GJS & MH.
- (define heuristic-zero-test-bugger-factor 1e-9)
-
- ;;; LU decomposition -- uses Crout's algorithm (see Press, section 2.3).
- ;;; The LU decomposition returns a LIST of the resulting matrix, the
- ;;; permutation vector, and the sign (+1 or -1) of the permutation.
- ;;; The only currently-defined flag controls error-trapping on detection
- ;;; of a zero-pivot. This flag, if omitted, defaults to true.
-
- ;;; We define the flags first:
- (define-integrable barf-on-zero-pivot true)
- (define-integrable allow-zero-pivot false)
-
- (define (lu-decompose m . flags)
- (let* ((barf-if-zero-pivot? (if flags (car flags) true))
- (n (num-rows m))
- (perms (generate-vector n (lambda (i) i))) ;row permutations
- (sign 1) ;1 for even permutations, -1 for odd
- ;; We must copy the matrix, since Crout's algorithm clobbers it.
- (m (copy-matrix m)))
- (let jloop ((j 0))
- (if (< j n)
- (begin
- (let iloop ((i 0)) ;compute elements above diagonal
- (if (<= i j)
- (begin (matrix-set! m i j (upper-eqn i j m))
- (iloop (1+ i)))))
- (let iloop ((i (1+ j))) ;compute elements below diagonal
- (if (< i n)
- (begin (matrix-set! m i j (lower-eqn i j m))
- (iloop (1+ i)))))
- (let* ((pivot-info (find-best-pivot m j n))
- (pivot (car pivot-info))
- (pivot-index (cdr pivot-info)))
- (if (< (abs pivot) tiny-pivot-bugger-factor)
- (if barf-if-zero-pivot?
- (error "Singular matrix - zero pivot")
- (jloop (1+ j)))
- (let ((1/pivot (/ 1 pivot)))
- (lu-row-swap m j pivot-index perms)
- (if (not (= j pivot-index))
- (set! sign (- sign)))
- (let iloop ((i (1+ j))) ;divide through by pivot
- (if (= i n)
- 'done
- (begin (matrix-set! m i j
- (* (matrix-ref m i j) 1/pivot))
- (iloop (1+ i)))))
- (jloop (1+ j))))))))
- (list m perms sign)))
-
- ;;; As a practical matter, the following theoretical factor need not
- ;;; be adjusted if conditions of STP do not precisely apply.
- (define tiny-pivot-bugger-factor (/ 1 6.0238e23))
-
- ;;; Note that we start looping at the jth row in searching for the
- ;;; next pivot. We can make this somewhat better numerically by
- ;;; including scale factors for the columns, but i'm too lazy to do
- ;;; this now. -- HAL
-
- (define (find-best-pivot m j n)
- (let ((column (generate-vector n (lambda (i) (matrix-ref m i j)))))
- (let iloop ((i j) (bestindex -1) (maxval -1) (pivot -1))
- (if (= i n)
- (cons pivot bestindex)
- (let* ((p (vector-ref column i))
- (absp (abs p)))
- (if (> absp maxval)
- (iloop (+ i 1) i absp p)
- (iloop (+ i 1) bestindex maxval pivot)))))))
-
- (define (upper-eqn i j m)
- (if (= i 0)
- (matrix-ref m i j)
- (let kloop ((k 0) (sum 0))
- (if (= k i)
- (- (matrix-ref m i j) sum)
- (kloop (+ k 1)
- (+ sum (* (matrix-ref m i k) (matrix-ref m k j))))))))
-
- (define (lower-eqn i j m)
- (let kloop ((k 0) (sum 0))
- (if (= k j)
- (- (matrix-ref m i j) sum)
- (kloop (+ k 1)
- (+ sum (* (matrix-ref m i k) (matrix-ref m k j)))))))
-
- (define (lu-row-swap m i1 i2 perms)
- (define (swap-elements vector i j)
- (let ((temp (vector-ref vector i)))
- (vector-set! vector i (vector-ref vector j))
- (vector-set! vector j temp)))
- (if (not (= i1 i2))
- (begin (swap-elements perms i1 i2)
- ;;uses fact that matrix is a vector of rows
- (swap-elements m i1 i2))))
-
- ;;; Back substitution (see Press, page 32)
-
- (define (lu-backsubstitute m perm b)
- (let* ((n (vector-length b))
- (top (- n 1))
- (y (make-vector n '()))
- (x (make-vector n '())))
- (let fdloop ((i 0))
- (if (< i n)
- (begin
- (vector-set! y i
- (- (vector-ref b (vector-ref perm i))
- (let jloop ((j 0) (sum 0))
- (if (= j i)
- sum
- (jloop (+ 1 j)
- (+ sum
- (* (vector-ref y j)
- (matrix-ref m i j))))))))
- (fdloop (+ i 1)))))
- (let bkloop ((i top))
- (if (>= i 0)
- (begin
- (vector-set! x i
- (/ (- (vector-ref y i)
- (let jloop ((j (+ i 1)) (sum 0))
- (if (= j n)
- sum
- (jloop (+ 1 j)
- (+ sum
- (* (vector-ref x j)
- (matrix-ref m i j)))))))
- (matrix-ref m i i)))
- (bkloop (- i 1)))))
- x))
-
- ;;; In the case of a homogeneous system we can solve if the matrix is
- ;;; singular. For each non-negative integer index less than the nullity of
- ;;; the lu-matrix we can obtain an independent solution vector (the entire
- ;;; set spans the null space).
-
- ;;; In this case, the upper part of the matrix contains the U part of
- ;;; the LU decomposition. The L part is irrelevant to this
- ;;; process since we are solving a homogeneous equation. The
- ;;; algorithm is to work backwards, from n-1 to 0, solving the
- ;;; equations m[i,i]x[i] + m[i,i+1]x[i+1] + ... = 0.
- ;;; If m[i,i] is zero (to within the tolerance), then we can set x[i]
- ;;; to an arbitrary value.
- ;;; We set the kth arbitary unknown to 1 and the rest to 0 (0<=k<nullity).
- ;;; If a luser supplies a k>=nullity, he will get a zero vector.
-
- (define (lu-null-vector m k)
- (let* ((n (num-rows m))
- (top (- n 1))
- (x (make-vector n '())))
- (let bkloop ((i top) (acount 0))
- (if (>= i 0)
- (let ((p (matrix-ref m i i)))
- (if (heuristically-zero? p)
- (begin
- (if (= acount k)
- (vector-set! x i 1)
- (vector-set! x i 0))
- (bkloop (-1+ i) (1+ acount)))
- (let ((s (let jloop ((j (+ i 1)) (sum 0))
- (if (= j n)
- sum
- (jloop (+ 1 j)
- (+ sum
- (* (vector-ref x j)
- (matrix-ref m i j))))))))
- (vector-set! x i (/ (- s) p))
- (bkloop (- i 1) acount))))))
- x))
-
- ;;; The following procedure is a hack to get the sign of the permutation
- ;;; generated by LU-DECOMPOSE. This is unnecessary now, because LU-DECOMPOSE
- ;;; has since been modified to return the permutation sign directly.
-
- (define (sign-of-permutation perm) ;perm = #(p1 p2 ... pn)
- (define (make-ordered-pairs L) ;L is a list, not a vector
- (define (pair-first-with-others L)
- (if (< (length L) 2)
- '()
- (let ((a (car L)) (b (cdr L)))
- (map (lambda (u) (cons a u)) b))))
- (if (< (length L) 2)
- '()
- (append (pair-first-with-others L)
- (make-ordered-pairs (cdr L)))))
- (let ((ordered-pairs (make-ordered-pairs (vector->list perm))))
- (let loop ((L ordered-pairs) (inversion-count 0))
- (if (null? L)
- (if (even? inversion-count) 1 -1)
- (let ((pair (car L)))
- (if (> (car pair) (cdr pair))
- (loop (cdr L) (+ inversion-count 1))
- (loop (cdr L) inversion-count)))))))
-
- ;;; This is stuff to test the LU-decomposition
-
- (define (hilb n)
- (generate-matrix n n (lambda (i j) (/ 1 (+ i j 2)))))
-
- (define (upper-matrix m)
- (let ((n (num-rows m)))
- (generate-matrix n n
- (lambda (i j)
- (if (> i j)
- 0
- (matrix-ref m i j))))))
-
- (define (lower-matrix m)
- (let ((n (num-rows m)))
- (generate-matrix n n
- (lambda (i j)
- (cond ((< i j) 0)
- ((= i j) 1)
- (else (matrix-ref m i j)))))))
-
-
- (define (lower*upper m)
- (matrix*matrix (lower-matrix m)
- (upper-matrix m)))
-
-
- (define (checklu m)
- (let ((ans (lu-decompose m allow-zero-pivot)))
- (let ((ans-mat (car ans)) (ans-perm (cadr ans)))
- (let ((prod (lower*upper ans-mat)))
- (let ((n (num-rows m)))
- (generate-matrix n n
- (lambda (i j)
- (- (matrix-ref prod i j)
- (matrix-ref m
- (vector-ref ans-perm i)
- j)))))))))
-
-