1 /* ========================================================================== */
2 /* === Supernodal/cholmod_super_numeric ===================================== */
3 /* ========================================================================== */
4 
5 /* -----------------------------------------------------------------------------
6  * CHOLMOD/Supernodal Module.  Copyright (C) 2005-2006, Timothy A. Davis
7  * http://www.suitesparse.com
8  * -------------------------------------------------------------------------- */
9 
10 /* Computes the Cholesky factorization of A+beta*I or A*F+beta*I.  Only the
11  * the lower triangular part of A+beta*I or A*F+beta*I is accessed.  The
12  * matrices A and F must already be permuted according to the fill-reduction
13  * permutation L->Perm.  cholmod_factorize is an "easy" wrapper for this code
14  * which applies that permutation.  beta is real.
15  *
16  * Symmetric case: A is a symmetric (lower) matrix.  F is not accessed.
17  * With a fill-reducing permutation, A(p,p) should be passed instead, where is
18  * p is L->Perm.
19  *
20  * Unsymmetric case: A is unsymmetric, and F must be present.  Normally, F=A'.
21  * With a fill-reducing permutation, A(p,f) and A(p,f)' should be passed as A
22  * and F, respectively, where f is a list of the subset of the columns of A.
23  *
24  * The input factorization L must be supernodal (L->is_super is TRUE).  It can
25  * either be symbolic or numeric.  In the first case, L has been analyzed by
26  * cholmod_analyze or cholmod_super_symbolic, but the matrix has not yet been
27  * numerically factorized.  The numerical values are allocated here and the
28  * factorization is computed.  In the second case, a prior matrix has been
29  * analyzed and numerically factorized, and a new matrix is being factorized.
30  * The numerical values of L are replaced with the new numerical factorization.
31  *
32  * L->is_ll is ignored, and set to TRUE.  This routine always computes an LL'
33  * factorization.  Supernodal LDL' factorization is not (yet) supported.
34  * FUTURE WORK: perform a supernodal LDL' factorization if L->is_ll is FALSE.
35  *
36  * Uses BLAS routines dsyrk, dgemm, dtrsm, and the LAPACK routine dpotrf.
37  * The supernodal solver uses BLAS routines dtrsv, dgemv, dtrsm, and dgemm.
38  *
39  * If the matrix is not positive definite the routine returns TRUE, but sets
40  * Common->status to CHOLMOD_NOT_POSDEF and L->minor is set to the column at
41  * which the failure occurred.  The supernode containing the non-positive
42  * diagonal entry is set to zero (this includes columns to the left of L->minor
43  * in the same supernode), as are all subsequent supernodes.
44  *
45  * workspace: Flag (nrow), Head (nrow+1), Iwork (2*nrow + 5*nsuper).
46  *	Allocates temporary space of size L->maxcsize * sizeof(double)
47  *	(twice that for the complex/zomplex case).
48  *
49  * If L is supernodal symbolic on input, it is converted to a supernodal numeric
50  * factor on output, with an xtype of real if A is real, or complex if A is
51  * complex or zomplex.  If L is supernodal numeric on input, its xtype must
52  * match A (except that L can be complex and A zomplex).  The xtype of A and F
53  * must match.
54  */
55 
56 #ifndef NGPL
57 #ifndef NSUPERNODAL
58 
59 #include "cholmod_internal.h"
60 #include "cholmod_supernodal.h"
61 
62 #ifdef GPU_BLAS
63 #include "cholmod_gpu.h"
64 #endif
65 
66 /* ========================================================================== */
67 /* === TEMPLATE codes for GPU and regular numeric factorization ============= */
68 /* ========================================================================== */
69 
70 #ifdef DLONG
71 #ifdef GPU_BLAS
72 #define REAL
73 #include "../GPU/t_cholmod_gpu.c"
74 #define COMPLEX
75 #include "../GPU/t_cholmod_gpu.c"
76 #define ZOMPLEX
77 /* no #include of "../GPU/t_cholmod_gpu.c".  Zomplex case relies on complex */
78 #endif
79 #endif
80 
81 #define REAL
82 #include "t_cholmod_super_numeric.c"
83 #define COMPLEX
84 #include "t_cholmod_super_numeric.c"
85 #define ZOMPLEX
86 #include "t_cholmod_super_numeric.c"
87 
88 /* ========================================================================== */
89 /* === cholmod_super_numeric ================================================ */
90 /* ========================================================================== */
91 
92 /* Returns TRUE if successful, or if the matrix is not positive definite.
93  * Returns FALSE if out of memory, inputs are invalid, or other fatal error
94  * occurs.
95  */
96 
CHOLMOD(super_numeric)97 int CHOLMOD(super_numeric)
98 (
99     /* ---- input ---- */
100     cholmod_sparse *A,	/* matrix to factorize */
101     cholmod_sparse *F,	/* F = A' or A(:,f)' */
102     double beta [2],	/* beta*I is added to diagonal of matrix to factorize */
103     /* ---- in/out --- */
104     cholmod_factor *L,	/* factorization */
105     /* --------------- */
106     cholmod_common *Common
107 )
108 {
109     cholmod_dense *C ;
110     Int *Super, *Map, *SuperMap ;
111     size_t maxcsize ;
112     Int nsuper, n, i, k, s, stype, nrow ;
113     int ok = TRUE, symbolic ;
114     size_t t, w ;
115 
116     /* ---------------------------------------------------------------------- */
117     /* check inputs */
118     /* ---------------------------------------------------------------------- */
119 
120     RETURN_IF_NULL_COMMON (FALSE) ;
121     RETURN_IF_NULL (L, FALSE) ;
122     RETURN_IF_NULL (A, FALSE) ;
123     RETURN_IF_XTYPE_INVALID (A, CHOLMOD_REAL, CHOLMOD_ZOMPLEX, FALSE) ;
124     RETURN_IF_XTYPE_INVALID (L, CHOLMOD_PATTERN, CHOLMOD_COMPLEX, FALSE) ;
125     stype = A->stype ;
126     if (stype < 0)
127     {
128 	if (A->nrow != A->ncol || A->nrow != L->n)
129 	{
130 	    ERROR (CHOLMOD_INVALID, "invalid dimensions") ;
131 	    return (FALSE) ;
132 	}
133     }
134     else if (stype == 0)
135     {
136 	if (A->nrow != L->n)
137 	{
138 	    ERROR (CHOLMOD_INVALID, "invalid dimensions") ;
139 	    return (FALSE) ;
140 	}
141 	RETURN_IF_NULL (F, FALSE) ;
142 	RETURN_IF_XTYPE_INVALID (F, CHOLMOD_REAL, CHOLMOD_ZOMPLEX, FALSE) ;
143 	if (A->nrow != F->ncol || A->ncol != F->nrow || F->stype != 0)
144 	{
145 	    ERROR (CHOLMOD_INVALID, "F invalid") ;
146 	    return (FALSE) ;
147 	}
148 	if (A->xtype != F->xtype)
149 	{
150 	    ERROR (CHOLMOD_INVALID, "A and F must have same xtype") ;
151 	    return (FALSE) ;
152 	}
153     }
154     else
155     {
156 	/* symmetric upper case not suppored */
157 	ERROR (CHOLMOD_INVALID, "symmetric upper case not supported") ;
158 	return (FALSE) ;
159     }
160     if (!(L->is_super))
161     {
162 	ERROR (CHOLMOD_INVALID, "L not supernodal") ;
163 	return (FALSE) ;
164     }
165     if (L->xtype != CHOLMOD_PATTERN)
166     {
167 	if (! ((A->xtype == CHOLMOD_REAL    && L->xtype == CHOLMOD_REAL)
168 	    || (A->xtype == CHOLMOD_COMPLEX && L->xtype == CHOLMOD_COMPLEX)
169 	    || (A->xtype == CHOLMOD_ZOMPLEX && L->xtype == CHOLMOD_COMPLEX)))
170 	{
171 	    ERROR (CHOLMOD_INVALID, "complex type mismatch") ;
172 	    return (FALSE) ;
173 	}
174     }
175     Common->status = CHOLMOD_OK ;
176 
177     /* ---------------------------------------------------------------------- */
178     /* allocate workspace in Common */
179     /* ---------------------------------------------------------------------- */
180 
181     nsuper = L->nsuper ;
182     maxcsize = L->maxcsize ;
183     nrow = A->nrow ;
184     n = nrow ;
185 
186     PRINT1 (("nsuper "ID" maxcsize %g\n", nsuper, (double) maxcsize)) ;
187     ASSERT (nsuper >= 0 && maxcsize > 0) ;
188 
189     /* w = 2*n + 5*nsuper */
190     w = CHOLMOD(mult_size_t) (n, 2, &ok) ;
191     t = CHOLMOD(mult_size_t) (nsuper, 5, &ok) ;
192     w = CHOLMOD(add_size_t) (w, t, &ok) ;
193     if (!ok)
194     {
195 	ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
196 	return (FALSE) ;
197     }
198 
199     CHOLMOD(allocate_work) (n, w, 0, Common) ;
200     if (Common->status < CHOLMOD_OK)
201     {
202 	return (FALSE) ;
203     }
204     ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
205 
206     /* ---------------------------------------------------------------------- */
207     /* get the current factor L and allocate numerical part, if needed */
208     /* ---------------------------------------------------------------------- */
209 
210     Super = L->super ;
211     symbolic = (L->xtype == CHOLMOD_PATTERN) ;
212     if (symbolic)
213     {
214 	/* convert to supernodal numeric by allocating L->x */
215 	CHOLMOD(change_factor) (
216 		(A->xtype == CHOLMOD_REAL) ? CHOLMOD_REAL : CHOLMOD_COMPLEX,
217 		TRUE, TRUE, TRUE, TRUE, L, Common) ;
218 	if (Common->status < CHOLMOD_OK)
219 	{
220 	    /* the factor L remains in symbolic supernodal form */
221 	    return (FALSE) ;
222 	}
223     }
224     ASSERT (L->dtype == DTYPE) ;
225     ASSERT (L->xtype == CHOLMOD_REAL || L->xtype == CHOLMOD_COMPLEX) ;
226 
227     /* supernodal LDL' is not supported */
228     L->is_ll = TRUE ;
229 
230     /* ---------------------------------------------------------------------- */
231     /* get more workspace */
232     /* ---------------------------------------------------------------------- */
233 
234     C = CHOLMOD(allocate_dense) (maxcsize, 1, maxcsize, L->xtype, Common) ;
235     if (Common->status < CHOLMOD_OK)
236     {
237 	int status = Common->status ;
238 	if (symbolic)
239 	{
240 	    /* Change L back to symbolic, since the numeric values are not
241 	     * initialized.  This cannot fail. */
242 	    CHOLMOD(change_factor) (CHOLMOD_PATTERN, TRUE, TRUE, TRUE, TRUE,
243 		    L, Common) ;
244 	}
245 	/* the factor L is now back to the form it had on input */
246 	Common->status = status ;
247 	return (FALSE) ;
248     }
249 
250     /* ---------------------------------------------------------------------- */
251     /* get workspace */
252     /* ---------------------------------------------------------------------- */
253 
254     SuperMap = Common->Iwork ;		/* size n (i/i/l) */
255     Map = Common->Flag ;    /* size n, use Flag as workspace for Map array */
256     for (i = 0 ; i < n ; i++)
257     {
258 	Map [i] = EMPTY ;
259     }
260 
261     /* ---------------------------------------------------------------------- */
262     /* find the mapping of nodes to relaxed supernodes */
263     /* ---------------------------------------------------------------------- */
264 
265     /* SuperMap [k] = s if column k is contained in supernode s */
266     for (s = 0 ; s < nsuper ; s++)
267     {
268 	PRINT1 (("Super ["ID"] "ID" ncols "ID"\n",
269 		    s, Super[s], Super[s+1]-Super[s]));
270 	for (k = Super [s] ; k < Super [s+1] ; k++)
271 	{
272 	    SuperMap [k] = s ;
273 	    PRINT2 (("relaxed SuperMap ["ID"] = "ID"\n", k, SuperMap [k])) ;
274 	}
275     }
276 
277     /* ---------------------------------------------------------------------- */
278     /* supernodal numerical factorization, using template routine */
279     /* ---------------------------------------------------------------------- */
280 
281     switch (A->xtype)
282     {
283 	case CHOLMOD_REAL:
284 	    ok = r_cholmod_super_numeric (A, F, beta, L, C, Common) ;
285 	    break ;
286 
287 	case CHOLMOD_COMPLEX:
288 	    ok = c_cholmod_super_numeric (A, F, beta, L, C, Common) ;
289 	    break ;
290 
291 	case CHOLMOD_ZOMPLEX:
292 	    /* This operates on complex L, not zomplex */
293 	    ok = z_cholmod_super_numeric (A, F, beta, L, C, Common) ;
294 	    break ;
295     }
296 
297     /* ---------------------------------------------------------------------- */
298     /* clear Common workspace, free temp workspace C, and return */
299     /* ---------------------------------------------------------------------- */
300 
301     /* Flag array was used as workspace, clear it */
302     Common->mark = EMPTY ;
303     /* CHOLMOD(clear_flag) (Common) ; */
304     CHOLMOD_CLEAR_FLAG (Common) ;
305     ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
306     CHOLMOD(free_dense) (&C, Common) ;
307     return (ok) ;
308 }
309 #endif
310 #endif
311