Page MenuHomec4science

blkchol.c
No OneTemporary

File Metadata

Created
Mon, Jul 14, 01:53

blkchol.c

/*
% [L.L, L.d, skip, diagadd] = blkchol(L,ADA [,pars [,absd]])
% BLKCHOL Computes sparse lower-triangular Cholesky factor L,
% L*L' = P(perm,perm)
% Input Parameter L is typically generated by symbchol.
% Parameters: pars.canceltol and pars.maxu.
% Optional: absd to force adding diag if drops below canceltol*absd.
%
% There are important differences with standard CHOL(P(L.perm,L.perm))':
%
% - BLKCHOL uses the supernodal partition XSUPER, possibly splitted
% by SPLIT, to use dense linear algebra on dense subblocks.
% Much faster than CHOL.
%
% - BLKCHOL never fails. It only sees the lower triangular part of P;
% if during the elimination, a diagonal entry becomes negative
% (due to massive cancelation), the corresponding d[k] is set to 0.
% If d[k] suffers from cancelation and norm(L(:,k)) becomes too big, then
% the column is skipped, and listed in (skip, Lskip).
%
% SEE ALSO sparchol, fwblkslv, bwblkslv
% 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 <string.h>
#include "mex.h"
#include "blksdp.h"
#define L_OUT myplhs[0]
#define D_OUT myplhs[1]
#define SKIP_OUT myplhs[2]
#define DIAGADD_OUT myplhs[3]
#define NPAROUT 4
#define L_IN prhs[0]
#define P_IN prhs[1]
#define NPARINMIN 2
#define PARS_IN prhs[2]
#define ABSD_IN prhs[3]
#define NPARIN 4
/* ------------------------------------------------------------
PROTOTYPES:
------------------------------------------------------------ */
mwIndex blkLDL(const mwIndex neqns, const mwIndex nsuper, const mwIndex *xsuper,
const mwIndex *snode, const mwIndex *xlindx, const mwIndex *lindx,
double *lb,
const mwIndex *ljc, double *lpr, double *d, const mwIndex *perm,
const double ub, const double maxu, mwIndex *skipIr,
mwIndex iwsiz, mwIndex *iwork, mwIndex fwsiz, double *fwork);
/* ============================================================
SUBROUTINES:
============================================================*/
/* ------------------------------------------------------------
PERMUTEP - Let L = tril(P(perm,perm))
INPUT
Ljc, Lir - sparsity structure of output matrix L = tril(P(perm,perm)).
Pjc, Pir, Ppr - Input matrix, before ordering.
perm - length m pivot ordering.
m - order: P is m x m.
WORKING ARRAY
Pj - Length m float work array.
IMPORTANT: L, P and PERM in C style.
------------------------------------------------------------ */
void permuteP(const mwIndex *Ljc,const mwIndex *Lir,double *Lpr,
const mwIndex *Pjc,const mwIndex *Pir,const double *Ppr,
const mwIndex *perm, double *Pj, const mwIndex m)
{
mwIndex j,inz,jcol;
/* ------------------------------------------------------------
Let Pj = all-0
------------------------------------------------------------ */
fzeros(Pj,m);
/* ------------------------------------------------------------
For each column j, let
Pj(:) = P(:,PERM(j)) and L(:,j) = Pj(PERM(:)) (L sparse)
------------------------------------------------------------ */
for(j = 0; j < m; j++){
jcol = perm[j];
for(inz = Pjc[jcol]; inz < Pjc[jcol+1]; inz++)
Pj[Pir[inz]] = Ppr[inz];
for(inz = Ljc[j]; inz < Ljc[j+1]; inz++)
Lpr[inz] = Pj[perm[Lir[inz]]];
/* ------------------------------------------------------------
Let Pj = all-0
------------------------------------------------------------ */
for(inz = Pjc[jcol]; inz < Pjc[jcol+1]; inz++)
Pj[Pir[inz]] = 0.0;
}
}
/* ************************************************************
SPCHOL - calls the block cholesky blkLDL.
INPUT:
m - Order of L: L is m x m, ne.At is N x m.
nsuper - Number of supernodes (blocks).
xsuper - Length nsuper+1: first simple-node of each supernode
snode - Length neqns: snode(node) is the supernode containing "node".
ljc - Length neqns+1: start of the columns of L.
abstol - minimum diagonal value. If 0, then no absolute threshold.
canceltol - Force d >= canceltol * orgd (by adding low-rank diag).
maxu - Maximal allowed max(abs(L(:,k)))..
If L gets to big in these columns, we skip the pivots.
iwsiz, fwsiz - size of integer and floating-point working storage.
See "WORKING ARRAYS" for required amount.
UPDATED:
lindx - row indices. On INPUT: for each column (by ljc),
on OUTPUT: for each supernode (by xlindx).
Lpr - On input, contains tril(X), on output
such that X = L*diag(d)*L'.
lb - Length neqns. INPUT: diag entries BEFORE cancelation;
OUTPUT: lb(skipIr) are values of low rank diag. matrix that is
added before factorization.
OUTPUT
xlindx - Length nsuper+1: Start of sparsity structure in lindx,
for each supernode (all simple nodes in a supernode have the
same nonzero-structure).
snode - Length m: snode(node) is the supernode containing "node".
d - length neqns vector, diagonal in L*diag(d)*L'.
skipIr - length nskip (<= neqns) array. skipIr(1:nskip) lists the
columns that have been skipped in the Cholesky. d[skipIr] = 0.
WORKING ARRAYS:
iwork - Length iwsiz working array; iwsiz = 2*m + 2 * nsuper.
fwork - Length fwsiz working vector; fwsiz = L.tmpsiz.
RETURNS - nskip (<=neqns), number of skipped nodes. Length of skipIr.
*********************************************************************** */
mwIndex spchol(const mwIndex m, const mwIndex nsuper, const mwIndex *xsuper,
mwIndex *snode, mwIndex *xlindx, mwIndex *lindx, double *lb,
const mwIndex *ljc, double *lpr, double *d, const mwIndex *perm,
const double abstol,
const double canceltol, const double maxu, mwIndex *skipIr,
mwIndex *pnadd,
const mwIndex iwsiz, mwIndex *iwork, const mwIndex fwsiz, double *fwork)
{
mwIndex jsup,j,ix,jcol,collen, nskip, nadd;
double ub, dj;
/* ------------------------------------------------------------
Let ub = max(diag(L)) / maxu^2
------------------------------------------------------------ */
ub = 0.0;
for(j = 0; j < m; j++)
if((dj = lpr[ljc[j]]) > ub)
ub = dj;
ub /= SQR(maxu);
/* ------------------------------------------------------------
Let lb = MAX(abstol, canceltol * lbIN), where lbIN is diag(L),
or those quantities before being affected by cancelation.
------------------------------------------------------------ */
for(j = 0; j < m; j++)
if((dj = canceltol * lb[j]) > abstol)
lb[j] = dj;
else
lb[j] = abstol;
/* ------------------------------------------------------------
SNODE: map each column to the supernode containing it
------------------------------------------------------------ */
j = xsuper[0];
for(jsup = 0; jsup < nsuper; jsup++){
while(j < xsuper[jsup + 1])
snode[j++] = jsup;
}
/* ------------------------------------------------------------
COMPRESS SUBSCRIPTS:
Let (xlindx,lindx) = ljc(xsuper(:)), i.e store only once
for each snode, instead of once per column.
------------------------------------------------------------ */
for(ix = 0, jsup = 0; jsup < nsuper; jsup++){
xlindx[jsup] = ix;
jcol = xsuper[jsup];
collen = ljc[jcol+1] - ljc[jcol];
memmove(lindx + ix, lindx + ljc[jcol], collen * sizeof(mwIndex));
ix += collen;
}
xlindx[nsuper] = ix;
/* ------------------------------------------------------------
Do the block sparse Cholesky L*D*L'
------------------------------------------------------------ */
nskip = blkLDL(m, nsuper, xsuper, snode, xlindx, lindx, lb,
ljc, lpr, d, perm,
ub, maxu, skipIr, iwsiz, iwork, fwsiz, fwork);
if(nskip == (mwIndex)-1)
return nskip;
/* ------------------------------------------------------------
Let iwork = diag-adding indices. Viz. where d(skipIr)>0.0.
Let skipIr = skipIr except diag-adding indices. Hence d(skipIr)=0.
------------------------------------------------------------ */
if(iwsiz < nskip)
return (mwIndex)-1;
ix = 0;
nadd = 0;
for(j = 0; j < nskip; j++){
jsup = skipIr[j];
if(d[jsup] > 0.0)
iwork[nadd++] = jsup; /* diagonal adding */
else
skipIr[ix++] = jsup; /* pivot skipping */
}
*pnadd = nadd;
return ix;
}
/* ============================================================
MAIN: MEXFUNCTION
============================================================ */
/* ************************************************************
PROCEDURE mexFunction - Entry for Matlab
************************************************************ */
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
const mxArray *L_FIELD;
mxArray *myplhs[NPAROUT];
mwIndex m, i, j, iwsiz, nsuper, tmpsiz, fwsiz, nskip, nadd, m1;
double *fwork, *d, *skipPr, *orgd;
const double *permPr,*xsuperPr,*Ppr,*absd;
mwIndex *perm, *snode, *xsuper, *iwork, *xlindx, *skip, *skipJc;
const mwIndex *LINir, *Pjc, *Pir;
double canceltol, maxu, abstol;
jcir L;
char useAbsd, useDelay;
/* ------------------------------------------------------------
Check for proper number of arguments
blkchol(L,P, pars,absd) with nparinmin=2.
------------------------------------------------------------ */
mxAssert(nrhs >= NPARINMIN, "blkchol requires more input arguments");
mxAssert(nlhs <= NPAROUT, "blkchol produces less output arguments");
/* ------------------------------------------------------------
Get input matrix P to be factored.
------------------------------------------------------------ */
m = mxGetM(P_IN);
mxAssert( m == mxGetN(P_IN), "P must be square");
mxAssert(mxIsSparse(P_IN), "P must be sparse");
Pjc = mxGetJc(P_IN);
Pir = mxGetIr(P_IN);
Ppr = mxGetPr(P_IN);
/* ------------------------------------------------------------
Disassemble block Cholesky structure L
------------------------------------------------------------ */
mxAssert(mxIsStruct(L_IN), "Parameter `L' should be a structure.");
L_FIELD = mxGetField(L_IN,(mwIndex)0,"perm"); /* L.perm */
mxAssert( L_FIELD != NULL, "Missing field L.perm.");
mxAssert(m == mxGetM(L_FIELD) * mxGetN(L_FIELD), "perm size mismatch");
permPr = mxGetPr(L_FIELD);
L_FIELD = mxGetField(L_IN,(mwIndex)0,"L"); /* L.L */
mxAssert( L_FIELD != NULL, "Missing field L.L.");
mxAssert( m == mxGetM(L_FIELD) && m == mxGetN(L_FIELD), "Size L.L mismatch.");
mxAssert(mxIsSparse(L_FIELD), "L.L should be sparse.");
L.jc = mxGetJc(L_FIELD);
LINir = mxGetIr(L_FIELD);
L_FIELD = mxGetField(L_IN,(mwIndex)0,"xsuper"); /* L.xsuper */
mxAssert( L_FIELD != NULL, "Missing field L.xsuper.");
nsuper = mxGetM(L_FIELD) * mxGetN(L_FIELD) - 1;
mxAssert( nsuper <= m, "Size L.xsuper mismatch.");
xsuperPr = mxGetPr(L_FIELD);
L_FIELD = mxGetField(L_IN,(mwIndex)0,"tmpsiz"); /* L.tmpsiz */
mxAssert( L_FIELD != NULL, "Missing field L.tmpsiz.");
tmpsiz = (mwIndex) mxGetScalar(L_FIELD);
/* ------------------------------------------------------------
Disassemble pars structure: canceltol, maxu
------------------------------------------------------------ */
canceltol = 1E-12; /* supply with defaults */
maxu = 5E2;
abstol = 1E-20;
useAbsd = 0;
useDelay = 0;
if(nrhs >= NPARINMIN + 1){ /* 3rd argument = pars */
mxAssert(mxIsStruct(PARS_IN), "Parameter `pars' should be a structure.");
if( (L_FIELD = mxGetField(PARS_IN,(mwIndex)0,"canceltol")) != NULL)
canceltol = mxGetScalar(L_FIELD); /* pars.canceltol */
if( (L_FIELD = mxGetField(PARS_IN,(mwIndex)0,"maxu")) != NULL)
maxu = mxGetScalar(L_FIELD); /* pars.maxu */
if( (L_FIELD = mxGetField(PARS_IN,(mwIndex)0,"abstol")) != NULL){
abstol = mxGetScalar(L_FIELD); /* pars.abstol */
abstol = MAX(abstol, 0.0);
}
if( (L_FIELD = mxGetField(PARS_IN,(mwIndex)0,"delay")) != NULL)
useDelay = (char) mxGetScalar(L_FIELD); /* pars.delay */
/* ------------------------------------------------------------
Get optional vector absd
------------------------------------------------------------ */
if(nrhs >= NPARIN){
useAbsd = 1;
absd = mxGetPr(ABSD_IN);
mxAssert(m == mxGetM(ABSD_IN) * mxGetN(ABSD_IN), "absd size mismatch");
}
}
/* ------------------------------------------------------------
Create sparse output matrix L(m x m).
------------------------------------------------------------ */
L_OUT = mxCreateSparse(m,m, L.jc[m],mxREAL);
L.ir = mxGetIr(L_OUT);
L.pr = mxGetPr(L_OUT);
memcpy(mxGetJc(L_OUT), L.jc, (m+1) * sizeof(mwIndex));
memcpy(L.ir, LINir, L.jc[m] * sizeof(mwIndex));
/* ------------------------------------------------------------
Create ouput vector d(m).
------------------------------------------------------------ */
D_OUT = mxCreateDoubleMatrix(m,(mwSize)1,mxREAL);
d = mxGetPr(D_OUT);
/* ------------------------------------------------------------
Compute required sizes of working arrays:
iwsiz = 2*(m + nsuper).
fwsiz = tmpsiz.
------------------------------------------------------------ */
iwsiz = MAX(2*(m+nsuper), 1);
fwsiz = MAX(tmpsiz, 1);
/* ------------------------------------------------------------
Allocate working arrays:
integer: perm(m), snode(m), xsuper(nsuper+1),
iwork(iwsiz), xlindx(m+1), skip(m),
double: orgd(m), fwork(fwsiz).
------------------------------------------------------------ */
m1 = MAX(m,1); /* avoid alloc to 0 */
perm = (mwIndex *) mxCalloc(m1,sizeof(mwIndex));
snode = (mwIndex *) mxCalloc(m1,sizeof(mwIndex));
xsuper = (mwIndex *) mxCalloc(nsuper+1,sizeof(mwIndex));
iwork = (mwIndex *) mxCalloc(iwsiz,sizeof(mwIndex));
xlindx = (mwIndex *) mxCalloc(m+1,sizeof(mwIndex));
skip = (mwIndex *) mxCalloc(m1, sizeof(mwIndex));
orgd = (double *) mxCalloc(m1,sizeof(double));
fwork = (double *) mxCalloc(fwsiz,sizeof(double));
/* ------------------------------------------------------------
Convert PERM, XSUPER to integer and C-Style
------------------------------------------------------------ */
for(i = 0; i < m; i++){
j = (mwIndex) permPr[i];
mxAssert(j>0,"");
perm[i] = --j;
}
for(i = 0; i <= nsuper; i++){
j = (mwIndex) xsuperPr[i];
mxAssert(j>0,"");
xsuper[i] = --j;
}
/* ------------------------------------------------------------
Let L = tril(P(PERM,PERM)), uses orgd(m) as temp working storage.
------------------------------------------------------------ */
permuteP(L.jc,L.ir,L.pr, Pjc,Pir,Ppr, perm, orgd, m);
/* ------------------------------------------------------------
If no orgd has been supplied, take orgd = diag(L on input)
Otherwise, let orgd = absd(perm).
------------------------------------------------------------ */
if(useAbsd)
for(j = 0; j < m; j++)
orgd[j] = absd[perm[j]];
else
for(j = 0; j < m; j++)
orgd[j] = L.pr[L.jc[j]];
/* ------------------------------------------------------------
Create "snode" and "xlindx"; change L.ir to the compact subscript
array (with xlindx), and do BLOCK SPARSE CHOLESKY.
------------------------------------------------------------ */
nskip = spchol(m, nsuper, xsuper, snode, xlindx,
L.ir, orgd, L.jc, L.pr, d, perm, abstol,
canceltol, maxu, skip, &nadd, iwsiz, iwork, fwsiz, fwork);
mxAssert(nskip >= 0, "Insufficient workspace in pblkchol");
/* ------------------------------------------------------------
Copy original row-indices from LINir to L.ir.
------------------------------------------------------------ */
memcpy(L.ir, LINir, L.jc[m] * sizeof(mwIndex));
/* ------------------------------------------------------------
Create output matrices skip = sparse([],[],[],m,1,nskip),
diagadd = sparse([],[],[],m,1,nadd),
------------------------------------------------------------ */
SKIP_OUT = mxCreateSparse(m,(mwSize)1, MAX(1,nskip),mxREAL);
memcpy(mxGetIr(SKIP_OUT), skip, nskip * sizeof(mwIndex));
skipJc = mxGetJc(SKIP_OUT);
skipJc[0] = 0; skipJc[1] = nskip;
skipPr = mxGetPr(SKIP_OUT);
/* ------------------------------------------------------------
useDelay = 1 then L(:,i) is i-th column before ith pivot; useful
for pivot-delaying strategy. (Fwslv(L, L(:,i)) still required.)
------------------------------------------------------------ */
if(useDelay == 1)
for(j = 0; j < nskip; j++)
skipPr[j] = 1.0;
else
for(j = 0; j < nskip; j++){
i = skip[j];
skipPr[j] = L.pr[L.jc[i]]; /* Set skipped l(:,i)=ei. */
L.pr[L.jc[i]] = 1.0;
fzeros(L.pr+L.jc[i]+1,L.jc[i+1]-L.jc[i]-1);
}
DIAGADD_OUT = mxCreateSparse(m,(mwSize)1, MAX(1,nadd),mxREAL);
memcpy(mxGetIr(DIAGADD_OUT), iwork, nadd * sizeof(mwIndex));
skipJc = mxGetJc(DIAGADD_OUT);
skipJc[0] = 0; skipJc[1] = nadd;
skipPr = mxGetPr(DIAGADD_OUT);
for(j = 0; j < nadd; j++)
skipPr[j] = orgd[iwork[j]];
/* ------------------------------------------------------------
Release working arrays.
------------------------------------------------------------ */
mxFree(fwork);
mxFree(orgd);
mxFree(skip);
mxFree(xlindx);
mxFree(iwork);
mxFree(xsuper);
mxFree(snode);
mxFree(perm);
/* ------------------------------------------------------------
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]);
}

Event Timeline