The Dantzig-Wolfe Decomposition Algorithm

Example 19.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 8. More generally, an n-way swap can be performed involving n donors and n recipients (CNN 2012).

Figure 8: Kidney Donor Exchange Two-Way Swap

Kidney Donor Exchange Two-Way Swap


Figure 9: Kidney Donor Exchange Network

Kidney Donor Exchange Network


To model this problem, define a directed graph as follows. Each node is an incompatible donor-recipient pair. Link left-parenthesis i comma j right-parenthesis exists if the donor from node i is compatible with the recipient from node j, as shown in Figure 9. Let N define the set of nodes and A define the set of arcs. The link weight, w Subscript i j, 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 upper M equals StartSet 1 comma ellipsis comma StartAbsoluteValue upper N EndAbsoluteValue slash 2 EndSet of candidate disjoint unions of short cycles (called matchings). Let x Subscript i j m be a binary variable that, if set to 1, indicates that arc left-parenthesis i comma j right-parenthesis is in a matching m. Let y Subscript i m be a binary variable that, if set to 1, indicates that node i is covered by matching m. In addition, let s Subscript i 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:

StartLayout 1st Row 1st Column Blank 2nd Column maximize 3rd Column sigma-summation Underscript left-parenthesis i comma j right-parenthesis element-of upper A Endscripts sigma-summation Underscript m element-of upper M Endscripts w Subscript i j Baseline x Subscript i j m 2nd Row 1st Column Blank 2nd Column subject to 3rd Column sigma-summation Underscript m element-of upper M Endscripts y Subscript i m plus s Subscript i 4th Column equals 1 5th Column Blank 6th Column i element-of upper N 7th Column Blank 8th Column left-parenthesis Packing right-parenthesis 3rd Row 1st Column Blank 2nd Column Blank 3rd Column sigma-summation Underscript left-parenthesis i comma j right-parenthesis element-of upper A Endscripts x Subscript i j m 4th Column equals y Subscript i m Baseline 5th Column Blank 6th Column i element-of upper N comma m element-of upper M 7th Column Blank 8th Column left-parenthesis Donate right-parenthesis 4th Row 1st Column Blank 2nd Column Blank 3rd Column sigma-summation Underscript left-parenthesis i comma j right-parenthesis element-of upper A Endscripts x Subscript i j m 4th Column equals y Subscript j m Baseline 5th Column Blank 6th Column j element-of upper N comma m element-of upper M 7th Column Blank 8th Column left-parenthesis Receive right-parenthesis 5th Row 1st Column Blank 2nd Column Blank 3rd Column sigma-summation Underscript left-parenthesis i comma j right-parenthesis element-of upper A Endscripts x Subscript i j m 4th Column less-than-or-equal-to upper L 5th Column Blank 6th Column m element-of upper M 7th Column Blank 8th Column left-parenthesis Cardinality right-parenthesis 6th Row 1st Column Blank 2nd Column Blank 3rd Column x Subscript i j m 4th Column element-of StartSet 0 comma 1 EndSet 5th Column Blank 6th Column left-parenthesis i comma j right-parenthesis element-of upper A comma m element-of upper M 7th Row 1st Column Blank 2nd Column Blank 3rd Column y Subscript i m 4th Column element-of StartSet 0 comma 1 EndSet 5th Column Blank 6th Column i element-of upper N comma m element-of upper M 8th Row 1st Column Blank 2nd Column Blank 3rd Column s Subscript i 4th Column element-of StartSet 0 comma 1 EndSet 5th Column Blank 6th Column i element-of upper N EndLayout

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 Dantzig-Wolfe 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 Dantzig-Wolfe decomposition algorithm to solve it:

%let max_length = 10;
proc optmodel;
   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 19.11.1.

Output 19.11.1: Solution Summary

The OPTMODEL Procedure

Solution Summary
SolverMILP
AlgorithmDecomposition
Objective FunctionTotalWeight
Solution StatusOptimal
Objective Value24.850855395
  
Relative Gap0
Absolute Gap0
Primal Infeasibility1.265654E-14
Bound Infeasibility1.265654E-14
Integer Infeasibility1.265654E-14
  
Best Bound24.850855395
Nodes11
Solutions Found14
Iterations99
Presolve Time0.01
Solution Time26.57


The iteration log is displayed in Output 19.11.2.

Output 19.11.2: Log

NOTE: There were 208 observations read from the data set WORK.ARCDATA.                          
NOTE: Problem generation will use 4 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 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 4 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%    0    0                   
         3     335.1881      11.5201      11.5201   96.56%   96.56%    0    0                   
         4     321.9254      11.5201      11.5201   96.42%   96.42%    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     310.5432      17.1869      17.1869   94.47%   94.47%    1    1                   
         9     294.9180      18.3680      17.1869   93.77%   94.17%    1    1                   
         .     294.9180      19.1629      17.1869   93.50%   94.17%    1    1                   
        10     247.6362      19.1629      17.1869   92.26%   93.06%    1    1                   
        12     216.5000      21.1044      19.5501   90.25%   90.97%    4    4                   
        14     171.3347      22.5470      22.5470   86.84%   86.84%    4    4                   
        15     161.5033      23.8247      22.5470   85.25%   86.04%    4    4                   
        16     141.8202      23.8247      22.5470   83.20%   84.10%    4    4                   
        17     134.7237      23.8247      22.5470   82.32%   83.26%    4    4                   
        18      98.8615      23.8247      22.5470   75.90%   77.19%    4    4                   
        19      94.4571      23.8247      22.5470   74.78%   76.13%    4    4                   
         .      94.4571      24.5455      22.5470   74.01%   76.13%    4    4                   
        20      94.4571      24.5455      22.5470   74.01%   76.13%    4    4                   
        21      73.2728      24.5455      22.5470   66.50%   69.23%    4    4                   
        25      66.9605      24.5455      22.5470   63.34%   66.33%    4    4                   
        30      66.9605      24.8540      22.5470   62.88%   66.33%    4    4                   
        31      57.4276      24.8628      22.5470   56.71%   60.74%    4    4                   
        33      44.8346      25.0805      22.5470   44.06%   49.71%    5    5                   
         .      44.8346      25.2926      23.1069   43.59%   48.46%    5    5                   
        40      42.7043      25.2926      23.1069   40.77%   45.89%    5    5                   
        41      39.0144      25.3307      23.1069   35.07%   40.77%    5    5                   
        42      32.3831      25.3349      23.1069   21.77%   28.65%    5    5                   
        43      31.7496      25.3565      23.1069   20.14%   27.22%    5    5                   
        45      28.2684      25.4097      23.1069   10.11%   18.26%    5    5                   
        46      26.9391      25.4155      23.1069    5.66%   14.23%    5    5                   
        47      25.4194      25.4194      23.1069    0.00%    9.10%    5    5                   
         .      25.4194      25.4194      23.6456    0.00%    6.98%    5    5                   
NOTE: Starting branch and bound.                                                                
         Node  Active   Sols         Best         Best      Gap    CPU   Real                   
                                  Integer        Bound            Time   Time                   
            0       1     12      23.6456      25.4194    6.98%      5      5                   
            1       3     13      24.8509      25.4194    2.24%     13     13                   
            5       3     13      24.8509      25.0950    0.97%     25     25                   
            7       3     14      24.8509      25.0247    0.69%     26     25                   
           10       0     14      24.8509      24.8509    0.00%     26     26                   
NOTE: The Decomposition algorithm used 4 threads.                                               
NOTE: The Decomposition algorithm time is 26.57 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 mylib.Cycles.

data mylib.Solution;
   set Solution;
run;
proc optnetwork
   direction = directed
   links     = mylib.Solution;
   linksVar
      from   = i
      to     = j;
   cycle
      maxCycles = all
      out       = mylib.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 16, The Network Solver). The optimal donor exchanges from the output data table mylib.Cycles are displayed in Figure 10.

Figure 10: Optimal Donor Exchanges

ordernode
10
299
333
429
520
624
797
831
946
100

ordernode
12
218
390
426
584
653
762
82

ordernode
13
256
35
483
545
663
714
864
969
1092
113

ordernode
16
296
327
493
523
651
787
878
943
1041
116

ordernode
121
285
382
473
521

ordernode
137
239
389
477
537

ordernode
138
279
371
438


Last updated: September 09, 2026