Skip to content
Snippets Groups Projects
metis_util.hpp 10.35 KiB
/*
 * metis_util.hpp
 *
 *  Created on: Nov 21, 2014
 *      Author: Pietro Incardona
 */

#ifndef METIS_UTIL_HPP
#define METIS_UTIL_HPP

#include <iostream>
#include "metis.h"
#include "SubdomainGraphNodes.hpp"
#include "VTKWriter/VTKWriter.hpp"

/*! \brief Metis graph structure
 *
 * Metis graph structure
 *
 */
struct Metis_graph
{
	//! The number of vertices in the graph
	idx_t * nvtxs;

	//! number of balancing constrains
	//! more practical, are the number of weights for each vertex
	//! PS even we you specify vwgt == NULL ncon must be set at leat to
	//! one
	idx_t * ncon;

	//! For each vertex it store the adjacency lost start for the vertex i
	idx_t * xadj;

	//! For each vertex it store a list of all neighborhood vertex
	idx_t * adjncy;

	//! Array that store the weight for each vertex
	idx_t * vwgt;

	//! Array of the vertex size, basically is the total communication amount
	idx_t * vsize;
	//! The weight of the edge
	idx_t * adjwgt;
	//! number of part to partition the graph
	idx_t * nparts;
	//! Desired weight for each partition (one for each constrain)
	real_t * tpwgts;
	//! For each partition load imbalance tollerated
	real_t * ubvec;
	//! Additional option for the graph partitioning
	idx_t * options;
	//! return the total comunication cost for each partition
	idx_t * objval;
	//! Is a output vector containing the partition for each vertex
	idx_t * part;
};

//! Balance communication and computation
#define BALANCE_CC 1
//! Balance communication computation and memory
#define BALANCE_CCM 2
//! Balance computation and comunication and others
#define BALANCE_CC_O(c) c+1

/*! \brief Helper class to define Metis graph
 *
 * \tparam graph structure that store the graph
 *
 */

template<typename Graph>
class Metis
{
	//! Graph in metis reppresentation
	Metis_graph Mg;

	//! Original graph
	Graph & g;

	//! indicate how many time decompose/refine/re-decompose has been called
	size_t n_dec;

	//! Distribution tolerance
	real_t dist_tol = 1.05;

	/*! \brief Construct Adjacency list
	 *
	 * \param g Graph
	 *
	 */
	void constructAdjList(Graph & g)
	{
		// create xadj and adjlist

		// create xadj, adjlist, vwgt, adjwgt and vsize
		Mg.xadj = new idx_t[g.getNVertex() + 1];
		Mg.adjncy = new idx_t[g.getNEdge()];

		//! starting point in the adjacency list
		size_t prev = 0;

		// actual position
		size_t id = 0;

		// for each vertex calculate the position of the starting point in the adjacency list
		for (size_t i = 0; i < g.getNVertex(); i++)
		{
			// Calculate the starting point in the adjacency list
			Mg.xadj[id] = prev;

			// Create the adjacency list
			for (size_t s = 0; s < g.getNChilds(i); s++)
			{
				Mg.adjncy[prev + s] = g.getChild(i, s);
			}

			// update the position for the next vertex
			prev += g.getNChilds(i);

			id++;
		}

		// Fill the last point
		Mg.xadj[id] = prev;
	}

	/*! \brief Construct Adjacency list
	 *
	 * \param g Graph
	 *
	 */
	void constructAdjListWithWeights(Graph & g)
	{
		// create xadj, adjlist, vwgt, adjwgt and vsize
		if (Mg.xadj != NULL)
		{delete [] Mg.xadj;}
		if (Mg.adjncy != NULL)
		{delete [] Mg.adjncy;}
		if (Mg.vwgt != NULL)
		{delete [] Mg.vwgt;}
		if (Mg.adjwgt != NULL)
		{delete [] Mg.adjwgt;}
		if (Mg.vsize != NULL)
		{delete [] Mg.vsize;}
		Mg.xadj = new idx_t[g.getNVertex() + 1];
		Mg.adjncy = new idx_t[g.getNEdge()];
		Mg.vwgt = new idx_t[g.getNVertex()];
		Mg.adjwgt = new idx_t[g.getNEdge()];
		Mg.vsize = new idx_t[g.getNVertex()];

		//! starting point in the adjacency list
		size_t prev = 0;

		// actual position
		size_t id = 0;

		// for each vertex calculate the position of the starting point in the adjacency list
		for (size_t i = 0; i < g.getNVertex(); i++)
		{
			// Add weight to vertex and migration cost
			Mg.vwgt[i] = g.vertex(i).template get<nm_v_computation>();
			Mg.vwgt[i] = (Mg.vwgt[i] == 0)?1:Mg.vwgt[i];
			Mg.vsize[i] = g.vertex(i).template get<nm_v_migration>();
			Mg.vsize[i] = (Mg.vsize[i] == 0)?1:Mg.vsize[i];

			// Calculate the starting point in the adjacency list
			Mg.xadj[id] = prev;

			// Create the adjacency list
			for (size_t s = 0; s < g.getNChilds(i); s++)
			{
				Mg.adjncy[prev + s] = g.getChild(i, s);

				// zero values on Metis are dangerous
				Mg.adjwgt[prev + s] = g.getChildEdge(i, s).template get<nm_e::communication>();
				Mg.adjwgt[prev + s] = (Mg.adjwgt[prev + s] == 0)?1:Mg.adjwgt[prev + s];
			}

			// update the position for the next vertex
			prev += g.getNChilds(i);

			id++;
		}

		// Fill the last point
		Mg.xadj[id] = prev;
	}


	void reset_pointer()
	{
		Mg.nvtxs = NULL;
		Mg.ncon = NULL;
		Mg.xadj = NULL;
		Mg.adjncy = NULL;
		Mg.vwgt = NULL;
		Mg.adjwgt = NULL;
		Mg.nparts = NULL;
		Mg.tpwgts = NULL;
		Mg.ubvec = NULL;
		Mg.options = NULL;
		Mg.objval = NULL;
		Mg.part = NULL;
		Mg.vsize = NULL;
	}

public:

	/*! \brief Constructor
	 *
	 * Construct a metis graph from Graph_CSR
	 *
	 * \param g Graph we want to convert to decompose
	 * \param nc number of partitions
	 * \param useWeights tells if weights are used or not
	 *
	 */
	Metis(Graph & g, size_t nc, bool useWeights)
	:g(g),n_dec(0)
	{
		reset_pointer();
		initMetisGraph(nc,useWeights);
	}

	/*! \brief Constructor
	 *
	 * Construct a metis graph from Graph_CSR
	 *
	 * \param g Graph we want to convert to decompose
	 * \param nc number of partitions
	 *
	 */
	Metis(Graph & g, size_t nc)
	:g(g),n_dec(0)
	{
		reset_pointer();
		initMetisGraph(nc,false);
	}

	/*! \brief Constructor
	 *
	 * This constructor does not initialize the internal metis graph
	 * you have to use initMetisGraph to initialize
	 *
	 * \param g Graph we want to convert to decompose
	 *
	 */
	Metis(Graph & g)
	:g(g),n_dec(0)
	{
		reset_pointer();
	}


	/*! \brief Initialize the METIS graph
	 *
	 * \param nc number of partitions
	 * \param useWeights use the weights on the graph
	 *
	 */
	void initMetisGraph(int nc, bool useWeights)
	{

		// Get the number of vertex

		if (Mg.nvtxs != NULL)
		{delete[] Mg.nvtxs;}
		Mg.nvtxs = new idx_t[1];
		Mg.nvtxs[0] = g.getNVertex();

		// Set the number of constrains

		if (Mg.ncon != NULL)
		{delete[] Mg.ncon;}
		Mg.ncon = new idx_t[1];
		Mg.ncon[0] = 1;

		// Set to null the weight of the vertex
		if (Mg.vwgt != NULL)
		{delete[] Mg.vwgt;}
		Mg.vwgt = NULL;

		// Put the total communication size to NULL

		if (Mg.vsize != NULL)
		{delete[] Mg.vsize;}
		Mg.vsize = NULL;

		// Set to null the weight of the edge

		if (Mg.adjwgt != NULL)
		{delete[] Mg.adjwgt;}
		Mg.adjwgt = NULL;

		// construct the adjacency list
		if (useWeights)
			constructAdjListWithWeights(g);
		else
			constructAdjList(g);

		// Set the total number of partitions

		if (Mg.nparts != NULL)
		{delete[] Mg.nparts;}
		Mg.nparts = new idx_t[1];
		Mg.nparts[0] = nc;

		// Set to null the desired weight for each partition (one for each constrain)

		if (Mg.tpwgts != NULL)
		{delete[] Mg.tpwgts;}
		Mg.tpwgts = NULL;

		//! Set to null the partition load imbalance tolerance
		if (Mg.ubvec != NULL)
		{delete[] Mg.ubvec;}
		Mg.ubvec = NULL;

		//! Set tp null additional option for the graph partitioning

		if (Mg.options != NULL)
		{delete[] Mg.options;}
		Mg.options = NULL;

		//! set the objective value
		if (Mg.objval != NULL)
		{delete[] Mg.objval;}
		Mg.objval = new idx_t[1];

		//! Is an output vector containing the partition for each vertex
		if (Mg.part != NULL)
		{delete[] Mg.part;}
		Mg.part = new idx_t[g.getNVertex()];

		for (size_t i = 0; i < g.getNVertex(); i++)
			Mg.part[i] = 0;
	}

	/*! \brief destructor
	 *
	 * Destructor, It destroy all the memory allocated
	 *
	 */
	~Metis()
	{
		// Deallocate the Mg structure
		if (Mg.nvtxs != NULL)
		{
			delete[] Mg.nvtxs;
		}

		if (Mg.ncon != NULL)
		{
			delete[] Mg.ncon;
		}

		if (Mg.xadj != NULL)
		{
			delete[] Mg.xadj;
		}

		if (Mg.adjncy != NULL)
		{
			delete[] Mg.adjncy;
		}
		if (Mg.vwgt != NULL)
		{
			delete[] Mg.vwgt;
		}
		if (Mg.adjwgt != NULL)
		{
			delete[] Mg.adjwgt;
		}
		if (Mg.nparts != NULL)
		{
			delete[] Mg.nparts;
		}
		if (Mg.tpwgts != NULL)
		{
			delete[] Mg.tpwgts;
		}
		if (Mg.ubvec != NULL)
		{
			delete[] Mg.ubvec;
		}
		if (Mg.options != NULL)
		{
			delete[] Mg.options;
		}
		if (Mg.objval != NULL)
		{
			delete[] Mg.objval;
		}
		if (Mg.part != NULL)
		{
			delete[] Mg.part;
		}
        if (Mg.vsize != NULL)
        {
            delete[] Mg.vsize;
        }
	}

	/*! \brief Decompose the graph
	 *
	 * \tparam i which property store the decomposition
	 *
	 */

	template<unsigned int i>
	void decompose()
	{
		if (Mg.nparts[0] != 1)
		{
			// Decompose
			METIS_PartGraphRecursive(Mg.nvtxs, Mg.ncon, Mg.xadj, Mg.adjncy, Mg.vwgt, Mg.vsize, Mg.adjwgt, Mg.nparts, Mg.tpwgts, Mg.ubvec, Mg.options, Mg.objval, Mg.part);

			// vertex id

			size_t id = 0;

			// For each vertex store the processor that contain the data

			auto it = g.getVertexIterator();

			while (it.isNext())
			{
				g.vertex(it).template get<i>() = Mg.part[id];

				++id;
				++it;
			}
		}
		else
		{
			// Trivially assign all the domains to the processor 0

			auto it = g.getVertexIterator();

			while (it.isNext())
			{
				g.vertex(it).template get<i>() = 0;

				++it;
			}
		}

		n_dec++;
	}

	/*! \brief Decompose the graph
	 *
	 * \tparam i which property store the decomposition
	 *
	 */

	template<unsigned int i, typename Graph_part>
	void decompose(Graph_part & gp)
	{
		// Decompose
		METIS_PartGraphRecursive(Mg.nvtxs, Mg.ncon, Mg.xadj, Mg.adjncy, Mg.vwgt, Mg.vsize, Mg.adjwgt, Mg.nparts, Mg.tpwgts, Mg.ubvec, Mg.options, Mg.objval, Mg.part);

		// vertex id

		size_t id = 0;

		// For each vertex store the processor that contain the data

		auto it = gp.getVertexIterator();

		while (it.isNext())
		{
			gp.vertex(it).template get<i>() = Mg.part[id];

			++id;
			++it;
		}

		n_dec++;
	}

	/*! \brief It set Metis on test
	 *
	 * \param testing set to true to disable the testing
	 *
	 * At the moment disable the seed randomness to keep the result
	 * reproducible
	 *
	 */
	void onTest(bool testing)
	{
		if (testing == false)
			return;

		if (Mg.options == NULL)
		{
			// allocate
			Mg.options = new idx_t[METIS_NOPTIONS];

			// set default options
			METIS_SetDefaultOptions(Mg.options);
		}

		Mg.options[METIS_OPTION_SEED] = 0;
	}

	/*! \brief Distribution tolerance
	 *
	 * \param tol tolerance
	 *
	 */
	void setDistTol(real_t tol)
	{
		dist_tol = tol;
	}

	/*! \brief Get the decomposition counter
	 *
	 * \return the decomposition counter
	 *
	 */
	size_t get_ndec()
	{
		return n_dec;
	}

	/*! \brief Increment the decomposition counter
	 *
	 *
	 */
	void inc_dec()
	{
		n_dec++;
	}
};

#endif