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 12/01/25 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.