2 (in-package :lineal.math
)
5 (defun tuple-multiple-nsubtrn (u k v
)
6 "Vector /u/ minus scalar /k/ times vector /v/"
7 (do ((u-rem u
(cdr u-rem
))
9 ((not (or u-rem v-rem
)) u
)
10 (decf (car u-rem
) (* k
(car v-rem
)))))
12 (defun row-clear-leading-with (u rows
)
13 (declare (inline tuple-multiple-nsubtrn
))
14 "Clear the first column with the leading-1 row."
15 (loop :for to-clear
:in rows
:collect
16 (cdr (tuple-multiple-nsubtrn
17 to-clear
(car to-clear
) u
))))
19 (defun row-reduction (a incheck-fn nolead-fn
)
23 :with toprow
= (car a
)
25 :unless
(zerop (caar rows
)) :do
27 (rotatef (caar rows
) (car toprow
))
28 (rotatef (cdar rows
) (cdr toprow
))
29 (funcall incheck-fn
(cdr nat-zero
)
32 :collect
(cdar rows
) :into nat-zero
33 :finally
(funcall nolead-fn nat-zero
))
34 (funcall incheck-fn nil
(car a
) (cdr a
)))))
36 (defun det (a &optional
(destroy nil
)
42 (setq curr-det
(* curr-det
(car row
)))
43 (row-clear-leading-with
44 (tuple-scalar-ndivisn row
(car row
))
49 (setq a tmp-a
) #'incheck-fn
#'nolead-fn
))
51 (nat-zero lead toclear
)
52 (when nat-zero
(setq curr-det
(- curr-det
)))
55 (fix-in-lead lead toclear
))))
58 (declare (ignore nat-zero
))
60 (det-recurse (if destroy a
(copy-tree a
)))
61 ;^ First call the recursion...^
62 (if a
0 curr-det
)));->... then return the result.
65 (a &optional
(destroy nil
)
66 &aux
(orig-mtrix (if destroy a
(copy-tree a
))))
72 (setq a tmp-a
) #'incheck-fn
#'nolead-fn
))
74 (nat-zero lead toclear
)
78 (row-clear-leading-with
79 (tuple-scalar-ndivisn lead
(car lead
))
81 (nolead-fn (nat-zero) (r-ef-recurse nat-zero
)))
82 (r-ef-recurse orig-mtrix
)
86 (a &optional
(destroy nil
)
88 (orig-mtrix (if destroy a
(copy-tree a
))))
89 "Reduced row-echelon form"
94 (setq a tmp-a
) #'incheck-fn
#'nolead-fn
))
96 (nat-zero lead toclear
)
97 (tuple-scalar-ndivisn lead
(car lead
))
100 (row-clear-leading-with
104 (row-clear-leading-with
108 ;V No leading 1 can be obtained, go to V
109 ;V next column in /also-clear/ without V
110 ;V clearing the current one. V
111 (do ((toclear also-clear
(cdr toclear
)))
113 (rplaca toclear
(cdar toclear
)))
114 (rr-ef-recurse nat-zero
)))
115 (rr-ef-recurse orig-mtrix
)
119 (a &key
(destroy nil
)
120 ((dimdom dimdom-left
) (length (car a
)))
121 &aux
(also-clear nil
)
122 ;V Collect basis for nullspace.V
123 (null-basis nil
) (nullity-sofar 0))
124 "A basis for the nullspace of a matrix."
128 (do ((toclear also-clear
(cdr toclear
))
131 (make-list nullity-sofar
133 (cons -
1 (make-list (decf dimdom-left
)
134 :initial-element
0)))))
137 (push newcol null-basis
))
138 (push (pop (car toclear
)) newcol
)))
142 (setq a tmp-a
) #'incheck-fn
#'nolead-fn
))
144 (nat-zero lead toclear
)
145 (tuple-scalar-ndivisn lead
(car lead
))
148 (row-clear-leading-with
153 (row-clear-leading-with
158 (nullspace-recurse nat-zero
)))
159 (nullspace-recurse (if destroy a
(copy-tree a
)))
160 ;V When done with rr-ef, add all V
161 ;V remaining columns to the basis.V
162 (dotimes (iters dimdom-left
) (add-to-basis))
165 (defun mtrix-square-identity (n)
166 "Create a square identity matrix with /n/ rows and columns."
169 :until
(= x n
) :collect
170 (nconc (make-list x
:initial-element
0)
173 :initial-element
0)))))
175 (defun mtrix-augment (a b
)
176 "Augment matrix /a/ with /b/"
179 #'(lambda (arow brow
)
180 (rplacd (last arow
) brow
))
182 ;V Return an "undo" procedure.V
184 (loop :for atail
:in tails
185 :do
(rplacd atail nil
)))))
187 ; todo: make row swaps more efficient,
188 ; perhaps a full-fledged row-reduction function is needed
190 (a &optional
(row-count (length a
)))
191 "Inverse of a given matrix."
192 (setq a
(copy-tree a
))
193 (mapc #'nconc a
(mtrix-square-identity row-count
))
194 (mapcar #'(lambda (row) (nthcdr row-count row
))