The C Interface

`--prefix=$PREFIX`

was used for
In order to solve an optimization problem with the C interface, one
has to create an `IpoptProblem`^{17}with the function `CreateIpoptProblem`, which later has to be
passed to the `IpoptSolve` function.

The `IpoptProblem` created by `CreateIpoptProblem` contains
the problem dimensions, the variable and constraint bounds, and the
function pointers for callbacks that will be used to evaluate the NLP
problem functions and their derivatives (see also the discussion of
the C++ methods `get_nlp_info` and `get_bounds_info` in
Section 3.3.1 for information about the arguments of
`CreateIpoptProblem`).

The prototypes for the callback functions, `Eval_F_CB`, ` Eval_Grad_F_CB`, etc., are defined in the header file ` IpStdCInterface.h`. Their arguments correspond one-to-one to the
arguments for the C++ methods discussed in
Section 3.3.1; for example, for the meaning of , ,
,
in the declaration of ` Eval_F_CB` see the discussion of ```eval_f`''. The callback
functions should return `TRUE`, unless there was a problem doing
the requested function/derivative evaluation at the given point ` x` (then it should return `FALSE`).

Note the additional argument of type `UserDataPtr` in the callback
functions. This pointer argument is available for you to communicate
information between the main program that calls `IpoptSolve` and
any of the callback functions. This pointer is simply passed
unmodified by IPOPT among those functions. For example, you can
use this to pass constants that define the optimization problem and
are computed before the optimization in the main C program to the
callback functions.

After an `IpoptProblem` has been created, you can set algorithmic
options for IPOPT (see Section 5) using the ` AddIpopt...Option` functions. Finally, the IPOPT algorithm is
called with `IpoptSolve`, giving IPOPT the `IpoptProblem`,
the starting point, and arrays to store the solution values (primal
and dual variables), if desired. Finally, after everything is done,
you should call `FreeIpoptProblem` to release internal memory that
is still allocated inside IPOPT.

In the remainder of this section we discuss how the example problem
(4)-(7) can be solved using the C
interface. A completed version of this example can be found in ` Ipopt/examples/hs071_c`.

In order to implement the example problem on your own, create a new
directory `MyCExample` and create a new file, ` hs071_c.c`. Here, include the interface header file ` IpStdCInterface.h`, along with other necessary header files, such as
`stdlib.h` and `assert.h`. Add the prototypes and
implementations for the five callback functions. Have a look at the
C++ implementation for `eval_f`, `eval_g`, ` eval_grad_f`, `eval_jac_g`, and `eval_h` in Section
3.3.1. The C implementations have somewhat different
prototypes, but are implemented almost identically to the C++ code.
See the completed example in `Ipopt/examples/hs071_c/hs071_c.c` if you
are not sure how to do this.

We now need to implement the `main` function, create the ` IpoptProblem`, set options, and call `IpoptSolve`. The ` CreateIpoptProblem` function requires the problem dimensions, the
variable and constraint bounds, and the function pointers to the
callback routines. The `IpoptSolve` function requires the ` IpoptProblem`, the starting point, and allocated arrays for the
solution. The `main` function from the example is shown next and
discussed below.

int main() { Index n=-1; /* number of variables */ Index m=-1; /* number of constraints */ Number* x_L = NULL; /* lower bounds on x */ Number* x_U = NULL; /* upper bounds on x */ Number* g_L = NULL; /* lower bounds on g */ Number* g_U = NULL; /* upper bounds on g */ IpoptProblem nlp = NULL; /* IpoptProblem */ enum ApplicationReturnStatus status; /* Solve return code */ Number* x = NULL; /* starting point and solution vector */ Number* mult_x_L = NULL; /* lower bound multipliers at the solution */ Number* mult_x_U = NULL; /* upper bound multipliers at the solution */ Number obj; /* objective value */ Index i; /* generic counter */ /* set the number of variables and allocate space for the bounds */ n=4; x_L = (Number*)malloc(sizeof(Number)*n); x_U = (Number*)malloc(sizeof(Number)*n); /* set the values for the variable bounds */ for (i=0; i<n; i++) { x_L[i] = 1.0; x_U[i] = 5.0; } /* set the number of constraints and allocate space for the bounds */ m=2; g_L = (Number*)malloc(sizeof(Number)*m); g_U = (Number*)malloc(sizeof(Number)*m); /* set the values of the constraint bounds */ g_L[0] = 25; g_U[0] = 2e19; g_L[1] = 40; g_U[1] = 40; /* create the IpoptProblem */ nlp = CreateIpoptProblem(n, x_L, x_U, m, g_L, g_U, 8, 10, 0, &eval_f, &eval_g, &eval_grad_f, &eval_jac_g, &eval_h); /* We can free the memory now - the values for the bounds have been copied internally in CreateIpoptProblem */ free(x_L); free(x_U); free(g_L); free(g_U); /* set some options */ AddIpoptNumOption(nlp, "tol", 1e-9); AddIpoptStrOption(nlp, "mu_strategy", "adaptive"); /* allocate space for the initial point and set the values */ x = (Number*)malloc(sizeof(Number)*n); x[0] = 1.0; x[1] = 5.0; x[2] = 5.0; x[3] = 1.0; /* allocate space to store the bound multipliers at the solution */ mult_x_L = (Number*)malloc(sizeof(Number)*n); mult_x_U = (Number*)malloc(sizeof(Number)*n); /* solve the problem */ status = IpoptSolve(nlp, x, NULL, &obj, NULL, mult_x_L, mult_x_U, NULL); if (status == Solve_Succeeded) { printf("\n\nSolution of the primal variables, x\n"); for (i=0; i<n; i++) printf("x[%d] = %e\n", i, x[i]); printf("\n\nSolution of the bound multipliers, z_L and z_U\n"); for (i=0; i<n; i++) printf("z_L[%d] = %e\n", i, mult_x_L[i]); for (i=0; i<n; i++) printf("z_U[%d] = %e\n", i, mult_x_U[i]); printf("\n\nObjective value\nf(x*) = %e\n", obj); } /* free allocated memory */ FreeIpoptProblem(nlp); free(x); free(mult_x_L); free(mult_x_U); return 0; }

Here, we declare all the necessary variables and set the dimensions of
the problem. The problem has 4 variables, so we set `n` and
allocate space for the variable bounds (don't forget to call ` free` for each of your `malloc` calls before the end of the
program). We then set the values for the variable bounds.

The problem has 2 constraints, so we set `m` and allocate space
for the constraint bounds. The first constraint has a lower bound of
and no upper bound. Here we set the upper bound to
`2e19`. IPOPT interprets any number greater than or equal to
`nlp_upper_bound_inf` as infinity.
The default value of `nlp_lower_bound_inf`
and `nlp_upper_bound_inf` is
`-1e19` and `1e19`, respectively, and can be changed
through IPOPT options. The second constraint is an equality with
right hand side 40, so we set both the upper and the lower bound to
40.

We next create an instance of the `IpoptProblem` by calling `CreateIpoptProblem`, giving it the problem dimensions and the variable
and constraint bounds. The arguments `nele_jac` and `nele_hess` are the number of elements in Jacobian and the Hessian,
respectively. See Appendix A for a description of the
sparse matrix format. The `index_style` argument specifies whether
we want to use C style indexing for the row and column indices of the
matrices or Fortran style indexing. Here, we set it to `0` to
indicate C style. We also include the references to each of our
callback functions. IPOPT uses these function pointers to ask for
evaluation of the NLP when required.

After freeing the bound arrays that are no longer required, the next
two lines illustrate how you can change the value of options through
the interface. IPOPT options can also be changed by creating a `ipopt.opt` file (see Section 5). We next allocate
space for the initial point and set the values as given in the problem
definition.

The call to `IpoptSolve` can provide us with information about the
solution, but most of this is optional. Here, we want values for the
bound multipliers at the solution and we allocate space for these.

We can now make the call to `IpoptSolve` and find the solution of
the problem. We pass in the `IpoptProblem`, the starting point
`x` (IPOPT will use this array to return the solution or final
point as well). The next 5 arguments are pointers so IPOPT can fill
in values at the solution. If these pointers are set to `NULL`,
IPOPT will ignore that entry. For example, here, we do not want the
constraint function values at the solution or the constraint
multipliers, so we set those entries to `NULL`. We do want the
value of the objective, and the multipliers for the variable bounds.
The last argument is a `void*` for user data. Any pointer you give
here will also be passed to you in the callback functions.

The return code is an `ApplicationReturnStatus` enumeration, see
the header file `ReturnCodes_inc.h` which is installed along ` IpStdCInterface.h` in the IPOPT include directory.

After the optimizer terminates, we check the status and print the
solution if successful. Finally, we free the `IpoptProblem` and
the remaining memory and return from `main`.

- ...
`IpoptProblem`^{17} `IpoptProblem`is a pointer to a C structure; you should not access this structure directly, only through the functions provided in the C interface.