The CLP Procedure
Example 2.14 Progressive Party Problem
(View the complete code for this example.)
This example demonstrates the use of the PACK constraint to solve an instance of the progressive party problem (Smith et al. 1996). In the original progressive party problem, a number of yacht crews and their boats congregate at a yachting rally. In order for each crew to socialize with as many other crews as possible, some of the boats are selected to serve as "host boats" for six rounds of parties. The crews of the host boats stay with their boats for all six rounds. The crews of the remaining boats, called "guest crews," are assigned to visit a different host boat in each round.
Given the number of boats at the rally, the capacity of each boat, and the size of each crew, the objective of the original problem is to assign all the guest crews to host boats for each of the six rounds, using the minimum number of host boats. The partitioning of crews into guests and hosts is fixed throughout all rounds. No two crews should meet more than once. The assignments are constrained by the spare capacities (total capacity minus crew size) of the host boats and the crew sizes of the guest boats. Some boats cannot be hosts (zero spare capacity), and other boats must be hosts.
In this instance of the problem, the designation of the minimum requirement of thirteen hosts is assumed (boats 1 through 12 and 14). The total capacities and crew sizes of the boats are shown in Figure 4.
Figure 4: Progressive Party Problem Input
| Progressive Party Problem Input |
| boatnum | capacity | crewsize |
|---|---|---|
| 1 | 6 | 2 |
| 2 | 8 | 2 |
| 3 | 12 | 2 |
| 4 | 12 | 2 |
| 5 | 12 | 4 |
| 6 | 12 | 4 |
| 7 | 12 | 4 |
| 8 | 10 | 1 |
| 9 | 10 | 2 |
| 10 | 10 | 2 |
| 11 | 10 | 2 |
| 12 | 10 | 3 |
| 13 | 8 | 4 |
| 14 | 8 | 2 |
| 15 | 8 | 3 |
| 16 | 12 | 6 |
| 17 | 8 | 2 |
| 18 | 8 | 2 |
| 19 | 8 | 4 |
| 20 | 8 | 2 |
| 21 | 8 | 4 |
| 22 | 8 | 5 |
| 23 | 7 | 4 |
| 24 | 7 | 4 |
| 25 | 7 | 2 |
| 26 | 7 | 2 |
| 27 | 7 | 4 |
| 28 | 7 | 5 |
| 29 | 6 | 2 |
| 30 | 6 | 4 |
| 31 | 6 | 2 |
| 32 | 6 | 2 |
| 33 | 6 | 2 |
| 34 | 6 | 2 |
| 35 | 6 | 2 |
| 36 | 6 | 2 |
| 37 | 6 | 4 |
| 38 | 6 | 5 |
| 39 | 9 | 7 |
| 40 | 0 | 2 |
| 41 | 0 | 3 |
| 42 | 0 | 4 |
The following statements and DATA steps process the data and designate host boats:
data hostability;
set capacities;
spareCapacity = capacity - crewsize;
run;
data hosts guests;
set hostability;
if (boatnum <= 12 or boatnum eq 14) then do;
output hosts;
end;
else do;
output guests;
end;
run;
/* sort so guest boats with larger crews appear first */
proc sort data=guests;
by descending crewsize;
run;
data capacities;
format boatnum capacity 2.;
set hosts guests;
seqno = _n_;
run;
To model the progressive party problem for the CLP solver, first define the following sets of variables:
Next, describe the set of constraints that are used in the model:
Alldifferent constraints ensure that a guest boat is not assigned to the same host boat in different rounds.
Reify constraints regulate the values that are assigned to the aforementioned indicator variables
.
The reified indicator variables appear in linear constraints to enforce the requirement to meet no more than once.
One pack constraint per round maintains the capacity limits of the host boats.
Finally, a symmetry-breaking linear constraint orders the host boat assignments for the highest-numbered guest boat across rounds.
The following statements call the CLP procedure to define the variables, specify the constraints, and solve the problem.
%let rounds=2;
%let numhosts=13;
%macro ppp;
proc sql noprint;
select count(*) into :numboats from capacities;
select max(capacity) into :maxcap from capacities;
%do i = 0 %to &maxcap;
select count(*) into :numclass_&i from capacities where capacity = &i;
%end;
select crewsize, spareCapacity into
:crewsize_1-:crewsize_%scan(&numboats,1),
:cap_1-:cap_%scan(&numboats,1) from capacities order by seqno;
quit;
proc clp out=out varselect=FIFO;
/* assume first &numhosts boats are hosts */
/* process each round in turn */
%do t = 1 %to &rounds;
%do i = &numhosts+1 %to &numboats;
/* boat i assigned host value for round t */
var x_&i._&t = [1,&numhosts];
%end;
%do h = 1 %to &numhosts;
var L_&h._&t = [0,&&cap_&h]; /* load of host boat */
%end;
%end;
%do i = &numhosts+1 %to &numboats;
/* assign different host each round */
alldiff (x_&i._1-x_&i._&rounds);
%end;
%do t = 1 %to &rounds;
%do i = &numhosts+1 %to &numboats-1;
/* boat i assigned host value for round t */
%do j = &i+1 %to &numboats;
var m_&i._&j._&t = [0,1];
reify m_&i._&j._&t : (x_&i._&t = x_&j._&t);
%end;
%end;
%end;
%do i = &numhosts+1 %to &numboats-1;
%do j = &i+1 %to &numboats;
lincon 1 >= 0
%do t = 1 %to &rounds;
+ m_&i._&j._&t
%end;
;
%end;
%end;
/* honor capacities */
%do t = 1 %to &rounds;
PACK((
%do i = &numhosts+1 %to &numboats;
x_&i._&t
%end;
) (
%do i = &numhosts+1 %to &numboats;
&&crewsize_&i
%end;
) (
%do h = 1 %to &numhosts;
L_&h._&t
%end;
));
%end;
/* break symmetries */
%do t = 1 %to &rounds-1;
lincon x_%scan(&numboats,1)_&t < x_%scan(&numboats,1)_%eval(&t+1);
%end;
run;
%mend ppp;
%ppp;
The two charts in Output 2.14.1 show the boat assignments for the first two rounds. The horizontal axis shows the load for each host boat. Slack capacity is highlighted in red.
Output 2.14.1: Gantt Chart: Boat Schedule by Round


The charts in Output 2.14.2 break down the assignments by boat number for selected boats.
Output 2.14.2: Gantt Chart: Host Boat Schedule by Round



