1 /* Copyright (c) 2007-2014 Massachusetts Institute of Technology
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be
12  * included in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <octave/oct.h>
24 #include <octave/oct-map.h>
25 #include <octave/ov.h>
26 #include <math.h>
27 #include <stdio.h>
28 
29 #include "nlopt.h"
30 #include "nlopt_optimize_usage.h"
31 
struct_val_default(Octave_map & m,const std::string & k,int dflt)32 static int struct_val_default(Octave_map &m, const std::string& k,
33 				 int dflt)
34 {
35   if (m.contains(k)) {
36     if (m.contents(k).length() == 1 && (m.contents(k))(0).is_real_scalar())
37       return (m.contents(k))(0).int_value();
38   }
39   return dflt;
40 }
41 
struct_val_default(Octave_map & m,const std::string & k,double dflt)42 static double struct_val_default(Octave_map &m, const std::string& k,
43 				 double dflt)
44 {
45   if (m.contains(k)) {
46     if (m.contents(k).length() == 1 && (m.contents(k))(0).is_real_scalar())
47       return (m.contents(k))(0).double_value();
48   }
49   return dflt;
50 }
51 
struct_val_default(Octave_map & m,const std::string & k,Matrix & dflt)52 static Matrix struct_val_default(Octave_map &m, const std::string& k,
53 				 Matrix &dflt)
54 {
55   if (m.contains(k)) {
56     if ((m.contents(k)).length() == 1) {
57       if ((m.contents(k))(0).is_real_scalar())
58 	return Matrix(1, dflt.length(), (m.contents(k))(0).double_value());
59       else if ((m.contents(k))(0).is_real_matrix())
60 	return (m.contents(k))(0).matrix_value();
61     }
62   }
63   return dflt;
64 }
65 
66 typedef struct {
67   octave_function *f;
68   int neval, verbose;
69   nlopt_opt opt;
70 } user_function_data;
71 
user_function(unsigned n,const double * x,double * gradient,void * data_)72 static double user_function(unsigned n, const double *x,
73 			    double *gradient, /* NULL if not needed */
74 			    void *data_)
75 {
76   user_function_data *data = (user_function_data *) data_;
77   octave_value_list args(1, 0);
78   Matrix xm(1,n);
79   for (unsigned i = 0; i < n; ++i)
80     xm(i) = x[i];
81   args(0) = xm;
82   octave_value_list res = data->f->do_multi_index_op(gradient ? 2 : 1, args);
83   if (res.length() < (gradient ? 2 : 1))
84     gripe_user_supplied_eval("nlopt_optimize");
85   else if (!res(0).is_real_scalar()
86 	   || (gradient && !res(1).is_real_matrix()
87 	       && !(n == 1 && res(1).is_real_scalar())))
88     gripe_user_returned_invalid("nlopt_optimize");
89   else {
90     if (gradient) {
91       if (n == 1 && res(1).is_real_scalar())
92 	gradient[0] = res(1).double_value();
93       else {
94 	Matrix grad = res(1).matrix_value();
95 	for (unsigned i = 0; i < n; ++i)
96 	  gradient[i] = grad(i);
97       }
98     }
99     data->neval++;
100     if (data->verbose) printf("nlopt_optimize eval #%d: %g\n",
101 			      data->neval, res(0).double_value());
102     double f = res(0).double_value();
103     if (f != f /* isnan(f) */) nlopt_force_stop(data->opt);
104     return f;
105   }
106   return 0;
107 }
108 
user_function1(unsigned n,const double * x,double * gradient,void * data_)109 static double user_function1(unsigned n, const double *x,
110 			    double *gradient, /* NULL if not needed */
111 			    void *data_)
112 {
113   octave_function *f = (octave_function *) data_;
114   octave_value_list args(1, 0);
115   Matrix xm(1,n);
116   for (unsigned i = 0; i < n; ++i)
117     xm(i) = x[i];
118   args(0) = xm;
119   octave_value_list res = f->do_multi_index_op(gradient ? 2 : 1, args);
120   if (res.length() < (gradient ? 2 : 1))
121     gripe_user_supplied_eval("nlopt_optimize");
122   else if (!res(0).is_real_scalar()
123 	   || (gradient && !res(1).is_real_matrix()
124 	       && !(n == 1 && res(1).is_real_scalar())))
125     gripe_user_returned_invalid("nlopt_optimize");
126   else {
127     if (gradient) {
128       if (n == 1 && res(1).is_real_scalar())
129 	gradient[0] = res(1).double_value();
130       else {
131 	Matrix grad = res(1).matrix_value();
132 	for (unsigned i = 0; i < n; ++i)
133 	  gradient[i] = grad(i);
134       }
135     }
136     return res(0).double_value();
137   }
138   return 0;
139 }
140 
141 #define CHECK1(cond, msg) if (!(cond)) { fprintf(stderr, msg "\n\n"); nlopt_destroy(opt); nlopt_destroy(local_opt); return NULL; }
142 
make_opt(Octave_map & opts,int n)143 nlopt_opt make_opt(Octave_map &opts, int n)
144 {
145   nlopt_opt opt = NULL, local_opt = NULL;
146 
147   nlopt_algorithm algorithm =
148     nlopt_algorithm(struct_val_default(opts, "algorithm",
149 				       NLOPT_NUM_ALGORITHMS));
150   CHECK1(((int)algorithm) >= 0 && algorithm < NLOPT_NUM_ALGORITHMS,
151 	"invalid opt.algorithm");
152 
153   opt = nlopt_create(algorithm, n);
154   CHECK1(opt, "nlopt: out of memory");
155 
156   Matrix m_inf(1, n, -HUGE_VAL);
157   Matrix lb = struct_val_default(opts, "lower_bounds", m_inf);
158   CHECK1(n == lb.length(), "wrong length of opt.lower_bounds");
159   CHECK1(nlopt_set_lower_bounds(opt, lb.data()) > 0, "nlopt: out of memory");
160 
161   Matrix p_inf(1, n, +HUGE_VAL);
162   Matrix ub = struct_val_default(opts, "upper_bounds", p_inf);
163   CHECK1(n == ub.length(), "wrong length of opt.upper_bounds");
164   CHECK1(nlopt_set_upper_bounds(opt, ub.data()) > 0, "nlopt: out of memory");
165 
166   nlopt_set_stopval(opt, struct_val_default(opts, "stopval", -HUGE_VAL));
167   nlopt_set_ftol_rel(opt, struct_val_default(opts, "ftol_rel", 0.0));
168   nlopt_set_ftol_abs(opt, struct_val_default(opts, "ftol_abs", 0.0));
169   nlopt_set_xtol_rel(opt, struct_val_default(opts, "xtol_rel", 0.0));
170 
171   {
172     Matrix zeros(1, n, 0.0);
173     Matrix xtol_abs = struct_val_default(opts, "xtol_abs", zeros);
174     CHECK1(n == xtol_abs.length(), "stop.xtol_abs must have same length as x");
175     CHECK1(nlopt_set_xtol_abs(opt, xtol_abs.data())>0, "nlopt: out of memory");
176   }
177 
178   nlopt_set_maxeval(opt, struct_val_default(opts, "maxeval", 0) < 0 ?
179 		    0 : struct_val_default(opts, "maxeval", 0));
180   nlopt_set_maxtime(opt, struct_val_default(opts, "maxtime", 0.0));
181 
182   nlopt_set_population(opt, struct_val_default(opts, "population", 0));
183   nlopt_set_vector_storage(opt, struct_val_default(opts, "vector_storage", 0));
184 
185   if (opts.contains("initial_step")) {
186     Matrix zeros(1, n, 0.0);
187     Matrix initial_step = struct_val_default(opts, "initial_step", zeros);
188     CHECK1(n == initial_step.length(),
189 	  "stop.initial_step must have same length as x");
190     CHECK1(nlopt_set_initial_step(opt, initial_step.data()) > 0,
191 	  "nlopt: out of memory");
192   }
193 
194   if (opts.contains("local_optimizer")) {
195     CHECK1(opts.contents("local_optimizer").length() == 1
196 	  && (opts.contents("local_optimizer"))(0).is_map(),
197 	  "opt.local_optimizer must be a structure");
198     Octave_map local_opts = (opts.contents("local_optimizer"))(0).map_value();
199     CHECK1((local_opt = make_opt(local_opts, n)),
200 	  "error initializing local optimizer");
201     nlopt_set_local_optimizer(opt, local_opt);
202     nlopt_destroy(local_opt); local_opt = NULL;
203   }
204 
205   return opt;
206 }
207 
208 #define CHECK(cond, msg) if (!(cond)) { fprintf(stderr, msg "\n\n"); nlopt_destroy(opt); return retval; }
209 
DEFUN_DLD(nlopt_optimize,args,nargout,NLOPT_OPTIMIZE_USAGE)210 DEFUN_DLD(nlopt_optimize, args, nargout, NLOPT_OPTIMIZE_USAGE)
211 {
212   octave_value_list retval;
213   double A;
214   nlopt_opt opt = NULL;
215 
216   CHECK(args.length() == 2 && nargout <= 3, "wrong number of args");
217 
218   CHECK(args(0).is_map(), "opt must be structure")
219   Octave_map opts = args(0).map_value();
220 
221   CHECK(args(1).is_real_matrix() || args(1).is_real_scalar(),
222 	"x must be real vector");
223   Matrix x = args(1).is_real_scalar() ?
224     Matrix(1, 1, args(1).double_value()) : args(1).matrix_value();
225   int n = x.length();
226 
227   CHECK((opt = make_opt(opts, n)), "error initializing nlopt options");
228 
229   user_function_data d;
230   d.neval = 0;
231   d.verbose = struct_val_default(opts, "verbose", 0);
232   d.opt = opt;
233   if (opts.contains("min_objective")) {
234     CHECK(opts.contents("min_objective").length() == 1
235 	  && (opts.contents("min_objective"))(0).is_function_handle(),
236 	  "opt.min_objective must be a function");
237       d.f = (opts.contents("min_objective"))(0).function_value();
238       nlopt_set_min_objective(opt, user_function, &d);
239   }
240   else if (opts.contains("max_objective")) {
241     CHECK(opts.contents("max_objective").length() == 1
242 	  && (opts.contents("max_objective"))(0).is_function_handle(),
243 	  "opt.max_objective must be a function");
244       d.f = (opts.contents("max_objective"))(0).function_value();
245       nlopt_set_max_objective(opt, user_function, &d);
246   }
247   else {
248     CHECK(0,"either opt.min_objective or opt.max_objective must exist");
249   }
250 
251   if (opts.contains("fc") && opts.contents("fc").length() == 1) {
252     CHECK((opts.contents("fc"))(0).is_cell(), "opt.fc must be cell array");
253     Cell fc = (opts.contents("fc"))(0).cell_value();
254     Matrix zeros(1, fc.length(), 0.0);
255     Matrix fc_tol = struct_val_default(opts, "fc_tol", zeros);
256     CHECK(fc_tol.length() == fc.length(),
257 	  "opt.fc must have same length as opt.fc_tol");
258     for (int i = 0; i < fc.length(); ++i) {
259       CHECK(fc(i).is_function() || fc(i).is_function_handle(),
260 	    "opt.fc must be a cell array of function handles");
261       CHECK(nlopt_add_inequality_constraint(opt, user_function1,
262 					    fc(i).function_value(),
263 					    fc_tol(i)) > 0,
264 	    "nlopt error adding inequality constraint");
265     }
266   }
267 
268   if (opts.contains("h") && opts.contents("h").length() == 1) {
269     CHECK((opts.contents("h"))(0).is_cell(), "opt.h must be cell array");
270     Cell h = (opts.contents("h"))(0).cell_value();
271     Matrix zeros(1, h.length(), 0.0);
272     Matrix h_tol = struct_val_default(opts, "h_tol", zeros);
273     CHECK(h_tol.length() == h.length(),
274 	  "opt.h must have same length as opt.h_tol");
275     for (int i = 0; i < h.length(); ++i) {
276       CHECK(h(i).is_function() || h(i).is_function_handle(),
277 	    "opt.h must be a cell array of function handles");
278       CHECK(nlopt_add_equality_constraint(opt, user_function1,
279 					    h(i).function_value(),
280 					    h_tol(i)) > 0,
281 	    "nlopt error adding equality constraint");
282     }
283   }
284 
285 
286   double opt_f;
287   nlopt_result ret = nlopt_optimize(opt, x.fortran_vec(), &opt_f);
288 
289   retval(0) = x;
290   if (nargout > 1)
291     retval(1) = opt_f;
292   if (nargout > 2)
293     retval(2) = int(ret);
294 
295   nlopt_destroy(opt);
296 
297   return retval;
298 }
299