Home | History | Annotate | Download | only in OrderingMethods
      1 // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2010 Gael Guennebaud <gael.guennebaud (at) inria.fr>
      5 //
      6 // This Source Code Form is subject to the terms of the Mozilla
      7 // Public License v. 2.0. If a copy of the MPL was not distributed
      8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9 
     10 /*
     11 
     12 NOTE: this routine has been adapted from the CSparse library:
     13 
     14 Copyright (c) 2006, Timothy A. Davis.
     15 http://www.cise.ufl.edu/research/sparse/CSparse
     16 
     17 CSparse is free software; you can redistribute it and/or
     18 modify it under the terms of the GNU Lesser General Public
     19 License as published by the Free Software Foundation; either
     20 version 2.1 of the License, or (at your option) any later version.
     21 
     22 CSparse is distributed in the hope that it will be useful,
     23 but WITHOUT ANY WARRANTY; without even the implied warranty of
     24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     25 Lesser General Public License for more details.
     26 
     27 You should have received a copy of the GNU Lesser General Public
     28 License along with this Module; if not, write to the Free Software
     29 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     30 
     31 */
     32 
     33 #include "../Core/util/NonMPL2.h"
     34 
     35 #ifndef EIGEN_SPARSE_AMD_H
     36 #define EIGEN_SPARSE_AMD_H
     37 
     38 namespace Eigen {
     39 
     40 namespace internal {
     41 
     42 template<typename T> inline T amd_flip(const T& i) { return -i-2; }
     43 template<typename T> inline T amd_unflip(const T& i) { return i<0 ? amd_flip(i) : i; }
     44 template<typename T0, typename T1> inline bool amd_marked(const T0* w, const T1& j) { return w[j]<0; }
     45 template<typename T0, typename T1> inline void amd_mark(const T0* w, const T1& j) { return w[j] = amd_flip(w[j]); }
     46 
     47 /* clear w */
     48 template<typename Index>
     49 static int cs_wclear (Index mark, Index lemax, Index *w, Index n)
     50 {
     51   Index k;
     52   if(mark < 2 || (mark + lemax < 0))
     53   {
     54     for(k = 0; k < n; k++)
     55       if(w[k] != 0)
     56         w[k] = 1;
     57     mark = 2;
     58   }
     59   return (mark);     /* at this point, w[0..n-1] < mark holds */
     60 }
     61 
     62 /* depth-first search and postorder of a tree rooted at node j */
     63 template<typename Index>
     64 Index cs_tdfs(Index j, Index k, Index *head, const Index *next, Index *post, Index *stack)
     65 {
     66   int i, p, top = 0;
     67   if(!head || !next || !post || !stack) return (-1);    /* check inputs */
     68   stack[0] = j;                 /* place j on the stack */
     69   while (top >= 0)                /* while (stack is not empty) */
     70   {
     71     p = stack[top];           /* p = top of stack */
     72     i = head[p];              /* i = youngest child of p */
     73     if(i == -1)
     74     {
     75       top--;                 /* p has no unordered children left */
     76       post[k++] = p;        /* node p is the kth postordered node */
     77     }
     78     else
     79     {
     80       head[p] = next[i];   /* remove i from children of p */
     81       stack[++top] = i;     /* start dfs on child node i */
     82     }
     83   }
     84   return k;
     85 }
     86 
     87 
     88 /** \internal
     89   * Approximate minimum degree ordering algorithm.
     90   * \returns the permutation P reducing the fill-in of the input matrix \a C
     91   * The input matrix \a C must be a selfadjoint compressed column major SparseMatrix object. Both the upper and lower parts have to be stored, but the diagonal entries are optional.
     92   * On exit the values of C are destroyed */
     93 template<typename Scalar, typename Index>
     94 void minimum_degree_ordering(SparseMatrix<Scalar,ColMajor,Index>& C, PermutationMatrix<Dynamic,Dynamic,Index>& perm)
     95 {
     96   using std::sqrt;
     97   typedef SparseMatrix<Scalar,ColMajor,Index> CCS;
     98 
     99   int d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1,
    100       k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi, nvj, nvk, mark, wnvi,
    101       ok, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, t;
    102   unsigned int h;
    103 
    104   Index n = C.cols();
    105   dense = std::max<Index> (16, Index(10 * sqrt(double(n))));   /* find dense threshold */
    106   dense = std::min<Index> (n-2, dense);
    107 
    108   Index cnz = C.nonZeros();
    109   perm.resize(n+1);
    110   t = cnz + cnz/5 + 2*n;                 /* add elbow room to C */
    111   C.resizeNonZeros(t);
    112 
    113   Index* W       = new Index[8*(n+1)]; /* get workspace */
    114   Index* len     = W;
    115   Index* nv      = W +   (n+1);
    116   Index* next    = W + 2*(n+1);
    117   Index* head    = W + 3*(n+1);
    118   Index* elen    = W + 4*(n+1);
    119   Index* degree  = W + 5*(n+1);
    120   Index* w       = W + 6*(n+1);
    121   Index* hhead   = W + 7*(n+1);
    122   Index* last    = perm.indices().data();                              /* use P as workspace for last */
    123 
    124   /* --- Initialize quotient graph ---------------------------------------- */
    125   Index* Cp = C.outerIndexPtr();
    126   Index* Ci = C.innerIndexPtr();
    127   for(k = 0; k < n; k++)
    128     len[k] = Cp[k+1] - Cp[k];
    129   len[n] = 0;
    130   nzmax = t;
    131 
    132   for(i = 0; i <= n; i++)
    133   {
    134     head[i]   = -1;                     // degree list i is empty
    135     last[i]   = -1;
    136     next[i]   = -1;
    137     hhead[i]  = -1;                     // hash list i is empty
    138     nv[i]     = 1;                      // node i is just one node
    139     w[i]      = 1;                      // node i is alive
    140     elen[i]   = 0;                      // Ek of node i is empty
    141     degree[i] = len[i];                 // degree of node i
    142   }
    143   mark = internal::cs_wclear<Index>(0, 0, w, n);         /* clear w */
    144   elen[n] = -2;                         /* n is a dead element */
    145   Cp[n] = -1;                           /* n is a root of assembly tree */
    146   w[n] = 0;                             /* n is a dead element */
    147 
    148   /* --- Initialize degree lists ------------------------------------------ */
    149   for(i = 0; i < n; i++)
    150   {
    151     d = degree[i];
    152     if(d == 0)                         /* node i is empty */
    153     {
    154       elen[i] = -2;                 /* element i is dead */
    155       nel++;
    156       Cp[i] = -1;                   /* i is a root of assembly tree */
    157       w[i] = 0;
    158     }
    159     else if(d > dense)                 /* node i is dense */
    160     {
    161       nv[i] = 0;                    /* absorb i into element n */
    162       elen[i] = -1;                 /* node i is dead */
    163       nel++;
    164       Cp[i] = amd_flip (n);
    165       nv[n]++;
    166     }
    167     else
    168     {
    169       if(head[d] != -1) last[head[d]] = i;
    170       next[i] = head[d];           /* put node i in degree list d */
    171       head[d] = i;
    172     }
    173   }
    174 
    175   while (nel < n)                         /* while (selecting pivots) do */
    176   {
    177     /* --- Select node of minimum approximate degree -------------------- */
    178     for(k = -1; mindeg < n && (k = head[mindeg]) == -1; mindeg++) {}
    179     if(next[k] != -1) last[next[k]] = -1;
    180     head[mindeg] = next[k];          /* remove k from degree list */
    181     elenk = elen[k];                  /* elenk = |Ek| */
    182     nvk = nv[k];                      /* # of nodes k represents */
    183     nel += nvk;                        /* nv[k] nodes of A eliminated */
    184 
    185     /* --- Garbage collection ------------------------------------------- */
    186     if(elenk > 0 && cnz + mindeg >= nzmax)
    187     {
    188       for(j = 0; j < n; j++)
    189       {
    190         if((p = Cp[j]) >= 0)      /* j is a live node or element */
    191         {
    192           Cp[j] = Ci[p];          /* save first entry of object */
    193           Ci[p] = amd_flip (j);    /* first entry is now amd_flip(j) */
    194         }
    195       }
    196       for(q = 0, p = 0; p < cnz; ) /* scan all of memory */
    197       {
    198         if((j = amd_flip (Ci[p++])) >= 0)  /* found object j */
    199         {
    200           Ci[q] = Cp[j];       /* restore first entry of object */
    201           Cp[j] = q++;          /* new pointer to object j */
    202           for(k3 = 0; k3 < len[j]-1; k3++) Ci[q++] = Ci[p++];
    203         }
    204       }
    205       cnz = q;                       /* Ci[cnz...nzmax-1] now free */
    206     }
    207 
    208     /* --- Construct new element ---------------------------------------- */
    209     dk = 0;
    210     nv[k] = -nvk;                     /* flag k as in Lk */
    211     p = Cp[k];
    212     pk1 = (elenk == 0) ? p : cnz;      /* do in place if elen[k] == 0 */
    213     pk2 = pk1;
    214     for(k1 = 1; k1 <= elenk + 1; k1++)
    215     {
    216       if(k1 > elenk)
    217       {
    218         e = k;                     /* search the nodes in k */
    219         pj = p;                    /* list of nodes starts at Ci[pj]*/
    220         ln = len[k] - elenk;      /* length of list of nodes in k */
    221       }
    222       else
    223       {
    224         e = Ci[p++];              /* search the nodes in e */
    225         pj = Cp[e];
    226         ln = len[e];              /* length of list of nodes in e */
    227       }
    228       for(k2 = 1; k2 <= ln; k2++)
    229       {
    230         i = Ci[pj++];
    231         if((nvi = nv[i]) <= 0) continue; /* node i dead, or seen */
    232         dk += nvi;                 /* degree[Lk] += size of node i */
    233         nv[i] = -nvi;             /* negate nv[i] to denote i in Lk*/
    234         Ci[pk2++] = i;            /* place i in Lk */
    235         if(next[i] != -1) last[next[i]] = last[i];
    236         if(last[i] != -1)         /* remove i from degree list */
    237         {
    238           next[last[i]] = next[i];
    239         }
    240         else
    241         {
    242           head[degree[i]] = next[i];
    243         }
    244       }
    245       if(e != k)
    246       {
    247         Cp[e] = amd_flip (k);      /* absorb e into k */
    248         w[e] = 0;                 /* e is now a dead element */
    249       }
    250     }
    251     if(elenk != 0) cnz = pk2;         /* Ci[cnz...nzmax] is free */
    252     degree[k] = dk;                   /* external degree of k - |Lk\i| */
    253     Cp[k] = pk1;                      /* element k is in Ci[pk1..pk2-1] */
    254     len[k] = pk2 - pk1;
    255     elen[k] = -2;                     /* k is now an element */
    256 
    257     /* --- Find set differences ----------------------------------------- */
    258     mark = internal::cs_wclear<Index>(mark, lemax, w, n);  /* clear w if necessary */
    259     for(pk = pk1; pk < pk2; pk++)    /* scan 1: find |Le\Lk| */
    260     {
    261       i = Ci[pk];
    262       if((eln = elen[i]) <= 0) continue;/* skip if elen[i] empty */
    263       nvi = -nv[i];                      /* nv[i] was negated */
    264       wnvi = mark - nvi;
    265       for(p = Cp[i]; p <= Cp[i] + eln - 1; p++)  /* scan Ei */
    266       {
    267         e = Ci[p];
    268         if(w[e] >= mark)
    269         {
    270           w[e] -= nvi;          /* decrement |Le\Lk| */
    271         }
    272         else if(w[e] != 0)        /* ensure e is a live element */
    273         {
    274           w[e] = degree[e] + wnvi; /* 1st time e seen in scan 1 */
    275         }
    276       }
    277     }
    278 
    279     /* --- Degree update ------------------------------------------------ */
    280     for(pk = pk1; pk < pk2; pk++)    /* scan2: degree update */
    281     {
    282       i = Ci[pk];                   /* consider node i in Lk */
    283       p1 = Cp[i];
    284       p2 = p1 + elen[i] - 1;
    285       pn = p1;
    286       for(h = 0, d = 0, p = p1; p <= p2; p++)    /* scan Ei */
    287       {
    288         e = Ci[p];
    289         if(w[e] != 0)             /* e is an unabsorbed element */
    290         {
    291           dext = w[e] - mark;   /* dext = |Le\Lk| */
    292           if(dext > 0)
    293           {
    294             d += dext;         /* sum up the set differences */
    295             Ci[pn++] = e;     /* keep e in Ei */
    296             h += e;            /* compute the hash of node i */
    297           }
    298           else
    299           {
    300             Cp[e] = amd_flip (k);  /* aggressive absorb. e->k */
    301             w[e] = 0;             /* e is a dead element */
    302           }
    303         }
    304       }
    305       elen[i] = pn - p1 + 1;        /* elen[i] = |Ei| */
    306       p3 = pn;
    307       p4 = p1 + len[i];
    308       for(p = p2 + 1; p < p4; p++) /* prune edges in Ai */
    309       {
    310         j = Ci[p];
    311         if((nvj = nv[j]) <= 0) continue; /* node j dead or in Lk */
    312         d += nvj;                  /* degree(i) += |j| */
    313         Ci[pn++] = j;             /* place j in node list of i */
    314         h += j;                    /* compute hash for node i */
    315       }
    316       if(d == 0)                     /* check for mass elimination */
    317       {
    318         Cp[i] = amd_flip (k);      /* absorb i into k */
    319         nvi = -nv[i];
    320         dk -= nvi;                 /* |Lk| -= |i| */
    321         nvk += nvi;                /* |k| += nv[i] */
    322         nel += nvi;
    323         nv[i] = 0;
    324         elen[i] = -1;             /* node i is dead */
    325       }
    326       else
    327       {
    328         degree[i] = std::min<Index> (degree[i], d);   /* update degree(i) */
    329         Ci[pn] = Ci[p3];         /* move first node to end */
    330         Ci[p3] = Ci[p1];         /* move 1st el. to end of Ei */
    331         Ci[p1] = k;               /* add k as 1st element in of Ei */
    332         len[i] = pn - p1 + 1;     /* new len of adj. list of node i */
    333         h %= n;                    /* finalize hash of i */
    334         next[i] = hhead[h];      /* place i in hash bucket */
    335         hhead[h] = i;
    336         last[i] = h;              /* save hash of i in last[i] */
    337       }
    338     }                                   /* scan2 is done */
    339     degree[k] = dk;                   /* finalize |Lk| */
    340     lemax = std::max<Index>(lemax, dk);
    341     mark = internal::cs_wclear<Index>(mark+lemax, lemax, w, n);    /* clear w */
    342 
    343     /* --- Supernode detection ------------------------------------------ */
    344     for(pk = pk1; pk < pk2; pk++)
    345     {
    346       i = Ci[pk];
    347       if(nv[i] >= 0) continue;         /* skip if i is dead */
    348       h = last[i];                      /* scan hash bucket of node i */
    349       i = hhead[h];
    350       hhead[h] = -1;                    /* hash bucket will be empty */
    351       for(; i != -1 && next[i] != -1; i = next[i], mark++)
    352       {
    353         ln = len[i];
    354         eln = elen[i];
    355         for(p = Cp[i]+1; p <= Cp[i] + ln-1; p++) w[Ci[p]] = mark;
    356         jlast = i;
    357         for(j = next[i]; j != -1; ) /* compare i with all j */
    358         {
    359           ok = (len[j] == ln) && (elen[j] == eln);
    360           for(p = Cp[j] + 1; ok && p <= Cp[j] + ln - 1; p++)
    361           {
    362             if(w[Ci[p]] != mark) ok = 0;    /* compare i and j*/
    363           }
    364           if(ok)                     /* i and j are identical */
    365           {
    366             Cp[j] = amd_flip (i);  /* absorb j into i */
    367             nv[i] += nv[j];
    368             nv[j] = 0;
    369             elen[j] = -1;         /* node j is dead */
    370             j = next[j];          /* delete j from hash bucket */
    371             next[jlast] = j;
    372           }
    373           else
    374           {
    375             jlast = j;             /* j and i are different */
    376             j = next[j];
    377           }
    378         }
    379       }
    380     }
    381 
    382     /* --- Finalize new element------------------------------------------ */
    383     for(p = pk1, pk = pk1; pk < pk2; pk++)   /* finalize Lk */
    384     {
    385       i = Ci[pk];
    386       if((nvi = -nv[i]) <= 0) continue;/* skip if i is dead */
    387       nv[i] = nvi;                      /* restore nv[i] */
    388       d = degree[i] + dk - nvi;         /* compute external degree(i) */
    389       d = std::min<Index> (d, n - nel - nvi);
    390       if(head[d] != -1) last[head[d]] = i;
    391       next[i] = head[d];               /* put i back in degree list */
    392       last[i] = -1;
    393       head[d] = i;
    394       mindeg = std::min<Index> (mindeg, d);       /* find new minimum degree */
    395       degree[i] = d;
    396       Ci[p++] = i;                      /* place i in Lk */
    397     }
    398     nv[k] = nvk;                      /* # nodes absorbed into k */
    399     if((len[k] = p-pk1) == 0)         /* length of adj list of element k*/
    400     {
    401       Cp[k] = -1;                   /* k is a root of the tree */
    402       w[k] = 0;                     /* k is now a dead element */
    403     }
    404     if(elenk != 0) cnz = p;           /* free unused space in Lk */
    405   }
    406 
    407   /* --- Postordering ----------------------------------------------------- */
    408   for(i = 0; i < n; i++) Cp[i] = amd_flip (Cp[i]);/* fix assembly tree */
    409   for(j = 0; j <= n; j++) head[j] = -1;
    410   for(j = n; j >= 0; j--)              /* place unordered nodes in lists */
    411   {
    412     if(nv[j] > 0) continue;          /* skip if j is an element */
    413     next[j] = head[Cp[j]];          /* place j in list of its parent */
    414     head[Cp[j]] = j;
    415   }
    416   for(e = n; e >= 0; e--)              /* place elements in lists */
    417   {
    418     if(nv[e] <= 0) continue;         /* skip unless e is an element */
    419     if(Cp[e] != -1)
    420     {
    421       next[e] = head[Cp[e]];      /* place e in list of its parent */
    422       head[Cp[e]] = e;
    423     }
    424   }
    425   for(k = 0, i = 0; i <= n; i++)       /* postorder the assembly tree */
    426   {
    427     if(Cp[i] == -1) k = internal::cs_tdfs<Index>(i, k, head, next, perm.indices().data(), w);
    428   }
    429 
    430   perm.indices().conservativeResize(n);
    431 
    432   delete[] W;
    433 }
    434 
    435 } // namespace internal
    436 
    437 } // end namespace Eigen
    438 
    439 #endif // EIGEN_SPARSE_AMD_H
    440