3 ;;; Simple Maxima interface to COBYLA, Constrained Optimization BY
4 ;;; Linear Approximations.
8 (defvar *debug-cobyla
* nil
9 "Non-Nil to enable some debugging prints in the coblya interface")
11 ;; Variable that will hold the function that calculates the function
12 ;; value and the constraints.
15 ;; COBYLA always calls CALCFC to compute the function value and the
16 ;; constraint equations. But we want to be able to specify different
17 ;; versions. So, COBYLA calls CALCFC, which then calls *CALCFC* to
18 ;; do the real computation.
19 (defun cobyla::calcfc
(n m x f con
)
21 (funcall maxima
::*calcfc
* n m x con
))
25 &key constraints
(rhobeg 1d0
) (rhoend 1d-6
) ( iprint
0) (maxfun 1000))
26 "COBYLA (Constrained Optimization BY Linear Approximation).
28 This is a maxima interface to the routine COBYLA. Interface
29 Copyright Raymond Toy 2010 Interface released under the terms of the
30 GNU General Public License.
32 The COBYLA Fortran routines are included with permission from the
33 author Michael J. D. Powell, 2010/10/08. The code was obtained from
34 http://plato.asu.edu/sub/nlores.html#general.
36 fmin_cobyla(f, vars, init_x, [args])
38 f Real function to be minimized
39 vars List of variables over which to minimize
40 init_x Initial guess. Do NOT set to all zeroes.
42 The optional arguments are all keyword arguments of the form key =
45 constraints List of expressions for the constraints on the variables.
46 Each expression is assumed to be greater than or equal
47 to zero. Constraints must be of the form f >= g, f <=
49 rhobeg Initial value of the internal RHO variable which controls
50 the size of simplex. (Defaults to 1.)
51 rhoend The desired final value rho parameter. It is approximately
52 the accuracy in the variables. (Defaults to 1d-6.)
53 iprint Verbose output level. (Defaults to 0.)
55 1 - Summary at the end of the calculation
56 2 - Each new value of RHO and SIGMA is printed, including
57 the vector of variables, some function information when
59 3 - Like 2, but information is printed when F(X) is computed.
60 maxfun The maximum number of function evaluations. (Defaults to
63 The parameter SIGMA is an internal penalty parameter, it being
64 assumed that a change to X is an improvement if it reduces the merit
67 F(X)+SIGMA*MAX(0.0,-C1(X),-C2(X),...,-CM(X))
69 where C1, C2 are the constraint functions. When printed the
70 parameter MAXCV is the MAX term above, which stands for MAXimum
73 On return, a vector of four elements is given:
75 1: The value of the variables giving the minimum
76 2: The minimized function value
77 3: The number of function evaluations.
80 1 - maxfun limit reached
82 -1 - maxcv exceeds rhoend probably indicating constraints not
85 You can find some examples in share/cobyla/ex.
87 An example of minimizing x1*x2 with 1-x1^2-x2^2 >= 0:
90 fmin_cobyla(x1*x2, [x1, x2], [1,1], constraints = [1-x1^2-x2^2 >= 0], iprint=1);
92 => [[x1 = 0.707107555323284, x2 = - 0.7071060070503778],
93 - 0.4999999999998015, 64, 0]
95 The theoretical solution is x1 = 1/sqrt(2), x2 = -1/sqrt(2).
98 (merror "~M: vars must be a list of variables. Got: ~M"
100 (unless (listp init-x
)
101 (merror "~M: Initial values must be a list of values. Got: ~M"
102 %%pretty-fname init-x
))
104 (unless (= (length (cdr vars
))
105 (length (cdr init-x
)))
107 "~M: Number of initial values (~M) does not match the number of variables ~M~%"
109 (length (cdr init-x
))
110 (length (cdr vars
))))
112 (unless (and (integerp iprint
)
115 "~M: iprint must be an integer between 0 and 3, inclusive, not: ~M~%"
116 %%pretty-fname iprint
))
118 (unless (and (integerp maxfun
) (plusp maxfun
))
120 "~M: maxfun must be a positive integer, not: ~M~%"
121 %%pretty-fname maxfun
))
123 ;; Go through constraints and convert f >= g to f - g, f <= g to g -
124 ;; f, and f = g to f - g and g - f. This is because cobyla expects
125 ;; all constraints to of the form h>=0.
126 (let (normalized-constraints)
127 (dolist (c (cdr constraints
))
129 (cond ((string-equal op
">=")
130 (push (sub ($lhs c
) ($rhs c
)) normalized-constraints
))
131 ((string-equal op
"<=")
132 (push (sub ($rhs c
) ($lhs c
)) normalized-constraints
))
133 ((string-equal op
"=")
134 (push (sub ($lhs c
) ($rhs c
)) normalized-constraints
)
135 (push (sub ($rhs c
) ($lhs c
)) normalized-constraints
))
137 (merror "~M: Constraint equation must be =, <= or >=: got ~M"
138 %%pretty-fname op
)))))
140 (setf normalized-constraints
142 (nreverse normalized-constraints
)))
144 (mformat t
"cons = ~M~%" normalized-constraints
))
146 (let* ((n (length (cdr vars
)))
147 (m (length (cdr normalized-constraints
)))
149 :element-type
'double-float
151 (mapcar #'(lambda (z)
152 (let ((r ($float z
)))
155 (merror "~M: Does not evaluate to a float: ~M"
158 ;; Real work array for cobyla.
159 (w (make-array (+ (* n
(+ (* 3 n
)
164 :element-type
'double-float
))
165 ;; Integer work array for cobyla.
166 (iact (make-array (+ m
1) :element-type
'f2cl-lib
::integer4
))
167 (fv (coerce-float-fun f vars
))
168 (cv (coerce-float-fun normalized-constraints vars
))
170 #'(lambda (nn mm xval cval
)
171 ;; Compute the function and the constraints at
172 ;; the given xval. The function value is
173 ;; returned is returned, and the constraint
174 ;; values are stored in cval.
175 (declare (fixnum nn mm
)
176 (type (cl:array cl
:double-float
(*)) xval cval
))
177 (let* ((x-list (coerce xval
'list
))
178 (f (apply fv x-list
))
179 (c (apply cv x-list
)))
180 ;; Do we really need these checks?
182 (merror "~M: The objective function did not evaluate to a number at ~M"
183 %%pretty-fname
(list* '(mlist) x-list
)))
184 (unless (every #'floatp
(cdr c
))
186 (loop for cval in
(cdr c
)
190 ;; List the constraints that did not
191 ;; evaluate to a number to make it easier
192 ;; for the user to figure out which
193 ;; constraints were bad.
194 (mformat t
"~M: At the point ~M:~%"
195 %%pretty-fname
(list* '(mlist) x-list
))
197 (with-output-to-string (msg)
198 (loop for index in bad-cons
201 "Constraint ~M: ~M did not evaluate to a number.~%"
203 (elt normalized-constraints index
)))))))
204 (replace cval c
:start2
1)
205 ;; This is the f2cl calling convention for
206 ;; CALCFC. For some reason, f2cl thinks n
207 ;; and m are modified, so they must be
209 (values nn mm nil f nil
)))))
210 (multiple-value-bind (null-0 null-1 null-2 null-3 null-4 null-5
211 neval null-6 null-7 ierr
)
212 (cobyla:cobyla n m x rhobeg rhoend iprint maxfun w iact
0)
213 (declare (ignore null-0 null-1 null-2 null-3 null-4 null-5 null-6 null-7
))
214 ;; Should we put a check here if the number of function
215 ;; evaluations equals maxfun? When iprint is not 0, the output
216 ;; from COBYLA makes it clear that something bad happened.
217 (let* ((x-list (coerce x
'list
))
218 (soln (list* '(mlist)
219 (mapcar #'(lambda (var val
)
220 `((mequal) ,var
,val
))
223 ;; The maxcv value. See cobyla.f MPP value. MPP = M +
224 ;; 2, adjusted for 0-indexing lisp arrays.
225 (max-cv (aref w
(+ m
1))))
226 ;; Return a list off the following items:
227 ;; 1: The point that gives the optimum in the form var=val
228 ;; 2: The corresponding value of the function
229 ;; 3: Number of function evaluations
232 ;; 1 - maxfun limit reached
233 ;; 2 - rounding issues
234 ;; -1 - maxcv exceeds rhoend probably indicating
235 ;; constraints not satisfied.
240 (if (> max-cv rhoend
) -
1 ierr
)))))))