1 /* ========================================================================== */
2 /* === Cholesky/cholmod_postorder =========================================== */
3 /* ========================================================================== */
4 
5 /* -----------------------------------------------------------------------------
6  * CHOLMOD/Cholesky Module.  Copyright (C) 2005-2006, Timothy A. Davis
7  * -------------------------------------------------------------------------- */
8 
9 /* Compute the postorder of a tree. */
10 
11 #ifndef NCHOLESKY
12 
13 #include "cholmod_internal.h"
14 #include "cholmod_cholesky.h"
15 
16 
17 /* ========================================================================== */
18 /* === dfs ================================================================== */
19 /* ========================================================================== */
20 
21 /* The code below includes both a recursive and non-recursive depth-first-search
22  * of a tree.  The recursive code is simpler, but can lead to stack overflow.
23  * It is left here for reference, to understand what the non-recursive code
24  * is computing.  To try the recursive version, uncomment the following
25  * #define, or compile the code with -DRECURSIVE.  Be aware that stack
26  * overflow may occur.
27 #define RECURSIVE
28  */
29 
30 #ifdef RECURSIVE
31 
32 /* recursive version: a working code for reference only, not actual use */
33 
dfs(Int p,Int k,Int Post[],Int Head[],Int Next[],Int Pstack[])34 static Int dfs			/* return the new value of k */
35 (
36     Int p,		/* start a DFS at node p */
37     Int k,		/* start the node numbering at k */
38     Int Post [ ],	/* Post ordering, modified on output */
39     Int Head [ ],	/* Head [p] = youngest child of p; EMPTY on output */
40     Int Next [ ],	/* Next [j] = sibling of j; unmodified */
41     Int Pstack [ ]	/* unused */
42 )
43 {
44     Int j ;
45     /* start a DFS at each child of node p */
46     for (j = Head [p] ; j != EMPTY ; j = Next [j])
47     {
48 	/* start a DFS at child node j */
49 	k = dfs (j, k, Post, Head, Next, Pstack) ;
50     }
51     Post [k++] = p ;	/* order node p as the kth node */
52     Head [p] = EMPTY ;	/* link list p no longer needed */
53     return (k) ;	/* the next node will be numbered k */
54 }
55 
56 #else
57 
58 /* non-recursive version for actual use */
59 
dfs(Int p,Int k,Int Post[],Int Head[],Int Next[],Int Pstack[])60 static Int dfs		/* return the new value of k */
61 (
62     Int p,		/* start the DFS at a root node p */
63     Int k,		/* start the node numbering at k */
64     Int Post [ ],	/* Post ordering, modified on output */
65     Int Head [ ],	/* Head [p] = youngest child of p; EMPTY on output */
66     Int Next [ ],	/* Next [j] = sibling of j; unmodified */
67     Int Pstack [ ]	/* workspace of size n, undefined on input or output */
68 )
69 {
70     Int j, phead ;
71 
72     /* put the root node on the stack */
73     Pstack [0] = p ;
74     phead = 0 ;
75 
76     /* while the stack is not empty, do: */
77     while (phead >= 0)
78     {
79 	/* grab the node p from top of the stack and get its youngest child j */
80 	p = Pstack [phead] ;
81 	j = Head [p] ;
82 	if (j == EMPTY)
83 	{
84 	    /* all children of p ordered.  remove p from stack and order it */
85 	    phead-- ;
86 	    Post [k++] = p ;	/* order node p as the kth node */
87 	}
88 	else
89 	{
90 	    /* leave p on the stack.  Start a DFS at child node j by putting
91 	     * j on the stack and removing j from the list of children of p. */
92 	    Head [p] = Next [j] ;
93 	    Pstack [++phead] = j ;
94 	}
95     }
96     return (k) ;	/* the next node will be numbered k */
97 }
98 
99 #endif
100 
101 /* ========================================================================== */
102 /* === cholmod_postorder ==================================================== */
103 /* ========================================================================== */
104 
105 /* Postorder a tree.  The tree is either an elimination tree (the output from
106  * from cholmod_etree) or a component tree (from cholmod_nested_dissection).
107  *
108  * An elimination tree is a complete tree of n nodes with Parent [j] > j or
109  * Parent [j] = EMPTY if j is a root.  On output Post [0..n-1] is a complete
110  * permutation vector.
111  *
112  * A component tree is a subset of 0..n-1.  Parent [j] = -2 if node j is not
113  * in the component tree.  Parent [j] = EMPTY if j is a root of the component
114  * tree, and Parent [j] is in the range 0 to n-1 if j is in the component
115  * tree but not a root.  On output, Post [k] is defined only for nodes in
116  * the component tree.  Post [k] = j if node j is the kth node in the
117  * postordered component tree, where k is in the range 0 to the number of
118  * components minus 1.
119  *
120  * Node j is ignored and not included in the postorder if Parent [j] < EMPTY.
121  *
122  * As a result, check_parent (Parent, n,...) may fail on input, since
123  * cholmod_check_parent assumes Parent is an elimination tree.  Similarly,
124  * cholmod_check_perm (Post, ...) may fail on output, since Post is a partial
125  * permutation if Parent is a component tree.
126  *
127  * An optional node weight can be given.  When starting a postorder at node j,
128  * the children of j are ordered in increasing order of their weight.
129  * If no weights are given (Weight is NULL) then children are ordered in
130  * increasing order of their node number.  The weight of a node must be in the
131  * range 0 to n-1.  Weights outside that range are silently converted to that
132  * range (weights < 0 are treated as zero, and weights >= n are treated as n-1).
133  *
134  *
135  * workspace: Head (n), Iwork (2*n)
136  */
137 
CHOLMOD(postorder)138 SuiteSparse_long CHOLMOD(postorder)	/* return # of nodes postordered */
139 (
140     /* ---- input ---- */
141     Int *Parent,	/* size n. Parent [j] = p if p is the parent of j */
142     size_t n,
143     Int *Weight,	/* size n, optional. Weight [j] is weight of node j */
144     /* ---- output --- */
145     Int *Post,		/* size n. Post [k] = j is kth in postordered tree */
146     /* --------------- */
147     cholmod_common *Common
148 )
149 {
150     Int *Head, *Next, *Pstack, *Iwork ;
151     Int j, p, k, w, nextj ;
152     size_t s ;
153     int ok = TRUE ;
154 
155     /* ---------------------------------------------------------------------- */
156     /* check inputs */
157     /* ---------------------------------------------------------------------- */
158 
159     RETURN_IF_NULL_COMMON (EMPTY) ;
160     RETURN_IF_NULL (Parent, EMPTY) ;
161     RETURN_IF_NULL (Post, EMPTY) ;
162     Common->status = CHOLMOD_OK ;
163 
164     /* ---------------------------------------------------------------------- */
165     /* allocate workspace */
166     /* ---------------------------------------------------------------------- */
167 
168     /* s = 2*n */
169     s = CHOLMOD(mult_size_t) (n, 2, &ok) ;
170     if (!ok)
171     {
172 	ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
173 	return (EMPTY) ;
174     }
175 
176     CHOLMOD(allocate_work) (n, s, 0, Common) ;
177     if (Common->status < CHOLMOD_OK)
178     {
179 	return (EMPTY) ;
180     }
181     ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
182 
183     /* ---------------------------------------------------------------------- */
184     /* get inputs */
185     /* ---------------------------------------------------------------------- */
186 
187     Head  = Common->Head ;	/* size n+1, initially all EMPTY */
188     Iwork = Common->Iwork ;
189     Next  = Iwork ;		/* size n (i/i/l) */
190     Pstack = Iwork + n ;	/* size n (i/i/l) */
191 
192     /* ---------------------------------------------------------------------- */
193     /* construct a link list of children for each node */
194     /* ---------------------------------------------------------------------- */
195 
196     if (Weight == NULL)
197     {
198 
199 	/* in reverse order so children are in ascending order in each list */
200 	for (j = n-1 ; j >= 0 ; j--)
201 	{
202 	    p = Parent [j] ;
203 	    if (p >= 0 && p < ((Int) n))
204 	    {
205 		/* add j to the list of children for node p */
206 		Next [j] = Head [p] ;
207 		Head [p] = j ;
208 	    }
209 	}
210 
211 	/* Head [p] = j if j is the youngest (least-numbered) child of p */
212 	/* Next [j1] = j2 if j2 is the next-oldest sibling of j1 */
213 
214     }
215     else
216     {
217 
218 	/* First, construct a set of link lists according to Weight.
219 	 *
220 	 * Whead [w] = j if node j is the first node in bucket w.
221 	 * Next [j1] = j2 if node j2 follows j1 in a link list.
222 	 */
223 
224 	Int *Whead = Pstack ;	    /* use Pstack as workspace for Whead [ */
225 
226 	for (w = 0 ; w < ((Int) n) ; w++)
227 	{
228 	    Whead [w] = EMPTY ;
229 	}
230 	/* do in forward order, so nodes that ties are ordered by node index */
231 	for (j = 0 ; j < ((Int) n) ; j++)
232 	{
233 	    p = Parent [j] ;
234 	    if (p >= 0 && p < ((Int) n))
235 	    {
236 		w = Weight [j] ;
237 		w = MAX (0, w) ;
238 		w = MIN (w, ((Int) n) - 1) ;
239 		/* place node j at the head of link list for weight w */
240 		Next [j] = Whead [w] ;
241 		Whead [w] = j ;
242 	    }
243 	}
244 
245 	/* traverse weight buckets, placing each node in its parent's list */
246 	for (w = n-1 ; w >= 0 ; w--)
247 	{
248 	    for (j = Whead [w] ; j != EMPTY ; j = nextj)
249 	    {
250 		nextj = Next [j] ;
251 		/* put node j in the link list of its parent */
252 		p = Parent [j] ;
253 		ASSERT (p >= 0 && p < ((Int) n)) ;
254 		Next [j] = Head [p] ;
255 		Head [p] = j ;
256 	    }
257 	}
258 
259 	/* Whead no longer needed ] */
260 	/* Head [p] = j if j is the lightest child of p */
261 	/* Next [j1] = j2 if j2 is the next-heaviest sibling of j1 */
262     }
263 
264     /* ---------------------------------------------------------------------- */
265     /* start a DFS at each root node of the etree */
266     /* ---------------------------------------------------------------------- */
267 
268     k = 0 ;
269     for (j = 0 ; j < ((Int) n) ; j++)
270     {
271 	if (Parent [j] == EMPTY)
272 	{
273 	    /* j is the root of a tree; start a DFS here */
274 	    k = dfs (j, k, Post, Head, Next, Pstack) ;
275 	}
276     }
277 
278     /* this would normally be EMPTY already, unless Parent is invalid */
279     for (j = 0 ; j < ((Int) n) ; j++)
280     {
281 	Head [j] = EMPTY ;
282     }
283 
284     PRINT1 (("postordered "ID" nodes\n", k)) ;
285     ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
286     return (k) ;
287 }
288 #endif
289