Getting started with the object-oriented interface

The Knitro object-oriented interface provides an object-oriented wrapper around the Knitro callable library. The interface is available in C++, C#, and Java. This document focuses on the C++ version of the interface. The interfaces are the same in functionality, differing only in language syntax and data types used in functions (e.g., std::vector<> in C++, IList<> in C#, and List<> in Java). Examples for each of the languages are available in the Knitro examples folders.

Complete source code for the interface is included with Knitro for informational purposes. Usage requires including the knitro.h header within the include directory of Knitro and linking against the appropriate Knitro library file within the lib directory of Knitro.

The object-oriented API is used to solve an optimization problem through a sequence of function calls:

  • KNProblem instance: create an instance of the problem to be solved by Knitro. The class is user-defined, inherits from the KNProblem class, and defines the problem characteristics.

  • KNSolver solver(instance): load the problem definition into the Knitro solver and check out a Knitro license.

  • solver.solve(): solve the problem, with output stored in the solver object.

The example below shows how to define a problem and class and use these function calls.

First example

The follwing defines the same toy problem solved using AMPL, MATLAB, and the callable library.

 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
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  This example demonstrates how to use Knitro to solve the following
 *  simple quadratically constrained quadratic programming problem (QCQP).
 *
 *  min   1000 - x0^2 - 2 x1^2 - x2^2 - x0 x1 - x0 x2
 *  s.t.  8 x0 + 14 x1 + 7 x2 = 56
 *        x0^2 + x1^2 + x2^2 >= 25
 *        x0 >= 0, x1 >= 0, x2 >= 0
 *
 *  The start point (2, 2, 2) converges to the minimum at (0, 0, 8),
 *  with final objective = 936.0.  From a different start point,
 *  Knitro may converge to an alternate local solution at (7, 0, 0),
 *  with objective = 951.0.
 *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "KNSolver.h"
#include "KNProblem.h"

using namespace knitro;

class ProblemQCQP1 : public knitro::KNProblem {
public:

  ProblemQCQP1() : KNProblem(3,2) {
    // Variables
    this->setVarLoBnds({{0, 0, 0}});
    this->setXInitial({{2.0, 2.0, 2.0}});

    // Constraints Strucutres
    // -> Linear parts (in 1 constraint (0))
    // 8 x0 + 14 x1 + 7 x2
    this->getConstraintsLinearParts().add(0, {{ 0, 1, 2}, {8.0, 14.0, 7.0}});
    // -> Quaddratic parts (in 1 constraint (1))
    // x0^2 + x1^2 + x2^2
    this->getConstraintsQuadraticParts().add(1, {{ 0, 1, 2}, {0, 1, 2}, {1.0, 1.0, 1.0}});
    // Bounds
    // c0 = 56
    // c1 >= 25
    this->getConEqBnds().set({0}, {56.0});
    this->getConLoBnds().set({1}, {25.0});

    // Objective structure
    this->setObjConstPart(1000.0);
    // -> Objective quadratic structure
    // - x0^2 - 2 x1^2 - x2^2 - x0 x1 - x0 x2
    this->setObjectiveQuadraticPart({ { 0, 1, 2, 0, 0}, { 0, 1, 2, 1, 2}, {-1.0, -2.0, -1.0, -1.0, -1.0} });
  }
};

int main() {
  // Create a problem instance.
  ProblemQCQP1 instance = ProblemQCQP1();

  // Create a solver
  knitro::KNSolver solver(&instance);

  solver.initProblem();
  int solveStatus = solver.solve();

  std::vector<double> x;
  std::vector<double> lambda;
  int nStatus = solver.getSolution(x, lambda);
  printf ("Knitro converged with final status = %d\n", nStatus);
  printf ("  optimal objective value  = %e\n", solver.getObjValue());
  printf ("  optimal primal values x  = (%e, %e, %e)\n", x[0], x[1], x[2]);
  printf ("  feasibility violation    = %e\n", solver.getAbsFeasError());
  printf ("  KKT optimality violation = %e\n", solver.getAbsOptError());

  return 0;
}

This is similar to the callable library example. The problem definition is contained in a class definition and is simpler. Variable and constraint properties can be defined more compactly; memory for the problem characteristics does not need to be allocated by the user; and the Jacobian sparsity pattern is automatically defined internally by Knitro because no Jacobian non-zero size is need for linear and quadratic elements of a problem.

The Knitro solver functions are called from the KNSolver class. Like the callable library, the object-oriented interface does not provide automatic derivatives. Derivatives can be computed manually and defined in the KNProblem class. This is covered in the chapter on Derivatives.

The above example can be compiled and linked against Knitro, and produces the same output as the callable library output.

 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
    =======================================
              Commercial License
             Artelys Knitro 12.4.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.

    HINT: Knitro spent   8.5% of solution time (0.000707 secs) checking model
          convexity. To skip the automatic convexity checker for QPs and QCQPs,
          explicity set the user option convex=0 or convex=1.

    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.00791 (     0.004 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.484136e-14, 8.000000e+00)
      feasibility violation    = 0.000000e+00
      KKT optimality violation = 1.514577e-09

The object-oriented interface provide ease-of-use over the callable library with similar functionality and performance.

Further information

Another chapter of this documentation is dedicated to the object-oriented interface (Object-oriented interface reference). The reference manual chapter on the callable library (Callable library API reference) provides information on the callable library underlying the object-oriented interface. This section provides a comprehensive documentation of the Knitro callable library functions, which are accessible through methods of the KNSolver class.

Finally, the source code for the object-oriented interface is provided as a reference. The .hpp header files for the C++ interface document the interface functionality.

Additional examples

More examples using the object-oriented interface are provided in the examples/C++, examples/CSharp and examples/Java directories of the Knitro distribution.