| 1 | // graph-tool -- a general graph modification and manipulation thingy |
|---|
| 2 | // |
|---|
| 3 | // Copyright (C) 2007-2010 Tiago de Paula Peixoto <tiago@forked.de> |
|---|
| 4 | // |
|---|
| 5 | // This program is free software; you can redistribute it and/or |
|---|
| 6 | // modify it under the terms of the GNU General Public License |
|---|
| 7 | // as published by the Free Software Foundation; either version 3 |
|---|
| 8 | // of the License, or (at your option) any later version. |
|---|
| 9 | // |
|---|
| 10 | // This program is distributed in the hope that it will be useful, |
|---|
| 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | // GNU General Public License for more details. |
|---|
| 14 | // |
|---|
| 15 | // You should have received a copy of the GNU General Public License |
|---|
| 16 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | |
|---|
| 18 | #include "graph_filtering.hh" |
|---|
| 19 | #include <boost/python/type_id.hpp> |
|---|
| 20 | |
|---|
| 21 | using namespace graph_tool; |
|---|
| 22 | using namespace graph_tool::detail; |
|---|
| 23 | using namespace boost; |
|---|
| 24 | |
|---|
| 25 | bool graph_tool::graph_filtering_enabled() |
|---|
| 26 | { |
|---|
| 27 | #ifndef NO_GRAPH_FILTERING |
|---|
| 28 | return true; |
|---|
| 29 | #else |
|---|
| 30 | return false; |
|---|
| 31 | #endif |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | // Whenever no implementation is called, the following exception is thrown |
|---|
| 35 | graph_tool::ActionNotFound::ActionNotFound(const boost::any& graph_view, |
|---|
| 36 | const type_info& action, |
|---|
| 37 | const vector<const type_info*>& args) |
|---|
| 38 | : GraphException(""), _graph_view(graph_view), |
|---|
| 39 | _action(action), _args(args) {} |
|---|
| 40 | |
|---|
| 41 | const char * graph_tool::ActionNotFound::what () const throw () |
|---|
| 42 | { |
|---|
| 43 | using python::detail::gcc_demangle; |
|---|
| 44 | |
|---|
| 45 | string error = |
|---|
| 46 | "No static implementation was found for the desired routine. " |
|---|
| 47 | "This is a graph_tool bug. :-( Please follow but report " |
|---|
| 48 | "instructions at " PACKAGE_BUGREPORT ". What follows is debug " |
|---|
| 49 | "information.\n\n"; |
|---|
| 50 | |
|---|
| 51 | error += "Graph view: " + string(gcc_demangle(_graph_view.type().name())) |
|---|
| 52 | + "\n\n"; |
|---|
| 53 | |
|---|
| 54 | error += "Action: " + string(gcc_demangle(_action.name())) + "\n\n"; |
|---|
| 55 | for (size_t i = 0; i < _args.size(); ++i) |
|---|
| 56 | { |
|---|
| 57 | error += "Arg " + lexical_cast<string>(i+1) + ": " + |
|---|
| 58 | string(gcc_demangle(_args[i]->name())) + "\n\n"; |
|---|
| 59 | } |
|---|
| 60 | return error.c_str(); |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | // this function retrieves a graph view stored in graph_views, or stores one if |
|---|
| 64 | // non-existent |
|---|
| 65 | template <class Graph> |
|---|
| 66 | typename remove_const<Graph>::type& |
|---|
| 67 | retrieve_graph(vector<boost::any>& graph_views, Graph& init) |
|---|
| 68 | { |
|---|
| 69 | typedef typename remove_const<Graph>::type g_t; |
|---|
| 70 | size_t index = mpl::find<all_graph_views,g_t>::type::pos::value; |
|---|
| 71 | if (index >= graph_views.size()) |
|---|
| 72 | graph_views.resize(index+1); |
|---|
| 73 | boost::any gview = graph_views[index]; |
|---|
| 74 | shared_ptr<g_t>* gptr = any_cast<shared_ptr<g_t> >(&gview); |
|---|
| 75 | if (gptr == 0) |
|---|
| 76 | { |
|---|
| 77 | shared_ptr<g_t> new_g(new g_t(init)); |
|---|
| 78 | gptr = &new_g; |
|---|
| 79 | gview = new_g; |
|---|
| 80 | graph_views[index] = gview; |
|---|
| 81 | } |
|---|
| 82 | return **gptr; |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | |
|---|
| 86 | // this will check whether a graph is reversed and return the proper view |
|---|
| 87 | // encapsulated |
|---|
| 88 | template <class Graph> |
|---|
| 89 | boost::any check_reverse(const Graph &g, bool reverse, |
|---|
| 90 | vector<boost::any>& graph_views) |
|---|
| 91 | { |
|---|
| 92 | if (reverse) |
|---|
| 93 | { |
|---|
| 94 | typedef typename mpl::if_<is_const<Graph>, |
|---|
| 95 | const reverse_graph |
|---|
| 96 | <typename remove_const<Graph>::type>, |
|---|
| 97 | reverse_graph<Graph> >::type |
|---|
| 98 | reverse_graph_t; |
|---|
| 99 | |
|---|
| 100 | reverse_graph_t rg(g); |
|---|
| 101 | return &retrieve_graph(graph_views, rg); |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | return boost::any(&const_cast<Graph&>(g)); |
|---|
| 105 | }; |
|---|
| 106 | |
|---|
| 107 | // this will check whether a graph is directed and return the proper view |
|---|
| 108 | // encapsulated |
|---|
| 109 | template <class Graph> |
|---|
| 110 | boost::any check_directed(const Graph &g, bool reverse, bool directed, |
|---|
| 111 | vector<boost::any>& graph_views) |
|---|
| 112 | { |
|---|
| 113 | if (directed) |
|---|
| 114 | { |
|---|
| 115 | return check_reverse(g, reverse, graph_views); |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | typedef UndirectedAdaptor<Graph> ug_t; |
|---|
| 119 | ug_t ug(g); |
|---|
| 120 | return &retrieve_graph(graph_views, ug); |
|---|
| 121 | }; |
|---|
| 122 | |
|---|
| 123 | // this will check whether a graph is filtered and return the proper view |
|---|
| 124 | // encapsulated |
|---|
| 125 | template <class Graph, class EdgeFilter, class VertexFilter> |
|---|
| 126 | boost::any |
|---|
| 127 | check_filtered(const Graph &g, const EdgeFilter& edge_filter, |
|---|
| 128 | const bool& e_invert, bool e_active, size_t max_eindex, |
|---|
| 129 | const VertexFilter& vertex_filter, const bool& v_invert, |
|---|
| 130 | bool v_active, vector<boost::any>& graph_views, bool reverse, |
|---|
| 131 | bool directed) |
|---|
| 132 | { |
|---|
| 133 | #ifndef NO_GRAPH_FILTERING |
|---|
| 134 | MaskFilter<EdgeFilter> e_filter(edge_filter, e_invert); |
|---|
| 135 | MaskFilter<VertexFilter> v_filter(vertex_filter, v_invert); |
|---|
| 136 | |
|---|
| 137 | if (e_active) |
|---|
| 138 | { |
|---|
| 139 | if (max_eindex > 0) |
|---|
| 140 | edge_filter.reserve(max_eindex+1); |
|---|
| 141 | if (v_active) |
|---|
| 142 | { |
|---|
| 143 | if (num_vertices(g) > 0) |
|---|
| 144 | vertex_filter.reserve(num_vertices(g)); |
|---|
| 145 | typedef filtered_graph<Graph, MaskFilter<EdgeFilter>, |
|---|
| 146 | MaskFilter<VertexFilter> > fg_t; |
|---|
| 147 | fg_t init(g, e_filter, v_filter); |
|---|
| 148 | fg_t& fg = retrieve_graph(graph_views, init); |
|---|
| 149 | fg.m_edge_pred = e_filter; |
|---|
| 150 | fg.m_vertex_pred = v_filter; |
|---|
| 151 | return check_directed(fg, reverse, directed, graph_views); |
|---|
| 152 | } |
|---|
| 153 | else |
|---|
| 154 | { |
|---|
| 155 | typedef filtered_graph<Graph, MaskFilter<EdgeFilter>, |
|---|
| 156 | keep_all> fg_t; |
|---|
| 157 | fg_t init(g, e_filter, keep_all()); |
|---|
| 158 | fg_t& fg = retrieve_graph(graph_views, init); |
|---|
| 159 | fg.m_edge_pred = e_filter; |
|---|
| 160 | return check_directed(fg, reverse, directed, graph_views); |
|---|
| 161 | } |
|---|
| 162 | } |
|---|
| 163 | else |
|---|
| 164 | { |
|---|
| 165 | if (v_active) |
|---|
| 166 | { |
|---|
| 167 | if (num_vertices(g) > 0) |
|---|
| 168 | vertex_filter.reserve(num_vertices(g)); |
|---|
| 169 | typedef filtered_graph<Graph, keep_all, |
|---|
| 170 | MaskFilter<VertexFilter> > fg_t; |
|---|
| 171 | fg_t init(g, keep_all(), v_filter); |
|---|
| 172 | fg_t& fg = retrieve_graph(graph_views, init); |
|---|
| 173 | fg.m_vertex_pred = v_filter; |
|---|
| 174 | return check_directed(fg, reverse, directed, graph_views); |
|---|
| 175 | } |
|---|
| 176 | else |
|---|
| 177 | { |
|---|
| 178 | return check_directed(g, reverse, directed, graph_views); |
|---|
| 179 | } |
|---|
| 180 | } |
|---|
| 181 | #else |
|---|
| 182 | return check_directed(g, reverse, directed, graph_views); |
|---|
| 183 | #endif |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | // gets the correct graph view at run time |
|---|
| 187 | boost::any GraphInterface::GetGraphView() const |
|---|
| 188 | { |
|---|
| 189 | // TODO: implement memoization |
|---|
| 190 | |
|---|
| 191 | boost::any graph = |
|---|
| 192 | check_filtered(_mg, _edge_filter_map, _edge_filter_invert, |
|---|
| 193 | _edge_filter_active, _max_edge_index, _vertex_filter_map, |
|---|
| 194 | _vertex_filter_invert, _vertex_filter_active, |
|---|
| 195 | const_cast<vector<boost::any>&>(_graph_views), |
|---|
| 196 | _reversed, _directed); |
|---|
| 197 | return graph; |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | // these test whether or not the vertex and edge filters are active |
|---|
| 201 | bool GraphInterface::IsVertexFilterActive() const |
|---|
| 202 | { return _vertex_filter_active; } |
|---|
| 203 | |
|---|
| 204 | bool GraphInterface::IsEdgeFilterActive() const |
|---|
| 205 | { return _edge_filter_active; } |
|---|
| 206 | |
|---|
| 207 | |
|---|
| 208 | // this function will reindex all the edges, in the order in which they are |
|---|
| 209 | // found |
|---|
| 210 | void GraphInterface::ReIndexEdges() |
|---|
| 211 | { |
|---|
| 212 | size_t index = 0; |
|---|
| 213 | graph_traits<multigraph_t>::edge_iterator e, e_end; |
|---|
| 214 | for (tie(e, e_end) = edges( _mg); e != e_end; ++e) |
|---|
| 215 | _edge_index[*e] = index++; |
|---|
| 216 | _max_edge_index = (index > 0) ? index - 1 : 0; |
|---|
| 217 | _nedges = index; |
|---|
| 218 | _free_indexes.clear(); |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | // this will definitively remove all the edges from the graph, which are being |
|---|
| 222 | // currently filtered out. This will also disable the edge filter |
|---|
| 223 | void GraphInterface::PurgeEdges() |
|---|
| 224 | { |
|---|
| 225 | if (!IsEdgeFilterActive()) |
|---|
| 226 | return; |
|---|
| 227 | |
|---|
| 228 | MaskFilter<edge_filter_t> filter(_edge_filter_map, _edge_filter_invert); |
|---|
| 229 | graph_traits<multigraph_t>::vertex_iterator v, v_end; |
|---|
| 230 | graph_traits<multigraph_t>::out_edge_iterator e, e_end; |
|---|
| 231 | vector<graph_traits<multigraph_t>::edge_descriptor> deleted_edges; |
|---|
| 232 | for (tie(v, v_end) = vertices(_mg); v != v_end; ++v) |
|---|
| 233 | { |
|---|
| 234 | for (tie(e, e_end) = out_edges(*v, _mg); e != e_end; ++e) |
|---|
| 235 | if (!filter(*e)) |
|---|
| 236 | deleted_edges.push_back(*e); |
|---|
| 237 | for (typeof(deleted_edges.begin()) iter = deleted_edges.begin(); |
|---|
| 238 | iter != deleted_edges.end(); ++iter) |
|---|
| 239 | RemoveEdgeIndex(*iter); |
|---|
| 240 | deleted_edges.clear(); |
|---|
| 241 | } |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | |
|---|
| 245 | // this will definitively remove all the verticess from the graph, which are |
|---|
| 246 | // being currently filtered out. This will also disable the vertex filter |
|---|
| 247 | void GraphInterface::PurgeVertices() |
|---|
| 248 | { |
|---|
| 249 | if (!IsVertexFilterActive()) |
|---|
| 250 | return; |
|---|
| 251 | |
|---|
| 252 | MaskFilter<vertex_filter_t> filter(_vertex_filter_map, |
|---|
| 253 | _vertex_filter_invert); |
|---|
| 254 | size_t N = num_vertices(_mg); |
|---|
| 255 | vector<bool> deleted(N, false); |
|---|
| 256 | for (size_t i = 0; i < N; ++i) |
|---|
| 257 | deleted[i] = !filter(vertex(i, _mg)); |
|---|
| 258 | |
|---|
| 259 | vector<graph_traits<multigraph_t>::edge_descriptor> edges; |
|---|
| 260 | |
|---|
| 261 | //remove vertices |
|---|
| 262 | for (int i = N-1; i >= 0; --i) |
|---|
| 263 | { |
|---|
| 264 | if (deleted[i]) |
|---|
| 265 | { |
|---|
| 266 | graph_traits<multigraph_t>::vertex_descriptor v = vertex(i, _mg); |
|---|
| 267 | graph_traits<multigraph_t>::out_edge_iterator e, e_end; |
|---|
| 268 | for(tie(e, e_end) = out_edges(v, _mg); e != e_end; ++e) |
|---|
| 269 | edges.push_back(*e); |
|---|
| 270 | graph_traits<multigraph_t>::in_edge_iterator ei, ei_end; |
|---|
| 271 | for(tie(ei, ei_end) = in_edges(v, _mg); ei != ei_end; ++ei) |
|---|
| 272 | edges.push_back(*ei); |
|---|
| 273 | for(size_t j = 0; j < edges.size(); ++j) |
|---|
| 274 | RemoveEdgeIndex(edges[j]); |
|---|
| 275 | clear_vertex(v, _mg); |
|---|
| 276 | remove_vertex(v, _mg); |
|---|
| 277 | edges.clear(); |
|---|
| 278 | } |
|---|
| 279 | } |
|---|
| 280 | } |
|---|
| 281 | |
|---|
| 282 | void GraphInterface::SetVertexFilterProperty(boost::any property, bool invert) |
|---|
| 283 | { |
|---|
| 284 | #ifdef NO_GRAPH_FILTERING |
|---|
| 285 | throw GraphException("graph filtering was not enabled at compile time"); |
|---|
| 286 | #endif |
|---|
| 287 | |
|---|
| 288 | try |
|---|
| 289 | { |
|---|
| 290 | _vertex_filter_map = |
|---|
| 291 | any_cast<vertex_filter_t::checked_t>(property).get_unchecked(); |
|---|
| 292 | _vertex_filter_invert = invert; |
|---|
| 293 | _vertex_filter_active = true; |
|---|
| 294 | } |
|---|
| 295 | catch(bad_any_cast&) |
|---|
| 296 | { |
|---|
| 297 | _vertex_filter_active = false; |
|---|
| 298 | } |
|---|
| 299 | } |
|---|
| 300 | |
|---|
| 301 | void GraphInterface::SetEdgeFilterProperty(boost::any property, bool invert) |
|---|
| 302 | { |
|---|
| 303 | #ifdef NO_GRAPH_FILTERING |
|---|
| 304 | throw GraphException("graph filtering was not enabled at compile time"); |
|---|
| 305 | #endif |
|---|
| 306 | |
|---|
| 307 | try |
|---|
| 308 | { |
|---|
| 309 | _edge_filter_map = |
|---|
| 310 | any_cast<edge_filter_t::checked_t>(property).get_unchecked(); |
|---|
| 311 | _edge_filter_invert = invert; |
|---|
| 312 | _edge_filter_active = true; |
|---|
| 313 | } |
|---|
| 314 | catch(bad_any_cast&) |
|---|
| 315 | { |
|---|
| 316 | _edge_filter_active = false; |
|---|
| 317 | } |
|---|
| 318 | } |
|---|
| 319 | |
|---|