The standard pooling problem

  • A pooling problem is a directed network flow problem
  • Three types of nodes: sources, pools and terminals.
  • Commodities flow from sources to terminals, and commodities can be blended in intermediate pools.
  • Capacities for all nodes.
  • Different commodities have different quality parameters, and the terminals have quality requirements.
  • The pools control the quality at terminals by a blending process.
  • A nonlinear flow problem, NP hard in general.

We store the network in a structure, where cost is an adjacency matrix storing the cost for each edge in the network, we also store the quality parameters (or requirements) in q and the capacities in cap,


In [1]:
immutable PoolingNetwork
    ns   :: Int
    np   :: Int
    nt   :: Int
    cost :: SparseMatrixCSC{Float64,Int}
    q    :: Vector{Float64}
    cap  :: Vector{Float64}     
end

The following block of code is not important, it just plots a pooling network.


In [2]:
using Compose

immutable Node
    x :: Float64
    y :: Float64    
    radius :: Float64
    label :: String
    text  :: String
end

draw_node(n::Node) = compose(context(), ( compose(context(), circle(n.x, n.y, n.radius)),
    compose(context(), text(n.x, n.y, n.label, hcenter, vcenter)),
    compose(context(), text(n.x, n.y - (2*n.radius), n.text, hcenter, vbottom)) ))    

function draw_edge(a::Node, b::Node, label::String)
    p = [b.x-a.x, b.y-a.y] ./ norm([b.x-a.x, b.y-a.y])
    compose(context(), 
        (compose(context(), line([(a.x+p[1]*a.radius, a.y+p[2]*a.radius), (b.x-p[1]*b.radius, b.y-p[2]*b.radius)])),
         compose(context(), text(a.x+(b.x-a.x)/2, a.y+(b.y-a.y)/2+0.02, label, hcenter, vtop))) )
end

function draw_network(G::PoolingNetwork)
    dx, dy = 0.4, 1.0/G.ns 
    
    N = [ [ Node(0.1,     dy*(i-0.5),.05,string(i),string("q<sub>",i,"</sub>")) for i=1:G.ns ];
          [ Node(0.1+dx,  dy*(i-0.5),.05,string(i+G.ns),string("w<sub>",i+G.ns,"</sub>")) for i=1:G.np ];
          [ Node(0.1+2*dx,dy*(i-0.5),.05,string(i+G.ns+G.np),string("q<sub>",i+G.ns+G.np,"</sub>")) for i=1:G.nt ] ]
    
    p = [draw_node(ni) for ni=N]
    for j=1:G.ns+G.np+G.nt
        for i=find(G.cost[:,j])
            push!(p, draw_edge(N[i], N[j], string("x<sub>",i,j,"</sub>"))) 
        end             
    end    
    compose(context(), fill(nothing), stroke("black"), p)
end


Out[2]:
draw_network (generic function with 1 method)

One standard pooling test instance is the Haverly model,


In [3]:
N = PoolingNetwork(3, 1, 2, 
                   sparse([1,2,3,3,4,4],[4,4,5,6,5,6],[6,16,1,5,-9,-15],6,6), 
                   [3,1,2,0,2.5,1.5], 
                   [1,1,1,1,1/3,2/3] );

with quality parameters


In [4]:
N.q


Out[4]:
6-element Array{Float64,1}:
 3.0
 1.0
 2.0
 0.0
 2.5
 1.5

capacities,


In [5]:
N.cap


Out[5]:
6-element Array{Float64,1}:
 1.0     
 1.0     
 1.0     
 1.0     
 0.333333
 0.666667

and costs


In [6]:
N.cost


Out[6]:
6×6 sparse matrix with 6 Float64 nonzero entries:
	[1, 4]  =  6.0
	[2, 4]  =  16.0
	[3, 5]  =  1.0
	[4, 5]  =  -9.0
	[3, 6]  =  5.0
	[4, 6]  =  -15.0

Edges to terminals have a negative cost, corresponding to an income when a commodity is delivered.


In [7]:
draw_network(N)


Out[7]:
x46 x36 x45 x35 x24 x14 q6 6 q5 5 w4 4 q3 3 q2 2 q1 1

Setting up the problem


In [8]:
using Polyopt

In [9]:
x14, x24, x35, x36, x45, x46, w4 = variables(["x14"; "x24"; "x35"; "x36"; "x45"; "x46"; "w4"]);

In the minimium cost formulation we minimize $\sum_{(i,j)\in N} c_{i,j}x_{i,j}$.


In [10]:
obj = N.cost[1,4]*x14 + N.cost[2,4]*x24 + N.cost[3,5]*x35 + N.cost[3,6]*x36 + N.cost[4,5]*x45 + N.cost[4,6]*x46;

We then have flow equilibrium constraints $x_{14} + x_{24} = x_{45} + x_{46}$,


In [11]:
flow_eq = x14 + x24 - (x45 + x46);

and a (nonconvex) defining equation for the blending variables $w_4=(x_{45} + x_{46}) = q_1x_{14} + q_2 x_{24}$ as the blended quality at the pool,


In [12]:
blend_eq = w4*(x45 + x46) - (N.q[1]*x14 + N.q[2]*x24);

quality bounds at the terminals $q_5(x_{45}+x_{36}) \geq (w_4 x_{45} + q_3 x_{35})$ and $q_6(x_{46}+x_{36}) \geq (w_4 x_{46} + q_3 x_{36})$


In [13]:
qual_bnd = [ N.q[5]*(x45+x36)-(w4*x45+N.q[3]*x35); N.q[6]*(x46+x36)-(w4*x46+N.q[3]*x36) ];

capacity bounds,


In [14]:
cap_bnd = [ N.cap[1]-x14; N.cap[2]-x24; N.cap[3]-(x35+x36); N.cap[4]-(x45+x46); N.cap[5]-(x35+x45); N.cap[6]-(x36+x46) ];

and finally nonnegativity of flow variables,


In [15]:
flow_bnd = [ x14; x24; x35; x36; x45; x46 ];

Solving the problem

We solve the second-order moment relaxation


In [16]:
prob = momentprob(2, obj, [qual_bnd; cap_bnd; flow_bnd], [flow_eq; blend_eq]);

In [17]:
X, Z, t, y, solsta = solve_mosek(prob);


Open file 'polyopt.task'
Problem
  Name                   :                 
  Objective sense        : max             
  Type                   : CONIC (conic optimization problem)
  Constraints            : 330             
  Cones                  : 0               
  Scalar variables       : 73              
  Matrix variables       : 15              
  Integer variables      : 0               

Optimizer started.
Conic interior-point optimizer started.
Presolve started.
Linear dependency checker started.
Linear dependency checker terminated.
Eliminator started.
Freed constraints in eliminator : 0
Eliminator terminated.
Eliminator - tries                  : 1                 time                   : 0.00            
Lin. dep.  - tries                  : 1                 time                   : 0.00            
Lin. dep.  - number                 : 0               
Presolve terminated. Time: 0.00    
Optimizer  - threads                : 4               
Optimizer  - solved problem         : the primal      
Optimizer  - Constraints            : 330
Optimizer  - Cones                  : 1
Optimizer  - Scalar variables       : 74                conic                  : 74              
Optimizer  - Semi-definite variables: 15                scalarized             : 1170            
Factor     - setup time             : 0.01              dense det. time        : 0.00            
Factor     - ML order time          : 0.00              GP order time          : 0.00            
Factor     - nonzeros before factor : 5.46e+04          after factor           : 5.46e+04        
Factor     - dense dim.             : 0                 flops                  : 1.34e+07        
ITE PFEAS    DFEAS    GFEAS    PRSTATUS   POBJ              DOBJ              MU       TIME  
0   6.0e+00  1.0e+00  1.0e+00  0.00e+00   0.000000000e+00   0.000000000e+00   1.0e+00  0.01  
1   4.0e+00  6.7e-01  6.6e-01  -2.97e-01  -4.567060837e+00  -7.323747534e+00  6.7e-01  0.02  
2   2.3e+00  3.8e-01  2.2e+00  1.40e+00   -7.861933135e+00  -3.579444348e+00  3.8e-01  0.03  
3   7.3e-01  1.2e-01  1.2e+00  1.98e+00   -3.657716750e+00  -2.911843312e+00  1.2e-01  0.04  
4   1.4e-01  2.4e-02  3.6e-01  1.36e+00   -1.055578024e+00  -9.657199588e-01  2.4e-02  0.05  
5   3.7e-02  6.2e-03  1.6e-01  9.03e-01   -9.025845415e-01  -8.798743142e-01  6.2e-03  0.06  
6   1.1e-02  1.8e-03  8.5e-02  8.61e-01   -9.821738372e-01  -9.751823981e-01  1.8e-03  0.09  
7   2.8e-03  4.7e-04  4.6e-02  1.05e+00   -9.932720321e-01  -9.913317806e-01  4.7e-04  0.09  
8   6.8e-04  1.1e-04  2.3e-02  9.93e-01   -9.980389445e-01  -9.975679811e-01  1.1e-04  0.10  
9   1.6e-04  2.6e-05  1.1e-02  9.96e-01   -9.994847565e-01  -9.993741334e-01  2.6e-05  0.11  
10  4.1e-05  6.9e-06  5.8e-03  9.76e-01   -9.998493980e-01  -9.998199913e-01  6.9e-06  0.12  
11  9.7e-06  1.6e-06  2.8e-03  9.52e-01   -9.999640790e-01  -9.999570189e-01  1.6e-06  0.13  
12  2.4e-06  4.1e-07  1.4e-03  1.03e+00   -9.999914158e-01  -9.999897015e-01  4.1e-07  0.14  
13  5.8e-07  9.7e-08  6.8e-04  9.97e-01   -9.999979801e-01  -9.999975726e-01  9.7e-08  0.14  
14  1.4e-07  2.4e-08  3.4e-04  9.84e-01   -9.999994713e-01  -9.999993710e-01  2.4e-08  0.15  
15  3.4e-08  5.1e-09  1.5e-04  9.36e-01   -9.999998493e-01  -9.999998280e-01  5.0e-09  0.16  
16  8.7e-09  1.2e-09  7.3e-05  9.99e-01   -9.999999637e-01  -9.999999587e-01  1.2e-09  0.17  
17  2.1e-09  4.9e-08  3.6e-05  1.00e+00   -9.999999911e-01  -9.999999899e-01  3.0e-10  0.18  
Interior-point optimizer terminated. Time: 0.18. 

Optimizer terminated. Time: 0.20    

Interior-point solution summary
  Problem status  : PRIMAL_AND_DUAL_FEASIBLE
  Solution status : OPTIMAL
  Primal.  obj: -9.9999999113e-01   nrm: 2e+01    Viol.  con: 6e-09    var: 0e+00    barvar: 0e+00  
  Dual.    obj: -9.9999998990e-01   nrm: 2e+01    Viol.  con: 0e+00    var: 5e-10    barvar: 3e-10  

We examine the solution,


In [18]:
[prob.basis y]


Out[18]:
330×2 Array{Polyopt.Poly{Float64},2}:
 1.0            1.0         
 w4             1.5         
 w4^2           2.25043     
 w4^3           3.37542     
 w4^4           23.3419     
 x46            0.666667    
 x46*w4         1.0         
 x46*w4^2       1.5         
 x46*w4^3       2.24986     
 x46^2          0.444444    
 x46^2*w4       0.666667    
 x46^2*w4^2     1.0         
 x46^3          0.296296    
 ⋮                          
 x14^2*x24*x45  0.000748091 
 x14^2*x24*x36  0.00202102  
 x14^2*x24*x35  0.000684209 
 x14^2*x24^2    1.59411     
 x14^3          0.00462963  
 x14^3*w4       -0.0104982  
 x14^3*x46      0.00763729  
 x14^3*x45      0.0061658   
 x14^3*x36      -0.000642224
 x14^3*x35      0.00612184  
 x14^3*x24      0.0146385   
 x14^4          3.30797     

and we we test feasibility,


In [19]:
xo = Polyopt.vectorize([x14,x24,x35,x36,x45,x46,w4],2*2)*y


Out[19]:
7-element Array{Float64,1}:
 0.166667   
 0.5        
 5.99336e-11
 1.91497e-9 
 6.48003e-9 
 0.666667   
 1.5        

In [20]:
[ Polyopt.evalpoly(h, xo) for h=[flow_eq; blend_eq] ]


Out[20]:
2-element Array{Float64,1}:
 -4.67222e-10
 -3.47856e-9 

In [21]:
[ Polyopt.evalpoly(g, xo) for g=[qual_bnd; cap_bnd; flow_bnd] ]


Out[21]:
14-element Array{Float64,1}:
  1.11476e-8 
 -6.69261e-9 
  0.833333   
  0.5        
  1.0        
  0.333333   
  0.333333   
  1.62058e-8 
  0.166667   
  0.5        
  5.99336e-11
  1.91497e-9 
  6.48003e-9 
  0.666667   

since the extracted monomial solution is feasible, we have found the optimal solution.