Commit 51195c11 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Fix copy_property() to allow copying between non-identical graphs

Now properties can be copied from vertices which are not identical
(e.g. from unfiltered to filtered graphs, etc). This fixes a problem
with graph copying and pruning, where the properties were not correclty
copied.
parent 679bfe41
......@@ -24,6 +24,7 @@ libgraph_tool_core_la_SOURCES = \
graph_filtering.cc \
graph_io.cc \
graph_properties.cc \
graph_properties_copy.cc \
graph_properties_group.cc \
graph_properties_ungroup.cc \
graph_python_interface.cc \
......@@ -46,6 +47,7 @@ libgraph_tool_core_la_include_HEADERS = \
graph_adaptor.hh \
graph_wrap.hh \
graph_filtering.hh \
graph_exceptions.hh \
graph.hh \
graph_properties.hh \
graph_properties_group.hh \
......
......@@ -30,6 +30,7 @@
#include <boost/python/dict.hpp>
#include <boost/mpl/vector.hpp>
#include "graph_properties.hh"
#include "graph_exceptions.hh"
namespace graph_tool
{
......@@ -210,36 +211,6 @@ private:
bool _edge_filter_active;
};
// Exceptions
// ==========
//
// This is the main exception which will be thrown the outside world, when
// things go wrong
class GraphException : public std::exception
{
string _error;
public:
GraphException(const string& error) {_error = error;}
virtual ~GraphException() throw () {}
virtual const char * what () const throw () {return _error.c_str();}
protected:
virtual void SetError(const string& error) {_error = error;}
};
class IOException : public GraphException
{
public:
IOException(const string& error): GraphException(error) {}
};
class ValueException : public GraphException
{
public:
ValueException(const string& error): GraphException(error) {}
};
} //namespace graph_tool
#endif
......
......@@ -91,206 +91,3 @@ GraphInterface::GraphInterface(const GraphInterface& gi, bool keep_ref)
// filters will be copied in python
}
//
// Property map copying
// ====================
// handle type convertions
// generic types
template <class Type1, class Type2>
struct convert
{
Type1 operator()(const Type2& v) const
{
return do_convert(v, is_convertible<Type2,Type1>());
}
Type1 do_convert(const Type2& v, mpl::bool_<true>) const
{
return Type1(v);
}
Type1 do_convert(const Type2& v, mpl::bool_<false>) const
{
return specific_convert<Type1,Type2>()(v);
}
template <class T1, class T2>
struct specific_convert
{
T1 operator()(const T2& v) const
{
throw bad_lexical_cast(); // default action
}
};
// specific specializations
// python::object
template <class T1>
struct specific_convert<T1,python::object>
{
T1 operator()(const python::object& v) const
{
python::extract<Type1> x(v);
if (x.check())
return x();
else
throw bad_lexical_cast();
}
};
// string
template <class T1>
struct specific_convert<T1,string>
{
T1 operator()(const string& v) const
{
//uint8_t is not char, it is bool!
if (is_same<T1, uint8_t>::value)
return convert<T1,int>()(lexical_cast<int>(v));
else
return lexical_cast<Type1>(v);
}
};
template <class T2>
struct specific_convert<string,T2>
{
string operator()(const T2& v) const
{
//uint8_t is not char, it is bool!
if (is_same<T2, uint8_t>::value)
return lexical_cast<string>(convert<int,T2>()(v));
else
return lexical_cast<string>(v);
}
};
// vectors
template <class T1, class T2>
struct specific_convert<vector<T1>, vector<T2> >
{
vector<T1> operator()(const vector<T2>& v) const
{
vector<T1> v2(v.size());
convert<T1,T2> c;
for (size_t i = 0; i < v.size(); ++i)
v2[i] = c(v[i]);
return v2;
}
};
};
// python::object to string, to solve ambiguity
template<> template<>
struct convert<string,python::object>::specific_convert<string,python::object>
{
string operator()(const python::object& v) const
{
python::extract<string> x(v);
if (x.check())
return x();
else
throw bad_lexical_cast();
}
};
template <class IteratorSel>
struct copy_property
{
template <class Graph, class PropertySrc,
class PropertyTgt>
void operator()(const Graph& tgt, const Graph& src, PropertySrc src_map,
PropertyTgt dst_map) const
{
typedef typename property_traits<PropertySrc>::value_type val_src;
typedef typename property_traits<PropertyTgt>::value_type val_tgt;
try
{
convert<val_tgt,val_src> c;
typename IteratorSel::template apply<Graph>::type vs, vs_end;
typename IteratorSel::template apply<Graph>::type vt, vt_end;
tie(vt, vt_end) = IteratorSel::range(tgt);
for (tie(vs, vs_end) = IteratorSel::range(src); vs != vs_end; ++vs)
{
if (vt == vt_end)
throw ValueException("Error copying properties: "
"graphs not identical");
dst_map[*vt] = c(src_map[*vs]);
++vt;
}
}
catch (bad_lexical_cast&)
{
throw ValueException("property values are not convertible");
}
}
};
struct edge_selector
{
template <class Graph>
struct apply
{
typedef typename graph_traits<Graph>::edge_iterator type;
};
template <class Graph>
static pair<typename apply<Graph>::type,
typename apply<Graph>::type>
range(Graph& g)
{
return edges(g);
}
};
struct vertex_selector
{
template <class Graph>
struct apply
{
typedef typename graph_traits<Graph>::vertex_iterator type;
};
template <class Graph>
static pair<typename apply<Graph>::type,
typename apply<Graph>::type>
range(Graph& g)
{
return vertices(g);
}
};
typedef mpl::vector<GraphInterface::multigraph_t> unfiltered;
void GraphInterface::CopyVertexProperty(const GraphInterface& src,
boost::any prop_src,
boost::any prop_tgt)
{
typedef edge_properties writable_edge_properties;
run_action<unfiltered>()
(*this, bind<void>(copy_property<vertex_selector>(),
_1, ref(src._state->_mg), _2, _3),
vertex_properties(), writable_vertex_properties())
(prop_src, prop_tgt);
}
void GraphInterface::CopyEdgeProperty(const GraphInterface& src,
boost::any prop_src,
boost::any prop_tgt)
{
typedef edge_properties writable_edge_properties;
run_action<unfiltered>()
(*this, bind<void>(copy_property<edge_selector>(),
_1, ref(src._state->_mg), _2, _3),
edge_properties(), writable_edge_properties())
(prop_src, prop_tgt);
}
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2007-2011 Tiago de Paula Peixoto <tiago@skewed.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 3
// 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, see <http://www.gnu.org/licenses/>.
#ifndef GRAPH_EXCEPTIONS_HH
#define GRAPH_EXCEPTIONS_HH
#include "config.h"
#include <string>
// Exceptions
// ==========
//
// This is the main exception which will be thrown the outside world, when
// things go wrong
namespace graph_tool{
using namespace std;
class GraphException : public std::exception
{
string _error;
public:
GraphException(const string& error) {_error = error;}
virtual ~GraphException() throw () {}
virtual const char * what () const throw () {return _error.c_str();}
protected:
virtual void SetError(const string& error) {_error = error;}
};
class IOException : public GraphException
{
public:
IOException(const string& error): GraphException(error) {}
};
class ValueException : public GraphException
{
public:
ValueException(const string& error): GraphException(error) {}
};
} // namespace std
#endif // GRAPH_EXCEPTIONS_HH
......@@ -34,6 +34,7 @@
#endif
#include <boost/functional/hash.hpp>
#include <boost/python/object.hpp>
#include <boost/python/extract.hpp>
#include <boost/version.hpp>
#if (BOOST_VERSION >= 104000)
......@@ -51,6 +52,7 @@
#include <boost/bind.hpp>
#include "graph.hh"
#include "graph_exceptions.hh"
// this file provides general functions for manipulating graph properties
......@@ -257,6 +259,110 @@ vector<string> get_type_name<TypeSequence,NamedSequence>::_all_names;
// Extra Property Map Types
// ========================
// handle type convertions
// generic types
template <class Type1, class Type2>
struct convert
{
Type1 operator()(const Type2& v) const
{
return do_convert(v, is_convertible<Type2,Type1>());
}
Type1 do_convert(const Type2& v, mpl::bool_<true>) const
{
return Type1(v);
}
Type1 do_convert(const Type2& v, mpl::bool_<false>) const
{
return specific_convert<Type1,Type2>()(v);
}
template <class T1, class T2>
struct specific_convert
{
T1 operator()(const T2& v) const
{
throw bad_lexical_cast(); // default action
}
};
// specific specializations
// python::object
template <class T1>
struct specific_convert<T1,python::object>
{
T1 operator()(const python::object& v) const
{
python::extract<Type1> x(v);
if (x.check())
return x();
else
throw bad_lexical_cast();
}
};
// string
template <class T1>
struct specific_convert<T1,string>
{
T1 operator()(const string& v) const
{
//uint8_t is not char, it is bool!
if (is_same<T1, uint8_t>::value)
return convert<T1,int>()(lexical_cast<int>(v));
else
return lexical_cast<Type1>(v);
}
};
template <class T2>
struct specific_convert<string,T2>
{
string operator()(const T2& v) const
{
//uint8_t is not char, it is bool!
if (is_same<T2, uint8_t>::value)
return lexical_cast<string>(convert<int,T2>()(v));
else
return lexical_cast<string>(v);
}
};
// vectors
template <class T1, class T2>
struct specific_convert<vector<T1>, vector<T2> >
{
vector<T1> operator()(const vector<T2>& v) const
{
vector<T1> v2(v.size());
convert<T1,T2> c;
for (size_t i = 0; i < v.size(); ++i)
v2[i] = c(v[i]);
return v2;
}
};
};
// python::object to string, to solve ambiguity
template<> template<>
struct convert<string,python::object>::specific_convert<string,python::object>
{
string operator()(const python::object& v) const
{
python::extract<string> x(v);
if (x.check())
return x();
else
throw bad_lexical_cast();
}
};
// the following class wraps a generic property map, so it can be used as a
// property with a given Key and Value type. The keys and values are converted
// to the desired Key and Value type, which may cause a performance impact,
......@@ -308,22 +414,57 @@ private:
{
public:
ValueConverterImp(PropertyMap pmap): _pmap(pmap) {}
typedef typename property_traits<PropertyMap>::value_type val_t;
typedef typename property_traits<PropertyMap>::key_type key_t;
virtual Value get(const Key& k)
{
typedef typename property_traits<PropertyMap>::key_type key_t;
return boost::get(_pmap, key_t(k));
return get_dispatch(_pmap, k,
is_convertible<typename property_traits<PropertyMap>::category,
readable_property_map_tag>());
}
virtual void put(const Key& k, const Value& val)
{
typedef typename property_traits<PropertyMap>::key_type key_t;
typedef typename property_traits<PropertyMap>::value_type val_t;
boost::put(_pmap, key_t(k), val_t(val));
return put_dispatch(_pmap, k, _c_put(val),
is_convertible<typename property_traits<PropertyMap>::category,
writable_property_map_tag>());
}
template <class PMap>
Value get_dispatch(PMap pmap, const typename property_traits<PMap>::key_type& k,
mpl::bool_<true>)
{
return _c_get(boost::get(pmap, k));
}
template <class PMap>
Value get_dispatch(PMap pmap, const typename property_traits<PMap>::key_type& k,
mpl::bool_<false>)
{
throw graph_tool::ValueException("Property map is not readable.");
}
template <class PMap>
void put_dispatch(PMap pmap, const typename property_traits<PMap>::key_type& k,
typename property_traits<PMap>::value_type val,
mpl::bool_<true>)
{
boost::put(pmap, k, val);
}
template <class PMap>
void put_dispatch(PMap pmap, const typename property_traits<PMap>::key_type& k,
typename property_traits<PMap>::value_type val,
mpl::bool_<false>)
{
throw ValueException("Property map is not writable.");
}
private:
PropertyMap _pmap;
convert<Value, val_t> _c_get;
convert<val_t, Value> _c_put;
};
struct choose_converter
......
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2007-2011 Tiago de Paula Peixoto <tiago@skewed.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 3
// 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, see <http://www.gnu.org/licenses/>.
#include <boost/mpl/contains.hpp>
#include <boost/python/extract.hpp>
#include "graph.hh"
#include "graph_filtering.hh"
#include "graph_properties.hh"
using namespace std;
using namespace boost;
using namespace graph_tool;
//
// Property map copying
// ====================
template <class IteratorSel, class PropertyMaps>
struct copy_property
{
template <class GraphTgt, class GraphSrc, class PropertyTgt>
void operator()(const GraphTgt& tgt, const GraphSrc* src,
PropertyTgt dst_map, boost::any prop_src) const
{
try
{
typedef typename property_traits<PropertyTgt>::value_type val_tgt;
typedef typename IteratorSel::template get_descriptor<GraphSrc>::type src_d;
DynamicPropertyMapWrap<val_tgt, src_d> src_map(prop_src, PropertyMaps());
typename IteratorSel::template apply<GraphSrc>::type vs, vs_end;
typename IteratorSel::template apply<GraphTgt>::type vt, vt_end;
tie(vt, vt_end) = IteratorSel::range(tgt);
for (tie(vs, vs_end) = IteratorSel::range(*src); vs != vs_end; ++vs)
{
if (vt == vt_end)
throw ValueException("Error copying properties: "
"graphs not compatible");
dst_map[*vt] = get(src_map, *vs);
++vt;
}
}
catch (bad_lexical_cast&)
{
throw ValueException("property values are not convertible");
}
}
};
struct edge_selector
{
template <class Graph>
struct apply
{
typedef typename graph_traits<Graph>::edge_iterator type;
};
template <class Graph>
struct get_descriptor
{
typedef typename graph_traits<Graph>::edge_descriptor type;
};
template <class Graph>
static pair<typename apply<Graph>::type,
typename apply<Graph>::type>
range(Graph& g)
{
return edges(g);
}
};
struct vertex_selector
{
template <class Graph>
struct apply
{