Remove the obsolete DEFMTRFUN-EXTERNAL macro
[maxima.git] / share / hompack / hompack-interface.lisp
blob5838c15af6a91db0ff84d88cc85de952d5b2d8f6
1 ;;; -*- Mode: lisp -*-
3 ;; Simple Maxima interface to hompack routines
5 (in-package #:maxima)
7 (defmvar $debug_hompack nil
8 "Set to non-NIL to enable some debugging prints")
10 (defun parse-equations (eqnlist varlist fname)
11 (let ((eqns (cdr eqnlist))
12 (vars (cdr varlist))
13 (total-deg 1)
14 numt
15 final-kdeg
16 final-coef)
17 ;; TODO: Check that varlist is a list of symbols.
19 ;; Process each equation
20 (dolist (eqn eqns)
21 (when $debug_hompack
22 (displa eqn))
24 (unless ($polynomialp eqn varlist)
25 (merror "~M: Is not a polynomial in ~M: ~M"
26 fname varlist eqn))
28 (let ((args (if (string-equal ($op eqn) "+")
29 (cdr ($args eqn))
30 (list eqn)))
31 eqn-deg kdeg coef)
33 (push (length args) numt)
34 ;; TODO: Verify that eqn is a sum of terms.
36 ;; Process each term of the equation
37 (dolist (term args)
38 (when $debug_hompack
39 (displa term))
40 (let* ((prod 1)
41 (term-deg 0)
42 (deg (mapcar #'(lambda (v)
43 ;; TODO: verify that V is a product
45 ;; For the term, determine the power
46 ;; of each variable and also create
47 ;; a product of the variable raised
48 ;; to the corresponding power.
49 (let ((p ($hipow term v)))
50 (incf term-deg p)
51 ;; TODO: Check that p is non-negative integer
52 (setf prod (mul prod (power v p)))
53 p))
54 vars))
55 (c ($float ($expand (div term prod)))))
56 ;; Check that c is a real number since it's supposed
57 ;; to be the numerical coefficient of the polynomial term.
58 (unless (floatp c)
59 (merror "~M: coefficient of ~M is not a number: ~M"
60 fname term c))
61 (when $debug_hompack
62 (format t "deg ~A~%" deg)
63 (format t "c ~A~%" c)
64 (format t "term deg ~A~%" term-deg))
65 (push term-deg eqn-deg)
66 ;; Accumulate these results for each term on a list.
67 (push c coef)
68 (push (list* '(mlist) deg) kdeg)))
69 (setf total-deg (* total-deg (reduce #'max eqn-deg)))
70 ;; Now accumulate the results from each equation on to the final list
71 (push (list* '(mlist) (nreverse kdeg)) final-kdeg)
72 (push (list* '(mlist) (nreverse coef)) final-coef)))
73 ;; Finally, return the full list of coefficients and degress for
74 ;; each term of each equation.
75 (setf final-kdeg (nreverse final-kdeg))
76 (setf final-coef (nreverse final-coef))
77 (setf numt (nreverse numt))
78 (values total-deg
79 final-kdeg
80 final-coef
81 numt)))
83 (defun convert-coef (coef)
84 (let* ((dim-j (length coef))
85 ;;(dim-k (reduce #'max (mapcar #'$length coef)))
86 (dim-k (reduce #'max coef :key #'$length))
87 (array (make-array (list dim-j dim-k) :element-type 'double-float))
88 (f2cl-array (make-array (* dim-j dim-k) :element-type 'double-float)))
89 (when $debug_hompack
90 (format t "dim ~A ~A~%" dim-j dim-k)
91 (format t "array ~A~%" array))
92 ;; F2CL maps multi-dimensional arrays into one-dimensional arrays
93 ;; in column-major order. That is, the order of indices is
94 ;; reversed.
95 (loop for eqn in coef for j from 1 do
96 (loop for term in (cdr eqn) for k from 1 do
97 (when $debug_hompack
98 (format t "eqn ~A coef ~A = ~A~%" j k term))
99 (setf (aref array (1- j) (1- k)) ($float term))
100 (setf (f2cl-lib::fref f2cl-array (j k) ((1 dim-j) (1 dim-k)))
101 ($float term))))
102 (when $debug_hompack
103 (format t "coef array ~A~%" array))
104 f2cl-array))
106 (defun convert-kdeg (kdeg numt)
107 (let* ((dim-j (length kdeg))
108 (dim-l (1+ dim-j))
109 (dim-k (reduce #'max numt))
110 (array (make-array (list dim-j dim-l dim-k) :element-type 'f2cl-lib:integer4))
111 (f2cl-array (make-array (* dim-j dim-l dim-k) :element-type 'f2cl-lib:integer4)))
112 (loop for eqn in kdeg for j from 1 do
113 (loop for term in (cdr eqn) for l from 1 do
114 (loop for p in (cdr term) for k from 1 do
115 (when $debug_hompack
116 (format t "set eqn ~A var ~A term ~A = ~A~%"
117 j k l p))
118 (setf (aref array (1- j) (1- k) (1- l)) p)
119 (setf (f2cl-lib::fref f2cl-array (j k l) ((1 dim-j) (1 dim-l) (1 dim-k))) p))))
120 (when $debug_hompack
121 (format t "kdeg array ~A~%" array))
122 f2cl-array))
124 (defmfun $hompack_polsys (eqnlist varlist
125 &key
126 (iflg1 0) (epsbig 1d-4) (epssml 1d-14) (numrr 10)
127 iflg2 sspar)
128 "Solve the system of n polynomial equations in n unknowns.
130 EQNLIST list of polynomial equations. Must be in the form sum(c[k]*x1^p1*x2^p2*...*xn^pn)
131 VARLIST list of the n variables.
133 Returns a list with the following components
134 ifail Indicates results of the algorithm:
135 -1 -
136 -2 -
137 -3 - computed total deg is too small (shouldn't happen)
138 -4 - length of real work area is too small (shouldn't happen)
139 -5 - length of integer work area is too small (shouldn't happen)
140 -6 - if ifgl1 is not 00 or 01 or 10 or 11 (shouldn't happen)
141 0 - normal return (iflg2 list only contains 1)
142 1 - error (iflg2 list has values other than 1)
143 roots The roots of the system of equations
144 iflg2 How the path terminated
145 1 - normal return
146 2 - specified error tolerance cannot be met. Increase epsbig
147 and epssml
148 3 - maximum number of steps exceeded. Increase numrr.
149 However, the path may be diverging if the lambda value is
150 near 1 and the root values are large
151 4 - Jacobian matrix does not have full rank
152 5 - Tracking algorithm has lost the zero curve and is not making
153 progress. The error tolerances were too lenient.
154 6 - Normal flow Newton iteration failed to converge. The error
155 tolerance epsbig may be too stringent
156 7 - Illegal input parameters
157 lambda List of the final lambda (continuation) parameter
158 arclen List of the arc length of the patch for each root
159 nfe List of the number of Jacobian matrix evaluations required
160 to track each path
162 Optional keywords args (key = val)
163 IFLG1 One of 0, 1, 10, 11. See docs for more info.
164 EPSBIG Local error tolerance allowed the path tracker
165 EPSSML Accuracy desired for final solution.
166 NUMRR Number of multiples of 1000 steps before abandoning path.
168 IFLG2 List indicating which paths to search. -2 means track this
169 path. Length must be the total degree of the equations.
170 SSPAR List of parameters used for optimal step size estimation. If
171 SSPAR[j] < 0, use default value. See Fortran polynf and
172 stepnf for more info."
174 (unless (= ($length eqnlist) ($length varlist))
175 (merror "~M: Number of equations (~M) is not the number of variables (~M)"
176 %%pretty-fname (length eqnlist) (length varlist)))
177 (unless (and (listp varlist) (eq (caar varlist) 'mlist))
178 (merror "~M: ~M is not a list of variables"
179 %%pretty-fname varlist))
180 (unless (member iflg1 '(0 1 10 11))
181 (merror "~M: iflg1 must be 0, 1, 10, or 11. Got ~M"
182 %%pretty-fname iflg1))
184 (when sspar
185 (unless (and sspar (listp sspar) (eq (caar sspar) 'mlist))
186 (merror "~M: sspar must be a list not ~M"
187 %%pretty-fname sspar))
188 (unless (= ($length sspar) 8)
189 (merror "~M: sspar must have length 8 not ~M"
190 %%pretty-fname ($length sspar)))
191 (unless (every #'(lambda (x)
192 (typep ($float x) 'double-float))
193 (cdr sspar))))
195 (multiple-value-bind (total-deg kdeg coef numt)
196 (parse-equations eqnlist varlist %%pretty-fname)
197 (when iflg2
198 (unless (= (length iflg2) total-deg)
199 (merror "~M: Length of iflg2 must be ~M not ~M"
200 %%pretty-fname total-deg (length iflg2)))
201 (unless (every #'integerp (cdr iflg2))
202 (merror "~M: iflg2 must be integers: ~M"
203 %%pretty-fname iflg2)))
204 (let* ((n ($length eqnlist))
205 (mmaxt (reduce #'max numt))
206 (lenwk (+ 21 (* 61 n) (* 10 n n) (* 7 n mmaxt) (* 4 n n mmaxt)))
207 (wk (make-array lenwk :element-type 'double-float))
208 (leniwk (+ 43 (* 7 n) (* n (1+ n) mmaxt)))
209 (iwk (make-array lenwk :element-type 'f2cl-lib:integer4))
210 (lamda (make-array total-deg :element-type 'double-float))
211 (arclen (make-array total-deg :element-type 'double-float))
212 (nfe (make-array total-deg :element-type 'f2cl-lib:integer4))
213 (roots (make-array (* 2 (1+ n) total-deg) :element-type 'double-float))
214 (iflg2 (if iflg2
215 (make-array total-deg :element-type 'f2cl-lib:integer4
216 :initial-contents (cdr iflg2))
217 (make-array total-deg :element-type 'f2cl-lib:integer4
218 :initial-element -2)))
219 (sspar (if sspar
220 (make-array 8 :element-type 'double-float
221 :initial-contents (cdr ($float sspar)))
222 (make-array 8 :element-type 'double-float
223 :initial-element -1d0)))
224 (coef-array (convert-coef coef))
225 (kdeg-array (convert-kdeg kdeg numt))
226 (numt (make-array (length numt) :element-type 'f2cl-lib:integer4
227 :initial-contents numt)))
228 (when $debug_hompack
229 (format t "coef-array ~A~%" coef-array)
230 (format t "kdeg-array ~A~%" kdeg-array))
232 (multiple-value-bind (ignore-n ignore-numt ignore-coef-array ignore-kdeg-array
233 ret-iflg1)
234 (hompack::polsys n numt coef-array kdeg-array iflg1 iflg2 epsbig epssml
235 sspar numrr n mmaxt total-deg lenwk leniwk
236 lamda roots arclen nfe wk iwk)
237 (declare (ignore ignore-n ignore-numt ignore-coef-array ignore-kdeg-array))
239 ;; Convert the roots into a list where each element of the
240 ;; list is a list of the form [x1=r1, x2=r2, ..., xn=rn].
242 (flet
243 ((maxify-roots (roots)
244 (list* '
245 (mlist)
246 (loop for m from 1 to total-deg
247 collect
248 (list* '(mlist)
249 (loop for j from 1 to n for var in (cdr varlist)
250 collect
251 (list
252 '(mequal)
253 var
254 (add (f2cl-lib::fref roots
255 (1 j m)
256 ((1 2) (1 (1+ n)) (1 total-deg)))
257 (mul '$%i
258 (f2cl-lib::fref roots
259 (2 j m)
260 ((1 2) (1 (1+ n)) (1 total-deg))))))))))))
261 (let
262 ((ret-code
263 ;; Figure out if anything bad happened. If iflg1 is
264 ;; negative, just use it. Otherwise, check that iflg2
265 ;; Has a normal return for all values. If so, return
266 ;; 0. Otherwise return 1 to indicate that.
267 (cond ((minusp ret-iflg1)
268 ret-iflg1)
269 ((every #'(lambda (x) (= x 1)) iflg2)
272 1)))
273 (r (maxify-roots roots)))
275 (list '(mlist)
276 ret-code
278 (list* '(mlist) (coerce iflg2 'list))
279 (list* '(mlist) (coerce lamda 'list))
280 (list* '(mlist) (coerce arclen 'list))
281 (list* '(mlist) (coerce nfe 'list)))))))))
284 (defvar *f-fun* nil)
285 (defvar *fjac-fun* nil)
287 (in-package "HOMPACK")
289 ;; For HOMPACK_FIXPDF to evaluate the given function at the given
290 ;; point X, returning the values in V. The function is in *F-FUN*,
291 ;; computed by Maxima.
292 (defun f (x v)
293 (declare (type (array double-float (*)) v x))
294 (f2cl-lib:with-multi-array-data
295 ((x cl:double-float x-%data% x-%offset%)
296 (v cl:double-float v-%data% v-%offset%))
297 (funcall maxima::*f-fun* x v)
298 (values nil nil)))
301 ;; For HOMPACK_FIXPDF to evaluate the k-th column of the Jacobian at
302 ;; the given point X, returning the values in V. The Jacobian is in
303 ;; *FJAC-FUN*, computed by Maxima.
304 (defun fjac (x v k)
305 (declare (type (f2cl-lib:integer4) k) (type (cl:array cl:double-float (*)) v x))
306 (f2cl-lib:with-multi-array-data
307 ((x double-float x-%data% x-%offset%)
308 (v double-float v-%data% v-%offset%))
309 (funcall maxima::*fjac-fun* x v k)
310 (values nil nil nil)))
312 (in-package "MAXIMA")
314 (defmfun $hompack_fixpdf (fcns varlist
315 &key
316 (iflag -1) (arctol -1d0) (eps 1d-5) (trace 0) inita)
317 "hompack_fixpdf"
319 (let* ((n (length (cdr varlist)))
320 (y (make-array (1+ n) :element-type 'double-float))
321 (ndima n)
322 (a (make-array ndima :element-type 'double-float))
323 (yp (make-array (1+ n) :element-type 'double-float))
324 (ypold (make-array (1+ n) :element-type 'double-float))
325 (qr (make-array (* n (1+ n)) :element-type 'double-float))
326 (alpha (make-array n :element-type 'double-float))
327 (tz (make-array (1+ n) :element-type 'double-float))
328 (pivot (make-array (1+ n) :element-type 'f2cl-lib:integer4))
329 (wt (make-array (1+ n) :element-type 'double-float))
330 (phi (make-array (* 16 (1+ n)) :element-type 'double-float))
331 (p (make-array (1+ n) :element-type 'double-float))
332 (par (make-array 1 :element-type 'double-float))
333 (ipar (make-array 1 :element-type 'f2cl-lib:integer4))
334 (arclen 0d0)
335 (nfe 0)
336 (fvs (coerce-float-fun fcns varlist))
337 (fj (let ((fj (meval `(($jacobian) ,fcns ,varlist))))
338 (mapcar #'(lambda (fjf)
339 (coerce-float-fun fjf varlist))
340 ;; The Fortran code wants the k-th column, so
341 ;; transpose the Jacobian here to make the
342 ;; interface between Maxima and Fortran a
343 ;; little simpler. Then we can just extract
344 ;; the k-th row of the transpose to get what's
345 ;; needed.
346 (cdr ($transpose fj)))))
347 (*f-fun*
348 #'(lambda (x v)
349 ;; Compute the functions at the point X and return the
350 ;; value in V.
351 (declare (type (cl:array cl:double-float (*)) x v))
352 (let* ((x-list (coerce x 'list))
353 (v-list (apply fvs x-list)))
354 (map-into v #'identity (cdr v-list)))))
355 (*fjac-fun*
356 #'(lambda (x v k)
357 (declare (type (f2cl-lib:integer4) k) (type (cl:array cl:double-float (*)) v x))
358 ;; Compute the k'th column of the Jacobian matrix of
359 ;; F(x) evaluated at X and store the result in V.
360 (let* ((x-list (coerce x 'list))
361 (v-list (apply (elt fj (1- k)) x-list)))
362 (map-into v #'identity (cdr v-list))))))
364 (when inita
365 (loop for ia in (cdr inita)
366 for k from 0 below (length a)
368 (let ((val ($float ia)))
369 (setf (aref a k) val)
370 (setf (aref y (1+ k)) val))))
371 (multiple-value-bind (ignore-n ignore-y
372 iflag arctol eps
373 ignore-trace ignore-a ignore-ndima
375 arclen ignore-yp ignore-ypold ignore-qr ignore-alpha
376 ignore-tz ignore-pivot ignore-wt ignore-phi ignore-p
377 ignore-par ignore-ipar)
378 (hompack::fixpdf n y iflag arctol eps trace a ndima nfe arclen yp ypold
379 qr alpha tz pivot wt phi p par ipar)
380 (declare (ignore ignore-n ignore-y ignore-trace ignore-a ignore-ndima
381 ignore-yp ignore-ypold ignore-qr ignore-alpha
382 ignore-tz ignore-pivot ignore-wt ignore-phi ignore-p ignore-par
383 ignore-ipar))
384 (let ((ans-y (list* '(mlist) (rest (coerce y 'list)))))
385 (list '(mlist)
386 iflag
387 ans-y
388 arctol
391 arclen)))))