Commit e44f8de7 authored by Alexis SALZMAN's avatar Alexis SALZMAN

[xGraph] turn exportDot into a parallel MPIIO export function + API change

exportDot is now working with MPIIO io library. If called with
MPI-com_self or a mono process communicator it output a full undirected
graph without parallel information. If called with many process each
containing part of undirected graph it encapsulate each part in a
subgraph named with the process id. If node ID is unique across process
common nodes are unique among the representation obtain with dot but
connection do exist in between subgraph(process).
Function API is now reacher. User must at least give something to
associate KEYTYPE object to a unique id. It represents the node
id in the graph associated to the key. Optionally user may set a
specific label to each node and a weight label to each edges.

Some new methods have been added to nodeTo class (one avoid exportDot
to becomes a friend function of the class).
parent e2e0d821
......@@ -8,6 +8,7 @@
#define _NODEANDCONNECTEDEDGE_H
#include <deque>
#include <unordered_map>
#include "xIteratorTools.h"
#include "xDataType.h"
namespace xgraph
{
......@@ -150,10 +151,26 @@ class nodeTo
typedef ET weight_t;
typedef NT node_data_t;
typedef std::pair < const KEYTYPE *, ET > child_t;
typedef std::unordered_map < const KEYTYPE *,ET > child_container_t;
void insertChild(const KEYTYPE * k,const ET &w)
{
to_childs.insert(std::make_pair(k,w));
}
size_t removeChild(const KEYTYPE * k)
{
to_childs.erase(k);
// return number of child after removal if any
return( to_childs.size());
}
size_t nbChild () const
{
return to_childs.size();
}
auto rangeChild() const -> xtool::xRange<typename child_container_t::const_iterator>
{
return xtool::make_range(to_childs.begin(),to_childs.end());
}
void setAttachedData( const node_data_t &data)
{
if (attached_data)
......@@ -173,41 +190,48 @@ class nodeTo
{
attached_data.reset(nullptr);
}
void setHasRemoteChild( bool setting)
{
has_remote_child = setting;
}
bool hasRemoteChild()
{
return has_remote_child;
}
private:
std::unique_ptr < NT > attached_data;
std::unordered_map < const KEYTYPE *,ET > to_childs;
child_container_t to_childs;
bool has_remote_child = false;
mutable bool visited;
template < typename NODE, class DATAMANAGER, typename F >
friend void breadthFirstSearch(const DATAMANAGER & graph, const typename NODE::key_t *key_source,F func_node);
template < typename NODE, class DATAMANAGER, typename F >
friend void conditionalBreadthFirstSearch(const DATAMANAGER & graph, const typename NODE::key_t *key_source,F func_node);
template < typename NODE, class DATAMANAGER,typename ID, typename W >
friend void exportDot(const DATAMANAGER & graph, ostream &os,const ID &fid,const W &fw);
};
/// A simple function that output a nodeTo graph in dot format
/*!
* NODE the type of nodes of the graph (nodeTo<KEYTYPE,ET,NT>)
* DATAMANAGER the data manager type that hold NODE nodes associated to KEYTYPE keys.
* ID a function that associate a unique ID to the key holding a node
* W a function that associate a unsigned int to the weight of each edges.
*/
template < typename NODE, class DATAMANAGER,typename ID, typename W >
void exportDot(const DATAMANAGER & graph, ostream &os,const ID &fid,const W &fw)
// run a function for each node with no child
template < typename NODE, typename DATAMANAGER, typename F >
void apllyToNoChild(const DATAMANAGER & graph, F func_node)
{
os<<"digraph X {"<<endl;
for (auto & key : xtool::make_range(graph.beginKey(),graph.endKey()))
{
const NODE *node = graph.getData(*key);
assert(node);
unsigned int parent = fid(key);
for (auto pair : node->to_childs)
{
os<<parent<<" -> "<<fid(pair.first)<<" [len="<<fw(pair.second)<<"]"<<std::endl;
}
if (!node->nbChild ())
func_node(key,node);
}
}
// run a function for each node fulfilling condition
template < typename NODE, typename DATAMANAGER, typename C, typename F >
void apllyToConditionalNode(const DATAMANAGER & graph, F func_node)
{
for (auto & key : xtool::make_range(graph.beginKey(),graph.endKey()))
{
const NODE *node = graph.getData(*key);
assert(node);
if (!C(key,node))
func_node(key,node);
}
os<<"}"<<std::endl;
}
/// A breath first search algorithm dedicated to nodeTo graph traversal via edges
/*!
......
/*
This file is a part of eXlibris C++ Library
under the GNU Lesser General Public License.
See the NOTICE.md & LICENSE.md files for terms
and conditions.
*/
#ifndef _NODEANDCONNECTEDEDGEDIST_H
#define _NODEANDCONNECTEDEDGEDIST_H
#include "nodeAndConnectedEdge.h"
#include "nodeAndConnectedEdgeDistInternal.h"
namespace xgraph
{
//==============================================================================================
/// A simple function that output a distributed nodeTo graph in dot format
/*!
* NODE the type of nodes of the graph (nodeTo<KEYTYPE,ET,NT>)
* DATAMANAGER the data manager type that hold NODE nodes associated to KEYTYPE keys.
* ID a function that associate a unique ID across process to the key holding a node
*/
template < typename NODE, class DATAMANAGER,typename ID>
void exportDot(const DATAMANAGER & graph, std::string f_name,MPI_Comm world,const ID &fid)
{
auto * fw_null = static_cast < unsigned int ( * )(const double) >( nullptr );
auto * fl_null = static_cast < char ( * )(const typename NODE::key_t*) >( nullptr );
internal::exportDot<NODE,DATAMANAGER,ID>(graph,f_name,world,fid, fl_null, fw_null);
}
/// A simple function that output a distributed nodeTo graph in dot format
/*!
* NODE the type of nodes of the graph (nodeTo<KEYTYPE,ET,NT>)
* DATAMANAGER the data manager type that hold NODE nodes associated to KEYTYPE keys.
* ID a function that associate a unique ID across process to the key holding a node
* L a function that associate a label(anything that iostream handle corectelly) to the node related to the key given as argument
*/
template < typename NODE, class DATAMANAGER,typename ID, typename L >
void exportDot(const DATAMANAGER & graph, std::string f_name,MPI_Comm world,const ID &fid, const L &fl)
{
auto * fw_null = static_cast < unsigned int ( * )(const double) >( nullptr );
internal::exportDot<NODE,DATAMANAGER,ID>(graph,f_name,world,fid, &fl, fw_null);
}
/// A simple function that output a distributed nodeTo graph in dot format
/*!
* NODE the type of nodes of the graph (nodeTo<KEYTYPE,ET,NT>)
* DATAMANAGER the data manager type that hold NODE nodes associated to KEYTYPE keys.
* ID a function that associate a unique ID across process to the key holding a node
* L a function that associate a label(anything that iostream handle corectelly) to the node related to the key given as argument
* W a function that associate a unsigned int to the weight of each edges.
*/
template < typename NODE, class DATAMANAGER,typename ID, typename L, typename W >
void exportDot(const DATAMANAGER & graph, std::string f_name,MPI_Comm world,const ID &fid, const L &fl, const W &fw)
{
internal::exportDot<NODE,DATAMANAGER,ID>(graph,f_name,world,fid, &fl, &fw);
}
} //end namespace
#endif
/*
This file is a part of eXlibris C++ Library
under the GNU Lesser General Public License.
See the NOTICE.md & LICENSE.md files for terms
and conditions.
*/
#ifndef _NODEANDCONNECTEDEDGEDIST_H
#error "This header must not be included directly"
#endif
namespace xgraph
{
namespace internal
{
template < typename NODE, class DATAMANAGER,typename ID, typename L, typename W >
void exportDot(const DATAMANAGER & graph, std::string f_name,MPI_Comm world,const ID &fid, L *fl, W *fw)
{
// local
MPI_File file;
MPI_Status status;
int proc_id,nb_proc;
std::ostringstream os;
// open file an clean it
MPI_File_open(world, f_name.c_str(), MPI_MODE_WRONLY|MPI_MODE_CREATE, MPI_INFO_NULL, &file);
MPI_File_set_size(file,0);
MPI_Comm_rank(world,&proc_id);
MPI_Comm_size(world,&nb_proc);
auto node_range = xtool::make_range(graph.beginKey(),graph.endKey());
size_t nb_node = node_range.size();
if (!proc_id)
os<<"digraph X {"<<std::endl;
if (nb_proc>1 && nb_node)
{
os<<" subgraph cluster_"<<proc_id<<" {"<<std::endl;
os<<" label=\"p"<<proc_id<<"\";"<<std::endl;
}
if (fl)
{
for (auto & key : node_range)
{
unsigned int parent = fid(key);
os<< parent<<"[label=\""<<(*fl)(key)<<"\"];"<<std::endl;
}
}
else
{
for (auto & key : node_range)
{
unsigned int parent = fid(key);
os<< parent<<";"<<std::endl;
}
}
if (nb_proc>1 && nb_node)
os<<" }"<<std::endl;
if (fw)
{
for (auto & key : node_range)
{
const NODE *node = graph.getData(*key);
assert(node);
unsigned int parent = fid(key);
for (auto pair : node->rangeChild())
{
os<<parent<<" -> "<<fid(pair.first)<<" [label="<<(*fw)(pair.second)<<"];"<<std::endl;
}
}
}
else
{
for (auto & key : node_range)
{
const NODE *node = graph.getData(*key);
assert(node);
unsigned int parent = fid(key);
for (auto pair : node->rangeChild())
{
os<<parent<<" -> "<<fid(pair.first)<<";"<<std::endl;
}
}
}
if (proc_id == nb_proc-1)
os<<"}"<<std::endl;
// nb_info stor nb of char and char considerer here to be 1 Byte long
const MPI_Offset nb_info = os.str().size();
// compute offset
MPI_Offset offsets = 0;
MPI_Scan(&nb_info,&offsets,1,MPI_OFFSET,MPI_SUM,world);
MPI_Offset dist;
dist = offsets-nb_info;
// write info
if (nb_info)
MPI_File_write_at(file,dist,os.str().c_str(),nb_info,MPI_CHAR,&status);
// close file
MPI_File_close(&file);
}
} //end namespace
} //end namespace
......@@ -35,6 +35,7 @@ using datamanagerFM_t = xinterface::aomd::xAttachedDataManagerAOMDGeneric < AOMD
#ifdef HAVE_XGRAPH
// xgraph
#include "nodeAndConnectedEdgeDist.h"
#include "nodeAndConnectedEdge.h"
#endif
......
......@@ -34,7 +34,6 @@ void TLSFastMarchingValGradMode(std::function < double (AOMD::mVertex *) > get_v
{
// local
typedef xfastmarching::vector3d < double > vect_t;
double valx;
double *pvalx;
vect_t vectx;
vect_t *pvectx;
......@@ -61,7 +60,6 @@ void TLSFastMarchingValGradMode(std::function < double (AOMD::mVertex *) > get_v
{
// suplementary interface container object for FM
datamanagerFM_t < xlinalg::xSparseVector < double > > vn;
xlinalg::xSparseVector < double > *pmv = nullptr;
// set known values and mode initial value
......@@ -86,9 +84,10 @@ void TLSFastMarchingValGradMode(std::function < double (AOMD::mVertex *) > get_v
// output graph
if (output)
{
ofstream ofs("flux_to.dot");
xgraph::exportDot < gn_t >(flux_to
,ofs
,"flux_to.dot"
,MPI_COMM_SELF
,[](const AOMD::mVertex *key) {return static_cast < unsigned int >( key->getId()); }
,[](const AOMD::mVertex *key) {return static_cast < unsigned int >( key->getId()); }
,[](const double w) {return static_cast < unsigned int >( w*100.+1. ); }
);
......@@ -208,7 +207,7 @@ void TLSFastMarchingValGradMode(std::function < double (AOMD::mVertex *) > get_v
unsigned int *ip = nodal_index.getData(*parent_key);
assert(ip);
double source;
if (*ip == i_modes)
if (*ip == static_cast<unsigned int>(i_modes))
source = 1.;
else
{
......@@ -228,6 +227,7 @@ void TLSFastMarchingValGradMode(std::function < double (AOMD::mVertex *) > get_v
coeffs->addMatrix(i_modes,*j,source*node.second);
return true;
}
return true;
});
++i_modes;
......@@ -394,7 +394,7 @@ void TLSFastMarchingValGradMode(std::function < double (AOMD::mVertex *) > get_v
for (auto ef : to_find)
{
auto vf = static_cast < AOMD::mVertex * >( ef );
if ((pmv=vn.getData(*vf)))
if (( pmv = vn.getData(*vf)))
{
xtool::xRange < xlinalg::xSparseVector < double >::iter_idx_t > idx(pmv->beginIdx(),pmv->endIdx());
for (auto i : idx)
......@@ -506,7 +506,7 @@ void TLSFastMarchingValGradMode(std::function < double (AOMD::mVertex *) > get_v
if ( filter_func(e) )
{
auto v = static_cast < AOMD::mVertex * >( e );
if ((pmv=vn.getData(*v)))
if (( pmv = vn.getData(*v)))
{
for_each(pmv->beginIdx(),pmv->endIdx(),[&](int i)
{
......@@ -530,7 +530,7 @@ void TLSFastMarchingValGradMode(std::function < double (AOMD::mVertex *) > get_v
throw -1;
if (( pvectx = gls->getData(*v)))
set_grad(v,xtensor::xVector < >(( *pvectx )[0],( *pvectx )[1],( *pvectx )[2]), *pvalx );
if ((pmv=vn.getData(*v)))
if (( pmv = vn.getData(*v)))
{
for_each(pmv->beginIdx(),pmv->endIdx(),[&](int i)
{
......
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