This is a Common Lisp interface to NLopt. NLopt is a free/open-source library for nonlinear optimization, providing a common interface for a number of different free optimization routines available online as well as original implementations of various other algorithms. See link for more information on the available algorithms.
- For usual cases the simple and easy
nloptimize
macro suffices:
(defun solve ()
(nloptimize ((x y)
:initial '(1.234 5.678))
(:minimize (sqrt y))
(:satisfy (>= y (expt (* 2 x) 3))
(>= y (expt (- 1 x) 3))
(> y 0))
(setf (xtol-rel *nlopt-instance*) 1d-4)))
The above example minimizes sqrt(y)
subject to y >= (2x)^3
, y >= (1 - x)^3
and y>0
with initial guess for (x,y)
at (1.234, 5.678)
(This is the tutorial problem given here).
- Or you can use functions equivalent to those defined in original NLopt C library.
(defun solve ()
(let ((nlopt (create :nlopt_ld_mma 2)))
(set-lower-bounds nlopt (doubles -11d0 0.001d0))
(set-min-objective nlopt objective-function)
(add-inequality-constraint nlopt constraint1)
(add-inequality-constraint nlopt constraint2)
(set-xtol-rel nlopt 1d-4)
(optimize-nlp nlopt (darray 1.234d0 5.67d0))))
First you create a nlopt object representing the optimization problem by specifying the algorithm to use and the dimension (number of variables) of the problem.
(create algorithm dimension)
Then you add objective function, constraints, bounds, stopping criteria, etc to the problem. E.g.
(defun objective-function (x grad nlopt)
(setf-doubles grad grad_0 grad_1 grad_2 ...)
objective-function-value-at-x)
(set-min-objective nlopt objective-function))
Here an objective to minimize the function (objective-function) is set.
This callback function (or lambdas can also be used) accepts the value of variables (x) in an foreign-array of double-float. Elements of x can be accessed by using (dref x index)
function.
Also if the algorithm selected requires the gradient of the function to be computed then grad is set to a pointer to the foreign-array where calculated values have to set.
You can use (setf (dref grad 0) value0 (dref grad 1) value1 ...)
or (set-doubles grad value0 value1 ...)
. If the algorithm doesn't require gradient to be calculated then grad
is NIL.
The current nlopt
object is also passed to the callback.
Few examples are available in examples.lisp file. It may be helpful to follow along the original tutorial along with its implementation in lisp given in example.lisp file (example-mma)
Ubuntu
apt install nlopt
Arch
pacman -Suy nlopt
Download the prebuilt binary from here and copy the libnlopt.dll to any folder in your PATH (e.g. c:/Windows/System32/) or to this project's directory
Download or clone this repo to your `local-projects' directory then load it.
(ql:quickload :nlopt)
You can read the documentation of NLopt C library here. This Common Lisp binding to the NLopt library tries to follow the names of the original functions albiet with some lispier modifications. Let's see few example:
nlopt_create(nlopt_algorithm algorithm, unsigned n);
is available as
(create algorithm n)
The nlopt prefix is removed because all functions are in nlopt package. So, you may access them from any other package as (nlopt:create algorithm n)
nlopt_set_min_objective(nlopt_opt opt, nlopt_func f, void* f_data);
is available as
(set-min-objective nlopt function)
The nlopt prefix is removed and all underscores are converted to hypens. Also notice that, the function doesn't take a f_data
struct to pass along the callbacks.
Because in Common Lisp we can use dynamic binding to set the contex. Also, instead of f_data
, the nlopt instance is passed along in the callbacks. So, you may create a subclass of nlopt if you need
to store some state to use in the callbacks.
The callback for the objective function
double f(unsigned n, const double* x, double* grad, void* f_data);
should be defined as
(defun f(x grad nlopt) ...)
As mentioned above instead of a user defined data f_data
, the nlopt
instance is passed. Also note that parameter indicating dimension (n
) as also unnecessary because
it is available through the nlopt
object as : n
= (dimension nlopt)
.
Signature for callbacks to other functions are documented in the docstring of respective functions (set-min-objective
, set-inequality-constraints
, ...)
Since optimize
is a reserved word in Common Lisp, c nlopt_optimize(nlopt_opt opt, double *x, double *opt_f);
is avaibale as
commmon-lisp (optimize-nlp nlopt initial-x)
. Since functions can return multiples values, the opt_f
pointer is not required to get the optimum value. optimize
returns the optimum position x
and the optimum value f(x)
.
Functions that have set-foo
and get-foo
counterparts are defined as (setf (foo ...) value)
and (foo ...)
All other functions are name similarly.
NLopt | Common Lisp Binding |
---|---|
nlopt_create(nlopt_algorithm algorithm, unsigned n) | (create algorithm dimension) |
nlopt_copy(const nlopt_opt opt) | (copy nlopt) |
nlopt_get_algorithm(const nlopt_opt opt); | (algorithm nlopt) |
nlopt_get_dimension(const nlopt_opt opt); | (dimensions nlopt) |
nlopt_algorithm_name(nlopt_algorithm algorithm); | (algorithm-name algorithm) |
NLopt | Common Lisp Binding |
---|---|
nlopt_set_min_objective(nlopt_opt opt, nlopt_func f, void* f_data); | (set-min-objective nlopt function) |
nlopt_set_max_objective(nlopt_opt opt, nlopt_func f, void* f_data); | (set-max-objective nlopt function) |
nlopt_set_precond_min_objective(nlopt_opt opt, nlopt_func f, nlopt_precond pre, void *f_data); | (set-precond-min-objective nlopt function pre) |
nlopt_set_precond_min_objective(nlopt_opt opt, nlopt_func f, nlopt_precond pre, void *f_data); | (set-precond-max-objective nlopt function pre) |
NLopt | Common Lisp Binding |
---|---|
nlopt_add_inequality_constraint(nlopt_opt opt, nlopt_func fc, void* fc_data, double tol); | (add-inequality-constraint nlopt function tol) |
nlopt_add_equality_constraint(nlopt_opt opt, nlopt_func h, void* h_data, double tol); | (add-equality-constraint nlopt function tol) |
nlopt_remove_inequality_constraints(nlopt_opt opt); | (remove-inequality-constraint nlopt) |
nlopt_remove_equality_constraints(nlopt_opt opt); | (remove-equality-constraint nlopt) |
nlopt_add_inequality_mconstraint(nlopt_opt opt, unsigned m, nlopt_mfunc c, void* c_data, const double *tol); | (add-inequality-mconstraint nlopt m function tol) |
nlopt_add_equality_mconstraint(nlopt_opt opt, unsigned m, nlopt_mfunc c, void* c_data, const double *tol); | (add-equality-mconstraint nlopt m function tol) |
nlopt_set_lower_bounds(nlopt_opt opt, const double* lb); | (setf (lower-bounds nlopt) bounds) |
nlopt_set_upper_bounds(nlopt_opt opt, const double* ub); | (setf (upper-bounds nlopt) bounds) |
nlopt_set_lower_bounds1(nlopt_opt opt, double lb); | (setf (lower-bounds nlopt) bound) |
nlopt_set_upper_bounds1(nlopt_opt opt, double ub); | (setf (upper-bounds nlopt) bound) |
nlopt_get_lower_bounds(const nlopt_opt opt, double* lb); | (lower-bounds nlopt) |
nlopt_get_upper_bounds(const nlopt_opt opt, double* ub); | (upper-bounds nlopt) |
nlopt_set_lower_bound(nlopt_opt opt, int i, double lb); | (setf (lower-bound nlopt i) bound) |
nlopt_set_upper_bound(nlopt_opt opt, int i, double ub); | (setf (upper-bound nlopt i) bound) |
(lower-bound nlopt i) | |
(upper-bound nlopt i) |
NLopt | Common Lisp Binding |
---|---|
nlopt_set_stopval(nlopt_opt opt, double stopval); | (setf (stopval nlopt) stopval) |
nlopt_get_stopval(const nlopt_opt opt); | (stopval nlopt) |
nlopt_set_ftol_rel(nlopt_opt opt, double tol); | (setf (ftol-rel nlopt) tol) |
nlopt_get_ftol_rel(const nlopt_opt opt); | (ftol-rel nlopt) |
nlopt_set_ftol_abs(nlopt_opt opt, double tol); | (setf (ftol-abs nlopt) tol) |
nlopt_get_ftol_abs(const nlopt_opt opt); | (ftol-abs nlopt) |
nlopt_set_xtol_rel(nlopt_opt opt, double tol); | (setf (xtol-rel nlopt) tol) |
nlopt_get_xtol_rel(const nlopt_opt opt); | (xtol-rel nlopt) |
nlopt_set_x_weights(nlopt_opt opt, const double *w); | (setf (x-weights nlopt) weights) |
nlopt_set_x_weights1(nlopt_opt opt, const double w); | (setf (x-weights nlopt) weight) |
nlopt_get_x_weights(const nlopt_opt opt, double *w); | (x-weights nlopt) |
nlopt_set_xtol_abs(nlopt_opt opt, const double *tol); | (setf (xtol-abs nlopt) tol) |
nlopt_set_xtol_abs1(nlopt_opt opt, double tol); | (setf (xtol-abs nlopt) tol) |
nlopt_get_xtol_abs(const nlopt_opt opt, double *tol); | (xtol-abs nlopt) |
nlopt_set_maxeval(nlopt_opt opt, int maxeval); | (setf (maxval nlopt) maxeval) |
nlopt_get_maxeval(nlopt_opt opt); | (maxeval nlopt) |
nlopt_set_maxtime(nlopt_opt opt, double maxtime); | (setf (maxtime nlopt) maxtime) |
nlopt_get_maxtime(nlopt_opt opt); | (maxtime nlopt) |
nlopt_get_numevals(nlopt_opt opt); | (numevals nlopt) |
NLopt | Common Lisp Binding |
---|---|
nlopt_force_stop(nlopt_opt opt); | (force-stop nlopt) |
nlopt_set_force_stop(nlopt_opt opt, int val) | (setf (force-stop-value nlopt) val) |
nlopt_get_force_stop(nlopt_opt opt) | (force-stop-value nlopt) |
NLopt | Common Lisp Binding |
---|---|
nlopt_optimize(nlopt_opt opt, double *x, double *opt_f); | (optimize-nlopt nlopt x) |
nlopt_set_local_optimizer(nlopt_opt opt, const nlopt_opt local_opt); | (set-local-optimizer nlopt local-nlopt) |
NLopt | Common Lisp Binding |
---|---|
nlopt_set_initial_step(nlopt_opt opt, const double* dx); | (setf (initial-step nlopt) dx) |
nlopt_set_initial_step1(nlopt_opt opt, const double dx); | (setf (initial-step nlopt) dx) |
nlopt_get_initial_step(const nlopt_opt opt, const double *x, double *dx); | (initila-step nlopt) |
nlopt_set_population(nlopt_opt opt, unsigned pop); | (set-population nlopt size) |
nlopt_srand(unsigned long seed); | (srand seed) |
nlopt_srand_time(void); | (srand-time) |
nlopt_set_vector_storage(nlopt_opt opt, unsigned M); | (setf (vector-storage nlopt) M) |
nlopt_get_vector_storage(const nlopt_opt opt); | (vector-storage nlopt) |
nlopt_version(int *major, int *minor, int *bugfix); | (version) |
MIT