an example of a multi-precision call#

This is an example of how to use the expo package to solve a nonlinearly constrained optimization problem with data provided both as 32-bit integers and double precision reals, and then again as 64-bit integers and single precision reals; the code is available in $GALAHAD/src/expo/C/expotm.c .

Notice that C-style indexing is used, and that this is flagged by setting control.f_indexing to false. The appropriate real and integer types are supplied to each set of data. The structures and calls for the first set of data takes double reals and int32_t integers, while for the second the data uses float reals and int64_t integers, and appends _s_64 to all structure and function names.

/* expotm.c */
/* Test for the EXPO C interface using C sparse matrix indexing */
/* and simulataneous use of different precisions and integer types */
/* Jari Fowkes & Nick Gould, STFC-Rutherford Appleton Laboratory, 2025 */

#include <stdio.h>
#include <math.h>
#include "galahad_c.h"
#ifdef REAL_128
#include <quadmath.h>
#endif

// Custom userdata struct
struct userdata_type {
   double p;
};
struct userdata_type_s_64 {
   float p;
};

// Function prototypes
int fc( int32_t n, int32_t m, const double x[], double *f, double c[],
        const void * );
int gj( int32_t n, int32_t m, int32_t jne, const double x[], double g[],
         double jval[], const void * );
int hl( int32_t n, int32_t m, int32_t hne, const double x[], const double y[],
         double hval[], const void * );
int fc_s_64( int64_t n, int64_t m, const float x[], float *f, float c[],
            const void * );
int gj_s_64( int64_t n, int64_t m, int64_t jne, const float x[], float g[],
            float jval[], const void * );
int hl_s_64( int64_t n, int64_t m, int64_t hne, const float x[],
             const float y[], float hval[], const void * );

int main(void) {

    // double precision reals and 32-bit integers
    // Derived types
    void *data;
    struct expo_control_type control;
    struct expo_inform_type inform;

    printf(" tests double precision reals and 32-bit integers\n\n");

    // Set user data
    struct userdata_type userdata;
    userdata.p = 9.0;

    // Set problem data
    int32_t n = 2; // # variables
    int32_t m = 5; // # constraints
    int32_t j_ne = 10; // Jacobian elements
    int32_t h_ne = 2; // Hesssian (lower triangle) elements
    // 0-based indices
    int32_t J_row[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4}; // Jacobian J
    int32_t J_col[] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
    int32_t H_row[] = {0, 1}; // Hessian H, NB lower triangle
    int32_t H_col[] = {0, 1};

    // Set storage
    double y[m]; // multipliers
    double z[n]; // dual variables
    double c[m]; // constraints
    double gl[n]; // gradients
    double xl[] = {-50.0, -50.0}; // lower variable bounds
    double xu[] = {50.0, 50.0}; // upper variable bounds
    double cl[] = {0.0, 0.0, 0.0, 0.0, 0.0}; // lower constraint bounds
    double cu[] = {INFINITY, INFINITY, INFINITY,
                   INFINITY, INFINITY}; // upper constraint bounds
    double x[] = {3.0,1.0}; // starting point
    int32_t status;

    // Initialize EXPO
    expo_initialize( &data, &control, &inform );

    // Set user-defined control options
    control.f_indexing = false; // C sparse matrix indexing
    //control.print_level = 1;
    control.max_it = 20;
    control.max_eval = 100;
    control.stop_abs_p = 0.00001;
    control.stop_abs_d = 0.00001;
    control.stop_abs_c = 0.00001;

    // Solve the problem
    expo_import( &control, &data, &status, n, m,
                 "coordinate", j_ne, J_row, J_col, NULL,
                 "coordinate", h_ne, H_row, H_col, NULL );
    expo_solve_hessian_direct( &data, &userdata, &status,
                               n, m, j_ne, h_ne,
                               cl, cu, xl, xu, x, y, z, c, gl,
                                           fc, gj, hl );
    expo_information( &data, &inform, &status );

    if(inform.status == 0){
      printf("%6i iterations. Optimal objective value"
            " = %.2f status = %1i\n",  inform.iter, inform.obj, inform.status);
    }else{
      printf("EXPO_solve exit status = %1i\n", inform.status);
    }
    // Delete internal workspace
    expo_terminate( &data, &control, &inform );

    // double precision reals and 32-bit integers
    // Derived types
    void *data_s_64;
    struct expo_control_type_s_64 control_s_64;
    struct expo_inform_type_s_64 inform_s_64;

    printf("\n expo test with single precision reals and 64-bit integers\n\n");

    // Set user data
    struct userdata_type_s_64 userdata_s_64;
    userdata_s_64.p = 9.0;

    // Set problem data
    int64_t n_64 = 2; // # variables
    int64_t m_64 = 5; // # constraints
    int64_t j_ne_64 = 10; // Jacobian elements
    int64_t h_ne_64 = 2; // Hesssian elements
    // 0-based indices
    int64_t J_row_64[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4}; // Jacobian J
    int64_t J_col_64[] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
    int64_t H_row_64[] = {0, 1}; // Hessian H, NB lower triangle
    int64_t H_col_64[] = {0, 1};

    // Set storage
    float y_s[m]; // multipliers
    float z_s[n]; // dual variables
    float c_s[m]; // constraints
    float gl_s[n]; // gradients
    float xl_s[] = {-50.0, -50.0}; // lower variable bounds
    float xu_s[] = {50.0, 50.0}; // upper variable bounds
    float cl_s[] = {0.0, 0.0, 0.0, 0.0, 0.0}; // lower constraint bounds
    float cu_s[] = {INFINITY, INFINITY, INFINITY,
                   INFINITY, INFINITY}; // upper constraint bounds
    float x_s[] = {3.0,1.0}; // starting point
    int64_t status_64;

    // Initialize EXPO
    expo_initialize_s_64( &data_s_64, &control_s_64, &inform_s_64 );

    // Set user-defined control options
    control_s_64.f_indexing = false; // C sparse matrix indexing
    //control_s_64.print_level = 1;
    control_s_64.max_it = 20;
    control_s_64.max_eval = 100;
    control_s_64.stop_abs_p = 0.001;
    control_s_64.stop_abs_d = 0.001;
    control_s_64.stop_abs_c = 0.001;
    control_s_64.tru_control.error = 0;

    // Solve the problem
    expo_import_s_64( &control_s_64, &data_s_64, &status_64, n_64, m_64,
                 "coordinate", j_ne_64, J_row_64, J_col_64, NULL,
                 "coordinate", h_ne_64, H_row_64, H_col_64, NULL );
    expo_solve_hessian_direct_s_64( &data_s_64, &userdata_s_64, &status_64,
                                    n_64, m_64, j_ne_64, h_ne_64,
                                    cl_s, cu_s, xl_s, xu_s,
                                    x_s, y_s, z_s, c_s, gl_s,
                                    fc_s_64, gj_s_64, hl_s_64 );
    expo_information_s_64( &data_s_64, &inform_s_64, &status_64 );

    if(inform_s_64.status == 0){
      printf("%6li iterations. Optimal objective value = %.2f status = %1li\n",
          inform_s_64.iter, inform_s_64.obj, inform_s_64.status);
    }else{
      printf("EXPO_solve exit status = %1li\n", inform_s_64.status);
    }
    // Delete internal workspace
    expo_terminate_s_64( &data_s_64, &control_s_64, &inform_s_64 );

}

// compute the function and constraint values
int fc( int32_t n, int32_t m, const double x[], double *f, double c[],
         const void *userdata ){
    struct userdata_type *myuserdata = ( struct userdata_type * ) userdata;
    double p = myuserdata->p;
    *f = pow(x[0],2.0) + pow(x[1],2.0);
    c[0] = x[0] + x[1] - 1.0;
    c[1] = pow(x[0],2.0) + pow(x[1],2.0) - 1.0;
    c[2] = p * pow(x[0],2.0) + pow(x[1],2.0) - p;
    c[3] = pow(x[0],2.0) - x[1];
    c[4] = pow(x[1],2.0) - x[0];
    return 0;
}

// compute the gradient and Jacobian
int gj( int32_t n, int32_t m, int32_t jne, const double x[], double g[],
        double jval[], const void *userdata ){
    struct userdata_type *myuserdata = (struct userdata_type *) userdata;
    double p = myuserdata->p;
    g[0] = 2.0 * x[0];
    g[1] = 2.0 * x[1];
    jval[0] = 1.0;
    jval[1] = 1.0;
    jval[2] = 2.0 * x[0];
    jval[3] = 2.0 * x[1];
    jval[4] = 2.0 * p * x[0];
    jval[5] = 2.0 * x[1];
    jval[6] = 2.0 * x[0];
    jval[7] = - 1.0;
    jval[8] = - 1.0;
    jval[9] = 2.0 * x[1];
    return 0;
}

// compute the Hessian of the Lagrangian
int hl( int32_t n, int32_t m, int32_t hne, const double x[], const double y[],
         double hval[], const void *userdata ){
    struct userdata_type *myuserdata = (struct userdata_type *) userdata;
    double p = myuserdata->p;
    hval[0] = 2.0 - 2.0 * (y[1] + p * y[2] + y[3]);
    hval[1] = 2.0 - 2.0 * (y[1] + y[2] + y[4]);
    return 0;
}

// compute the function and constraint values
int fc_s_64( int64_t n, int64_t m, const float x[], float *f, float c[],
            const void *userdata_s_64 ){
    struct userdata_type_s_64 *myuserdata
      = (struct userdata_type_s_64 *) userdata_s_64;
    float p = myuserdata->p;
    *f = pow(x[0],2.0) + pow(x[1],2.0);
    c[0] = x[0] + x[1] - 1.0;
    c[1] = pow(x[0],2.0) + pow(x[1],2.0) - 1.0;
    c[2] = p * pow(x[0],2.0) + pow(x[1],2.0) - p;
    c[3] = pow(x[0],2.0) - x[1];
    c[4] = pow(x[1],2.0) - x[0];
    return 0;
}

// compute the gradient and Jacobian
int gj_s_64( int64_t n, int64_t m, int64_t jne, const float x[],
            float g[], float jval[], const void *userdata_s_64 ){
    struct userdata_type_s_64 *myuserdata
      = (struct userdata_type_s_64 *) userdata_s_64;
    float p = myuserdata->p;
    g[0] = 2.0 * x[0];
    g[1] = 2.0 * x[1];
    jval[0] = 1.0;
    jval[1] = 1.0;
    jval[2] = 2.0 * x[0];
    jval[3] = 2.0 * x[1];
    jval[4] = 2.0 * p * x[0];
    jval[5] = 2.0 * x[1];
    jval[6] = 2.0 * x[0];
    jval[7] = - 1.0;
    jval[8] = - 1.0;
    jval[9] = 2.0 * x[1];
    return 0;
}

// compute the Hessian of the Lagrangian
int hl_s_64( int64_t n, int64_t m, int64_t hne, const float x[],
            const float y[], float hval[], const void *userdata_s_64 ){
    struct userdata_type_s_64 *myuserdata
      = (struct userdata_type_s_64 *) userdata_s_64;
    float p = myuserdata->p;
    hval[0] = 2.0 - 2.0 * (y[1] + p * y[2] + y[3]);
    hval[1] = 2.0 - 2.0 * (y[1] + y[2] + y[4]);
    return 0;
}