Home > matpower7.1 > mp-opt-model > .github > osqp > osqp.m

osqp

PURPOSE ^

SYNOPSIS ^

This is a script file.

DESCRIPTION ^

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 classdef osqp < handle
0002     % osqp interface class for OSQP solver v0.6.0
0003     % This class provides a complete interface to the C implementation
0004     % of the OSQP solver.
0005     %
0006     % osqp Properties:
0007     %   objectHandle - pointer to the C structure of OSQP solver
0008     %
0009     % osqp Methods:
0010     %
0011     %   setup             - configure solver with problem data
0012     %   solve             - solve the QP
0013     %   update            - modify problem vectors
0014     %   warm_start        - set warm starting variables x and y
0015     %
0016     %   default_settings  - create default settings structure
0017     %   current_settings  - get the current solver settings structure
0018     %   update_settings   - update the current solver settings structure
0019     %
0020     %   get_dimensions    - get the number of variables and constraints
0021     %   version           - return OSQP version
0022     %   constant          - return a OSQP internal constant
0023     %
0024     %   codegen           - generate embeddable C code for the problem
0025 
0026 
0027     properties (SetAccess = private, Hidden = true)
0028         objectHandle % Handle to underlying C instance
0029     end
0030     methods
0031         %% Constructor - Create a new solver instance
0032         function this = osqp(varargin)
0033             % Construct OSQP solver class
0034             this.objectHandle = osqp_mex('new', varargin{:});
0035         end
0036 
0037         %% Destructor - destroy the solver instance
0038         function delete(this)
0039             % Destroy OSQP solver class
0040             osqp_mex('delete', this.objectHandle);
0041         end
0042 
0043         %%
0044         function out = current_settings(this)
0045             % CURRENT_SETTINGS get the current solver settings structure
0046             out = osqp_mex('current_settings', this.objectHandle);
0047 
0048        % Convert linsys solver to string
0049        out.linsys_solver = linsys_solver_to_string(out.linsys_solver);
0050 
0051         end
0052 
0053         %%
0054         function update_settings(this,varargin)
0055             % UPDATE_SETTINGS update the current solver settings structure
0056 
0057             %second input 'false' means that this is *not* a settings
0058             %initialization, so some parameter/values will be disallowed
0059             newSettings = validateSettings(this,false,varargin{:});
0060 
0061             %write the solver settings.  C-mex does not check input
0062             %data or protect against disallowed parameter modifications
0063             osqp_mex('update_settings', this.objectHandle, newSettings);
0064 
0065         end
0066 
0067         %%
0068         function [n,m]  = get_dimensions(this)
0069             % GET_DIMENSIONS get the number of variables and constraints
0070 
0071             [n,m] = osqp_mex('get_dimensions', this.objectHandle);
0072 
0073         end
0074 
0075         %%
0076         function update(this,varargin)
0077             % UPDATE modify the linear cost term and/or lower and upper bounds
0078 
0079             %second input 'false' means that this is *not* a settings
0080             %initialization, so some parameter/values will be disallowed
0081             allowedFields = {'q','l','u','Px','Px_idx','Ax','Ax_idx'};
0082 
0083             if(isempty(varargin))
0084                 return;
0085             elseif(length(varargin) == 1)
0086                 if(~isstruct(varargin{1}))
0087                     error('Single input should be a structure with new problem data');
0088                 else
0089                     newData = varargin{1};
0090                 end
0091             else % param / value style assumed
0092                 newData = struct(varargin{:});
0093             end
0094 
0095             %check for unknown fields
0096             newFields = fieldnames(newData);
0097             badFieldsIdx = find(~ismember(newFields,allowedFields));
0098             if(~isempty(badFieldsIdx))
0099                  error('Unrecognized input field ''%s'' detected',newFields{badFieldsIdx(1)});
0100             end
0101 
0102             %get all of the terms.  Nonexistent fields will be passed
0103             %as empty mxArrays
0104             try q = double(full(newData.q(:))); catch q = []; end
0105             try l = double(full(newData.l(:))); catch l = []; end
0106             try u = double(full(newData.u(:))); catch u = []; end
0107             try Px = double(full(newData.Px(:))); catch Px = []; end
0108             try Px_idx = double(full(newData.Px_idx(:))); catch Px_idx = []; end
0109             try Ax = double(full(newData.Ax(:))); catch Ax = []; end
0110             try Ax_idx = double(full(newData.Ax_idx(:))); catch Ax_idx = []; end
0111 
0112             [n,m]  = get_dimensions(this);
0113 
0114             assert(isempty(q) || length(q) == n, 'input ''q'' is the wrong size');
0115             assert(isempty(l) || length(l) == m, 'input ''u'' is the wrong size');
0116             assert(isempty(u) || length(u) == m, 'input ''l'' is the wrong size');
0117             assert(isempty(Px) || isempty(Px_idx) || length(Px) == length(Px_idx), ...
0118                 'inputs ''Px'' and ''Px_idx'' must be the same size');
0119             assert(isempty(Ax) || isempty(Ax_idx) || length(Ax) == length(Ax_idx), ...
0120                 'inputs ''Ax'' and ''Ax_idx'' must be the same size');
0121 
0122             % Adjust index of Px_idx and Ax_idx to match 0-based indexing
0123             % in C
0124             if (~isempty(Px_idx))
0125                 Px_idx = Px_idx - 1;
0126             end
0127             if (~isempty(Ax_idx))
0128                 Ax_idx = Ax_idx - 1;
0129             end
0130             
0131             % Convert infinity values to OSQP_INFTY
0132             if (~isempty(u))
0133                 u = min(u, osqp.constant('OSQP_INFTY'));
0134             end
0135             if (~isempty(l))
0136                 l = max(l, -osqp.constant('OSQP_INFTY'));
0137             end
0138 
0139             %write the new problem data.  C-mex does not protect
0140             %against unknown fields, but will handle empty values
0141             osqp_mex('update', this.objectHandle, ...
0142             q, l, u, Px, Px_idx, length(Px), Ax, Ax_idx, length(Ax));
0143 
0144         end
0145 
0146         %%
0147         function varargout = setup(this, varargin)
0148             % SETUP configure solver with problem data
0149             %
0150             %   setup(P,q,A,l,u,options)
0151 
0152             nargin = length(varargin);
0153 
0154             %dimension checks on user data. Mex function does not
0155             %perform any checks on inputs, so check everything here
0156             assert(nargin >= 5, 'incorrect number of inputs');
0157             [P,q,A,l,u] = deal(varargin{1:5});
0158 
0159             %
0160             % Get problem dimensions
0161             %
0162 
0163             % Get number of variables n
0164             if (isempty(P))
0165                 if (~isempty(q))
0166                     n = length(q);
0167                 else
0168                     if (~isempty(A))
0169                         n = size(A, 2);
0170                     else
0171                         error('The problem does not have any variables');
0172                     end
0173                 end
0174             else
0175                 n = size(P, 1);
0176             end
0177 
0178             % Get number of constraints m
0179             if (isempty(A))
0180                 m = 0;
0181             else
0182                 m = size(A, 1);
0183             end
0184 
0185             %
0186             % Create sparse matrices and full vectors if they are empty
0187             %
0188 
0189             if (isempty(P))
0190                 P = sparse(n, n);
0191             else
0192                 P = sparse(P);
0193             end
0194             if (~istriu(P))
0195                 P = triu(P);
0196             end
0197             if (isempty(q))
0198                 q = zeros(n, 1);
0199             else
0200                 q   = full(q(:));
0201             end
0202 
0203             % Create proper constraints if they are not passed
0204             if (isempty(A) && (~isempty(l) || ~isempty(u))) || ...
0205                 (~isempty(A) && (isempty(l) && isempty(u)))
0206                 error('A must be supplied together with at least one bound l or u');
0207             end
0208 
0209             if (~isempty(A) && isempty(l))
0210                 l = -Inf(m, 1);
0211             end
0212 
0213             if (~isempty(A) && isempty(u))
0214                 u = Inf(m, 1);
0215             end
0216 
0217             if (isempty(A))
0218                 A = sparse(m, n);
0219                 l = -Inf(m, 1);
0220                 u = Inf(m, 1);
0221             else
0222                 l  = full(l(:));
0223                 u  = full(u(:));
0224                 A = sparse(A);
0225             end
0226 
0227 
0228             %
0229             % Check vector dimensions (not checked from the C solver)
0230             %
0231 
0232             assert(length(q) == n, 'Incorrect dimension of q');
0233             assert(length(l) == m, 'Incorrect dimension of l');
0234             assert(length(u) == m, 'Incorrect dimension of u');
0235 
0236             %
0237             % Convert infinity values to OSQP_INFINITY
0238             %
0239             u = min(u, osqp.constant('OSQP_INFTY'));
0240             l = max(l, -osqp.constant('OSQP_INFTY'));
0241 
0242 
0243             %make a settings structure from the remainder of the arguments.
0244             %'true' means that this is a settings initialization, so all
0245             %parameter/values are allowed.  No extra inputs will result
0246             %in default settings being passed back
0247             theSettings = validateSettings(this,true,varargin{6:end});
0248 
0249             [varargout{1:nargout}] = osqp_mex('setup', this.objectHandle, n,m,P,q,A,l,u,theSettings);
0250 
0251         end
0252 
0253 
0254         %%
0255 
0256         function warm_start(this, varargin)
0257             % WARM_START warm start primal and/or dual variables
0258             %
0259             %   warm_start('x', x, 'y', y)
0260             %
0261             %   or warm_start('x', x)
0262             %   or warm_start('y', y)
0263 
0264 
0265             % Get problem dimensions
0266             [n, m]  = get_dimensions(this);
0267 
0268             % Get data
0269             allowedFields = {'x','y'};
0270 
0271             if(isempty(varargin))
0272                 return;
0273             elseif(length(varargin) == 1)
0274                 if(~isstruct(varargin{1}))
0275                     error('Single input should be a structure with new problem data');
0276                 else
0277                     newData = varargin{1};
0278                 end
0279             else % param / value style assumed
0280                 newData = struct(varargin{:});
0281             end
0282 
0283             %check for unknown fields
0284             newFields = fieldnames(newData);
0285             badFieldsIdx = find(~ismember(newFields,allowedFields));
0286             if(~isempty(badFieldsIdx))
0287                  error('Unrecognized input field ''%s'' detected',newFields{badFieldsIdx(1)});
0288             end
0289 
0290             %get all of the terms.  Nonexistent fields will be passed
0291             %as empty mxArrays
0292             try x = double(full(newData.x(:))); catch x = []; end
0293             try y = double(full(newData.y(:))); catch y = []; end
0294 
0295             % Check dimensions
0296             assert(isempty(x) || length(x) == n, 'input ''x'' is the wrong size');
0297             assert(isempty(y) || length(y) == m, 'input ''y'' is the wrong size');
0298 
0299 
0300             % Decide which function to call
0301             if (~isempty(x) && isempty(y))
0302                 osqp_mex('warm_start_x', this.objectHandle, x);
0303                 return;
0304             end
0305 
0306             if (isempty(x) && ~isempty(y))
0307                 osqp_mex('warm_start_y', this.objectHandle, y);
0308             end
0309 
0310             if (~isempty(x) && ~isempty(y))
0311                 osqp_mex('warm_start', this.objectHandle, x, y);
0312             end
0313 
0314             if (isempty(x) && isempty(y))
0315                 error('Unrecognized fields');
0316             end
0317 
0318         end
0319 
0320         %%
0321         function varargout = solve(this, varargin)
0322             % SOLVE solve the QP
0323 
0324             nargoutchk(0,1);  %either return nothing (but still solve), or a single output structure
0325             [out.x, out.y, out.prim_inf_cert, out.dual_inf_cert, out.info] = osqp_mex('solve', this.objectHandle);
0326             if(nargout)
0327                 varargout{1} = out;
0328             end
0329             return;
0330         end
0331 
0332         %%
0333         function codegen(this, target_dir, varargin)
0334             % CODEGEN generate C code for the parametric problem
0335             %
0336             %   codegen(target_dir,options)
0337 
0338             % Parse input arguments
0339             p = inputParser;
0340             defaultProject = '';
0341             expectedProject = {'', 'Makefile', 'MinGW Makefiles', 'Unix Makefiles', 'CodeBlocks', 'Xcode'};
0342             defaultParams = 'vectors';
0343             expectedParams = {'vectors', 'matrices'};
0344             defaultMexname = 'emosqp';
0345             defaultFloat = false;
0346             defaultLong = true;
0347             defaultFW = false;
0348 
0349             addRequired(p, 'target_dir', @isstr);
0350             addParameter(p, 'project_type', defaultProject, ...
0351                          @(x) ischar(validatestring(x, expectedProject)));
0352             addParameter(p, 'parameters', defaultParams, ...
0353                          @(x) ischar(validatestring(x, expectedParams)));
0354             addParameter(p, 'mexname', defaultMexname, @isstr);
0355             addParameter(p, 'FLOAT', defaultFloat, @islogical);
0356             addParameter(p, 'LONG', defaultLong, @islogical);
0357             addParameter(p, 'force_rewrite', defaultFW, @islogical);
0358 
0359             parse(p, target_dir, varargin{:});
0360 
0361             % Set internal variables
0362             if strcmp(p.Results.parameters, 'vectors')
0363                 embedded = 1;
0364             else
0365                 embedded = 2;
0366             end
0367             if p.Results.FLOAT
0368                 float_flag = 'ON';
0369             else
0370                 float_flag = 'OFF';
0371             end
0372             if p.Results.LONG
0373                 long_flag = 'ON';
0374             else
0375                 long_flag = 'OFF';
0376             end
0377             if strcmp(p.Results.project_type, 'Makefile')
0378                 if (ispc)
0379                     project_type = 'MinGW Makefiles';   % Windows
0380                 elseif (ismac || isunix)
0381                     project_type = 'Unix Makefiles';    % Unix
0382                 end
0383             else
0384                 project_type = p.Results.project_type;
0385             end
0386 
0387             % Check whether the specified directory already exists
0388             if exist(target_dir, 'dir')
0389                 if p.Results.force_rewrite
0390                     rmdir(target_dir, 's');
0391                 else
0392                     while(1)
0393                         prompt = sprintf('Directory "%s" already exists. Do you want to replace it? y/n [y]: ', target_dir);
0394                         str = input(prompt, 's');
0395 
0396                         if any(strcmpi(str, {'','y'}))
0397                             rmdir(target_dir, 's');
0398                             break;
0399                         elseif strcmpi(str, 'n')
0400                             return;
0401                         end
0402                     end
0403                 end
0404             end
0405 
0406             % Import OSQP path
0407             [osqp_path,~,~] = fileparts(which('osqp.m'));
0408 
0409             % Add codegen directory to path
0410             addpath(fullfile(osqp_path, 'codegen'));
0411 
0412             % Path of osqp module
0413             cg_dir = fullfile(osqp_path, 'codegen');
0414             files_to_generate_path = fullfile(cg_dir, 'files_to_generate');
0415 
0416             % Get workspace structure
0417             work = osqp_mex('get_workspace', this.objectHandle);
0418 
0419             % Make target directory
0420             fprintf('Creating target directories...\t\t\t\t\t');
0421             target_configure_dir = fullfile(target_dir, 'configure');
0422             target_include_dir = fullfile(target_dir, 'include');
0423             target_src_dir = fullfile(target_dir, 'src');
0424 
0425             if ~exist(target_dir, 'dir')
0426                 mkdir(target_dir);
0427             end
0428             if ~exist(target_configure_dir, 'dir')
0429                 mkdir(target_configure_dir);
0430             end
0431             if ~exist(target_include_dir, 'dir')
0432                 mkdir(target_include_dir);
0433             end
0434             if ~exist(target_src_dir, 'dir')
0435                 mkdir(fullfile(target_src_dir, 'osqp'));
0436             end
0437             fprintf('[done]\n');
0438 
0439             % Copy source files to target directory
0440             fprintf('Copying OSQP source files...\t\t\t\t\t');
0441             cdir   = fullfile(cg_dir, 'sources', 'src');
0442             cfiles = dir(fullfile(cdir, '*.c'));
0443             for i = 1 : length(cfiles)
0444                 if embedded == 1
0445                     % Do not copy kkt.c if embedded is 1
0446                     if ~strcmp(cfiles(i).name, 'kkt.c')
0447                         copyfile(fullfile(cdir, cfiles(i).name), ...
0448                             fullfile(target_src_dir, 'osqp', cfiles(i).name));    
0449                     end
0450                 else
0451                     copyfile(fullfile(cdir, cfiles(i).name), ...
0452                         fullfile(target_src_dir, 'osqp', cfiles(i).name));
0453                 end
0454             end
0455             configure_dir = fullfile(cg_dir, 'sources', 'configure');
0456             configure_files = dir(fullfile(configure_dir, '*.h.in'));
0457             for i = 1 : length(configure_files)
0458                 copyfile(fullfile(configure_dir, configure_files(i).name), ...
0459                     fullfile(target_configure_dir, configure_files(i).name));
0460             end
0461             hdir   = fullfile(cg_dir, 'sources', 'include');
0462             hfiles = dir(fullfile(hdir, '*.h'));
0463             for i = 1 : length(hfiles)
0464                 if embedded == 1
0465                     % Do not copy kkt.h if embedded is 1
0466                     if ~strcmp(hfiles(i).name, 'kkt.h')
0467                         copyfile(fullfile(hdir, hfiles(i).name), ...
0468                             fullfile(target_include_dir, hfiles(i).name));  
0469                     end
0470                 else
0471                     copyfile(fullfile(hdir, hfiles(i).name), ...
0472                         fullfile(target_include_dir, hfiles(i).name));
0473                 end
0474             end
0475 
0476                 % Copy cmake files
0477             copyfile(fullfile(cdir, 'CMakeLists.txt'), ...
0478                     fullfile(target_src_dir, 'osqp', 'CMakeLists.txt'));
0479             copyfile(fullfile(hdir, 'CMakeLists.txt'), ...
0480                     fullfile(target_include_dir, 'CMakeLists.txt'));
0481             fprintf('[done]\n');
0482 
0483             % Copy example.c
0484             copyfile(fullfile(files_to_generate_path, 'example.c'), target_src_dir);
0485 
0486             % Render CMakeLists.txt
0487             fidi = fopen(fullfile(files_to_generate_path, 'CMakeLists.txt'),'r');
0488             fido = fopen(fullfile(target_dir, 'CMakeLists.txt'),'w');
0489             while ~feof(fidi)
0490                 l = fgetl(fidi);   % read line
0491                 % Replace EMBEDDED_FLAG in CMakeLists.txt by a numerical value
0492                 newl = strrep(l, 'EMBEDDED_FLAG', num2str(embedded));
0493                 fprintf(fido, '%s\n', newl);
0494             end
0495             fclose(fidi);
0496             fclose(fido);
0497 
0498             % Render workspace.h and workspace.c
0499             work_hfile = fullfile(target_include_dir, 'workspace.h');
0500             work_cfile = fullfile(target_src_dir, 'osqp', 'workspace.c');
0501             fprintf('Generating workspace.h/.c...\t\t\t\t\t\t');
0502             render_workspace(work, work_hfile, work_cfile, embedded);
0503             fprintf('[done]\n');
0504 
0505             % Create project
0506             if ~isempty(project_type)
0507                 fprintf('Creating project...\t\t\t\t\t\t\t\t');
0508                 orig_dir = pwd;
0509                 cd(target_dir);
0510                 mkdir('build')
0511                 cd('build');
0512                 cmd = sprintf('cmake -G "%s" ..', project_type);
0513                 [status, output] = system(cmd);
0514                 if(status)
0515                     fprintf('\n');
0516                     fprintf(output);
0517                     error('Error configuring CMake environment');
0518                 else
0519                     fprintf('[done]\n');
0520                 end
0521                 cd(orig_dir);
0522             end
0523 
0524             % Make mex interface to the generated code
0525             mex_cfile  = fullfile(files_to_generate_path, 'emosqp_mex.c');
0526             make_emosqp(target_dir, mex_cfile, embedded, float_flag, long_flag);
0527 
0528             % Rename the mex file
0529             old_mexfile = ['emosqp_mex.', mexext];
0530             new_mexfile = [p.Results.mexname, '.', mexext];
0531             movefile(old_mexfile, new_mexfile);
0532 
0533         end
0534 
0535     end
0536     methods(Static)
0537         %%
0538         function out = default_settings()
0539             % DEFAULT_SETTINGS get the default solver settings structure
0540             out = osqp_mex('default_settings', 'static');
0541 
0542             % Convert linsys solver to string
0543             out.linsys_solver = linsys_solver_to_string(out.linsys_solver);
0544 
0545         end
0546         
0547         %%
0548         function out = constant(constant_name)
0549             % CONSTANT Return solver constant
0550             %   C = CONSTANT(CONSTANT_NAME) return constant called CONSTANT_NAME
0551             out = osqp_mex('constant', 'static', constant_name);
0552         end
0553         
0554         %%
0555         function out = version()
0556             % Return OSQP version
0557             out = osqp_mex('version', 'static');
0558         end
0559         
0560     end
0561 end

Generated on Fri 09-Oct-2020 11:21:31 by m2html © 2005