Commit c0632bed authored by Nghia Truong's avatar Nghia Truong
Browse files

ENH: Implement Welsh-Powell graph coloring algorithm

parent 045b2e76
......@@ -19,77 +19,176 @@
=========================================================================*/
#include <algorithm>
#include <numeric>
#include <map>
#include <climits>
#undef min
#undef max
#include "imstkGraph.h"
#include "imstkParallelUtils.h"
#include <g3log/g3log.hpp>
namespace imstk
{
Graph::Graph(vector<Edge> edges)
{
// add edges to the undirected graph
for (size_t i = 0; i < edges.size(); i++)
{
size_t src = edges[i].src;
size_t dest = edges[i].dest;
m_adjList[src].push_back(dest);
m_adjList[dest].push_back(src);
}
}
void
Graph::addEdge(const size_t v, const size_t w)
{
if (v < m_adjList.size() && w < m_adjList.size())
{
m_adjList[v].push_back(w);
m_adjList[w].push_back(v);
m_adjList[v].insert(w);
m_adjList[w].insert(v);
}
else
{
cout << "Vertex id exceeds the graph size: cannot add edge!" << endl;
LOG(WARNING) << "Vertex id exceeds the graph size: cannot add edge!" << std::endl;
}
}
void
Graph::print() const
{
cout << "Graph: " << "\nTotal nodes: " << m_adjList.size() << "\nAdjacency:" << endl;
std::cout << "Graph: " << "\nTotal nodes: " << m_adjList.size() << "\nAdjacency:" << std::endl;
for (size_t i = 0; i < m_adjList.size(); i++)
{
cout << "\t[" << i << "] : ";
std::cout << "\t[" << i << "] : ";
for (auto v : m_adjList[i])
{
cout << v << " ";
std::cout << v << " ";
}
std::cout << std::endl;
}
}
std::pair<std::vector<unsigned short>, unsigned short>
Graph::doColoring(ColoringMethod method, bool print /*= false*/) const
{
return method == ColoringMethod::WelshPowell ?
doColoringWelshPowell(print) :
doColoringGreedy(print);
}
std::pair<std::vector<unsigned short>, unsigned short>
Graph::doColoringWelshPowell(bool print /*= false*/) const
{
const auto numNodes = m_adjList.size();
// Must initialize colors to inf number
std::vector<unsigned short> colors(numNodes, std::numeric_limits<unsigned short>::max());
// Count the number of neighbors for each node
std::vector<size_t> neighborCounts(numNodes);
ParallelUtils::parallelFor(numNodes,
[&](const size_t idx)
{
neighborCounts[idx] = m_adjList[idx].size();
});
std::vector<unsigned short> coloringOrder(numNodes);
std::iota(coloringOrder.begin(), coloringOrder.end(), static_cast<unsigned short>(0));
std::vector<bool> coloredNodes;
unsigned short color = 0;
while (coloringOrder.size() > 0)
{
coloredNodes.resize(coloringOrder.size());
coloredNodes.assign(coloringOrder.size(), false);
coloredNodes.front() = true;
// Node with largest number of neighbors is processed first
tbb::parallel_sort(coloringOrder.begin(), coloringOrder.end(),
[&](const size_t idx0, const size_t idx1) {
return neighborCounts[idx0] > neighborCounts[idx1];
});
colors[coloringOrder.front()] = color; // The first node is colorized
// Cannot run in parallel
for (size_t i = 1; i < coloringOrder.size(); ++i)
{
const auto u = coloringOrder[i];
bool bOK = true;
for (const auto v : m_adjList[u])
{
// Check if any neighbor node has the same color as the first processing node
if (colors[v] == color)
{
bOK = false;
break;
}
}
if (bOK)
{
colors[u] = color;
coloredNodes[i] = true;
}
}
// Done with the current color
++color;
// Remove colorized nodes
size_t writeIdx = 0;
for (size_t readIdx = 1; readIdx < coloringOrder.size(); ++readIdx)
{
if (!coloredNodes[readIdx])
{
coloringOrder[writeIdx++] = coloringOrder[readIdx];
}
}
cout << endl;
coloringOrder.resize(writeIdx);
}
// print the result
if (print)
{
std::map<size_t, size_t> verticesPerColor;
std::cout << "Num. of nodes: " << numNodes << " | Num. of colors: " << color << std::endl;
for (size_t i = 0; i < numNodes; ++i)
{
std::cout << "V " << i << "-C " << colors[i] << " | ";
verticesPerColor[colors[i]]++;
}
std::cout << std::endl;
std::cout << "Vertices per color: " << std::endl;
for (const auto& kv : verticesPerColor)
{
std::cout << "C: " << kv.first << " - " << kv.second << std::endl;
}
std::cout << std::endl;
}
return std::make_pair(colors, color);
}
vector<size_t>
Graph::doGreedyColoring(bool print /*= false*/) const
std::pair<std::vector<unsigned short>, unsigned short>
Graph::doColoringGreedy(bool print /*= false*/) const
{
const auto numNodes = m_adjList.size();
vector<size_t> result(numNodes, -1);
vector<bool> available(numNodes, false);
std::vector<unsigned short> colors(numNodes, std::numeric_limits<unsigned short>::max());
std::vector<bool> available(numNodes, false);
result[0] = 0;
colors[0] = 0;
unsigned short numColors = 0;
// Assign colors to remaining V-1 vertices
for (auto u = 1; u < numNodes; ++u)
for (size_t u = 1; u < numNodes; ++u)
{
// Process all adjacent vertices and flag their colors
// as unavailable
for (const auto& i : m_adjList[u])
{
if (result[i] != -1)
if (colors[i] != std::numeric_limits<unsigned short>::max())
{
available[result[i]] = true;
available[colors[i]] = true;
}
}
// Find the first available color
int cr;
unsigned short cr;
for (cr = 0; cr < numNodes; cr++)
{
if (!available[cr])
......@@ -97,14 +196,18 @@ Graph::doGreedyColoring(bool print /*= false*/) const
break;
}
}
result[u] = cr; // Assign the found color
colors[u] = cr; // Assign the found color
if (cr + 1 > numColors)
{
numColors = cr + 1;
}
// Reset the values back to false for the next iteration
for (const auto& i : m_adjList[u])
{
if (result[i] != -1)
if (colors[i] != std::numeric_limits<unsigned short>::max())
{
available[result[i]] = false;
available[colors[i]] = false;
}
}
}
......@@ -112,14 +215,22 @@ Graph::doGreedyColoring(bool print /*= false*/) const
// print the result
if (print)
{
std::cout << "Num. of colors: " << *max_element(begin(result), end(result)) + 1 << std::endl;
for (auto i = 0; i < numNodes; ++i)
std::map<size_t, size_t> verticesPerColor;
std::cout << "Num. of nodes: " << numNodes << " | Num. of colors: " << numColors << std::endl;
for (size_t i = 0; i < numNodes; ++i)
{
std::cout << "V " << i << "-C " << colors[i] << " | ";
verticesPerColor[colors[i]]++;
}
std::cout << std::endl;
std::cout << "Vertices per color: " << std::endl;
for (const auto& kv : verticesPerColor)
{
std::cout << "V " << i << "-C " << result[i] << " | ";
std::cout << "C: " << kv.first << " - " << kv.second << std::endl;
}
std::cout << std::endl;
}
return std::move(result);
return std::make_pair(colors, numColors);
}
}
}
\ No newline at end of file
......@@ -22,6 +22,7 @@
#ifndef imstkGraph_h
#define imstkGraph_h
#include <unordered_set>
#include <iostream>
#include <algorithm>
......@@ -29,27 +30,22 @@
namespace imstk
{
using namespace std;
///
/// \brief data structure to store graph edges
///
struct Edge
{
size_t src, dest;
};
///
/// \brief class to represent a graph object
///
class Graph
{
public:
enum class ColoringMethod
{
Greedy,
WelshPowell
};
///
/// \brief Constructor/destructor
///
Graph(const size_t size){ m_adjList.resize(size); };
Graph(vector<Edge> edges);
Graph(const size_t size){ m_adjList.resize(size); }
~Graph() = default;
///
......@@ -57,20 +53,52 @@ public:
///
void addEdge(const size_t v, const size_t w);
///
/// \brief Get size of the graph
///
size_t size() const { return m_adjList.size(); }
///
/// \brief print adjacency list representation of graph
///
void print() const;
///
/// \brief Assigns colors (starting from 0) to all vertices and prints
/// the assignment of colors
/// \brief Set the default colorizing method
///
vector<size_t> doGreedyColoring(bool print = false) const;
void setDefaultColoringMethod(ColoringMethod method) { m_ColoringMethod = method; }
///
/// \brief Colorize using the default method and prints the assignment of colors
/// \return Vertex colors and number of colors
///
std::pair<std::vector<unsigned short>, unsigned short>
doColoring(bool print = false) const { return doColoring(m_ColoringMethod, print); }
///
/// \brief Colorize using the given method and prints the assignment of colors
/// \return Vertex colors and number of colors
///
std::pair<std::vector<unsigned short>, unsigned short>
doColoring(ColoringMethod method, bool print = false) const;
protected:
///
/// \brief Colorize using greedy algorithm and print the assignment of colors
/// \return Vertex colors and number of colors
///
std::pair<std::vector<unsigned short>, unsigned short>
doColoringGreedy(bool print = false) const;
///
/// \brief Colorize using Welsh-Powell algorithm and print the assignment of colors
/// \return Vertex colors and number of colors
///
std::pair<std::vector<unsigned short>, unsigned short>
doColoringWelshPowell(bool print = false) const;
vector<vector<size_t>> m_adjList; ///< A array of vectors to represent adjacency list
std::vector<std::unordered_set<size_t>> m_adjList; ///< A array of std::vectors to represent adjacency list
ColoringMethod m_ColoringMethod = ColoringMethod::WelshPowell;
};
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment