diff --git a/graph/alt_graph.cpp b/graph/alt_graph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b687a0af0f31723158cbf811dc008a555e6f7eae --- /dev/null +++ b/graph/alt_graph.cpp @@ -0,0 +1,164 @@ +// Old implementation, maybe superior for some graphs. + +//============================================================================= +// Class for Graph Nodes (one line of the adjacency matrix): +//============================================================================= + +class Node { + public: + + // Constructor: + Node(int graph_num_nodes) + { + num_nodes_ = graph_num_nodes; + int bytes = (num_nodes_ + BYTE_SIZE - 1) / BYTE_SIZE; + connections_ = new char[bytes]; + char *p = connections_; + for(int i = bytes; i > 0; i--) + *p++ = '\0'; + } + + // Accessor Functions: + inline int num_nodes() const { return num_nodes_; } + + // Store edge in adjacency matrix: + void store_connection(int to) { + // Check nodes: + if(to <= 0 || to > num_nodes_) { + std::cerr << "Invalid to node: " << to << "\n"; + exit(3); + } + + // Determine position in adjacency matrix: + long index = to - 1; + int bit = index % 8; + index = index / 8; + + // Set bit: + connections_[index] |= (0x1) << bit; + } + + // Check whether edge exists: + bool connection_exists(int to) const { + // Check node: + if(to <= 0 || to > num_nodes_) { + std::cerr << "Invalid to node: " << to << "\n"; + exit(4); + } + + // Determine position in adjacency matrix: + long index = to - 1; + int bit = index % 8; + index = index / 8; + + // Check bit: + return (connections_[index] & ((0x1) << bit)) != 0; + } + + // Copy other node into this node: + void copy(Node *node) { + if(node->num_nodes_ != num_nodes_) { + std::cerr << "Can copy only node of same size!\n"; + exit(5); + } + int bytes = (num_nodes_ + BYTE_SIZE - 1) / BYTE_SIZE; + char *to = connections_; + char *from = node->connections_; + while(bytes-- > 0) + *to++ = *from++; + } + + // Attributes: + private: + int num_nodes_; + char *connections_; +}; + +//----------------------------------------------------------------------------- +// Pointer Type for Node Data: +//----------------------------------------------------------------------------- + +typedef Node *node_t; + +#define NODE_NULL (static_cast<node_t>(0)) + + +//============================================================================= +// Class for Graphs (contains adjacency matrix): +//============================================================================= + +class Graph { + public: + + // Constructor: + Graph(int graph_num_nodes) + { + num_nodes_ = graph_num_nodes; + nodes_ = new node_t[num_nodes_]; + for(int i = 0; i < graph_num_nodes; i++) + nodes_[i] = new Node(graph_num_nodes); + } + + // Accessor Functions: + inline int num_nodes() const { return num_nodes_; } + inline node_t node(int i) const { return nodes_[i-1]; } + + // Store edge in adjacency matrix: + void store_edge(int from, int to) { + // Check nodes: + if(from <= 0 || from > num_nodes_) { + std::cerr << "Invalid from node: " << from << "\n"; + exit(6); + } + if(to <= 0 || to > num_nodes_) { + std::cerr << "Invalid to node: " << to << "\n"; + exit(7); + } + + // Store edge in adjacency matrix: + long index = from - 1; + nodes_[index]->store_connection(to); + } + + // Check whether edge exists: + bool edge_exists(int from, int to) const { + // Check nodes: + if(from <= 0 || from > num_nodes_) { + std::cerr << "Invalid from node: " << from << "\n"; + exit(8); + } + if(to <= 0 || to > num_nodes_) { + std::cerr << "Invalid to node: " << to << "\n"; + exit(9); + } + + // Look up edge in adjacency matrix: + long index = from - 1; + return nodes_[index]->connection_exists(to); + } + + // Copy a graph (of the same size) into this graph: + void copy(Graph *g) + { + if(g->num_nodes_ != num_nodes_) { + std::cerr << "Can copy only graph of same size!\n"; + exit(10); + } + for(int i = 0; i < num_nodes_; i++) + nodes_[i]->copy(g->nodes_[i]); + } + + // Attributes: + private: + int num_nodes_; + node_t *nodes_; +}; + +//----------------------------------------------------------------------------- +// Pointer Type for Graph Data: +//----------------------------------------------------------------------------- + +typedef Graph *graph_t; + +#define GRAPH_NULL (static_cast<graph_t>(0)) + diff --git a/graph/graph.cpp b/graph/graph.cpp index 71e518e771d142594316d475e4add3128b035b6f..7a96e60c4a3b3655a4ed51d27ed6095aada40e3b 100644 --- a/graph/graph.cpp +++ b/graph/graph.cpp @@ -179,13 +179,19 @@ #define COST_TABLE "BENCH_COST" - //============================================================================= // How many bytes does the bitstring in one element of the adjacency list have? //============================================================================= #define BITSTRING_BYTES 4 +//============================================================================= +// Should Assertions be tested or is speed of highest importance? +//============================================================================= + +// If this is defined, asserstions are skipped (faster code): +//#define NDEBUG + //============================================================================= // Include Files: //============================================================================= @@ -194,6 +200,7 @@ #include <fstream> #include <cstdint> #include <time.h> +#include <assert.h> //============================================================================= // Type for Large Integers (64 Bit): @@ -364,6 +371,162 @@ long str_int(str_t digits) { } +//============================================================================= +// Class for Graph/Benchmark Size Measures: +//============================================================================= + +class GSize { + public: + + // Constructor: + GSize() + { + num_nodes_ = -1; + num_edges_ = -1; + tc_size_ = -1; + tc_iter_ = -1; + tc_inst_ = -1; + sg_size_ = -1; + sg_iter_ = -1; + sg_inst_ = -1; + } + + // Accessor Functions: + inline int num_nodes() const { return num_nodes_; } + inline int num_edges() const { return num_edges_; } + inline bigint_t tc_size() const { return tc_size_; } + inline int tc_iter() const { return tc_iter_; } + inline bigint_t tc_inst() const { return tc_inst_; } + inline bigint_t sg_size() const { return sg_size_; } + inline int sg_iter() const { return sg_iter_; } + inline bigint_t sg_inst() const { return sg_inst_; } + + // Set functions: + inline void set_num_nodes(int n) { num_nodes_ = n; } + inline void set_num_edges(int n) { num_edges_ = n; } + inline void set_tc_size(bigint_t n) { tc_size_ = n; } + inline void set_tc_iter(int n) { tc_iter_ = n; } + inline void set_tc_inst(bigint_t n) { tc_inst_ = n; } + inline void set_sg_size(bigint_t n) { sg_size_ = n; } + inline void set_sg_iter(int n) { sg_iter_ = n; } + inline void set_sg_inst(bigint_t n) { sg_inst_ = n; } + + // Print defined values: + void print() { + if(num_nodes_ >= 0) + std::cout << "\tNodes: " << num_nodes_ << "\n"; + if(num_edges_ >= 0) + std::cout << "\tEdges: " << num_edges_ << "\n"; + if(tc_iter_ >= 0) + std::cout << "\tTC Iter: " << tc_iter_ << "\n"; + if(tc_size_ >= 0) + std::cout << "\tTC Size: " << tc_size_ << "\n"; + if(tc_inst_ >= 0) + std::cout << "\tTC Inst: " << tc_inst_ << "\n"; + if(sg_iter_ >= 0) + std::cout << "\tSG Iter: " << sg_iter_ << "\n"; + if(sg_size_ >= 0) + std::cout << "\tSG Size: " << sg_size_ << "\n"; + if(sg_inst_ >= 0) + std::cout << "\tSG Inst: " << sg_inst_ << "\n"; + } + + // Check consistency of two graph size objects: + bool consistent(GSize *gsize) { + + // Compare number of nodes (if defined in both objects): + if(num_nodes_ != -1 && gsize->num_nodes_ != -1) { + if(num_nodes_ != gsize->num_nodes_) + return false; + } + + // Compare number of edges: + if(num_edges_ != -1 && gsize->num_edges_ != -1) { + if(num_edges_ != gsize->num_edges_) + return false; + } + + // Compare transitive closure iterations: + if(tc_iter_ != -1 && gsize->tc_iter_ != -1) { + if(tc_iter_ != gsize->tc_iter_) + return false; + } + + // Compare transitive closure size: + if(tc_size_ != -1 && gsize->tc_size_ != -1) { + if(tc_size_ != gsize->tc_size_) + return false; + } + + // Compare transitive closure rule instances: + if(tc_inst_ != -1 && gsize->tc_inst_ != -1) { + if(tc_inst_ != gsize->tc_inst_) + return false; + } + + // Compare same generation iterations: + if(sg_iter_ != -1 && gsize->sg_iter_ != -1) { + if(sg_iter_ != gsize->sg_iter_) + return false; + } + + // Compare same generation size: + if(sg_size_ != -1 && gsize->sg_size_ != -1) { + if(sg_size_ != gsize->sg_size_) + return false; + } + + // Compare same generation rule instances: + if(sg_inst_ != -1 && gsize->sg_inst_ != -1) { + if(sg_inst_ != gsize->sg_inst_) + return false; + } + + // Ok: + return true; + } + + // Attributes: + private: + int num_nodes_; + int num_edges_; + bigint_t tc_size_; + int tc_iter_; + bigint_t tc_inst_; + bigint_t sg_size_; + int sg_iter_; + bigint_t sg_inst_; +}; + +//----------------------------------------------------------------------------- +// Pointer Type for Graph Size Data: +//----------------------------------------------------------------------------- + +typedef GSize *gsize_t; + +#define GSIZE_NULL (static_cast<gsize_t>(0)) + + +//============================================================================= +// Type for Nodes in a Graph: +//============================================================================= + +// In order to make the algorithms for computing problem sizes independent +// from the data structures used for representing graphs, we define a type +// for graph nodes here. +// Currently, the graph nodes are represented as positive integers starting +// from 1. +// The graph data structures use a linked list of bitmaps to represent +// sets of nodes. +// These bitmaps could also represent the number 0, which is not used. +// However, it would complicate the programs, and probably slow down them, +// to distinguish between bit positions (starting from 0) and node numbers +// (starting from 1). + +typedef int node_t; + +#define NODE_NULL 0 + //============================================================================= // Class for Linked List Element in Node Set (contains bit array): //============================================================================= @@ -371,7 +534,7 @@ long str_int(str_t digits) { class NodeSetElem { public: - // Constructor: + // Normal Constructor: NodeSetElem(int start_value) { next_ = (NodeSetElem *) 0; @@ -381,6 +544,16 @@ class NodeSetElem { *p++ = '\0'; } + // Constructor that copies a given node (except next pointer): + NodeSetElem(NodeSetElem *elem) { + next_ = (NodeSetElem *) 0; + start_ = elem->start_; + unsigned char *to = bits_; + unsigned char *from = elem->bits_; + for(int i = BITSTRING_BYTES; i > 0; i--) + *to++ = *from++; + } + // Get start node number (to be added to all numbers in bit array): inline int start() const { return start_; @@ -399,21 +572,15 @@ class NodeSetElem { // Get bit in bit array: inline bool bit(int index) const { - // Check validity, part I: - if(index < 0) { - std::cerr << "get_bit: Invalid bit index (negative)!"; - exit(3); - } + // Check validity of parameter, part I: + assert(index >= 0); // Determine position in bit array: int bit_no = index % 8; index = index / 8; - // Check parameter, part II: - if(index >= BITSTRING_BYTES) { - std::cerr << "get_bit: Invalid bit index (too large)!"; - exit(4); - } + // Check parameter of parameter, part II: + assert(index < BITSTRING_BYTES); // Get bit: return (bits_[index] & ((0x1) << bit_no)) != 0; @@ -422,21 +589,15 @@ class NodeSetElem { // Set bit in bit array: inline void set_bit(int index) { - // Check validity, part I: - if(index < 0) { - std::cerr << "set_bit: Invalid bit index (negative)!"; - exit(5); - } + // Check validity of parameter, part I: + assert(index >= 0); // Determine position in bit array: int bit_no = index % 8; index = index / 8; - // Check parameter, part II: - if(index >= BITSTRING_BYTES) { - std::cerr << "set_bit: Invalid bit index (too large)!"; - exit(6); - } + // Check parameter of parameter, part II: + assert(index < BITSTRING_BYTES); // Set bit: bits_[index] |= (0x1) << bit_no; @@ -445,21 +606,15 @@ class NodeSetElem { // Find next set bit in bit array with position >= index: inline int next_bit(int index) { - // Check validity, part I: - if(index < 0) { - std::cerr << "next_bit: Invalid bit index (negative)!"; - exit(7); - } + // Check validity of parameter, part I: + assert(index >= 0); // Determine position in bit array: int bit_no = index % 8; index = index / 8; // Check parameter, part II: - if(index >= BITSTRING_BYTES) { - std::cerr << "next_bit: Invalid bit index (too large)!"; - exit(8); - } + assert(index < BITSTRING_BYTES); // Find next set bit (-1 if there is no further bit): unsigned char byte = bits_[index]; @@ -524,6 +679,18 @@ class NodeSetElem { return -1; } + // Copy start and bitmap from another element (sets next to null): + inline void copy_data(NodeSetElem *other_elem) { + next_ = static_cast<NodeSetElem *>(0); + start_ = other_elem->start_; + + // Copy bitmap: + unsigned char *from = other_elem->bits_; + unsigned char *to = bits_; + for(int i = BITSTRING_BYTES; i-- > 0; ) + *to++ = *from++; + } + // Attributes: private: NodeSetElem *next_; @@ -541,78 +708,10 @@ typedef NodeSetElem *node_set_elem_t; //============================================================================= -// Class for Scan/Cursor over Node Set (set of non-negative integers): +// Forward declaration for NodeSetScan: //============================================================================= -class NodeSetScan { - public: - - // Constructor: - NodeSetScan(node_set_elem_t list) - { - list_ = list; - curr_elem_ = list; - if(curr_elem_ != NODE_SET_ELEM_NULL) - start_ = curr_elem_->start(); - else - start_ = 0; - next_index_ = 0; - } - - // Get next node number in set (-1 if there is no further element): - int next() { - // Check whether we are at the end of the list: - if(curr_elem_ == NODE_SET_ELEM_NULL) - return -1; - - // Try to find next value in current list element: - if(next_index_ < BITSTRING_BYTES * 8) { - int bit = curr_elem_->next_bit(next_index_); - if(bit >= 0) { - next_index_ = bit + 1; - return start_ + bit; - } - } - - // Move to next list element: - curr_elem_ = curr_elem_->next(); - if(curr_elem_ == NODE_SET_ELEM_NULL) - return -1; - - // We know that list elements are non-empty: - start_ = curr_elem_->start(); - int bit = curr_elem_->next_bit(next_index_); - next_index_ = bit + 1; - return start_ + bit; - } - - // Reset scan to start: - void reset() { - curr_elem_ = list_; - if(curr_elem_ != NODE_SET_ELEM_NULL) - start_ = curr_elem_->start(); - else - start_ = 0; - next_index_ = 0; - } - - // Attributes: - private: - node_set_elem_t list_; - node_set_elem_t curr_elem_; - int start_; - int next_index_; -}; - -//----------------------------------------------------------------------------- -// Pointer Type for Node Number List Element: -//----------------------------------------------------------------------------- - -typedef NodeSetScan *node_set_scan_t; - -#define NODE_SET_SCAN_NULL (static_cast<node_set_scan_t>(0)) - - +class NodeSetScan; //============================================================================= // Class for Node Set (represented as linked list of bit arrays): @@ -632,15 +731,16 @@ class NodeSet { // Destructor: ~NodeSet() { - node_set_elem_t next = NODE_SET_ELEM_NULL; - for(node_set_elem_t elem = list_; elem; elem = next) { - next = elem->next(); - delete elem; - } + free_list(list_); } // Insert a node number: - void insert(int n) { + void insert(node_t n) { + + // Check parameter: + assert(n >= 1); + + // Compute position in bitmap list: int start = n / (BITSTRING_BYTES * 8); int index = n % (BITSTRING_BYTES * 8); @@ -694,9 +794,16 @@ class NodeSet { } // Check whether a nonnegative integer exists in the set: - bool contains(int n) { + bool contains(node_t n) { + + // Check parameter: + assert(n >= 1); + + // Compute position in bitmap list: int start = n / (BITSTRING_BYTES * 8); int index = n % (BITSTRING_BYTES * 8); + + // Search list element with requested start value: for(node_set_elem_t elem = list_; elem; elem = elem->next()) { int elem_start = elem->start(); if(elem_start == start) @@ -704,318 +811,308 @@ class NodeSet { if(elem_start > start) return false; } + return false; } - // Attributes: - private: - node_set_elem_t list_; -}; + // Check whether node set is empty: + inline bool is_empty() { + // List elements are only constructed when needed, + // i.e. there are no empty bitmaps. + return list_ == NODE_SET_ELEM_NULL; + } -//----------------------------------------------------------------------------- -// Pointer Type for Node Set (really a set of non-negative integers): -//----------------------------------------------------------------------------- + // Copy a given node set into this node set (overwrites existing data): + void copy(NodeSet *node_set) { -typedef NodeSet *node_set_t; + // The current list is only used as recycle material for + // NodeSetElem objects. + // The objects themselves are overwritten. + // Currently, all calls to the copy function are called + // with a superset of nodes. This is not a requirement, + // but this motivates that the NodeSet does not keep its + // own free list, but directly deletes any superfluous nodes. + // With the current usage, this will anyway not happen. -#define NODE_SET_NULL (static_cast<node_set_t>(0)) + // List of nodes to be used: + node_set_elem_t nodes = list_; -//============================================================================= -// Class for Graph Nodes (one line of the adjacency matrix): -//============================================================================= + // prev is the previous element of the list that is built. + // If it is null, then the start (attribute list_) must be set. + node_set_elem_t prev = NODE_SET_ELEM_NULL; -class Node { - public: + // Copy list, using existing nodes: + node_set_elem_t from = node_set->list_; + for( ; from && nodes; from = from->next()) { - // Constructor: - Node(int graph_num_nodes) - { - num_nodes_ = graph_num_nodes; - int bytes = (num_nodes_ + BYTE_SIZE - 1) / BYTE_SIZE; - connections_ = new char[bytes]; - char *p = connections_; - for(int i = bytes; i > 0; i--) - *p++ = '\0'; - } + // Get node from recycle list: + node_set_elem_t elem = nodes; + nodes = nodes->next(); - // Accessor Functions: - inline int num_nodes() const { return num_nodes_; } + // Copy node (except next pointer): + elem->copy_data(from); - // Store edge in adjacency matrix: - void store_connection(int to) { - // Check nodes: - if(to <= 0 || to > num_nodes_) { - std::cerr << "Invalid to node: " << to << "\n"; - exit(3); + // Link new element to constructed list: + if(prev == NODE_SET_ELEM_NULL) + list_ = elem; + else + prev->set_next(elem); + + // The copied node is now the previous node: + prev = elem; } - // Determine position in adjacency matrix: - long index = to - 1; - int bit = index % 8; - index = index / 8; + // For the remaining elements, allocate new nodes: + for( ; from; from = from->next()) { + node_set_elem_t elem = new NodeSetElem(from); - // Set bit: - connections_[index] |= (0x1) << bit; - } + // Link new element to "to" list (via prev): + if(prev == NODE_SET_ELEM_NULL) + list_ = elem; + else + prev->set_next(elem); - // Check whether edge exists: - bool connection_exists(int to) const { - // Check node: - if(to <= 0 || to > num_nodes_) { - std::cerr << "Invalid to node: " << to << "\n"; - exit(4); + // The previous element pointer is always one behind: + prev = elem; } - // Determine position in adjacency matrix: - long index = to - 1; - int bit = index % 8; - index = index / 8; - - // Check bit: - return (connections_[index] & ((0x1) << bit)) != 0; + // If from list is shorter, delete remaining recycle elements: + free_list(nodes); } - // Copy other node into this node: - void copy(Node *node) { - if(node->num_nodes_ != num_nodes_) { - std::cerr << "Can copy only node of same size!\n"; - exit(5); + // Auxiliary function to free all (remaining) list elements: + private: + void free_list(node_set_elem_t list) { + node_set_elem_t next; + for(node_set_elem_t elem = list; elem; elem = next) { + next = elem->next(); + delete elem; } - int bytes = (num_nodes_ + BYTE_SIZE - 1) / BYTE_SIZE; - char *to = connections_; - char *from = node->connections_; - while(bytes-- > 0) - *to++ = *from++; } // Attributes: private: - int num_nodes_; - char *connections_; + node_set_elem_t list_; + + // The NodeSetScan constructor and reset must be able to access list_: + friend NodeSetScan; }; //----------------------------------------------------------------------------- -// Pointer Type for Node Data: +// Pointer Type for Node Set (really a set of non-negative integers): //----------------------------------------------------------------------------- -typedef Node *node_t; +typedef NodeSet *node_set_t; -#define NODE_NULL (static_cast<node_t>(0)) +#define NODE_SET_NULL (static_cast<node_set_t>(0)) //============================================================================= -// Class for Graphs (contains adjacency matrix): +// Class for Scan/Cursor over Node Set (set of non-negative integers): //============================================================================= -class Graph { +class NodeSetScan { public: // Constructor: - Graph(int graph_num_nodes) + NodeSetScan(NodeSet *node_set) { - num_nodes_ = graph_num_nodes; - nodes_ = new node_t[num_nodes_]; - for(int i = 0; i < graph_num_nodes; i++) - nodes_[i] = new Node(graph_num_nodes); - } - - // Accessor Functions: - inline int num_nodes() const { return num_nodes_; } - inline node_t node(int i) const { return nodes_[i-1]; } + // Access is permitted by friend declaration in NodeSet: + node_set_ = node_set; - // Store edge in adjacency matrix: - void store_edge(int from, int to) { - // Check nodes: - if(from <= 0 || from > num_nodes_) { - std::cerr << "Invalid from node: " << from << "\n"; - exit(6); - } - if(to <= 0 || to > num_nodes_) { - std::cerr << "Invalid to node: " << to << "\n"; - exit(7); - } + // Set current bitmap list elem: + curr_elem_ = node_set_->list_; + if(curr_elem_ != NODE_SET_ELEM_NULL) + start_ = curr_elem_->start(); + else + start_ = 0; - // Store edge in adjacency matrix: - long index = from - 1; - nodes_[index]->store_connection(to); + // Next bit that will be checked in current bitmap list elem: + next_index_ = 0; } - // Check whether edge exists: - bool edge_exists(int from, int to) const { - // Check nodes: - if(from <= 0 || from > num_nodes_) { - std::cerr << "Invalid from node: " << from << "\n"; - exit(8); - } - if(to <= 0 || to > num_nodes_) { - std::cerr << "Invalid to node: " << to << "\n"; - exit(9); + // Get next node number in set (-1 if there is no further element): + node_t next() { + // Check whether we are at the end of the list: + if(curr_elem_ == NODE_SET_ELEM_NULL) + return NODE_NULL; + + // Try to find next value in current list element: + if(next_index_ < BITSTRING_BYTES * 8) { + int bit = curr_elem_->next_bit(next_index_); + if(bit >= 0) { + next_index_ = bit + 1; + return start_ + bit; + } } - // Look up edge in adjacency matrix: - long index = from - 1; - return nodes_[index]->connection_exists(to); + // Move to next list element: + curr_elem_ = curr_elem_->next(); + if(curr_elem_ == NODE_SET_ELEM_NULL) + return NODE_NULL; + + // We know that list elements are non-empty: + start_ = curr_elem_->start(); + int bit = curr_elem_->next_bit(next_index_); + next_index_ = bit + 1; + return start_ + bit; } - // Copy a graph (of the same size) into this graph: - void copy(Graph *g) - { - if(g->num_nodes_ != num_nodes_) { - std::cerr << "Can copy only graph of same size!\n"; - exit(10); - } - for(int i = 0; i < num_nodes_; i++) - nodes_[i]->copy(g->nodes_[i]); + // Reset scan to start: + void reset() { + curr_elem_ = node_set_->list_; + if(curr_elem_ != NODE_SET_ELEM_NULL) + start_ = curr_elem_->start(); + else + start_ = 0; + next_index_ = 0; } // Attributes: private: - int num_nodes_; - node_t *nodes_; + node_set_t node_set_; + node_set_elem_t curr_elem_; + int start_; + int next_index_; }; //----------------------------------------------------------------------------- -// Pointer Type for Graph Data: +// Pointer Type for Node Set Scan: //----------------------------------------------------------------------------- -typedef Graph *graph_t; +typedef NodeSetScan *node_set_scan_t; -#define GRAPH_NULL (static_cast<graph_t>(0)) +#define NODE_SET_SCAN_NULL (static_cast<node_set_scan_t>(0)) +//----------------------------------------------------------------------------- +// Macro for Loops over the Nodes in a Node Set Scan: +//----------------------------------------------------------------------------- + +#define NODE_LOOP(N, S) for(node_t N = S.next(); N != NODE_NULL; N = S.next()) //============================================================================= -// Class for Graph/Benchmark Size Measures: +// Class for Graphs: //============================================================================= -class GSize { +class Graph { public: // Constructor: - GSize() + Graph(int graph_num_nodes) : out_nodes_() { - num_nodes_ = -1; - num_edges_ = -1; - tc_size_ = -1; - tc_iter_ = -1; - tc_inst_ = -1; - sg_size_ = -1; - sg_iter_ = -1; - sg_inst_ = -1; + num_nodes_ = graph_num_nodes; + incoming_ = new NodeSet[num_nodes_ + 1];// Index 0 is not used + outgoing_ = new NodeSet[num_nodes_ + 1];// Index 0 is not used + changed_ = false; } // Accessor Functions: - inline int num_nodes() const { return num_nodes_; } - inline int num_edges() const { return num_edges_; } - inline bigint_t tc_size() const { return tc_size_; } - inline int tc_iter() const { return tc_iter_; } - inline bigint_t tc_inst() const { return tc_inst_; } - inline bigint_t sg_size() const { return sg_size_; } - inline int sg_iter() const { return sg_iter_; } - inline bigint_t sg_inst() const { return sg_inst_; } + inline int num_nodes() const { return num_nodes_; } - // Set functions: - inline void set_num_nodes(int n) { num_nodes_ = n; } - inline void set_num_edges(int n) { num_edges_ = n; } - inline void set_tc_size(bigint_t n) { tc_size_ = n; } - inline void set_tc_iter(int n) { tc_iter_ = n; } - inline void set_tc_inst(bigint_t n) { tc_inst_ = n; } - inline void set_sg_size(bigint_t n) { sg_size_ = n; } - inline void set_sg_iter(int n) { sg_iter_ = n; } - inline void set_sg_inst(bigint_t n) { sg_inst_ = n; } + // Store edge in adjacency matrix: + void store_edge(node_t from, node_t to) { - // Print defined values: - void print() { - if(num_nodes_ >= 0) - std::cout << "\tNodes: " << num_nodes_ << "\n"; - if(num_edges_ >= 0) - std::cout << "\tEdges: " << num_edges_ << "\n"; - if(tc_iter_ >= 0) - std::cout << "\tTC Iter: " << tc_iter_ << "\n"; - if(tc_size_ >= 0) - std::cout << "\tTC Size: " << tc_size_ << "\n"; - if(tc_inst_ >= 0) - std::cout << "\tTC Inst: " << tc_inst_ << "\n"; - if(sg_iter_ >= 0) - std::cout << "\tSG Iter: " << sg_iter_ << "\n"; - if(sg_size_ >= 0) - std::cout << "\tSG Size: " << sg_size_ << "\n"; - if(sg_inst_ >= 0) - std::cout << "\tSG Inst: " << sg_inst_ << "\n"; + // Check nodes: + assert(from > 0); + assert(from <= num_nodes_); + assert(to > 0); + assert(to <= num_nodes_); + + // Store edge: + incoming_[to].insert(from); + outgoing_[from].insert(to); + + // Mark change: + changed_ = true; } - // Check consistency of two graph size objects: - bool consistent(GSize *gsize) { + // Check whether edge exists: + bool edge_exists(node_t from, node_t to) const { - // Compare number of nodes (if defined in both objects): - if(num_nodes_ != -1 && gsize->num_nodes_ != -1) { - if(num_nodes_ != gsize->num_nodes_) - return false; - } + // Check nodes: + assert(from > 0); + assert(from <= num_nodes_); + assert(to > 0); + assert(to <= num_nodes_); - // Compare number of edges: - if(num_edges_ != -1 && gsize->num_edges_ != -1) { - if(num_edges_ != gsize->num_edges_) - return false; - } + // Look up edge: + return outgoing_[from].contains(to); + } - // Compare transitive closure iterations: - if(tc_iter_ != -1 && gsize->tc_iter_ != -1) { - if(tc_iter_ != gsize->tc_iter_) - return false; - } + // Return set of nodes reachable from a given node: + node_set_t outgoing(node_t from) const { - // Compare transitive closure size: - if(tc_size_ != -1 && gsize->tc_size_ != -1) { - if(tc_size_ != gsize->tc_size_) - return false; - } + // Check parameter node: + assert(from > 0); + assert(from <= num_nodes_); - // Compare transitive closure rule instances: - if(tc_inst_ != -1 && gsize->tc_inst_ != -1) { - if(tc_inst_ != gsize->tc_inst_) - return false; - } + // Look up edge: + return &(outgoing_[from]); + } - // Compare same generation iterations: - if(sg_iter_ != -1 && gsize->sg_iter_ != -1) { - if(sg_iter_ != gsize->sg_iter_) - return false; - } - // Compare same generation size: - if(sg_size_ != -1 && gsize->sg_size_ != -1) { - if(sg_size_ != gsize->sg_size_) - return false; - } + // Access the set of nodes from which this node is reachable: + node_set_t incoming(node_t to) { - // Compare same generation rule instances: - if(sg_inst_ != -1 && gsize->sg_inst_ != -1) { - if(sg_inst_ != gsize->sg_inst_) - return false; + // Check parameter node: + assert(to > 0); + assert(to <= num_nodes_); + + // Return set of nodes: + return &(incoming_[to]); + } + + // Access the set of nodes that have at least one outgoing edge: + node_set_t out_nodes() { + + // Usually, this is only called after the graph was fully + // constructed. + // Therefore, it seems more efficient to compute the node set + // only once instead of doing an insertion for every inserted + // edge. + + // Do we need to recompute the set? + if(changed_) { + for(int i = 1; i <= num_nodes_; i++) { + if(!outgoing_[i].is_empty()) + out_nodes_.insert(i); + } + changed_ = false; } - // Ok: - return true; + // Return precomputed node set: + return &out_nodes_; + } + + // Copy a graph (of the same size) into this graph: + void copy(Graph *g) + { + if(g->num_nodes_ != num_nodes_) { + std::cerr << "Can copy only graph of same size!\n"; + exit(10); + } + for(int i = 1; i <= num_nodes_; i++) { + incoming_[i].copy(&(g->incoming_[i])); + outgoing_[i].copy(&(g->outgoing_[i])); + } + changed_ = true; } // Attributes: private: - int num_nodes_; - int num_edges_; - bigint_t tc_size_; - int tc_iter_; - bigint_t tc_inst_; - bigint_t sg_size_; - int sg_iter_; - bigint_t sg_inst_; + int num_nodes_; + bool changed_; + node_set_t incoming_; + node_set_t outgoing_; + NodeSet out_nodes_; }; //----------------------------------------------------------------------------- -// Pointer Type for Graph Size Data: +// Pointer Type for Graph Data: //----------------------------------------------------------------------------- -typedef GSize *gsize_t; +typedef Graph *graph_t; -#define GSIZE_NULL (static_cast<gsize_t>(0)) +#define GRAPH_NULL (static_cast<graph_t>(0)) //============================================================================= @@ -1037,15 +1134,16 @@ class TC { iter_ = 0; num_edges_ = 0; - // Compute transitive closure: + // Compute transitive closure. First nonrecursive rule: bool changed = false; std::cout << "Iteration 0.\n"; - for(int i = 1; i <= num_nodes_; i++) { - node_t input_node = input_->node(i); - node_t tc_node = tc_->node(i); - for(int j = 1; j <= num_nodes_; j++) { - if(input_node->connection_exists(j)) { - tc_node->store_connection(j); + { // xs is used locally in this rule (avoids shadowing warning) + // tc(X, Y) :- input(X,Y). + NodeSetScan xs(input_->out_nodes()); + NODE_LOOP(x, xs) { + NodeSetScan ys(input_->outgoing(x)); + NODE_LOOP(y, ys) { + tc_->store_edge(x, y); rule_inst_++; tc_size_++; num_edges_++; @@ -1060,23 +1158,16 @@ class TC { changed = false; tc_prev_->copy(tc_); iter_++; - for(int i = 1; i <= num_nodes_; i++) { - node_t par_x = input_->node(i); - node_t tc_x = tc_->node(i); - for(int j = 1; j <= num_nodes_; j++) { - if(!par_x->connection_exists(j)) - continue; - node_t tc_y = tc_prev_->node(j); - for(int k = 1; k <= num_nodes_; k++) { - if(!tc_y->connection_exists(k)) - continue; - if(tc_x->connection_exists(k)) - continue; - // I.e. conditions are: - // input_->edge_exists(i,j) - // tc_prev_->edge_exists(j,k) - // !tc_->edge_exists(i,k)) - tc_x->store_connection(k); + // tc(X, Z) :- input(X,Y), tc_prev(Y, Z). + NodeSetScan xs(input_->out_nodes()); + NODE_LOOP(x, xs) { + NodeSetScan ys(input_->outgoing(x)); + NODE_LOOP(y, ys) { + NodeSetScan zs(tc_prev_->outgoing(y)); + NODE_LOOP(z, zs) { + if(tc_->edge_exists(x,z)) + continue; // duplicate + tc_->store_edge(x,z); tc_size_++; changed = true; } @@ -1087,15 +1178,12 @@ class TC { // Computation of Rule Instances: std::cout << "Computation of Rule Instances ...\n"; // Instances of the non-recursive rule were counted above. - for(int i = 1; i <= num_nodes_; i++) { - node_t par_x = input_->node(i); - for(int j = 1; j <= num_nodes_; j++) { - if(!par_x->connection_exists(j)) - continue; - node_t tc_y = tc_->node(j); - for(int k = 1; k <= num_nodes_; k++) { - if(!tc_y->connection_exists(k)) - continue; + NodeSetScan xs(input_->out_nodes()); + NODE_LOOP(x, xs) { + NodeSetScan ys(input_->outgoing(x)); + NODE_LOOP(y, ys) { + NodeSetScan zs(tc_->outgoing(y)); + NODE_LOOP(z, zs) { rule_inst_++; } } @@ -1120,13 +1208,12 @@ class TC { // Print TC: void print() { - for(int i = 1; i <= num_nodes_; i++) { - node_t node = tc_->node(i); - for(int j = 1; j <= num_nodes_; j++) { - if(node->connection_exists(j)) { - std::cout << "\ttc(" << i << "," << j << + NodeSetScan xs(tc_->out_nodes()); + NODE_LOOP(x, xs) { + NodeSetScan ys(tc_->outgoing(x)); + NODE_LOOP(y, ys) { + std::cout << "\ttc(" << x << "," << y << ").\n"; - } } } std::cout << "\n"; @@ -1175,18 +1262,19 @@ class SG { // Compute same generation cousins: bool changed = false; std::cout << "Iteration 0.\n"; - for(int i = 1; i <= num_nodes_; i++) { - node_t input_node = input_->node(i); - for(int j = 1; j <= num_nodes_; j++) { - if(input_node->connection_exists(j)) { - node_t sg_node = sg_->node(j); - if(!sg_node->connection_exists(j)) { - sg_node->store_connection(j); - sg_size_++; - changed = true; - } - rule_inst_++; + // sg(Y, Y) :- input(X, Y). + { // xs is used locally in this rule (avoids shadowing warning) + NodeSetScan xs(input_->out_nodes()); + NODE_LOOP(x, xs) { + NodeSetScan ys(input_->outgoing(x)); + NODE_LOOP(y, ys) { num_edges_++; + rule_inst_++; + if(sg_->edge_exists(y, y)) + continue; // duplicate + sg_->store_edge(y, y); + sg_size_++; + changed = true; } } } @@ -1201,30 +1289,21 @@ class SG { rec_inst = 0; sg_prev_->copy(sg_); iter_++; - for(int i = 1; i <= num_nodes_; i++) { - node_t sg_prev_i = sg_prev_->node(i); - for(int j = 1; j <= num_nodes_; j++) { - if(!sg_prev_i->connection_exists(j)) - continue; - for(int i2 = 1; i2 <= num_nodes_; i2++){ - if(!input_->edge_exists(i2,i)) - continue; - for(int j2 = 1; - j2 <= num_nodes_; - j2++){ - if(!input_-> - edge_exists(j2,j)) - continue; + // sg(X, Y) :- input(X, A), sg(A, B), input(Y, B). + NodeSetScan as(sg_prev_->out_nodes()); + NODE_LOOP(a, as) { + NodeSetScan bs(sg_prev_->outgoing(a)); + NODE_LOOP(b, bs) { + NodeSetScan xs(input_->incoming(a)); + NODE_LOOP(x, xs) { + NodeSetScan ys( + input_->incoming(b)); + NODE_LOOP(y, ys) { rec_inst++; if(sg_-> - edge_exists(i2,j2)) + edge_exists(x,y)) continue; - // I.e. conditions are: - // sg_prev_->edge_exists(i,j) - // input_->edge_exists(i2,i) - // input_->edge_exists(j2,j) - // !sg_->edge_exists(i2,j2)) - sg_->store_edge(i2,j2); + sg_->store_edge(x,y); sg_size_++; changed = true; } @@ -1235,6 +1314,9 @@ class SG { // Add instances of recursive rule to rule instances: rule_inst_ += rec_inst; + // Note that this is outside the iteration. + // I.e. it is the number of rule instances + // considered in the last iteration. } // Accessor Functions: @@ -1255,13 +1337,12 @@ class SG { // Print SG: void print() { - for(int i = 1; i <= num_nodes_; i++) { - node_t node = sg_->node(i); - for(int j = 1; j <= num_nodes_; j++) { - if(node->connection_exists(j)) { - std::cout << "\tsg(" << i << "," << j << - ").\n"; - } + NodeSetScan xs(sg_->out_nodes()); + NODE_LOOP(x, xs) { + NodeSetScan ys(sg_->outgoing(x)); + NODE_LOOP(y, ys) { + std::cout << "\tsg(" << x << "," << y << + ").\n"; } } std::cout << "\n";