diet_c.c#

/* Copyright 2024, Gurobi Optimization, LLC */

/* Solve the classic diet model, showing how to add constraints
   to an existing model. */

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

int printSolution(GRBmodel* model, int nCategories, int nFoods);


int
main(int   argc,
     char *argv[])
{
  GRBenv   *env   = NULL;
  GRBmodel *model = NULL;
  int       error = 0;
  int       i, j;
  int      *cbeg, *cind, idx;
  double   *cval, *rhs;
  char     *sense;

  /* Nutrition guidelines, based on
     USDA Dietary Guidelines for Americans, 2005
     http://www.health.gov/DietaryGuidelines/dga2005/ */

  const int nCategories = 4;
  char *Categories[] =
    { "calories", "protein", "fat", "sodium" };
  double minNutrition[] = { 1800, 91, 0, 0 };
  double maxNutrition[] = { 2200, GRB_INFINITY, 65, 1779 };

  /* Set of foods */
  const int nFoods = 9;
  char* Foods[] =
    { "hamburger", "chicken", "hot dog", "fries",
      "macaroni", "pizza", "salad", "milk", "ice cream" };
  double cost[] =
    { 2.49, 2.89, 1.50, 1.89, 2.09, 1.99, 2.49, 0.89, 1.59 };

  /* Nutrition values for the foods */
  double nutritionValues[][4] = {
                                  { 410, 24, 26, 730 },
                                  { 420, 32, 10, 1190 },
                                  { 560, 20, 32, 1800 },
                                  { 380, 4, 19, 270 },
                                  { 320, 12, 10, 930 },
                                  { 320, 15, 12, 820 },
                                  { 320, 31, 12, 1230 },
                                  { 100, 8, 2.5, 125 },
                                  { 330, 8, 10, 180 }
                                };

  /* Create environment */
  error = GRBloadenv(&env, "diet.log");
  if (error) goto QUIT;

  /* Create initial model */
  error = GRBnewmodel(env, &model, "diet", nFoods + nCategories,
                      NULL, NULL, NULL, NULL, NULL);
  if (error) goto QUIT;

  /* Initialize decision variables for the foods to buy */
  for (j = 0; j < nFoods; ++j)
  {
    error = GRBsetdblattrelement(model, "Obj", j, cost[j]);
    if (error) goto QUIT;
    error = GRBsetstrattrelement(model, "VarName", j, Foods[j]);
    if (error) goto QUIT;
  }

  /* Initialize decision variables for the nutrition information,
     which we limit via bounds */
  for (j = 0; j < nCategories; ++j)
  {
    error = GRBsetdblattrelement(model, "LB", j + nFoods, minNutrition[j]);
    if (error) goto QUIT;
    error = GRBsetdblattrelement(model, "UB", j + nFoods, maxNutrition[j]);
    if (error) goto QUIT;
    error = GRBsetstrattrelement(model, "VarName", j + nFoods, Categories[j]);
    if (error) goto QUIT;
  }

  /* The objective is to minimize the costs */
  error = GRBsetintattr(model, "ModelSense", GRB_MINIMIZE);
  if (error) goto QUIT;

  /* Nutrition constraints */
  cbeg = malloc(sizeof(int) * nCategories);
  if (!cbeg) goto QUIT;
  cind = malloc(sizeof(int) * nCategories * (nFoods + 1));
  if (!cind) goto QUIT;
  cval = malloc(sizeof(double) * nCategories * (nFoods + 1));
  if (!cval) goto QUIT;
  rhs = malloc(sizeof(double) * nCategories);
  if (!rhs) goto QUIT;
  sense = malloc(sizeof(char) * nCategories);
  if (!sense) goto QUIT;
  idx = 0;
  for (i = 0; i < nCategories; ++i)
  {
    cbeg[i] = idx;
    rhs[i] = 0.0;
    sense[i] = GRB_EQUAL;
    for (j = 0; j < nFoods; ++j)
    {
      cind[idx] = j;
      cval[idx++] = nutritionValues[j][i];
    }
    cind[idx] = nFoods + i;
    cval[idx++] = -1.0;
  }

  error = GRBaddconstrs(model, nCategories, idx, cbeg, cind, cval, sense,
                        rhs, Categories);
  if (error) goto QUIT;

  /* Solve */
  error = GRBoptimize(model);
  if (error) goto QUIT;
  error = printSolution(model, nCategories, nFoods);
  if (error) goto QUIT;

  printf("\nAdding constraint: at most 6 servings of dairy\n");
  cind[0] = 7;
  cval[0] = 1.0;
  cind[1] = 8;
  cval[1] = 1.0;
  error = GRBaddconstr(model, 2, cind, cval, GRB_LESS_EQUAL, 6.0,
                       "limit_dairy");
  if (error) goto QUIT;

  /* Solve */
  error = GRBoptimize(model);
  if (error) goto QUIT;
  error = printSolution(model, nCategories, nFoods);
  if (error) goto QUIT;



QUIT:

  /* Error reporting */

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

  /* Free data */

  free(cbeg);
  free(cind);
  free(cval);
  free(rhs);
  free(sense);

  /* Free model */

  GRBfreemodel(model);

  /* Free environment */

  GRBfreeenv(env);

  return 0;
}

int printSolution(GRBmodel* model, int nCategories, int nFoods)
{
  int error, status, i, j;
  double obj, x;
  char* vname;

  error = GRBgetintattr(model, "Status", &status);
  if (error) return error;
  if (status == GRB_OPTIMAL)
  {
    error = GRBgetdblattr(model, "ObjVal", &obj);
    if (error) return error;
    printf("\nCost: %f\n\nBuy:\n", obj);
    for (j = 0; j < nFoods; ++j)
    {
      error = GRBgetdblattrelement(model, "X", j, &x);
      if (error) return error;
      if (x > 0.0001)
      {
        error = GRBgetstrattrelement(model, "VarName", j, &vname);
        if (error) return error;
        printf("%s %f\n", vname, x);
      }
    }
    printf("\nNutrition:\n");
    for (i = 0; i < nCategories; ++i)
    {
      error = GRBgetdblattrelement(model, "X", i + nFoods, &x);
      if (error) return error;
      error = GRBgetstrattrelement(model, "VarName", i + nFoods, &vname);
      if (error) return error;
      printf("%s %f\n", vname, x);
    }
  }
  else
  {
    printf("No solution\n");
  }

  return 0;
}