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
63
64
65
66
67
68
=======================================
          Commercial License
         Artelys Knitro 12.0.0
=======================================

Knitro presolve eliminated 0 variables and 0 constraints.

The problem is identified as a QCQP.
Knitro changing algorithm from AUTO to 1.
Knitro changing bar_initpt from AUTO to 3.
Knitro changing bar_murule from AUTO to 4.
Knitro changing bar_penaltycons from AUTO to 1.
Knitro changing bar_penaltyrule from AUTO to 2.
Knitro changing bar_switchrule from AUTO to 2.
Knitro changing linesearch from AUTO to 1.
Knitro changing linsolver from AUTO to 2.

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

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.