Market Sharing (mpex13)

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

data retailer_data;
   input region oil delivery spirit growth $;
   datalines;
1  9  11  34 A
1 13  47 411 A
1 14  44  82 A
1 17  25 157 B
1 18  10   5 A
1 19  26 183 A
1 23  26  14 B
1 21  54 215 B
2  9  18 102 B
2 11  51  21 A
2 17  20  54 B
2 18 105   0 B
2 18   7   6 B
2 17  16  96 B
2 22  34 118 A
2 24 100 112 B
2 36  50 535 B
2 43  21   8 B
3  6  11  53 B
3 15  19  28 A
3 15  14  69 B
3 25  10  65 B
3 39  11  27 B
;

data division_data;
   input target;
   datalines;
0.40
0.60
;

%let tolerance = 0.05;

proc optmodel;
   set RETAILERS;
   num region {RETAILERS};
   num oil {RETAILERS};
   num delivery {RETAILERS};
   num spirit {RETAILERS};
   str growth {RETAILERS};
   read data retailer_data into RETAILERS=[_N_]
      region oil delivery spirit growth;

   set REGIONS init {};
   set RETAILERS_region {REGIONS} init {};
   num r;
   set <str> GROUPS init {};
   set RETAILERS_group {GROUPS} init {};
   str g;
   for {retailer in RETAILERS} do;
      r = region[retailer];
      REGIONS = REGIONS union {r};
      RETAILERS_region[r] = RETAILERS_region[r] union {retailer};
      g = growth[retailer];
      GROUPS = GROUPS union {g};
      RETAILERS_group[g] = RETAILERS_group[g] union {retailer};
   end;

   set DIVISIONS;
   num target {DIVISIONS};
   read data division_data into DIVISIONS=[_N_] target;

   num tolerance = &tolerance;

   var Assign {RETAILERS, DIVISIONS} binary;

   con Assign_con {retailer in RETAILERS}:
      sum {division in DIVISIONS} Assign[retailer,division] = 1;

   set CATEGORIES = {'delivery','spirit'}
      union (setof {reg in REGIONS} 'oil'||reg)
      union (setof {group in GROUPS} 'growth'||group);
   var MarketShare {CATEGORIES, DIVISIONS};
   var Surplus     {CATEGORIES, DIVISIONS} >= 0 <= tolerance;
   var Slack       {CATEGORIES, DIVISIONS} >= 0 <= tolerance;

   min Objective1 =
      sum {category in CATEGORIES, division in DIVISIONS}
         (Surplus[category,division] + Slack[category,division]);

   con Delivery_con {division in DIVISIONS}:
      MarketShare['delivery',division]
    = (sum {retailer in RETAILERS} delivery[retailer] *
         Assign[retailer,division])
         / (sum {retailer in RETAILERS} delivery[retailer]);

   con Spirit_con {division in DIVISIONS}:
      MarketShare['spirit',division]
    = (sum {retailer in RETAILERS} spirit[retailer] *
         Assign[retailer,division])
         / (sum {retailer in RETAILERS} spirit[retailer]);

   con Oil_con {reg in REGIONS, division in DIVISIONS}:
      MarketShare['oil'||reg,division]
    = (sum {retailer in RETAILERS_region[reg]}
         oil[retailer] * Assign[retailer,division])
         / (sum {retailer in RETAILERS_region[reg]} oil[retailer]);

   con Growth_con {group in GROUPS, division in DIVISIONS}:
      MarketShare['growth'||group,division]
    = (sum {retailer in RETAILERS_group[group]} Assign[retailer,division])
         / card(RETAILERS_group[group]);

   con Abs_dev_con {category in CATEGORIES, division in DIVISIONS}:
      MarketShare[category,division]
    - Surplus[category,division] + Slack[category,division]
    = target[division];

   num sum_abs_dev =
      sum {category in CATEGORIES, division in DIVISIONS}
         abs(MarketShare[category,division].sol - target[division]);
   num max_abs_dev =
      max {category in CATEGORIES, division in DIVISIONS}
         abs(MarketShare[category,division].sol - target[division]);

   solve obj Objective1;
   print sum_abs_dev max_abs_dev;
   print Assign;
   print MarketShare Surplus Slack;

   var MinMax >= 0 init max_abs_dev;
   min Objective2 = MinMax;
   con MinMax_con {category in CATEGORIES, division in DIVISIONS}:
      MinMax >= Surplus[category,division] + Slack[category,division];

   solve obj Objective2 with MILP / primalin;
   print sum_abs_dev max_abs_dev;
   print Assign;
   print MarketShare Surplus Slack;
quit;