function [sorted_vertices, ... h_fes, h_bnd, h_fill, h_vert, h_int, h_max, g_labels] = ... plot_feasible(A, b, c, lower_b, upper_b, varargin) % Plot the feasible region of a linear program % % file: plot_feasible.m, (c) Matthew Roughan, 2009 % created: Mon Jun 22 2009 % author: Matthew Roughan % email: matthew.roughan@adelaide.edu.au % % Plot the feasible region of the 2D linear program % maximize f = c'*x % subject to A x <= b % on the region bounded by lower_b and upper_b. Note that the inputs % A is a mx2 matrix, where there are m constraints % b is a mx1 column vector, where there are m constraints % c is a 2x1 column vector % lower_b, upper_b are 2x1 column vectors % % If the problem includes non-negativity bounds, i.e., % x >= 0 % which is quite common, then these need to be explicitly included into % the constraints: A x <= b, not implicitly through the bounds, which are mainly there to % allow us to make a nice plot. % % The feasible region is filled with lines at a setable angle and separation, much as fill % would do with a solid colour, though options allow you to fill with a color, or both. % % This code is primarily (for me) useful for teaching about linear programming, and only % works for 2D problems (for the obvious reason that problems with more than two variables % are hard to plot). % % Note that if you want to cross-hatch just call this routine twice, with a change in angle % of 90 degrees. % % There are a bunch of optional inputs in pairs, as for the plot command % 'linecolor' color of feasible region boundary lines (default black) % 'backgroundcolor' background color of feasible region (default no background) % 'linewidth' width of lines (default 1) % 'linestyle' style of the boundary lines % 'filllinestyle' style of the fill lines % 'linesep' separation of cross-hatch lines (default 1) % if this is set to -1, don't put any fill lines in % 'lineangle' angle (in degrees) of cross-hatch lines % the default is to put them along lines of equal values of the % objective function % 'hold' hold_n=1 means keep previous plot % hold_n=0 (default) means clear previous figure % use this to plot multiple feasible regions on same plot, or to do % crosshatching. % % 'plot_vertices' if defined indicates that we should plot the vertices of the feasible region % the value will determine the symbol to use to plot the vertices, % e.g., 'rx', or 'o+' % 'label_vertices' if not 0, then label the vertices with their position (label_vertices=1) % or value (label_vertices=1) or both (label_vertices=3) % 'label_vertices_size' size of vertex labels, default = 12 % 'label_vertices_color' size of vertex labels, default = 'k' % 'label_vertices_prec' precision of vertex labels (default = 2 decimal places) % % 'plot_intersections' if defined indicates that we should plot the intersection points of boundaries % the value will determine the symbol to use to plot the vertices, % e.g., 'rx', or 'o+' % Note that vertices are also intersection points, and will be double % plotted if used with the plot_vertices option % % 'plot_max' if not 0, then plot the location of the maximum, using the defined symbol % % 'extend_boundaries' if defined, plot each of the constraint lines past the feasible region % using the linewidth and color defined above. The style of the line can % be given as the value, e.g., ':' % % OUTPUTS: % sorted_vertices = a 2x(n+1) vector of the vertices of the feasible region % note the last one is a repeat of the first to aid in plotting the region % % % h_fes = handles to boundary lines of the linear program % h_bnd = handles to the boundary lines % h_fill = handles to the fill lines % h_vert = handles to the vertices (if plotted) % h_int = handles to the intersection points (if plotted) % h_max = a handle to the plotted maximum point % g_labels = handles to vertex labels if used % % version 0.1, July 22nd 2009 % % TODO % speckled fill % testing for boundedness and autosizing the figure % plotting bounds with cross-hatch on feasible side % at present the code uses the optimization toolkit (through the linprog) function % which we use to solve the linear program, but as this is a 2D problem we should be able to % remove this dependence with a little work. % currently the intersections with upper and lower bounds for plotting are also included % in intersection set, and plotted % automate ability to put text description of the problem in the window % % check sizes of inputs sA = size(A); sb = size(b); if (sA(1) ~= sb(1)) error('incorrect input sizes\n'); end if (sA(2) ~= 2) error('can only plot for 2 variables\n'); end n = sA(1); % number of constraints if (size(upper_b,1) ~=2 | size(upper_b,2) ~= 1) error('upper_b is the wrong size\n'); end if (size(lower_b,1) ~=2 | size(lower_b,2) ~= 1) error('lower_b is the wrong size\n'); end % set default output values h_fes = []; h_bnd = []; h_fill = []; h_vert = []; h_int = []; h_max = []; % parse the variable input arguments linecolor = 'k'; backgroundcolor = 0; linewidth = 1; linestyle = '-'; filllinestyle = '-'; linesep = 1; % default line angle is so that lines will be iso-objective if (abs(c(1)) < 1.0e-12) lineangle = 90; % degrees else lineangle = atand(c(2)/c(1)); end hold_n = 0; plot_vertices = 0; label_vertices = 0; label_vertices_size = 12; label_vertices_color = 'k'; label_vertices_prec = 2; plot_intersections = 0; extend_boundaries = 0; plot_max = 0; if (length(varargin) > 0) % process variable arguments for k = 1:2:length(varargin) if (ischar(varargin{k})) argument = char(varargin{k}); value = varargin{k+1}; switch argument case {'linecolor','lc'} linecolor = value; case {'backgroundcolor','bgc'} backgroundcolor = value; case {'linewidth','lw'} linewidth = value; case {'linestyle','ls'} linestyle = value; case {'filllinestyle','fls'} filllinestyle = value; case {'linesep','ls'} linesep = value; case {'lineangle','la'} lineangle = value; case {'hold'} hold_n = value; case {'plot_vertices', 'pv'} plot_vertices = value; case {'label_vertices', 'lv'} label_vertices = value; case {'label_vertices_size', 'lvs'} label_vertices_size = value; case {'label_vertices_color', 'lvc'} label_vertices_color = value; case {'label_vertices_prec', 'lvp'} label_vertices_prec = value; case {'plot_intersections', 'pi'} plot_intersections = value; case {'extend_boundaries', 'eb'} extend_boundaries = value; case {'plot_max', 'pm'} plot_max = value; otherwise error('incorrect input parameters'); end end end end % set up the plot region if (hold_n==0) hold off plot(lower_b(1), lower_b(2)); end diff_x = (upper_b(1)-lower_b(1))/20; diff_y = (upper_b(2)-lower_b(2))/20; set(gca, 'xlim', [lower_b(1)-diff_x upper_b(1)+diff_x]); set(gca, 'ylim', [lower_b(2)-diff_y upper_b(2)+diff_y]); hold on epsilon = 1.0e-14; % plot the boundary line lines of the linear program across the plot region if ~(extend_boundaries == 0) for i=1:n % find end-points of the lines along the boundaries constraint = A(i,:); k = b(i); if (abs(constraint(1)) < epsilon) % fprintf('(abs(constraint(1) < epsilon): i = %d\n', i); if (lower_b(1)<=k/constraint(2) & k/constraint(2)<=upper_b(1)) x = [lower_b(1) upper_b(1)]; y = [1 1] * k/constraint(2); else x = []; y = []; end elseif (abs(constraint(2)) < epsilon) % fprintf('(abs(constraint(2) < epsilon): i = %d\n', i); if (lower_b(2)<=k/constraint(1) & k/constraint(1)<=upper_b(2)) x = [1 1] * k/constraint(1); y = [lower_b(2) upper_b(2)]; else x = []; y = []; end else % compute all four intersections, and decide which is feasible % horizontal intercept points y_1 = [lower_b(2) upper_b(2)]; x_1 = (k - constraint(2)*y_1) / constraint(1); i_1 = find(lower_b(1) 0) % find min and max via linprog to see start and end points of lines % add in implicit boundary constraints when do it c_line = [cosd(lineangle), sind(lineangle)]; x_min = linprog(c_line, A, b, [], [], lower_b, upper_b); % plot(x_min(1), x_min(2), 'b*'); x_max = linprog(-c_line, A, b, [], [], lower_b, upper_b); % plot(x_max(1), x_max(2), 'b*'); distance = sqrt( sum((x_max-x_min).^2) ); % draw lines clipping them at the boundaries for i=0:linesep:distance counter = ceil(i/linesep) + 1; % sigma = x_min + i*(x_max-x_min)/distance; sigma = x_min + i*c_line'; % plot(sigma(1), sigma(2), '+', 'color', linecolor); % for each line, consider its intersepts with each possible % boundary, and check whether they are feasible edge_vertices = []; for j=1:m M = [Aex(j,:); c_line]; d = [bex(j); sum(c_line.*sigma')]; x = M \ d; % test for feasibility if (A*x <= b) edge_vertices = [edge_vertices, x]; end end edge_vertices = round(100*edge_vertices)'/100; edge_vertices = unique(edge_vertices, 'rows')'; if (size(edge_vertices,2) >= 2) h_fill(counter) = plot(edge_vertices(1,:), edge_vertices(2,:), ... 'color', linecolor, 'linestyle', filllinestyle, 'linewidth', linewidth); end end end