/* ************************************************************ % [perm, dz] = incorder(At [,Ajc1,ifirst]) % INCORDER % perm sorts the columns of At greedily, by iteratively picking % the 1st unprocessed column with the least number of nonzero % subscripts THAT ARE NOT YET COVERED (hence incremental) by % the previously processed columns. % dz has the corresponding incremental sparsity structure, i.e. % each column lists only the ADDITIONAL subscripts w.r.t. the % previous ones. % % SEE ALSO getada3, dpr1fact. % ******************** INTERNAL FUNCTION OF SEDUMI ******************** function [perm, dz] = incorder(At,Ajc1,ifirst) % This file is part of SeDuMi 1.1 by Imre Polik and Oleksandr Romanko % Copyright (C) 2005 McMaster University, Hamilton, CANADA (since 1.1) % % Copyright (C) 2001 Jos F. Sturm (up to 1.05R5) % Dept. Econometrics & O.R., Tilburg University, the Netherlands. % Supported by the Netherlands Organization for Scientific Research (NWO). % % Affiliation SeDuMi 1.03 and 1.04Beta (2000): % Dept. Quantitative Economics, Maastricht University, the Netherlands. % % Affiliations up to SeDuMi 1.02 (AUG1998): % CRL, McMaster University, Canada. % Supported by the Netherlands Organization for Scientific Research (NWO). % % This program is free software; you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation; either version 2 of the License, or % (at your option) any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program; if not, write to the Free Software % Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA % 02110-1301, USA ************************************************************ */ #include #include "mex.h" #include "blksdp.h" #define PERM_OUT myplhs[0] /* incremental ordering */ #define DZ_OUT myplhs[1] /* incremental nonzero structure */ #define NPAROUT 2 #define AT_IN prhs[0] #define NPARINMIN 1 #define AJC1_IN prhs[1] #define IFIRST_IN prhs[2] #define NPARIN 3 /* ************************************************************ PROCEDURE spPartTransp - Let (Ajc, Air) = At(first:lenfull-1,:)'. INPUT Atir - length Atjc2[m-1] subscript array. Atjc1 - length m array, pointing to 1st index in At(:,j) that is in range first:lenfull-1. Atjc2 - length m array, pointing to column end. first, lenfull - index range we're interested in. (lenud:=lenfull-first) m - number of constraints (rows in At). OUTPUT Ajc - length 1+lenud mwIndex-array, column pointers of A := At(first:lenfull-1,:)'. Air - subscript array of output matrix A. length is: sum(Atjc(2:m) - Atjcs(1:m)). WORK iwork - length lenud (=lenfull-first) integer array. ************************************************************ */ void spPartTransp(mwIndex *Air, mwIndex *Ajc, const mwIndex *Atir, const mwIndex *Atjc1, const mwIndex *Atjc2, const mwIndex first, const mwIndex lenfull, const mwIndex m, mwIndex *iwork) { mwIndex i,j,inz; mwIndex *inxnz; /* ------------------------------------------------------------ INIT: Make indices Ajc[first:lenfull] valid. Then set Ajc(:)=all-0. ------------------------------------------------------------ */ Ajc -= first; for(i = first; i <= lenfull; i++) Ajc[i] = 0; /* ------------------------------------------------------------ For each column j=0:m-1, in each nz PSD entry Atj(i): ++inxnz[i], where inxnz := Ajc+1. (we use Ajc[first+1:lenfull]) ------------------------------------------------------------ */ inxnz = Ajc + 1; for(j = 0; j < m; j++) for(inz = Atjc1[j]; inz < Atjc2[j]; inz++) ++inxnz[Atir[inz]]; /* ------------------------------------------------------------ cumsum(inxnz). Note that Ajc[0] =0 already. ------------------------------------------------------------ */ for(inz = 0, i = first; i < lenfull; i++) inxnz[i] += inxnz[i-1]; /* ------------------------------------------------------------ Write the subscripts of A:= At(first:lenfull-1,:)'. ------------------------------------------------------------ */ memcpy(iwork, Ajc + first, (lenfull - first) * sizeof(mwIndex)); iwork -= first; /* points to next avl. entry in A(:,i) */ for(j = 0; j < m; j++) for(inz = Atjc1[j]; inz < Atjc2[j]; inz++){ i = Atir[inz]; Air[iwork[i]++] = j; /* as (j,i) entry */ } } /* ************************************************************ PROCEDURE incorder - Greedy ordering of PSD-part of columns in At, starting with least number of (PSD)-nonzeros. Good fot getada3. Produces N x m sparse matrix dz, having at most lenud nonzeros. The last columns correspond to the "deg" columns, and are not reordered. The first m-ndeg are reordered, see "perm". INPUT Atjc1 - length m, start of At(first:lenful,j) for each j=1:m. Atjc2 - column end pointers, length m. Atir - row-subscripts of At. Ajc, Air - sparsity structure of A := At(first:lenful,:)', is m x lenud. m - number of columns in At. first - first PSD subscipt. OUTPUT perm - length m array. perm(1:m-ndeg) is the greedy ordering, starting from sparsest. perm(m-ndeg:m) = deg. dzjc - length m+1, column pointers into dzir. dzir - length dzjc[m] <= lenud. jth column lists additional subscripts to dz(:,0:j-1). WORK iwork - (length m) Remaining length of PSD part in each column j=1:m. discard - length lenud of Booleans. 1 means index already in dz. ************************************************************ */ void incorder(mwIndex *perm, mwIndex *dzjc, mwIndex *dzir, const mwIndex *Atjc1,const mwIndex *Atjc2,const mwIndex *Atir, const mwIndex *Ajc,const mwIndex *Air, const mwIndex m, const mwIndex first, const mwIndex lenud, mwIndex *iwork, char *discard) { mwIndex kmin,lenmin,i,j,k, inz,jnz, permk; /* ------------------------------------------------------------ initialize: dzjc[0]=0, nperm = m - ndeg. Let Ajc-=first, so that Ajc[first:lenfull] is valid. Similar for discard. Initialize discard to all-0 ("False"). ------------------------------------------------------------ */ dzjc[0] = 0; Ajc -= first; memset(discard, '\0', lenud); /* all-0 */ discard -= first; /* ------------------------------------------------------------ Let perm(1:m) be 1:m ------------------------------------------------------------ */ for(i = 0; i < m; i++) perm[i] = i; /* ------------------------------------------------------------ Set iwork = Atjc2(1:m)-Atjc1(1:m). The number of (not yet discarded) nz-PSD indices per constraint. ------------------------------------------------------------ */ for(k = 0; k < m; k++){ iwork[k] = Atjc2[k] - Atjc1[k]; } /* ------------------------------------------------------------ In iterate k=0:m-1, pivot on constraint with length iwork[k] minimal. ------------------------------------------------------------ */ for(k = 0; k < m; k++){ kmin = k; lenmin = iwork[perm[k]]; for(j = k+1; j < m; j++) if(iwork[perm[j]] < lenmin){ lenmin = iwork[perm[j]]; kmin = j; } mxAssert(lenmin >= 0,""); permk = perm[kmin]; /* make pivot in perm */ perm[kmin] = perm[k]; perm[k] = permk; /* ------------------------------------------------------------ Write the (additional) subscripts of the pivot column into k-th column of dz, i.e. dz(:,k) = At(:,permk). ------------------------------------------------------------ */ jnz = dzjc[k]; for(inz = Atjc1[permk]; inz < Atjc2[permk]; inz++){ i = Atir[inz]; if(!discard[i]){ discard[i] = 1; dzir[jnz++] = i; } } mxAssert(jnz == dzjc[k] + lenmin,""); /* ------------------------------------------------------------ Discard dz(:,k)'s subscripts: adjust the constraint lengths where applicable. ------------------------------------------------------------ */ dzjc[k+1] = jnz; for(jnz = dzjc[k]; jnz < dzjc[k+1]; jnz++){ i = dzir[jnz]; /* i is discarded subscript */ for(inz = Ajc[i]; inz < Ajc[i+1]; inz++){ j = Air[inz]; iwork[j]--; /* subscript discard here */ } } } } /* ************************************************************ PROCEDURE mexFunction - Entry for Matlab [perm,dz] = incorder(At,Ajc1,ifirst) ************************************************************ */ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { mxArray *myplhs[NPAROUT]; mwIndex i, m, firstPSD, lenud, iwsiz, lenfull, maxnnz; mwIndex *iwork, *Atjc1, *Ajc, *Air, *perm; const mwIndex *Atjc2, *Atir; double *permPr; const double *Ajc1Pr; char *cwork; jcir dz; /* ------------------------------------------------------------ Check for proper number of arguments ------------------------------------------------------------ */ mxAssert(nrhs >= NPARINMIN, "incorder requires more input arguments."); mxAssert(nlhs <= NPAROUT, "incorder produces less output arguments."); /* -------------------------------------------------- GET STATISTICS: -------------------------------------------------- */ if(nrhs == NPARIN){ firstPSD = (mwIndex) mxGetScalar(IFIRST_IN); /* double to mwIndex */ mxAssert(firstPSD>0,""); --firstPSD; /* Fortran to C */ } else firstPSD = 0; /* default: use all subscripts */ lenfull = mxGetM(AT_IN); lenud = lenfull - firstPSD; /* -------------------------------------------------- Check size At, and get At. -------------------------------------------------- */ mxAssert(mxIsSparse(AT_IN), "At must be a sparse matrix."); m = mxGetN(AT_IN); Atjc2 = mxGetJc(AT_IN)+1; /* points to end of constraint */ Atir = mxGetIr(AT_IN); /* subscripts */ /* ------------------------------------------------------------ ALLOCATE WORKING arrays: mwIndex Atjc1(m), Ajc(1+lenud), Air(maxnnz), perm(m), iwork(iwsiz). iwsiz = MAX(lenud,1+m) char cwork(lenud). ------------------------------------------------------------ */ Atjc1 = (mwIndex *) mxCalloc(MAX(m,1), sizeof(mwIndex)); Ajc = (mwIndex *) mxCalloc(1+lenud, sizeof(mwIndex)); perm = (mwIndex *) mxCalloc(MAX(1,m), sizeof(mwIndex)); iwsiz = MAX(lenud, 1 + m); iwork = (mwIndex *) mxCalloc(iwsiz, sizeof(mwIndex)); /* iwork(iwsiz) */ cwork = (char *) mxCalloc(MAX(1,lenud), sizeof(char)); /* ------------------------------------------------------------ Get input AJc1 ------------------------------------------------------------ */ if(nrhs == NPARIN){ Ajc1Pr = mxGetPr(AJC1_IN); mxAssert(mxGetM(AJC1_IN) * mxGetN(AJC1_IN) >= m, "Ajc1 size mismatch"); /* ------------------------------------------------------------ Double to mwIndex: Atjc1 ------------------------------------------------------------ */ for(i = 0; i < m; i++) Atjc1[i] = (mwIndex) Ajc1Pr[i]; /* double to mwIndex */ /* ------------------------------------------------------------ Let maxnnz = number of PSD nonzeros = sum(Atjc2-Atjc1) ------------------------------------------------------------ */ maxnnz = 0; for(i = 0; i < m; i++) maxnnz += Atjc2[i] - Atjc1[i]; } else{ memcpy(Atjc1, mxGetJc(AT_IN), m * sizeof(mwIndex)); /* default column start */ maxnnz = Atjc2[m-1]; /* maxnnz = nnz(At) */ } /* ------------------------------------------------------------ ALLOCATE WORKING array mwIndex Air(maxnnz) ------------------------------------------------------------ */ Air = (mwIndex *) mxCalloc(MAX(1,maxnnz), sizeof(mwIndex)); /* ------------------------------------------------------------ CREATE OUTPUT ARRAYS PERM(m) and DZ=sparse(lenfull,m,lenud). ------------------------------------------------------------ */ PERM_OUT = mxCreateDoubleMatrix(m,(mwSize)1,mxREAL); permPr = mxGetPr(PERM_OUT); DZ_OUT = mxCreateSparse(lenfull,m,lenud,mxREAL); dz.jc = mxGetJc(DZ_OUT); dz.ir = mxGetIr(DZ_OUT); /* ------------------------------------------------------------ Let (Ajc,Air) := At(first:end,:)', the transpose of PSD-part. Uses iwork(lenud) ------------------------------------------------------------ */ spPartTransp(Air,Ajc, Atir,Atjc1,Atjc2, firstPSD,lenfull, m,iwork); /* ------------------------------------------------------------ The main job: greedy order of columns of At ------------------------------------------------------------ */ incorder(perm, dz.jc,dz.ir, Atjc1,Atjc2,Atir, Ajc,Air, m, firstPSD,lenud, iwork, cwork); /* uses iwork(m+1) */ /* ------------------------------------------------------------ REALLOC (shrink) dz to dz.jc[m] nonzeros. ------------------------------------------------------------ */ mxAssert(dz.jc[m] <= lenud,""); maxnnz = MAX(1,dz.jc[m]); /* avoid realloc to 0 */ if((dz.ir = (mwIndex *) mxRealloc(dz.ir, maxnnz * sizeof(mwIndex))) == NULL) mexErrMsgTxt("Memory allocation error"); if((dz.pr = (double *) mxRealloc(mxGetPr(DZ_OUT), maxnnz*sizeof(double))) == NULL) mexErrMsgTxt("Memory allocation error"); mxSetPr(DZ_OUT,dz.pr); mxSetIr(DZ_OUT,dz.ir); mxSetNzmax(DZ_OUT,maxnnz); for(i = 0; i < maxnnz; i++) dz.pr[i] = 1.0; /* ------------------------------------------------------------ Convert C-mwIndex to Fortran-double ------------------------------------------------------------ */ for(i = 0; i < m; i++) permPr[i] = perm[i] + 1.0; /* ------------------------------------------------------------ Release working arrays ------------------------------------------------------------ */ mxFree(cwork); mxFree(iwork); mxFree(perm); mxFree(Air); mxFree(Ajc); mxFree(Atjc1); /* ------------------------------------------------------------ Copy requested output parameters (at least 1), release others. ------------------------------------------------------------ */ i = MAX(nlhs, 1); memcpy(plhs,myplhs, i * sizeof(mxArray *)); for(; i < NPAROUT; i++) mxDestroyArray(myplhs[i]); }