Eigen  3.2.8
Amd.h
00001 // This file is part of Eigen, a lightweight C++ template library
00002 // for linear algebra.
00003 //
00004 // Copyright (C) 2010 Gael Guennebaud <gael.guennebaud@inria.fr>
00005 
00006 /*
00007 
00008 NOTE: this routine has been adapted from the CSparse library:
00009 
00010 Copyright (c) 2006, Timothy A. Davis.
00011 http://www.suitesparse.com
00012 
00013 CSparse is free software; you can redistribute it and/or
00014 modify it under the terms of the GNU Lesser General Public
00015 License as published by the Free Software Foundation; either
00016 version 2.1 of the License, or (at your option) any later version.
00017 
00018 CSparse is distributed in the hope that it will be useful,
00019 but WITHOUT ANY WARRANTY; without even the implied warranty of
00020 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00021 Lesser General Public License for more details.
00022 
00023 You should have received a copy of the GNU Lesser General Public
00024 License along with this Module; if not, write to the Free Software
00025 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00026 
00027 */
00028 
00029 #include "../Core/util/NonMPL2.h"
00030 
00031 #ifndef EIGEN_SPARSE_AMD_H
00032 #define EIGEN_SPARSE_AMD_H
00033 
00034 namespace Eigen { 
00035 
00036 namespace internal {
00037   
00038 template<typename T> inline T amd_flip(const T& i) { return -i-2; }
00039 template<typename T> inline T amd_unflip(const T& i) { return i<0 ? amd_flip(i) : i; }
00040 template<typename T0, typename T1> inline bool amd_marked(const T0* w, const T1& j) { return w[j]<0; }
00041 template<typename T0, typename T1> inline void amd_mark(const T0* w, const T1& j) { return w[j] = amd_flip(w[j]); }
00042 
00043 /* clear w */
00044 template<typename Index>
00045 static int cs_wclear (Index mark, Index lemax, Index *w, Index n)
00046 {
00047   Index k;
00048   if(mark < 2 || (mark + lemax < 0))
00049   {
00050     for(k = 0; k < n; k++)
00051       if(w[k] != 0)
00052         w[k] = 1;
00053     mark = 2;
00054   }
00055   return (mark);     /* at this point, w[0..n-1] < mark holds */
00056 }
00057 
00058 /* depth-first search and postorder of a tree rooted at node j */
00059 template<typename Index>
00060 Index cs_tdfs(Index j, Index k, Index *head, const Index *next, Index *post, Index *stack)
00061 {
00062   int i, p, top = 0;
00063   if(!head || !next || !post || !stack) return (-1);    /* check inputs */
00064   stack[0] = j;                 /* place j on the stack */
00065   while (top >= 0)                /* while (stack is not empty) */
00066   {
00067     p = stack[top];           /* p = top of stack */
00068     i = head[p];              /* i = youngest child of p */
00069     if(i == -1)
00070     {
00071       top--;                 /* p has no unordered children left */
00072       post[k++] = p;        /* node p is the kth postordered node */
00073     }
00074     else
00075     {
00076       head[p] = next[i];   /* remove i from children of p */
00077       stack[++top] = i;     /* start dfs on child node i */
00078     }
00079   }
00080   return k;
00081 }
00082 
00083 
00090 template<typename Scalar, typename Index>
00091 void minimum_degree_ordering(SparseMatrix<Scalar,ColMajor,Index>& C, PermutationMatrix<Dynamic,Dynamic,Index>& perm)
00092 {
00093   using std::sqrt;
00094   
00095   int d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1,
00096       k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi, nvj, nvk, mark, wnvi,
00097       ok, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, t;
00098   unsigned int h;
00099   
00100   Index n = C.cols();
00101   dense = std::max<Index> (16, Index(10 * sqrt(double(n))));   /* find dense threshold */
00102   dense = std::min<Index> (n-2, dense);
00103   
00104   Index cnz = C.nonZeros();
00105   perm.resize(n+1);
00106   t = cnz + cnz/5 + 2*n;                 /* add elbow room to C */
00107   C.resizeNonZeros(t);
00108   
00109   Index* W       = new Index[8*(n+1)]; /* get workspace */
00110   Index* len     = W;
00111   Index* nv      = W +   (n+1);
00112   Index* next    = W + 2*(n+1);
00113   Index* head    = W + 3*(n+1);
00114   Index* elen    = W + 4*(n+1);
00115   Index* degree  = W + 5*(n+1);
00116   Index* w       = W + 6*(n+1);
00117   Index* hhead   = W + 7*(n+1);
00118   Index* last    = perm.indices().data();                              /* use P as workspace for last */
00119   
00120   /* --- Initialize quotient graph ---------------------------------------- */
00121   Index* Cp = C.outerIndexPtr();
00122   Index* Ci = C.innerIndexPtr();
00123   for(k = 0; k < n; k++)
00124     len[k] = Cp[k+1] - Cp[k];
00125   len[n] = 0;
00126   nzmax = t;
00127   
00128   for(i = 0; i <= n; i++)
00129   {
00130     head[i]   = -1;                     // degree list i is empty
00131     last[i]   = -1;
00132     next[i]   = -1;
00133     hhead[i]  = -1;                     // hash list i is empty 
00134     nv[i]     = 1;                      // node i is just one node
00135     w[i]      = 1;                      // node i is alive
00136     elen[i]   = 0;                      // Ek of node i is empty
00137     degree[i] = len[i];                 // degree of node i
00138   }
00139   mark = internal::cs_wclear<Index>(0, 0, w, n);         /* clear w */
00140   
00141   /* --- Initialize degree lists ------------------------------------------ */
00142   for(i = 0; i < n; i++)
00143   {
00144     bool has_diag = false;
00145     for(p = Cp[i]; p<Cp[i+1]; ++p)
00146       if(Ci[p]==i)
00147       {
00148         has_diag = true;
00149         break;
00150       }
00151    
00152     d = degree[i];
00153     if(d == 1 && has_diag)           /* node i is empty */
00154     {
00155       elen[i] = -2;                 /* element i is dead */
00156       nel++;
00157       Cp[i] = -1;                   /* i is a root of assembly tree */
00158       w[i] = 0;
00159     }
00160     else if(d > dense || !has_diag)  /* node i is dense or has no structural diagonal element */
00161     {
00162       nv[i] = 0;                    /* absorb i into element n */
00163       elen[i] = -1;                 /* node i is dead */
00164       nel++;
00165       Cp[i] = amd_flip (n);
00166       nv[n]++;
00167     }
00168     else
00169     {
00170       if(head[d] != -1) last[head[d]] = i;
00171       next[i] = head[d];           /* put node i in degree list d */
00172       head[d] = i;
00173     }
00174   }
00175   
00176   elen[n] = -2;                         /* n is a dead element */
00177   Cp[n] = -1;                           /* n is a root of assembly tree */
00178   w[n] = 0;                             /* n is a dead element */
00179   
00180   while (nel < n)                         /* while (selecting pivots) do */
00181   {
00182     /* --- Select node of minimum approximate degree -------------------- */
00183     for(k = -1; mindeg < n && (k = head[mindeg]) == -1; mindeg++) {}
00184     if(next[k] != -1) last[next[k]] = -1;
00185     head[mindeg] = next[k];          /* remove k from degree list */
00186     elenk = elen[k];                  /* elenk = |Ek| */
00187     nvk = nv[k];                      /* # of nodes k represents */
00188     nel += nvk;                        /* nv[k] nodes of A eliminated */
00189     
00190     /* --- Garbage collection ------------------------------------------- */
00191     if(elenk > 0 && cnz + mindeg >= nzmax)
00192     {
00193       for(j = 0; j < n; j++)
00194       {
00195         if((p = Cp[j]) >= 0)      /* j is a live node or element */
00196         {
00197           Cp[j] = Ci[p];          /* save first entry of object */
00198           Ci[p] = amd_flip (j);    /* first entry is now amd_flip(j) */
00199         }
00200       }
00201       for(q = 0, p = 0; p < cnz; ) /* scan all of memory */
00202       {
00203         if((j = amd_flip (Ci[p++])) >= 0)  /* found object j */
00204         {
00205           Ci[q] = Cp[j];       /* restore first entry of object */
00206           Cp[j] = q++;          /* new pointer to object j */
00207           for(k3 = 0; k3 < len[j]-1; k3++) Ci[q++] = Ci[p++];
00208         }
00209       }
00210       cnz = q;                       /* Ci[cnz...nzmax-1] now free */
00211     }
00212     
00213     /* --- Construct new element ---------------------------------------- */
00214     dk = 0;
00215     nv[k] = -nvk;                     /* flag k as in Lk */
00216     p = Cp[k];
00217     pk1 = (elenk == 0) ? p : cnz;      /* do in place if elen[k] == 0 */
00218     pk2 = pk1;
00219     for(k1 = 1; k1 <= elenk + 1; k1++)
00220     {
00221       if(k1 > elenk)
00222       {
00223         e = k;                     /* search the nodes in k */
00224         pj = p;                    /* list of nodes starts at Ci[pj]*/
00225         ln = len[k] - elenk;      /* length of list of nodes in k */
00226       }
00227       else
00228       {
00229         e = Ci[p++];              /* search the nodes in e */
00230         pj = Cp[e];
00231         ln = len[e];              /* length of list of nodes in e */
00232       }
00233       for(k2 = 1; k2 <= ln; k2++)
00234       {
00235         i = Ci[pj++];
00236         if((nvi = nv[i]) <= 0) continue; /* node i dead, or seen */
00237         dk += nvi;                 /* degree[Lk] += size of node i */
00238         nv[i] = -nvi;             /* negate nv[i] to denote i in Lk*/
00239         Ci[pk2++] = i;            /* place i in Lk */
00240         if(next[i] != -1) last[next[i]] = last[i];
00241         if(last[i] != -1)         /* remove i from degree list */
00242         {
00243           next[last[i]] = next[i];
00244         }
00245         else
00246         {
00247           head[degree[i]] = next[i];
00248         }
00249       }
00250       if(e != k)
00251       {
00252         Cp[e] = amd_flip (k);      /* absorb e into k */
00253         w[e] = 0;                 /* e is now a dead element */
00254       }
00255     }
00256     if(elenk != 0) cnz = pk2;         /* Ci[cnz...nzmax] is free */
00257     degree[k] = dk;                   /* external degree of k - |Lk\i| */
00258     Cp[k] = pk1;                      /* element k is in Ci[pk1..pk2-1] */
00259     len[k] = pk2 - pk1;
00260     elen[k] = -2;                     /* k is now an element */
00261     
00262     /* --- Find set differences ----------------------------------------- */
00263     mark = internal::cs_wclear<Index>(mark, lemax, w, n);  /* clear w if necessary */
00264     for(pk = pk1; pk < pk2; pk++)    /* scan 1: find |Le\Lk| */
00265     {
00266       i = Ci[pk];
00267       if((eln = elen[i]) <= 0) continue;/* skip if elen[i] empty */
00268       nvi = -nv[i];                      /* nv[i] was negated */
00269       wnvi = mark - nvi;
00270       for(p = Cp[i]; p <= Cp[i] + eln - 1; p++)  /* scan Ei */
00271       {
00272         e = Ci[p];
00273         if(w[e] >= mark)
00274         {
00275           w[e] -= nvi;          /* decrement |Le\Lk| */
00276         }
00277         else if(w[e] != 0)        /* ensure e is a live element */
00278         {
00279           w[e] = degree[e] + wnvi; /* 1st time e seen in scan 1 */
00280         }
00281       }
00282     }
00283     
00284     /* --- Degree update ------------------------------------------------ */
00285     for(pk = pk1; pk < pk2; pk++)    /* scan2: degree update */
00286     {
00287       i = Ci[pk];                   /* consider node i in Lk */
00288       p1 = Cp[i];
00289       p2 = p1 + elen[i] - 1;
00290       pn = p1;
00291       for(h = 0, d = 0, p = p1; p <= p2; p++)    /* scan Ei */
00292       {
00293         e = Ci[p];
00294         if(w[e] != 0)             /* e is an unabsorbed element */
00295         {
00296           dext = w[e] - mark;   /* dext = |Le\Lk| */
00297           if(dext > 0)
00298           {
00299             d += dext;         /* sum up the set differences */
00300             Ci[pn++] = e;     /* keep e in Ei */
00301             h += e;            /* compute the hash of node i */
00302           }
00303           else
00304           {
00305             Cp[e] = amd_flip (k);  /* aggressive absorb. e->k */
00306             w[e] = 0;             /* e is a dead element */
00307           }
00308         }
00309       }
00310       elen[i] = pn - p1 + 1;        /* elen[i] = |Ei| */
00311       p3 = pn;
00312       p4 = p1 + len[i];
00313       for(p = p2 + 1; p < p4; p++) /* prune edges in Ai */
00314       {
00315         j = Ci[p];
00316         if((nvj = nv[j]) <= 0) continue; /* node j dead or in Lk */
00317         d += nvj;                  /* degree(i) += |j| */
00318         Ci[pn++] = j;             /* place j in node list of i */
00319         h += j;                    /* compute hash for node i */
00320       }
00321       if(d == 0)                     /* check for mass elimination */
00322       {
00323         Cp[i] = amd_flip (k);      /* absorb i into k */
00324         nvi = -nv[i];
00325         dk -= nvi;                 /* |Lk| -= |i| */
00326         nvk += nvi;                /* |k| += nv[i] */
00327         nel += nvi;
00328         nv[i] = 0;
00329         elen[i] = -1;             /* node i is dead */
00330       }
00331       else
00332       {
00333         degree[i] = std::min<Index> (degree[i], d);   /* update degree(i) */
00334         Ci[pn] = Ci[p3];         /* move first node to end */
00335         Ci[p3] = Ci[p1];         /* move 1st el. to end of Ei */
00336         Ci[p1] = k;               /* add k as 1st element in of Ei */
00337         len[i] = pn - p1 + 1;     /* new len of adj. list of node i */
00338         h %= n;                    /* finalize hash of i */
00339         next[i] = hhead[h];      /* place i in hash bucket */
00340         hhead[h] = i;
00341         last[i] = h;              /* save hash of i in last[i] */
00342       }
00343     }                                   /* scan2 is done */
00344     degree[k] = dk;                   /* finalize |Lk| */
00345     lemax = std::max<Index>(lemax, dk);
00346     mark = internal::cs_wclear<Index>(mark+lemax, lemax, w, n);    /* clear w */
00347     
00348     /* --- Supernode detection ------------------------------------------ */
00349     for(pk = pk1; pk < pk2; pk++)
00350     {
00351       i = Ci[pk];
00352       if(nv[i] >= 0) continue;         /* skip if i is dead */
00353       h = last[i];                      /* scan hash bucket of node i */
00354       i = hhead[h];
00355       hhead[h] = -1;                    /* hash bucket will be empty */
00356       for(; i != -1 && next[i] != -1; i = next[i], mark++)
00357       {
00358         ln = len[i];
00359         eln = elen[i];
00360         for(p = Cp[i]+1; p <= Cp[i] + ln-1; p++) w[Ci[p]] = mark;
00361         jlast = i;
00362         for(j = next[i]; j != -1; ) /* compare i with all j */
00363         {
00364           ok = (len[j] == ln) && (elen[j] == eln);
00365           for(p = Cp[j] + 1; ok && p <= Cp[j] + ln - 1; p++)
00366           {
00367             if(w[Ci[p]] != mark) ok = 0;    /* compare i and j*/
00368           }
00369           if(ok)                     /* i and j are identical */
00370           {
00371             Cp[j] = amd_flip (i);  /* absorb j into i */
00372             nv[i] += nv[j];
00373             nv[j] = 0;
00374             elen[j] = -1;         /* node j is dead */
00375             j = next[j];          /* delete j from hash bucket */
00376             next[jlast] = j;
00377           }
00378           else
00379           {
00380             jlast = j;             /* j and i are different */
00381             j = next[j];
00382           }
00383         }
00384       }
00385     }
00386     
00387     /* --- Finalize new element------------------------------------------ */
00388     for(p = pk1, pk = pk1; pk < pk2; pk++)   /* finalize Lk */
00389     {
00390       i = Ci[pk];
00391       if((nvi = -nv[i]) <= 0) continue;/* skip if i is dead */
00392       nv[i] = nvi;                      /* restore nv[i] */
00393       d = degree[i] + dk - nvi;         /* compute external degree(i) */
00394       d = std::min<Index> (d, n - nel - nvi);
00395       if(head[d] != -1) last[head[d]] = i;
00396       next[i] = head[d];               /* put i back in degree list */
00397       last[i] = -1;
00398       head[d] = i;
00399       mindeg = std::min<Index> (mindeg, d);       /* find new minimum degree */
00400       degree[i] = d;
00401       Ci[p++] = i;                      /* place i in Lk */
00402     }
00403     nv[k] = nvk;                      /* # nodes absorbed into k */
00404     if((len[k] = p-pk1) == 0)         /* length of adj list of element k*/
00405     {
00406       Cp[k] = -1;                   /* k is a root of the tree */
00407       w[k] = 0;                     /* k is now a dead element */
00408     }
00409     if(elenk != 0) cnz = p;           /* free unused space in Lk */
00410   }
00411   
00412   /* --- Postordering ----------------------------------------------------- */
00413   for(i = 0; i < n; i++) Cp[i] = amd_flip (Cp[i]);/* fix assembly tree */
00414   for(j = 0; j <= n; j++) head[j] = -1;
00415   for(j = n; j >= 0; j--)              /* place unordered nodes in lists */
00416   {
00417     if(nv[j] > 0) continue;          /* skip if j is an element */
00418     next[j] = head[Cp[j]];          /* place j in list of its parent */
00419     head[Cp[j]] = j;
00420   }
00421   for(e = n; e >= 0; e--)              /* place elements in lists */
00422   {
00423     if(nv[e] <= 0) continue;         /* skip unless e is an element */
00424     if(Cp[e] != -1)
00425     {
00426       next[e] = head[Cp[e]];      /* place e in list of its parent */
00427       head[Cp[e]] = e;
00428     }
00429   }
00430   for(k = 0, i = 0; i <= n; i++)       /* postorder the assembly tree */
00431   {
00432     if(Cp[i] == -1) k = internal::cs_tdfs<Index>(i, k, head, next, perm.indices().data(), w);
00433   }
00434   
00435   perm.indices().conservativeResize(n);
00436 
00437   delete[] W;
00438 }
00439 
00440 } // namespace internal
00441 
00442 } // end namespace Eigen
00443 
00444 #endif // EIGEN_SPARSE_AMD_H
 All Classes Functions Variables Typedefs Enumerations Enumerator Friends