├── README.md ├── examples ├── afiro.mat ├── blend.mat └── testExamples.m ├── license.txt ├── setup.m └── src ├── initialPoint.m ├── mpcSol.m ├── newtonDirection.m ├── setParamOptions.m └── stepSize.m /README.md: -------------------------------------------------------------------------------- 1 | MPC: Mehrotra's Predictor-Corrector Interior Point Method 2 | --------------------------------------------------------------------------- 3 | 4 | Author: Yiming Yan @ University of Edinburgh 5 | 6 | Before you start 7 | --------------------------------------------------------------------------- 8 | This is a just very simple demo version of the implementation 9 | of the Methrotra's predictor-corrector IPM for Linear Programming. 10 | There is no guarantee that it can solve very hard or large-scale 11 | problems and its performance may not be as good as the standard commercial 12 | codes, but it does reflect the general ideas of the interior point methods. 13 | 14 | The main solver MPCSOL (/solver/mpcSol.m) only accepts LP problems 15 | in the standard form, i.e. 16 | ``` 17 | min c'*x 18 | s.t. Ax = b, 19 | x>=0. 20 | ``` 21 | Any LP problem can be transformed to the standard form. 22 | 23 | 24 | How To Use it 25 | --------------------------------------------------------------------------- 26 | 1. Run the script setup.m first to check and setup necessary 27 | system environment. 28 | 29 | 2. If your problem is in the standard form, you can use 30 | 31 | ```[x, y, s, N] = mpcSol(A,b,c)``` 32 | 33 | or 34 | 35 | ```[x, y, s, N] = mpcSol(A,b,c, param_in, Name)``` 36 | 37 | when you would like to specify the name of the problem and the parameters. 38 | 39 | The default value of input Name is "testProb". Regardnig to the default 40 | value of parameters, see setParamOptions.m. 41 | 42 | 3. To test the examples, just run "testExamples" in your command line. 43 | The source code and data files of the script "testExamples.m" can be 44 | found in folder "examples". 45 | 46 | Have Fun! :) 47 | Yiming 48 | 49 | 50 | References 51 | --------------------------------------------------------------------------- 52 | 1. Stephen Wright, Primal-Dual Interior-Point Method 53 | 2. Sanjay Mehrotra, On the Implementation of a Primal-Dual 54 | Interior Point Method. 55 | -------------------------------------------------------------------------------- /examples/afiro.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YimingYAN/mpc/cf1f3f73f9d15d1f77460cd30da4da0d66a06da3/examples/afiro.mat -------------------------------------------------------------------------------- /examples/blend.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YimingYAN/mpc/cf1f3f73f9d15d1f77460cd30da4da0d66a06da3/examples/blend.mat -------------------------------------------------------------------------------- /examples/testExamples.m: -------------------------------------------------------------------------------- 1 | % Test examples 2 | 3 | clear; 4 | clc; 5 | 6 | param_in = []; 7 | 8 | % Example: random 9 | m = 4; 10 | n = 6; 11 | 12 | A = randn(m,n) 13 | b = A*rand(n,1) 14 | c = A'*rand(m,1) + rand(n,1) 15 | 16 | param_in.verbose = 0; 17 | 18 | [f, x, y, s, N] = mpcSol(A, b, c, param_in); 19 | x 20 | N 21 | 22 | % Example: afiro -- in standard form 23 | param_in.verbose = 1; 24 | load afiro; 25 | [f, x, y, s, N] = mpcSol(A, b, c, param_in, Name); 26 | 27 | % Example: blend -- in standard form 28 | clear; 29 | load blend; 30 | param_in.verbose = 2; 31 | [f, x, y, s, N] = mpcSol(A, b, c, param_in, Name); -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2015, Yiming Yan 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /setup.m: -------------------------------------------------------------------------------- 1 | % SETUP 2 | % This scrtipt is used to setup the system enviroment for the solver. 3 | % Necessory paths will be added. 4 | % 5 | % Please run this secript at the root of the folder. 6 | % 7 | % Yiming @ Uinversity of Edinburgh 8 | clear; 9 | clc; 10 | 11 | folderLocation = pwd; 12 | 13 | % add paths 14 | disp('* Add necessary paths...'); 15 | if isunix || ismac 16 | addpath(folderLocation); 17 | addpath([folderLocation '/src']); 18 | addpath([folderLocation '/examples']); 19 | elseif ispc 20 | addpath(folderLocation); 21 | addpath([folderLocation '\src']); 22 | addpath([folderLocation '\examples']); 23 | else 24 | error(' Cannot determine your OS.') 25 | end 26 | 27 | disp(' Done.') 28 | 29 | % try to save the paths to Matlab 30 | reply = input('* Do you want save the paht? Y/N [Y]:','s'); 31 | if isempty(reply) 32 | reply = 'Y'; 33 | end 34 | 35 | if strcmpi(reply,'y') 36 | flag = savepath; 37 | if flag == 1 38 | disp(' Cannot save the path. Please check if you have admin rights.') 39 | else 40 | disp(' Path saved successfully.'); 41 | end 42 | else 43 | disp(' Saving path aborted.'); 44 | end 45 | 46 | disp('Done.'); 47 | 48 | clear; 49 | -------------------------------------------------------------------------------- /src/initialPoint.m: -------------------------------------------------------------------------------- 1 | function [x0,y0,s0] = initialPoint(A,b,c) 2 | % Function INITIALPOINT 3 | % ------------------------------------------------------------------------- 4 | % Syntex: [x0, y0, s0] = initialPoint(A, b, c) 5 | % ------------------------------------------------------------------------- 6 | % Get the starting point (x0,y0,s0) for the primal-dual interior point 7 | % mehtod, especially for Mehrotra's predictor-corrector method. 8 | % 9 | % For reference, please refer to "On the Implementation of a Primal-Dual 10 | % Interior Point Method" by Sanjay Mehrotra. 11 | % ------------------------------------------------------------------------- 12 | % Version 0.3 13 | % ------------------------------------------------------------------------- 14 | % Yiming Yan, University of Edinburgh 21/02/2012 15 | n = size(A,2); 16 | e = ones(n,1); 17 | 18 | % solution for min norm(s) s.t. A'*y + s = c 19 | y = (A*A')\(A*c); 20 | s = c-A'*y; 21 | 22 | % solution for min norm(x) s.t. Ax = b 23 | x = A'*((A*A')\b); 24 | 25 | % delta_x and delta_s 26 | delta_x = max(-1.5*min(x),0); 27 | delta_s = max(-1.5*min(s),0); 28 | 29 | % delta_x_c and delta_s_c 30 | pdct = 0.5*(x+delta_x*e)'*(s+delta_s*e); 31 | delta_x_c = delta_x+pdct/(sum(s)+n*delta_s); 32 | delta_s_c = delta_s+pdct/(sum(x)+n*delta_x); 33 | 34 | % output 35 | x0 = x+delta_x_c*e; 36 | s0 = s+delta_s_c*e; 37 | y0 = y; -------------------------------------------------------------------------------- /src/mpcSol.m: -------------------------------------------------------------------------------- 1 | function [f, x, y, s, N] = mpcSol(A, b, c, param_in, Name) 2 | % Function MPCSOL --- augumented system used 3 | % Mehrotra's predictor-corrector interior point algorithm 4 | % 5 | % Syntax: [f, x, y, s, N] = mpc(A, b, c) 6 | % [f, x, y, s, N] = mpc(A, b, c, param) 7 | % [f, x, y, s, N] = mpc(A, b, c, [], Name) 8 | % [f, x, y, s, N] = mpc(A, b, c, param, Name) 9 | % 10 | % Required Input: 11 | % (A, b, c) --- problem data 12 | % 13 | % Optional Input 14 | % Name --- name of the test problems. 15 | % Default name is testProb 16 | % param --- struct of parameters 17 | % 18 | % Output 19 | % (x, y, s) --- optimal solution 20 | % N --- total number of iterations 21 | % f --- optimal value of the objective function 22 | % 23 | % Version 0.7 24 | % Author: Yiming Yan, University of Edinburgh 25 | % 18/12/2013 26 | 27 | warning('off'); 28 | %% Check inputs and set parameters 29 | if nargin < 5 || isempty(Name) 30 | Name = 'testProb'; 31 | end 32 | 33 | if nargin < 4 || isempty(param_in) 34 | [param, options] = setParamOptions; 35 | else 36 | [param, options] = setParamOptions(param_in); 37 | end 38 | 39 | if nargin < 3 40 | error('MPCSOL: Not enough inputs.'); 41 | end 42 | 43 | % Check if A is sparese 44 | if ~issparse(A) 45 | A = sparse(A); 46 | end 47 | 48 | % Initialization 49 | 50 | [m,n] = size(A); 51 | alphax = 0; alphas = 0; 52 | 53 | if param.verbose > 0 54 | fprintf('\n======== %s ========\n', Name); 55 | end 56 | if param.verbose > 1 57 | fprintf('\n%3s %6s %11s %9s %9s\n',... 58 | 'ITER', 'MU', 'RESIDUAL', 'ALPHAX', 'ALPHAS'); 59 | end 60 | %% Choose initial point 61 | [x, y, s] = initialPoint(A, b, c); 62 | 63 | bc = 1+max([norm(b),norm(c)]); 64 | 65 | % Start the loop 66 | for iter = 0:param.maxN 67 | %% Compute residuals and update mu 68 | Rb = A*x-b; 69 | Rc = A'*y+s-c; 70 | Rxs = x.*s; 71 | mu = mean(Rxs); 72 | 73 | %% Check relative decrease in residual, for purposes of convergence test 74 | residual = norm([Rb;Rc;Rxs])/bc; 75 | 76 | if param.verbose > 1 77 | fprintf('%3d %9.2e %9.2e %9.4g %9.4g\n',... 78 | iter, full(mu), full(residual), alphax, alphas); 79 | end 80 | 81 | if residual < param.eps 82 | break; 83 | end 84 | 85 | %% ----- Predictor step ----- 86 | 87 | % Get affine-scaling direction 88 | [dx_aff, dy_aff, ds_aff, L, D, pm] =... 89 | newtonDirection(Rb, Rc, Rxs, A, m, n,... 90 | x, s, [], [], [], options.errorCheck); 91 | 92 | % Get affine-scaling step length 93 | [alphax_aff, alphas_aff] = stepSize(x, s, dx_aff, ds_aff, 1); 94 | mu_aff = (x+alphax_aff*dx_aff)'*(s+alphas_aff*ds_aff)/n; 95 | 96 | % Set central parameter 97 | sigma = (mu_aff/mu)^3; 98 | 99 | %% ----- Corrector step ----- 100 | 101 | % Set up right hand sides 102 | Rxs = Rxs + dx_aff.*ds_aff - sigma*mu*ones(n,1); 103 | 104 | % Get corrector's direction 105 | [dx_cc, dy_cc, ds_cc, ~, ~, ~] =... 106 | newtonDirection(Rb, Rc, Rxs, A, m, n,... 107 | x, s, L, D, pm, options.errorCheck); 108 | 109 | %% Compute search direction and step 110 | dx = dx_aff+dx_cc; 111 | dy = dy_aff+dy_cc; 112 | ds = ds_aff+ds_cc; 113 | 114 | [alphax, alphas] = stepSize(x, s, dx, ds, param.theta); 115 | 116 | %% Update iterates 117 | x = x + alphax*dx; 118 | y = y + alphas*dy; 119 | s = s + alphas*ds; 120 | 121 | if iter == param.maxN && param.verbose > 1 122 | fprintf('maxN reached!\n'); 123 | end 124 | end 125 | if param.verbose > 0 126 | fprintf('\nDONE! [m,n] = [%d, %d], N = %d\n',m,n,iter); 127 | end 128 | 129 | %% Output results 130 | N = iter; 131 | x = full(x); y = full(y); s = full(s); f = c'*x; 132 | 133 | end 134 | -------------------------------------------------------------------------------- /src/newtonDirection.m: -------------------------------------------------------------------------------- 1 | function [dx, dy, ds, L, D, pm] =... 2 | newtonDirection(Rb, Rc, Rxs, A, m, n, x, s, L, D, pm, errorCheck) 3 | % Function NEWTONDIRECTION 4 | % ----------------------------------------------------------------------- 5 | % Syntax: 6 | % [dx, dy, ds, L, D] =... 7 | % newtonDirection(Rb, Rc, Rxs, A, m, n, x, y, s, L, D, errorCheck) 8 | % ----------------------------------------------------------------------- 9 | % Augmented system is used. More stable but less efficient. For the 10 | % corrector step, we reuse the LDL' factorization from the predictor step 11 | % ----------------------------------------------------------------------- 12 | % Input: 13 | % Output: 14 | % ----------------------------------------------------------------------- 15 | % Version 0.3 23/02/2012 16 | % Yiming Yan @ University of Edinburgh 17 | 18 | rhs = sparse([-Rb; -Rc+Rxs./x]); 19 | D_2 = -min(1e+16, s./x); 20 | B = [sparse(m,m) A; A' sparse(1:n,1:n,D_2)]; 21 | 22 | % ldl' factorization 23 | % if L and D are not provided, we calc new factorization; otherwise, 24 | % reuse them 25 | if isempty(L) || isempty(D) || isempty(pm) 26 | [L,D,pm] = ldl(B,'vector'); 27 | end 28 | 29 | sol(pm,:) = L'\(D\(L\(rhs(pm,:)))); 30 | 31 | dy = sol(1:m); 32 | dx = sol(m+1:m+n); 33 | ds = -(Rxs+s.*dx)./x; 34 | 35 | 36 | if errorCheck == 1 37 | fprintf('error = %6.2e',norm(A'*dy + ds + Rc)+ norm(A*dx + Rb) + norm(s.*dx + x.*ds + Rxs)); 38 | fprintf('\t + err_d = %6.2e',norm(A'*dy + ds + Rc)); 39 | fprintf('\t + err_p = %6.2e',norm(A*dx + Rb)); 40 | fprintf('\t + err_gap = %6.2e\n',norm(s.*dx + x.*ds + Rxs)); 41 | end 42 | end -------------------------------------------------------------------------------- /src/setParamOptions.m: -------------------------------------------------------------------------------- 1 | function [param, options] = setParamOptions(param_in) 2 | % SETPARAMOPTIONS 3 | % ------------------------------------------------------------------------- 4 | % This script is used to set the default value of parameters that control 5 | % the behavior of the algorithm. 6 | % ------------------------------------------------------------------------- 7 | % Input: 8 | % param_in: optional, user-defined parameters 9 | % Output: 10 | % param : struct, parameters 11 | % options : struct, options 12 | % 13 | % ------------------------------------------------------------------------- 14 | % Yiming Yan @ University of Edinburgh 15 | % Version 0.1 Date: 23/02/2012 16 | 17 | param = []; options=[]; 18 | 19 | % Basic paramters --- default value 20 | param.maxN = 100; % maximum number of iterations allowed 21 | param.eps = 1e-08; % tolorence for termination 22 | param.theta = 0.9995; % ratio for choosing step sizes 23 | param.verbose = 2; % 0, nothing 24 | % 1, only optimal information 25 | % 2, iterative info 26 | 27 | if nargin > 0 28 | if isfield(param_in, 'maxN') 29 | param.maxN = param_in.maxN; 30 | end 31 | if isfield(param_in, 'eps') 32 | param.eps = param_in.eps; 33 | end 34 | if isfield(param_in, 'verbose') 35 | param.verbose = param_in.verbose; 36 | end 37 | end 38 | 39 | % Debuging options 40 | options.errorCheck = 0; % 1, check the residuals of Newton's 41 | % directions. 0, do nothing. 42 | -------------------------------------------------------------------------------- /src/stepSize.m: -------------------------------------------------------------------------------- 1 | function [alphax, alphas] = stepSize(x, s, Dx, Ds, eta) 2 | % Function STEPSIZE 3 | % ------------------------------------------------------------------------- 4 | % Syntex: [alphax, alphas] = stepSize(x, s, Dx, Ds, eta) 5 | % ------------------------------------------------------------------------- 6 | % Get the stepsizes (alphax, alphas) for the primal and dual Newton's 7 | % directions 8 | % ------------------------------------------------------------------------- 9 | % Version 0.8 10 | % ------------------------------------------------------------------------- 11 | % Yiming Yan, University of Edinburgh 21/02/2012 12 | 13 | if nargin < 5 14 | eta = 0.9995; 15 | end 16 | if nargin < 4 17 | error('STEPSIZE: Not enough inputs') 18 | end 19 | 20 | alphax = -1/min(min(Dx./x),-1); alphax = min(1, eta * alphax); 21 | alphas = -1/min(min(Ds./s),-1); alphas = min(1, eta * alphas); 22 | %alpha = min(alphax, alphas); 23 | end --------------------------------------------------------------------------------