Farm Planning (mpex08)

/***************************************************************/
/*                                                             */
/*          S A S   S A M P L E   L I B R A R Y                */
/*                                                             */
/*    NAME: mpex08                                             */
/*   TITLE: Farm Planning (mpex08)                             */
/* PRODUCT: OR                                                 */
/*  SYSTEM: ALL                                                */
/*   PROCS: OPTMODEL                                           */
/*    DATA:                                                    */
/*                                                             */
/* SUPPORT:                             UPDATE:                */
/*     REF:                                                    */
/*    MISC: Example 08 from the Mathematical Programming       */
/*          Examples book.                                     */
/*                                                             */
/***************************************************************/

data cow_data;
   do age = 0 to 11;
      init_num_cows = 10;
      if age < 2 then do;
         acres_needed = 2/3;
         annual_loss = 0.05;
         bullock_yield = 0;
         heifer_yield = 0;
         milk_revenue = 0;
         grain_req = 0;
         sugar_beet_req = 0;
         labour_req = 10;
         other_costs = 50;
      end;
      else do;
         acres_needed = 1;
         annual_loss = 0.02;
         bullock_yield = 1.1/2;
         heifer_yield = 1.1/2;
         milk_revenue = 370;
         grain_req = 0.6;
         sugar_beet_req = 0.7;
         labour_req = 42;
         other_costs = 100;
      end;
      output;
   end;
run;

data grain_data;
   input group $ acres yield;
   datalines;
group1 20 1.1
group2 30 0.9
group3 20 0.8
group4 10 0.65
;

%let num_years = 5;
%let num_acres = 200;
%let bullock_revenue = 30;
%let heifer_revenue = 40;
%let dairy_cow_selling_age = 12;
%let dairy_cow_selling_revenue = 120;
%let max_num_cows = 130;
%let sugar_beet_yield = 1.5;
%let grain_cost = 90;
%let grain_revenue = 75;
%let grain_labour_req = 4;
%let grain_other_costs = 15;
%let sugar_beet_cost = 70;
%let sugar_beet_revenue = 58;
%let sugar_beet_labour_req = 14;
%let sugar_beet_other_costs = 10;
%let nominal_labour_cost = 4000;
%let nominal_labour_hours = 5500;
%let excess_labour_cost = 1.2;
%let capital_outlay_unit = 200;
%let num_loan_years = 10;
%let annual_interest_rate = 0.15;
%let max_decrease_ratio = 0.50;
%let max_increase_ratio = 0.75;

proc optmodel;
   set AGES;
   num init_num_cows {AGES};
   num acres_needed {AGES};
   num annual_loss {AGES};
   num bullock_yield {AGES};
   num heifer_yield {AGES};
   num milk_revenue {AGES};
   num grain_req {AGES};
   num sugar_beet_req {AGES};
   num cow_labour_req {AGES};
   num cow_other_costs {AGES};
   read data cow_data into AGES=[age]
      init_num_cows acres_needed annual_loss bullock_yield heifer_yield
      milk_revenue grain_req sugar_beet_req cow_labour_req=labour_req
      cow_other_costs=other_costs;

   num num_years = &num_years;
   set YEARS = 1..num_years;
   set YEARS0 = {0} union YEARS;

   var NumCows {AGES union {&dairy_cow_selling_age}, YEARS0} >= 0;
   for {age in AGES} fix NumCows[age,0] = init_num_cows[age];
   fix NumCows[&dairy_cow_selling_age,0] = 0;

   var NumBullocksSold {YEARS} >= 0;
   var NumHeifersSold {YEARS} >= 0;

   set <str> GROUPS;
   num acres {GROUPS};
   num grain_yield {GROUPS};
   var GrainAcres {GROUPS, YEARS} >= 0;

   read data grain_data into GROUPS=[group]
      {year in YEARS} <GrainAcres[group,year].ub=acres>
      grain_yield=yield;
   var GrainBought {YEARS} >= 0;
   var GrainSold {YEARS} >= 0;

   var SugarBeetAcres {YEARS} >= 0;
   var SugarBeetBought {YEARS} >= 0;
   var SugarBeetSold {YEARS} >= 0;

   var NumExcessLabourHours {YEARS} >= 0;
   var CapitalOutlay {YEARS} >= 0;

   num yearly_loan_payment =
      -finance('pmt', &annual_interest_rate, &num_loan_years,
         &capital_outlay_unit);
   print yearly_loan_payment;

   impvar Revenue {year in YEARS} =
      &bullock_revenue * NumBullocksSold[year]
    + &heifer_revenue * NumHeifersSold[year]
    + &dairy_cow_selling_revenue * NumCows[&dairy_cow_selling_age,year]
    + sum {age in AGES} milk_revenue[age] * NumCows[age,year]
    + &grain_revenue * GrainSold[year]
    + &sugar_beet_revenue * SugarBeetSold[year]
   ;
   impvar Cost {year in YEARS} =
      &grain_cost * GrainBought[year]
    + &sugar_beet_cost * SugarBeetBought[year]
    + &nominal_labour_cost
    + &excess_labour_cost * NumExcessLabourHours[year]
    + sum {age in AGES} cow_other_costs[age] * NumCows[age,year]
    + sum {group in GROUPS} &grain_other_costs * GrainAcres[group,year]
    + &sugar_beet_other_costs * SugarBeetAcres[year]
    + sum {y in YEARS: y <= year} yearly_loan_payment * CapitalOutlay[y]
   ;
   impvar Profit {year in YEARS} = Revenue[year] - Cost[year];

   max TotalProfit =
      sum {year in YEARS} (Profit[year]
         - yearly_loan_payment * (num_years - 1 + year) * CapitalOutlay[year]);

   con Num_acres_con {year in YEARS}:
      sum {age in AGES} acres_needed[age] * NumCows[age,year]
    + sum {group in GROUPS} GrainAcres[group,year]
    + SugarBeetAcres[year]
   <= &num_acres;

   con Aging {age in AGES diff {&dairy_cow_selling_age},
              year in YEARS0 diff {num_years}}:
      NumCows[age+1,year+1] = (1 - annual_loss[age]) * NumCows[age,year];

   con NumBullocksSold_def {year in YEARS}:
      NumBullocksSold[year]
    = sum {age in AGES} bullock_yield[age] * NumCows[age,year];

   con NumHeifersSold_def {year in YEARS}:
      NumCows[0,year]
    = sum {age in AGES} heifer_yield[age] * NumCows[age,year]
    - NumHeifersSold[year];

   con Max_num_cows_def {year in YEARS}:
      sum {age in AGES} NumCows[age,year]
   <= &max_num_cows + sum {y in YEARS: y <= year} CapitalOutlay[y];

   impvar GrainGrown {group in GROUPS, year in YEARS} =
      grain_yield[group] * GrainAcres[group,year];
   con Grain_req_def {year in YEARS}:
      sum {age in AGES} grain_req[age] * NumCows[age,year]
   <= sum {group in GROUPS} GrainGrown[group,year]
    + GrainBought[year] - GrainSold[year];

   impvar SugarBeetGrown {year in YEARS} =
      &sugar_beet_yield * SugarBeetAcres[year];
   con Sugar_beet_req_def {year in YEARS}:
      sum {age in AGES} sugar_beet_req[age] * NumCows[age,year]
   <= SugarBeetGrown[year] + SugarBeetBought[year] - SugarBeetSold[year];

   con Labour_req_def {year in YEARS}:
      sum {age in AGES} cow_labour_req[age] * NumCows[age,year]
    + sum {group in GROUPS} &grain_labour_req * GrainAcres[group,year]
    + &sugar_beet_labour_req * SugarBeetAcres[year]
   <= &nominal_labour_hours + NumExcessLabourHours[year];

   con Cash_flow {year in YEARS}:
      Profit[year] >= 0;

   con Final_dairy_cows_range:
      1 - &max_decrease_ratio
   <= (sum {age in AGES: age >= 2} NumCows[age,num_years])
    / (sum {age in AGES: age >= 2} init_num_cows[age])
   <= 1 + &max_increase_ratio;

   solve;

   print NumCows NumBullocksSold NumHeifersSold CapitalOutlay
      NumExcessLabourHours Revenue Cost Profit;
   print GrainAcres;
   print GrainGrown;
   print GrainBought GrainSold SugarBeetAcres SugarBeetGrown SugarBeetBought
      SugarBeetSold;
   print Num_acres_con.body Max_num_cows_def.body Final_dairy_cows_range.body;

   create data sol_data1 from [age]=AGES
      {year in YEARS} <col('NumCows_year'||year)=NumCows[age,year].sol>;

   create data sol_data2 from [group year] GrainAcres GrainGrown;
   create data sol_data3 from [year]
      NumBullocksSold NumHeifersSold CapitalOutlay NumExcessLabourHours
      Revenue Cost Profit GrainBought GrainSold
      SugarBeetAcres SugarBeetGrown SugarBeetBought SugarBeetSold;
quit;