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