// Copyright (C) 2007 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_GRAPH_KERNEl_1_ #define DLIB_GRAPH_KERNEl_1_ #include #include #include "../serialize.h" #include "../noncopyable.h" #include "../std_allocator.h" #include "../algs.h" #include "graph_kernel_abstract.h" #include "../is_kind.h" namespace dlib { // ---------------------------------------------------------------------------------------- template struct graph_checker_helper { /*! This object is used to check preconditions based on the value of is_checked !*/ static void check_neighbor ( unsigned long edge_index, const node_type& self ) { // make sure requires clause is not broken DLIB_CASSERT(edge_index < self.number_of_neighbors(), "\tnode_type& graph::node_type::neighbor(edge_index)" << "\n\tYou have specified an invalid index" << "\n\tedge_index: " << edge_index << "\n\tnumber_of_neighbors(): " << self.number_of_neighbors() << "\n\tthis: " << &self ); } static void check_edge ( unsigned long edge_index, const node_type& self ) { // make sure requires clause is not broken DLIB_CASSERT(edge_index < self.number_of_neighbors(), "\tE& graph::node_type::edge(edge_index)" << "\n\tYou have specified an invalid index" << "\n\tedge_index: " << edge_index << "\n\tnumber_of_neighbors(): " << self.number_of_neighbors() << "\n\tthis: " << &self ); } static void check_node ( unsigned long index, const graph& self ) { // make sure requires clause is not broken DLIB_CASSERT(index < self.number_of_nodes(), "\tnode_type& graph::node(index)" << "\n\tYou have specified an invalid index" << "\n\tindex: " << index << "\n\tnumber_of_nodes(): " << self.number_of_nodes() << "\n\tthis: " << &self ); } static void check_has_edge ( unsigned long node_index1, unsigned long node_index2, const graph& self ) { // make sure requires clause is not broken DLIB_CASSERT(node_index1 < self.number_of_nodes() && node_index2 < self.number_of_nodes(), "\tvoid graph::has_edge(node_index1, node_index2)" << "\n\tYou have specified an invalid index" << "\n\tnode_index1: " << node_index1 << "\n\tnode_index2: " << node_index2 << "\n\tnumber_of_nodes(): " << self.number_of_nodes() << "\n\tthis: " << &self ); } static void check_add_edge ( unsigned long node_index1, unsigned long node_index2, const graph& self ) { DLIB_CASSERT(node_index1 < self.number_of_nodes() && node_index2 < self.number_of_nodes(), "\tvoid graph::add_edge(node_index1, node_index2)" << "\n\tYou have specified an invalid index" << "\n\tnode_index1: " << node_index1 << "\n\tnode_index2: " << node_index2 << "\n\tnumber_of_nodes(): " << self.number_of_nodes() << "\n\tthis: " << &self ); DLIB_CASSERT( self.has_edge(node_index1, node_index2) == false, "\tvoid graph::add_edge(node_index1, node_index2)" << "\n\tYou can't add an edge if it already exists in the graph" << "\n\tnode_index1: " << node_index1 << "\n\tnode_index2: " << node_index2 << "\n\tnumber_of_nodes(): " << self.number_of_nodes() << "\n\tthis: " << &self ); } static void check_remove_edge ( unsigned long node_index1, unsigned long node_index2, const graph& self ) { DLIB_CASSERT(node_index1 < self.number_of_nodes() && node_index2 < self.number_of_nodes(), "\tvoid graph::remove_edge(node_index1, node_index2)" << "\n\tYou have specified an invalid index" << "\n\tnode_index1: " << node_index1 << "\n\tnode_index2: " << node_index2 << "\n\tnumber_of_nodes(): " << self.number_of_nodes() << "\n\tthis: " << &self ); DLIB_CASSERT( self.has_edge(node_index1, node_index2) == true, "\tvoid graph::remove_edge(node_index1, node_index2)" << "\n\tYou can't remove an edge if it isn't in the graph" << "\n\tnode_index1: " << node_index1 << "\n\tnode_index2: " << node_index2 << "\n\tnumber_of_nodes(): " << self.number_of_nodes() << "\n\tthis: " << &self ); } static void check_remove_node ( unsigned long index, const graph& self ) { // make sure requires clause is not broken DLIB_CASSERT(index < self.number_of_nodes(), "\tvoid graph::remove_node(index)" << "\n\tYou have specified an invalid index" << "\n\tindex: " << index << "\n\tnumber_of_nodes(): " << self.number_of_nodes() << "\n\tthis: " << &self ); } }; template struct graph_checker_helper { static inline void check_edge ( unsigned long , const node_type& ) { } static inline void check_neighbor ( unsigned long , const node_type& ) { } static inline void check_node ( unsigned long , const graph& ) { } static inline void check_has_edge ( unsigned long , unsigned long , const graph& ) { } static inline void check_add_edge ( unsigned long , unsigned long , const graph& ) { } static inline void check_remove_edge ( unsigned long , unsigned long , const graph& ) { } static inline void check_remove_node ( unsigned long , const graph& ) { } }; // ---------------------------------------------------------------------------------------- template < typename T, typename E = char, typename mem_manager = default_memory_manager, bool is_checked = true > class graph_kernel_1 : noncopyable { /*! INITIAL VALUE - nodes.size() == 0 CONVENTION - nodes.size() == number_of_nodes() - for all valid i: - *nodes[i] == node(i) - nodes[i]->neighbors.size() == nodes[i]->number_of_neighbors(i) - nodes[i]->idx == i == nodes[i]->index() - for all valid n: - nodes[i]->neighbors[n] == pointer to the n'th parent node of i - *nodes[i]->neighbors[n] == node(i).neighbor(n) - *nodes[i]->edges[n] == node(i).edge(n) !*/ public: struct node_type; private: typedef graph_checker_helper checker; public: typedef T type; typedef E edge_type; typedef mem_manager mem_manager_type; graph_kernel_1( ) {} virtual ~graph_kernel_1( ) {} void clear( ) { nodes.clear(); } void set_number_of_nodes ( unsigned long new_size ); unsigned long number_of_nodes ( ) const { return nodes.size(); } node_type& node ( unsigned long index ) { checker::check_node(index,*this); return *nodes[index]; } const node_type& node ( unsigned long index ) const { checker::check_node(index,*this); return *nodes[index]; } bool has_edge ( unsigned long node_index1, unsigned long node_index2 ) const; void add_edge ( unsigned long node_index1, unsigned long node_index2 ); void remove_edge ( unsigned long node_index1, unsigned long node_index2 ); unsigned long add_node ( ); void remove_node ( unsigned long index ); void swap ( graph_kernel_1& item ) { nodes.swap(item.nodes); } public: struct node_type { T data; typedef graph_kernel_1 graph_type; unsigned long index( ) const { return idx; } unsigned long number_of_neighbors ( ) const { return neighbors.size(); } const node_type& neighbor ( unsigned long edge_index ) const { checker::check_neighbor(edge_index,*this); return *neighbors[edge_index]; } node_type& neighbor ( unsigned long edge_index ) { checker::check_neighbor(edge_index,*this); return *neighbors[edge_index]; } const E& edge ( unsigned long edge_index ) const { checker::check_edge(edge_index,*this); return *edges[edge_index]; } E& edge ( unsigned long edge_index ) { checker::check_edge(edge_index,*this); return *edges[edge_index]; } private: friend class graph_kernel_1; typedef std_allocator alloc_type; typedef std_allocator,mem_manager> alloc_edge_type; std::vector neighbors; std::vector,alloc_edge_type> edges; unsigned long idx; }; private: typedef std_allocator,mem_manager> alloc_type; typedef std::vector, alloc_type> vector_type; vector_type nodes; }; // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > inline void swap ( graph_kernel_1& a, graph_kernel_1& b ) { a.swap(b); } // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > struct is_graph > { static const bool value = true; }; // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > void serialize ( const graph_kernel_1& item, std::ostream& out ) { try { serialize(item.number_of_nodes(), out); // serialize each node for (unsigned long i = 0; i < item.number_of_nodes(); ++i) { serialize(item.node(i).data, out); // serialize all the edges for (unsigned long n = 0; n < item.node(i).number_of_neighbors(); ++n) { // only serialize edges that we haven't already serialized if (item.node(i).neighbor(n).index() >= i) { serialize(item.node(i).neighbor(n).index(), out); serialize(item.node(i).edge(n), out); } } const unsigned long stop_mark = 0xFFFFFFFF; serialize(stop_mark, out); } } catch (serialization_error& e) { throw serialization_error(e.info + "\n while serializing object of type graph_kernel_1"); } } // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > void deserialize ( graph_kernel_1& item, std::istream& in ) { try { unsigned long size; deserialize(size, in); item.clear(); item.set_number_of_nodes(size); // deserialize each node for (unsigned long i = 0; i < item.number_of_nodes(); ++i) { deserialize(item.node(i).data, in); const unsigned long stop_mark = 0xFFFFFFFF; // Add all the edges going to this node's neighbors unsigned long index; deserialize(index, in); while (index != stop_mark) { item.add_edge(i, index); // find the edge unsigned long j = 0; for (j = 0; j < item.node(i).number_of_neighbors(); ++j) if (item.node(i).neighbor(j).index() == index) break; deserialize(item.node(i).edge(j), in); deserialize(index, in); } } } catch (serialization_error& e) { throw serialization_error(e.info + "\n while deserializing object of type graph_kernel_1"); } } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // member function definitions // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > void graph_kernel_1:: set_number_of_nodes ( unsigned long new_size ) { try { nodes.resize(new_size); for (unsigned long i = 0; i < nodes.size(); ++i) { nodes[i].reset(new node_type); nodes[i]->idx = i; } } catch (...) { clear(); throw; } } // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > bool graph_kernel_1:: has_edge ( unsigned long node_index1, unsigned long node_index2 ) const { checker::check_has_edge(node_index1, node_index2, *this); node_type& n = *nodes[node_index1]; // search all the child nodes to see if there is a link to the right node for (unsigned long i = 0; i < n.neighbors.size(); ++i) { if (n.neighbors[i]->idx == node_index2) return true; } return false; } // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > void graph_kernel_1:: add_edge ( unsigned long node_index1, unsigned long node_index2 ) { checker::check_add_edge(node_index1, node_index2, *this); try { node_type& n1 = *nodes[node_index1]; node_type& n2 = *nodes[node_index2]; n1.neighbors.push_back(&n2); std::shared_ptr e(new E); n1.edges.push_back(e); // don't add this twice if this is an edge from node_index1 back to itself if (node_index1 != node_index2) { n2.neighbors.push_back(&n1); n2.edges.push_back(e); } } catch (...) { clear(); throw; } } // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > void graph_kernel_1:: remove_edge ( unsigned long node_index1, unsigned long node_index2 ) { checker::check_remove_edge(node_index1, node_index2, *this); node_type& n1 = *nodes[node_index1]; node_type& n2 = *nodes[node_index2]; // remove the record of the link from n1 unsigned long pos = static_cast(find(n1.neighbors.begin(), n1.neighbors.end(), &n2) - n1.neighbors.begin()); n1.neighbors.erase(n1.neighbors.begin() + pos); n1.edges.erase(n1.edges.begin() + pos); // check if this is an edge that goes from node_index1 back to itself if (node_index1 != node_index2) { // remove the record of the link from n2 unsigned long pos = static_cast(find(n2.neighbors.begin(), n2.neighbors.end(), &n1) - n2.neighbors.begin()); n2.neighbors.erase(n2.neighbors.begin() + pos); n2.edges.erase(n2.edges.begin() + pos); } } // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > unsigned long graph_kernel_1:: add_node ( ) { try { std::shared_ptr n(new node_type); n->idx = nodes.size(); nodes.push_back(n); return n->idx; } catch (...) { clear(); throw; } } // ---------------------------------------------------------------------------------------- template < typename T, typename E, typename mem_manager, bool is_checked > void graph_kernel_1:: remove_node ( unsigned long index ) { checker::check_remove_node(index,*this); node_type& n = *nodes[index]; // remove all edges pointing to this node from its neighbors for (unsigned long i = 0; i < n.neighbors.size(); ++i) { // remove the edge from this specific parent unsigned long pos = static_cast(find(n.neighbors[i]->neighbors.begin(), n.neighbors[i]->neighbors.end(), &n) - n.neighbors[i]->neighbors.begin()); n.neighbors[i]->neighbors.erase(n.neighbors[i]->neighbors.begin() + pos); n.neighbors[i]->edges.erase(n.neighbors[i]->edges.begin() + pos); } // now remove this node by replacing it with the last node in the nodes vector nodes[index] = nodes[nodes.size()-1]; // update the index for the node we just moved nodes[index]->idx = index; // now remove the duplicated node at the end of the vector nodes.pop_back(); } // ---------------------------------------------------------------------------------------- } #endif // DLIB_GRAPH_KERNEl_1_