├── .bumpversion.cfg ├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── compile_direct.m ├── compile_gpu.m ├── compile_indirect.m ├── examples ├── README ├── random_cone_problem.m ├── run_examples_cvx.m ├── run_l1logreg_ex.m ├── run_lasso_ex.m ├── run_pnorm_ex.m ├── run_portfolio_ex.m ├── run_rpca_ex.m └── solve_cone_cvx.m ├── make_scs.m ├── scs.m ├── scs_direct.m ├── scs_indirect.m ├── scs_mex.c ├── scs_version_mex.c └── test ├── basic.m ├── quad_box.m ├── string_params.m └── zero.m /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 3.2.5 3 | 4 | [bumpversion:file:scs.m] 5 | 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Build (${{ matrix.os }}) 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [ubuntu-latest] # not working: [macos-latest, windows-latest] 12 | 13 | runs-on: ${{ matrix.os }} 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | submodules: 'recursive' 19 | 20 | - uses: matlab-actions/setup-matlab@v2 21 | 22 | - uses: matlab-actions/run-command@v2 23 | with: 24 | command: make_scs 25 | 26 | - name: Test 27 | uses: matlab-actions/run-tests@v2 28 | with: 29 | source-folder: ./ 30 | select-by-folder: test 31 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | examples/DIMACS/ 3 | local.mk 4 | sftp-config.json 5 | .settings 6 | .cproject 7 | .project 8 | .pydevproject 9 | .idea 10 | *.pyc 11 | *dSYM 12 | *demo_direct 13 | *demo_indirect 14 | python/flags.tmp 15 | python/.coverage 16 | python/build/ 17 | python/cover/ 18 | python_dist 19 | linsys_dense/out 20 | test/data_dump_* 21 | test/log_* 22 | out 23 | .swo 24 | *.o 25 | *.a 26 | *.DS_Store 27 | *.out 28 | *.nav 29 | *.dvi 30 | *.ps 31 | *.pdf 32 | *.aux 33 | *.log 34 | *.bbl 35 | *.blg 36 | *.toc 37 | *.swp 38 | *.m~ 39 | *.mat 40 | *.mex* 41 | *.mexmaci 42 | *.mexa64 43 | *.mexmaci64 44 | *.dependencies 45 | *.classpath 46 | *.class 47 | *.dylib 48 | *.jar 49 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "scs"] 2 | path = scs 3 | url = https://github.com/cvxgrp/scs.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Brendan O'Donoghue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | scs-matlab 2 | === 3 | 4 | [![Build Status](https://github.com/bodono/scs-matlab/actions/workflows/build.yml/badge.svg)](https://github.com/bodono/scs-matlab/actions/workflows/build.yml) 5 | [![Documentation](https://img.shields.io/badge/docs-online-brightgreen?logo=read-the-docs&style=flat)](https://www.cvxgrp.org/scs/) 6 | 7 | Matlab interface for [SCS](https://github.com/cvxgrp/scs) 3.0.0 and higher. 8 | The full documentation is available [here](https://www.cvxgrp.org/scs/). 9 | -------------------------------------------------------------------------------- /compile_direct.m: -------------------------------------------------------------------------------- 1 | function compile_direct(flags, common_scs) 2 | % compile direct 3 | cmd = sprintf('mex -O -v %s %s %s %s COMPFLAGS="$COMPFLAGS %s" CFLAGS="$CFLAGS %s" -Iscs -Iscs/linsys -Iscs/include', flags.arr, flags.LCFLAG, flags.INCS, flags.INT, flags.COMPFLAGS, flags.CFLAGS); 4 | 5 | amd_files = {'amd_order', 'amd_dump', 'amd_postorder', 'amd_post_tree', ... 6 | 'amd_aat', 'amd_2', 'amd_1', 'amd_defaults', 'amd_control', ... 7 | 'amd_info', 'amd_valid', 'amd_global', 'amd_preprocess', ... 8 | 'SuiteSparse_config'} ; 9 | for i = 1 : length (amd_files) 10 | cmd = sprintf ('%s scs/linsys/external/amd/%s.c', cmd, amd_files {i}) ; 11 | end 12 | 13 | cmd = sprintf ('%s %s scs/linsys/external/qdldl/qdldl.c scs/linsys/cpu/direct/private.c %s %s %s -output scs_direct', cmd, common_scs, flags.link, flags.LOCS, flags.BLASLIB); 14 | eval(cmd); 15 | -------------------------------------------------------------------------------- /compile_gpu.m: -------------------------------------------------------------------------------- 1 | function compile_gpu(flags, common_scs) 2 | 3 | flags.link = sprintf('-lcudart -lcublas -lcusparse %s', flags.link); 4 | flags.INCS = sprintf('-I/usr/local/cuda/include %s', flags.INCS); 5 | if (ismac()) 6 | flags.link = sprintf('-L/usr/local/cuda/lib %s', flags.link); 7 | else 8 | % TODO probably not right for windows 9 | flags.link = sprintf('-L/usr/local/cuda/lib64 %s', flags.link); 10 | end 11 | 12 | % compile gpu 13 | cmd = sprintf('mex -O -v %s %s %s COMPFLAGS="$COMPFLAGS %s" CFLAGS="$CFLAGS %s" scs/linsys/gpu/indirect/private.c %s -Iscs -Iscs/linsys -Iscs/include %s %s %s -output scs_gpu', flags.arr, flags.LCFLAG, common_scs, flags.COMPFLAGS, flags.CFLAGS, flags.INCS, flags.link, flags.LOCS, flags.BLASLIB); 14 | eval(cmd); 15 | -------------------------------------------------------------------------------- /compile_indirect.m: -------------------------------------------------------------------------------- 1 | function compile_indirect(flags, common_scs) 2 | % compile indirect 3 | cmd = sprintf('mex -O -v %s %s %s %s -DINDIRECT COMPFLAGS="$COMPFLAGS %s" CFLAGS="$CFLAGS %s" scs/linsys/cpu/indirect/private.c %s -Iscs -Iscs/linsys -Iscs/include %s %s %s -output scs_indirect', flags.arr, flags.LCFLAG, common_scs, flags.INCS, flags.COMPFLAGS, flags.CFLAGS, flags.link, flags.LOCS, flags.BLASLIB, flags.INT); 4 | eval(cmd); 5 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | This directory contains examples for SCS in matlab. 2 | -------------------------------------------------------------------------------- /examples/random_cone_problem.m: -------------------------------------------------------------------------------- 1 | clear all;close all; 2 | 3 | addpath('../../matlab') 4 | addpath('./scs_matlab') 5 | 6 | %cd '../../matlab'; make_scs; cd '../examples/matlab'; 7 | 8 | randn('seed',0);rand('seed',0); 9 | 10 | %% generate random cone problem (solution NOT necessarily unique): 11 | 12 | %%% types of probs to solve: 13 | gen_feasible = true; 14 | gen_infeasible = true; 15 | gen_unbounded = true; 16 | %%% solvers to test: 17 | run_indirect = true; 18 | run_direct = true; 19 | run_cvx = true; % won't work if ep or ed > 0 20 | cvx_solver = 'scs'; 21 | run_scs_matlab = false; % SCS implemented in MATLAB 22 | 23 | % set cone sizes (ep = ed = 0 if you want to compare against cvx): 24 | K = struct('f',50,'l',60,'q',[0;1;0;2;100;20],'s',[0;2;1;2;20],'ep',10,'ed',15,'p',[-0.3, 0.25, 0.75, -0.4]); 25 | 26 | density = 0.1; % A matrix density 27 | 28 | m = get_scs_cone_dims(K); 29 | n = round(m/3); 30 | params = struct('eps', 1e-5, 'normalize', 1, 'scale', 1, 'cg_rate', 2, 'max_iters', 5000, 'alpha', 1.8); 31 | 32 | %% generate primal-dual feasible cone prob: 33 | % Ax + s = b, s \in K, A'y + c = 0, y \in K*, s'y = 0 34 | if (gen_feasible) 35 | z = randn(m,1); 36 | y = proj_dual_cone(z,K); % y = s - z; 37 | s = y - z; %s = proj_cone(z,K); 38 | 39 | A = sprandn(m,n,density); 40 | x = randn(n,1); 41 | c = -A'*y; 42 | b = A*x + s; 43 | 44 | data.A = A; 45 | data.b = b; 46 | data.c = c; 47 | 48 | %cd '../../matlab'; write_scs_data(data,K,params,'randomScsConeFeasible'); cd '../examples/matlab'; 49 | 50 | %indirect 51 | if (run_indirect) 52 | [xi,yi,si,infoi] = scs_indirect(data,K,params); 53 | c'*x 54 | (c'*xi - c'*x) / (c'*x) 55 | b'*y 56 | (b'*yi - b'*y) / (b'*y) 57 | end 58 | if (run_direct) 59 | % direct: 60 | [xd,yd,sd,infod] = scs_direct(data,K,params); 61 | c'*x 62 | (c'*xd - c'*x) / (c'*x) 63 | b'*y 64 | (b'*yd - b'*y) / (b'*y) 65 | end 66 | if (run_cvx) [xc,yc,sc] = solve_cone_cvx(data,K,cvx_solver); end 67 | if (run_scs_matlab) 68 | params.use_indirect = true; 69 | [xi_m,yi_m,si_m,infoi_m] = scs_matlab(data,K,params); 70 | c'*x 71 | (c'*xi_m - c'*x) / (c'*x) 72 | b'*yi_m 73 | (b'*yi_m - b'*y) / (b'*y) 74 | params.use_indirect = false; 75 | [xd_m,yd_m,sd_m,infod_m] = scs_matlab(data,K,params); 76 | c'*x 77 | (c'*xd_m - c'*x) / (c'*x) 78 | b'*y 79 | (b'*yd_m - b'*y) / (b'*y) 80 | end 81 | end 82 | 83 | %% generate infeasible (NOT SPARSE,SLOW AS A RESULT) 84 | % A'y = 0, y \in K*, b'*y = -1 85 | if (gen_infeasible) 86 | z = randn(m,1); 87 | y = proj_dual_cone(z,K); % y = s - z; 88 | A = randn(m,n); 89 | 90 | A = A - ((A'*y)*y'/norm(y)^2)'; % dense... 91 | 92 | b = randn(m,1); 93 | b = -b / (b'*y); 94 | 95 | data.A = sparse(A); 96 | data.b = b; 97 | data.c = randn(n,1); 98 | 99 | params.scale = 0.5; 100 | %indirect 101 | if(run_indirect) [xi,yi,si,infoi] = scs_indirect(data,K,params); end 102 | % direct: 103 | if (run_direct) [xd,yd,sd,infod] = scs_direct(data,K,params); end 104 | 105 | % cvx: 106 | if (run_cvx) [xc,yc,sc] = solve_cone_cvx(data,K,cvx_solver); end 107 | 108 | if(run_scs_matlab) 109 | params.use_indirect = true; 110 | [xi_m,yi_m,si_m,infoi_m] = scs_matlab(data,K,params); 111 | params.use_indirect = false; 112 | [xd_m,yd_m,sd_m,infod_m] = scs_matlab(data,K,params); 113 | end 114 | 115 | end 116 | %% generate unbounded (NOT SPARSE,SLOW AS A RESULT) 117 | % Ax + s = 0, s \in K, c'*x = -1 118 | if(gen_unbounded) 119 | z = randn(m,1); 120 | s = proj_cone(z,K); 121 | A = randn(m,n); 122 | x = randn(n,1); 123 | A = A - (s + A*x)*x'/(norm(x)^2); % dense... 124 | c = randn(n,1); 125 | c = - c / (c'*x); 126 | 127 | data.A = sparse(A); 128 | data.b = randn(m,1); 129 | data.c = c; 130 | 131 | params.scale = 0.5; 132 | %indirect 133 | if(run_indirect) [xi,yi,si,infoi] = scs_indirect(data,K,params); end 134 | % direct: 135 | if (run_direct) [xd,yd,sd,infod] = scs_direct(data,K,params); end 136 | 137 | if (run_cvx) [xc,yc,sc] = solve_cone_cvx(data,K,cvx_solver); end 138 | 139 | if(run_scs_matlab) 140 | params.use_indirect = true; 141 | [xi_m,yi_m,si_m,infoi_m] = scs_matlab(data,K,params); 142 | params.use_indirect = false; 143 | [xd_m,yd_m,sd_m,infod_m] = scs_matlab(data,K,params); 144 | end 145 | end 146 | -------------------------------------------------------------------------------- /examples/run_examples_cvx.m: -------------------------------------------------------------------------------- 1 | addpath ~/cvx 2 | addpath ~/scs/matlab 3 | addpath ~/ecos-matlab/bin 4 | 5 | cvx_setup ~/cvx_license.dat 6 | 7 | parameters.run_cvx = true; 8 | parameters.save_results = true; 9 | parameters.run_scs_direct = false; 10 | parameters.run_scs_indirect = false; 11 | 12 | solvers = {'mosek', 'gurobi', 'ecos', 'sdpt3', 'sedumi'} 13 | 14 | for i = 1:length(solvers) 15 | parameters.cvx_use_solver = solvers{i} 16 | run_lasso_ex(parameters) 17 | run_portfolio_ex(parameters) 18 | run_rpca_ex(parameters) 19 | run_pnorm_ex(parameters) 20 | %run_l1logreg_ex(parameters) 21 | end 22 | -------------------------------------------------------------------------------- /examples/run_l1logreg_ex.m: -------------------------------------------------------------------------------- 1 | function run_l1logreg_ex(params) 2 | 3 | disp('------------------------------------------------------------') 4 | disp('WARNING: this can take a very long time to run.') 5 | disp('It may also crash/run out of memory.') 6 | disp('------------------------------------------------------------') 7 | 8 | save_results = false; 9 | run_cvx = false; 10 | cvx_use_solver = 'sdpt3'; 11 | run_scs_direct = true; 12 | run_scs_indirect = true; 13 | 14 | if nargin==1 15 | if isfield(params,'save_results'); save_results = params.save_results;end 16 | if isfield(params,'run_cvx'); run_cvx = params.run_cvx;end 17 | if isfield(params,'cvx_use_solver');cvx_use_solver = params.cvx_use_solver;end 18 | if isfield(params,'run_scs_direct');run_scs_direct = params.run_scs_direct;end 19 | if isfield(params,'run_scs_indirect');run_scs_indirect = params.run_scs_indirect;end 20 | end 21 | 22 | sizes = [100 10000; 1000 100000; 10000 1000000]; 23 | sze_str{1} = 'small'; 24 | sze_str{2} = 'med'; 25 | sze_str{3} = 'large'; 26 | density = 0.1; 27 | 28 | %% 29 | for i=1:size(sizes,1) 30 | disp(sprintf('ScsSolutionving %s l1 regularized logistic regresssion.',sze_str{i})) 31 | clearvars -except i sizes sze_str direct_data indirect_data save_results density run_scs_direct run_scs_indirect run_cvx cvx_use_solver 32 | 33 | str = ['data/l1logreg_' sze_str{i}]; 34 | randn('seed',sum(str));rand('seed',sum(str)) 35 | 36 | p = sizes(i,1); % features 37 | q = sizes(i,2); % total samples 38 | w_true = sprandn(p,1,0.2); 39 | 40 | X_tmp = 3*sprandn(p, q, density); 41 | ips = -w_true'*X_tmp; 42 | ps = (exp(ips)./(1 + exp(ips)))'; 43 | labels = 2*(rand(q,1) < ps) - 1; 44 | 45 | X_pos = X_tmp(:,labels==1); 46 | X_neg = X_tmp(:,labels==-1); 47 | 48 | X = [X_pos -X_neg]; % include labels with data 49 | 50 | lam = min(0.01*norm(X*ones(q,1),'inf')/2, 10); % too large gives bad results 51 | 52 | clear X_tmp ips ps labels; 53 | %% 54 | c = [zeros(p,1);lam*ones(p,1);ones(q,1);zeros(q,1);zeros(q,1)]; 55 | b = [zeros(p,1);zeros(p,1);ones(q,1)]; 56 | 57 | Anz = nnz(X) + 6*q + 4*p; 58 | 59 | %At = zeros(2*p + 3*q,2*p + q + 6*q); 60 | At = sparse([],[],[],2*p + 3*q,2*p + q + 6*q,Anz); 61 | At(:,1:2*p+q) = [speye(p) -speye(p) sparse(p,q) sparse(p,q) sparse(p,q); 62 | -speye(p) -speye(p) sparse(p,q) sparse(p,q) sparse(p,q); 63 | sparse(q,p) sparse(q,p) sparse(q,q) speye(q) speye(q)]'; 64 | idx = 2*p+q; 65 | for j=1:q 66 | b = [b;[0;1;0]]; 67 | M1 = sparse(q,3); 68 | M1(j,1) = 1; 69 | M2 = sparse(q,3); 70 | M2(j,3) = -1; 71 | At(:,idx+1:idx+3) = [sparse(p,3); sparse(p,3); M1; M2; sparse(q,3)]; 72 | idx = idx + 3; 73 | end 74 | for j=1:q 75 | b = [b;[0;1;0]]; 76 | M1 = sparse(q,3); 77 | M1(j,1) = 1; 78 | M2 = sparse(q,3); 79 | M2(j,3) = -1; 80 | At(:,idx+1:idx+3) = [[-X(:,j) sparse(p,2)]; sparse(p,3); M1 ; sparse(q,3); M2]; 81 | idx = idx + 3; 82 | end 83 | A = sparse(At'); 84 | data.A = A; 85 | data.b = b; 86 | data.c = c; 87 | 88 | K.f = 0; 89 | K.l = p+p+q; 90 | K.q = []; 91 | K.s = []; 92 | K.ep = 2*q; 93 | K.ed = 0; 94 | 95 | params.verbose = 1; 96 | params.scale = 5; 97 | params.cg_rate = 1.5; 98 | 99 | %write_scs_data_sparse(data,K,params,str) 100 | 101 | if (run_scs_direct) 102 | if (save_results); 103 | [direct_data.output{i}, xd,yd,sd,infod] = evalc('scs_direct(data,K,params);'); 104 | direct_data.output{i} 105 | else 106 | [xd,yd,sd,infod]=scs_direct(data,K,params); 107 | end 108 | direct_data.x{i} = xd; 109 | direct_data.y{i} = yd; 110 | direct_data.s{i} = sd; 111 | direct_data.info{i} = infod; 112 | if (save_results); 113 | save('data/l1logreg_direct', 'direct_data'); 114 | end 115 | end 116 | 117 | if (run_scs_indirect) 118 | if (save_results); 119 | [indirect_data.output{i},xi,yi,si,infoi] = evalc('scs_indirect(data,K,params);'); 120 | indirect_data.output{i} 121 | else 122 | [xi,yi,si,infoi]=scs_indirect(data,K,params); 123 | end 124 | indirect_data.x{i} = xi; 125 | indirect_data.y{i} = yi; 126 | indirect_data.s{i} = si; 127 | indirect_data.info{i} = infoi; 128 | if (save_results); 129 | save('data/l1logreg_indirect', 'indirect_data'); 130 | end 131 | end 132 | 133 | if run_cvx 134 | try 135 | tic 136 | cvx_begin 137 | cvx_solver(cvx_use_solver) 138 | variable w(p) 139 | minimize(sum(log_sum_exp([zeros(1,q);w'*full(X)])) + lam * norm(w,1)) 140 | if (save_results) 141 | cvx.output{i} = evalc('cvx_end') 142 | else 143 | cvx_end 144 | end 145 | toc 146 | 147 | catch err 148 | err 149 | cvx.err{i} = err; 150 | end 151 | 152 | if (save_results); save(sprintf('data/l1logreg_cvx_%s', cvx_use_solver), 'cvx'); end; 153 | 154 | end 155 | 156 | end 157 | 158 | %{ 159 | %% cvx can solve: 160 | cvx_begin 161 | variable w(p) 162 | minimize(sum(log_sum_exp([zeros(1,q);w'*full(X)])) + lam * norm(w,1)) 163 | cvx_end 164 | %} 165 | %{ 166 | %% cvx cone formulation: 167 | tic 168 | cvx_begin 169 | variables x(2*p + 3*q) s(2*p + q + 6*q) 170 | minimize(c'*x) 171 | A*x + s == b 172 | s(1:K.l) >= 0 173 | idx = K.l; 174 | for j=1:K.ep 175 | -s(idx+1) >= rel_entr(s(idx+2),s(idx+3)); 176 | idx = idx + 3; 177 | end 178 | output = evalc('cvx_end') 179 | cvx.output{i} = output; 180 | cvx.x{i} = x; 181 | cvx.s{i} = s; 182 | if (save_results); save('data/l1logreg_cvx', 'cvx'); end 183 | %} 184 | -------------------------------------------------------------------------------- /examples/run_lasso_ex.m: -------------------------------------------------------------------------------- 1 | function run_lasso_ex(params) 2 | 3 | disp('------------------------------------------------------------') 4 | disp('WARNING: this can take a very long time to run.') 5 | disp('It may also crash/run out of memory.') 6 | disp('Set run_cvx = false if you just want to run scs.') 7 | disp('------------------------------------------------------------') 8 | 9 | save_results = false; 10 | run_cvx = false; 11 | cvx_use_solver = 'sdpt3'; 12 | run_scs_direct = true; 13 | run_scs_indirect = true; 14 | 15 | if nargin==1 16 | if isfield(params,'save_results'); save_results = params.save_results;end 17 | if isfield(params,'run_cvx'); run_cvx = params.run_cvx;end 18 | if isfield(params,'cvx_use_solver');cvx_use_solver = params.cvx_use_solver;end 19 | if isfield(params,'run_scs_direct');run_scs_direct = params.run_scs_direct;end 20 | if isfield(params,'run_scs_indirect');run_scs_indirect = params.run_scs_indirect;end 21 | end 22 | 23 | ns = [10000, 30000, 100000]; 24 | ms = ceil(ns/5); 25 | 26 | density = 0.1; 27 | 28 | for i = 1:length(ns) 29 | seedstr = sprintf('scs_lasso_ex_%i',i); 30 | randn('seed',sum(seedstr));rand('seed',sum(seedstr)) 31 | 32 | n=ns(i); 33 | m=ms(i); 34 | %r=n; 35 | s=ceil(n/10); 36 | 37 | x_true=[randn(s,1);zeros(n-s,1)]; % true sparse signal 38 | x_true=x_true(randperm(n)); 39 | A=sprandn(m,n,density); 40 | W=speye(n); % vanilla lasso 41 | b = A*x_true + 0.1*randn(m,1); % measurements 42 | mu = 1; 43 | 44 | %% 45 | if run_scs_direct 46 | tic 47 | cvx_begin 48 | cvx_solver scs 49 | cvx_solver_settings('eps',1e-3,'scale',1) 50 | variable x_c(n) 51 | minimize(0.5*sum_square(A*x_c - b) + mu*norm(W*x_c,1)) 52 | if (save_results) 53 | output = evalc('cvx_end') 54 | else 55 | output=''; 56 | cvx_end 57 | end 58 | toc 59 | 60 | scs_direct.x{i} = x_c; 61 | scs_direct.obj(i) = 0.5*sum_square(A*x_c - b) + mu*norm(W*x_c,1); 62 | scs_direct.output{i} = output; 63 | 64 | 65 | if (save_results); save('data/lasso_scs_direct', 'scs_direct'); end 66 | end 67 | %% 68 | if run_scs_indirect 69 | 70 | tic 71 | cvx_begin 72 | cvx_solver scs 73 | cvx_solver_settings('use_indirect',1,'eps',1e-3,'scale',1,'cg_rate',1.5) 74 | variable x_c(n) 75 | minimize(0.5*sum_square(A*x_c - b) + mu*norm(W*x_c,1)) 76 | if (save_results) 77 | output = evalc('cvx_end') 78 | else 79 | output=''; 80 | cvx_end 81 | end 82 | toc 83 | 84 | scs_indirect.x{i} = x_c; 85 | scs_indirect.obj(i) = 0.5*sum_square(A*x_c - b) + mu*norm(W*x_c,1); 86 | scs_indirect.output{i} = output; 87 | 88 | if (save_results); save('data/lasso_scs_indirect', 'scs_indirect'); end 89 | 90 | end 91 | %% 92 | if run_cvx 93 | try 94 | tic 95 | cvx_begin 96 | cvx_solver(cvx_use_solver) 97 | variable x_s(n) 98 | minimize(0.5*sum_square(A*x_s - b) + mu*norm(W*x_s,1)) 99 | if (save_results) 100 | output = evalc('cvx_end') 101 | else 102 | output=''; 103 | cvx_end 104 | end 105 | toc 106 | 107 | cvx.x{i} = x_s; 108 | cvx.obj(i) = 0.5*sum_square(A*x_s - b) + mu*norm(W*x_s,1); 109 | cvx.output{i} = output; 110 | cvx.err{i} = 0; 111 | 112 | catch err 113 | err 114 | cvx.err{i} = err; 115 | end 116 | 117 | if (save_results); save(sprintf('data/lasso_cvx_%s',cvx_use_solver), 'cvx'); end; 118 | 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /examples/run_pnorm_ex.m: -------------------------------------------------------------------------------- 1 | function run_pnorm_ex(params) 2 | 3 | disp('------------------------------------------------------------') 4 | disp('WARNING: this can take a very long time to run.') 5 | disp('It may also crash/run out of memory.') 6 | disp('Set run_cvx = false if you just want to run scs.') 7 | disp('------------------------------------------------------------') 8 | 9 | save_results = false; 10 | run_cvx = false; 11 | cvx_use_solver = 'sdpt3'; 12 | run_scs_direct = true; 13 | run_scs_indirect = true; 14 | 15 | if nargin==1 16 | if isfield(params,'save_results'); save_results = params.save_results;end 17 | if isfield(params,'run_cvx'); run_cvx = params.run_cvx;end 18 | if isfield(params,'cvx_use_solver');cvx_use_solver = params.cvx_use_solver;end 19 | if isfield(params,'run_scs_direct');run_scs_direct = params.run_scs_direct;end 20 | if isfield(params,'run_scs_indirect');run_scs_indirect = params.run_scs_indirect;end 21 | end 22 | 23 | ns = [1000, 10000, 100000]; 24 | ms = ceil(ns/2); 25 | 26 | density = 0.1; 27 | 28 | for i = 1:length(ns) 29 | seedstr = sprintf('scs_pnorm_ex_%i',i); 30 | randn('seed',sum(seedstr));rand('seed',sum(seedstr)) 31 | 32 | n = ns(i); 33 | m = ms(i); 34 | pow = pi; 35 | G = sprandn(m,n,density); 36 | f = randn(m,1) * n * density; 37 | 38 | n=ns(i); 39 | m=ms(i); 40 | 41 | %% 42 | if (run_scs_direct || run_scs_indirect) 43 | %% scs, with power cone formulation 44 | Anz = nnz(G) + n + 1 + 3*n; 45 | data.A = sparse([],[],[],m+3*n,2*n+1,Anz); 46 | data.A(1:m+1,:) = [G sparse(m,n) sparse(m,1); sparse(1,n) ones(1,n) -1]; 47 | for j=1:n 48 | ej = sparse(n,1);ej(j) = -1; 49 | data.A(m+1+(j-1)*3+1:m+1+j*3,:) = [sparse(1,n) ej' 0; sparse(1,2*n) -1; ej' sparse(1,n) 0]; 50 | end 51 | data.c = [zeros(2*n,1); 1]; 52 | data.b = [f; 0 ; zeros(3*n,1)]; 53 | K = struct('f', m+1, 'p', ones(n,1) / pow); 54 | 55 | params.eps = 1e-3; 56 | params.scale = 1; 57 | params.cg_rate = 1.5; 58 | 59 | if (run_scs_direct) 60 | if (save_results) 61 | [out,x_scs,y_scs,s_scs,info] = evalc('scs_direct(data, K, params)'); 62 | out 63 | save('data/pnorm_scs_direct', 'out'); 64 | else 65 | [x_scs,y_scs,s_scs,info] = scs_direct(data, K, params); 66 | end 67 | end 68 | if (run_scs_indirect) 69 | if (save_results) 70 | [out,x_scs,y_scs,s_scs,info] = evalc('scs_indirect(data, K, params)'); 71 | out 72 | save('data/pnorm_scs_indirect', 'out'); 73 | else 74 | [x_scs,y_scs,s_scs,info] = scs_indirect(data, K, params); 75 | end 76 | end 77 | 78 | end 79 | %% 80 | if run_cvx 81 | try 82 | tic 83 | cvx_begin 84 | cvx_solver(cvx_use_solver) 85 | variable x(n) 86 | minimize(norm(x, pow)) 87 | subject to 88 | G*x == f 89 | if (save_results) 90 | cvx.output{i} = evalc('cvx_end') 91 | else 92 | cvx_end 93 | end 94 | toc 95 | 96 | catch err 97 | err 98 | cvx.err{i} = err; 99 | end 100 | 101 | if (save_results); save(sprintf('data/pnorm_cvx_%s',cvx_use_solver), 'cvx'); end; 102 | 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /examples/run_portfolio_ex.m: -------------------------------------------------------------------------------- 1 | function run_portfolio_ex(params) 2 | 3 | disp('------------------------------------------------------------') 4 | disp('WARNING: this can take a very long time to run.') 5 | disp('It may also crash/run out of memory.') 6 | disp('Set run_cvx = false if you just want to run scs.') 7 | disp('------------------------------------------------------------') 8 | 9 | save_results = false; 10 | run_cvx = false; 11 | cvx_use_solver = 'sdpt3'; 12 | run_scs_direct = true; 13 | run_scs_indirect = true; 14 | 15 | if nargin==1 16 | if isfield(params,'save_results'); save_results = params.save_results;end 17 | if isfield(params,'run_cvx'); run_cvx = params.run_cvx;end 18 | if isfield(params,'cvx_use_solver');cvx_use_solver = params.cvx_use_solver;end 19 | if isfield(params,'run_scs_direct');run_scs_direct = params.run_scs_direct;end 20 | if isfield(params,'run_scs_indirect');run_scs_indirect = params.run_scs_indirect;end 21 | end 22 | 23 | ns = [10000, 50000, 250000]; 24 | ms = [100, 500, 2500]; 25 | 26 | density = 0.1; 27 | 28 | for i = 1:length(ns) 29 | seedstr = sprintf('scs_portfolio_ex_%i',i); 30 | randn('seed',sum(seedstr));rand('seed',sum(seedstr)) 31 | 32 | n = ns(i); 33 | m = ms(i); 34 | 35 | mu = exp(0.01*randn(n,1))-1; % returns 36 | D = rand(n,1)/10; % idiosyncratic risk 37 | F = sprandn(n,m,density)/10; % factor model 38 | gamma = 1; 39 | B = 1; 40 | %% 41 | if run_scs_direct 42 | 43 | tic 44 | cvx_begin 45 | cvx_solver scs_matlab 46 | cvx_solver_settings('eps',1e-3,'scale',1) 47 | variable x(n) 48 | maximize (mu'*x - gamma*(sum_square(F'*x) + sum_square(D.*x))) 49 | sum(x) == B 50 | x >= 0 51 | if (save_results) 52 | output = evalc('cvx_end') 53 | else 54 | output=''; 55 | cvx_end 56 | end 57 | toc 58 | 59 | scs_direct.x{i} = x; 60 | scs_direct.x_viol{i} = min(x); 61 | scs_direct.budget_viol{i} = abs(1-sum(x)); 62 | scs_direct.obj(i) = (mu'*x - gamma*(sum_square(F'*x) + sum_square(D.*x))); 63 | scs_direct.output{i} = output; 64 | 65 | if (save_results); save('data/portfolio_scs_direct', 'scs_direct'); end 66 | end 67 | if run_scs_indirect 68 | %% 69 | tic 70 | cvx_begin 71 | cvx_solver scs 72 | cvx_solver_settings('use_indirect',1,'eps',1e-3,'scale',1,'cg_rate',1.5) 73 | variable x(n) 74 | maximize (mu'*x - gamma*(sum_square(F'*x) + sum_square(D.*x))) 75 | sum(x) == B 76 | x >= 0 77 | if (save_results) 78 | output = evalc('cvx_end') 79 | else 80 | output=''; 81 | cvx_end 82 | end 83 | toc 84 | 85 | scs_indirect.x{i} = x; 86 | scs_indirect.x_viol{i} = min(x); 87 | scs_indirect.budget_viol{i} = abs(1-sum(x)); 88 | scs_indirect.obj(i) = (mu'*x - gamma*(sum_square(F'*x) + sum_square(D.*x))); 89 | scs_indirect.output{i} = output; 90 | 91 | 92 | if (save_results); save('data/portfolio_scs_indirect', 'scs_indirect'); end 93 | end 94 | %% 95 | if run_cvx 96 | try 97 | tic 98 | cvx_begin 99 | cvx_solver(cvx_use_solver) 100 | variable x(n) 101 | maximize (mu'*x - gamma*(sum_square(F'*x) + sum_square(D.*x))) 102 | sum(x) == B 103 | x >= 0 104 | if (save_results) 105 | output = evalc('cvx_end') 106 | else 107 | output=''; 108 | cvx_end 109 | end 110 | toc 111 | 112 | cvx.x{i} = x; 113 | cvx.x_viol{i} = min(x); 114 | cvx.budget_viol{i} = abs(1-sum(x)); 115 | cvx.obj(i) = (mu'*x - gamma*(sum_square(F'*x) + sum_square(D.*x))); 116 | cvx.output{i} = output; 117 | cvx.err{i} = 0; 118 | 119 | catch err 120 | err 121 | cvx.err{i} = err; 122 | end 123 | 124 | if (save_results); save(sprintf('data/portfolio_cvx_%s',cvx_use_solver), 'cvx'); end 125 | 126 | end 127 | end 128 | -------------------------------------------------------------------------------- /examples/run_rpca_ex.m: -------------------------------------------------------------------------------- 1 | function run_rpca_ex(params) 2 | 3 | disp('------------------------------------------------------------') 4 | disp('WARNING: this can take a very long time to run.') 5 | disp('It may also crash/run out of memory.') 6 | disp('Set run_cvx = false if you just want to run scs.') 7 | disp('------------------------------------------------------------') 8 | 9 | save_results = false; 10 | run_cvx = false; 11 | cvx_use_solver = 'sdpt3'; 12 | run_scs_direct = true; 13 | run_scs_indirect = true; 14 | 15 | if nargin==1 16 | if isfield(params,'save_results'); save_results = params.save_results;end 17 | if isfield(params,'run_cvx'); run_cvx = params.run_cvx;end 18 | if isfield(params,'cvx_use_solver');cvx_use_solver = params.cvx_use_solver;end 19 | if isfield(params,'run_scs_direct');run_scs_direct = params.run_scs_direct;end 20 | if isfield(params,'run_scs_indirect');run_scs_indirect = params.run_scs_indirect;end 21 | end 22 | 23 | ns = [100, 500, 1000]; 24 | ms = ns; % square matrices, but doesn't have to be 25 | density = 0.1; 26 | 27 | for i = 1:length(ns) 28 | seedstr = sprintf('scs_rpca_ex_%i',i); 29 | randn('seed',sum(seedstr));rand('seed',sum(seedstr)) 30 | 31 | n = ns(i); 32 | m = ms(i); 33 | r = 10; % rank 34 | 35 | L1 = randn(m,r); 36 | L2 = randn(r,n); 37 | L = L1*L2; 38 | S = 10*sprandn(m,n,density); 39 | M = L + S; 40 | kap = sum(sum(abs(S))); 41 | 42 | %% 43 | if run_scs_direct 44 | 45 | tic 46 | cvx_begin 47 | cvx_solver scs 48 | cvx_solver_settings('eps',1e-3,'scale',1) 49 | variables Lc(m,n) Sc(m,n) 50 | dual variable Yc 51 | minimize(norm_nuc(Lc)) 52 | sum(norms(Sc,1)) <= kap 53 | Yc:Lc + Sc == M; 54 | if (save_results) 55 | output = evalc('cvx_end') 56 | else 57 | output=''; 58 | cvx_end 59 | end 60 | toc 61 | 62 | scs_direct.L{i} = Lc; 63 | scs_direct.obj(i) = norm_nuc(Lc); 64 | scs_direct.output{i} = output; 65 | 66 | 67 | if (save_results); save('data/rpca_scs_direct', 'scs_direct'); end 68 | end 69 | %% 70 | if run_scs_indirect 71 | tic 72 | cvx_begin 73 | cvx_solver scs 74 | cvx_solver_settings('use_indirect',1,'eps',1e-3,'scale',1,'cg_rate',1.5) 75 | variables Lc(m,n) Sc(m,n) 76 | dual variable Yc 77 | minimize(norm_nuc(Lc)) 78 | sum(norms(Sc,1)) <= kap 79 | Yc:Lc + Sc == M; 80 | if (save_results) 81 | output = evalc('cvx_end') 82 | else 83 | output=''; 84 | cvx_end 85 | end 86 | toc 87 | 88 | scs_indirect.L{i} = Lc; 89 | scs_indirect.obj(i) = norm_nuc(Lc); 90 | scs_indirect.output{i} = output; 91 | 92 | if (save_results); save('data/rpca_scs_indirect', 'scs_indirect'); end 93 | end 94 | %% 95 | if run_cvx 96 | try 97 | tic 98 | cvx_begin 99 | cvx_solver(cvx_use_solver) 100 | variables Lt(m,n) St(m,n) 101 | dual variable Yt 102 | minimize(norm_nuc(Lt)) 103 | sum(norms(St,1)) <= kap 104 | Yt:Lt + St == M; 105 | if (save_results) 106 | output = evalc('cvx_end') 107 | else 108 | output=''; 109 | cvx_end 110 | end 111 | toc 112 | 113 | cvx.L{i} = Lt; 114 | cvx.obj(i) = norm_nuc(Lt); 115 | cvx.output{i} = output; 116 | cvx.err{i} = 0; 117 | 118 | catch err 119 | err 120 | cvx.err{i} = err; 121 | end 122 | 123 | if (save_results); save(sprintf('data/rpca_cvx_%s',cvx_use_solver), 'cvx'); end 124 | 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /examples/solve_cone_cvx.m: -------------------------------------------------------------------------------- 1 | function [x,y,s]=solveScsConeCvx(data,K,cs) 2 | if (K.ep>0 || K.ed>0) 3 | x = nan; 4 | y = nan; 5 | s = nan; 6 | disp('cvx cannot solve ECPs'); 7 | return; 8 | end 9 | n = length(data.c); 10 | m = length(data.b); 11 | % can NOT solve ECPs 12 | cvx_begin 13 | cvx_solver(cs) 14 | variables x(n) s(m) 15 | dual variable y 16 | minimize(data.c'*x) 17 | y:data.A*x + s == data.b 18 | s(1:K.f)==0 19 | l = K.f; 20 | s(l+1:l+K.l) >= 0 21 | l = l + K.l; 22 | for i=1:length(K.q) 23 | s(l+1) >= norm(s(l+2:l + K.q(i))); 24 | l = l + K.q(i); 25 | end 26 | for i=1:length(K.s) 27 | reshape(s(l+1:l + (K.s(i))^2),K.s(i),K.s(i)) == semidefinite(K.s(i)); 28 | l = l + (K.s(i))^2; 29 | end 30 | cvx_end 31 | end 32 | -------------------------------------------------------------------------------- /make_scs.m: -------------------------------------------------------------------------------- 1 | gpu = false; % compile the gpu version of SCS 2 | float = false; % using single precision (rather than double) floating points 3 | int = false; % use 32 bit integers for indexing 4 | % WARNING: OPENMP WITH MATLAB CAN CAUSE ERRORS AND CRASH, USE WITH CAUTION: 5 | % openmp parallelizes the matrix multiply for the indirect solver (using CG) 6 | % and some cone projections. 7 | use_open_mp = false; 8 | 9 | flags.BLASLIB = '-lmwblas -lmwlapack'; 10 | % MATLAB_MEX_FILE env variable sets blasint to ptrdiff_t 11 | flags.LCFLAG = '-DMATLAB_MEX_FILE -DUSE_LAPACK -DCTRLC=1 -DCOPYAMATRIX -DGPU_TRANSPOSE_MAT -DVERBOSITY=0'; 12 | flags.INCS = ''; 13 | flags.LOCS = ''; 14 | 15 | common_scs = 'scs/src/linalg.c scs/src/cones.c scs/src/exp_cone.c scs/src/aa.c scs/src/util.c scs/src/scs.c scs/src/ctrlc.c scs/src/normalize.c scs/src/scs_version.c scs/linsys/scs_matrix.c scs/linsys/csparse.c scs/src/rw.c scs_mex.c'; 16 | if (contains(computer, '64')) 17 | flags.arr = '-largeArrayDims'; 18 | else 19 | flags.arr = ''; 20 | end 21 | 22 | if ( isunix && ~ismac ) 23 | flags.link = '-lm -lut -lrt'; 24 | elseif ( ismac ) 25 | flags.link = '-lm -lut'; 26 | else 27 | flags.link = '-lut'; 28 | flags.LCFLAG = sprintf('-DNOBLASSUFFIX %s', flags.LCFLAG); 29 | end 30 | 31 | if (float) 32 | flags.LCFLAG = sprintf('-DSFLOAT %s', flags.LCFLAG); 33 | end 34 | if (int) 35 | flags.INT = ''; 36 | else 37 | flags.INT = '-DDLONG'; 38 | end 39 | 40 | flags.CFLAGS = ''; 41 | flags.COMPFLAGS = ''; 42 | 43 | if (use_open_mp) 44 | flags.link = strcat(flags.link, ' -lgomp'); 45 | flags.CFLAGS = sprintf('%s /openmp', flags.CFLAGS); 46 | flags.COMPFLAGS = sprintf('%s -fopenmp', flags.COMPFLAGS); 47 | end 48 | 49 | % add c99 to handle qldl comments 50 | if (~ismac() && isunix()) 51 | flags.CFLAGS = sprintf('%s -std=c99', flags.CFLAGS); 52 | end 53 | 54 | compile_direct(flags, common_scs); 55 | compile_indirect(flags, common_scs); 56 | if (gpu) 57 | compile_gpu(flags, common_scs); 58 | end 59 | 60 | % compile scs_version 61 | mex -v -O -Iscs/include -Iscs/linsys scs/src/scs_version.c scs_version_mex.c -output scs_version 62 | 63 | addpath '.' 64 | 65 | disp('SUCCESSFULLY INSTALLED SCS') 66 | disp('(If using SCS with CVX, note that SCS only supports CVX v3.0 or later).') 67 | -------------------------------------------------------------------------------- /scs.m: -------------------------------------------------------------------------------- 1 | function [ x, y, s, info ] = scs( varargin ) 2 | % scs 3.2.5 3 | % for version call: scs_version() 4 | data = varargin{1}; 5 | K = varargin{2}; 6 | if nargin >= 3 7 | pars = varargin{3}; 8 | else 9 | pars = []; 10 | end 11 | 12 | if isfield(data, 'P') 13 | data.P = sparse(data.P); 14 | if (~istriu(data.P)) 15 | data.P = triu(data.P); 16 | end 17 | end 18 | 19 | if isfield(data, 'A') 20 | data.A = sparse(data.A); 21 | end 22 | 23 | if size(data.b, 2) > 1 24 | data.b = data.b(:); 25 | end 26 | 27 | if size(data.c, 2) > 1 28 | data.c = data.c(:); 29 | end 30 | 31 | assert(size(data.A, 1) == size(data.b, 1), "A and b shape mismatch") 32 | assert(size(data.A, 2) == size(data.c, 1), "A and c shape mismatch") 33 | 34 | if isfield(data, 'P') 35 | assert(size(data.P, 1) == size(data.P, 2), "P is not square") 36 | assert(size(data.P, 1) == size(data.c, 1), "P and c shape mismatch") 37 | end 38 | 39 | if (isfield(pars,'use_indirect') && pars.use_indirect) 40 | [ x, y, s, info ] = scs_indirect( data, K, pars ); 41 | elseif (isfield(pars,'gpu') && pars.gpu) 42 | [ x, y, s, info ] = scs_gpu( data, K, pars ); 43 | else 44 | [ x, y, s, info ] = scs_direct( data, K, pars ); 45 | end 46 | -------------------------------------------------------------------------------- /scs_direct.m: -------------------------------------------------------------------------------- 1 | function [x, y, s, info] = scs_direct(data, cone, params) 2 | % Operator-splitting method for solving cone problems (direct) 3 | % 4 | % This implements a cone solver. It solves: 5 | % 6 | % min. c'x 7 | % subject to Ax + s = b 8 | % s \in K 9 | % 10 | % where x \in R^n, s \in R^m 11 | % 12 | % this uses the direct linear equation solver version of SCS 13 | % 14 | % K is product of cones in this particular order: 15 | % free cone, lp cone, second order cone(s), semi-definite cone(s), primal 16 | % exponential cones, dual exponential cones 17 | % 18 | % data must consist of data.A, data.b, data.c, where A,b,c used as above. 19 | % 20 | % cone struct must consist of: 21 | % cone.f, length of free cone (for equality constraints) 22 | % cone.l, length of lp cone 23 | % cone.q, array of SOC lengths 24 | % cone.s, array of SD lengths 25 | % cone.ep, number of primal exp cones 26 | % cone.ed, number of dual exp cones 27 | % 28 | % Optional fields in the params struct are: 29 | % alpha : over-relaxation parameter, between (0,2). 30 | % rho_x : momentum of x term (1e-3 works well) 31 | % max_iters : maximum number of ADMM iterations. 32 | % eps : accuracy of solution 33 | % verbose : verbosity level (0 or 1) 34 | % normalize : heuristic data rescaling (0 or 1, off or on) 35 | % scale : rescales data up by this factor (only used if normalize=1) 36 | % 37 | % to warm-start the solver add guesses for (x, y, s) to the data struct 38 | % 39 | error ('scs_direct mexFunction not found') ; 40 | -------------------------------------------------------------------------------- /scs_indirect.m: -------------------------------------------------------------------------------- 1 | function [x, y, s, info] = scs_indirect(data, cone, params) 2 | % Operator-splitting method for solving cone problems (indirect) 3 | % 4 | % This implements a cone solver. It solves: 5 | % 6 | % min. c'x 7 | % subject to Ax + s = b 8 | % s \in K 9 | % 10 | % where x \in R^n, s \in R^m 11 | % 12 | % this uses the indirect linear equation solver version of SCS 13 | % 14 | % K is product of cones in this particular order: 15 | % free cone, lp cone, second order cone(s), semi-definite cone(s), primal 16 | % exponential cones, dual exponential cones 17 | % 18 | % data must consist of data.A, data.b, data.c, where A,b,c used as above. 19 | % 20 | % cone struct must consist of: 21 | % cone.f, length of free cone (for equality constraints) 22 | % cone.l, length of lp cone 23 | % cone.q, array of SOC lengths 24 | % cone.s, array of SD lengths 25 | % cone.ep, number of primal exp cones 26 | % cone.ed, number of dual exp cones 27 | % 28 | % Optional fields in the params struct are: 29 | % alpha : over-relaxation parameter, between (0,2). 30 | % rho_x : momentum of x term (1e-3 works well) 31 | % max_iters : maximum number of ADMM iterations. 32 | % eps : accuracy of solution 33 | % verbose : verbosity level (0 or 1) 34 | % normalize : heuristic data rescaling (0 or 1, off or on) 35 | % scale : rescales data up by this factor (only used if normalize=1) 36 | % cg_rate : the rate at which the CG tolerance is tightened (higher is tighter) 37 | % 38 | % to warm-start the solver add guesses for (x, y, s) to the data struct 39 | % 40 | error ('scs_indirect mexFunction not found') ; 41 | -------------------------------------------------------------------------------- /scs_mex.c: -------------------------------------------------------------------------------- 1 | #include "glbopts.h" 2 | #include "linalg.h" 3 | #include "matrix.h" 4 | #include "mex.h" 5 | #include "scs.h" 6 | #include "scs_matrix.h" 7 | #include "util.h" 8 | 9 | void free_mex(ScsData *d, ScsCone *k, ScsSettings *stgs); 10 | 11 | scs_int parse_warm_start(const mxArray *p_mex, scs_float **p, scs_int l) { 12 | *p = (scs_float *)scs_calloc( 13 | l, sizeof(scs_float)); /* this allocates memory used for ScsSolution */ 14 | if (p_mex == SCS_NULL) { 15 | return 0; 16 | } else if (mxIsSparse(p_mex) || (scs_int)*mxGetDimensions(p_mex) != l) { 17 | scs_printf("Error parsing warm start input (make sure vectors are not " 18 | "sparse and of correct size), running without full " 19 | "warm-start"); 20 | return 0; 21 | } else { 22 | memcpy(*p, mxGetPr(p_mex), l * sizeof(scs_float)); 23 | return 1; 24 | } 25 | } 26 | 27 | #if !(DLONG > 0) 28 | /* this memory must be freed */ 29 | scs_int *cast_to_scs_int_arr(mwIndex *arr, scs_int len) { 30 | scs_int i; 31 | scs_int *arr_out = (scs_int *)scs_malloc(sizeof(scs_int) * len); 32 | for (i = 0; i < len; i++) { 33 | arr_out[i] = (scs_int)arr[i]; 34 | } 35 | return arr_out; 36 | } 37 | #endif 38 | 39 | #if SFLOAT > 0 40 | /* this memory must be freed */ 41 | scs_float *cast_to_scs_float_arr(double *arr, scs_int len) { 42 | scs_int i; 43 | scs_float *arr_out = (scs_float *)scs_malloc(sizeof(scs_float) * len); 44 | for (i = 0; i < len; i++) { 45 | arr_out[i] = (scs_float)arr[i]; 46 | } 47 | return arr_out; 48 | } 49 | 50 | double *cast_to_double_arr(scs_float *arr, scs_int len) { 51 | scs_int i; 52 | double *arr_out = (double *)scs_malloc(sizeof(double) * len); 53 | for (i = 0; i < len; i++) { 54 | arr_out[i] = (double)arr[i]; 55 | } 56 | return arr_out; 57 | } 58 | #endif 59 | 60 | void set_output_field(mxArray **pout, scs_float *out, scs_int len) { 61 | *pout = mxCreateDoubleMatrix(0, 0, mxREAL); 62 | #if SFLOAT > 0 63 | mxSetPr(*pout, cast_to_double_arr(out, len)); 64 | scs_free(out); 65 | #else 66 | mxSetPr(*pout, out); 67 | #endif 68 | mxSetM(*pout, len); 69 | mxSetN(*pout, 1); 70 | } 71 | 72 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 73 | /* matlab usage: [x,y,s,info] = scs(data,cone,settings); */ 74 | scs_int i, ns, status, nbl, nbu, blen; 75 | ScsData *d; 76 | ScsCone *k; 77 | ScsSettings *stgs; 78 | ScsSolution sol = {0}; 79 | ScsInfo info; 80 | ScsMatrix *A; 81 | ScsMatrix *P = SCS_NULL; 82 | 83 | const mxArray *data; 84 | const mxArray *A_mex; 85 | const mxArray *P_mex; 86 | const mxArray *b_mex; 87 | const mxArray *c_mex; 88 | 89 | const mxArray *kf; 90 | const mxArray *kz; 91 | const mxArray *kl; 92 | const mxArray *kbl; 93 | const mxArray *kbu; 94 | const mxArray *kq; 95 | const mxArray *ks; 96 | const mxArray *kep; 97 | const mxArray *ked; 98 | const mxArray *kp; 99 | const double *q_mex; 100 | const double *bl_mex; 101 | const double *bu_mex; 102 | const double *s_mex; 103 | const double *p_mex; 104 | const size_t *bl_dims; 105 | const size_t *bu_dims; 106 | const size_t *q_dims; 107 | const size_t *s_dims; 108 | const size_t *p_dims; 109 | 110 | const mxArray *cone; 111 | const mxArray *settings; 112 | 113 | const mwSize one[1] = {1}; 114 | const int num_info_fields = 16; 115 | const char *info_fields[] = { 116 | "iter", "status", "pobj", "dobj", 117 | "res_pri", "res_dual", "res_infeas", "res_unbdd_a", 118 | "scale", "status_val", "res_unbdd_p", "gap", 119 | "setup_time", "solve_time", "scale_updates", "comp_slack"}; 120 | mxArray *tmp; 121 | #if VERBOSITY > 0 122 | scs_printf("SIZE OF mwSize = %i\n", (int)sizeof(mwSize)); 123 | scs_printf("SIZE OF mwIndex = %i\n", (int)sizeof(mwIndex)); 124 | #endif 125 | 126 | if (nrhs != 3) { 127 | mexErrMsgTxt("Three arguments are required in this order: data struct, " 128 | "cone struct, settings struct"); 129 | } 130 | if (nlhs > 4) { 131 | mexErrMsgTxt("scs returns up to 4 output arguments only."); 132 | } 133 | d = (ScsData *)mxMalloc(sizeof(ScsData)); 134 | stgs = (ScsSettings *)mxMalloc(sizeof(ScsSettings)); 135 | k = (ScsCone *)mxMalloc(sizeof(ScsCone)); 136 | data = prhs[0]; 137 | 138 | A_mex = (mxArray *)mxGetField(data, 0, "A"); 139 | if (A_mex == SCS_NULL) { 140 | scs_free(d); 141 | scs_free(k); 142 | mexErrMsgTxt("ScsData struct must contain a `A` entry."); 143 | } 144 | if (!mxIsSparse(A_mex)) { 145 | scs_free(d); 146 | scs_free(k); 147 | mexErrMsgTxt("Input matrix A must be in sparse format (pass in sparse(A))"); 148 | } 149 | P_mex = (mxArray *)mxGetField(data, 0, "P"); /* can be SCS_NULL */ 150 | if (P_mex && !mxIsSparse(P_mex)) { 151 | scs_free(d); 152 | scs_free(k); 153 | mexErrMsgTxt("Input matrix P must be in sparse format (pass in sparse(A))"); 154 | } 155 | b_mex = (mxArray *)mxGetField(data, 0, "b"); 156 | if (b_mex == SCS_NULL) { 157 | scs_free(d); 158 | scs_free(k); 159 | mexErrMsgTxt("ScsData struct must contain a `b` entry."); 160 | } 161 | if (mxIsSparse(b_mex)) { 162 | scs_free(d); 163 | scs_free(k); 164 | mexErrMsgTxt("Input vector b must be in dense format (pass in full(b))"); 165 | } 166 | c_mex = (mxArray *)mxGetField(data, 0, "c"); 167 | if (c_mex == SCS_NULL) { 168 | scs_free(d); 169 | scs_free(k); 170 | mexErrMsgTxt("ScsData struct must contain a `c` entry."); 171 | } 172 | if (mxIsSparse(c_mex)) { 173 | scs_free(d); 174 | scs_free(k); 175 | mexErrMsgTxt("Input vector c must be in dense format (pass in full(c))"); 176 | } 177 | cone = prhs[1]; 178 | settings = prhs[2]; 179 | d->n = (scs_int) * (mxGetDimensions(c_mex)); 180 | d->m = (scs_int) * (mxGetDimensions(b_mex)); 181 | #if SFLOAT > 0 182 | d->b = cast_to_scs_float_arr(mxGetPr(b_mex), d->m); 183 | d->c = cast_to_scs_float_arr(mxGetPr(c_mex), d->n); 184 | #else 185 | d->b = (scs_float *)mxGetPr(b_mex); 186 | d->c = (scs_float *)mxGetPr(c_mex); 187 | #endif 188 | scs_set_default_settings(stgs); 189 | 190 | /* settings */ 191 | tmp = mxGetField(settings, 0, "alpha"); 192 | if (tmp != SCS_NULL) { 193 | stgs->alpha = (scs_float)*mxGetPr(tmp); 194 | } 195 | 196 | tmp = mxGetField(settings, 0, "rho_x"); 197 | if (tmp != SCS_NULL) { 198 | stgs->rho_x = (scs_float)*mxGetPr(tmp); 199 | } 200 | 201 | tmp = mxGetField(settings, 0, "max_iters"); 202 | if (tmp != SCS_NULL) { 203 | stgs->max_iters = (scs_int)*mxGetPr(tmp); 204 | } 205 | 206 | tmp = mxGetField(settings, 0, "scale"); 207 | if (tmp != SCS_NULL) { 208 | stgs->scale = (scs_float)*mxGetPr(tmp); 209 | } 210 | 211 | tmp = mxGetField(settings, 0, "eps_abs"); 212 | if (tmp != SCS_NULL) { 213 | stgs->eps_abs = (scs_float)*mxGetPr(tmp); 214 | } 215 | 216 | tmp = mxGetField(settings, 0, "eps_rel"); 217 | if (tmp != SCS_NULL) { 218 | stgs->eps_rel = (scs_float)*mxGetPr(tmp); 219 | } 220 | 221 | tmp = mxGetField(settings, 0, "eps_infeas"); 222 | if (tmp != SCS_NULL) { 223 | stgs->eps_infeas = (scs_float)*mxGetPr(tmp); 224 | } 225 | 226 | tmp = mxGetField(settings, 0, "verbose"); 227 | if (tmp != SCS_NULL) { 228 | stgs->verbose = (scs_int)*mxGetPr(tmp); 229 | } 230 | 231 | tmp = mxGetField(settings, 0, "normalize"); 232 | if (tmp != SCS_NULL) { 233 | stgs->normalize = (scs_int)*mxGetPr(tmp); 234 | } 235 | 236 | tmp = mxGetField(settings, 0, "acceleration_lookback"); 237 | if (tmp != SCS_NULL) { 238 | stgs->acceleration_lookback = (scs_int)*mxGetPr(tmp); 239 | } 240 | 241 | tmp = mxGetField(settings, 0, "acceleration_interval"); 242 | if (tmp != SCS_NULL) { 243 | stgs->acceleration_interval = (scs_int)*mxGetPr(tmp); 244 | } 245 | 246 | tmp = mxGetField(settings, 0, "adaptive_scale"); 247 | if (tmp != SCS_NULL) { 248 | stgs->adaptive_scale = (scs_int)*mxGetPr(tmp); 249 | } 250 | 251 | tmp = mxGetField(settings, 0, "time_limit_secs"); 252 | if (tmp != SCS_NULL) { 253 | stgs->time_limit_secs = (scs_float)*mxGetPr(tmp); 254 | } 255 | 256 | tmp = mxGetField(settings, 0, "write_data_filename"); 257 | if (tmp != SCS_NULL) { 258 | /* need to free this later */ 259 | stgs->write_data_filename = mxArrayToString(tmp); 260 | } 261 | 262 | tmp = mxGetField(settings, 0, "log_csv_filename"); 263 | if (tmp != SCS_NULL) { 264 | /* need to free this later */ 265 | stgs->log_csv_filename = mxArrayToString(tmp); 266 | } 267 | 268 | /* cones */ 269 | 270 | /* TODO rm this */ 271 | kf = mxGetField(cone, 0, "f"); 272 | if (kf && !mxIsEmpty(kf)) { 273 | scs_printf("SCS deprecation warning: The 'f' field in the cone struct \n" 274 | "has been replaced by 'z' to better reflect the Zero cone. \n" 275 | "Please replace usage of 'f' with 'z'. If both 'f' and 'z' \n" 276 | "are set then we sum the two fields to get the final zero \n" 277 | "cone size.\n"); 278 | k->z = (scs_int)*mxGetPr(kf); 279 | } else { 280 | k->z = 0; 281 | } 282 | 283 | kz = mxGetField(cone, 0, "z"); 284 | if (kz && !mxIsEmpty(kz)) { 285 | k->z += (scs_int)*mxGetPr(kz); /* TODO rm this */ 286 | } 287 | 288 | kl = mxGetField(cone, 0, "l"); 289 | if (kl && !mxIsEmpty(kl)) { 290 | k->l = (scs_int)*mxGetPr(kl); 291 | } else { 292 | k->l = 0; 293 | } 294 | 295 | kep = mxGetField(cone, 0, "ep"); 296 | if (kep && !mxIsEmpty(kep)) { 297 | k->ep = (scs_int)*mxGetPr(kep); 298 | } else { 299 | k->ep = 0; 300 | } 301 | 302 | ked = mxGetField(cone, 0, "ed"); 303 | if (ked && !mxIsEmpty(ked)) { 304 | k->ed = (scs_int)*mxGetPr(ked); 305 | } else { 306 | k->ed = 0; 307 | } 308 | 309 | kbu = mxGetField(cone, 0, "bu"); 310 | kbl = mxGetField(cone, 0, "bl"); 311 | if (kbl && kbu && !mxIsEmpty(kbl) && !mxIsEmpty(kbu)) { 312 | bl_mex = mxGetPr(kbl); 313 | bu_mex = mxGetPr(kbu); 314 | 315 | nbl = (scs_int)mxGetNumberOfDimensions(kbl); 316 | nbu = (scs_int)mxGetNumberOfDimensions(kbu); 317 | if (nbl != nbu) { 318 | mexErrMsgTxt("bl,bu cone entries not the same size."); 319 | } 320 | 321 | bl_dims = mxGetDimensions(kbl); 322 | bu_dims = mxGetDimensions(kbu); 323 | for (i = 0; i < nbu; i++) { 324 | if (bl_dims[i] != bu_dims[i]) { 325 | mexErrMsgTxt("bl,bu cone entries not the same size."); 326 | } 327 | } 328 | blen = (scs_int)bu_dims[0]; 329 | if (nbl > 1 && bu_dims[0] == 1) { 330 | blen = (scs_int)bu_dims[1]; 331 | } 332 | k->bu = (scs_float *)mxMalloc(sizeof(scs_int) * blen); 333 | k->bl = (scs_float *)mxMalloc(sizeof(scs_int) * blen); 334 | 335 | for (i = 0; i < blen; i++) { 336 | k->bl[i] = (scs_float)bl_mex[i]; 337 | k->bu[i] = (scs_float)bu_mex[i]; 338 | } 339 | k->bsize = blen + 1; /* bsize is total length of cone (t,s) */ 340 | } else { 341 | k->bsize = 0; 342 | k->bl = SCS_NULL; 343 | k->bu = SCS_NULL; 344 | } 345 | 346 | kq = mxGetField(cone, 0, "q"); 347 | if (kq && !mxIsEmpty(kq)) { 348 | q_mex = mxGetPr(kq); 349 | ns = (scs_int)mxGetNumberOfDimensions(kq); 350 | q_dims = mxGetDimensions(kq); 351 | k->qsize = (scs_int)q_dims[0]; 352 | if (ns > 1 && q_dims[0] == 1) { 353 | k->qsize = (scs_int)q_dims[1]; 354 | } 355 | k->q = (scs_int *)mxMalloc(sizeof(scs_int) * k->qsize); 356 | for (i = 0; i < k->qsize; i++) { 357 | k->q[i] = (scs_int)q_mex[i]; 358 | } 359 | } else { 360 | k->qsize = 0; 361 | k->q = SCS_NULL; 362 | } 363 | 364 | ks = mxGetField(cone, 0, "s"); 365 | if (ks && !mxIsEmpty(ks)) { 366 | s_mex = mxGetPr(ks); 367 | ns = (scs_int)mxGetNumberOfDimensions(ks); 368 | s_dims = mxGetDimensions(ks); 369 | k->ssize = (scs_int)s_dims[0]; 370 | if (ns > 1 && s_dims[0] == 1) { 371 | k->ssize = (scs_int)s_dims[1]; 372 | } 373 | k->s = (scs_int *)mxMalloc(sizeof(scs_int) * k->ssize); 374 | for (i = 0; i < k->ssize; i++) { 375 | k->s[i] = (scs_int)s_mex[i]; 376 | } 377 | } else { 378 | k->ssize = 0; 379 | k->s = SCS_NULL; 380 | } 381 | 382 | kp = mxGetField(cone, 0, "p"); 383 | if (kp && !mxIsEmpty(kp)) { 384 | p_mex = mxGetPr(kp); 385 | ns = (scs_int)mxGetNumberOfDimensions(kp); 386 | p_dims = mxGetDimensions(kp); 387 | k->psize = (scs_int)p_dims[0]; 388 | if (ns > 1 && p_dims[0] == 1) { 389 | k->psize = (scs_int)p_dims[1]; 390 | } 391 | k->p = (scs_float *)mxMalloc(sizeof(scs_float) * k->psize); 392 | for (i = 0; i < k->psize; i++) { 393 | k->p[i] = (scs_float)p_mex[i]; 394 | } 395 | } else { 396 | k->psize = 0; 397 | k->p = SCS_NULL; 398 | } 399 | 400 | A = (ScsMatrix *)scs_malloc(sizeof(ScsMatrix)); 401 | A->n = d->n; 402 | A->m = d->m; 403 | 404 | if (P_mex) { 405 | P = (ScsMatrix *)scs_malloc(sizeof(ScsMatrix)); 406 | P->n = d->n; 407 | P->m = d->n; 408 | } 409 | 410 | /* TODO: 411 | * these return (mwIndex *), equivalent to (size_t *) 412 | * casting as (scs_int *), when scs_int = long seems to work 413 | * although maybe not on all machines: 414 | * 415 | * If scs_int is not long, then we explictly cast the entire 416 | * array to get the correct width 417 | */ 418 | #if DLONG > 0 419 | A->p = (scs_int *)mxGetJc(A_mex); 420 | A->i = (scs_int *)mxGetIr(A_mex); 421 | if (P_mex) { 422 | P->p = (scs_int *)mxGetJc(P_mex); 423 | P->i = (scs_int *)mxGetIr(P_mex); 424 | } 425 | #else 426 | A->p = cast_to_scs_int_arr(mxGetJc(A_mex), A->n + 1); 427 | A->i = cast_to_scs_int_arr(mxGetIr(A_mex), A->p[A->n]); 428 | if (P_mex) { 429 | P->p = cast_to_scs_int_arr(mxGetJc(P_mex), P->n + 1); 430 | P->i = cast_to_scs_int_arr(mxGetIr(P_mex), P->p[P->n]); 431 | } 432 | #endif 433 | #if SFLOAT > 0 434 | A->x = cast_to_scs_float_arr(mxGetPr(A_mex), A->p[A->n]); 435 | if (P_mex) { 436 | P->x = cast_to_scs_float_arr(mxGetPr(P_mex), P->p[P->n]); 437 | } 438 | #else 439 | A->x = (scs_float *)mxGetPr(A_mex); 440 | if (P_mex) { 441 | P->x = (scs_float *)mxGetPr(P_mex); 442 | } 443 | #endif 444 | d->A = A; 445 | d->P = P; 446 | /* warm-start inputs, allocates sol->x, ->y, ->s even if warm start not used 447 | */ 448 | stgs->warm_start = 449 | parse_warm_start((mxArray *)mxGetField(data, 0, "x"), &(sol.x), d->n); 450 | stgs->warm_start |= 451 | parse_warm_start((mxArray *)mxGetField(data, 0, "y"), &(sol.y), d->m); 452 | stgs->warm_start |= 453 | parse_warm_start((mxArray *)mxGetField(data, 0, "s"), &(sol.s), d->m); 454 | 455 | status = scs(d, k, stgs, &sol, &info); 456 | 457 | set_output_field(&plhs[0], sol.x, d->n); 458 | set_output_field(&plhs[1], sol.y, d->m); 459 | set_output_field(&plhs[2], sol.s, d->m); 460 | 461 | plhs[3] = mxCreateStructArray(1, one, num_info_fields, info_fields); 462 | 463 | /* if you add/remove fields here update the info_fields above */ 464 | mxSetField(plhs[3], 0, "status", mxCreateString(info.status)); 465 | 466 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 467 | mxSetField(plhs[3], 0, "iter", tmp); 468 | *mxGetPr(tmp) = (scs_float)info.iter; 469 | 470 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 471 | mxSetField(plhs[3], 0, "scale_updates", tmp); 472 | *mxGetPr(tmp) = (scs_float)info.scale_updates; 473 | 474 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 475 | mxSetField(plhs[3], 0, "status_val", tmp); 476 | *mxGetPr(tmp) = (scs_float)info.status_val; 477 | 478 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 479 | mxSetField(plhs[3], 0, "pobj", tmp); 480 | *mxGetPr(tmp) = info.pobj; 481 | 482 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 483 | mxSetField(plhs[3], 0, "dobj", tmp); 484 | *mxGetPr(tmp) = info.dobj; 485 | 486 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 487 | mxSetField(plhs[3], 0, "res_pri", tmp); 488 | *mxGetPr(tmp) = info.res_pri; 489 | 490 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 491 | mxSetField(plhs[3], 0, "res_dual", tmp); 492 | *mxGetPr(tmp) = info.res_dual; 493 | 494 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 495 | mxSetField(plhs[3], 0, "res_infeas", tmp); 496 | *mxGetPr(tmp) = info.res_infeas; 497 | 498 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 499 | mxSetField(plhs[3], 0, "res_unbdd_a", tmp); 500 | *mxGetPr(tmp) = info.res_unbdd_a; 501 | 502 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 503 | mxSetField(plhs[3], 0, "res_unbdd_p", tmp); 504 | *mxGetPr(tmp) = info.res_unbdd_p; 505 | 506 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 507 | mxSetField(plhs[3], 0, "comp_slack", tmp); 508 | *mxGetPr(tmp) = info.comp_slack; 509 | 510 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 511 | mxSetField(plhs[3], 0, "gap", tmp); 512 | *mxGetPr(tmp) = info.gap; 513 | 514 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 515 | mxSetField(plhs[3], 0, "scale", tmp); 516 | *mxGetPr(tmp) = info.scale; 517 | 518 | /*info.time is millisecs - return value in secs */ 519 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 520 | mxSetField(plhs[3], 0, "setup_time", tmp); 521 | *mxGetPr(tmp) = info.setup_time; 522 | 523 | /*info.time is millisecs - return value in secs */ 524 | tmp = mxCreateDoubleMatrix(1, 1, mxREAL); 525 | mxSetField(plhs[3], 0, "solve_time", tmp); 526 | *mxGetPr(tmp) = info.solve_time; 527 | 528 | free_mex(d, k, stgs); 529 | return; 530 | } 531 | 532 | void free_mex(ScsData *d, ScsCone *k, ScsSettings *stgs) { 533 | if (k) { 534 | if (k->q) { 535 | scs_free(k->q); 536 | } 537 | if (k->bl) { 538 | scs_free(k->bl); 539 | } 540 | if (k->bu) { 541 | scs_free(k->bu); 542 | } 543 | if (k->s) { 544 | scs_free(k->s); 545 | } 546 | if (k->p) { 547 | scs_free(k->p); 548 | } 549 | scs_free(k); 550 | } 551 | if (stgs) { 552 | if (stgs->write_data_filename) { 553 | scs_free((void *)stgs->write_data_filename); 554 | } 555 | if (stgs->log_csv_filename) { 556 | scs_free((void *)stgs->log_csv_filename); 557 | } 558 | scs_free(stgs); 559 | } 560 | if (d) { 561 | #if SFLOAT > 0 /* only free if copies, which is only when flags set */ 562 | if (d->b) { 563 | scs_free(d->b); 564 | } 565 | if (d->c) { 566 | scs_free(d->c); 567 | } 568 | #endif 569 | if (d->A) { 570 | #if !(DLONG > 0) /* only free if copies, which is only when flags set */ 571 | if (d->A->p) { 572 | scs_free(d->A->p); 573 | } 574 | if (d->A->i) { 575 | scs_free(d->A->i); 576 | } 577 | #endif 578 | #if SFLOAT > 0 /* only free if copies, which is only when flags set */ 579 | if (d->A->x) { 580 | scs_free(d->A->x); 581 | } 582 | #endif 583 | scs_free(d->A); 584 | } 585 | if (d->P) { 586 | #if !(DLONG > 0) /* only free if copies, which is only when flags set */ 587 | if (d->P->p) { 588 | scs_free(d->P->p); 589 | } 590 | if (d->P->i) { 591 | scs_free(d->P->i); 592 | } 593 | #endif 594 | #if SFLOAT > 0 /* only free if copies, which is only when flags set */ 595 | if (d->P->x) { 596 | scs_free(d->P->x); 597 | } 598 | #endif 599 | scs_free(d->P); 600 | } 601 | scs_free(d); 602 | } 603 | } 604 | -------------------------------------------------------------------------------- /scs_version_mex.c: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | #include "scs.h" 3 | 4 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 5 | /* matlab usage: ver = scs_version() */ 6 | if (nrhs != 0) { 7 | mexErrMsgTxt("Too many input arguments."); 8 | } 9 | if (nlhs > 1) { 10 | mexErrMsgTxt("Too many output arguments."); 11 | } 12 | plhs[0] = mxCreateString(scs_version()); 13 | return; 14 | } 15 | -------------------------------------------------------------------------------- /test/basic.m: -------------------------------------------------------------------------------- 1 | classdef basic < matlab.unittest.TestCase 2 | 3 | properties 4 | data 5 | cones 6 | end 7 | 8 | properties (TestParameter) 9 | use_indirect = {false, true} 10 | end 11 | 12 | methods(TestMethodSetup) 13 | function setup_problem(testCase) 14 | % Create Problem 15 | rng(1234) 16 | m = 9; 17 | n = 3; 18 | testCase.data.A = sparse(randn(m,n)); 19 | testCase.data.b = randn(m,1); 20 | testCase.data.c = randn(n,1); 21 | testCase.cones.l = m; 22 | end 23 | end 24 | 25 | methods (Test) 26 | function test_random(testCase, use_indirect) 27 | pars.use_indirect = use_indirect; 28 | pars.acceleration_lookback = 10; 29 | [x,y,s,info] = scs(testCase.data,testCase.cones,pars); 30 | testCase.verifyEqual(info.status, 'solved') 31 | 32 | % warm-start test 33 | testCase.data.x = x; 34 | testCase.data.y = y; 35 | testCase.data.s = s; 36 | [~,~,~,info] = scs(testCase.data,testCase.cones,pars); 37 | testCase.verifyEqual(info.status, 'solved') 38 | testCase.verifyLessThanOrEqual(info.iter, 25) 39 | end 40 | end 41 | end 42 | 43 | -------------------------------------------------------------------------------- /test/quad_box.m: -------------------------------------------------------------------------------- 1 | classdef quad_box < matlab.unittest.TestCase 2 | 3 | properties 4 | data 5 | cones 6 | end 7 | 8 | properties (TestParameter) 9 | use_indirect = {false, true} 10 | end 11 | 12 | methods(TestMethodSetup) 13 | function setup_problem(testCase) 14 | % Create Problem 15 | rng(1234) 16 | P = randn(3, 3); 17 | P = P * P'; 18 | testCase.data.A = sparse([zeros(1,3);randn(3,3)]); 19 | testCase.data.P = sparse(P); 20 | testCase.data.b = [1., 0., 0. 0.]'; 21 | testCase.data.c = -ones(3,1); 22 | testCase.cones.bl = [0., 1., -2.]; 23 | testCase.cones.bu = [1., 2., -1.]; 24 | end 25 | end 26 | 27 | methods (Test) 28 | function test_random(testCase, use_indirect) 29 | pars.use_indirect = use_indirect; 30 | pars.acceleration_lookback = 10; 31 | [x,y,s,info] = scs(testCase.data,testCase.cones,pars); 32 | testCase.verifyEqual(info.status, 'solved') 33 | 34 | % warm-start test 35 | testCase.data.x = x; 36 | testCase.data.y = y; 37 | testCase.data.s = s; 38 | [~,~,~,info] = scs(testCase.data,testCase.cones,pars); 39 | testCase.verifyEqual(info.status, 'solved') 40 | testCase.verifyLessThanOrEqual(info.iter, 25) 41 | end 42 | end 43 | end 44 | 45 | -------------------------------------------------------------------------------- /test/string_params.m: -------------------------------------------------------------------------------- 1 | classdef string_params < matlab.unittest.TestCase 2 | 3 | properties 4 | data 5 | cones 6 | end 7 | 8 | properties (TestParameter) 9 | use_indirect = {false, true} 10 | end 11 | 12 | methods(TestMethodSetup) 13 | function setup_problem(testCase) 14 | % Create Problem 15 | rng(1234) 16 | m = 9; 17 | n = 3; 18 | testCase.data.A = sparse(randn(m,n)); 19 | testCase.data.b = randn(m,1); 20 | testCase.data.c = randn(n,1); 21 | testCase.cones.l = m; 22 | end 23 | end 24 | 25 | methods (Test) 26 | function test_random(testCase, use_indirect) 27 | pars.use_indirect = use_indirect; 28 | pars.acceleration_lookback = 10; 29 | % test string parameters 30 | pars.write_data_filename = sprintf('data_dump_indirect_%d', use_indirect); 31 | pars.log_csv_filename = sprintf('log_indirect_%d.csv', use_indirect); 32 | % test other parameters added at the same time 33 | pars.time_limit_secs = 11.0; 34 | pars.adaptive_scale = true; 35 | [~,~,~,info] = scs(testCase.data,testCase.cones,pars); 36 | testCase.verifyEqual(info.status, 'solved') 37 | 38 | pars.time_limit_secs = 0.000001; 39 | pars.adaptive_scale = false; 40 | [~,~,~,info] = scs(testCase.data,testCase.cones,pars); 41 | testCase.verifyEqual(info.status, 'solved (inaccurate - reached time_limit_secs)') 42 | end 43 | end 44 | end 45 | 46 | -------------------------------------------------------------------------------- /test/zero.m: -------------------------------------------------------------------------------- 1 | classdef zero < matlab.unittest.TestCase 2 | 3 | properties 4 | data 5 | cones 6 | end 7 | 8 | properties (TestParameter) 9 | use_indirect = {false, true} 10 | end 11 | 12 | methods(TestMethodSetup) 13 | function setup_problem(testCase) 14 | % Create Problem 15 | rng(1234) 16 | m = 9; 17 | n = 3; 18 | testCase.data.A = sparse(randn(m,n)); 19 | testCase.data.b = randn(m,1); 20 | testCase.data.c = randn(n,1); 21 | testCase.cones.z = m; 22 | end 23 | end 24 | 25 | methods (Test) 26 | function test_random(testCase, use_indirect) 27 | pars.use_indirect = use_indirect; 28 | pars.acceleration_lookback = 0; 29 | [~,~,~,info] = scs(testCase.data,testCase.cones,pars); 30 | testCase.verifyEqual(info.status, 'infeasible') 31 | end 32 | end 33 | end 34 | 35 | --------------------------------------------------------------------------------