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