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