The Decomposition Algorithm
Example 18.11 Kidney Donor Exchange and METHOD=SET
This example looks at an application of integer programming to help create a kidney donor exchange. Suppose someone needs a kidney transplant and a family member is willing to be a donor. If the donor and recipient are incompatible (because of blood type, tissue mismatch, and so on), the transplant cannot happen. Now suppose two donor-recipient pairs, i and j, are in this situation, but donor i is compatible with recipient j and donor j is compatible with recipient i. Then two transplants can take place in a two-way swap, shown in Figure 9. More generally, an n-way swap can be performed involving n donors and n recipients (CNN 2012).
Figure 9: Kidney Donor Exchange Two-Way Swap

Figure 10: Kidney Donor Exchange Network

To model this problem, define a directed graph as follows. Each node is an incompatible donor-recipient pair. Link exists if the donor from node i is compatible with the recipient from node j, as shown in Figure 10. Let N define the set of nodes and A define the set of arcs. The link weight,
, is a measure of the quality of the match. By introducing dummy links whose weight is 0, you can also include altruistic donors who have no recipients or recipients who have no donors. The idea is to find a maximum-weight node-disjoint union of directed cycles. You want the union to be node-disjoint so that no kidney is donated more than once, and you want cycles so that the donor from node i donates a kidney if and only if the recipient from node i receives a kidney.
Without any other constraints, the problem could be solved as a linear assignment problem. But doing so would allow arbitrarily long cycles in the solution. For practical considerations (such as travel) and to mitigate risk, each cycle must have no more than L links. The kidney exchange problem is to find a maximum-weight node-disjoint union of short directed cycles.
Define an index set of candidate disjoint unions of short cycles (called matchings). Let
be a binary variable that, if set to 1, indicates that arc
is in a matching m. Let
be a binary variable that, if set to 1, indicates that node i is covered by matching m. In addition, let
be a binary slack variable that, if set to 1, indicates that node i is not covered by any matching.
The kidney donor exchange can be formulated as a MILP as follows:
In this formulation, the Packing constraints ensure that each node is covered by at most one matching. The Donate and Receive constraints enforce the condition that if node i is covered by matching m, then the matching m must use exactly one arc that leaves node i (Donate) and one arc that enters node i (Receive). Conversely, if node i is not covered by matching m, then no arcs that enter or leave node i can be used by matching m. The Cardinality constraints enforce the condition that the number of arcs in matching m must not exceed L.
In this formulation, the matching identifier is arbitrary. Because it is not necessary to cover each incompatible donor-recipient pair (node), the Packing constraints can be modeled by using set partitioning constraints and the slack variable s. Consider a decomposition by matching, in which the Packing constraints form the master problem and all other constraints form identical matching subproblems. As described in the section Special Case: Identical Blocks and Ryan-Foster Branching, this is a situation in which an aggregate formulation and Ryan-Foster branching can greatly improve performance by reducing symmetry.
The following DATA step sets up the problem by first creating a random graph on n nodes with link probability p and Uniform(0,1) weight:
/* create random graph on n nodes with arc probability p
and uniform(0,1) weight */
%let n = 100;
%let p = 0.02;
data ArcData;
call streaminit(1);
do i = 0 to &n - 1;
do j = 0 to &n - 1;
if i eq j then continue;
else if rand('UNIFORM') < &p then do;
weight = rand('UNIFORM');
output;
end;
end;
end;
run;
In this case, you can specify METHOD=SET and let the decomposition algorithm automatically detect the set partitioning master constraints (Packing) and each independent matching subproblem. The following PROC OPTMODEL statements read in the data, declare the optimization model, and use the decomposition algorithm to solve it:
%let max_length = 10;
proc optmodel sessref="mysess";
set <num,num> ARCS;
num weight {ARCS};
read data ArcData into ARCS=[i j] weight;
print weight;
set NODES = union {<i,j> in ARCS} {i,j};
set MATCHINGS = 1..card(NODES)/2;
/* UseNode[i,m] = 1 if node i is used in matching m, 0 otherwise */
var UseNode {NODES, MATCHINGS} binary;
/* UseArc[i,j,m] = 1 if arc (i,j) is used in matching m, 0 otherwise */
var UseArc {ARCS, MATCHINGS} binary;
/* maximize total weight of arcs used */
max TotalWeight
= sum {<i,j> in ARCS, m in MATCHINGS} weight[i,j] * UseArc[i,j,m];
/* each node appears in at most one matching */
/* rewrite as set partitioning (so decomp uses identical blocks)
sum{} x <= 1 => sum{} x + s = 1, s >= 0 with no associated cost */
var Slack {NODES} binary;
con Packing {i in NODES}:
sum {m in MATCHINGS} UseNode[i,m] + Slack[i] = 1;
/* at most one recipient for each donor */
con Donate {i in NODES, m in MATCHINGS}:
sum {<(i),j> in ARCS} UseArc[i,j,m] = UseNode[i,m];
/* at most one donor for each recipient */
con Receive {j in NODES, m in MATCHINGS}:
sum {<i,(j)> in ARCS} UseArc[i,j,m] = UseNode[j,m];
/* exclude long matchings */
con Cardinality {m in MATCHINGS}:
sum {<i,j> in ARCS} UseArc[i,j,m] <= &max_length;
/* automatically decompose using METHOD=SET */
solve with milp / presolver=basic decomp=(method=set);
/* save solution to a data set */
create data Solution from
[m i j]={m in MATCHINGS, <i,j> in ARCS: UseArc[i,j,m].sol > 0.5}
weight[i,j];
quit;
In this case, the PRESOLVER=BASIC option ensures that the model maintains its specified symmetry, enabling the algorithm to use the aggregate formulation and Ryan-Foster branching. The solution summary is displayed in Output 18.11.1.
Output 18.11.1: Solution Summary
| Solution Summary | |
|---|---|
| Solver | MILP |
| Algorithm | Decomposition |
| Objective Function | TotalWeight |
| Solution Status | Optimal |
| Objective Value | 24.850855395 |
| Relative Gap | 0 |
| Absolute Gap | 0 |
| Primal Infeasibility | 1.110223E-15 |
| Bound Infeasibility | 8.881784E-16 |
| Integer Infeasibility | 8.881784E-16 |
| Best Bound | 24.850855395 |
| Nodes | 11 |
| Solutions Found | 17 |
| Iterations | 110 |
| Presolve Time | 0.01 |
| Solution Time | 33.90 |
The iteration log is displayed in Output 18.11.2.
Output 18.11.2: Log
| NOTE: There were 208 observations read from the data set WORK.ARCDATA. |
| NOTE: Problem generation will use 8 threads. |
| NOTE: The problem has 15092 variables (0 free, 0 fixed). |
| NOTE: The problem has 15092 binary and 0 integer variables. |
| NOTE: The problem has 9751 linear constraints (49 LE, 9702 EQ, 0 GE, 0 range). |
| NOTE: The problem has 45080 linear constraint coefficients. |
| NOTE: The problem has 0 nonlinear constraints (0 LE, 0 EQ, 0 GE, 0 range). |
| NOTE: The SOLVE statement is executing in the distributed computing environment in |
| single-machine mode. |
| NOTE: The initial MILP heuristics are applied. |
| NOTE: The MILP presolver value BASIC is applied. |
| NOTE: The MILP presolver removed 5081 variables and 3366 constraints. |
| NOTE: The MILP presolver removed 15175 constraint coefficients. |
| NOTE: The MILP presolver modified 0 constraint coefficients. |
| NOTE: The presolved problem has 10011 variables, 6385 constraints, and 29905 constraint |
| coefficients. |
| NOTE: The MILP solver is called. |
| NOTE: The Decomposition algorithm is used. |
| NOTE: The Decomposition algorithm is executing in single-machine mode. |
| NOTE: The DECOMP method value SET is applied. |
| NOTE: All blocks are identical and the master model is set partitioning. |
| NOTE: The Decomposition algorithm is using an aggregate formulation and Ryan-Foster branching. |
| NOTE: The number of block threads has been reduced to 1 threads. |
| NOTE: The problem has a decomposable structure with 49 blocks. The largest block covers 2.02% |
| of the constraints in the problem. |
| NOTE: The decomposition subproblems cover 9947 (99.36%) variables and 6321 (99%) constraints. |
| NOTE: The deterministic parallel mode is enabled. |
| NOTE: The Decomposition algorithm is using up to 80 threads. |
| Iter Best Master Best LP IP CPU Real |
| Bound Objective Integer Gap Gap Time Time |
| . 350.7353 11.5201 11.5201 96.72% 96.72% 1 1 |
| 3 339.7809 11.5201 11.5201 96.61% 96.61% 1 1 |
| 4 333.3981 11.5201 11.5201 96.54% 96.54% 1 1 |
| 5 314.4588 15.7076 15.7076 95.00% 95.00% 1 1 |
| 6 314.4588 17.1869 17.1869 94.53% 94.53% 1 1 |
| 8 306.9517 17.1869 17.1869 94.40% 94.40% 2 2 |
| 9 301.1787 19.8897 17.1869 93.40% 94.29% 3 3 |
| . 301.1787 19.8897 17.1869 93.40% 94.29% 3 3 |
| 10 296.4397 19.8897 17.1869 93.29% 94.20% 3 3 |
| 11 233.2433 19.8897 17.1869 91.47% 92.63% 4 3 |
| 12 205.8663 21.2404 17.1869 89.68% 91.65% 4 3 |
| 15 196.2609 22.9988 17.1869 88.28% 91.24% 4 3 |
| 17 173.1685 23.2887 21.0803 86.55% 87.83% 4 3 |
| 19 139.1889 24.3948 21.0803 82.47% 84.85% 4 4 |
| . 139.1889 24.5468 22.6821 82.36% 83.70% 4 4 |
| 20 139.1889 24.5468 22.6821 82.36% 83.70% 4 4 |
| 23 126.9471 24.6079 22.6821 80.62% 82.13% 5 4 |
| 24 125.2742 24.6642 22.6821 80.31% 81.89% 5 5 |
| 26 72.9270 24.8011 22.6821 65.99% 68.90% 5 5 |
| 30 72.9270 25.2129 22.6821 65.43% 68.90% 6 6 |
| 31 50.7784 25.2129 22.6821 50.35% 55.33% 7 6 |
| 32 47.9590 25.2129 22.6821 47.43% 52.71% 7 6 |
| 34 47.9590 25.2857 23.1920 47.28% 51.64% 7 6 |
| 36 45.2516 25.3484 23.1920 43.98% 48.75% 8 7 |
| 38 35.8558 25.3867 23.1920 29.20% 35.32% 8 7 |
| 39 32.3748 25.3867 23.1920 21.58% 28.36% 9 7 |
| . 32.3748 25.3911 23.4272 21.57% 27.64% 9 8 |
| 40 29.4267 25.3911 23.4272 13.71% 20.39% 9 8 |
| 41 28.9902 25.4132 23.4272 12.34% 19.19% 9 8 |
| 42 27.5972 25.4173 23.4272 7.90% 15.11% 9 8 |
| 43 26.2637 25.4173 23.4272 3.22% 10.80% 10 8 |
| 45 25.4194 25.4194 23.4272 0.00% 7.84% 10 9 |
| . 25.4194 25.4194 24.3243 0.00% 4.31% 10 9 |
| NOTE: Starting branch and bound. |
| Node Active Sols Best Best Gap CPU Real |
| Integer Bound Time Time |
| 0 1 13 24.3243 25.4194 4.31% 10 9 |
| 1 3 14 24.5652 25.4194 3.36% 15 13 |
| 7 7 15 24.5652 25.0268 1.84% 27 23 |
| 10 0 17 24.8509 24.8509 0.00% 39 33 |
| NOTE: The Decomposition algorithm used 80 threads. |
| NOTE: The Decomposition algorithm time is 33.90 seconds. |
| NOTE: Optimal. |
| NOTE: Objective = 24.850855395. |
| NOTE: The data set WORK.SOLUTION has 47 observations and 4 variables. |
The solution is a set of arcs that define a union of short directed cycles (matchings). The following call to PROC OPTNETWORK extracts the corresponding cycles from the list of arcs and outputs them to the data table mycas.Cycles. The DATA step assumes that your CAS engine libref is named mycas, but you can substitute any appropriately defined CAS engine libref.
data mycas.Solution;
set Solution;
run;
proc optnetwork
direction = directed
links = mycas.Solution;
linksVar
from = i
to = j;
cycle
maxCycles = all
out = mycas.Cycles;
run;
For more information about PROC OPTNETWORK, see SAS Optimization: The OPTNETWORK Procedure. Alternatively, you can extract the cycles by using the SOLVE WITH NETWORK statement in PROC OPTMODEL (see Chapter 15, The Network Solver). The optimal donor exchanges from the output data table mycas.Cycles are displayed in Figure 11.
Figure 11: Optimal Donor Exchanges
| order | node |
|---|---|
| 1 | 0 |
| 2 | 99 |
| 3 | 33 |
| 4 | 29 |
| 5 | 20 |
| 6 | 24 |
| 7 | 97 |
| 8 | 31 |
| 9 | 46 |
| 10 | 0 |
| order | node |
|---|---|
| 1 | 2 |
| 2 | 18 |
| 3 | 90 |
| 4 | 26 |
| 5 | 84 |
| 6 | 53 |
| 7 | 62 |
| 8 | 2 |
| order | node |
|---|---|
| 1 | 3 |
| 2 | 56 |
| 3 | 5 |
| 4 | 83 |
| 5 | 45 |
| 6 | 63 |
| 7 | 14 |
| 8 | 64 |
| 9 | 69 |
| 10 | 92 |
| 11 | 3 |
| order | node |
|---|---|
| 1 | 6 |
| 2 | 96 |
| 3 | 27 |
| 4 | 93 |
| 5 | 23 |
| 6 | 51 |
| 7 | 87 |
| 8 | 78 |
| 9 | 43 |
| 10 | 41 |
| 11 | 6 |
| order | node |
|---|---|
| 1 | 21 |
| 2 | 85 |
| 3 | 82 |
| 4 | 73 |
| 5 | 21 |
| order | node |
|---|---|
| 1 | 37 |
| 2 | 39 |
| 3 | 89 |
| 4 | 77 |
| 5 | 37 |
| order | node |
|---|---|
| 1 | 38 |
| 2 | 79 |
| 3 | 71 |
| 4 | 38 |