| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676 |
- // -----------------------------------------------------------------------
- // <copyright file="CuthillMcKee.cs" company="">
- // Original Matlab code by John Burkardt, Florida State University
- // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
- // </copyright>
- // -----------------------------------------------------------------------
- namespace UnityEngine.U2D.Animation.TriangleNet
- .Tools
- {
- using System;
- /// <summary>
- /// Applies the Cuthill and McKee renumbering algorithm to reduce the bandwidth of
- /// the adjacency matrix associated with the mesh.
- /// </summary>
- internal class CuthillMcKee
- {
- // The adjacency matrix of the mesh.
- AdjacencyMatrix matrix;
- /// <summary>
- /// Gets the permutation vector for the Reverse Cuthill-McKee numbering.
- /// </summary>
- /// <param name="mesh">The mesh.</param>
- /// <returns>Permutation vector.</returns>
- public int[] Renumber(Mesh mesh)
- {
- // Algorithm needs linear numbering of the nodes.
- mesh.Renumber(NodeNumbering.Linear);
- return Renumber(new AdjacencyMatrix(mesh));
- }
- /// <summary>
- /// Gets the permutation vector for the Reverse Cuthill-McKee numbering.
- /// </summary>
- /// <param name="mesh">The mesh.</param>
- /// <returns>Permutation vector.</returns>
- public int[] Renumber(AdjacencyMatrix matrix)
- {
- this.matrix = matrix;
- int bandwidth1 = matrix.Bandwidth();
- var pcol = matrix.ColumnPointers;
- // Adjust column pointers (1-based indexing).
- Shift(pcol, true);
- // TODO: Make RCM work with 0-based matrix.
- // Compute the RCM permutation.
- int[] perm = GenerateRcm();
- int[] perm_inv = PermInverse(perm);
- int bandwidth2 = PermBandwidth(perm, perm_inv);
- if (Log.Verbose)
- {
- Log.Instance.Info(String.Format("Reverse Cuthill-McKee (Bandwidth: {0} > {1})",
- bandwidth1, bandwidth2));
- }
- // Adjust column pointers (0-based indexing).
- Shift(pcol, false);
- return perm_inv;
- }
- #region RCM
- /// <summary>
- /// Finds the reverse Cuthill-Mckee ordering for a general graph.
- /// </summary>
- /// <returns>The RCM ordering.</returns>
- /// <remarks>
- /// For each connected component in the graph, the routine obtains
- /// an ordering by calling RCM.
- /// </remarks>
- int[] GenerateRcm()
- {
- // Number of nodes in the mesh.
- int n = matrix.N;
- int[] perm = new int[n];
- int i, num, root;
- int iccsze = 0;
- int level_num = 0;
- /// Index vector for a level structure. The level structure is stored in the
- /// currently unused spaces in the permutation vector PERM.
- int[] level_row = new int[n + 1];
- /// Marks variables that have been numbered.
- int[] mask = new int[n];
- for (i = 0; i < n; i++)
- {
- mask[i] = 1;
- }
- num = 1;
- for (i = 0; i < n; i++)
- {
- // For each masked connected component...
- if (mask[i] != 0)
- {
- root = i;
- // Find a pseudo-peripheral node ROOT. The level structure found by
- // ROOT_FIND is stored starting at PERM(NUM).
- FindRoot(ref root, mask, ref level_num, level_row, perm, num - 1);
- // RCM orders the component using ROOT as the starting node.
- Rcm(root, mask, perm, num - 1, ref iccsze);
- num += iccsze;
- // We can stop once every node is in one of the connected components.
- if (n < num)
- {
- return perm;
- }
- }
- }
- return perm;
- }
- /// <summary>
- /// RCM renumbers a connected component by the reverse Cuthill McKee algorithm.
- /// </summary>
- /// <param name="root">the node that defines the connected component. It is used as the starting
- /// point for the RCM ordering.</param>
- /// <param name="mask">Input/output, int MASK(NODE_NUM), a mask for the nodes. Only those nodes with
- /// nonzero input mask values are considered by the routine. The nodes numbered by RCM will have
- /// their mask values set to zero.</param>
- /// <param name="perm">Output, int PERM(NODE_NUM), the RCM ordering.</param>
- /// <param name="iccsze">Output, int ICCSZE, the size of the connected component that has been numbered.</param>
- /// <param name="node_num">the number of nodes.</param>
- /// <remarks>
- /// The connected component is specified by a node ROOT and a mask.
- /// The numbering starts at the root node.
- ///
- /// An outline of the algorithm is as follows:
- ///
- /// X(1) = ROOT.
- ///
- /// for ( I = 1 to N-1)
- /// Find all unlabeled neighbors of X(I),
- /// assign them the next available labels, in order of increasing degree.
- ///
- /// When done, reverse the ordering.
- /// </remarks>
- void Rcm(int root, int[] mask, int[] perm, int offset, ref int iccsze)
- {
- int[] pcol = matrix.ColumnPointers;
- int[] irow = matrix.RowIndices;
- int fnbr;
- int i, j, k, l;
- int jstop, jstrt;
- int lbegin, lnbr, lperm, lvlend;
- int nbr, node;
- // Number of nodes in the mesh.
- int n = matrix.N;
- /// Workspace, int DEG[NODE_NUM], a temporary vector used to hold
- /// the degree of the nodes in the section graph specified by mask and root.
- int[] deg = new int[n];
- // Find the degrees of the nodes in the component specified by MASK and ROOT.
- Degree(root, mask, deg, ref iccsze, perm, offset);
- mask[root] = 0;
- if (iccsze <= 1)
- {
- return;
- }
- lvlend = 0;
- lnbr = 1;
- // LBEGIN and LVLEND point to the beginning and
- // the end of the current level respectively.
- while (lvlend < lnbr)
- {
- lbegin = lvlend + 1;
- lvlend = lnbr;
- for (i = lbegin; i <= lvlend; i++)
- {
- // For each node in the current level...
- node = perm[offset + i - 1];
- jstrt = pcol[node];
- jstop = pcol[node + 1] - 1;
- // Find the unnumbered neighbors of NODE.
- // FNBR and LNBR point to the first and last neighbors
- // of the current node in PERM.
- fnbr = lnbr + 1;
- for (j = jstrt; j <= jstop; j++)
- {
- nbr = irow[j - 1];
- if (mask[nbr] != 0)
- {
- lnbr += 1;
- mask[nbr] = 0;
- perm[offset + lnbr - 1] = nbr;
- }
- }
- // Node has neighbors
- if (lnbr > fnbr)
- {
- // Sort the neighbors of NODE in increasing order by degree.
- // Linear insertion is used.
- k = fnbr;
- while (k < lnbr)
- {
- l = k;
- k = k + 1;
- nbr = perm[offset + k - 1];
- while (fnbr < l)
- {
- lperm = perm[offset + l - 1];
- if (deg[lperm - 1] <= deg[nbr - 1])
- {
- break;
- }
- perm[offset + l] = lperm;
- l = l - 1;
- }
- perm[offset + l] = nbr;
- }
- }
- }
- }
- // We now have the Cuthill-McKee ordering. Reverse it.
- ReverseVector(perm, offset, iccsze);
- }
- /// <summary>
- /// Finds a pseudo-peripheral node.
- /// </summary>
- /// <param name="root">On input, ROOT is a node in the the component of the graph for
- /// which a pseudo-peripheral node is sought. On output, ROOT is the pseudo-peripheral
- /// node obtained.</param>
- /// <param name="mask">MASK[NODE_NUM], specifies a section subgraph. Nodes for which MASK
- /// is zero are ignored by FNROOT.</param>
- /// <param name="level_num">Output, int LEVEL_NUM, is the number of levels in the level
- /// structure rooted at the node ROOT.</param>
- /// <param name="level_row">Output, int LEVEL_ROW(NODE_NUM+1), the level structure array pair
- /// containing the level structure found.</param>
- /// <param name="level">Output, int LEVEL(NODE_NUM), the level structure array pair
- /// containing the level structure found.</param>
- /// <param name="node_num">the number of nodes.</param>
- /// <remarks>
- /// The diameter of a graph is the maximum distance (number of edges)
- /// between any two nodes of the graph.
- ///
- /// The eccentricity of a node is the maximum distance between that
- /// node and any other node of the graph.
- ///
- /// A peripheral node is a node whose eccentricity equals the
- /// diameter of the graph.
- ///
- /// A pseudo-peripheral node is an approximation to a peripheral node;
- /// it may be a peripheral node, but all we know is that we tried our
- /// best.
- ///
- /// The routine is given a graph, and seeks pseudo-peripheral nodes,
- /// using a modified version of the scheme of Gibbs, Poole and
- /// Stockmeyer. It determines such a node for the section subgraph
- /// specified by MASK and ROOT.
- ///
- /// The routine also determines the level structure associated with
- /// the given pseudo-peripheral node; that is, how far each node
- /// is from the pseudo-peripheral node. The level structure is
- /// returned as a list of nodes LS, and pointers to the beginning
- /// of the list of nodes that are at a distance of 0, 1, 2, ...,
- /// NODE_NUM-1 from the pseudo-peripheral node.
- ///
- /// Reference:
- /// Alan George, Joseph Liu,
- /// Computer Solution of Large Sparse Positive Definite Systems,
- /// Prentice Hall, 1981.
- ///
- /// Norman Gibbs, William Poole, Paul Stockmeyer,
- /// An Algorithm for Reducing the Bandwidth and Profile of a Sparse Matrix,
- /// SIAM Journal on Numerical Analysis,
- /// Volume 13, pages 236-250, 1976.
- ///
- /// Norman Gibbs,
- /// Algorithm 509: A Hybrid Profile Reduction Algorithm,
- /// ACM Transactions on Mathematical Software,
- /// Volume 2, pages 378-387, 1976.
- /// </remarks>
- void FindRoot(ref int root, int[] mask, ref int level_num, int[] level_row,
- int[] level, int offset)
- {
- int[] pcol = matrix.ColumnPointers;
- int[] irow = matrix.RowIndices;
- int iccsze;
- int j, jstrt;
- int k, kstop, kstrt;
- int mindeg;
- int nghbor, ndeg;
- int node;
- int level_num2 = 0;
- // Determine the level structure rooted at ROOT.
- GetLevelSet(ref root, mask, ref level_num, level_row, level, offset);
- // Count the number of nodes in this level structure.
- iccsze = level_row[level_num] - 1;
- // Extreme cases:
- // A complete graph has a level set of only a single level.
- // Every node is equally good (or bad).
- // or
- // A "line graph" 0--0--0--0--0 has every node in its only level.
- // By chance, we've stumbled on the ideal root.
- if (level_num == 1 || level_num == iccsze)
- {
- return;
- }
- // Pick any node from the last level that has minimum degree
- // as the starting point to generate a new level set.
- for (;;)
- {
- mindeg = iccsze;
- jstrt = level_row[level_num - 1];
- root = level[offset + jstrt - 1];
- if (jstrt < iccsze)
- {
- for (j = jstrt; j <= iccsze; j++)
- {
- node = level[offset + j - 1];
- ndeg = 0;
- kstrt = pcol[node - 1];
- kstop = pcol[node] - 1;
- for (k = kstrt; k <= kstop; k++)
- {
- nghbor = irow[k - 1];
- if (mask[nghbor] > 0)
- {
- ndeg += 1;
- }
- }
- if (ndeg < mindeg)
- {
- root = node;
- mindeg = ndeg;
- }
- }
- }
- // Generate the rooted level structure associated with this node.
- GetLevelSet(ref root, mask, ref level_num2, level_row, level, offset);
- // If the number of levels did not increase, accept the new ROOT.
- if (level_num2 <= level_num)
- {
- break;
- }
- level_num = level_num2;
- // In the unlikely case that ROOT is one endpoint of a line graph,
- // we can exit now.
- if (iccsze <= level_num)
- {
- break;
- }
- }
- }
- /// <summary>
- /// Generates the connected level structure rooted at a given node.
- /// </summary>
- /// <param name="root">the node at which the level structure is to be rooted.</param>
- /// <param name="mask">MASK[NODE_NUM]. On input, only nodes with nonzero MASK are to be processed.
- /// On output, those nodes which were included in the level set have MASK set to 1.</param>
- /// <param name="level_num">Output, int LEVEL_NUM, the number of levels in the level structure. ROOT is
- /// in level 1. The neighbors of ROOT are in level 2, and so on.</param>
- /// <param name="level_row">Output, int LEVEL_ROW[NODE_NUM+1], the rooted level structure.</param>
- /// <param name="level">Output, int LEVEL[NODE_NUM], the rooted level structure.</param>
- /// <param name="node_num">the number of nodes.</param>
- /// <remarks>
- /// Only nodes for which MASK is nonzero will be considered.
- ///
- /// The root node chosen by the user is assigned level 1, and masked.
- /// All (unmasked) nodes reachable from a node in level 1 are
- /// assigned level 2 and masked. The process continues until there
- /// are no unmasked nodes adjacent to any node in the current level.
- /// The number of levels may vary between 2 and NODE_NUM.
- ///
- /// Reference:
- /// Alan George, Joseph Liu,
- /// Computer Solution of Large Sparse Positive Definite Systems,
- /// Prentice Hall, 1981.
- /// </remarks>
- void GetLevelSet(ref int root, int[] mask, ref int level_num, int[] level_row,
- int[] level, int offset)
- {
- int[] pcol = matrix.ColumnPointers;
- int[] irow = matrix.RowIndices;
- int i, iccsze;
- int j, jstop, jstrt;
- int lbegin, lvlend, lvsize;
- int nbr;
- int node;
- mask[root] = 0;
- level[offset] = root;
- level_num = 0;
- lvlend = 0;
- iccsze = 1;
- // LBEGIN is the pointer to the beginning of the current level, and
- // LVLEND points to the end of this level.
- for (;;)
- {
- lbegin = lvlend + 1;
- lvlend = iccsze;
- level_num += 1;
- level_row[level_num - 1] = lbegin;
- // Generate the next level by finding all the masked neighbors of nodes
- // in the current level.
- for (i = lbegin; i <= lvlend; i++)
- {
- node = level[offset + i - 1];
- jstrt = pcol[node];
- jstop = pcol[node + 1] - 1;
- for (j = jstrt; j <= jstop; j++)
- {
- nbr = irow[j - 1];
- if (mask[nbr] != 0)
- {
- iccsze += 1;
- level[offset + iccsze - 1] = nbr;
- mask[nbr] = 0;
- }
- }
- }
- // Compute the current level width (the number of nodes encountered.)
- // If it is positive, generate the next level.
- lvsize = iccsze - lvlend;
- if (lvsize <= 0)
- {
- break;
- }
- }
- level_row[level_num] = lvlend + 1;
- // Reset MASK to 1 for the nodes in the level structure.
- for (i = 0; i < iccsze; i++)
- {
- mask[level[offset + i]] = 1;
- }
- }
- /// <summary>
- /// Computes the degrees of the nodes in the connected component.
- /// </summary>
- /// <param name="root">the node that defines the connected component.</param>
- /// <param name="mask">MASK[NODE_NUM], is nonzero for those nodes which are to be considered.</param>
- /// <param name="deg">Output, int DEG[NODE_NUM], contains, for each node in the connected component, its degree.</param>
- /// <param name="iccsze">Output, int ICCSIZE, the number of nodes in the connected component.</param>
- /// <param name="ls">Output, int LS[NODE_NUM], stores in entries 1 through ICCSIZE the nodes in the
- /// connected component, starting with ROOT, and proceeding by levels.</param>
- /// <param name="node_num">the number of nodes.</param>
- /// <remarks>
- /// The connected component is specified by MASK and ROOT.
- /// Nodes for which MASK is zero are ignored.
- ///
- /// Reference:
- /// Alan George, Joseph Liu,
- /// Computer Solution of Large Sparse Positive Definite Systems,
- /// Prentice Hall, 1981.
- /// </remarks>
- void Degree(int root, int[] mask, int[] deg, ref int iccsze, int[] ls, int offset)
- {
- int[] pcol = matrix.ColumnPointers;
- int[] irow = matrix.RowIndices;
- int i, ideg;
- int j, jstop, jstrt;
- int lbegin, lvlend;
- int lvsize = 1;
- int nbr, node;
- // The sign of ADJ_ROW(I) is used to indicate if node I has been considered.
- ls[offset] = root;
- pcol[root] = -pcol[root];
- lvlend = 0;
- iccsze = 1;
- // If the current level width is nonzero, generate another level.
- while (lvsize > 0)
- {
- // LBEGIN is the pointer to the beginning of the current level, and
- // LVLEND points to the end of this level.
- lbegin = lvlend + 1;
- lvlend = iccsze;
- // Find the degrees of nodes in the current level,
- // and at the same time, generate the next level.
- for (i = lbegin; i <= lvlend; i++)
- {
- node = ls[offset + i - 1];
- jstrt = -pcol[node];
- jstop = Math.Abs(pcol[node + 1]) - 1;
- ideg = 0;
- for (j = jstrt; j <= jstop; j++)
- {
- nbr = irow[j - 1];
- if (mask[nbr] != 0) // EDIT: [nbr - 1]
- {
- ideg = ideg + 1;
- if (0 <= pcol[nbr]) // EDIT: [nbr - 1]
- {
- pcol[nbr] = -pcol[nbr]; // EDIT: [nbr - 1]
- iccsze = iccsze + 1;
- ls[offset + iccsze - 1] = nbr;
- }
- }
- }
- deg[node] = ideg;
- }
- // Compute the current level width.
- lvsize = iccsze - lvlend;
- }
- // Reset ADJ_ROW to its correct sign and return.
- for (i = 0; i < iccsze; i++)
- {
- node = ls[offset + i];
- pcol[node] = -pcol[node];
- }
- }
- #endregion
- #region Tools
- /// <summary>
- /// Computes the bandwidth of a permuted adjacency matrix.
- /// </summary>
- /// <param name="perm">The permutation.</param>
- /// <param name="perm_inv">The inverse permutation.</param>
- /// <returns>Bandwidth of the permuted adjacency matrix.</returns>
- /// <remarks>
- /// The matrix is defined by the adjacency information and a permutation.
- /// The routine also computes the bandwidth and the size of the envelope.
- /// </remarks>
- int PermBandwidth(int[] perm, int[] perm_inv)
- {
- int[] pcol = matrix.ColumnPointers;
- int[] irow = matrix.RowIndices;
- int col, i, j;
- int band_lo = 0;
- int band_hi = 0;
- int n = matrix.N;
- for (i = 0; i < n; i++)
- {
- for (j = pcol[perm[i]]; j < pcol[perm[i] + 1]; j++)
- {
- col = perm_inv[irow[j - 1]];
- band_lo = Math.Max(band_lo, i - col);
- band_hi = Math.Max(band_hi, col - i);
- }
- }
- return band_lo + 1 + band_hi;
- }
- /// <summary>
- /// Produces the inverse of a given permutation.
- /// </summary>
- /// <param name="n">Number of items permuted.</param>
- /// <param name="perm">PERM[N], a permutation.</param>
- /// <returns>The inverse permutation.</returns>
- int[] PermInverse(int[] perm)
- {
- int n = matrix.N;
- int[] perm_inv = new int[n];
- for (int i = 0; i < n; i++)
- {
- perm_inv[perm[i]] = i;
- }
- return perm_inv;
- }
- /// <summary>
- /// Reverses the elements of an integer vector.
- /// </summary>
- /// <param name="size">number of entries in the array.</param>
- /// <param name="a">the array to be reversed.</param>
- /// <example>
- /// Input:
- /// N = 5,
- /// A = ( 11, 12, 13, 14, 15 ).
- ///
- /// Output:
- /// A = ( 15, 14, 13, 12, 11 ).
- /// </example>
- void ReverseVector(int[] a, int offset, int size)
- {
- int i;
- int j;
- for (i = 0; i < size / 2; i++)
- {
- j = a[offset + i];
- a[offset + i] = a[offset + size - 1 - i];
- a[offset + size - 1 - i] = j;
- }
- }
- void Shift(int[] a, bool up)
- {
- int length = a.Length;
- if (up)
- {
- for (int i = 0; i < length; a[i]++, i++) ;
- }
- else
- {
- for (int i = 0; i < length; a[i]--, i++) ;
- }
- }
- #endregion
- }
- }
|