Select Your Region
Americas
Europe
Middle East & Africa
Asia Pacific
/***************************************************************/ /* */ /* S A S S A M P L E L I B R A R Y */ /* */ /* NAME: clp12 */ /* TITLE: Scheduling a Major Basketball Conference (clp12) */ /* PRODUCT: OR */ /* SYSTEM: ALL */ /* KEYS: OR */ /* PROCS: CLP, GANTT, SQL */ /* DATA: */ /* */ /* SUPPORT: UPDATE: */ /* REF: */ /* MISC: Example 12 from the CLP Procedure chapter of the */ /* Constraint Programming book. */ /* */ /***************************************************************/ /****************************************************************/ /* First, find all possible patterns. Consider only time */ /* constraints at this point. A pattern should be suitable */ /* for any team. Do not consider individual teams yet. */ /****************************************************************/ %macro patterns(); proc clp out=all_patterns findall; /* For date 1 to 18. */ %do j = 1 %to 18; var h&j = [0, 1]; /* home */ var a&j = [0, 1]; /* away */ var b&j = [0, 1]; /* bye */ /* A team is either home, away, or bye. */ lincon h&j + a&j + b&j=1; %end; /*------------------------------------------------------------*/ /* Criterion 1 - Mirroring Scheme */ /*------------------------------------------------------------*/ /* The dates are grouped into pairs (j, j1), such that each */ /* team plays the same opponent on dates j and j1. */ /* A home game on date j will be an away game on date j1 */ %do j = 1 %to 18; %do j1 = %eval(&j+1) %to 18; %if ( &j=1 and &j1=8 ) or ( &j=2 and &j1=9 ) or ( &j=3 and &j1=12 ) or ( &j=4 and &j1=13 ) or ( &j=5 and &j1=14 ) or ( &j=6 and &j1=15 ) or ( &j=7 and &j1=16 ) or ( &j=10 and &j1=17 ) or ( &j=11 and &j1=18 ) %then lincon h&j = a&j1, a&j = h&j1, b&j = b&j1;; %end; %end; /*------------------------------------------------------------*/ /* Criterion 2 - Initial and Final Home and Away Games */ /*------------------------------------------------------------*/ /* Every team must play home on at least one of the first three dates. */ lincon h1 + h2 + h3 >= 1; /* Every team must play home on at least one of the last three dates. */ lincon h16 + h17 + h18 >= 1; /* No team can play away on both last two dates. */ lincon a17 + a18 < 2; /*------------------------------------------------------------*/ /* Criterion 3 - Home/Away/Bye Pattern */ /*------------------------------------------------------------*/ %do j = 1 %to 16; /* No team can have more than two away matches in a row.*/ lincon a&j + a%eval(&j+1) + a%eval(&j+2) < 3; /* No team can have more than two home matches in a row.*/ lincon h&j + h%eval(&j+1) + h%eval(&j+2) < 3; %end; /* No team can have more than three away matches or byes in a row.*/ %do j = 1 %to 15; lincon a&j + b&j + a%eval(&j+1) + b%eval(&j+1) + a%eval(&j+2) + b%eval(&j+2) + a%eval(&j+3) + b%eval(&j+3) < 4; %end; /* No team can have more than four home matches or byes in a row.*/ %do j = 1 %to 14; lincon h&j + b&j + h%eval(&j+1) + b%eval(&j+1) + h%eval(&j+2) + b%eval(&j+2) + h%eval(&j+3) + b%eval(&j+3) + h%eval(&j+4) + b%eval(&j+4) < 5; %end; /*------------------------------------------------------------*/ /* Criterion 4 - Weekend Pattern */ /*------------------------------------------------------------*/ /* Each team plays four weekends at home. */ lincon 0 %do j = 2 %to 18 %by 2; +h&j %end; =4; /* Each team plays four weekends away. */ lincon 0 %do j = 2 %to 18 %by 2; +a&j %end; =4; /* Each team has 1 weekend with a bye */ lincon 0 %do j = 2 %to 18 %by 2; +b&j %end; =1; /*------------------------------------------------------------*/ /* Criterion 5 - First Weekends */ /*------------------------------------------------------------*/ /* Each team must have home games or byes on at least two */ /* of the first five weekends. */ lincon 0 %do j = 2 %to 10 %by 2; + h&j + b&j %end; >=2; /*------------------------------------------------------------*/ /* Criterion 9 - (Partial) */ /*------------------------------------------------------------*/ /* The team with a bye in date 1 does not play away on the */ /* last date or home in date 17 (Wake) */ /* The team with a bye in date 16 does not play away in */ /* date 18 (Duke) */ lincon b1 + a18 < 2, b1 + h17 < 2, b16 + a18 < 2; run; %mend; %patterns; /*****************************************************************/ /* Determine all possible "pattern sets" considering only time */ /* constraints. */ /* Individual teams are not considered at this stage. */ /* xi - binary variable indicates pattern i is in pattern set */ /*****************************************************************/ %macro pattern_sets(); data _null_; set all_patterns; %do i=1 %to 38; if _n_=&i then do; %do j=1 %to 18; call symput("h&i._&j", put(h&j,best.)); call symput("a&i._&j", put(a&j,best.)); call symput("b&i._&j", put(b&j,best.)); %end; end; %end; run; proc clp out=pattern_sets findall; /* xi=1 if pattern i belongs to pattern set */ var (x1-x38)= [0, 1]; /* Exactly nine patterns per patterns set */ lincon 0 %do i = 1 %to 38; + x&i %end;=9; /* time slot constraints */ %do j = 1 %to 18; /* Four home games per time slot */ lincon 0 %do i = 1 %to 38; + &&h&i._&j*x&i %end; =4; /* Four away games per time slot */ lincon 0 %do i = 1 %to 38; + &&a&i._&j*x&i %end; =4; /* One bye per time slot */ lincon 0 %do i = 1 %to 38; + &&b&i._&j*x&i %end; =1; %end; /* Exclude pattern pairs that do not support a meeting date */ %do i = 1 %to 38; %do i1 = %eval(&i+1) %to 38; %let count=0; %do j=1 %to 18; %if ( (&&h&i._&j=0 or &&a&i1._&j=0) and (&&a&i._&j=0 or &&h&i1._&j=0)) %then %do; %let count=%eval(&count+1); %end; %end; %if (&count=18) %then %do; lincon x&i+x&i1<=1; %end; %end; %end; run; %mend; %pattern_sets; /*********************************************************************/ /* Assign individual teams to pattern set k */ /* Teams: 1 Clem, 2 Duke, 3 FSU, 4 GT, 5 UMD, 6 UNC, 7 NCSU, 8 UVA, */ /* 9 Wake */ /*********************************************************************/ %macro timetable(k); proc clp out=ACC_ds_&k varselect=minrmaxc findall; %do j = 1 %to 18; /* alpha(i,j): Team i's opponent on date j ( 0 = bye ). */ %do i = 1 %to 9; var alpha&i._&j = [0, 9]; %end; /* Timetable constraint 1 */ /* Opponents in a time slot must be distinct */ alldiff ( %do i = 1 %to 9; alpha&i._&j %end; ); /* Timetable constraint 2 */ %do i = 1 %to 9; %do i1 = 1 %to 9; /* indicates if teams i and i1 play in time slot j */ var X&i._&i1._&j = [0, 1]; reify X&i._&i1._&j: (alpha&i._&j = &i1); /* team i plays i1 iff team i1 plays i */ %if (&i1 > &i ) %then %do; lincon X&i._&i1._&j = X&i1._&i._&j; %end; %end; %end; %end; /* Mirroring Scheme at team level. */ /* The dates are grouped into pairs (j, j1) such that each */ /* team plays the same opponent in dates j and j1. */ /* One of these should be a home game for each team. */ %do i = 1 %to 9; %do j = 1 %to 18; %do j1 = %eval(&j+1) %to 18; %if ( &j=1 and &j1=8 ) or ( &j=2 and &j1=9 ) or ( &j=3 and &j1=12 ) or ( &j=4 and &j1=13 ) or ( &j=5 and &j1=14 ) or ( &j=6 and &j1=15 ) or ( &j=7 and &j1=16 ) or ( &j=10 and &j1=17 ) or ( &j=11 and &j1=18 ) %then %do; lincon alpha&i._&j=alpha&i._&j1, /* H and A are matrices that indicate home */ /* and away games */ H&i._&j=A&i._&j1, H&i._&j1=A&i._&j; %end; %end; %end; %end; /* Timetable constraint 3 */ /* Each team plays every other team twice */ %do i = 1 %to 9; %do i1 = 1 %to 9; %if &i1 ne &i %then %do; lincon 0 %do j = 1 %to 18; + X&i._&i1._&j %end; = 2; %end; %end; %end; /* Timetable constraint 4 */ /* Teams do not play against themselves */ %do j = 1 %to 18; %do i = 1 %to 9; lincon alpha&i._&j<>&i; lincon X&i._&i._&j = 0; /* redundant */ %end; %end; /* Timetable constraint 5 */ /* Setup Bye Matrix */ /* alpha&i._&j=0 means team &i has a bye on date &j. */ %do j = 1 %to 18; %do i = 1 %to 9; var B&i._&j = [0, 1]; /*Bye matrix*/ reify B&i._&j: ( alpha&i._&j = 0 ); %end; %end; /* Timetable constraint 6 */ /* alpha&i._&j=&i1 implies teams &i and &i1 play on date &j */ /* It must be a home game for one, away game for the other */ %do j = 1 %to 18; %do i = 1 %to 9; %do i1 = 1 %to 9; /* reify control variables.*/ var U&i._&i1._&j = [0, 1] V&i._&i1._&j = [0, 1]; /* if &i is home and &i1 is away. */ reify U&i._&i1._&j: ( H&i._&j + A&i1._&j = 2); /* if &i1 is home and &i is away. */ reify V&i._&i1._&j: ( A&i._&j + H&i1._&j = 2); /* Necessary condition if &i plays &i1 on date j */ lincon X&i._&i1._&j <= U&i._&i1._&j + V&i._&i1._&j; %end; %end; %end; /* Timetable constraint 7 */ /* Each team must be home, away or have a bye on a given date */ %do j = 1 %to 18; %do i = 1 %to 9; /* Team &i is home (away) at date &j. */ var H&i._&j = [0, 1] A&i._&j = [0, 1]; lincon H&i._&j + A&i._&j + B&i._&j = 1; %end; %end; %do i = 1 %to 9; %do i1 = %eval(&i+1) %to 9; /* Timetable constraint 8 */ /*-------------------------------------------------------*/ /* Criterion 6 - Rival Matches */ /*-------------------------------------------------------*/ /* The final weekend is reserved for 'rival games' */ /* unless the team plays FSU or has a bye */ %if ( &i=1 and &i1=4 ) or ( &i=2 and &i1=6 ) or ( &i=5 and &i1=8 ) or ( &i=7 and &i1=9 ) %then %do; lincon X&i._&i1._18 + B&i._18 + X&i._3_18 = 1; /* redundant */ lincon X&i1._&i._18 + B&i1._18 + X&i1._3_18 = 1; %end; /* Timetable constraint 9 */ /*-------------------------------------------------------*/ /* Criterion 7 - Popular Matches */ /*-------------------------------------------------------*/ /* The following pairings are specified to occur at */ /* least once in February. */ %if ( &i=2 and &i1=4 ) or ( &i=2 and &i1=9 ) or ( &i=4 and &i1=6 ) or ( &i=6 and &i1=9 ) %then %do; lincon 0 %do j = 11 %to 18; + X&i._&i1._&j %end; >= 1; /* redundant */ lincon 0 %do j = 11 %to 18; + X&i1._&i._&j %end; >= 1; %end; %end; %end; /* Timetable constraint 10 */ /*-------------------------------------------------------*/ /* Criterion 8 - Opponent Sequence */ /*-------------------------------------------------------*/ %do i = 1 %to 9; /* No team plays two consecutive away dates against */ /* Duke (2) and UNC (6) */ %do j = 1 %to 17; var Q&i._26_&j = [0, 1] P&i._26_&j = [0, 1]; reify Q&i._26_&j: ( X&i._2_&j + X&i._6_&j = 1 ); reify P&i._26_&j: ( X&i._2_%eval(&j+1) + X&i._6_%eval(&j+1) =1 ); lincon Q&i._26_&j + A&i._&j + P&i._26_&j + A&i._%eval(&j+1) < 4; %end; /* No team plays three consecutive dates against */ /* Duke(2), UNC(6) and Wake(9). */ %do j = 1 %to 16; var L&i._269_&j = [0, 1] M&i._269_&j = [0, 1] N&i._269_&j = [0, 1]; reify L&i._269_&j: ( X&i._2_&j + X&i._6_&j + X&i._9_&j = 1 ); reify M&i._269_&j: ( X&i._2_%eval(&j+1) + X&i._6_%eval(&j+1) + X&i._9_%eval(&j+1) =1 ); reify N&i._269_&j: ( X&i._2_%eval(&j+2) + X&i._6_%eval(&j+2) + X&i._9_%eval(&j+2) =1 ); lincon L&i._269_&j + M&i._269_&j + N&i._269_&j < 3; %end; %end; /* Timetable constraint 11 */ /*-------------------------------------------------------*/ /* Criterion 9 - Idiosyncratic Constraints */ /*-------------------------------------------------------*/ /* UNC plays Duke in date 11 and 18 */ lincon alpha6_11 = 2 ; lincon alpha6_18 = 2 ; /* UNC plays Clem in the second date. */ lincon alpha6_2 = 1 ; /* Duke has a bye in date 16. */ lincon B2_16 = 1 ; /* Wake does not play home in date 17. */ lincon H9_17 = 0 ; /* Wake has a bye in the first date. */ lincon B9_1 = 1 ; /* Clem, Duke, UMD and Wake do not play away in the last date. */ lincon A1_18 = 0 ; lincon A2_18 = 0 ; lincon A5_18 = 0 ; lincon A9_18 = 0 ; /* Clem, FSU, and GT do not play away in the first date. */ lincon A1_1 = 0 ; lincon A3_1 = 0 ; lincon A4_1 = 0 ; /* FSU and NCSU do not have a bye in the last date. */ lincon B3_18 = 0 ; lincon B7_18 = 0 ; /* UNC does not have a bye in the first date. */ lincon B6_1 = 0 ; /* Timetable constraint 12 */ /*-------------------------------------------------------*/ /* Match teams with patterns. */ /*-------------------------------------------------------*/ %do i = 1 %to 9; /* For each team */ var p&i=[1,9]; %do j=1 %to 18; /* For each date */ element ( p&i, (&&col&k._h_&j), H&i._&j ) ( p&i, (&&col&k._a_&j), A&i._&j ) ( p&i, (&&col&k._b_&j), B&i._&j ); %end; %end; run; %mend; /**************************************************************/ /* Try all possible pattern sets to find all valid schedules. */ /**************************************************************/ %macro find_schedules; proc transpose data=pattern_sets out=trans_good; run; data _temp; set trans_good; set all_patterns; run; proc sql noprint; %do k = 1 %to 17; /* For each pattern */ %do j=1 %to 18; /* For each date */ select h&j into :col&k._h_&j separated by ',' from _temp where col&k=1; select a&j into :col&k._a_&j separated by ',' from _temp where col&k=1; select b&j into :col&k._b_&j separated by ',' from _temp where col&k=1; %end; %end; run; data all; run; %do k = 1 %to 17; /* For each pattern set */ %timetable(k=&k); data all; set all ACC_ds_&k; run; %end; data all; set all; if _n_=1 then delete; run; %mend; %find_schedules; /********************/ /* Generate report! */ /********************/ %macro acc_report; %let Univ1 = Clem; %let Univ2 = Duke; %let Univ3 = FSU; %let Univ4 = GT; %let Univ5 = UMD; %let Univ6 = UNC; %let Univ7 = NCSU; %let Univ8 = UVA; %let Univ9 = Wake; %let Clem=1; %let Duke=2; %let FSU=3; %let GT=4; %let UMD=5; %let UNC=6; %let NCSU=7; %let UVA=8; %let Wake=9; %let Bye=0; data schedule1997; set all; if (alpha&Clem._1 = &UMD) and (alpha&Duke._1 = &UVA) and (alpha&FSU._1 = &UNC) and (alpha>._1 = &NCSU) and (alpha&UMD._1 = &Clem) and (alpha&UNC._1 = &FSU) and (alpha&NCSU._1 = >) and (alpha&UVA._1 = &Duke) and (alpha&Wake._1 = &Bye) and (alpha&Clem._2 = &UNC) and (alpha&Duke._2 = &UMD) and (alpha&FSU._2 = &NCSU) and (alpha>._2 = &Bye) and (alpha&UMD._2 = &Duke) and (alpha&UNC._2 = &Clem) and (alpha&NCSU._2 = &FSU) and (alpha&UVA._2 = &Wake) and (alpha&Wake._2 = &UVA) and (alpha&Clem._3 = &Wake) and (alpha&Duke._3 = &NCSU) and (alpha&FSU._3 = &UMD) and (alpha>._3 = &UNC) and (alpha&UMD._3 = &FSU) and (alpha&UNC._3 = >) and (alpha&NCSU._3 = &Duke) and (alpha&UVA._3 = &Bye) and (alpha&Wake._3 = &Clem) then output; run; data report1997 (keep= %do i = 1 %to 9; &&Univ&i %end;); set schedule1997; format %do i = 1 %to 9; &&Univ&i $8. %end; ; %do j = 1 %to 18; %do i = 1 %to 9; %do i1 = 1 %to 9; if (alpha&i._&j = &i1) and (H&i._&j =1 ) then &&Univ&i= "&&Univ&i1"; else if (alpha&i._&j = &i1) and (A&i._&j =1 ) then &&Univ&i= "@&&Univ&i1"; else if (alpha&i._&j = 0) and (B&i._&j =1 ) then &&Univ&i= "Bye"; %end; %end; output; %end; run; data printfirst; Date="Date"||strip(put(_n_,best.)); set report1997; run; proc print; run; %mend; %acc_report; %macro gantt; %let Univ1 = Clem; %let Univ2 = Duke; %let Univ3 = FSU; %let Univ4 = GT; %let Univ5 = UMD; %let Univ6 = UNC; %let Univ7 = NCSU; %let Univ8 = UVA; %let Univ9 = Wake; data pattern_color_map; format color $10.; %do i = 1 %to 9; _pattern = &i; color = "black"; output; %end; run; %macro setPattern(count, color); pattern&count. v=e color=&color repeat=1; %mend setPattern; %macro setPatterns(data=pattern_color_map); data _null_; set &data; retain count 1; dummy = resolve('%setPattern('||count||','||color||')'); call execute(dummy); count = count + 1; run; %mend setPatterns; %setPatterns; proc format; value stage 0.5 = '1' 17.5 = '18' 18.5 = ' ' ; run; data activity; format _label $6. act$5.; format s_start stage.; label act='Team'; set schedule1997; keep _pattern _label segmt_no act s_start s_finish duration; retain duration 0.95; %do i = 1 %to 9;/* home teams.*/ %do j = 1 %to 18; /* dates */ if (alpha&i._&j = 0) and (B&i._&j =1 ) then do; s_start=.; s_finish=.; segmt_no = &j; _label = ""; _pattern = .; act="&&Univ&i"; output; end; else do; %do i1 = 1 %to 9; if (alpha&i._&j = &i1) and (H&i._&j =1 ) then do; s_start=%eval(&j-1)+0.5; s_finish=&j+0.45; segmt_no = &j; _label = "&&Univ&i1"; _pattern = &i; act="&&Univ&i"; output; end; else if (alpha&i._&j = &i1) and (A&i._&j =1 ) then do; s_start=%eval(&j-1)+0.5; s_finish=&j+0.45; segmt_no = &j; _label = "@&&Univ&i1"; _pattern = &i1; act="&&Univ&i"; output; end; %end; end; %end; %end; run; data labels; _y=-1; _lvar="_label"; _xvar="s_start"; _hlabel=1.0; _yoffset = -0.2; _xoffset = 0; run; title1 "ACC Basketball Tournament Scheduling"; proc gantt data=activity labdata=labels; chart / skip=3 nojobnum nolegend chartwidth=95 pcompress act=act duration=duration mindate=0.5 useformat increment=1 ; id act; run; %mend; %gantt;