Array * page_rank(Table * inbound, unsigned int order, float alpha, float convergence, unsigned int max_times) { register int t = 0, k, i; register float norm2 = 1.0, prod_scalar, x; unsigned int total_size = order; Array * vector = initial(total_size); Array * new_vector = initial(total_size); Array * tmp; x = 1.0 - alpha; while (t < max_times && norm2 >= convergence) { t++; norm2 = 0.0; prod_scalar = 0.0; for (k = 0; k < total_size; k++) { if (is_sink(inbound, k)) prod_scalar += array_get(vector, k) * alpha; } prod_scalar += x; for (k = 0; k < total_size; k++) { array_set(new_vector, k, prod_scalar); if (!is_sink(inbound, k)) { tmp = table_get(inbound, k); for (i = 0; i < array_len(tmp); i++) { array_incr(new_vector, k, array_get(vector, (int)array_get(tmp, i)) * alpha); } } norm2 += (array_get(new_vector, k) - array_get(vector, k)) * (array_get(new_vector, k) - array_get(vector, k)); } t++; norm2 = sqrt((double)norm2) / total_size; array_copy(vector, new_vector); } array_delete(new_vector); return normalize(vector); }
/** * @brief Print the graph * * Print the graph. Useful for debugging. * * @warning The format in which this function prints a graph is not the same * as the DAG string format used by the XSocket API. To obtain a * string version of the DAG for use with the XSocket API, use * Graph::dag_string() instead. */ void Graph::print_graph() const { for (std::size_t i = 0; i < nodes_.size(); i++) { if (is_source(i)) printf("[SRC] "); else printf(" "); printf("Node %zu: [%s] ", i, nodes_[i].type_string().c_str()); //printf("%20s", nodes_[i].id()); for (std::size_t j = 0; j < Node::ID_LEN; j++) printf("%02x", nodes_[i].id()[j]); bool first = true; for (std::size_t j = 0; j < out_edges_[i].size(); j++) { if (first) { first = false; printf(" ->"); } printf(" Node %zu", out_edges_[i][j]); } if (is_sink(i)) printf(" [SNK]"); printf("\n"); } }
Graph& Graph::operator*=(const Graph& r) { std::vector<std::size_t> sinks; std::vector<std::size_t> sources; for (std::size_t i = 0; i < nodes_.size(); i++) if (is_sink(i)) sinks.push_back(i); for (std::size_t i = 0; i < r.nodes_.size(); i++) if (r.is_source(i)) { if (r.nodes_[i].type() == Node::XID_TYPE_DUMMY_SOURCE) { for (std::vector<std::size_t>::const_iterator it = r.out_edges_[i].begin(); it != r.out_edges_[i].end(); ++it) vector_push_back_unique(sources, *it); } else { sources.push_back(i); } } std::vector<std::size_t> node_mapping_r; merge_graph(r, node_mapping_r, true); for (std::vector<std::size_t>::const_iterator it_sink = sinks.begin(); it_sink != sinks.end(); ++it_sink) for (std::vector<std::size_t>::const_iterator it_source = sources.begin(); it_source != sources.end(); ++it_source) add_edge(*it_sink, node_mapping_r[*it_source]); return *this; }
/** * @brief Test if a node is the final intent * * Check whether or not the supplied Node is the final intent of the DAG * * @param n The node to check * * @return true if n is the final intent, false otherwise */ bool Graph::is_final_intent(const Node& n) { for (std::size_t i = 0; i < nodes_.size(); i++) { if (nodes_[i] == n) return is_sink(i); } printf("Warning: is_final_intent: supplied node not found in DAG: %s\n", n.id_string().c_str()); return false; }
/** * @brief Get the index of the final intent node * * Get the index of the final intent node. This method returns the index of the * first sink node it finds. * * @return The index of the DAG's final intent node. */ std::size_t Graph::final_intent_index() const { for (std::size_t i = 0; i < nodes_.size(); i++) { if (is_sink(i)) return i; } printf("Warning: source_index: no sink node found\n"); return -1; }
/** * @brief Get a node from the DAG * * Get a Node from the graph at the specified index. The sink node will always * be returned last (that is, it has index num_nodes()-1). * * @note This function skips the starting node * * @param i The index of the node to return * * @return The node at index i */ Node Graph::get_node(int i) const { std::size_t src_index, sink_index; for (std::size_t j = 0; j < nodes_.size(); j++) { if (is_source(j)) src_index = j; if (is_sink(j)) sink_index = j; } return nodes_[index_from_dag_string_index(i, src_index, sink_index)]; }
/** * @brief Return the graph in string form * * Get the DAG string representation of the graph. This string is suitable for * use with the XSocket API. * * @return The graph in DAG string form. */ std::string Graph::dag_string() const { // TODO: check DAG first (one source, one sink, actually a DAG) std::string dag_string; int sink_index = -1, source_index = -1; // Find source and sink for (std::size_t i = 0; i < nodes_.size(); i++) { if (is_source(i)) source_index = i; if (is_sink(i)) sink_index = i; } if (sink_index >= 0 && source_index >= 0) { // Add source first dag_string += "DAG"; dag_string += out_edges_for_index(source_index, source_index, sink_index); dag_string += " - \n"; // Add intermediate nodes for (std::size_t i = 0; i < nodes_.size(); i++) { if (i == source_index || i == sink_index) continue; // add XID type dag_string += nodes_[i].type_string() + ":"; // add XID dag_string += nodes_[i].id_string(); // add out edges dag_string += out_edges_for_index(i, source_index, sink_index); dag_string += " - \n"; } // Add sink last dag_string += nodes_[sink_index].type_string() + ":"; dag_string += nodes_[sink_index].id_string(); } else { printf("WARNING: dag_string(): could not find source and/or sink. Returning empty string.\n"); } return dag_string; }
/** * @brief Get the out edges for a node * * Get the out edges for a node at index i * * @note This function skips the starting node. Use index -1 to get the * starting node's outgoing edges. * * @param i The index of the node * * @return The out edges of node i */ std::vector<std::size_t> Graph::get_out_edges(int i) const { std::size_t src_index, sink_index; for (std::size_t j = 0; j < nodes_.size(); j++) { if (is_source(j)) src_index = j; if (is_sink(j)) sink_index = j; } std::size_t real_index; if (i == -1) real_index = src_index; else real_index = index_from_dag_string_index(i, src_index, sink_index); std::vector<std::size_t> out_edges; for (std::size_t j = 0; j < out_edges_[real_index].size(); j++) { out_edges.push_back(index_in_dag_string(out_edges_[real_index][j], src_index, sink_index)); } return out_edges; }
/** * @brief Get the next "intent unit" in the DAG starting at the supplied node * * Get the next "intent unit" in the DAG starting at the supplied node. An * intent node is a node that can be reached by following the highest priority * out edge of each node beginning with the source. An intent unit is an intent * node along with its fallbacks. In the new DAG, the previous intent unit * becomes the source node. * * @param n The node from which to find the next intent unit. n must be an * intent node. * * @return The next intent unit (as a Graph) */ Graph Graph::next_hop(const Node& n) { // first find n and make sure it's an intent unit std::size_t nIndex = -1; std::size_t curIndex = source_index(); while (nIndex == -1) { if (nodes_[curIndex] == n) { nIndex = curIndex; } else { if (is_sink(curIndex)) { printf("Warning: next_hop: n not found or is not an intent node\n"); return Graph(); } else { curIndex = out_edges_[curIndex][0]; } } } // if n is the DAG's sink, there is no next hop if (is_sink(nIndex)) { printf("Warning: next_hop: n is the final intent; no next hop\n"); return Graph(); } // find the next intent node (the final intent of the new DAG) // for now, we we'll only consider CIDs and SIDs to be intent nodes std::size_t intentIndex = nIndex; while (intentIndex == nIndex || (nodes_[intentIndex].type() != Node::XID_TYPE_CID && nodes_[intentIndex].type() != Node::XID_TYPE_SID)) { if (is_sink(intentIndex)) { break; } intentIndex = out_edges_[intentIndex][0]; } Graph g = Graph(); // make copies of all the nodes we need in the new Graph with BFS std::vector<std::size_t> to_process; std::map<std::size_t, std::size_t> old_to_new_map; vector_push_back_unique(to_process, nIndex); while (to_process.size() > 0) { curIndex = to_process.back(); to_process.pop_back(); // copy this node to the new graph and the mapping Node new_node; if (curIndex != nIndex) // the start node, n, should be replaced with a dummy source { new_node = Node(nodes_[curIndex]); } std::size_t newIndex = g.add_node(new_node); old_to_new_map[curIndex] = newIndex; // prepare to process its children if (curIndex == intentIndex) continue; // but if we've gotten to the new intent node, stop for (int i = 0; i < out_edges_[curIndex].size(); i++) { vector_push_back_unique(to_process, out_edges_[curIndex][i]); } } // add edges to the new graph for (std::map<std::size_t, std::size_t>::const_iterator it = old_to_new_map.begin(); it != old_to_new_map.end(); ++it) { std::size_t old_index = (*it).first; std::size_t new_index = (*it).second; if (old_index == intentIndex) continue; // the new final intent has no out edges for (int i = 0; i < out_edges_[old_index].size(); i++) { g.add_edge(new_index, old_to_new_map[out_edges_[old_index][i]]); } } return g; }