Commit 05414a53 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

* Added community network generation

* Added line graph generation
* src/graph-tool: some code de-uglification
* src/graph-tool: terminal output format changes


git-svn-id: https://svn.forked.de/graph-tool/trunk@56 d4600afd-f417-0410-95de-beed9576f240
parent 7287af55
......@@ -35,7 +35,16 @@ import string
import time
import signal
from time import *
import math
from math import *
try:
from scipy import *
from scipy.special import *
from scipy.optimize import *
from scipy.optimize.minpack import *
except ImportError:
pass
#import gc # garbage collector
#gc.enable()
......@@ -107,6 +116,7 @@ statistics.add_option("--average-vertex-property", action="callback", callback=p
statistics.add_option("--average-edge-property", action="callback", callback=push_option, type="string", metavar="PROPERTY|FILE", help="get the average of the edge property")
statistics.add_option("--reciprocity", action="callback", callback=push_option, type="string", metavar="FILE", help="get the edge reciprocity")
statistics.add_option("--minimum-spanning-tree", action="callback", callback=push_option, type="string", metavar="[WEIGHT|]PROPERTY", help="mark the minimum spanning tree edges in PROPERTY")
statistics.add_option("--line-graph", action="callback", callback=push_option, type="string", metavar="FILE[|FORMAT]", help="mark the minimum spanning tree edges in PROPERTY")
correlations = parser.add_option_group("Correlations")
correlations.add_option("--average-combined-vertex-correlation", action="callback", callback=push_option, type="string", metavar="DEGREE1|DEGREE2|FILE", help="get the average of DEGREE2 in function of DEGREE1. Scalar properties are also accepted as DEGREE1 or DEGREE2")
......@@ -128,6 +138,7 @@ layout.add_option("--compute-gursoy-atun-layout", action="callback", callback=pu
community = parser.add_option_group("Community")
community.add_option("--community-structure", action="callback", callback=push_option, type="string", metavar="PROPERTY|OPTIONS", help="calculate the community structure and assign it to PROPERTY. Options are: g (default: 1.0), N (default: 1000), Tmin (default: 0.01), Tmax (default: 1.0), spins (default: number of vertices), corr_type (default: uncorrelated), weight, seed (default: from clock), verbose, history. The value of corr_type can be 'random', 'uncorrelated' or 'correlated'.")
community.add_option("--modularity", action="callback", callback=push_option, type="string", metavar="PROPERTY[|WEIGHT]|FILE", help="calculate the modularity, given a community partition specified by PROPERTY")
community.add_option("--community-graph", action="callback", callback=push_option, type="string", metavar="PROPERTY|FILE[|FORMAT]", help="obtain the graph of communities, given a community partition specified by PROPERTY")
layout = parser.add_option_group("History")
layout.add_option("--for", action="callback", callback=push_option, type="string", metavar="INIT|CONDITION|STEP", help="simplified scripting")
......@@ -225,65 +236,72 @@ def generate_graph(parameters):
seed = int(get_suboption("seed",parameters))
except ValueError, e:
raise OptionError(opt.name, e)
variables = dict()
# default packages to be included
exec """
try:
from math import *
from scipy import *
from scipy.special import *
from scipy.optimize import *
from scipy.optimize.minpack import *
import psyco
psyco.full()
except ImportError:
pass
""" in variables
# some predefined functions
exec """
def inv_poisson(p,m):
return round(fsolve(lambda k,l: gammaincc(k,l)-p, m, (m))-0.5+1e-15)
""" in variables
exec """
def inv_exponential(p,m):
return round(log(1-p)/log(float(m)/(m+1))-0.5+1e-15)
""" in variables
exec """
def inv_power_law(p,b):
return round((1-p)**(-1/(b-1)) - 1)
""" in variables
exec """
def step(x):
if x >= 0:
return 1.0
else:
return 0.0
""" in variables
variables = globals()
def inv_poisson(p,m):
return round(fsolve(lambda k,l: gammaincc(k,l)-p, m, (m))-0.5+1e-15)
variables["inv_poisson"] = inv_poisson
def inv_exponential(p,m):
return round(log(1-p)/log(float(m)/(m+1))-0.5+1e-15)
variables["inv_exponential"] = inv_exponential
def inv_power_law(p,b):
return round((1-p)**(-1/(b-1)) - 1)
variables["inv_power_law"] = inv_power_law
def step(x):
if x >= 0:
return 1.0
else:
return 0.0
variables["step"] = step
# read main funtions
exec "def pjk(j,k):\n return %s\n" % pjk in variables
exec "def pjk_ceil(j,k):\n return %s\n" % pjk_ceil in variables
exec "m_pjk = %s" % m_pjk in variables
m_pjk = float(variables["m_pjk"])
def pjk_f(j,k):
return eval("%s" % pjk, variables, locals())
variables["pjk"] = pjk_f
def pjk_ceil_f(j,k):
return eval("%s" % pjk_ceil, variables, locals())
variables["pjk_ceil"] = pjk_ceil_f
m_pjk = float(m_pjk)
variables["m_pjk"] = m_pjk
if "file:" in inv_pjk_ceil:
exec open(inv_pjk_ceil.replace("file:","").strip()).read() in variables
else:
exec """
def inv_pjk_ceil(p,r):
retval = %s
return (int(round(retval[0])),int(round(retval[1])))
""" % inv_pjk_ceil in variables
exec "def corr(jl,kl,j,k):\n return %s\n" % corr in variables
exec "def corr_ceil(jl,kl,j,k):\n return %s\n" % corr_ceil in variables
exec "m_corr = %s" % m_corr in variables
m_corr = float(variables["m_corr"])
def inv_pjk_ceil_f(p,r):
variables["p"] = p
variables["r"] = r
retval = eval("%s" % inv_pjk_ceil, variables, locals())
del(variables["p"])
del(variables["r"])
return (int(round(retval[0])), int(round(retval[1])))
variables["inv_pjk_ceil"] = inv_pjk_ceil_f
def corr_f(jl,kl,j,k):
return eval("%s" % corr, variables, locals())
variables["corr"] = corr_f
def corr_ceil_f(jl,kl,j,k):
return eval("%s" % corr_ceil, variables, locals())
variables["corr_ceil"] = corr_ceil_f
m_corr = float(m_corr)
variables["m_corr"] = m_corr
if "file:" in inv_corr_ceil:
exec open(inv_corr_ceil.replace("file:","").strip()).read() in variables
else:
exec """
def inv_corr_ceil(p,r,j,k):
retval = %s
return (int(round(retval[0])),int(round(retval[1])))
""" % inv_corr_ceil in variables
def inv_corr_ceil_f(p,r,j,k):
retval = eval("%s" % inv_corr_ceil, variables, locals())
return (int(round(retval[0])), int(round(retval[1])))
variables["inv_corr_ceil"] = inv_corr_ceil_f
graph.GenerateCorrelatedConfigurationalModel(N, variables["pjk"], variables["pjk_ceil"], variables["inv_pjk_ceil"], m_pjk, variables["corr"],
variables["corr_ceil"], variables["inv_corr_ceil"], m_corr, undirected, seed, verbose)
......@@ -440,6 +458,17 @@ def parse_option(opt, just_file=False):
weight = values[0]
prop = values[1]
graph.GetMinimumSpanningTree(weight,prop)
elif opt.name == "line-graph":
values = parse_values(opt.value)
if len(values) < 1 or len(values) > 2:
raise OptionError(opt.name, "invalid value '%s'" % opt.value)
if just_file:
return None
if len(values) > 1:
format = values[1]
else:
format = ""
graph.GetLineGraph(values[0], format)
elif opt.name == "vertex-correlation-histogram":
values = parse_values(opt.value)
if len(values) != 3:
......@@ -588,6 +617,18 @@ def parse_option(opt, just_file=False):
if just_file:
return file_name
return (graph.GetModularity(weight, prop), file_name)
elif opt.name == "community-graph":
values = parse_values(opt.value)
if len(values) < 2 or len(values) > 3:
raise OptionError(opt.name, "invalid value '%s'" % opt.value)
prop, file_name = values[0], values[1]
if len(values) > 2:
format = values[2]
else:
format = ""
if just_file:
return
return (graph.GetCommunityNetwork(prop, file_name, format))
elif opt.name == "compute-spring-block-layout":
if just_file:
return None
......@@ -837,8 +878,13 @@ try:
if retval[1] != None:
prefix = ""
if retval[1] == "-":
prefix = opt.name.replace("-"," ")+": "
if opt.value.strip() != "-":
print "#",opt.name.replace("-"," "), "("+opt.value.replace("|",", ").replace(", -","")+"):"
else:
print "#",opt.name.replace("-"," ")+":"
write_data(retval[0], open_file(retval[1]), prefix)
if retval[1] == "-":
print
# deal with history
if history_range != None:
......
......@@ -37,6 +37,8 @@ libgraph_tool_la_SOURCES = \
graph_reciprocity.cc\
graph_minimum_spanning_tree.cc\
graph_community.cc\
graph_community_network.cc\
graph_line_graph.cc\
graph_io.cc\
graph_bind.cc\
graphml.hpp\
......
......@@ -95,6 +95,7 @@ public:
hist_t GetSampledDistanceHistogram(std::string weight, size_t samples, size_t seed) const;
double GetReciprocity() const;
void GetMinimumSpanningTree(std::string weight, std::string property);
void GetLineGraph(std::string out_file, std::string format);
// community structure
enum comm_corr_t
......@@ -106,6 +107,7 @@ public:
void GetCommunityStructure(double gamma, comm_corr_t corr, size_t n_iter, double Tmin, double Tmax, size_t Nseeds, size_t seed, bool verbose, std::string history_file, std::string weight, std::string property);
double GetModularity(std::string weight, std::string property);
void GetCommunityNetwork(std::string property, std::string out_file, std::string format) const;
// filtering
void SetDirected(bool directed) {_directed = directed;}
......
......@@ -237,7 +237,9 @@ BOOST_PYTHON_MODULE(libgraph_tool)
.def("GetSampledDistanceHistogram", &GraphInterfaceWrap::GetSampledDistanceHistogram)
.def("GetReciprocity", &GraphInterfaceWrap::GetReciprocity)
.def("GetMinimumSpanningTree", &GraphInterfaceWrap::GetMinimumSpanningTree)
.def("GetLineGraph", &GraphInterfaceWrap::GetLineGraph)
.def("GetCommunityStructure", &GraphInterfaceWrap::GetCommunityStructure)
.def("GetCommunityNetwork", &GraphInterfaceWrap::GetCommunityNetwork)
.def("GetModularity", &GraphInterfaceWrap::GetModularity)
.def("SetDirected", &GraphInterfaceWrap::SetDirected)
.def("GetDirected", &GraphInterfaceWrap::GetDirected)
......
// 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.
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <tr1/unordered_set>
#include <iostream>
#include <iomanip>
#include "graph.hh"
#include "histogram.hh"
#include "graph_filtering.hh"
#include "graph_selectors.hh"
#include "graph_properties.hh"
#include <boost/graph/graphviz.hpp>
#include "graphml.hpp"
using namespace std;
using namespace boost;
using namespace boost::lambda;
using namespace graph_tool;
//==============================================================================
// GetCommunityNetwork()
// retrieves the network of communities given a community structure
//==============================================================================
struct get_community_network
{
template <class Graph, class CommunityMap>
void operator()(const Graph& g, CommunityMap s, string property, dynamic_properties& edge_properties, string file, string format) const
{
tr1::unordered_set<typename boost::property_traits<CommunityMap>::value_type> comms;
typename graph_traits<Graph>::vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(g); v != v_end; ++v)
comms.insert(get(s, *v));
typedef boost::property<edge_index_t, size_t> EdgeProperty;
typedef adjacency_list <vecS, // edges
vecS, // vertices
typename mpl::if_<typename is_convertible<typename graph_traits<Graph>::directed_category, undirected_tag>::type,
bidirectionalS,
undirectedS>::type,
no_property,
EdgeProperty > comm_graph_t;
comm_graph_t comm_graph;
typedef typename property_map<comm_graph_t, vertex_index_t>::type comm_vertex_index_map_t;
comm_vertex_index_map_t comm_vertex_index(get(vertex_index, comm_graph));
vector_property_map<typename property_traits<CommunityMap>::value_type, comm_vertex_index_map_t> comm_map(comm_vertex_index);
tr1::unordered_map<typename property_traits<CommunityMap>::value_type, typename graph_traits<comm_graph_t>::vertex_descriptor> comm_vertex_map;
for (typeof(comms.begin()) iter = comms.begin(); iter != comms.end(); ++iter)
{
comm_vertex_map[*iter] = add_vertex(comm_graph);
comm_map[comm_vertex_map[*iter]] = *iter;
}
dynamic_properties dp;
dp.property(property, comm_map);
typedef typename property_map<comm_graph_t,edge_index_t>::type comm_edge_index_map_t;
comm_edge_index_map_t comm_edge_index(get(edge_index_t(), comm_graph));
typedef HashedDescriptorMap<comm_edge_index_map_t, typename graph_traits<Graph>::edge_descriptor> edge_map_t;
edge_map_t edge_map(comm_edge_index);
size_t e_index = 0;
typename graph_traits<Graph>::edge_iterator e, e_end;
for (tie(e, e_end) = edges(g); e != e_end; ++e)
{
typename graph_traits<comm_graph_t>::edge_descriptor edge;
edge = add_edge(comm_vertex_map[get(s,source(*e, g))], comm_vertex_map[get(s,target(*e, g))], comm_graph).first;
edge_map[edge] = *e;
comm_edge_index[edge] = e_index++;
}
for (typeof(edge_properties.begin()) iter = edge_properties.begin(); iter != edge_properties.end(); ++iter)
dp.insert(iter->first, auto_ptr<dynamic_property_map>(new dynamic_property_map_wrap<edge_map_t>(edge_map, *iter->second)));
bool graphviz = false;
if (format == "")
graphviz = ends_with(file,".dot") || ends_with(file,".dot.gz") || ends_with(file,".dot.bz2");
else if (format == "dot")
graphviz = true;
else if (format != "xml")
throw GraphException("error writing to file '" + file + "': requested invalid format '" + format + "'");
try
{
iostreams::filtering_stream<iostreams::output> stream;
ofstream file_stream;
if (file == "-")
stream.push(cout);
else
{
file_stream.open(file.c_str(), ios_base::out | ios_base::binary);
file_stream.exceptions(ios_base::badbit | ios_base::failbit);
if (ends_with(file,".gz"))
stream.push(iostreams::gzip_compressor());
if (ends_with(file,".bz2"))
stream.push(iostreams::bzip2_compressor());
stream.push(file_stream);
}
stream.exceptions(ios_base::badbit | ios_base::failbit);
if (graphviz)
{
dp.property("vertex_id", comm_vertex_index);
write_graphviz(stream, comm_graph, dp, string("vertex_id"));
}
else
{
write_graphml(stream, comm_graph, comm_vertex_index, dp, true);
}
stream.reset();
}
catch (ios_base::failure &e)
{
throw GraphException("error writing to file '" + file + "':" + e.what());
}
}
template <class EdgeMap>
class dynamic_property_map_wrap: public dynamic_property_map
{
public:
dynamic_property_map_wrap(EdgeMap& edge_map, dynamic_property_map& dm): _edge_map(edge_map), _dm(dm) {}
any get(const any& key)
{
return _dm.get(_edge_map[any_cast<typename property_traits<EdgeMap>::key_type>(key)]);
}
string get_string(const any& key)
{
return _dm.get_string(_edge_map[any_cast<typename property_traits<EdgeMap>::key_type>(key)]);
}
void put(const any& key, const any& value)
{
return _dm.put(_edge_map[any_cast<typename property_traits<EdgeMap>::key_type>(key)], value);
}
const type_info& key() const
{
return typeid(typename property_traits<EdgeMap>::key_type);
}
const type_info& value() const
{
return _dm.value();
}
private:
EdgeMap& _edge_map;
dynamic_property_map& _dm;
};
};
void GraphInterface::GetCommunityNetwork(string property, string out_file, string format) const
{
try
{
typedef DynamicPropertyMapWrap<double, graph_traits<multigraph_t>::vertex_descriptor> comm_map_t;
comm_map_t comm_map(find_property_map(_properties, property, typeid(graph_traits<multigraph_t>::vertex_descriptor)));
dynamic_properties_copy edge_properties;
for (typeof(_properties.begin()) iter = _properties.begin(); iter != _properties.end(); ++iter)
if (iter->second->key() == typeid(graph_traits<multigraph_t>::edge_descriptor))
edge_properties.insert(iter->first, auto_ptr<dynamic_property_map>(iter->second));
check_filter(*this, bind<void>(get_community_network(), _1, var(comm_map), property, var(edge_properties), out_file, format), reverse_check(), directed_check());
}
catch (property_not_found)
{
throw GraphException("edge property " + property + " not found");
}
}
// 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.
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <tr1/unordered_set>
#include <iostream>
#include <iomanip>
#include "graph.hh"
#include "histogram.hh"
#include "graph_filtering.hh"
#include "graph_selectors.hh"
#include "graph_properties.hh"
#include <boost/graph/graphviz.hpp>
#include "graphml.hpp"
using namespace std;
using namespace boost;
using namespace boost::lambda;
using namespace graph_tool;
//==============================================================================
// GetLineGraph()
// retrieves the line graph
//==============================================================================
struct get_line_graph
{
template <class Graph, class EdgeIndexMap>
void operator()(const Graph& g, EdgeIndexMap edge_index, dynamic_properties& properties, string file, string format) const
{
typedef boost::property<edge_index_t, size_t> EdgeProperty;
typedef adjacency_list <vecS, // edges
vecS, // vertices
undirectedS,
no_property,
EdgeProperty > line_graph_t;
line_graph_t line_graph;
typedef typename property_map<line_graph_t, vertex_index_t>::type line_vertex_index_map_t;
line_vertex_index_map_t line_vertex_index(get(vertex_index, line_graph));
typedef HashedDescriptorMap<line_vertex_index_map_t, typename graph_traits<Graph>::edge_descriptor> edge_map_t;
edge_map_t edge_map(line_vertex_index);
typedef HashedDescriptorMap<EdgeIndexMap, typename graph_traits<line_graph_t>::vertex_descriptor> edge_to_vertex_map_t;
edge_to_vertex_map_t edge_to_vertex_map(edge_index);
typename graph_traits<Graph>::edge_iterator e, e_end;
for (tie(e, e_end) = edges(g); e != e_end; ++e)
{
typename graph_traits<line_graph_t>::vertex_descriptor v;
v = add_vertex(line_graph);
edge_to_vertex_map[*e] = v;
edge_map[v] = *e;
}
typedef typename property_map<line_graph_t,edge_index_t>::type line_edge_index_map_t;
line_edge_index_map_t line_edge_index(get(edge_index_t(), line_graph));
typedef HashedDescriptorMap<line_edge_index_map_t, typename graph_traits<Graph>::vertex_descriptor> vertex_map_t;
vertex_map_t vertex_map(line_edge_index);
size_t e_index = 0;
typename graph_traits<Graph>::vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(g); v != v_end; ++v)
{
typename graph_traits<Graph>::out_edge_iterator e1, e2, e_end;
for (tie(e1, e_end) = out_edges(*v, g); e1 != e_end; ++e1)
for (tie(e2, e_end) = out_edges(*v, g); e2 != e_end; ++e2)
if (*e1 != *e2)
{
typename graph_traits<line_graph_t>::edge_descriptor new_edge;
new_edge = add_edge(edge_to_vertex_map[*e1], edge_to_vertex_map[*e2], line_graph).first;
line_edge_index[new_edge] = e_index++;
vertex_map[new_edge] = *v;
}
}
dynamic_properties dp;
for (typeof(properties.begin()) iter = properties.begin(); iter != properties.end(); ++iter)
{
if (iter->second->key() == typeid(typename graph_traits<Graph>::vertex_descriptor))
dp.insert(iter->first, auto_ptr<dynamic_property_map>(new dynamic_property_map_wrap<vertex_map_t>(vertex_map, *iter->second)));
else
dp.insert(iter->first, auto_ptr<dynamic_property_map>(new dynamic_property_map_wrap<edge_map_t>(edge_map, *iter->second)));
}
bool graphviz = false;
if (format == "")
graphviz = ends_with(file,".dot") || ends_with(file,".dot.gz") || ends_with(file,".dot.bz2");
else if (format == "dot")
graphviz = true;
else if (format != "xml")
throw GraphException("error writing to file '" + file + "': requested invalid format '" + format + "'");
try
{
iostreams::filtering_stream<iostreams::output> stream;
ofstream file_stream;
if (file == "-")
stream.push(cout);
else
{
file_stream.open(file.c_str(), ios_base::out | ios_base::binary);
file_stream.exceptions(ios_base::badbit | ios_base::failbit);
if (ends_with(file,".gz"))
stream.push(iostreams::gzip_compressor());
if (ends_with(file,".bz2"))
stream.push(iostreams::bzip2_compressor());
stream.push(file_stream);
}
stream.exceptions(ios_base::badbit | ios_base::failbit);
if (graphviz)
{
dp.property("vertex_id", line_vertex_index);
write_graphviz(stream, line_graph, dp, string("vertex_id"));
}
else
{
write_graphml(stream, line_graph, line_vertex_index, dp, true);
}
stream.reset();
}
catch (ios_base::failure &e)
{
throw GraphException("error writing to file '" + file + "':" + e.what());
}
}
template <class DescriptorMap>
class dynamic_property_map_wrap: public dynamic_property_map
{
public:
dynamic_property_map_wrap(DescriptorMap& edge_map, dynamic_property_map& dm): _descriptor_map(edge_map), _dm(dm) {}
any get(const any& key)