From 29059a4337cb85af66690d44827ded3e1dbb3b32 Mon Sep 17 00:00:00 2001 From: stevenj Date: Thu, 30 Aug 2007 17:09:14 -0400 Subject: [PATCH] added octave plug-in darcs-hash:20070830210914-c8de0-2ff67f1fb7eb4ac60968712d7b4259742593c445.gz --- Makefile.am | 2 +- configure.ac | 39 ++++++ octave/Makefile.am | 27 +++++ octave/NLOPT_GLOBAL_DIRECT.m | 2 + octave/NLOPT_GLOBAL_DIRECT_L.m | 2 + octave/NLOPT_GLOBAL_DIRECT_L_RANDOMIZED.m | 2 + octave/NLOPT_GLOBAL_ORIG_DIRECT.m | 2 + octave/NLOPT_GLOBAL_ORIG_DIRECT_L.m | 2 + octave/NLOPT_GLOBAL_STOGO.m | 2 + octave/NLOPT_GLOBAL_STOGO_RANDOMIZED.m | 2 + octave/NLOPT_LOCAL_LBFGS.m | 2 + octave/NLOPT_LOCAL_SUBPLEX.m | 2 + octave/nlopt_minimize.cc | 139 ++++++++++++++++++++++ octave/nlopt_minimize.m | 61 ++++++++++ 14 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 octave/Makefile.am create mode 100644 octave/NLOPT_GLOBAL_DIRECT.m create mode 100644 octave/NLOPT_GLOBAL_DIRECT_L.m create mode 100644 octave/NLOPT_GLOBAL_DIRECT_L_RANDOMIZED.m create mode 100644 octave/NLOPT_GLOBAL_ORIG_DIRECT.m create mode 100644 octave/NLOPT_GLOBAL_ORIG_DIRECT_L.m create mode 100644 octave/NLOPT_GLOBAL_STOGO.m create mode 100644 octave/NLOPT_GLOBAL_STOGO_RANDOMIZED.m create mode 100644 octave/NLOPT_LOCAL_LBFGS.m create mode 100644 octave/NLOPT_LOCAL_SUBPLEX.m create mode 100644 octave/nlopt_minimize.cc create mode 100644 octave/nlopt_minimize.m diff --git a/Makefile.am b/Makefile.am index cb31dc3..7e8c741 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,7 @@ lib_LTLIBRARIES = libnlopt.la ACLOCAL_AMFLAGS=-I ./m4 -SUBDIRS= util lbfgs subplex direct cdirect stogo api . test +SUBDIRS= util lbfgs subplex direct cdirect stogo api octave . test EXTRA_DIST=COPYRIGHT autogen.sh nlopt.pc.in m4 libnlopt_la_SOURCES = diff --git a/configure.ac b/configure.ac index 46ca6b6..7503eda 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,44 @@ if test "$ok" = "yes"; then fi AC_MSG_RESULT(${ok}) +dnl ----------------------------------------------------------------------- +dnl Compiling Octave plug-in + +AC_ARG_VAR(OCT_INSTALL_DIR, [where to install GNU Octave .oct plug-ins]) +AC_ARG_VAR(M_INSTALL_DIR, [where to install GNU Octave .m plug-ins]) +AC_ARG_VAR(MKOCTFILE, [name of mkoctfile program to compile Octave plug-ins]) + +AC_CHECK_PROGS(MKOCTFILE, mkoctfile, echo) +if test "$MKOCTFILE" = "echo"; then + AC_MSG_WARN([can't find mkoctfile: won't be able to compile GNU Octave plugin]) +elif test x"$OCT_INSTALL_DIR" = "x"; then + # try to find installation directory + AC_CHECK_PROGS(OCTAVE, octave, echo) + AC_MSG_CHECKING(for Octave loadpath) + OCTAVE_LOADPATH=`echo "DEFAULT_LOADPATH" | $OCTAVE -q | cut -d'=' -f2` + AC_MSG_RESULT($OCTAVE_LOADPATH) + AC_MSG_CHECKING(where Octave plugins go) + OCT_INSTALL_DIR=`echo "$OCTAVE_LOADPATH" | tr ':' '\n' | grep "site/oct" | head -1` + if test -n "$OCT_INSTALL_DIR"; then + AC_MSG_RESULT($OCT_INSTALL_DIR) + else + AC_MSG_RESULT(unknown) + AC_MSG_WARN([can't find where to install GNU Octave plugins]) + fi + AC_MSG_CHECKING(where Octave scripts go) + M_INSTALL_DIR=`echo "$OCTAVE_LOADPATH" | tr ':' '\n' | grep "site/m" | head -1` + if test -n "$M_INSTALL_DIR"; then + AC_MSG_RESULT($M_INSTALL_DIR) + else + AC_MSG_RESULT(unknown) + AC_MSG_WARN([can't find where to install GNU Octave scripts]) + fi +fi +AM_CONDITIONAL(WITH_OCTAVE, test x"$OCT_INSTALL_DIR" != "x") +AC_SUBST(OCT_INSTALL_DIR) +AC_SUBST(M_INSTALL_DIR) +AC_SUBST(MKOCTFILE) + dnl ----------------------------------------------------------------------- dnl Debugging @@ -95,6 +133,7 @@ AC_CONFIG_FILES([ nlopt.pc api/Makefile util/Makefile + octave/Makefile direct/Makefile cdirect/Makefile stogo/Makefile diff --git a/octave/Makefile.am b/octave/Makefile.am new file mode 100644 index 0000000..3528aa3 --- /dev/null +++ b/octave/Makefile.am @@ -0,0 +1,27 @@ +AM_CPPFLAGS = -I$(top_srcdir)/api + +MFILES = NLOPT_GLOBAL_DIRECT_L.m NLOPT_GLOBAL_DIRECT_L_RANDOMIZED.m \ +NLOPT_GLOBAL_DIRECT.m NLOPT_GLOBAL_ORIG_DIRECT_L.m \ +NLOPT_GLOBAL_ORIG_DIRECT.m NLOPT_GLOBAL_STOGO.m \ +NLOPT_GLOBAL_STOGO_RANDOMIZED.m NLOPT_LOCAL_LBFGS.m \ +NLOPT_LOCAL_SUBPLEX.m nlopt_minimize.m + +octdir = $(OCT_INSTALL_DIR) +mdir = $(M_INSTALL_DIR) + +if WITH_OCTAVE +oct_DATA = nlopt_minimize.oct +m_DATA = $(MFILES) +endif + +nlopt_minimize.oct: nlopt_minimize.cc nlopt_minimize_usage.h + $(MKOCTFILE) $(DEFS) $(CPPFLAGS) $(srcdir)/nlopt_minimize.cc $(LDFLAGS) -L$(top_builddir)/.libs -lnlopt + +nlopt_minimize_usage.h: $(srcdir)/nlopt_minimize.m + echo "#define NLOPT_MINIMIZE_USAGE \\" > $@ + sed 's/\"/\\"/g' $(srcdir)/nlopt_minimize.m | sed 's,^% ,\",;s,^%,\",;s,$$,\\n\" \\,' >> $@ + echo "" >> $@ + +EXTRA_DIST = nlopt_minimize.cc $(MFILES) + +CLEANFILES = nlopt_minimize.oct nlopt_minimize_usage.h diff --git a/octave/NLOPT_GLOBAL_DIRECT.m b/octave/NLOPT_GLOBAL_DIRECT.m new file mode 100644 index 0000000..c0623d3 --- /dev/null +++ b/octave/NLOPT_GLOBAL_DIRECT.m @@ -0,0 +1,2 @@ +function a = NLOPT_GLOBAL_DIRECT + a = 0; diff --git a/octave/NLOPT_GLOBAL_DIRECT_L.m b/octave/NLOPT_GLOBAL_DIRECT_L.m new file mode 100644 index 0000000..ab749d2 --- /dev/null +++ b/octave/NLOPT_GLOBAL_DIRECT_L.m @@ -0,0 +1,2 @@ +function a = NLOPT_GLOBAL_DIRECT_L + a = 1; diff --git a/octave/NLOPT_GLOBAL_DIRECT_L_RANDOMIZED.m b/octave/NLOPT_GLOBAL_DIRECT_L_RANDOMIZED.m new file mode 100644 index 0000000..6dc8d38 --- /dev/null +++ b/octave/NLOPT_GLOBAL_DIRECT_L_RANDOMIZED.m @@ -0,0 +1,2 @@ +function a = NLOPT_GLOBAL_DIRECT_L_RANDOMIZED + a = 2; diff --git a/octave/NLOPT_GLOBAL_ORIG_DIRECT.m b/octave/NLOPT_GLOBAL_ORIG_DIRECT.m new file mode 100644 index 0000000..9b749a5 --- /dev/null +++ b/octave/NLOPT_GLOBAL_ORIG_DIRECT.m @@ -0,0 +1,2 @@ +function a = NLOPT_GLOBAL_ORIG_DIRECT + a = 3; diff --git a/octave/NLOPT_GLOBAL_ORIG_DIRECT_L.m b/octave/NLOPT_GLOBAL_ORIG_DIRECT_L.m new file mode 100644 index 0000000..f133cf3 --- /dev/null +++ b/octave/NLOPT_GLOBAL_ORIG_DIRECT_L.m @@ -0,0 +1,2 @@ +function a = NLOPT_GLOBAL_ORIG_DIRECT_L + a = 4; diff --git a/octave/NLOPT_GLOBAL_STOGO.m b/octave/NLOPT_GLOBAL_STOGO.m new file mode 100644 index 0000000..3929ff0 --- /dev/null +++ b/octave/NLOPT_GLOBAL_STOGO.m @@ -0,0 +1,2 @@ +function a = NLOPT_GLOBAL_STOGO + a = 6; diff --git a/octave/NLOPT_GLOBAL_STOGO_RANDOMIZED.m b/octave/NLOPT_GLOBAL_STOGO_RANDOMIZED.m new file mode 100644 index 0000000..06e6be6 --- /dev/null +++ b/octave/NLOPT_GLOBAL_STOGO_RANDOMIZED.m @@ -0,0 +1,2 @@ +function a = NLOPT_GLOBAL_STOGO_RANDOMIZED + a = 7; diff --git a/octave/NLOPT_LOCAL_LBFGS.m b/octave/NLOPT_LOCAL_LBFGS.m new file mode 100644 index 0000000..c50e72f --- /dev/null +++ b/octave/NLOPT_LOCAL_LBFGS.m @@ -0,0 +1,2 @@ +function a = NLOPT_LOCAL_LBFGS + a = 8; diff --git a/octave/NLOPT_LOCAL_SUBPLEX.m b/octave/NLOPT_LOCAL_SUBPLEX.m new file mode 100644 index 0000000..95c2fbc --- /dev/null +++ b/octave/NLOPT_LOCAL_SUBPLEX.m @@ -0,0 +1,2 @@ +function a = NLOPT_LOCAL_SUBPLEX + a = 5; diff --git a/octave/nlopt_minimize.cc b/octave/nlopt_minimize.cc new file mode 100644 index 0000000..cc6fd57 --- /dev/null +++ b/octave/nlopt_minimize.cc @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +#include "nlopt.h" +#include "nlopt_minimize_usage.h" + +static double struct_val_default(Octave_map &m, const std::string& k, + double dflt) +{ + if (m.contains(k)) { + if (m.contents(k).length() == 1 && (m.contents(k))(0).is_real_scalar()) + return (m.contents(k))(0).double_value(); + } + return dflt; +} + +static Matrix struct_val_default(Octave_map &m, const std::string& k, + Matrix &dflt) +{ + if (m.contains(k)) { + if ((m.contents(k)).length() == 1) { + if ((m.contents(k))(0).is_real_scalar()) + return Matrix(1, dflt.length(), (m.contents(k))(0).double_value()); + else if ((m.contents(k))(0).is_real_matrix()) + return (m.contents(k))(0).matrix_value(); + } + } + return dflt; +} + +typedef struct { + octave_function *f; + Cell f_data; +} user_function_data; + +static double user_function(int n, const double *x, + double *gradient, /* NULL if not needed */ + void *data_) +{ + user_function_data *data = (user_function_data *) data_; + octave_value_list args(1 + data->f_data.length(), 0); + Matrix xm(1,n); + for (int i = 0; i < n; ++i) + xm(i) = x[i]; + args(0) = xm; + for (int i = 0; i < data->f_data.length(); ++i) + args(1 + i) = data->f_data(i); + octave_value_list res = data->f->do_multi_index_op(gradient ? 2 : 1, args); + if (res.length() < (gradient ? 2 : 1)) + gripe_user_supplied_eval("nlopt_minimize"); + else if (!res(0).is_real_scalar() + || (gradient && !res(1).is_real_matrix() + && !(n == 1 && res(1).is_real_scalar()))) + gripe_user_returned_invalid("nlopt_minimize"); + else { + if (gradient) { + if (n == 1 && res(1).is_real_scalar()) + gradient[0] = res(1).double_value(); + else { + Matrix grad = res(1).matrix_value(); + for (int i = 0; i < n; ++i) + gradient[i] = grad(i); + } + } + return res(0).double_value(); + } + return 0; +} + +#define CHECK(cond, msg) if (!(cond)) { fprintf(stderr, msg "\n\n"); print_usage("nlopt_minimize"); return retval; } + +DEFUN_DLD(nlopt_minimize, args, nargout, NLOPT_MINIMIZE_USAGE) +{ + octave_value_list retval; + double A; + + CHECK(args.length() == 7 && nargout <= 3, "wrong number of args"); + + CHECK(args(0).is_real_scalar(), "n must be real scalar"); + nlopt_algorithm algorithm = nlopt_algorithm(args(0).int_value()); + + user_function_data d; + CHECK(args(1).is_function() || args(1).is_function_handle(), + "f must be function"); + d.f = args(1).function_value(); + CHECK(args(2).is_cell(), "f_data must be cell array"); + d.f_data = args(2).cell_value(); + + CHECK(args(3).is_real_matrix() || args(3).is_real_scalar(), + "lb must be real vector"); + Matrix lb = args(3).is_real_scalar() ? + Matrix(1, 1, args(3).double_value()) : args(3).matrix_value(); + int n = lb.length(); + + CHECK(args(4).is_real_matrix() || args(4).is_real_scalar(), + "ub must be real vector"); + Matrix ub = args(4).is_real_scalar() ? + Matrix(1, 1, args(4).double_value()) : args(4).matrix_value(); + CHECK(n == ub.length(), "lb and ub must have same length"); + + CHECK(args(5).is_real_matrix() || args(5).is_real_scalar(), + "x must be real vector"); + Matrix x = args(5).is_real_scalar() ? + Matrix(1, 1, args(5).double_value()) : args(5).matrix_value(); + CHECK(n == x.length(), "x and lb/ub must have same length"); + + CHECK(args(6).is_map(), "stop must be structure"); + Octave_map stop = args(6).map_value(); + double fmin_max = struct_val_default(stop, "fmin_max", -HUGE_VAL); + double ftol_rel = struct_val_default(stop, "ftol_rel", 0); + double ftol_abs = struct_val_default(stop, "ftol_abs", 0); + double xtol_rel = struct_val_default(stop, "xtol_rel", 0); + Matrix zeros(1, n, 0.0); + Matrix xtol_abs = struct_val_default(stop, "xtol_abs", zeros); + CHECK(n == xtol_abs.length(), "stop.xtol_abs must have same length as x"); + int maxeval = int(struct_val_default(stop, "maxeval", -1)); + double maxtime = struct_val_default(stop, "maxtime", -1); + + double fmin = HUGE_VAL; + nlopt_result ret = nlopt_minimize(algorithm, + n, + user_function, &d, + lb.data(), ub.data(), + x.fortran_vec(), &fmin, + fmin_max, ftol_rel, ftol_abs, + xtol_rel, xtol_abs.data(), + maxeval, maxtime); + + retval(0) = x; + if (nargout > 1) + retval(1) = fmin; + if (nargout > 2) + retval(2) = int(ret); + + return retval; +} diff --git a/octave/nlopt_minimize.m b/octave/nlopt_minimize.m new file mode 100644 index 0000000..45283ed --- /dev/null +++ b/octave/nlopt_minimize.m @@ -0,0 +1,61 @@ +% Usage: [xopt, fmin, retcode] = nlopt_minimize(algorithm, f, f_data, lb, ub, +% xinit, stop) +% +% Minimizes a nonlinear multivariable function f(x, f_data{:}), where +% x is a row vector, returning the optimal x found (xopt) along with +% the minimum function value (fmin = f(xopt)) and a return code (retcode). +% A variety of local and global optimization algorithms can be used, +% as specified by the algorithm parameter described below. lb and ub +% are row vectors giving the upper and lower bounds on x, xinit is +% a row vector giving the initial guess for x, and stop is a struct +% containing termination conditions (see below). +% +% This function is a front-end for the external routine nlopt_minimize +% in the free NLopt nonlinear-optimization library, which is a wrapper +% around a number of free/open-source optimization subroutines. More +% details can be found on the NLopt web page (ab-initio.mit.edu/nlopt) +% and also under 'man nlopt_minimize' on Unix. +% +% f should be a handle (@) to a function of the form: +% +% [val, gradient] = f(x, ...) +% +% where x is a row vector, val is the function value f(x), and gradient +% is a row vector giving the gradient of the function with respect to x. +% The gradient is only used for gradient-based optimization algorithms; +% some of the algorithms (below) are derivative-free and only require +% f to return val (its value). f can take additional arguments (...) +% which are passed via the argument f_data: f_data is a cell array +% of the additional arguments to pass to f. (Recall that cell arrays +% are specified by curly brackets { ... }. For example, pass f_data={} +% for functions that require no additional arguments.) +% +% stop describes the termination criteria, and is a struct with a +% number of optional fields: +% stop.ftol_rel = fractional tolerance on function value +% stop.ftol_abs = absolute tolerance on function value +% stop.xtol_rel = fractional tolerance on x +% stop.xtol_abs = row vector of absolute tolerances on x components +% stop.fmin_max = stop when f < fmin_max is found +% stop.maxeval = maximum number of function evaluations +% stop.maxtime = maximum run time in seconds +% Minimization stops when any one of these conditions is met; any +% condition that is omitted from stop will be ignored. WARNING: +% not all algorithms interpret the stopping criteria in exactly the +% same way, and in any case ftol/xtol specify only a crude estimate +% for the accuracy of the minimum function value/x. +% +% The algorithm should be one of the following constants (name and +% interpretation are the same as for the C function): +% +% Derivative-free algorithms: +% NLOPT_GLOBAL_DIRECT, NLOPT_GLOBAL_DIRECT_L, +% NLOPT_GLOBAL_DIRECT_L_RANDOMIZED, +% NLOPT_GLOBAL_ORIG_DIRECT, NLOPT_GLOBAL_ORIG_DIRECT_L, +% NLOPT_LOCAL_SUBPLEX +% +% Gradient-based algorithms: +% NLOPT_GLOBAL_STOGO, NLOPT_GLOBAL_STOGO_RANDOMIZED, NLOPT_LOCAL_LBFGS +% +% For more information on individual functions, see their individual +% help pages (e.g. "help NLOPT_LOCAL_SUBPLEX"). -- 2.30.2