piecewise_c.c#

/* Copyright 2024, Gurobi Optimization, LLC */

/* This example considers the following separable, convex problem:

     minimize    f(x) - y + g(z)
     subject to  x + 2 y + 3 z <= 4
                 x +   y       >= 1
                 x,    y,    z <= 1

   where f(u) = exp(-u) and g(u) = 2 u^2 - 4 u, for all real u. It
   formulates and solves a simpler LP model by approximating f and
   g with piecewise-linear functions. Then it transforms the model
   into a MIP by negating the approximation for f, which corresponds
   to a non-convex piecewise-linear function, and solves it again.
*/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "gurobi_c.h"

double f(double u) { return exp(-u); }
double g(double u) { return 2 * u * u - 4 * u; }

int
main(int   argc,
     char *argv[])
{
  GRBenv   *env   = NULL;
  GRBmodel *model = NULL;
  int       error = 0;
  double    lb, ub;
  int       npts, i;
  double   *ptu = NULL;
  double   *ptf = NULL;
  double   *ptg = NULL;
  int       ind[3];
  double    val[3];
  int       ismip;
  double    objval;
  double    sol[3];

  /* Create environment */

  error = GRBloadenv(&env, NULL);
  if (error) goto QUIT;

  /* Create a new model */

  error = GRBnewmodel(env, &model, NULL, 0, NULL, NULL, NULL, NULL, NULL);
  if (error) goto QUIT;

  /* Add variables */

  lb = 0.0; ub = 1.0;

  error = GRBaddvar(model, 0, NULL, NULL, 0.0, lb, ub, GRB_CONTINUOUS, "x");
  if (error) goto QUIT;
  error = GRBaddvar(model, 0, NULL, NULL, 0.0, lb, ub, GRB_CONTINUOUS, "y");
  if (error) goto QUIT;
  error = GRBaddvar(model, 0, NULL, NULL, 0.0, lb, ub, GRB_CONTINUOUS, "z");
  if (error) goto QUIT;

  /* Set objective for y */

  error = GRBsetdblattrelement(model, GRB_DBL_ATTR_OBJ, 1, -1.0);
  if (error) goto QUIT;

  /* Add piecewise-linear objective functions for x and z */

  npts = 101;
  ptu = (double *) malloc(npts * sizeof(double));
  ptf = (double *) malloc(npts * sizeof(double));
  ptg = (double *) malloc(npts * sizeof(double));

  for (i = 0; i < npts; i++) {
    ptu[i] = lb + (ub - lb) * i / (npts - 1);
    ptf[i] = f(ptu[i]);
    ptg[i] = g(ptu[i]);
  }

  error = GRBsetpwlobj(model, 0, npts, ptu, ptf);
  if (error) goto QUIT;
  error = GRBsetpwlobj(model, 2, npts, ptu, ptg);
  if (error) goto QUIT;

  /* Add constraint: x + 2 y + 3 z <= 4 */

  ind[0] = 0; ind[1] = 1; ind[2] = 2;
  val[0] = 1; val[1] = 2; val[2] = 3;

  error = GRBaddconstr(model, 3, ind, val, GRB_LESS_EQUAL, 4.0, "c0");
  if (error) goto QUIT;

  /* Add constraint: x + y >= 1 */

  ind[0] = 0; ind[1] = 1;
  val[0] = 1; val[1] = 1;

  error = GRBaddconstr(model, 2, ind, val, GRB_GREATER_EQUAL, 1.0, "c1");
  if (error) goto QUIT;

  /* Optimize model as an LP */

  error = GRBoptimize(model);
  if (error) goto QUIT;

  error = GRBgetintattr(model, "IsMIP", &ismip);
  if (error) goto QUIT;
  error = GRBgetdblattr(model, "ObjVal", &objval);
  if (error) goto QUIT;
  error = GRBgetdblattrarray(model, "X", 0, 3, sol);
  if (error) goto QUIT;

  printf("IsMIP: %d\n", ismip);
  printf("x %g\ny %g\nz %g\n", sol[0], sol[1], sol[2]);
  printf("Obj: %g\n", objval);
  printf("\n");

  /* Negate piecewise-linear objective function for x */

  for (i = 0; i < npts; i++) {
    ptf[i] = -ptf[i];
  }

  error = GRBsetpwlobj(model, 0, npts, ptu, ptf);
  if (error) goto QUIT;

  /* Optimize model as a MIP */

  error = GRBoptimize(model);
  if (error) goto QUIT;

  error = GRBgetintattr(model, "IsMIP", &ismip);
  if (error) goto QUIT;
  error = GRBgetdblattr(model, "ObjVal", &objval);
  if (error) goto QUIT;
  error = GRBgetdblattrarray(model, "X", 0, 3, sol);
  if (error) goto QUIT;

  printf("IsMIP: %d\n", ismip);
  printf("x %g\ny %g\nz %g\n", sol[0], sol[1], sol[2]);
  printf("Obj: %g\n", objval);

QUIT:

  /* Error reporting */

  if (error) {
    printf("ERROR: %s\n", GRBgeterrormsg(env));
    exit(1);
  }

  /* Free data */

  free(ptu);
  free(ptf);
  free(ptg);

  /* Free model */

  GRBfreemodel(model);

  /* Free environment */

  GRBfreeenv(env);

  return 0;
}