Commit 8f495045 authored by Tiago Peixoto's avatar Tiago Peixoto

* added extended clustering code

* separated graphml code into graphml.hpp and graphml.cpp, and added support for default property values
* added HashedDescriptorMap with automatic memory management



git-svn-id: https://svn.forked.de/graph-tool/trunk@36 d4600afd-f417-0410-95de-beed9576f240
parent d108f600
......@@ -115,6 +115,7 @@ correlations.add_option("--scalar-assortativity-coefficient", action="callback",
clustering = parser.add_option_group("Clustering")
clustering.add_option("--set-local-clustering-to-property", action="callback", callback=push_option, type="string", metavar="PROPERTY", help="set the local clustering coefficient to vertex property")
clustering.add_option("--global-clustering-coefficient", action="callback", callback=push_option, type="string", metavar="FILE", help="get the global clustering coefficient")
clustering.add_option("--set-extended-clustering-to-property", action="callback", callback=push_option, type="string", metavar="PROPERTY_PREFIX|MAX", help="set the extended clustering coefficients c0 to cMAX to vertex properties PROPERTY_PREFIX0 to PROPERTY_PREFIXMAX")
layout = parser.add_option_group("Layout")
layout.add_option("--compute-spring-block-layout", action="callback", callback=push_option, type="string", metavar="ITERATIONS[|SEED]", help="compute the spring block layout")
......@@ -419,6 +420,13 @@ def parse_option(opt, just_file=False):
if just_file:
return None
graph.SetLocalClusteringToProperty(opt.value)
elif opt.name == "set-extended-clustering-to-property":
if just_file:
return None
values = parse_values(opt.value)
if len(values) == 0 or len(values) > 2:
raise OptionError(opt.name, "invalid value '%s'" % opt.value)
graph.SetExtendedClusteringToProperty(values[0],int(values[1])+1)
elif opt.name == "compute-spring-block-layout":
if just_file:
return None
......
......@@ -30,11 +30,13 @@ libgraph_tool_la_SOURCES = \
graph_correlations_neighbours.cc\
graph_assortativity.cc\
graph_clustering.cc\
graph_extended_clustering.cc\
graph_generation.cc\
graph_distance.cc\
graph_io.cc\
graph_bind.cc\
graphml_io.hh\
graphml.hpp\
graphml.cpp\
histogram.hh\
read_graphviz_spirit.cpp
......
......@@ -84,8 +84,9 @@ public:
std::pair<double,double> GetScalarAssortativityCoefficient(deg_t deg) const;
//clustering
void SetLocalClusteringToProperty(std::string property);
void SetLocalClusteringToProperty(std::string property);
std::pair<double,double> GetGlobalClustering();
void SetExtendedClusteringToProperty(std::string property_prefix, size_t max_depth);
// other
hist_t GetComponentSizeHistogram() const;
......
......@@ -225,6 +225,7 @@ BOOST_PYTHON_MODULE(libgraph_tool)
.def("GetScalarAssortativityCoefficient", &GraphInterfaceWrap::GetScalarAssortativityCoefficient)
.def("GetGlobalClustering", &GraphInterfaceWrap::GetGlobalClustering)
.def("SetLocalClusteringToProperty", &GraphInterfaceWrap::SetLocalClusteringToProperty)
.def("SetExtendedClusteringToProperty", &GraphInterfaceWrap::SetExtendedClusteringToProperty)
.def("GetAverageDistance", &GraphInterfaceWrap::GetAverageDistance)
.def("GetAverageHarmonicDistance", &GraphInterfaceWrap::GetAverageHarmonicDistance)
.def("SetDirected", &GraphInterfaceWrap::SetDirected)
......
......@@ -171,7 +171,7 @@ struct set_clustering_to_property
void GraphInterface::SetLocalClusteringToProperty(string property)
{
typedef HashedDescriptorMap<vertex_index_map_t,double> clust_map_t;
clust_map_t clust_map(_vertex_index);
static clust_map_t clust_map(_vertex_index);
bool directed = _directed;
_directed = false;
......
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006 Tiago de Paula Peixoto <tiago@forked.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// based on code written by Alexandre Hannud Abdo <abdo@member.fsf.org>
#include <algorithm>
#include <tr1/unordered_set>
#include <boost/lambda/bind.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include "graph.hh"
#include "histogram.hh"
#include "graph_filtering.hh"
#include "graph_selectors.hh"
#include "graph_properties.hh"
using namespace std;
using namespace boost;
using namespace boost::lambda;
using namespace graph_tool;
// filters out a single vertex
template <class Vertex>
struct single_vertex_filter
{
single_vertex_filter() {}
single_vertex_filter(Vertex v):_v(v) {}
bool operator()(Vertex v) const
{
return v != _v;
}
Vertex _v;
};
class bfs_stop_exception {};
// this will abort the BFS search when no longer useful
template <class TargetSet, class DistanceMap>
struct bfs_max_depth_watcher
{
typedef on_tree_edge event_filter;
bfs_max_depth_watcher(TargetSet& targets, size_t max_depth, DistanceMap distance)
: _targets(targets), _max_depth(max_depth), _distance(distance) {}
template <class Graph>
void operator()(typename graph_traits<Graph>::edge_descriptor e, const Graph& g)
{
typename graph_traits<Graph>::vertex_descriptor v = target(e,g);
if (get(_distance, v) > _max_depth)
throw bfs_stop_exception();
if (_targets.find(v) != _targets.end())
_targets.erase(v);
if (_targets.empty())
throw bfs_stop_exception();
}
TargetSet& _targets;
size_t _max_depth;
DistanceMap _distance;
};
// this wraps a container as a property map which is automatically initialized
// with a given default value
template <class Container>
class InitializedPropertyMap
{
public:
typedef typename Container::value_type::second_type value_type;
typedef value_type& reference;
typedef typename Container::key_type key_type;
typedef boost::read_write_property_map_tag category;
InitializedPropertyMap(Container& base_map, value_type def)
: _base_map(&base_map), _default(def) {}
InitializedPropertyMap(){}
reference operator[](const key_type& k)
{
typename Container::iterator val;
val = _base_map->find(k);
if (val == _base_map->end())
{
val = _base_map->insert(make_pair(k, _default)).first;
}
return val->second;
}
private:
Container* _base_map;
value_type _default;
};
namespace boost
{
template <class Container>
void put(InitializedPropertyMap<Container>& m, const typename InitializedPropertyMap<Container>::key_type& key,
const typename InitializedPropertyMap<Container>::value_type& value)
{
m[key] = value;
}
template <class Container>
typename InitializedPropertyMap<Container>::value_type
get(InitializedPropertyMap<Container>& m, const typename InitializedPropertyMap<Container>::key_type& key)
{
return m[key];
}
}
struct get_extended_clustering
{
template <class Graph, class IndexMap, class ClusteringMap>
void operator()(Graph& g, IndexMap vertex_index, vector<ClusteringMap>& cmaps) const
{
typename graph_traits<Graph>::vertex_iterator v, v_end;
for (tie(v,v_end) = vertices(g); v != v_end; ++v)
{
// We must disconsider paths through the original vertex
typedef single_vertex_filter<typename graph_traits<Graph>::vertex_descriptor> filter_t;
typedef filtered_graph<Graph, keep_all, filter_t> fg_t;
fg_t fg(g, keep_all(), filter_t(*v));
typedef DescriptorHash<IndexMap> hasher_t;
typedef tr1::unordered_set<typename graph_traits<Graph>::vertex_descriptor,hasher_t> neighbour_set_t;
neighbour_set_t neighbours(0, hasher_t(vertex_index));
// collect the neighbours
typename graph_traits<Graph>::adjacency_iterator a, a_end;
for(tie(a, a_end) = adjacent_vertices(*v, g); a != a_end; ++a)
if (*a != *v) // no self-loops
neighbours.insert(*a);
size_t k = neighbours.size();
// And now we setup and start the BFS bonanza
for(tie(a, a_end) = adjacent_vertices(*v, g); a != a_end; ++a)
{
typedef tr1::unordered_map<typename graph_traits<Graph>::vertex_descriptor,size_t,DescriptorHash<IndexMap> > dmap_t;
dmap_t dmap(0, DescriptorHash<IndexMap>(vertex_index));
InitializedPropertyMap<dmap_t> distance_map(dmap, numeric_limits<size_t>::max());
typedef tr1::unordered_map<typename graph_traits<Graph>::vertex_descriptor,default_color_type,DescriptorHash<IndexMap> > cmap_t;
cmap_t cmap(0, DescriptorHash<IndexMap>(vertex_index));
InitializedPropertyMap<cmap_t> color_map(cmap, color_traits<default_color_type>::white());
try
{
bfs_max_depth_watcher<neighbour_set_t,InitializedPropertyMap<dmap_t> > watcher(neighbours, cmaps.size(), distance_map);
breadth_first_visit(fg, *a, visitor(make_bfs_visitor(make_pair(record_distances(distance_map, boost::on_tree_edge()),watcher))).
color_map(color_map));
}
catch(bfs_stop_exception) {}
typename graph_traits<Graph>::adjacency_iterator a2;
for(a2 = adjacent_vertices(*v, g).first ; a2 != a_end ; ++a2)
{
if (*a2 == *v) // no self-loops
continue;
if (distance_map[*a2] < cmaps.size())
{
cmaps[distance_map[*a2]][*v] += 1.0/(k*(k-1));
}
}
}
}
}
};
void GraphInterface::SetExtendedClusteringToProperty(string property_prefix, size_t max_depth)
{
typedef HashedDescriptorMap<vertex_index_map_t,double> cmap_t;
vector<cmap_t> cmaps(max_depth);
for (size_t i = 0; i < cmaps.size(); ++i)
cmaps[i] = cmap_t(_vertex_index);
bool directed = _directed;
_directed = false;
check_filter(*this, bind<void>(get_extended_clustering(), _1, _vertex_index, var(cmaps)), reverse_check(), always_undirected());
_directed = directed;
for (size_t i = 0; i < cmaps.size(); ++i)
{
string name = property_prefix + lexical_cast<string>(i);
try
{
find_property_map(_properties, name, typeid(graph_traits<multigraph_t>::vertex_descriptor));
RemoveVertexProperty(name);
}
catch (property_not_found) {}
_properties.property(name, cmaps[i]);
}
}
......@@ -276,20 +276,50 @@ private:
};
template <class IndexMap, class Value>
class HashedDescriptorMap:
public associative_property_map<std::tr1::unordered_map<typename IndexMap::key_type,Value,DescriptorHash<IndexMap> > >
class HashedDescriptorMap
{
public:
typedef DescriptorHash<IndexMap> hashfc_t;
typedef std::tr1::unordered_map<typename IndexMap::key_type,Value,hashfc_t> map_t;
typedef associative_property_map<map_t> prop_map_t;
HashedDescriptorMap(IndexMap index_map): prop_map_t(base_map), base_map(0, hashfc_t(index_map)) {}
typedef typename property_traits<prop_map_t>::value_type value_type;
typedef typename property_traits<prop_map_t>::reference reference;
typedef typename property_traits<prop_map_t>::key_type key_type;
typedef typename property_traits<prop_map_t>::category category;
HashedDescriptorMap(IndexMap index_map): _base_map(new map_t(0, hashfc_t(index_map))), _prop_map(*_base_map) {}
HashedDescriptorMap(){}
reference operator[](const key_type& k) { return _prop_map[k]; }
const reference operator[](const key_type& k) const { return _prop_map[k]; }
private:
map_t base_map;
shared_ptr<map_t> _base_map;
prop_map_t _prop_map;
};
} //namespace graph_tool
namespace boost
{
using namespace graph_tool;
template <class IndexMap, class Value>
typename HashedDescriptorMap<IndexMap,Value>::value_type
get(const HashedDescriptorMap<IndexMap,Value>& hmap, const typename HashedDescriptorMap<IndexMap,Value>::key_type& k)
{
return hmap[k];
}
template <class IndexMap, class Value>
void
put(HashedDescriptorMap<IndexMap,Value>& hmap, const typename HashedDescriptorMap<IndexMap,Value>::key_type& k,
const typename HashedDescriptorMap<IndexMap,Value>::value_type& value)
{
hmap[k] = value;
}
} //boost namespace
#endif
......@@ -24,7 +24,7 @@
#include "histogram.hh"
#include "graph_filtering.hh"
#include "graph_properties.hh"
#include "graphml_io.hh"
#include "graphml.hpp"
using namespace std;
using namespace boost;
......
......@@ -47,49 +47,15 @@
// Tiago de Paula Peixoto
#include "expat.h"
#include <boost/config.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/any.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/graph/graphviz.hpp> // for exceptions
#include <typeinfo>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/for_each.hpp>
#include <exception>
#include <sstream>
namespace boost
{
#include "graphml.hpp"
/////////////////////////////////////////////////////////////////////////////
// Graph reader exceptions
/////////////////////////////////////////////////////////////////////////////
struct parse_error : public graph_exception
{
parse_error(std::string error) {statement = "parse error: " + error;}
std::string statement;
virtual ~parse_error() throw() {}
const char* what() const throw() {return statement.c_str();}
};
using namespace boost;
template<typename MutableGraph>
class graphml_reader
{
typedef graphml_reader self_type;
typedef typename graph_traits<MutableGraph>::vertex_descriptor vertex_descriptor;
typedef typename graph_traits<MutableGraph>::edge_descriptor edge_descriptor;
typedef typename graph_traits<MutableGraph>::directed_category directed_category;
BOOST_STATIC_CONSTANT(bool,
graph_is_directed =
(is_convertible<directed_category*, directed_tag*>::value));
public:
graphml_reader(MutableGraph& g, dynamic_properties& dp)
: m_g(g), m_dp(dp), m_active_descriptor_is_vertex(false), m_canonical_vertices(false), m_canonical_edges(false) { }
graphml_reader(mutate_graph& g)
: m_g(g), m_active_descriptor_is_vertex(false), m_canonical_vertices(false), m_canonical_edges(false) { }
void run(std::istream& in)
{
......@@ -132,7 +98,7 @@ private:
on_start_element(void* user_data, const XML_Char *c_name,
const XML_Char **atts)
{
self_type* self = static_cast<self_type*>(user_data);
graphml_reader* self = static_cast<graphml_reader*>(user_data);
std::string name(c_name);
if (name == "key")
......@@ -169,6 +135,7 @@ private:
self->m_keys[id] = kind;
self->m_key_name[id] = key_name;
self->m_key_type[id] = key_type;
self->m_active_key = id;
}
else if (name == "node")
{
......@@ -201,7 +168,7 @@ private:
else if (name == "directed")
{
bool edge_is_directed = (value == "directed");
if (edge_is_directed != graph_is_directed)
if (edge_is_directed != self->m_g.is_directed())
{
if (edge_is_directed)
throw directed_graph_error();
......@@ -222,11 +189,10 @@ private:
std::string name = *atts++;
std::string value = *atts++;
if (name == "id") self->m_id = value;
else if (name == "edgedefault")
if (name == "edgedefault")
{
bool edge_is_directed = (value == "directed");
if (edge_is_directed != graph_is_directed)
if (edge_is_directed != self->m_g.is_directed())
{
if (edge_is_directed)
throw directed_graph_error();
......@@ -261,7 +227,7 @@ private:
static void
on_end_element(void* user_data, const XML_Char *c_name)
{
self_type* self = static_cast<self_type*>(user_data);
graphml_reader* self = static_cast<graphml_reader*>(user_data);
std::string name(c_name);
if (name == "data")
......@@ -269,19 +235,25 @@ private:
self->handle_property(self->m_active_key, self->m_active_descriptor,
self->m_active_descriptor_is_vertex,
self->m_character_data);
}
else if (name == "default")
{
self->m_key_default[self->m_active_key] = self->m_character_data;
}
}
static void
on_character_data(void* user_data, const XML_Char* s, int len)
{
self_type* self = static_cast<self_type*>(user_data);
graphml_reader* self = static_cast<graphml_reader*>(user_data);
self->m_character_data.append(s, len);
}
void
handle_vertex(const std::string& v)
{
bool is_new = false;
if (m_canonical_vertices)
{
size_t id;
......@@ -297,16 +269,32 @@ private:
}
while(id >= m_canonical_vertex.size())
m_canonical_vertex.push_back(add_vertex(m_g));
{
m_canonical_vertex.push_back(m_g.do_add_vertex());
is_new = true;
}
}
else
{
if (m_vertex.find(v) == m_vertex.end())
m_vertex[v] = add_vertex(m_g);
{
m_vertex[v] = m_g.do_add_vertex();
is_new = true;
}
}
if (is_new)
{
std::map<std::string, std::string>::iterator iter;
for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter)
{
if (m_keys[iter->first] == node_key)
handle_property(iter->first, v, true, iter->second);
}
}
}
vertex_descriptor
any
get_vertex_descriptor(const std::string& v)
{
if (m_canonical_vertices)
......@@ -327,15 +315,15 @@ private:
handle_vertex(u);
handle_vertex(v);
vertex_descriptor source, target;
any source, target;
source = get_vertex_descriptor(u);
target = get_vertex_descriptor(v);
edge_descriptor edge;
any edge;
bool added;
tie(edge, added) = add_edge(source,target,m_g);
tie(edge, added) = m_g.do_add_edge(source, target);
if (!added)
throw bad_parallel_edge(u,v);
throw bad_parallel_edge(u, v);
if (m_canonical_edges)
{
......@@ -358,72 +346,25 @@ private:
{
m_edge[e] = edge;
}
}
template <typename Key, typename ValueVector>
class put_property
{
public:
put_property(const std::string& name, dynamic_properties& dp, const Key& key,
const std::string& value, const std::string& value_type,
char** type_names, bool& type_found)
: m_name(name), m_dp(dp), m_key(key), m_value(value),
m_value_type(value_type), m_type_names(type_names),
m_type_found(type_found) {}
template <class Value>
void operator()(Value)
std::map<std::string, std::string>::iterator iter;
for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter)
{
if (m_value_type == m_type_names[mpl::find<ValueVector,Value>::type::pos::value])
{
put(m_name, m_dp, m_key, lexical_cast<Value>(m_value));
m_type_found = true;
}
if (m_keys[iter->first] == edge_key)
handle_property(iter->first, e, false, iter->second);
}
private:
const std::string& m_name;
dynamic_properties& m_dp;
const Key& m_key;
const std::string& m_value;
const std::string& m_value_type;
char** m_type_names;
bool& m_type_found;
};
}
void handle_property(std::string key_id, std::string descriptor,
bool is_vertex, std::string value)
{
typedef mpl::vector<bool, int, long, float, double, std::string> value_types;
char* type_names[] = {"bool", "int", "long", "float", "double", "string"};
bool type_found = false;
try
{
if (is_vertex)
mpl::for_each<value_types>
(put_property<vertex_descriptor,value_types>
(m_key_name[key_id], m_dp,
get_vertex_descriptor(descriptor),
value, m_key_type[key_id], type_names, type_found));
else
mpl::for_each<value_types>
(put_property<edge_descriptor,value_types>
(m_key_name[key_id], m_dp,
get_edge_descriptor(descriptor),
value, m_key_type[key_id], type_names, type_found));
}
catch (bad_lexical_cast)
{
throw parse_error("invalid value \"" + value + "\" for key " +
m_key_name[key_id] + " of type " +
m_key_type[key_id] );
}
if (!type_found)
throw parse_error("unrecognized type \"" + m_key_type[key_id] +
"\" for key " + m_key_name[key_id]);
if (is_vertex)
m_g.set_vertex_property(m_key_name[key_id], get_vertex_descriptor(descriptor), value, m_key_type[key_id]);