Getting started with the callable library
Knitro is written in C and C++, with a well-documented application
programming interface (API) defined in the file knitro.h
provided in the installation under the include
directory.
The Knitro callable library is used to build a model in pieces while providing special structures to Knitro (e.g. linear structures, quadratic structures), while providing callbacks to handle general, nonlinear structures. A typical sequence of function calls looks as follows:
KN_new()
: create a new Knitro solver context pointer, allocating resources.KN_add_vars()
/KN_add_cons()
/KN_set_*bnds()
: add basic problem information to Knitro.KN_add_*_linear_struct()
/KN_add_*_quadratic_struct()
: add special problem structures.KN_add_eval_callback()
: add callback for nonlinear evaluations if needed.KN_set_cb_*()
: set properties for nonlinear evaluation callbacks.KN_set_*_param()
: set user options/parameters.KN_solve()
: solve the problem.KN_free()
: delete the Knitro context pointer, releasing allocated resources.
The example below shows how to use these function calls.
First example
Again, let us consider the toy example that we already solved
twice, using AMPL and MATLAB. The C callable library equivalent is the
following (see exampleQCQP.c
provided with the distribution
in examples/C/
).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | #include <stdio.h>
#include <stdlib.h>
#include "knitro.h"
/* main */
int main (int argc, char *argv[]) {
int i, nStatus, error;
/** Declare variables. */
KN_context *kc;
int n, m;
double x[3];
double xLoBnds[3] = {0, 0, 0};
double xInitVals[3] = {2.0, 2.0, 2.0};
/** Used to define linear constraint. */
int lconIndexVars[3] = { 0, 1, 2};
double lconCoefs[3] = {8.0, 14.0, 7.0};
/** Used to specify quadratic constraint. */
int qconIndexVars1[3] = { 0, 1, 2};
int qconIndexVars2[3] = { 0, 1, 2};
double qconCoefs[3] = {1.0, 1.0, 1.0};
/** Used to specify quadratic objective terms. */
int qobjIndexVars1[5] = { 0, 1, 2, 0, 0};
int qobjIndexVars2[5] = { 0, 1, 2, 1, 2};
double qobjCoefs[5] = {-1.0, -2.0, -1.0, -1.0, -1.0};
/** Solution information */
double objSol;
double feasError, optError;
/** Create a new Knitro solver instance. */
error = KN_new(&kc);
if (error) exit(-1);
if (kc == NULL)
{
printf ("Failed to find a valid license.\n");
return( -1 );
}
/** Illustrate how to override default options by reading from
* the knitro.opt file. */
error = KN_load_param_file (kc, "knitro.opt");
if (error) exit(-1);
/** Initialize Knitro with the problem definition. */
/** Add the variables and set their bounds and initial values.
* Note: unset bounds assumed to be infinite. */
n = 3;
error = KN_add_vars(kc, n, NULL);
if (error) exit(-1);
error = KN_set_var_lobnds_all(kc, xLoBnds);
if (error) exit(-1);
error = KN_set_var_primal_init_values_all(kc, xInitVals);
if (error) exit(-1);
/** Add the constraints and set their bounds. */
m = 2;
error = KN_add_cons(kc, m, NULL);
if (error) exit(-1);
error = KN_set_con_eqbnd(kc, 0, 56.0);
if (error) exit(-1);
error = KN_set_con_lobnd(kc, 1, 25.0);
if (error) exit(-1);
/** Add coefficients for linear constraint. */
error = KN_add_con_linear_struct_one (kc, 3, 0, lconIndexVars,
lconCoefs);
if (error) exit(-1);
/** Add coefficients for quadratic constraint */
error = KN_add_con_quadratic_struct_one (kc, 3, 1, qconIndexVars1,
qconIndexVars2, qconCoefs);
if (error) exit(-1);
/** Set minimize or maximize (if not set, assumed minimize) */
error = KN_set_obj_goal(kc, KN_OBJGOAL_MINIMIZE);
if (error) exit(-1);
/** Add constant value to the objective. */
error= KN_add_obj_constant(kc, 1000.0);
if (error) exit(-1);
/** Set quadratic objective structure. */
error = KN_add_obj_quadratic_struct (kc, 5, qobjIndexVars1,
qobjIndexVars2, qobjCoefs);
if (error) exit(-1);
/** Solve the problem.
*
* Return status codes are defined in "knitro.h" and described
* in the Knitro manual. */
nStatus = KN_solve (kc);
printf ("\n\n");
printf ("Knitro converged with final status = %d\n",
nStatus);
/** An example of obtaining solution information. */
error = KN_get_solution(kc, &nStatus, &objSol, x, NULL);
if (!error) {
printf (" optimal objective value = %e\n", objSol);
printf (" optimal primal values x = (%e, %e, %e)\n", x[0], x[1], x[2]);
}
error = KN_get_abs_feas_error (kc, &feasError);
if (!error)
printf (" feasibility violation = %e\n", feasError);
error = KN_get_abs_opt_error (kc, &optError);
if (!error)
printf (" KKT optimality violation = %e\n", optError);
/** Delete the Knitro solver instance. */
KN_free (&kc);
return( 0 );
}
|
Note that the AMPL equivalent is much shorter and simpler (only a few lines of code). In both the AMPL example and this example, the quadratic structure is passed directly to Knitro, so no callback evaluations are needed. However, when there is more general nonlinear structure AMPL will often be more efficient since it is able to provide Knitro the exact derivatives of all nonlinear functions automatically as needed. To achieve the same efficiency in C, we would have to compute the derivatives manually, code them in C and input them to Knitro using a callback. We will show how to do this in the chapter on Derivatives. However the callable library has the advantage of greater control (for instance, on memory usage) and allows one to embed Knitro in a native application seamlessly.
The above example can be compiled and linked against the Knitro callable library with a standard C compiler. Its output is the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | =======================================
Commercial License
Artelys Knitro 14.1.0
=======================================
Knitro presolve eliminated 0 variables and 0 constraints.
The problem is identified as a QCQP.
Problem Characteristics ( Presolved)
-----------------------
Objective goal: Minimize
Objective type: quadratic
Number of variables: 3 ( 3)
bounded below only: 3 ( 3)
bounded above only: 0 ( 0)
bounded below and above: 0 ( 0)
fixed: 0 ( 0)
free: 0 ( 0)
Number of constraints: 2 ( 2)
linear equalities: 1 ( 1)
quadratic equalities: 0 ( 0)
gen. nonlinear equalities: 0 ( 0)
linear one-sided inequalities: 0 ( 0)
quadratic one-sided inequalities: 1 ( 1)
gen. nonlinear one-sided inequalities: 0 ( 0)
linear two-sided inequalities: 0 ( 0)
quadratic two-sided inequalities: 0 ( 0)
gen. nonlinear two-sided inequalities: 0 ( 0)
Number of nonzeros in Jacobian: 6 ( 6)
Number of nonzeros in Hessian: 5 ( 5)
Iter Objective FeasError OptError ||Step|| CGits
-------- -------------- ---------- ---------- ---------- -------
0 9.760000e+02 1.300e+01
9 9.360000e+02 0.000e+00 1.515e-09 5.910e-05 0
Knitro using the Interior-Point/Barrier Direct algorithm.
EXIT: Locally optimal solution found.
Final Statistics
----------------
Final objective value = 9.36000000015579e+02
Final feasibility error (abs / rel) = 0.00e+00 / 0.00e+00
Final optimality error (abs / rel) = 1.51e-09 / 9.47e-11
# of iterations = 9
# of CG iterations = 2
# of function evaluations = 0
# of gradient evaluations = 0
# of Hessian evaluations = 0
Total program time (secs) = 0.00207 ( 0.001 CPU time)
Time spent in evaluations (secs) = 0.00000
===============================================================================
Knitro converged with final status = 0
optimal objective value = 9.360000e+02
optimal primal values x = (1.514577e-09, 1.484137e-14, 8.000000e+00)
feasibility violation = 0.000000e+00
KKT optimality violation = 1.514577e-09
|
Again, the solution value is the same (about 936.0), and the details of the log are similar (we used a different algorithm in the AMPL example).
Further information
Another chapter of this documentation will be dedicated to the callable library (Callbacks), more specifically to the communication mode between the solver and the user-supplied optimization problem.
The reference manual (Callable library API reference) also contains a comprehensive documentation of the Knitro callable library API.
Finally, the file knitro.h
contains many useful comments
and can be used as an ultimate reference.
Additional examples
More C/C++ examples using the callable library are provided in
the examples/C
and examples/C++
directories of the Knitro
distribution.