///////////////////////////////////////////////////////////////////////// // Vertex Loader (used to read images and load the vertex data of the graph) //bool vertex_loader(graph_type& graph, const std::string& fname, // const std::string& line) bool vertex_loader(graphlab::distributed_control& dc, graph_type& graph, string img_path) { // force a "/" at the end of the path // make sure to check that the path is non-empty. (you do not // want to make the empty path "" the root path "/" ) string path = img_path; if (path.length() > 0 && path[path.length() - 1] != '/') path = path + "/"; vector<string> graph_files; string search_prefix; graphlab::fs_util::list_files_with_prefix(path, search_prefix, graph_files); if (graph_files.size() == 0) logstream(LOG_WARNING) << "No files found in " << path << std::endl; // vertex data & id graphlab::vertex_id_type vid(-1); /////////////////////////////////////////////////////// // Loop over files for(size_t i = 0; i < graph_files.size(); ++i) { // Each machine loads corresponding file if (i % dc.numprocs() == dc.procid()) { if (opts.verbose > 0) logstream(LOG_EMPH) << "Process: " << dc.procid() << "/" << dc.numprocs() << " " << "picked image: " << graph_files[i] << "\n"; vid = i; vertex_data vdata; vdata.empty = false; vdata.img_path = graph_files[i]; vdata.features.img_idx = i; graph.add_vertex(vid, vdata); } } return true; }
WorkGraphPolicy( const graph_type & arg_graph ) : m_graph(arg_graph) , m_queue( view_alloc( "queue" , WithoutInitializing ) , arg_graph.numRows() * 2 + 2 ) { { // Initialize using policy_type = RangePolicy<std::int32_t, execution_space, TagInit>; using closure_type = Kokkos::Impl::ParallelFor<self_type, policy_type>; const closure_type closure(*this, policy_type(0, m_queue.size())); closure.execute(); execution_space::fence(); } { // execute-after counts using policy_type = RangePolicy<std::int32_t, execution_space, TagCount>; using closure_type = Kokkos::Impl::ParallelFor<self_type, policy_type>; const closure_type closure(*this,policy_type(0,m_graph.entries.size())); closure.execute(); execution_space::fence(); } { // Scheduling ready tasks using policy_type = RangePolicy<std::int32_t, execution_space, TagReady>; using closure_type = Kokkos::Impl::ParallelFor<self_type, policy_type>; const closure_type closure(*this,policy_type(0,m_graph.numRows())); closure.execute(); execution_space::fence(); } }
void merge_values(vid_vdata_vector_type& element, graph_type& graph) { for (auto vertex : element) { if (graph.contains_vertex(vertex.first)) { const graphlab::lvid_type lvid = graph.local_vid(vertex.first); graph.l_vertex(lvid).data() = vertex.second; } } }
SynchronousEngine<algorithm_t>::SynchronousEngine(graph_type& graph): iteration_counter_(0), max_iterations_(5), graph_(graph) { vertex_programs_.resize(graph.num_local_vertices()); gather_accum_.resize(graph.num_local_vertices()); has_msg_.resize(graph.num_local_vertices(), 0); has_msg_.resize(graph.num_local_vertices(), message_type()); active_superstep_.resize(graph.num_local_vertices(), 0); active_minorstep_.resize(graph.num_local_vertices(), 0); }
graph_gather_apply<Graph,GatherType>::graph_gather_apply( graph_type& graph, gather_fun_type gather_fun, apply_fun_type apply_fun, const graphlab_options& opts) : gather_fun(gather_fun), apply_fun(apply_fun), rmi(graph.dc(), this), graph(graph), threads(opts.get_ncpus()), thread_barrier(opts.get_ncpus()), gather_exchange(graph.dc(), opts.get_ncpus(), 64 * 1024) { }
SynchronousEngine<algorithm_t>::SynchronousEngine(graph_type& graph): iteration_counter_(0), max_iterations_(5), graph_(graph) { vertex_programs_.resize(graph.num_local_vertices()); gather_accum_.resize(graph.num_local_vertices()); has_msg_.resize(graph.num_local_vertices(), 0); messages_.resize(graph.num_local_vertices(), message_type()); active_superstep_.resize(graph.num_local_vertices(), 0); active_minorstep_.resize(graph.num_local_vertices(), 0); context = new context_type(*this, graph); aggregator = new aggregator_type(graph, context); cout<<"sync engine init"<<endl; }
static bool dfs( const graph_type& graph, const int vertex_id, pk::vector<int, graph_type::max_num_of_vertices>& colors, pk::stack<int, graph_type::max_num_of_vertices>& s) { if(colors[vertex_id] == 1) return false; if(colors[vertex_id] == 2) return true; colors[vertex_id] = 1; const typename graph_type::adjacency_list& adjacent_edges = graph.get_adjacency_list(vertex_id); for(int i = 0; i < adjacent_edges.size(); ++i) { if(!dfs(graph, adjacent_edges[i].to, colors, s)) return false; } colors[vertex_id] = 2; s.push(vertex_id); return true; }
void write_results(char* ofile, graph_type& graph) { std::cout << "PageRank: Writing results to: " << ofile << std::endl; std::ofstream fout(ofile); assert(fout.is_open()); for(graphlab::lvid_type lvid = 0; lvid < graph.get_local_graph().num_vertices(); ++lvid) { if (!graph.l_is_master(lvid)) { continue; } size_t id = graph.global_vid(lvid); double val = graph.get_local_graph().vertex(lvid).data(); fout << id << "\t" << val << std::endl; } fout.close(); std::cout << "PageRank: Results written." << std::endl; }
void visit_next_component(const graph_type& g, const int starting_vertex_id, Sequence& visited) { pk::queue<int, graph_type::max_num_of_vertices> q; q.push(starting_vertex_id); visited[starting_vertex_id] = true; while(!q.empty()) { const int v = q.front(); q.pop(); component_ids[v] = number_of_components; const typename graph_type::adjacency_list& adj_v = g.get_adjacency_list(v); for(int i = 0; i < adj_v.size(); ++i) { const int u = adj_v[i].to; if(visited[u]) continue; q.push(u); visited[u] = true; } } }
/**\brief Attempt to pop the work item at the head of the queue. * * Find entry 'i' such that * ( m_queue[i] != BEGIN_TOKEN ) AND * ( i == 0 OR m_queue[i-1] == BEGIN_TOKEN ) * if found then * increment begin hint * return atomic_exchange( m_queue[i] , BEGIN_TOKEN ) * else if i < total work * return END_TOKEN * else * return COMPLETED_TOKEN * */ KOKKOS_INLINE_FUNCTION std::int32_t pop_work() const noexcept { const std::int32_t N = m_graph.numRows(); std::int32_t volatile * const ready_queue = & m_queue[0] ; std::int32_t volatile * const begin_hint = & m_queue[2*N] ; // begin hint is guaranteed to be less than or equal to // actual begin location in the queue. for ( std::int32_t i = *begin_hint ; i < N ; ++i ) { const std::int32_t w = ready_queue[i] ; if ( w == END_TOKEN ) { return END_TOKEN ; } if ( ( w != BEGIN_TOKEN ) && ( w == atomic_compare_exchange(ready_queue+i,w,(std::int32_t)BEGIN_TOKEN) ) ) { // Attempt to claim ready work index succeeded, // update the hint and return work index atomic_increment( begin_hint ); return w ; } // arrive here when ready_queue[i] == BEGIN_TOKEN } return COMPLETED_TOKEN ; }
// Second loader that only a single machine calls and pre-loads cameras. bool vertex_loader(graph_type& graph, string img_path, vector<CameraParams>& cameras) { // force a "/" at the end of the path // make sure to check that the path is non-empty. (you do not // want to make the empty path "" the root path "/" ) string path = img_path; if (path.length() > 0 && path[path.length() - 1] != '/') path = path + "/"; vector<string> graph_files; string search_prefix; graphlab::fs_util::list_files_with_prefix(path, search_prefix, graph_files); if (graph_files.size() == 0) logstream(LOG_WARNING) << "No files found in " << path << std::endl; // vertex data & id graphlab::vertex_id_type vid(-1); /////////////////////////////////////////////////////// // Loop over files for(size_t i = 0; i < graph_files.size(); ++i) { vid = i; vertex_data vdata; vdata.empty = false; vdata.img_path = graph_files[i]; vdata.features.img_idx = i; vdata.camera = cameras[i]; // addition to above function. graph.add_vertex(vid, vdata); } return true; }
///////////////////////////////////////////////////////////////////////// // Load the distributed UAI file bool line_parser(graph_type& graph, const std::string& filename, const std::string& textline) { std::stringstream strm(textline); graphlab::vertex_id_type vid; vertex_data vdata; vdata.dual_contrib = 0.0; string type; strm >> type; if(type == "v") { vdata.factor_type = VAR; vdata.nvars = 1; vdata.cards.resize(1); strm>>vid; strm >> vdata.cards[0]; vdata.potentials.resize(vdata.cards[0]); //vdata.beliefs.setOnes(vdata.cards[0]); //vdata.beliefs /= vdata.cards[0]; vdata.beliefs.setConstant(vdata.cards[0], 0.5); vdata.unary_degree.resize(vdata.cards[0], 0); //for(int i=0; i< vdata.cards[0]; i++){ // strm>>vdata.potentials[i]; // vdata.potentials[i] = log10(vdata.potentials[i]); // } // vdata.potentials.maxCoeff(&vdata.best_configuration); graph.add_vertex(vid,vdata); }
KOKKOS_INLINE_FUNCTION void operator()( const TagCount , int i ) const noexcept { std::int32_t volatile * const count_queue = & m_queue[ m_graph.numRows() ] ; atomic_increment( count_queue + m_graph.entries[i] ); }
KOKKOS_INLINE_FUNCTION void operator()( const TagReady , int w ) const noexcept { std::int32_t const * const count_queue = & m_queue[ m_graph.numRows() ] ; if ( 0 == count_queue[w] ) push_work(w); }
void test_contains_edge(graph_type g, vector<typename graph_type::location_type> present_locs, vector<typename graph_type::location_type> absent_locs) { for (auto loc : present_locs) { if (!g.contains_edge(loc)) { cout << "Test contains_egde failed on " << loc.first << ',' << loc.second << '\n'; exit(1); } } for (auto loc : absent_locs) { if (g.contains_edge(loc)) { cout << "Test does not contains_egde failed on " << loc.first << ',' << loc.second << '\n'; exit(1); } } cout << "Contains edge passed\n"; }
bool line_parser(graph_type& graph, const std::string& filename, const std::string& textline) { std::istringstream ssin(textline); graphlab::vertex_id_type vid; ssin >> vid; int out_nb; ssin >> out_nb; if (out_nb == 0) graph.add_vertex(vid); while (out_nb--) { graphlab::vertex_id_type other_vid; ssin >> other_vid; graph.add_edge(vid, other_vid); } return true; }
void test_delete_edges(graph_type g, vector<typename graph_type::location_type> kill_edges, vector<weight_type> expected_weights) { for (auto k : kill_edges) { g.delete_edge(k); } test_graph_iterator(g, expected_weights); cout << "Delete edges passed\n"; }
///////////////////////////////////////////////////////////////////////// // Edge Loader (used to read the adjacency list and add edges to the graph) bool edge_loader(graph_type& graph, const std::string& fname, const std::string& textline) { if ( textline.length() == 0 || textline[0] == '#' ) return true; // empty or comment line, return std::stringstream strm(textline); graphlab::vertex_id_type vid; // first entry in the line is a vertex ID strm >> vid; if (opts.verbose > 0) logstream(LOG_EMPH) << "Here's the input: " << textline << "\n" << vid << "\n"; // Line should contain at least 1 more number (degree of node) if (!strm.good()) { logstream(LOG_ERROR) << "The following ajacency list line is incomplete(check adj_list standard):\n" << textline << std::endl; return EXIT_FAILURE; } // second entry is the out-degree int outdeg; strm >> outdeg; graphlab::vertex_id_type other_vid; for (int i=0; i!=outdeg; ++i) { // Line should contain more numbers (id of neighbours) if (!strm.good()) { logstream(LOG_ERROR) << "The following ajacency list line is incomplete(check adj_list standard):\n" << textline << std::endl; return EXIT_FAILURE; } strm >> other_vid; // only add edges in one direction if (other_vid < vid) continue; if (opts.verbose > 0) logstream(LOG_EMPH) << "Adding edge: (" << vid << "," << other_vid << ")\n"; edge_data edata; edata.empty = false; graph.add_edge(vid,other_vid,edata); } return true; }
void test_edges_at(graph_type g, vector<typename graph_type::location_type> modify_edges, weight_type new_value, vector<weight_type> expected_weights) { for (auto m : modify_edges) { g.edge_at(m).weight() = new_value; } test_graph_iterator(g, expected_weights); cout << "Edges at passed\n"; }
KOKKOS_INLINE_FUNCTION void completed_work( std::int32_t w ) const noexcept { Kokkos::memory_fence(); // Make sure the completed work function's memory accesses are flushed. const std::int32_t N = m_graph.numRows(); std::int32_t volatile * const count_queue = & m_queue[N] ; const std::int32_t B = m_graph.row_map(w); const std::int32_t E = m_graph.row_map(w+1); for ( std::int32_t i = B ; i < E ; ++i ) { const std::int32_t j = m_graph.entries(i); if ( 1 == atomic_fetch_add(count_queue+j,-1) ) { push_work(j); } } }
bool parse_vertex_line(graph_type &graph, const std::string &file, const std::string &line) { if (line.empty() || line[0] == '#') { return true; } char *dst; size_t id = strtoul(line.c_str(), &dst, 10); if (dst == line.c_str()) return false; graph.add_vertex(id); return true; }
static bool run(const graph_type& graph, output_iterator output) { pk::vector<int, graph_type::max_num_of_vertices> colors(0, graph.get_num_of_vertices()); pk::stack<int, graph_type::max_num_of_vertices> s; for(int v = 0; v < graph.get_num_of_vertices(); ++v) { if(colors[v] != 0) continue; if(!dfs(graph, v, colors, s)) return false; } while(!s.empty()) { *output++ = s.top(); s.pop(); } return true; }
void find_components(const graph_type& g) { number_of_components = 0; pk::vector<bool, graph_type::max_num_of_vertices> visited(false, g.get_num_of_vertices()); for(int u = 0; u < visited.size(); ++u) { if(!visited[u]) { visit_next_component(g, u, visited); ++number_of_components; } } }
bool parse_edge_line(graph_type &graph, const std::string &file, const std::string &line) { if (line.empty() || line[0] == '#') { return true; } char *dst; size_t source = strtoul(line.c_str(), &dst, 10); if (dst == line.c_str()) return false; char *end; size_t target = strtoul(dst, &end, 10); if (dst == end) return false; if (source != target) graph.add_edge(source, target); return true; }
KOKKOS_INLINE_FUNCTION void push_work( const std::int32_t w ) const noexcept { const std::int32_t N = m_graph.numRows(); std::int32_t volatile * const ready_queue = & m_queue[0] ; std::int32_t volatile * const end_hint = & m_queue[2*N+1] ; // Push work to end of queue const std::int32_t j = atomic_fetch_add( end_hint , 1 ); if ( ( N <= j ) || ( END_TOKEN != atomic_exchange(ready_queue+j,w) ) ) { // ERROR: past the end of queue or did not replace END_TOKEN Kokkos::abort("WorkGraphPolicy push_work error"); } memory_fence(); }
///////////////////////////////////////////////////////////////////////// // Load the UAI file. Each factor as a different vertex void loadUAIfile(graphlab::distributed_control& dc, graph_type& graph, string graph_file) { // Not sure why this is needed dc.barrier(); // Open file ifstream in(graph_file.c_str()); //CHECK(in.good(),"Could not open file: "+graph_file); CHECK(in.good()); // Read type of network string name; in >> name; //CHECK(name.compare("MARKOV")==0, "Only Markov networks are supported. Are you sure this is a typeUAI energy file?"); CHECK(name.compare("MARKOV")==0); // Read size of graph int nnodes, nfactors; in >> nnodes; //CHECK(nnodes>0, "No. of nodes can't be negative. Are you sure this is a typeUAI energy file?"); CHECK(nnodes>0); // Read node cardinalities vector<int> cardinalities(nnodes,0); int cardinality_i, sum_of_cardinalities = 0; for (int i = 0; i != nnodes; ++i) { in >> cardinality_i; cardinalities[i] = cardinality_i; sum_of_cardinalities += cardinality_i; //cout << cardinalities[i] << " "; //CHECK(in.good(), "Could not finish reading cardinalities. Are you sure this is a typeUAI energy file?"); CHECK(in.good()); } // Read no. of factors in >> nfactors; //factor_size.resize(nfactors); factor_id.resize(nfactors); vector<int> factor_size(nfactors,0); //vector<int> factor_id(nfactors,0); vector< vector<int> > factor_memb; factor_memb.resize(nfactors); int temp1, temp2; // Loop and read factor members for (int i=0; i!=nfactors; ++i) { in >> temp1; factor_size[i] = temp1; factor_memb[i].resize(temp1); for (int j=0; j!=temp1; ++j) { in >> temp2; factor_memb[i][j] = temp2; } //CHECK(in.good(), "Could not finish reading cardinalities. Are you sure this is a typeUAI energy file?"); CHECK(in.good()); } if (opts.verbose > 0) cout << "Finished Reading UAI-Preamble:" << " #Nodes = " << nnodes << ", #Factors = "<< nfactors << ", Average Cardinality = " << double(sum_of_cardinalities)/nfactors << "\n"; // Now read factor potentials for (int i=0; i!=nfactors; ++i) { int cardprod; double potential_value; //, energy; in >> cardprod; vertex_data vdata; vdata.nvars = factor_size[i]; if (vdata.nvars > 1) { vdata.degree = vdata.nvars; // Factor degree. } vdata.cards.resize(factor_size[i]); vdata.neighbors.resize(factor_size[i]); vector<edge_data> edata(factor_size[i]); int cardprod2 = 1; for (int j=0; j!=factor_size[i]; ++j) { vdata.cards[j] = cardinalities[factor_memb[i][j]]; vdata.neighbors[j] = factor_memb[i][j]; // afm (check if this was intended!) cardprod2 *= vdata.cards[j]; // Also create edge structs here if (factor_size[i]>1) { edata[j].varid = factor_memb[i][j]; edata[j].card = cardinalities[edata[j].varid]; edata[j].multiplier_messages.setZero(edata[j].card); edata[j].local_messages.setZero(edata[j].card); } } //CHECK_EQ(cardprod, cardprod2, "Incorrectly sized factor"); CHECK_EQ(cardprod, cardprod2); // Read factor potentials vdata.potentials.resize(cardprod); for (int k = 0; k != cardprod; ++k) { in >> potential_value; //energy = Potential2Energy(potential_value); vdata.potentials[k] = log10(potential_value); } //CHECK(in.good(), "Could not finish reading factor tables. Are you sure this is a typeUAI energy file?"); CHECK(in.good()); // allocate factors evenly to different machines. if (i%dc.numprocs() != dc.procid()) continue; // If all is well, add vertex and edges graph.add_vertex(i,vdata); if (factor_size[i] > 1) // if not a unary, add edges to unaries for (int j=0; j!=factor_size[i]; ++j) graph.add_edge(i,edata[j].varid,edata[j]); if (opts.verbose > 1) { cout << "Machine #" << dc.procid() << ", Vertex Id = " << i << " with " << vdata.nvars << " variables."; if (factor_size[i] > 1) { cout << ", Edges = "; for (int j=0; j!=factor_size[i]; ++j) cout << ", (" << i << "," << edata[j].varid << ")"; } cout << "\n"; cout << "potential: " << vdata.potentials << "\n"; } } // End of reading factors dc.barrier(); } // end of loading UAI file
void test_aggregator(graphlab::distributed_control& dc, graphlab::command_line_options& clopts, graph_type& graph) { std::cout << "Constructing an engine for all neighbors" << std::endl; agg_engine_type engine(dc, graph, clopts); engine.add_vertex_aggregator<size_t>("num_vertices_counter", agg_map, agg_finalize); engine.add_edge_aggregator<size_t>("num_edges_counter", agg_edge_map, agg_edge_finalize); // reset all graph.transform_vertices(set_vertex_to_one); graph.transform_edges(set_edge_to_one); ASSERT_EQ(graph.map_reduce_vertices<size_t>(identity_vertex_map), graph.num_vertices()); graph.transform_vertices(vertex_plus_one); ASSERT_EQ(graph.map_reduce_vertices<size_t>(identity_vertex_map), 2 * graph.num_vertices()); engine.transform_vertices(vertex_minus_one_context); ASSERT_EQ(graph.map_reduce_vertices<size_t>(identity_vertex_map), graph.num_vertices()); ASSERT_EQ(engine.map_reduce_vertices<size_t>(identity_vertex_map_context), graph.num_vertices()); ASSERT_EQ(graph.map_reduce_edges<size_t>(identity_edge_map), graph.num_edges()); graph.transform_edges(edge_plus_one); ASSERT_EQ(graph.map_reduce_edges<size_t>(identity_edge_map), 2 * graph.num_edges()); engine.transform_edges(edge_minus_one_context); ASSERT_EQ(graph.map_reduce_edges<size_t>(identity_edge_map), graph.num_edges()); ASSERT_EQ(engine.map_reduce_edges<size_t>(identity_edge_map_context), graph.num_edges()); ASSERT_TRUE(engine.aggregate_now("num_vertices_counter")); ASSERT_TRUE(engine.aggregate_now("num_edges_counter")); ASSERT_TRUE(engine.aggregate_periodic("num_vertices_counter", 0.2)); ASSERT_TRUE(engine.aggregate_periodic("num_edges_counter", 0.2)); std::cout << "Scheduling all vertices to count their neighbors" << std::endl; engine.signal_all(100); std::cout << "Running!" << std::endl; engine.start(); std::cout << "Finished" << std::endl; }
rw_lock_manager(const graph_type& graph) : graph(graph), locks(graph.num_vertices()) { }
bool graph_loader(graphlab::distributed_control& dc, graph_type& graph, string img_dir) { // force a "/" at the end of the path // make sure to check that the path is non-empty. (you do not // want to make the empty path "" the root path "/" ) string path = img_dir; if (path.length() > 0 && path[path.length() - 1] != '/') path = path + "/"; vector<string> graph_files; string search_prefix; graphlab::fs_util::list_files_with_prefix(path, search_prefix, graph_files); if (graph_files.size() == 0) logstream(LOG_WARNING) << "No files found in " << path << std::endl; if (opts.verbose > 2) logstream(LOG_EMPH) << "Total number of images: " << graph_files.size() << "\n"; // vertex data & id graphlab::vertex_id_type vid(-1); graphlab::vertex_id_type other_vid; /////////////////////////////////////////////////////// // Loop over files for(size_t i = 0; i < graph_files.size(); ++i) { // Each machine loads corresponding file if (i % dc.numprocs() == dc.procid()) { if (opts.verbose > 0) logstream(LOG_EMPH) << "Process: " << dc.procid() << "/" << dc.numprocs() << " " << "picked image: " << graph_files[i] << "\n"; vid = i; vertex_data vdata; vdata.empty = false; vdata.img_path = graph_files[i]; vdata.features.img_idx = i; graph.add_vertex(vid, vdata); if (opts.verbose > 2) logstream(LOG_EMPH) << "Vertex " << i << " Image: " << vdata.img_path << "\n"; } } // Adding edges between every pair of vertices to create a fully connected graph // no duplicate edges are added for(size_t i = 0; i < graph_files.size()-1; ++i) { vid = i; for(size_t j = i+1; j < graph_files.size(); ++j) { other_vid = j; if (opts.verbose > 0) logstream(LOG_EMPH) << "Adding edge: (" << vid << "," << other_vid << ")\n"; edge_data edata; edata.empty = false; graph.add_edge(vid,other_vid,edata); } } return true; }
// Second Graph loader that only a single machine calls and pre-loads cameras. // It adds only the selected edges to the subgraph of the fully connected graph. // (Used in stitch_full_main) bool graph_loader(graph_type& graph, string img_dir, vector<CameraParams>& cameras, vector<string>& img_path, vector<int> indices, vector<MatchesInfo>& pairwise_matches) { // force a "/" at the end of the path // make sure to check that the path is non-empty. (you do not // want to make the empty path "" the root path "/" ) string path = img_dir; if (path.length() > 0 && path[path.length() - 1] != '/') path = path + "/"; // vertex data & id graphlab::vertex_id_type vid(-1); graphlab::vertex_id_type other_vid; if (opts.verbose > 2) logstream(LOG_EMPH) << "Total number of vertices in the second graph: " << indices.size() << "\n"; /////////////////////////////////////////////////////// // Loop over files for(size_t i = 0; i < indices.size(); ++i) { vid = i; vertex_data vdata; vdata.empty = false; vdata.img_path = img_path[i]; vdata.features.img_idx = i; vdata.camera = cameras[i]; // addition to above function. graph.add_vertex(vid, vdata); } // Adding edges between selected pair of vertices to create a subgraph of the fully connected graph // no duplicate edges are added if (opts.verbose > 2) logstream(LOG_EMPH) << "Pairwise_matches size : " << pairwise_matches.size() << "\n"; for(size_t i = 0; i < pairwise_matches.size(); ++i) { if (opts.verbose > 2) logstream(LOG_EMPH) << "Pairwise_matches : (" << pairwise_matches[i].src_img_idx << "," << pairwise_matches[i].dst_img_idx << ")\n"; if (pairwise_matches[i].src_img_idx >= 0 && pairwise_matches[i].dst_img_idx >= 0) { if (pairwise_matches[i].src_img_idx < pairwise_matches[i].dst_img_idx) // no duplicate edges are allowed { vid = pairwise_matches[i].src_img_idx; other_vid = pairwise_matches[i].dst_img_idx; if (opts.verbose > 0) logstream(LOG_EMPH) << "Adding edge: (" << vid << "," << other_vid << ")\n"; edge_data edata; edata.empty = false; graph.add_edge(vid,other_vid,edata); } } } return true; }