Decision Optimization

 View Only

 Remaining Nodes Keep Expanding While The Incumbent Stay The Same on ILOG CPLEX

Najwa Permata Hadi's profile image
Najwa Permata Hadi posted Mon December 01, 2025 11:08 PM
For practice, I tried to model the mathematical formulation from this paper (https://www.sciencedirect.com/science/article/pii/S095741742502977X) using CPLEX.
Below is my model code:
range I = 1..4;    // crops
range J = 1..4;   // farms
range A = 1..40;    // trays
range T = 1..10;   // periods

// Parameter
int demand_lower[I][T] = ...;
int demand_upper[I][T] = ...;
int initial_avail_tray[J][A] = ...;
int plots[J][A] = ...;
float yield_crop[I][J] = ...;
float yield_mult_normal  = ...;
float yield_mult_early   = ...;
float yield_mult_late    = ...;
int max_vehicle_cap = ...;
int min_load_trans  = ...;
int sprout_cost[I] = ...;
int fertilizer_cost[J][A] = ...;
int harvest_cost = ...;
int transport_cost[J] = ...;
int selling_price[I] = ...;

// Decision Variable
dvar float+ total_harvest[J][T];
dvar float+ total_harvest_early[I][J][A][T];
dvar float+ total_harvest_normal[I][J][A][T];
dvar float+ total_harvest_late[I][J][A][T];
dvar boolean availability_of_tray[J][A][T];
dvar int+ tray_harvested[J][T];
dvar boolean plant_decision[I][J][A][T];
dvar boolean harvest_early_decision[I][J][A][T];
dvar boolean harvest_normal_decision[I][J][A][T];
dvar boolean harvest_late_decision[I][J][A][T];
dvar boolean transport_decision[J][T];

// Objective
maximize
    sum(i in I, j in J, a in A, t in T)
    (
        selling_price[i] * (
        total_harvest_early[i][j][a][t] +
        total_harvest_normal[i][j][a][t] +
        total_harvest_late[i][j][a][t])
    ) // Revenue Harvest
    
    - sum(i in I, j in J, a in A, t in T)
    (
        sprout_cost[i] * plant_decision[i][j][a][t] * plots[j][a]
    ) // Sprout Cost
    
    - sum(i in I, j in J, a in A, t in T)
    (
        fertilizer_cost[j][a] * plant_decision[i][j][a][t]
    ) // Fertilizer Cost
    
    - sum(j in J, t in T)
    (
        harvest_cost * tray_harvested[j][t]
    ) // Harvest Cost

    - sum(j in J, t in T)
    (
        transport_cost[j] * transport_decision[j][t]
    ); //Transport Cost

//Constrains
subject to {

    // Demand lower bound
    forall(i in I, t in T)
      sum(j in J, a in A)
        (total_harvest_early[i][j][a][t] +
        total_harvest_normal[i][j][a][t] +
        total_harvest_late[i][j][a][t]) >= demand_lower[i][t];

    // Demand upper bound
    forall(i in I, t in T)
      sum(j in J, a in A)
        (total_harvest_early[i][j][a][t] +
        total_harvest_normal[i][j][a][t] +
        total_harvest_late[i][j][a][t]) <= demand_upper[i][t];

    // Constrain Plant and Harvest Link
    forall(j in J, a in A, t in 1..7)
      harvest_early_decision[1][j][a][t+1] +
      harvest_normal_decision[1][j][a][t+2] +
      harvest_late_decision[1][j][a][t+3] == plant_decision[1][j][a][t];

    forall(j in J, a in A, t in 1..7)
      harvest_early_decision[2][j][a][t+1] +
      harvest_normal_decision[2][j][a][t+2] +
      harvest_late_decision[2][j][a][t+3] == plant_decision[2][j][a][t];

    forall(j in J, a in A, t in 1..6)
      harvest_early_decision[3][j][a][t+2] +
      harvest_normal_decision[3][j][a][t+3] +
      harvest_late_decision[3][j][a][t+4] == plant_decision[3][j][a][t];
    
    forall(j in J, a in A, t in 1..6)
      harvest_early_decision[4][j][a][t+2] +
      harvest_normal_decision[4][j][a][t+3] +
      harvest_late_decision[4][j][a][t+4] == plant_decision[4][j][a][t];

    // Quantification
    forall(i in I, j in J, a in A, t in T)
      total_harvest_early[i][j][a][t] == 
      harvest_early_decision[i][j][a][t] * plots[j][a] * yield_crop[i][j] * yield_mult_early;

    forall(i in I, j in J, a in A, t in T)
      total_harvest_normal[i][j][a][t] == 
        harvest_normal_decision[i][j][a][t] * plots[j][a] * yield_crop[i][j] * yield_mult_normal;
    
    forall(i in I, j in J, a in A, t in T)
      total_harvest_late[i][j][a][t] == 
      harvest_late_decision[i][j][a][t] * plots[j][a] * yield_crop[i][j] * yield_mult_late;

    // Tray Capacity
    forall(j in J, a in A)
      availability_of_tray[j][a][1] == initial_avail_tray[j][a];
    
    forall(j in J, a in A, t in T: t > 1)
      availability_of_tray[j][a][t] ==
        availability_of_tray[j][a][t-1] -
        sum(i in I) plant_decision[i][j][a][t] +
        sum(i in I) (harvest_early_decision[i][j][a][t] +
        harvest_normal_decision[i][j][a][t] +
        harvest_late_decision[i][j][a][t]);

    // Total harvest
    forall(j in J, t in T)
      total_harvest[j][t] ==
        sum(i in I, a in A)
        (
            total_harvest_early[i][j][a][t] +
            total_harvest_normal[i][j][a][t] +
            total_harvest_late[i][j][a][t]
        );
    
    // Transport Cost
    forall(j in J, t in T)
      {
          total_harvest[j][t] <= max_vehicle_cap * transport_decision[j][t];
          total_harvest[j][t] >= min_load_trans * transport_decision[j][t];
    }
    
    // Tray Harvested
    forall(j in J, t in T)
        tray_harvested[j][t] ==
        sum(i in I, a in A)
        (
            harvest_early_decision[i][j][a][t] +
            harvest_normal_decision[i][j][a][t] +
            harvest_late_decision[i][j][a][t]
        );


};

And here's the .dat file:

demand_lower = [
    [0, 0, 0, 0, 0, 30, 30, 30, 30, 30], // GO
    [0, 0, 0, 0, 0, 30, 30, 30, 30, 30], // BC
    [0, 0, 0, 0, 0, 20, 20, 20, 20, 20], // RO
    [0, 0, 0, 0, 0, 30, 30, 30, 30, 30]  // C
];

demand_upper = [
    [0, 0, 0, 0, 0, 120, 120, 120, 120, 120], // GO
    [0, 0, 0, 0, 0, 120, 120, 120, 120, 120], // BC
    [0, 0, 0, 0, 0, 80, 80, 80, 80, 80],  // RO
    [0, 0, 0, 0, 0, 120, 120, 120, 120, 120]  // C
];

initial_avail_tray = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], //KK 1
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], //KK 2
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], //KS 1
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]  //KS 5
];

plots = [
 [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], //KK 1 
 [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], //KK 2
 [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0], //KS 1
 [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], //KS 5
];

yield_crop = [
    [0.25, 0.25, 0.30, 0.20], //GO
    [0.20, 0.20, 0.15, 0.15], //BC
    [0.25, 0.20, 0.20, 0.30], //RO
    [0.25, 0.20, 0.25, 0.30] //C
];

yield_mult_normal = 1.0;
yield_mult_early  = 0.8;
yield_mult_late   = 1.0;

max_vehicle_cap = 100;
min_load_trans  = 20;

fertilizer_cost = [
  [200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 0, 0, 0, 0, 0],
  [200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350],
];

harvest_cost   = 30;

sprout_cost    = [10, 10, 15, 12];
transport_cost = [1000, 1000, 1500, 1500];
selling_price  = [220, 220, 380, 210];

It has been running for 20 hours, and the remaining nodes keep increasing while the incumbent value stays the same. Is there something wrong with how I modeled it in CPLEX that is causing it to run so slowly?

Note: I still don’t know how to include the dummy period in my model, so I set the demand for the first five weeks to zero. I also tried to reduce the problem size by using only 4 farms and 10 periods.