Decision Optimization

Decision Optimization

Delivers prescriptive analytics capabilities and decision intelligence to improve decision-making.


#Analytics
#DecisionOptimization
#DecisionOptimization
 View Only
  • 1.  consuming from most depleted resource first

    Posted 05/12/14 10:05 AM

    Originally posted by: Rodders


    Hi there.

    I'm trying to implement a constraint that some of you have probably dealt with. I wonder if I'm on the right path here.

    I have an ATM with 3 cassettes of the same type of note. I have some cash that I can use to replenish the ATM. That cash is the end balance at the vault on the previous day. Each cassette will have a balance at the end of the previous day, as well. I have the forecasted consumption on the ATM, and I need to assign that consumption to each cassette on the order of most depleted. So, if cassettes 1, 2, 3 had 20, 26 and 15 dollars in them, and the day's forecasted consumption were 23 dollars, I would want to consume 15 dollars from cassette 3 and subsequently consume 8 dollars from cassette 1.

    If I decide to replenish any cassette I have to ship back to the vault the amount currently existing in the cassette. The replenishment happens at the end of the day.

    Right before the "total_cost" decision expression I explain the errors I'm getting.

    If someone could explain to me why I get those errors, that would be great. But my main question is - am I implementing the consumption constraint correctly?

    Thank you for any help you can provide.

    In the model below, day 1 is just to initialize the cash balances at the vault and at the cassettes. I receive cash regularly at the vault.

    Regards.

     

    /*********************************************
     * OPL 12.5 Model
     *********************************************/

    float bignum = 10000.0;

    setof (int) days = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    setof (int) cassettes = { 1, 2, 3 };

    float IRR = 0.0002;

    float var_cost = 0.0004;

    float residual_cost = 0.0003;

    float BB[cassettes] = [ 24.0, 21.0, 33.0 ];

    float RHS[days] = [ 0.0, 25.0, 23.0, 16.0, 5.0, 26.0, 32.0, 12.0, 24.0 ];

    float vault_ins[days] = [ 70.0, 0.0, 69.0, 0.0, 0.0, 30.0, 0.0, 35.0, 0.0 ];

    float max_SI[cassettes] = [ 40.0, 40.0, 40.0 ];

    dvar boolean consumed[cassettes, days];

    dvar boolean depleted[cassettes, days];

    dvar boolean replenished[cassettes, days];

    dvar float+ consumption[cassettes, days];

    dvar float+ EB[cassettes, days];
     
    dvar float+ SI[cassettes, days];

    dvar float+ SO[cassettes, days];
     
    dvar float+ tot_SI[days];

    dvar float+ tot_SO[days];

    dvar float+ vault_EB[days];
     

    // when I try to run the model, it tells me that Name "d" doesn't exist, and underlines the "d" in tot_SI[d].

    // if I simplify the total_cost as  sum (d in days) Vault_EB[d], it shows the error CPLEX Error 5002: Q in 'q1' is not positive semi-definite

    // However, this is clearly not a quadratic problem...
    dexpr float total_cost = sum (d in days)
                vault_EB[d] * IRR + (tot_SI[d] + tot_SO[d]) * var_cost + tot_SO[d] * residual_cost;



    constraint total_SI;
    constraint total_SO;
    constraint max_total_SI;
    constraint vault_end_bal1;
    constraint vault_end_bal2;
    constraint add_up;
    constraint consume;
    constraint was_consumed;
    constraint was_depleted;
    constraint sorted_cons;
    constraint max_SI_rule;
    constraint calc_SO;
    constraint calc_EB1;
    constraint calc_EB2;
    constraint did_replenish;
    constraint no_SO_if_no_replenish;
    constraint SO_all_residual;
    /*constraint ;
    constraint ;
    constraint ;
    constraint ;
    */


    minimize total_cost;

    subject to {
     
      total_SI =
          forall (d in days)
            tot_SI[d] == sum (c in cassettes) SI[c, d];
     
      total_SO =
          forall (d in days)
            tot_SO[d] == sum (c in cassettes) SO[c, d];

    // can't send more money to ATM than what was available at the vault on the previous night  
      max_total_SI =
          forall (d in days diff {1})
            tot_SI[d] <= vault_EB[d-1];
            
    // putting an initial balance at the vault
      vault_end_bal1 =
            vault_EB[1] == vault_ins[1];

    // tot_SI is the sum of replenishments for all cassettes
    // tot_SO is the sum of cash coming back from all cassettes
      vault_end_bal2 =
          forall (d in days diff {1})
            vault_EB[d] == vault_EB[d-1] - tot_SI[d] + tot_SO[d];

    // the withdrawals from all cassettes have to add up to the expected day's withdrawals
      add_up =
        forall (d in days)
            sum (c in cassettes) consumption[c, d] == RHS[d];

    // one can't withdraw more from a cassette than what was available at the previous night
    // I'm assuming there will not be withdrawals against a cassette that was replenished
      consume =
        forall (c in cassettes, d in days diff {1})
            consumption[c, d] <= EB[c, d-1];

    // turning the boolean "consumed" on
      was_consumed =
        forall (c in cassettes, d in days diff {1})
          consumption[c, d] <= consumed[c, d] * bignum;
     
     // turning the boolean "depleted" on
      was_depleted =
        forall (c in cassettes, d in days diff {1})
          EB[c, d-1] - consumption[c, d] <= (1 - depleted[c, d]) * bignum;
     
     // this is the key constraint: consumption occurs from cassettes in the ascending order
     // of how much was in each cassette on the previous night
      sorted_cons =
        forall (c1 in cassettes, c2 in cassettes, d in days diff {1})
          EB[c1, d-1] - EB[c2, d-1] <= (depleted[c2, d] - consumed[c1, d] + 1) * bignum;


      max_SI_rule =
           forall (c in cassettes, d in days diff {1})
             SI[c, d] <= max_SI[c];

    // SO is the residual amount in a cassette when it gets replenished
    // it can be zero (if the cassette was depleted)
    // it can't be higher than what was available on the previous night minus the day's consumption
      calc_SO =
          forall (c in cassettes, d in days diff {1})
            SO[c, d] <= EB[c, d-1] - consumption[c, d];

    // putting an initial balance in the cassettes
      calc_EB1 =
          forall (c in cassettes)
            EB[c, 1] == BB[c];
            
    // conservation of total cash flows
      calc_EB2 =
          forall (c in cassettes, d in days diff {1})
            EB[c, d] == EB[c, d-1] - consumption[c, d] + SI[c, d] - SO[c, d];
     
    // turning the "replenished" boolean on
      did_replenish =
          forall (c in cassettes, d in days diff {1})
          SI[c, d] <= replenished[c, d] * bignum;

    // if SI = 0, SO has to be zero
    // also, one does not remove cash from a cassette      
      no_SO_if_no_replenish =
          forall (c in cassettes, d in days diff {1})
           SO[c, d] <= SI[c, d];

    // SO has to be the residual, in case there was a replenishment
    // residual is calculated as previous balance minus the day's consumption       
      SO_all_residual =
          forall (c in cassettes, d in days diff {1})
           SO[c, d] == replenished[c, d] * (EB[c, d-1] - consumption[c, d]);

    }

    execute write_results {

      var fd = new IloOplOutputFile("shipments.txt");

      for (var c in cassettes) {
         for (var d in DAYs) {
            fd.write(c);
            fd.write("   ");
            fd.write(d);
            fd.write("   EB = ");
            fd.write(EB[c][d]);
            fd.write("   cons = ");
            fd.write(consumption[c][d]);
            fd.write("   SI = ");
            fd.write(SI[c][d]);
            fd.write("   SO = ");
            fd.write(SO[c][d]);
         }
      }
    }   


     
     
            


     


    #CPLEXOptimizers
    #DecisionOptimization


  • 2.  Re: consuming from most depleted resource first

    Posted 05/13/14 01:29 AM

    I did not look at your replenishment constraints but at least your syntax errors for the total_cost expression are easy to fix: You are lacking parentheses around the terms you are summing up. What you need to write is:

    dexpr float total_cost = sum (d in days)
      (vault_EB[d] * IRR + (tot_SI[d] + tot_SO[d]) * var_cost + tot_SO[d] * residual_cost);
    

    Note that parentheses at the beginning and end of the second line. Without those parentheses the sum operator only applies to the vault_EB[d]*IRR term and d is not defined in the remaining terms.


    #CPLEXOptimizers
    #DecisionOptimization


  • 3.  Re: consuming from most depleted resource first

    Posted 05/13/14 01:42 PM

    Originally posted by: Rodders


    Thank you, Daniel.

    I won't forget it, from now on.

     


    #CPLEXOptimizers
    #DecisionOptimization