File: minpack-interface.lisp

package info (click to toggle)
maxima 5.21.1-2squeeze
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 94,928 kB
  • ctags: 43,849
  • sloc: lisp: 298,974; fortran: 14,666; perl: 14,325; tcl: 10,494; sh: 4,052; makefile: 2,975; ansic: 471; awk: 24; sed: 7
file content (253 lines) | stat: -rw-r--r-- 8,843 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
;;; -*- Mode: lisp -*-

;;; Simple Maxima interface to minpack routines

(in-package #-gcl #:maxima #+gcl "MAXIMA")

(defun least-squares (vars init-x fcns &key (jacobian t)
		      (tolerance (sqrt double-float-epsilon)))
  (let* ((n (length (cdr vars)))
	 (m (length (cdr fcns)))
	 (x (make-array n :element-type 'double-float
			:initial-contents (mapcar #'(lambda (z)
						      ($float z))
						  (cdr init-x))))
	 (fvec (make-array m :element-type 'double-float))
	 (fjac (make-array (* m n) :element-type 'double-float))
	 (ldfjac m)
	 (info 0)
	 (ipvt (make-array n :element-type 'f2cl-lib:integer4))
	 (fv (coerce-float-fun fcns vars))
	 (fj (cond ((eq jacobian t)
		    ;; T means compute it ourselves
		    (mfuncall '$jacobian fcns vars))
		   (jacobian
		    ;; Use the specified Jacobian
		    )
		   (t
		    ;; No jacobian at all
		    nil))))
    ;; Massage the Jacobian into a function
    (when jacobian
      (setf fj (coerce-float-fun fj vars)))
    (cond
      (jacobian
       ;; Jacobian given (or computed by maxima), so use lmder1
       (let* ((lwa (+ m (* 5 n)))
	      (wa (make-array lwa :element-type 'double-float)))
	 (labels ((fcn-and-jacobian (m n x fvec fjac ldfjac iflag)
		    (declare (type f2cl-lib:integer4 m n ldfjac iflag)
			     (type (cl:array double-float (*)) x fvec fjac))
		    (ecase iflag
		      (1
		       ;; Compute function at point x, placing result
		       ;; in fvec (subseq needed because sometimes
		       ;; we're called with vector that is longer than
		       ;; we want.  Perfectly valid Fortran, though.)
		       (let ((val (apply 'funcall fv (subseq (coerce x 'list) 0 n))))
			 (replace fvec (mapcar #'(lambda (z)
						   (cl:float z 1d0))
					       (cdr val)))))
		      (2
		       ;; Compute Jacobian at point x, placing result in fjac
		       (let ((j (apply 'funcall fj (subseq (coerce x 'list) 0 n))))
			 ;; Extract out elements of Jacobian and place into
			 ;; fjac, in column-major order.
			 (let ((row-index 0))
			   (dolist (row (cdr j))
			     (let ((col-index 0))
			       (dolist (col (cdr row))
				 (setf (aref fjac (+ row-index (* ldfjac col-index)))
				       (cl:float col 1d0))
				 (incf col-index)))
			     (incf row-index))))))
		    (values m n nil nil nil ldfjac iflag)))
	   (multiple-value-bind (var-0 var-1 var-2 var-3 var-4 var-5 ldfjac var-6 info)
	       (minpack:lmder1 #'fcn-and-jacobian
			       m
			       n
			       x
			       fvec
			       fjac
			       ldfjac
			       tolerance
			       info
			       ipvt
			       wa
			       lwa)
	     (declare (ignore ldfjac var-0 var-1 var-2 var-3 var-4 var-5 var-6))

	     ;; Return a list of the solution and the info flag
	     (list '(mlist)
		   (list* '(mlist) (coerce x 'list))
		   (minpack:enorm m fvec)
		   info)))))
      (t
       ;; No Jacobian given so we need to use differences to compute
       ;; a numerical Jacobian.  Use lmdif1.
       (let* ((lwa (+ m (* 5 n) (* m n)))
	      (wa (make-array lwa :element-type 'double-float)))
	 (labels ((fval (m n x fvec iflag)
		    (declare (type f2cl-lib:integer4 m n ldfjac iflag)
			     (type (cl:array double-float (*)) x fvec fjac))
		    ;; Compute function at point x, placing result in fvec
		    (let ((val (apply 'funcall fv (subseq (coerce x 'list) 0 n))))
		      (replace fvec (mapcar #'(lambda (z)
						(cl:float z 1d0))
					    (cdr val))))
		    (values m n nil nil iflag)))
	   (multiple-value-bind (var-0 var-1 var-2 var-3 var-4 var-5 info)
	       (minpack:lmdif1 #'fval
			       m
			       n
			       x
			       fvec
			       tolerance
			       info
			       ipvt
			       wa
			       lwa)
	     (declare (ignore var-0 var-1 var-2 var-3 var-4 var-5))

	     ;; Return a list of the solution and the info flag
	     (list '(mlist)
		   (list* '(mlist) (coerce x 'list))
		   (minpack:enorm m fvec)
		   info))))))))

(defun nonlinear-solve (vars init-x fcns &key (jacobian t)
			(tolerance (sqrt double-float-epsilon)))
  (let* ((n (length (cdr vars)))
	 (x (make-array n :element-type 'double-float
			:initial-contents (mapcar #'(lambda (z)
						      ($float z))
						  (cdr init-x))))
	 (fvec (make-array n :element-type 'double-float))
	 (fjac (make-array (* n n) :element-type 'double-float))
	 (ldfjac n)
	 (info 0)
	 (fv (coerce-float-fun fcns vars))
	 (fj (cond ((eq jacobian t)
		    ;; T means compute it ourselves
		    (mfuncall '$jacobian fcns vars))
		   (jacobian
		    ;; Use the specified Jacobian
		    )
		   (t
		    ;; No jacobian at all
		    nil))))
    ;; Massage the Jacobian into a function
    (when jacobian
      (setf fj (coerce-float-fun fj vars)))
    (cond
      (jacobian
       ;; Jacobian given (or computed by maxima), so use lmder1
       (let* ((lwa (/ (* n (+ n 13)) 2))
	      (wa (make-array lwa :element-type 'double-float)))
	 (labels ((fcn-and-jacobian (n x fvec fjac ldfjac iflag)
		    (declare (type f2cl-lib:integer4 n ldfjac iflag)
			     (type (cl:array double-float (*)) x fvec fjac))
		    (ecase iflag
		      (1
		       ;; Compute function at point x, placing result
		       ;; in fvec (subseq needed because sometimes
		       ;; we're called with vector that is longer than
		       ;; we want.  Perfectly valid Fortran, though.)
		       (let ((val (apply 'funcall fv (subseq (coerce x 'list) 0 n))))
			 (replace fvec (mapcar #'(lambda (z)
						   (cl:float z 1d0))
					       (cdr val)))))
		      (2
		       ;; Compute Jacobian at point x, placing result in fjac
		       (let ((j (apply 'funcall fj (subseq (coerce x 'list) 0 n))))
			 ;; Extract out elements of Jacobian and place into
			 ;; fjac, in column-major order.
			 (let ((row-index 0))
			   (dolist (row (cdr j))
			     (let ((col-index 0))
			       (dolist (col (cdr row))
				 (setf (aref fjac (+ row-index (* ldfjac col-index)))
				       (cl:float col 1d0))
				 (incf col-index)))
			     (incf row-index))))))
		    (values n nil nil nil ldfjac iflag)))
	   (multiple-value-bind (var-0 var-1 var-2 var-3 var-4 var-5 ldfjac var-6 info)
	       (minpack:hybrj1 #'fcn-and-jacobian
			       n
			       x
			       fvec
			       fjac
			       ldfjac
			       tolerance
			       info
			       wa
			       lwa)
	     (declare (ignore ldfjac var-0 var-1 var-2 var-3 var-4 var-5 var-6))

	     ;; Return a list of the solution and the info flag
	     (list '(mlist)
		   (list* '(mlist) (coerce x 'list))
		   (minpack:enorm n fvec)
		   info)))))
      (t
       ;; No Jacobian given so we need to use differences to compute
       ;; a numerical Jacobian.  Use lmdif1.
       (let* ((lwa (/ (* n (+ n 13)) 2))
	      (wa (make-array lwa :element-type 'double-float)))
	 (labels ((fval (n x fvec iflag)
		    (declare (type f2cl-lib:integer4 n ldfjac iflag)
			     (type (cl:array double-float (*)) x fvec fjac))
		    ;; Compute function at point x, placing result in fvec
		    (let ((val (apply 'funcall fv (subseq (coerce x 'list) 0 n))))
		      (replace fvec (mapcar #'(lambda (z)
						(cl:float z 1d0))
					    (cdr val))))
		    (values n nil nil iflag)))
	   (multiple-value-bind (var-0 var-1 var-2 var-3 var-4 info)
	       (minpack:hybrd1 #'fval
			       n
			       x
			       fvec
			       tolerance
			       info
			       wa
			       lwa)
	     (declare (ignore var-0 var-1 var-2 var-3 var-4))

	     ;; Return a list of the solution and the info flag
	     (list '(mlist)
		   (list* '(mlist) (coerce x 'list))
		   (minpack:enorm n fvec)
		   info))))))))

(defun $minpack_lsquares (fcns vars init-x &rest options)
  "Minimize the sum of the squares of m functions in n unknowns (n <= m)

   VARS    list of the variables
   INIT-X  initial guess
   FCNS    list of the m functions

   Optional keyword args (key = val)
   TOLERANCE  tolerance in solution 
   JACOBIAN   If true, maxima computes the Jacobian directly from FCNS.
              If false, the Jacobian is internally computed using a
              forward-difference approximation.
              Otherwise, it is a function returning the Jacobian"
  (let ((args (lispify-maxima-keyword-options options '($jacobian $tolerance))))
    (apply #'least-squares vars init-x fcns args)))

(defun $minpack_solve (fcns vars init-x &rest options)
  "Solve the system of n equations in n unknowns

   VARS    list of the n variables
   INIT-X  initial guess
   FCNS    list of the n functions

   Optional keyword args (key = val)
   TOLERANCE  tolerance in solution 
   JACOBIAN   If true, maxima computes the Jacobian directly from FCNS.
              If false, the Jacobian is internally computed using a
              forward-difference approximation.
              Otherwise, it is a function returning the Jacobian"
  (let ((args (lispify-maxima-keyword-options options '($jacobian $tolerance))))
    (apply #'nonlinear-solve vars init-x fcns args)))