1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <inttypes.h>
5 #include <math.h>
6 #include <assert.h>
7 #include <nlopt.h>
8 // The "procedural" interface to Reduce/CSL.
9 #include "C_call_CSL.h"
10
11
12 // activate by setting to 1 or 2
13 int DEBUG = 0;
14
15 // The optimization "object":
16 static nlopt_opt opt = NULL;
17 // The result computed by NLOPT_optimize:
18 static double *result;
19 // The local optimization sub-algorithm/object, for algorithms that use it:
20 static nlopt_opt local_opt = NULL;
21
22 // Nlopt return codes, see nlopt_result in nlopt.h. Used only in few
23 // places. nlopt_main.red does error checking at the Reduce level.
nlopt_result_mess(nlopt_result r)24 char *nlopt_result_mess(nlopt_result r) {
25 char *s;
26 switch (r) {
27 case -1: s = "nlopt failure!"; break;
28 case -2: s = "invalid arguments or unsupported constraints (perhaps for this algorithm)!"; break;
29 case -3: s = "out of memory!"; break;
30 case -4: s = "progress limited by roundoff"; break;
31 case -5: s = "forced stop"; break;
32 case 1: s = "nlopt success"; break;
33 case 2: s = "stop value reached"; break;
34 case 3: s= "f-tolerance reached"; break;
35 case 4: s = "x-tolerance reached"; break;
36 case 5: s = "max evaluations reached"; break;
37 case 6: s = "max time reached"; break;
38 };
39 return s;
40 }
41
42 #define ERRORCHECK(who,r) if (r != NLOPT_SUCCESS) \
43 {fprintf(stderr, "*%s: %s\n", who, nlopt_result_mess(r));}
44
45
46 // =======================================================================
47 // Basics
48 // =======================================================================
49
NLOPT_create(int32_t anum,int32_t n)50 void NLOPT_create(int32_t anum, int32_t n) {
51 opt = nlopt_create(anum, n);
52 if (opt == NULL)
53 fprintf(stderr, "Couldn't create '%s'!\n", nlopt_algorithm_name(anum));
54 // nlopt_algorithm_name is built-in to the NLopt library
55 }
56
NLOPT_destroy()57 void NLOPT_destroy() {
58 nlopt_destroy(opt);
59 }
60
NLOPT_get_algorithm()61 int32_t NLOPT_get_algorithm() {
62 // The only reason for this 'NLOPT_' is to convert from nlopt_algorithm to 'int32_t'!
63 // Isn't there a better way?
64 int32_t a = nlopt_get_algorithm(opt);
65 return a;
66 }
67
NLOPT_get_dimension()68 int32_t NLOPT_get_dimension() {
69 // The only reason for this 'NLOPT_' is to convert from "unsigned" to 'int32_t'!
70 int32_t d = nlopt_get_dimension(opt);
71 return d;
72 }
73
74 // =======================================================================
75 // Setting upper and lower bounds (hyper-rectangle)
76 // =======================================================================
77
NLOPT_set_lower_bounds(const double * lb)78 int32_t NLOPT_set_lower_bounds(const double *lb) {
79 int32_t r = nlopt_set_lower_bounds(opt, lb);
80 return r;
81 }
NLOPT_set_upper_bounds(const double * ub)82 int32_t NLOPT_set_upper_bounds(const double *ub) {
83 int32_t r = nlopt_set_upper_bounds(opt, ub);
84 return r;
85 }
86
87 // acts like a Icon generator
NLOPT_get_lower_bound(int32_t i,int32_t n)88 double NLOPT_get_lower_bound(int32_t i, int32_t n) {
89 static double *slb = NULL;
90 if (i == 0) {
91 // Contortion 1:
92 slb = (double *) malloc(n*sizeof(double));
93 int32_t r = nlopt_get_lower_bounds(opt, slb);
94 ERRORCHECK("NLOPT_get_lower_bound",r);
95 }
96 // Contortion 2:
97 double lbi = slb[i];
98 if (i == n-1) free(slb);
99 return lbi;
100 }
101
102 // acts like a Icon generator
NLOPT_get_upper_bound(int32_t i,int32_t n)103 double NLOPT_get_upper_bound(int32_t i, int32_t n) {
104 static double *sub = NULL;
105 if (i == 0) {
106 // Contortion 1:
107 sub = (double *) malloc(n*sizeof(double));
108 int32_t r = nlopt_get_upper_bounds(opt, sub);
109 ERRORCHECK("NLOPT_get_upper_bound",r);
110 }
111 // Contortion 2:
112 double ubi = sub[i];
113 if (i == n-1) free(sub);
114 return ubi;
115 }
116
117 // =======================================================================
118 // Stopping criteria
119 // =======================================================================
120
NLOPT_set_xtol_rel(double t)121 int32_t NLOPT_set_xtol_rel(double t) {
122 return nlopt_set_xtol_rel(opt, t);
123 }
NLOPT_set_ftol_rel(double t)124 int32_t NLOPT_set_ftol_rel(double t) {
125 return nlopt_set_ftol_rel(opt, t);
126 }
NLOPT_set_ftol_abs(double t)127 int32_t NLOPT_set_ftol_abs(double t) {
128 return nlopt_set_ftol_abs(opt, t);
129 }
NLOPT_set_maxeval(int32_t m)130 int32_t NLOPT_set_maxeval(int32_t m) {
131 return nlopt_set_maxeval(opt, m);
132 }
NLOPT_set_maxtime(double m)133 int32_t NLOPT_set_maxtime(double m) {
134 return nlopt_set_maxtime(opt, m);
135 }
NLOPT_set_stopval(double v)136 int32_t NLOPT_set_stopval(double v) {
137 return nlopt_set_stopval(opt, v);
138 }
139
NLOPT_get_xtol_rel()140 double NLOPT_get_xtol_rel() {
141 return nlopt_get_xtol_rel(opt);
142 }
NLOPT_get_ftol_rel()143 double NLOPT_get_ftol_rel() {
144 return nlopt_get_ftol_rel(opt);
145 }
NLOPT_get_ftol_abs()146 double NLOPT_get_ftol_abs() {
147 return nlopt_get_ftol_abs(opt);
148 }
NLOPT_get_maxeval()149 int32_t NLOPT_get_maxeval() {
150 return nlopt_get_maxeval(opt);
151 }
NLOPT_get_maxtime()152 double NLOPT_get_maxtime() {
153 return nlopt_get_maxtime(opt);
154 }
155
156
157 // ===========================================================================
158 // Functions *internal* to the interface for dealing with arrays of doubles
159 // To understand what's going on see the comments in nlopt_set_lower_bounds()
160 // in nlopt_main.red.
161 // ===========================================================================
newDoubleArray(int32_t len)162 double *newDoubleArray(int32_t len) {
163 double *a;
164 a = (double *) malloc(len*sizeof(double));
165 return a;
166 }
setDoubleArray(double a[],int32_t i,double x)167 void setDoubleArray(double a[], int32_t i, double x) {
168 a[i] = x;
169 }
freeDoubleArray(double a[])170 void freeDoubleArray(double a[]) {
171 free(a);
172 }
173 // This internal function turns a C array of doubles into a Lisp list and
174 // leaves it at the top of the stack. The "stack" is used by the "procedural"
175 // interface to Reduce, see file "C_call_CSL.h".
C_darray_to_Lisp_list(int32_t n,const double x[])176 void C_darray_to_Lisp_list(int32_t n, const double x[]) {
177 // Push the x[i] on the stack.
178 for (int i = 0; i < n; i++) PROC_push_floating(x[i]);
179 // Make a list out of the x[i]; it is left at the top of the stack.
180 PROC_make_function_call("list", n);
181 // Now having put 'list on the front, get rid of it.
182 // (This perhaps shows that that ACN should really extend the procedural
183 // interface to make this slightly nicer. But with luck this will suffice
184 // for now.)
185 PROC_lisp_eval();
186 }
187
188
189 // ================================================================
190 // Setting the optimization objective
191 // ================================================================
192 // This is the most complex and difficult part of the interface so far.
193
194
195 // This function is internal to the interface (static), and is of type nlopt_func.
196 // It is modelled after f77_func_wrap in api/f77api.c. It is used as a wrapper
197 // for scalar functions, which can represent either the optimization objective,
198 // or a general equality or inequality constraint.
199 // The PROC_ code is borrowed from cuba_integrand in redcuba.c of the 'cuba' package.
red_func_wrap(unsigned n,const double * x,double * grad,void * f_data)200 static double red_func_wrap(unsigned n, const double *x, double *grad, void *f_data) {
201 double z;
202 char *fn = (char *) f_data;
203 if (DEBUG > 1) {
204 fprintf(stderr, "red_func_wrap: %s", fn);
205 if (grad) fprintf(stderr, " [G]");
206 fprintf(stderr, "\n");
207 }
208 // Convert the function name (string) to a symbol, push it on the stack, and
209 // wrap it in a quote:
210 PROC_push_symbol(fn);
211 PROC_make_function_call("quote", 1);
212 // Turn the array 'x' into a Lisp list and leave it at the top of the "stack".
213 C_darray_to_Lisp_list(n,x);
214 // Wrap it in a quote:
215 PROC_make_function_call("quote", 1);
216 // Evaluate a call to the Reduce algebraic function 'fn' that takes an n-list
217 // of reals 'x', and delivers either a single real, f(x), or, if 'grad' is not
218 // NULL, the flattened (n+1)-list of reals {f(x),grad(x)}.
219 int32_t r = PROC_make_function_call("call_alg_fn_list2list", 2);
220 PROC_lisp_eval();
221 if (DEBUG > 1) {
222 fprintf(stderr, " %s(", fn);
223 for (int i=0; i<n; i++) {
224 fprintf(stderr, "%.4g", x[i]);
225 if (i < n-1) fprintf(stderr, ",");
226 }
227 };
228 if (grad) {
229 // If 'grad' is not NULL, the Reduce function 'fn' is expected to return a
230 // flattened list {f(x),grad(x)}, where grad(x) is an n-list which should
231 // be assigned to 'grad'. (Also see the function red_mfunc_wrap.)
232 PROC_handle y = PROC_get_value();
233 z = PROC_floating_value(PROC_first(y));
234 if (DEBUG > 1) {
235 fprintf(stderr, ") = %.5g\n", z);
236 fprintf(stderr, " grad = {");
237 };
238 y = PROC_rest(y);
239 for (int i = 0; i < n; i++) {
240 grad[i] = PROC_floating_value(PROC_first(y));
241 y = PROC_rest(y);
242 if (DEBUG > 1) {
243 fprintf(stderr, "%.4g", grad[i]);
244 if (i < n-1) fprintf(stderr, ",");
245 }
246 }
247 if (DEBUG > 1) fprintf(stderr, "}\n");
248 }
249 else { // grad is NULL, result is just f(x)
250 z = PROC_floating_value(PROC_get_value());
251 if (DEBUG > 1) fprintf(stderr, ") = %.5g\n", z);
252 }
253 return z;
254 }
255
256
257
258 // Modelled after F77_nlo_set_min_objective in api/f77funcs_.h.
NLOPT_set_min_objective(char * fname)259 int32_t NLOPT_set_min_objective(char *fname) {
260 // In the call to nlopt_set_min_objective:
261 // 1. The wrapper red_func_wrap is the nlopt_func argument,
262 // 2. The name of the Reduce function defining the objective is the *f_data argument,
263 // 3. We can't just pass fname to to this argument, we need the string rfun on the heap!
264 char *rfun = malloc(strlen(fname)*sizeof(char)+1);
265 strcpy(rfun,fname);
266 return nlopt_set_min_objective(opt, red_func_wrap, rfun);
267 }
268
NLOPT_set_max_objective(char * fname)269 int32_t NLOPT_set_max_objective(char *fname) {
270 char *rfun = malloc(strlen(fname)*sizeof(char)+1);
271 strcpy(rfun,fname);
272 return nlopt_set_max_objective(opt, red_func_wrap, rfun);
273 }
274
NLOPT_optimize(double * x0,int32_t n)275 int32_t NLOPT_optimize(double *x0, int32_t n) {
276 double *x, f;
277 x = (double *) malloc(n*sizeof(double));
278 for (int j=0; j<n; j++) x[j] = x0[j];
279 if (DEBUG) fprintf(stderr, "NLOPT_optimize:\n");
280 int32_t r = nlopt_optimize(opt, x, &f);
281 // assign results to the static 'result' array:
282 result = (double *) malloc((n+1)*sizeof(double));
283 result[0] = f;
284 for (int j=0; j<n; j++) result[j+1] = x[j];
285 free(x);
286 if (DEBUG) {
287 fprintf(stderr, " f = %f\n", result[0]);
288 fprintf(stderr, " result =");
289 for (int j=0; j<n; j++)
290 fprintf(stderr, " %.5g", result[j+1]);
291 fprintf(stderr, "\n");
292 }
293 return r;
294 }
295
get_result(int32_t i)296 double get_result(int32_t i) {
297 return result[i];
298 }
299
free_result()300 void free_result() {
301 free(result);
302 }
303
304
305 // ================================================================
306 // Setting the local optimization sub-algorithm
307 // ================================================================
308
NLOPT_set_local_optimizer(int32_t anum)309 int32_t NLOPT_set_local_optimizer(int32_t anum) {
310 local_opt = nlopt_create(anum, nlopt_get_dimension(opt));
311 return nlopt_set_local_optimizer(opt, local_opt);
312 }
313
314
315 // =============================================================================
316 // Defining scalar constraints
317 // =============================================================================
318
319 // Note: in the next two functions
320 // 1. The wrapper function red_func_wrap is the nlopt_func argument,
321 // 2. The name of the Reduce function defining the constraint is the *f_data argument,
322 // 3. We can't just pass c_fname to to this argument, we need the string cfun on the heap!
323
NLOPT_add_eq_constraint(char * c_fname,double tol)324 int32_t NLOPT_add_eq_constraint(char *c_fname, double tol) {
325 char *cfun = malloc(strlen(c_fname)*sizeof(char)+1);
326 assert(cfun != NULL);
327 strcpy(cfun,c_fname);
328 if (DEBUG) fprintf(stderr, "added '%s' equality constraint, %.3g\n", cfun, tol);
329 return nlopt_add_equality_constraint(opt, red_func_wrap, cfun, tol);
330 }
NLOPT_add_ineq_constraint(char * c_fname,double tol)331 int32_t NLOPT_add_ineq_constraint(char *c_fname, double tol) {
332 char *cfun = malloc(strlen(c_fname)*sizeof(char)+1);
333 assert(cfun != NULL);
334 strcpy(cfun,c_fname);
335 if (DEBUG) fprintf(stderr, "added '%s' inequality constraint, %.3g\n", cfun, tol);
336 return nlopt_add_inequality_constraint(opt, red_func_wrap, cfun, tol);
337 }
338
339 // =============================================================================
340 // Defining vector-valued constraints
341 // =============================================================================
342
343 // This function is internal to the interface (static), and is of type nlopt_mfunc.
344 // It is modelled after f77_mfunc_wrap in api/f77api.c. It is used as a wrapper for
345 // functions that, given an n-vector 'x', return an m-vector in 'result'.
red_mfunc_wrap(unsigned m,double * result,unsigned n,const double * x,double * grad,void * f_data)346 static void red_mfunc_wrap(unsigned m, double *result, unsigned n, const double *x,
347 double *grad, void *f_data) {
348 char *fn = (char *) f_data;
349 if (DEBUG > 1) {
350 fprintf(stderr, "red_mfunc_wrap: %s", fn);
351 if (grad) fprintf(stderr, " [G]");
352 fprintf(stderr, "\n");
353 }
354 // Convert the function name (string) to a symbol, push it on the stack, and
355 // wrap it in a quote:
356 PROC_push_symbol(fn);
357 PROC_make_function_call("quote", 1);
358 // Turn the array x into a Lisp list and leave it at the top of the "stack".
359 C_darray_to_Lisp_list(n,x);
360 // Wrap it in a quote:
361 PROC_make_function_call("quote", 1);
362 // Evaluate a call to the Reduce m-dimensional algebraic function fn: R^n -> R^m
363 int32_t r = PROC_make_function_call("call_alg_fn_list2list", 2);
364 PROC_lisp_eval();
365 if (DEBUG > 1) {
366 fprintf(stderr, " %s(", fn);
367 for (int i=0; i<n; i++) {
368 fprintf(stderr, "%.4g", x[i]);
369 if (i < n-1) fprintf(stderr, ",");
370 }
371 fprintf(stderr, ") = {");
372 }
373 // If 'grad' is NULL, the result is a an m-list, which we assign to the C
374 // array 'result' element-by-element. If 'grad' is non-NULL, then it points
375 // to an array of length m*n, and the result of 'fn' also contains a list of
376 // length m*n, the gradients of the constraint functions with respect to x,
377 // and 'grad' should be set to this list. It is assumed that ∂c_i/∂x_j is
378 // stored in grad[i*n + j].
379 // Irrespective of 'grad', we first get the first m elements of the list and
380 // assign them to 'result'.
381 PROC_handle y = PROC_get_value();
382 for (int i = 0; i < m; i++) {
383 result[i] = PROC_floating_value(PROC_first(y));
384 y = PROC_rest(y);
385 if (DEBUG > 1) {
386 fprintf(stderr, "%.4g", result[i]);
387 if (i < m-1) fprintf(stderr, ",");
388 }
389 }
390 if (DEBUG > 1) fprintf(stderr, "}\n");
391 if (grad) {
392 // Now handle the last m*n elements of what 'fn' has returned
393 if (DEBUG > 1) fprintf(stderr, " grad = {");
394 for (int i = 0; i < m*n; i++) {
395 grad[i] = PROC_floating_value(PROC_first(y));
396 y = PROC_rest(y);
397 if (DEBUG > 1) {
398 fprintf(stderr, "%.4g", grad[i]);
399 if (i < m*n-1) fprintf(stderr, ",");
400 }
401 }
402 if (DEBUG > 1) fprintf(stderr, "}\n");
403 }
404 }
405
406
407
408 // Note: look at the notes for the functions for scalar constraints
NLOPT_add_eq_mconstraint(char * c_fname,int32_t m,double * tol)409 int32_t NLOPT_add_eq_mconstraint(char *c_fname, int32_t m, double *tol) {
410 char *cfun = malloc(strlen(c_fname)*sizeof(char)+1);
411 assert(cfun != NULL);
412 strcpy(cfun,c_fname);
413 if (DEBUG) fprintf(stderr, "added '%s' equality m-constraint\n", cfun);
414 return nlopt_add_equality_mconstraint(opt, m, red_mfunc_wrap, cfun, tol);
415 }
NLOPT_add_ineq_mconstraint(char * c_fname,int32_t m,double * tol)416 int32_t NLOPT_add_ineq_mconstraint(char *c_fname, int32_t m, double *tol) {
417 char *cfun = malloc(strlen(c_fname)*sizeof(char)+1);
418 assert(cfun != NULL);
419 strcpy(cfun,c_fname);
420 if (DEBUG) fprintf(stderr, "added '%s' inequality m-constraint\n", cfun);
421 return nlopt_add_inequality_mconstraint(opt, m, red_mfunc_wrap, cfun, tol);
422 }
423
424
425 // =============================================================================
426 // Removing constraints
427 // =============================================================================
428
NLOPT_remove_eq_constraints()429 int32_t NLOPT_remove_eq_constraints() {
430 return nlopt_remove_equality_constraints(opt);
431 }
NLOPT_remove_ineq_constraints()432 int32_t NLOPT_remove_ineq_constraints() {
433 return nlopt_remove_inequality_constraints(opt);
434 }
435
436 // =============================================================================
437 // Step size for algorithms without derivatives
438 // =============================================================================
439
NLOPT_set_initial_step(const double * dx)440 int32_t NLOPT_set_initial_step(const double *dx) {
441 return nlopt_set_initial_step(opt, dx);
442 }
NLOPT_set_initial_step1(double dx)443 int32_t NLOPT_set_initial_step1(double dx) {
444 return nlopt_set_initial_step1(opt, dx);
445 }
446
447 // ============================================================================
448 // Stochastic search algorithms
449 // ============================================================================
450
NLOPT_set_population(int p)451 int32_t NLOPT_set_population(int p) {
452 return nlopt_set_population(opt, (unsigned) p);
453 }
454
NLOPT_srand(int64_t seed)455 void NLOPT_srand(int64_t seed) {
456 nlopt_srand((unsigned long) seed);
457 }
458