1 /* ========================================================================== */
2 /* === UMF_row_search ======================================================= */
3 /* ========================================================================== */
4
5 /* -------------------------------------------------------------------------- */
6 /* UMFPACK Copyright (c) Timothy A. Davis, CISE, */
7 /* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */
8 /* web: http://www.cise.ufl.edu/research/sparse/umfpack */
9 /* -------------------------------------------------------------------------- */
10
11 /*
12 Find two candidate pivot rows in a column: the best one in the front,
13 and the best one not in the front. Return the two pivot row patterns and
14 their exact degrees. Called by UMF_local_search.
15
16 Returns UMFPACK_OK if successful, or UMFPACK_WARNING_singular_matrix or
17 UMFPACK_ERROR_different_pattern if not.
18
19 */
20
21 #include "umf_internal.h"
22 #include "umf_row_search.h"
23
UMF_row_search(NumericType * Numeric,WorkType * Work,SymbolicType * Symbolic,Int cdeg0,Int cdeg1,const Int Pattern[],const Int Pos[],Int pivrow[2],Int rdeg[2],Int W_i[],Int W_o[],Int prior_pivrow[2],const Entry Wxy[],Int pivcol,Int freebie[])24 GLOBAL Int UMF_row_search
25 (
26 NumericType *Numeric,
27 WorkType *Work,
28 SymbolicType *Symbolic,
29 Int cdeg0, /* length of column in Front */
30 Int cdeg1, /* length of column outside Front */
31 const Int Pattern [ ], /* pattern of column, Pattern [0..cdeg1 -1] */
32 const Int Pos [ ], /* Pos [Pattern [0..cdeg1 -1]] = 0..cdeg1 -1 */
33 Int pivrow [2], /* pivrow [IN] and pivrow [OUT] */
34 Int rdeg [2], /* rdeg [IN] and rdeg [OUT] */
35 Int W_i [ ], /* pattern of pivrow [IN], */
36 /* either Fcols or Woi */
37 Int W_o [ ], /* pattern of pivrow [OUT], */
38 /* either Wio or Woo */
39 Int prior_pivrow [2], /* the two other rows just scanned, if any */
40 const Entry Wxy [ ], /* numerical values Wxy [0..cdeg1-1],
41 either Wx or Wy */
42
43 Int pivcol, /* the candidate column being searched */
44 Int freebie [ ]
45 )
46 {
47
48 /* ---------------------------------------------------------------------- */
49 /* local variables */
50 /* ---------------------------------------------------------------------- */
51
52 double maxval, toler, toler2, value, pivot [2] ;
53 Int i, row, deg, col, *Frpos, fnrows, *E, j, ncols, *Cols, *Rows,
54 e, f, Wrpflag, *Fcpos, fncols, tpi, max_rdeg, nans_in_col, was_offdiag,
55 diag_row, prefer_diagonal, *Wrp, found, *Diagonal_map ;
56 Tuple *tp, *tpend, *tp1, *tp2 ;
57 Unit *Memory, *p ;
58 Element *ep ;
59 Int *Row_tuples, *Row_degree, *Row_tlen ;
60
61 #ifndef NDEBUG
62 Int *Col_degree ;
63 DEBUG2 (("Row_search:\n")) ;
64 for (i = 0 ; i < cdeg1 ; i++)
65 {
66 row = Pattern [i] ;
67 DEBUG4 ((" row: "ID"\n", row)) ;
68 ASSERT (row >= 0 && row < Numeric->n_row) ;
69 ASSERT (i == Pos [row]) ;
70 }
71 /* If row is not in Pattern [0..cdeg1-1], then Pos [row] == EMPTY */
72 if (UMF_debug > 0 || Numeric->n_row < 1000)
73 {
74 Int cnt = cdeg1 ;
75 DEBUG4 (("Scan all rows:\n")) ;
76 for (row = 0 ; row < Numeric->n_row ; row++)
77 {
78 if (Pos [row] < 0)
79 {
80 cnt++ ;
81 }
82 else
83 {
84 DEBUG4 ((" row: "ID" pos "ID"\n", row, Pos [row])) ;
85 }
86 }
87 ASSERT (cnt == Numeric->n_row) ;
88 }
89 Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro only */
90 ASSERT (pivcol >= 0 && pivcol < Work->n_col) ;
91 ASSERT (NON_PIVOTAL_COL (pivcol)) ;
92 #endif
93
94 pivot [IN] = 0. ;
95 pivot [OUT] = 0. ;
96
97 /* ---------------------------------------------------------------------- */
98 /* get parameters */
99 /* ---------------------------------------------------------------------- */
100
101 Row_degree = Numeric->Rperm ;
102 Row_tuples = Numeric->Uip ;
103 Row_tlen = Numeric->Uilen ;
104 Wrp = Work->Wrp ;
105 Frpos = Work->Frpos ;
106 E = Work->E ;
107 Memory = Numeric->Memory ;
108 fnrows = Work->fnrows ;
109
110 prefer_diagonal = Symbolic->prefer_diagonal ;
111 Diagonal_map = Work->Diagonal_map ;
112
113 if (Diagonal_map)
114 {
115 diag_row = Diagonal_map [pivcol] ;
116 was_offdiag = diag_row < 0 ;
117 if (was_offdiag)
118 {
119 /* the "diagonal" entry in this column was permuted here by an
120 * earlier pivot choice. The tighter off-diagonal tolerance will
121 * be used instead of the symmetric tolerance. */
122 diag_row = FLIP (diag_row) ;
123 }
124 ASSERT (diag_row >= 0 && diag_row < Numeric->n_row) ;
125 }
126 else
127 {
128 diag_row = EMPTY ; /* unused */
129 was_offdiag = EMPTY ; /* unused */
130 }
131
132 /* pivot row degree cannot exceed max_rdeg */
133 max_rdeg = Work->fncols_max ;
134
135 /* ---------------------------------------------------------------------- */
136 /* scan pivot column for candidate rows */
137 /* ---------------------------------------------------------------------- */
138
139 maxval = 0.0 ;
140 nans_in_col = FALSE ;
141
142 for (i = 0 ; i < cdeg1 ; i++)
143 {
144 APPROX_ABS (value, Wxy [i]) ;
145 if (SCALAR_IS_NAN (value))
146 {
147 nans_in_col = TRUE ;
148 maxval = value ;
149 break ;
150 }
151 /* This test can now ignore the NaN case: */
152 maxval = MAX (maxval, value) ;
153 }
154
155 /* if maxval is zero, the matrix is numerically singular */
156
157 toler = Numeric->relpt * maxval ;
158 toler2 = Numeric->relpt2 * maxval ;
159 toler2 = was_offdiag ? toler : toler2 ;
160
161 DEBUG5 (("Row_search begins [ maxval %g toler %g %g\n",
162 maxval, toler, toler2)) ;
163 if (SCALAR_IS_NAN (toler) || SCALAR_IS_NAN (toler2))
164 {
165 nans_in_col = TRUE ;
166 }
167
168 if (!nans_in_col)
169 {
170
171 /* look for the diagonal entry, if it exists */
172 found = FALSE ;
173 ASSERT (!SCALAR_IS_NAN (toler)) ;
174
175 if (prefer_diagonal)
176 {
177 ASSERT (diag_row != EMPTY) ;
178 i = Pos [diag_row] ;
179 if (i >= 0)
180 {
181 double a ;
182 ASSERT (i < cdeg1) ;
183 ASSERT (diag_row == Pattern [i]) ;
184
185 APPROX_ABS (a, Wxy [i]) ;
186
187 ASSERT (!SCALAR_IS_NAN (a)) ;
188 ASSERT (!SCALAR_IS_NAN (toler2)) ;
189
190 if (SCALAR_IS_NONZERO (a) && a >= toler2)
191 {
192 /* found it! */
193 DEBUG3 (("Symmetric pivot: "ID" "ID"\n", pivcol, diag_row));
194 found = TRUE ;
195 if (Frpos [diag_row] >= 0 && Frpos [diag_row] < fnrows)
196 {
197 pivrow [IN] = diag_row ;
198 pivrow [OUT] = EMPTY ;
199 }
200 else
201 {
202 pivrow [IN] = EMPTY ;
203 pivrow [OUT] = diag_row ;
204 }
205 }
206 }
207 }
208
209 /* either no diagonal found, or we didn't look for it */
210 if (!found)
211 {
212 if (cdeg0 > 0)
213 {
214
215 /* this is a column in the front */
216 for (i = 0 ; i < cdeg0 ; i++)
217 {
218 double a ;
219 APPROX_ABS (a, Wxy [i]) ;
220 ASSERT (!SCALAR_IS_NAN (a)) ;
221 ASSERT (!SCALAR_IS_NAN (toler)) ;
222 if (SCALAR_IS_NONZERO (a) && a >= toler)
223 {
224 row = Pattern [i] ;
225 deg = Row_degree [row] ;
226 #ifndef NDEBUG
227 DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",
228 i, row, deg, a)) ;
229 UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
230 #endif
231 ASSERT (Frpos [row] >= 0 && Frpos [row] < fnrows) ;
232 ASSERT (Frpos [row] == i) ;
233 /* row is in the current front */
234 DEBUG4 ((" in front\n")) ;
235 if (deg < rdeg [IN]
236 /* break ties by picking the largest entry: */
237 || (deg == rdeg [IN] && a > pivot [IN])
238 /* break ties by picking the diagonal entry: */
239 /* || (deg == rdeg [IN] && row == diag_row) */
240 )
241 {
242 /* best row in front, so far */
243 pivrow [IN] = row ;
244 rdeg [IN] = deg ;
245 pivot [IN] = a ;
246 }
247 }
248 }
249 for ( ; i < cdeg1 ; i++)
250 {
251 double a ;
252 APPROX_ABS (a, Wxy [i]) ;
253 ASSERT (!SCALAR_IS_NAN (a)) ;
254 ASSERT (!SCALAR_IS_NAN (toler)) ;
255 if (SCALAR_IS_NONZERO (a) && a >= toler)
256 {
257 row = Pattern [i] ;
258 deg = Row_degree [row] ;
259 #ifndef NDEBUG
260 DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",
261 i, row, deg, a)) ;
262 UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
263 #endif
264 ASSERT (Frpos [row] == i) ;
265 /* row is not in the current front */
266 DEBUG4 ((" NOT in front\n")) ;
267 if (deg < rdeg [OUT]
268 /* break ties by picking the largest entry: */
269 || (deg == rdeg [OUT] && a > pivot [OUT])
270 /* break ties by picking the diagonal entry: */
271 /* || (deg == rdeg [OUT] && row == diag_row) */
272 )
273 {
274 /* best row not in front, so far */
275 pivrow [OUT] = row ;
276 rdeg [OUT] = deg ;
277 pivot [OUT] = a ;
278 }
279 }
280 }
281
282 }
283 else
284 {
285
286 /* this column is not in the front */
287 for (i = 0 ; i < cdeg1 ; i++)
288 {
289 double a ;
290 APPROX_ABS (a, Wxy [i]) ;
291 ASSERT (!SCALAR_IS_NAN (a)) ;
292 ASSERT (!SCALAR_IS_NAN (toler)) ;
293 if (SCALAR_IS_NONZERO (a) && a >= toler)
294 {
295 row = Pattern [i] ;
296 deg = Row_degree [row] ;
297 #ifndef NDEBUG
298 DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",
299 i, row, deg, a)) ;
300 UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
301 #endif
302 if (Frpos [row] >= 0 && Frpos [row] < fnrows)
303 {
304 /* row is in the current front */
305 DEBUG4 ((" in front\n")) ;
306 if (deg < rdeg [IN]
307 /* break ties by picking the largest entry: */
308 || (deg == rdeg [IN] && a > pivot [IN])
309 /* break ties by picking the diagonal entry: */
310 /* || (deg == rdeg [IN] && row == diag_row) */
311 )
312 {
313 /* best row in front, so far */
314 pivrow [IN] = row ;
315 rdeg [IN] = deg ;
316 pivot [IN] = a ;
317 }
318 }
319 else
320 {
321 /* row is not in the current front */
322 DEBUG4 ((" NOT in front\n")) ;
323 if (deg < rdeg [OUT]
324 /* break ties by picking the largest entry: */
325 || (deg == rdeg[OUT] && a > pivot [OUT])
326 /* break ties by picking the diagonal entry: */
327 /* || (deg == rdeg[OUT] && row == diag_row) */
328 )
329 {
330 /* best row not in front, so far */
331 pivrow [OUT] = row ;
332 rdeg [OUT] = deg ;
333 pivot [OUT] = a ;
334 }
335 }
336 }
337 }
338 }
339 }
340 }
341
342 /* ---------------------------------------------------------------------- */
343 /* NaN handling */
344 /* ---------------------------------------------------------------------- */
345
346 /* if cdeg1 > 0 then we must have found a pivot row ... unless NaN's */
347 /* exist. Try with no numerical tests if no pivot found. */
348
349 if (cdeg1 > 0 && pivrow [IN] == EMPTY && pivrow [OUT] == EMPTY)
350 {
351 /* cleanup for the NaN case */
352 DEBUG0 (("Found a NaN in pivot column!\n")) ;
353
354 /* grab the first entry in the pivot column, ignoring degree, */
355 /* numerical stability, and symmetric preference */
356 row = Pattern [0] ;
357 deg = Row_degree [row] ;
358 if (Frpos [row] >= 0 && Frpos [row] < fnrows)
359 {
360 /* row is in the current front */
361 DEBUG4 ((" in front\n")) ;
362 pivrow [IN] = row ;
363 rdeg [IN] = deg ;
364 }
365 else
366 {
367 /* row is not in the current front */
368 DEBUG4 ((" NOT in front\n")) ;
369 pivrow [OUT] = row ;
370 rdeg [OUT] = deg ;
371 }
372
373 /* We are now guaranteed to have a pivot, no matter how broken */
374 /* (non-IEEE compliant) the underlying numerical operators are. */
375 /* This is particularly a problem for Microsoft compilers (they do */
376 /* not handle NaN's properly). Now try to find a sparser pivot, if */
377 /* possible. */
378
379 for (i = 1 ; i < cdeg1 ; i++)
380 {
381 row = Pattern [i] ;
382 deg = Row_degree [row] ;
383
384 if (Frpos [row] >= 0 && Frpos [row] < fnrows)
385 {
386 /* row is in the current front */
387 DEBUG4 ((" in front\n")) ;
388 if (deg < rdeg [IN] || (deg == rdeg [IN] && row == diag_row))
389 {
390 /* best row in front, so far */
391 pivrow [IN] = row ;
392 rdeg [IN] = deg ;
393 }
394 }
395 else
396 {
397 /* row is not in the current front */
398 DEBUG4 ((" NOT in front\n")) ;
399 if (deg < rdeg [OUT] || (deg == rdeg [OUT] && row == diag_row))
400 {
401 /* best row not in front, so far */
402 pivrow [OUT] = row ;
403 rdeg [OUT] = deg ;
404 }
405 }
406 }
407 }
408
409 /* We found a pivot if there are entries (even zero ones) in pivot col */
410 ASSERT (IMPLIES (cdeg1 > 0, pivrow[IN] != EMPTY || pivrow[OUT] != EMPTY)) ;
411
412 /* If there are no entries in the pivot column, then no pivot is found */
413 ASSERT (IMPLIES (cdeg1 == 0, pivrow[IN] == EMPTY && pivrow[OUT] == EMPTY)) ;
414
415 /* ---------------------------------------------------------------------- */
416 /* check for singular matrix */
417 /* ---------------------------------------------------------------------- */
418
419 if (cdeg1 == 0)
420 {
421 if (fnrows > 0)
422 {
423 /*
424 Get the pivrow [OUT][IN] from the current front.
425 The frontal matrix looks like this:
426
427 pivcol[OUT]
428 |
429 v
430 x x x x 0 <- so grab this row as the pivrow [OUT][IN].
431 x x x x 0
432 x x x x 0
433 0 0 0 0 0
434
435 The current frontal matrix has some rows in it. The degree
436 of the pivcol[OUT] is zero. The column is empty, and the
437 current front does not contribute to it.
438
439 */
440 pivrow [IN] = Work->Frows [0] ;
441 DEBUGm4 (("Got zero pivrow[OUT][IN] "ID" from current front\n",
442 pivrow [IN])) ;
443 }
444 else
445 {
446
447 /*
448 Get a pivot row from the row-merge tree, use as
449 pivrow [OUT][OUT]. pivrow [IN] remains EMPTY.
450 This can only happen if the current front is 0-by-0.
451 */
452
453 Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1,
454 row2, fleftmost, nfr, n_row, frontid ;
455
456 ASSERT (Work->fncols == 0) ;
457
458 Front_leftmostdesc = Symbolic->Front_leftmostdesc ;
459 Front_1strow = Symbolic->Front_1strow ;
460 Front_new1strow = Work->Front_new1strow ;
461 nfr = Symbolic->nfr ;
462 n_row = Numeric->n_row ;
463 frontid = Work->frontid ;
464
465 DEBUGm4 (("Note: pivcol: "ID" is empty front "ID"\n",
466 pivcol, frontid)) ;
467 #ifndef NDEBUG
468 DEBUG1 (("Calling dump rowmerge\n")) ;
469 UMF_dump_rowmerge (Numeric, Symbolic, Work) ;
470 #endif
471
472 /* Row-merge set is the non-pivotal rows in the range */
473 /* Front_new1strow [Front_leftmostdesc [frontid]] to */
474 /* Front_1strow [frontid+1] - 1. */
475 /* If this is empty, then use the empty rows, in the range */
476 /* Front_new1strow [nfr] to n_row-1. */
477 /* If this too is empty, then pivrow [OUT] will be empty. */
478 /* In both cases, update Front_new1strow [...]. */
479
480 fleftmost = Front_leftmostdesc [frontid] ;
481 row1 = Front_new1strow [fleftmost] ;
482 row2 = Front_1strow [frontid+1] - 1 ;
483 DEBUG1 (("Leftmost: "ID" Rows ["ID" to "ID"] srch ["ID" to "ID"]\n",
484 fleftmost, Front_1strow [frontid], row2, row1, row2)) ;
485
486 /* look in the range row1 ... row2 */
487 for (row = row1 ; row <= row2 ; row++)
488 {
489 DEBUG3 ((" Row: "ID"\n", row)) ;
490 if (NON_PIVOTAL_ROW (row))
491 {
492 /* found it */
493 DEBUG3 ((" Row: "ID" found\n", row)) ;
494 ASSERT (Frpos [row] == EMPTY) ;
495 pivrow [OUT] = row ;
496 DEBUGm4 (("got row merge pivrow %d\n", pivrow [OUT])) ;
497 break ;
498 }
499 }
500 Front_new1strow [fleftmost] = row ;
501
502 if (pivrow [OUT] == EMPTY)
503 {
504 /* not found, look in empty row set in "dummy" front */
505 row1 = Front_new1strow [nfr] ;
506 row2 = n_row-1 ;
507 DEBUG3 (("Empty: "ID" Rows ["ID" to "ID"] srch["ID" to "ID"]\n",
508 nfr, Front_1strow [nfr], row2, row1, row2)) ;
509
510 /* look in the range row1 ... row2 */
511 for (row = row1 ; row <= row2 ; row++)
512 {
513 DEBUG3 ((" Empty Row: "ID"\n", row)) ;
514 if (NON_PIVOTAL_ROW (row))
515 {
516 /* found it */
517 DEBUG3 ((" Empty Row: "ID" found\n", row)) ;
518 ASSERT (Frpos [row] == EMPTY) ;
519 pivrow [OUT] = row ;
520 DEBUGm4 (("got dummy row pivrow %d\n", pivrow [OUT])) ;
521 break ;
522 }
523 }
524 Front_new1strow [nfr] = row ;
525 }
526
527 if (pivrow [OUT] == EMPTY)
528 {
529 /* Row-merge set is empty. We can just discard */
530 /* the candidate pivot column. */
531 DEBUG0 (("Note: row-merge set empty\n")) ;
532 DEBUGm4 (("got no pivrow \n")) ;
533 return (UMFPACK_WARNING_singular_matrix) ;
534 }
535 }
536 }
537
538 /* ---------------------------------------------------------------------- */
539 /* construct the candidate row in the front, if any */
540 /* ---------------------------------------------------------------------- */
541
542 #ifndef NDEBUG
543 /* check Wrp */
544 ASSERT (Work->Wrpflag > 0) ;
545 if (UMF_debug > 0 || Work->n_col < 1000)
546 {
547 for (i = 0 ; i < Work->n_col ; i++)
548 {
549 ASSERT (Wrp [i] < Work->Wrpflag) ;
550 }
551 }
552 #endif
553
554 #ifndef NDEBUG
555 DEBUG4 (("pivrow [IN]: "ID"\n", pivrow [IN])) ;
556 UMF_dump_rowcol (0, Numeric, Work, pivrow [IN], TRUE) ;
557 #endif
558
559 if (pivrow [IN] != EMPTY)
560 {
561
562 /* the row merge candidate row is not pivrow [IN] */
563 freebie [IN] = (pivrow [IN] == prior_pivrow [IN]) && (cdeg1 > 0) ;
564 ASSERT (cdeg1 >= 0) ;
565
566 if (!freebie [IN])
567 {
568 /* include current front in the degree of this row */
569
570 Fcpos = Work->Fcpos ;
571 fncols = Work->fncols ;
572
573 Wrpflag = Work->Wrpflag ;
574
575 /* -------------------------------------------------------------- */
576 /* construct the pattern of the IN row */
577 /* -------------------------------------------------------------- */
578
579 #ifndef NDEBUG
580 /* check Fcols */
581 DEBUG5 (("ROW ASSEMBLE: rdeg "ID"\nREDUCE ROW "ID"\n",
582 fncols, pivrow [IN])) ;
583 for (j = 0 ; j < fncols ; j++)
584 {
585 col = Work->Fcols [j] ;
586 ASSERT (col >= 0 && col < Work->n_col) ;
587 ASSERT (Fcpos [col] >= 0) ;
588 }
589 if (UMF_debug > 0 || Work->n_col < 1000)
590 {
591 Int cnt = fncols ;
592 for (col = 0 ; col < Work->n_col ; col++)
593 {
594 if (Fcpos [col] < 0) cnt++ ;
595 }
596 ASSERT (cnt == Work->n_col) ;
597 }
598 #endif
599
600 rdeg [IN] = fncols ;
601
602 ASSERT (pivrow [IN] >= 0 && pivrow [IN] < Work->n_row) ;
603 ASSERT (NON_PIVOTAL_ROW (pivrow [IN])) ;
604
605 /* add the pivot column itself */
606 ASSERT (Wrp [pivcol] != Wrpflag) ;
607 if (Fcpos [pivcol] < 0)
608 {
609 DEBUG3 (("Adding pivot col to pivrow [IN] pattern\n")) ;
610 if (rdeg [IN] >= max_rdeg)
611 {
612 /* :: pattern change (in) :: */
613 return (UMFPACK_ERROR_different_pattern) ;
614 }
615 Wrp [pivcol] = Wrpflag ;
616 W_i [rdeg [IN]++] = pivcol ;
617 }
618
619 tpi = Row_tuples [pivrow [IN]] ;
620 if (tpi)
621 {
622 tp = (Tuple *) (Memory + tpi) ;
623 tp1 = tp ;
624 tp2 = tp ;
625 tpend = tp + Row_tlen [pivrow [IN]] ;
626 for ( ; tp < tpend ; tp++)
627 {
628 e = tp->e ;
629 ASSERT (e > 0 && e <= Work->nel) ;
630 if (!E [e])
631 {
632 continue ; /* element already deallocated */
633 }
634 f = tp->f ;
635 p = Memory + E [e] ;
636 ep = (Element *) p ;
637 p += UNITS (Element, 1) ;
638 Cols = (Int *) p ;
639 ncols = ep->ncols ;
640 Rows = Cols + ncols ;
641 if (Rows [f] == EMPTY)
642 {
643 continue ; /* row already assembled */
644 }
645 ASSERT (pivrow [IN] == Rows [f]) ;
646
647 for (j = 0 ; j < ncols ; j++)
648 {
649 col = Cols [j] ;
650 ASSERT (col >= EMPTY && col < Work->n_col) ;
651 if ((col >= 0) && (Wrp [col] != Wrpflag)
652 && Fcpos [col] <0)
653 {
654 ASSERT (NON_PIVOTAL_COL (col)) ;
655 if (rdeg [IN] >= max_rdeg)
656 {
657 /* :: pattern change (rdeg in failure) :: */
658 DEBUGm4 (("rdeg [IN] >= max_rdeg failure\n")) ;
659 return (UMFPACK_ERROR_different_pattern) ;
660 }
661 Wrp [col] = Wrpflag ;
662 W_i [rdeg [IN]++] = col ;
663 }
664 }
665
666 *tp2++ = *tp ; /* leave the tuple in the list */
667 }
668 Row_tlen [pivrow [IN]] = tp2 - tp1 ;
669 }
670
671 #ifndef NDEBUG
672 DEBUG4 (("Reduced IN row:\n")) ;
673 for (j = 0 ; j < fncols ; j++)
674 {
675 DEBUG6 ((" "ID" "ID" "ID"\n",
676 j, Work->Fcols [j], Fcpos [Work->Fcols [j]])) ;
677 ASSERT (Fcpos [Work->Fcols [j]] >= 0) ;
678 }
679 for (j = fncols ; j < rdeg [IN] ; j++)
680 {
681 DEBUG6 ((" "ID" "ID" "ID"\n", j, W_i [j], Wrp [W_i [j]]));
682 ASSERT (W_i [j] >= 0 && W_i [j] < Work->n_col) ;
683 ASSERT (Wrp [W_i [j]] == Wrpflag) ;
684 }
685 /* mark the end of the pattern in case we scan it by mistake */
686 /* Note that this means W_i must be of size >= fncols_max + 1 */
687 W_i [rdeg [IN]] = EMPTY ;
688 #endif
689
690 /* rdeg [IN] is now the exact degree of the IN row */
691
692 /* clear Work->Wrp. */
693 Work->Wrpflag++ ;
694 /* All Wrp [0..n_col] is now < Wrpflag */
695 }
696 }
697
698 #ifndef NDEBUG
699 /* check Wrp */
700 if (UMF_debug > 0 || Work->n_col < 1000)
701 {
702 for (i = 0 ; i < Work->n_col ; i++)
703 {
704 ASSERT (Wrp [i] < Work->Wrpflag) ;
705 }
706 }
707 #endif
708
709 /* ---------------------------------------------------------------------- */
710 /* construct the candidate row not in the front, if any */
711 /* ---------------------------------------------------------------------- */
712
713 #ifndef NDEBUG
714 DEBUG4 (("pivrow [OUT]: "ID"\n", pivrow [OUT])) ;
715 UMF_dump_rowcol (0, Numeric, Work, pivrow [OUT], TRUE) ;
716 #endif
717
718 /* If this is a candidate row from the row merge set, force it to be */
719 /* scanned (ignore prior_pivrow [OUT]). */
720
721 if (pivrow [OUT] != EMPTY)
722 {
723 freebie [OUT] = (pivrow [OUT] == prior_pivrow [OUT]) && cdeg1 > 0 ;
724 ASSERT (cdeg1 >= 0) ;
725
726 if (!freebie [OUT])
727 {
728
729 Wrpflag = Work->Wrpflag ;
730
731 /* -------------------------------------------------------------- */
732 /* construct the pattern of the row */
733 /* -------------------------------------------------------------- */
734
735 rdeg [OUT] = 0 ;
736
737 ASSERT (pivrow [OUT] >= 0 && pivrow [OUT] < Work->n_row) ;
738 ASSERT (NON_PIVOTAL_ROW (pivrow [OUT])) ;
739
740 /* add the pivot column itself */
741 ASSERT (Wrp [pivcol] != Wrpflag) ;
742 DEBUG3 (("Adding pivot col to pivrow [OUT] pattern\n")) ;
743 if (rdeg [OUT] >= max_rdeg)
744 {
745 /* :: pattern change (out) :: */
746 return (UMFPACK_ERROR_different_pattern) ;
747 }
748 Wrp [pivcol] = Wrpflag ;
749 W_o [rdeg [OUT]++] = pivcol ;
750
751 tpi = Row_tuples [pivrow [OUT]] ;
752 if (tpi)
753 {
754 tp = (Tuple *) (Memory + tpi) ;
755 tp1 = tp ;
756 tp2 = tp ;
757 tpend = tp + Row_tlen [pivrow [OUT]] ;
758 for ( ; tp < tpend ; tp++)
759 {
760 e = tp->e ;
761 ASSERT (e > 0 && e <= Work->nel) ;
762 if (!E [e])
763 {
764 continue ; /* element already deallocated */
765 }
766 f = tp->f ;
767 p = Memory + E [e] ;
768 ep = (Element *) p ;
769 p += UNITS (Element, 1) ;
770 Cols = (Int *) p ;
771 ncols = ep->ncols ;
772 Rows = Cols + ncols ;
773 if (Rows [f] == EMPTY)
774 {
775 continue ; /* row already assembled */
776 }
777 ASSERT (pivrow [OUT] == Rows [f]) ;
778
779 for (j = 0 ; j < ncols ; j++)
780 {
781 col = Cols [j] ;
782 ASSERT (col >= EMPTY && col < Work->n_col) ;
783 if ((col >= 0) && (Wrp [col] != Wrpflag))
784 {
785 ASSERT (NON_PIVOTAL_COL (col)) ;
786 if (rdeg [OUT] >= max_rdeg)
787 {
788 /* :: pattern change (rdeg out failure) :: */
789 DEBUGm4 (("rdeg [OUT] failure\n")) ;
790 return (UMFPACK_ERROR_different_pattern) ;
791 }
792 Wrp [col] = Wrpflag ;
793 W_o [rdeg [OUT]++] = col ;
794 }
795 }
796 *tp2++ = *tp ; /* leave the tuple in the list */
797 }
798 Row_tlen [pivrow [OUT]] = tp2 - tp1 ;
799 }
800
801 #ifndef NDEBUG
802 DEBUG4 (("Reduced row OUT:\n")) ;
803 for (j = 0 ; j < rdeg [OUT] ; j++)
804 {
805 DEBUG6 ((" "ID" "ID" "ID"\n", j, W_o [j], Wrp [W_o [j]])) ;
806 ASSERT (W_o [j] >= 0 && W_o [j] < Work->n_col) ;
807 ASSERT (Wrp [W_o [j]] == Wrpflag) ;
808 }
809 /* mark the end of the pattern in case we scan it by mistake */
810 /* Note that this means W_o must be of size >= fncols_max + 1 */
811 W_o [rdeg [OUT]] = EMPTY ;
812 #endif
813
814 /* rdeg [OUT] is now the exact degree of the row */
815
816 /* clear Work->Wrp. */
817 Work->Wrpflag++ ;
818 /* All Wrp [0..n] is now < Wrpflag */
819
820 }
821
822 }
823 DEBUG5 (("Row_search end ] \n")) ;
824
825 #ifndef NDEBUG
826 /* check Wrp */
827 if (UMF_debug > 0 || Work->n_col < 1000)
828 {
829 for (i = 0 ; i < Work->n_col ; i++)
830 {
831 ASSERT (Wrp [i] < Work->Wrpflag) ;
832 }
833 }
834 #endif
835
836 return (UMFPACK_OK) ;
837 }
838