File: ldl.c

package info (click to toggle)
ufsparse 1.2-7
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 27,536 kB
  • ctags: 5,848
  • sloc: ansic: 89,328; makefile: 4,721; fortran: 1,991; csh: 207; sed: 162; awk: 33; java: 30; sh: 8
file content (507 lines) | stat: -rw-r--r-- 20,697 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
/* ========================================================================== */
/* === ldl.c: sparse LDL' factorization and solve package =================== */
/* ========================================================================== */

/* LDL:  a simple set of routines for sparse LDL' factorization.  These routines
 * are not terrifically fast (they do not use dense matrix kernels), but the
 * code is very short.  The purpose is to illustrate the algorithms in a very
 * concise manner, primarily for educational purposes.  Although the code is
 * very concise, this package is slightly faster than the built-in sparse
 * Cholesky factorization in MATLAB 7.0 (chol), when using the same input
 * permutation.
 *
 * The routines compute the LDL' factorization of a real sparse symmetric
 * matrix A (or PAP' if a permutation P is supplied), and solve upper
 * and lower triangular systems with the resulting L and D factors.  If A is
 * positive definite then the factorization will be accurate.  A can be
 * indefinite (with negative values on the diagonal D), but in this case no
 * guarantee of accuracy is provided, since no numeric pivoting is performed.
 *
 * The n-by-n sparse matrix A is in compressed-column form.  The nonzero values
 * in column j are stored in Ax [Ap [j] ... Ap [j+1]-1], with corresponding row
 * indices in Ai [Ap [j] ... Ap [j+1]-1].  Ap [0] = 0 is required, and thus
 * nz = Ap [n] is the number of nonzeros in A.  Ap is an int array of size n+1.
 * The int array Ai and the double array Ax are of size nz.  This data structure
 * is identical to the one used by MATLAB, except for the following
 * generalizations.  The row indices in each column of A need not be in any
 * particular order, although they must be in the range 0 to n-1.  Duplicate
 * entries can be present; any duplicates are summed.  That is, if row index i
 * appears twice in a column j, then the value of A (i,j) is the sum of the two
 * entries.  The data structure used here for the input matrix A is more
 * flexible than MATLAB's, which requires sorted columns with no duplicate
 * entries.
 *
 * Only the diagonal and upper triangular part of A (or PAP' if a permutation
 * P is provided) is accessed.  The lower triangular parts of the matrix A or
 * PAP' can be present, but they are ignored.
 *
 * The optional input permutation is provided as an array P of length n.  If
 * P [k] = j, the row and column j of A is the kth row and column of PAP'.
 * If P is present then the factorization is LDL' = PAP' or L*D*L' = A(P,P) in
 * 0-based MATLAB notation.  If P is not present (a null pointer) then no
 * permutation is performed, and the factorization is LDL' = A.
 *
 * The lower triangular matrix L is stored in the same compressed-column
 * form (an int Lp array of size n+1, an int Li array of size Lp [n], and a
 * double array Lx of the same size as Li).  It has a unit diagonal, which is
 * not stored.  The row indices in each column of L are always returned in
 * ascending order, with no duplicate entries.  This format is compatible with
 * MATLAB, except that it would be more convenient for MATLAB to include the
 * unit diagonal of L.  Doing so here would add additional complexity to the
 * code, and is thus omitted in the interest of keeping this code short and
 * readable.
 *
 * The elimination tree is held in the Parent [0..n-1] array.  It is normally
 * not required by the user, but it is required by ldl_numeric.  The diagonal
 * matrix D is held as an array D [0..n-1] of size n.
 *
 * --------------------
 * C-callable routines:
 * --------------------
 *
 *	ldl_symbolic:  Given the pattern of A, computes the Lp and Parent arrays
 *	    required by ldl_numeric.  Takes time proportional to the number of
 *	    nonzeros in L.  Computes the inverse Pinv of P if P is provided.
 *	    Also returns Lnz, the count of nonzeros in each column of L below
 *	    the diagonal (this is not required by ldl_numeric).
 *	ldl_numeric:  Given the pattern and numerical values of A, the Lp array,
 *	    the Parent array, and P and Pinv if applicable, computes the
 *	    pattern and numerical values of L and D.
 *	ldl_lsolve:  Solves Lx=b for a dense vector b.
 *	ldl_dsolve:  Solves Dx=b for a dense vector b.
 *	ldl_ltsolve: Solves L'x=b for a dense vector b.
 *	ldl_perm:    Computes x=Pb for a dense vector b.
 *	ldl_permt:   Computes x=P'b for a dense vector b.
 *	ldl_valid_perm:  checks the validity of a permutation vector
 *	ldl_valid_matrix:  checks the validity of the sparse matrix A
 *
 * ----------------------------
 * Limitations of this package:
 * ----------------------------
 *
 * In the interest of keeping this code simple and readable, ldl_symbolic and
 * ldl_numeric assume their inputs are valid.  You can check your own inputs
 * prior to calling these routines with the ldl_valid_perm and ldl_valid_matrix
 * routines.  Except for the two ldl_valid_* routines, no routine checks to see
 * if the array arguments are present (non-NULL).  Like all C routines, no
 * routine can determine if the arrays are long enough and don't overlap.
 *
 * The ldl_numeric does check the numerical factorization, however.  It returns
 * n if the factorization is successful.  If D (k,k) is zero, then k is
 * returned, and L is only partially computed.
 *
 * No pivoting to control fill-in is performed, which is often critical for
 * obtaining good performance.  I recommend that you compute the permutation P
 * using AMD or SYMAMD (approximate minimum degree ordering routines), or an
 * appropriate graph-partitioning based ordering.  See the ldldemo.m routine for
 * an example in MATLAB, and the ldlmain.c stand-alone C program for examples of
 * how to find P.  Routines for manipulating compressed-column matrices are
 * available in UMFPACK.  AMD, SYMAMD, UMFPACK, and this LDL package are all
 * available at http://www.cise.ufl.edu/research/sparse.
 *
 * -------------------------
 * Possible simplifications:
 * -------------------------
 *
 * These routines could be made even simpler with a few additional assumptions.
 * If no input permutation were performed, the caller would have to permute the
 * matrix first, but the computation of Pinv, and the use of P and Pinv could be
 * removed.  If only the diagonal and upper triangular part of A or PAP' are
 * present, then the tests in the "if (i < k)" statement in ldl_symbolic and
 * "if (i <= k)" in ldl_numeric, are always true, and could be removed (i can
 * equal k in ldl_symbolic, but then the body of the if statement would
 * correctly do no work since Flag [k] == k).  If we could assume that no
 * duplicate entries are present, then the statement Y [i] += Ax [p] could be
 * replaced with Y [i] = Ax [p] in ldl_numeric.
 *
 * --------------------------
 * Description of the method:
 * --------------------------
 *
 * LDL computes the symbolic factorization by finding the pattern of L one row
 * at a time.  It does this based on the following theory.  Consider a sparse
 * system Lx=b, where L, x, and b, are all sparse, and where L comes from a
 * Cholesky (or LDL') factorization.  The elimination tree (etree) of L is
 * defined as follows.  The parent of node j is the smallest k > j such that
 * L (k,j) is nonzero.  Node j has no parent if column j of L is completely zero
 * below the diagonal (j is a root of the etree in this case).  The nonzero
 * pattern of x is the union of the paths from each node i to the root, for
 * each nonzero b (i).  To compute the numerical solution to Lx=b, we can
 * traverse the columns of L corresponding to nonzero values of x.  This
 * traversal does not need to be done in the order 0 to n-1.  It can be done in
 * any "topological" order, such that x (i) is computed before x (j) if i is a
 * descendant of j in the elimination tree.
 *
 * The row-form of the LDL' factorization is shown in the MATLAB function
 * ldlrow.m in this LDL package.  Note that row k of L is found via a sparse
 * triangular solve of L (1:k-1, 1:k-1) \ A (1:k-1, k), to use 1-based MATLAB
 * notation.  Thus, we can start with the nonzero pattern of the kth column of
 * A (above the diagonal), follow the paths up to the root of the etree of the
 * (k-1)-by-(k-1) leading submatrix of L, and obtain the pattern of the kth row
 * of L.  Note that we only need the leading (k-1)-by-(k-1) submatrix of L to
 * do this.  The elimination tree can be constructed as we go.
 *
 * The symbolic factorization does the same thing, except that it discards the
 * pattern of L as it is computed.  It simply counts the number of nonzeros in
 * each column of L and then constructs the Lp index array when it's done.  The
 * symbolic factorization does not need to do this in topological order.
 * Compare ldl_symbolic with the first part of ldl_numeric, and note that the
 * while (len > 0) loop is not present in ldl_symbolic.
 *
 * LDL Version 1.2, Copyright (c) 2005 by Timothy A Davis,
 * University of Florida.  All Rights Reserved.  Developed while on sabbatical
 * at Stanford University and Lawrence Berkeley National Laboratory.  Refer to
 * the README file for the License.  Available at
 * http://www.cise.ufl.edu/research/sparse.
 */

#include "ldl.h"

/* ========================================================================== */
/* === ldl_symbolic ========================================================= */
/* ========================================================================== */

/* The input to this routine is a sparse matrix A, stored in column form, and
 * an optional permutation P.  The output is the elimination tree
 * and the number of nonzeros in each column of L.  Parent [i] = k if k is the
 * parent of i in the tree.  The Parent array is required by ldl_numeric.
 * Lnz [k] gives the number of nonzeros in the kth column of L, excluding the
 * diagonal.
 *
 * One workspace vector (Flag) of size n is required.
 *
 * If P is NULL, then it is ignored.  The factorization will be LDL' = A.
 * Pinv is not computed.  In this case, neither P nor Pinv are required by
 * ldl_numeric.
 *
 * If P is not NULL, then it is assumed to be a valid permutation.  If
 * row and column j of A is the kth pivot, the P [k] = j.  The factorization
 * will be LDL' = PAP', or A (p,p) in MATLAB notation.  The inverse permutation
 * Pinv is computed, where Pinv [j] = k if P [k] = j.  In this case, both P
 * and Pinv are required as inputs to ldl_numeric.
 *
 * The floating-point operation count of the subsequent call to ldl_numeric
 * is not returned, but could be computed after ldl_symbolic is done.  It is
 * the sum of (Lnz [k]) * (Lnz [k] + 2) for k = 0 to n-1.
 */

void ldl_symbolic
(
    int n,		/* A and L are n-by-n, where n >= 0 */
    int Ap [ ],		/* input of size n+1, not modified */
    int Ai [ ],		/* input of size nz=Ap[n], not modified */
    int Lp [ ],		/* output of size n+1, not defined on input */
    int Parent [ ],	/* output of size n, not defined on input */
    int Lnz [ ],	/* output of size n, not defined on input */
    int Flag [ ],	/* workspace of size n, not defn. on input or output */
    int P [ ],		/* optional input of size n */
    int Pinv [ ]	/* optional output of size n (used if P is not NULL) */
)
{
    int i, k, p, kk, p2 ;
    if (P)
    {
	/* If P is present then compute Pinv, the inverse of P */
	for (k = 0 ; k < n ; k++)
	{
	    Pinv [P [k]] = k ;
	}
    }
    for (k = 0 ; k < n ; k++)
    {
	/* L(k,:) pattern: all nodes reachable in etree from nz in A(0:k-1,k) */
	Parent [k] = -1 ;	    /* parent of k is not yet known */
	Flag [k] = k ;		    /* mark node k as visited */
	Lnz [k] = 0 ;		    /* count of nonzeros in column k of L */
	kk = (P) ? (P [k]) : (k) ;  /* kth original, or permuted, column */
	p2 = Ap [kk+1] ;
	for (p = Ap [kk] ; p < p2 ; p++)
	{
	    /* A (i,k) is nonzero (original or permuted A) */
	    i = (Pinv) ? (Pinv [Ai [p]]) : (Ai [p]) ;
	    if (i < k)
	    {
		/* follow path from i to root of etree, stop at flagged node */
		for ( ; Flag [i] != k ; i = Parent [i])
		{
		    /* find parent of i if not yet determined */
		    if (Parent [i] == -1) Parent [i] = k ;
		    Lnz [i]++ ;				/* L (k,i) is nonzero */
		    Flag [i] = k ;			/* mark i as visited */
		}
	    }
	}
    }
    /* construct Lp index array from Lnz column counts */
    Lp [0] = 0 ;
    for (k = 0 ; k < n ; k++)
    {
	Lp [k+1] = Lp [k] + Lnz [k] ;
    }
}


/* ========================================================================== */
/* === ldl_numeric ========================================================== */
/* ========================================================================== */

/* Given a sparse matrix A (the arguments n, Ap, Ai, and Ax) and its symbolic
 * analysis (Lp and Parent, and optionally P and Pinv), compute the numeric LDL'
 * factorization of A or PAP'.  The outputs of this routine are arguments Li,
 * Lx, and D.  It also requires three size-n workspaces (Y, Pattern, and Flag).
 */

int ldl_numeric		/* returns n if successful, k if D (k,k) is zero */
(
    int n,		/* A and L are n-by-n, where n >= 0 */
    int Ap [ ],		/* input of size n+1, not modified */
    int Ai [ ],		/* input of size nz=Ap[n], not modified */
    double Ax [ ],	/* input of size nz=Ap[n], not modified */
    int Lp [ ],		/* input of size n+1, not modified */
    int Parent [ ],	/* input of size n, not modified */
    int Lnz [ ],	/* output of size n, not defn. on input */
    int Li [ ],		/* output of size lnz=Lp[n], not defined on input */
    double Lx [ ],	/* output of size lnz=Lp[n], not defined on input */
    double D [ ],	/* output of size n, not defined on input */
    double Y [ ],	/* workspace of size n, not defn. on input or output */
    int Pattern [ ],	/* workspace of size n, not defn. on input or output */
    int Flag [ ],	/* workspace of size n, not defn. on input or output */
    int P [ ],		/* optional input of size n */
    int Pinv [ ]	/* optional input of size n */
)
{
    double yi, l_ki ;
    int i, k, p, kk, p2, len, top ;
    for (k = 0 ; k < n ; k++)
    {
	/* compute nonzero Pattern of kth row of L, in topological order */
	Y [k] = 0.0 ;		    /* Y(0:k) is now all zero */
	top = n ;		    /* stack for pattern is empty */
	Flag [k] = k ;		    /* mark node k as visited */
	Lnz [k] = 0 ;		    /* count of nonzeros in column k of L */
	kk = (P) ? (P [k]) : (k) ;  /* kth original, or permuted, column */
	p2 = Ap [kk+1] ;
	for (p = Ap [kk] ; p < p2 ; p++)
	{
	    i = (Pinv) ? (Pinv [Ai [p]]) : (Ai [p]) ;	/* get A(i,k) */
	    if (i <= k)
	    {
		Y [i] += Ax [p] ;  /* scatter A(i,k) into Y (sum duplicates) */
		for (len = 0 ; Flag [i] != k ; i = Parent [i])
		{
		    Pattern [len++] = i ;   /* L(k,i) is nonzero */
		    Flag [i] = k ;	    /* mark i as visited */
		}
		while (len > 0) Pattern [--top] = Pattern [--len] ;
	    }
	}
	/* compute numerical values kth row of L (a sparse triangular solve) */
	D [k] = Y [k] ;		    /* get D(k,k) and clear Y(k) */
	Y [k] = 0.0 ;
	for ( ; top < n ; top++)
	{
	    i = Pattern [top] ;	    /* Pattern [top:n-1] is pattern of L(:,k) */
	    yi = Y [i] ;	    /* get and clear Y(i) */
	    Y [i] = 0.0 ;
	    p2 = Lp [i] + Lnz [i] ;
	    for (p = Lp [i] ; p < p2 ; p++)
	    {
		Y [Li [p]] -= Lx [p] * yi ;
	    }
	    l_ki = yi / D [i] ;	    /* the nonzero entry L(k,i) */
	    D [k] -= l_ki * yi ;
	    Li [p] = k ;	    /* store L(k,i) in column form of L */
	    Lx [p] = l_ki ;
	    Lnz [i]++ ;		    /* increment count of nonzeros in col i */
	}
	if (D [k] == 0.0) return (k) ;	    /* failure, D(k,k) is zero */
    }
    return (n) ;	/* success, diagonal of D is all nonzero */
}


/* ========================================================================== */
/* === ldl_lsolve:  solve Lx=b ============================================== */
/* ========================================================================== */

void ldl_lsolve
(
    int n,		/* L is n-by-n, where n >= 0 */
    double X [ ],	/* size n.  right-hand-side on input, soln. on output */
    int Lp [ ],		/* input of size n+1, not modified */
    int Li [ ],		/* input of size lnz=Lp[n], not modified */
    double Lx [ ]	/* input of size lnz=Lp[n], not modified */
)
{
    int j, p, p2 ;
    for (j = 0 ; j < n ; j++)
    {
	p2 = Lp [j+1] ;
	for (p = Lp [j] ; p < p2 ; p++)
	{
	    X [Li [p]] -= Lx [p] * X [j] ;
	}
    }
}


/* ========================================================================== */
/* === ldl_dsolve:  solve Dx=b ============================================== */
/* ========================================================================== */

void ldl_dsolve
(
    int n,		/* D is n-by-n, where n >= 0 */
    double X [ ],	/* size n.  right-hand-side on input, soln. on output */
    double D [ ]	/* input of size n, not modified */
)
{
    int j ;
    for (j = 0 ; j < n ; j++)
    {
	X [j] /= D [j] ;
    }
}


/* ========================================================================== */
/* === ldl_ltsolve: solve L'x=b  ============================================ */
/* ========================================================================== */

void ldl_ltsolve
(
    int n,		/* L is n-by-n, where n >= 0 */
    double X [ ],	/* size n.  right-hand-side on input, soln. on output */
    int Lp [ ],		/* input of size n+1, not modified */
    int Li [ ],		/* input of size lnz=Lp[n], not modified */
    double Lx [ ]	/* input of size lnz=Lp[n], not modified */
)
{
    int j, p, p2 ;
    for (j = n-1 ; j >= 0 ; j--)
    {
	p2 = Lp [j+1] ;
	for (p = Lp [j] ; p < p2 ; p++)
	{
	    X [j] -= Lx [p] * X [Li [p]] ;
	}
    }
}


/* ========================================================================== */
/* === ldl_perm: permute a vector, x=Pb ===================================== */
/* ========================================================================== */

void ldl_perm
(
    int n,		/* size of X, B, and P */
    double X [ ],	/* output of size n. */
    double B [ ],	/* input of size n. */
    int P [ ]		/* input permutation array of size n. */
)
{
    int j ;
    for (j = 0 ; j < n ; j++)
    {
	X [j] = B [P [j]] ;
    }
}


/* ========================================================================== */
/* === ldl_permt: permute a vector, x=P'b =================================== */
/* ========================================================================== */

void ldl_permt
(
    int n,		/* size of X, B, and P */
    double X [ ],	/* output of size n. */
    double B [ ],	/* input of size n. */
    int P [ ]		/* input permutation array of size n. */
)
{
    int j ;
    for (j = 0 ; j < n ; j++)
    {
	X [P [j]] = B [j] ;
    }
}


/* ========================================================================== */
/* === ldl_valid_perm: check if a permutation vector is valid =============== */
/* ========================================================================== */

int ldl_valid_perm	    /* returns 1 if valid, 0 otherwise */
(
    int n,
    int P [ ],		    /* input of size n, a permutation of 0:n-1 */
    int Flag [ ]	    /* workspace of size n */
)
{
    int j, k ;
    if (n < 0 || !Flag)
    {
	return (0) ;	    /* n must be >= 0, and Flag must be present */
    }
    if (!P)
    {
	return (1) ;	    /* If NULL, P is assumed to be the identity perm. */
    }
    for (j = 0 ; j < n ; j++)
    {
	Flag [j] = 0 ;	    /* clear the Flag array */
    }
    for (k = 0 ; k < n ; k++)
    {
	j = P [k] ;
	if (j < 0 || j >= n || Flag [j] != 0)
	{
	    return (0) ;    /* P is not valid */
	}
	Flag [j] = 1 ;
    }
    return (1) ;	    /* P is valid */
}


/* ========================================================================== */
/* === ldl_valid_matrix: check if a sparse matrix is valid ================== */
/* ========================================================================== */

/* This routine checks to see if a sparse matrix A is valid for input to
 * ldl_symbolic and ldl_numeric.  It returns 1 if the matrix is valid, 0
 * otherwise.  A is in sparse column form.  The numerical values in column j
 * are stored in Ax [Ap [j] ... Ap [j+1]-1], with row indices in
 * Ai [Ap [j] ... Ap [j+1]-1].  The Ax array is not checked.
 */

int ldl_valid_matrix
(
    int n,
    int Ap [ ],
    int Ai [ ]
)
{
    int j, p ;
    if (n < 0 || !Ap || !Ai || Ap [0] != 0)
    {
	return (0) ;	    /* n must be >= 0, and Ap and Ai must be present */
    }
    for (j = 0 ; j < n ; j++)
    {
	if (Ap [j] > Ap [j+1])
	{
	    return (0) ;    /* Ap must be monotonically nondecreasing */
	}
    }
    for (p = 0 ; p < Ap [n] ; p++)
    {
	if (Ai [p] < 0 || Ai [p] >= n)
	{
	    return (0) ;    /* row indices must be in the range 0 to n-1 */
	}
    }
    return (1) ;	    /* matrix is valid */
}