/* % ddotX = ddot(d,X,blkstart [, Xblkjc]) % DDOT Given N x m matrix X, creates (blkstart(end)-blkstart(1)) x m matrix % ddotX, having entries d[i]'* xj[i] for each (Lorentz norm bound) block % blkstart(i):blkstart(i+1)-1. If X is sparse, then Xblkjc(:,2:3) should % point to first and 1-beyond-last nonzero in blkstart range for each column. % % SEE ALSO sedumi, partitA. % ********** INTERNAL FUNCTION OF SEDUMI ********** function ddotX = ddot(d,X,blkstart, Xblkjc) % 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 DDOTX_OUT plhs[0] #define NPAROUT 1 #define D_IN prhs[0] #define X_IN prhs[1] #define BLKSTART_IN prhs[2] #define NPARINMIN 3 #define XBLKJC_IN prhs[3] #define NPARIN 4 /* ************************************************************ PROCEDURE ddotxj -Compute y[k]= d[k]'*xpr[k] for each lorentz block k. INPUT d - qDim scaling vector with qDim := blkstart[nblk]-blkstart[0]. xpr - qDim data vector. blkstart - length nblk+1 array, listing 1st subscript per block. NOTE: should have blkstart[0] == 0. nblk - Number of blocks. OUTPUT ypr - nblk vector. Gives d[k]'*xj[k] for each block. ************************************************************ */ void ddotxj(double *ypr, const double *d, const double *xpr, const mwIndex *blkstart, const mwIndex nblk) { mwIndex k; mxAssert(blkstart[0] == 0,""); for(k = 0; k < nblk; k++) ypr[k] = realdot(d+blkstart[k],xpr+blkstart[k], blkstart[k+1]-blkstart[k]); } /* ************************************************************ PROCEDURE spddotxj - Compute y[k] = d_k'*xj_k for each nonzero block in xj. INPUT d - qDim scaling vector with qDim := blkstart[nblk]-blkstart[0]. xir, xpr - sparse matrix. We compute d[k]'*xj[k] for each (lorentz) block where the column xj has nonzeros. xjc0, xjc1 - Length m arrays, subscripts of column j in blkstart-range are between xjc0(j) and xjc1(j). blkstart - length nblk+1 array. Lorentz block k has subscripts blkstart[k]:blkstart[k+1]-1. xblk - length qDim array, with k = xblk(i-blkstart[0]) iff blkstart[k] <= i < blkstart[k+1], k=0:nblk-1. OUTPUT y - sparse nblk x m matrix, with y.jc[m] <= sum(xjc1-xjc0). y(k,j) = d[k]'*xj[k] ************************************************************ */ void spddotxj(jcir y, const double *d, const mwIndex *xir, const double *xpr, const mwIndex *xjc0, const mwIndex *xjc1, const mwIndex *xblk, const mwIndex *blkstart, const mwIndex nblk, const mwIndex m) { mwIndex knz, nexti, inz, i, j, k, lend; double yk; /* ------------------------------------------------------------ INIT: Let blkstart[0] point to 1st nonzero in d and xblk, and let knz poin to 1st available entry in y. Let lend := blkstart[nblk] be 1 beyond valid subscripts. ------------------------------------------------------------ */ d -= blkstart[0]; /* Make d=d(blkstart[0]:blkstart[lorN]) */ xblk -= blkstart[0]; knz = 0; lend = blkstart[nblk]; for(j = 0; j < m; j++){ y.jc[j] = knz; /* ------------------------------------------------------------ Process column only if nonzero subscripts in blkstart[0:nblk]. ------------------------------------------------------------ */ if((inz = xjc0[j]) < xjc1[j]) if( (i = xir[inz]) < lend){ /* ------------------------------------------------------------ Open initial block k; current block has subscripts smaller than nexti. Accumulate yk = ddotxj[k]. ------------------------------------------------------------ */ k = xblk[i]; nexti = blkstart[k + 1]; yk = d[i] * xpr[inz]; /* ------------------------------------------------------------ Browse through nonzeros in xj ------------------------------------------------------------ */ for(++inz; inz < xjc1[j]; inz++) if( (i = xir[inz]) < nexti) yk += d[i] * xpr[inz]; else if(i < lend){ /* ------------------------------------------------------------ If we finished the previous nonzero Lorentz block, then write entry, and initialize new block. ------------------------------------------------------------ */ y.ir[knz] = k; /* yir lists Lorentz blocks */ y.pr[knz++] = yk; k = xblk[i]; /* init new Lorentz block */ nexti = blkstart[k + 1]; yk = d[i] * xpr[inz]; } else /* finished with all Lorentz blocks */ break; /* ------------------------------------------------------------ Write last yk = ddotxj[k] entry into y(:,j). ------------------------------------------------------------ */ y.ir[knz] = k; /* yir lists Lorentz blocks */ y.pr[knz++] = yk; } /* If column j has valid nonzeros */ } /* j=0:m-1 */ /* ------------------------------------------------------------ Close last column of y ------------------------------------------------------------ */ y.jc[m] = knz; } /* ============================================================ MEXFUNCTION ============================================================ */ /* ************************************************************ PROCEDURE mexFunction - Entry for Matlab ************************************************************ */ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { mwIndex i, j, k, m, nrows, maxnnz, nblk, qDim; const double *d, *XjcPr, *blkstartPr; mwIndex *xjc1, *xblk, *blkstart; jcir X, ddotx; /* ------------------------------------------------------------ Check for proper number of arguments ------------------------------------------------------------ */ mxAssert(nrhs >= NPARINMIN, "ddot requires more input arguments."); mxAssert(nlhs <= NPAROUT, "ddot generates less output arguments."); /* ------------------------------------------------------------ Get INPUTS d, X, blkstart. ------------------------------------------------------------ */ d = mxGetPr(D_IN); qDim = mxGetM(D_IN) * mxGetN(D_IN); nrows = mxGetM(X_IN); m = mxGetN(X_IN); X.pr = mxGetPr(X_IN); blkstartPr = mxGetPr(BLKSTART_IN); nblk = mxGetM(BLKSTART_IN) * mxGetN(BLKSTART_IN) - 1; mxAssert(nblk >= 0, "blkstart size mismatch."); /* ------------------------------------------------------------ Allocate mwIndex working array blkstart(nblk+1). ------------------------------------------------------------ */ blkstart = (mwIndex *) mxCalloc(nblk + 1, sizeof(mwIndex)); /* ------------------------------------------------------------ Convert Fortran double to C mwIndex ------------------------------------------------------------ */ for(i = 0; i <= nblk; i++){ j = (mwIndex) blkstartPr[i]; /* double to mwIndex */ mxAssert(j>0,""); blkstart[i] = --j; } if(qDim != blkstart[nblk] - blkstart[0]){ mxAssert(qDim >= blkstart[nblk], "d size mismatch."); d += blkstart[0]; /* Point to Lorentz norm-bound */ qDim = blkstart[nblk] - blkstart[0]; } /* ------------------------------------------------------------ CASE THAT X IS FULL: ------------------------------------------------------------ */ if(!mxIsSparse(X_IN)){ if(nrows != qDim) { if(nrows < blkstart[nblk]){ mxAssert(nrows == nblk + qDim, "X size mismatch"); X.pr += nblk; /* Lorentz tr + norm bound */ } else { /* LP, Lorentz, PSD */ X.pr += blkstart[0]; /* Point to Lorentz norm-bound */ } } /* ------------------------------------------------------------ DDOTX is full nblk x m. ------------------------------------------------------------ */ DDOTX_OUT = mxCreateDoubleMatrix(nblk, m, mxREAL); ddotx.pr = mxGetPr(DDOTX_OUT); /* ------------------------------------------------------------ Let blkstart -= blkstart[0], so that blkstart[0] = 0. ------------------------------------------------------------ */ j = blkstart[0]; for(i = 0; i <= nblk; i++) blkstart[i] -= j; /* ------------------------------------------------------------ Compute d[k]'*x[k,i] for all Lorentz blocks k. ------------------------------------------------------------ */ for(i = 0; i < m; i++){ ddotxj(ddotx.pr, d, X.pr, blkstart, nblk); ddotx.pr += nblk; X.pr += nrows; /* to next column */ } } else{ /* ------------------------------------------------------------ The CASE that X is SPARSE: ------------------------------------------------------------ */ mxAssert(nrows >= blkstart[nblk], "X size mismatch"); X.jc = mxGetJc(X_IN); X.ir = mxGetIr(X_IN); /* ------------------------------------------------------------ Get XqjcPr, pointing to start of Lorentz blocks in X. ------------------------------------------------------------ */ mxAssert(nrhs >= NPARIN, "ddot with sparse X requires more input arguments."); mxAssert(mxGetM(XBLKJC_IN) == m && mxGetN(XBLKJC_IN) >= 3, "Xjc size mismatch"); XjcPr = mxGetPr(XBLKJC_IN) + m; /* Point to Xjc(:,2) */ /* ------------------------------------------------------------ Allocate working arrays: mwIndex xjc1(2*m), xblk(qDim). ------------------------------------------------------------ */ xjc1 = (mwIndex *) mxCalloc(MAX(2*m,1), sizeof(mwIndex) ); xblk = (mwIndex *) mxCalloc(MAX(qDim,1), sizeof(mwIndex) ); /* ------------------------------------------------------------ Convert double to mwIndex: ------------------------------------------------------------ */ for(i = 0; i < 2*m; i++) xjc1[i] = (mwIndex) XjcPr[i]; /* double to mwIndex */ /* ------------------------------------------------------------ Let k = xblk(j-blkstart[0]) iff blkstart[k] <= j < blkstart[k+1], k=0:nblk-1. ------------------------------------------------------------ */ j = 0; for(k = 0; k < nblk; k++){ i = blkstart[k+1] - blkstart[0]; while(j < i) xblk[j++] = k; } /* ------------------------------------------------------------ Let maxnnz := sum(xjc1(:,2)-xjc1(:,1)). Create sparse output ddotX(nblk,m,maxnnz) ------------------------------------------------------------ */ maxnnz = 0; for(i = 0; i < m; i++) maxnnz += xjc1[m+i] - xjc1[i]; maxnnz = MAX(1, maxnnz); DDOTX_OUT = mxCreateSparse(nblk,m, maxnnz,mxREAL); ddotx.jc = mxGetJc(DDOTX_OUT); ddotx.ir = mxGetIr(DDOTX_OUT); ddotx.pr = mxGetPr(DDOTX_OUT); /* ------------------------------------------------------------ The real job: ------------------------------------------------------------ */ spddotxj(ddotx, d, X.ir, X.pr,xjc1,xjc1+m, xblk,blkstart,nblk,m); /* ------------------------------------------------------------ REALLOC (shrink) ddotx to ddotx.jc[m] nonzeros. ------------------------------------------------------------ */ maxnnz = MAX(1,ddotx.jc[m]); if((ddotx.ir = (mwIndex *) mxRealloc(ddotx.ir, maxnnz * sizeof(mwIndex))) == NULL) mexErrMsgTxt("Memory allocation error"); if((ddotx.pr = (double *) mxRealloc(ddotx.pr, maxnnz*sizeof(double))) == NULL) mexErrMsgTxt("Memory allocation error"); mxSetPr(DDOTX_OUT,ddotx.pr); mxSetIr(DDOTX_OUT,ddotx.ir); mxSetNzmax(DDOTX_OUT,maxnnz); /* ------------------------------------------------------------ Release working arrays (SPARSE PART). ------------------------------------------------------------ */ mxFree(xjc1); mxFree(xblk); } /* ------------------------------------------------------------ Release common working arrays. ------------------------------------------------------------ */ mxFree(blkstart); }