Accessing specific edges in boost::graph with integer index

前端 未结 1 1484
春和景丽
春和景丽 2020-12-21 02:52

This is related to a question I had yesterday about accessing vertices using integer indices. That thread is here: Accessing specific vertices in boost::graph

The so

相关标签:
1条回答
  • 2020-12-21 03:25

    You can only do this if you

    • use a different graph model
    • an external edge index

    Concepts

    You could be interested in the AdjacencyMatrix concept. It doesn't exactly sport integral edge ids, but AdjacencyMatrix has lookup of edge by source/target vertices as well.

    To get truly integral edge descriptors, you'd probably need write your own graph model class (modeling a set of existing BGL concepts). You might also be interested in grid_graph<> (which has a fixed set of numbered edges per vertex, where the vertices are a grid).

    • How to access edge_descriptor with given vertex_descriptor in boost::grid_graph - you could devise a "global" numering scheme and thus get linear lookup time

    Adjacency List

    Here's a modification from the previous answer showing an external index. It's akin to your solution. I chose bimap so at least you get the reverse lookup "automagically".

    // Create edges
    boost::bimaps::bimap<int, Graph::edge_descriptor> edge_idx;
    
    auto new_edge_pair = [&,edge_id=0](int from, int to) mutable {
        auto single = [&](int from, int to) {
            auto d = add_edge(from, to, EdgeProperty { edge_id, 4 }, g).first;
            if (!edge_idx.insert({edge_id++, d}).second)
                throw std::invalid_argument("duplicate key");
            return d;
        };
    
        auto a = single(from, to), b = single(to, from);
        rev[a] = b;
        rev[b] = a;
    };
    
    new_edge_pair(0, 1);
    new_edge_pair(0, 2);
    new_edge_pair(1, 3);
    new_edge_pair(2, 3);
    

    Now you can do the loop by edge id:

    auto& by_id = edge_idx.left;
    for (auto const& e : by_id) {
        std::cout << "Edge #" << e.first << " is (" << source(e.second, g) << " -> " << target(e.second, g) << ")\n";
    }
    

    You can directly lookup an edge by it's id:

    auto ed = by_id.at(random);
    std::cout << "Random edge #" << random << " is (" << source(ed, g) << " -> " << target(ed, g) << ")\n";
    

    The reverse lookup is a bit redundant, because you can do the same using BGL quite easily:

    std::cout << "Reverse lookup: " << by_desc.at(ed) << "\n"; // reverse, though not very spectacular
    std::cout << "Classic property lookup: " << g[ed].id << "\n"; // because it can be done using boost easily
    

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    #include <boost/property_map/transform_value_property_map.hpp>
    #include <boost/graph/boykov_kolmogorov_max_flow.hpp>
    #include <functional>
    #include <iostream>
    
    #include <boost/bimap.hpp>
    #include <random>
    
    std::mt19937 prng { std::random_device{}() };
    
    using namespace boost;
    
    struct VertexProperty { std::string name; };
    
    struct EdgeProperty {
        int id;
        double capacity, residual_capacity;
    
        EdgeProperty(int id, double cap, double res = 0)
            : id(id), capacity(cap), residual_capacity(res)
        { }
    };
    
    typedef adjacency_list<vecS, vecS, directedS, VertexProperty, EdgeProperty> Graph;
    
    int main() {
        int nonodes = 4;
        Graph g(nonodes);
    
        // reverse edge map
        auto rev    = make_vector_property_map<Graph::edge_descriptor>(get(&EdgeProperty::id, g));
    
        // Create edges
        boost::bimaps::bimap<int, Graph::edge_descriptor> edge_idx;
    
        auto new_edge_pair = [&,edge_id=0](int from, int to) mutable {
            auto single = [&](int from, int to) {
                auto d = add_edge(from, to, EdgeProperty { edge_id, 4 }, g).first;
                if (!edge_idx.insert({edge_id++, d}).second)
                    throw std::invalid_argument("duplicate key");
                return d;
            };
    
            auto a = single(from, to), b = single(to, from);
            rev[a] = b;
            rev[b] = a;
        };
    
        new_edge_pair(0, 1);
        new_edge_pair(0, 2);
        new_edge_pair(1, 3);
        new_edge_pair(2, 3);
    
        // property maps
        struct VertexEx {
            default_color_type color;
            double distance;
            Graph::edge_descriptor pred;
        };
    
        auto idx    = get(vertex_index, g);
        auto vex    = make_vector_property_map<VertexEx>(idx);
        auto pred   = make_transform_value_property_map(std::mem_fn(&VertexEx::pred),     vex);
        auto color  = make_transform_value_property_map(std::mem_fn(&VertexEx::color),    vex);
        auto dist   = make_transform_value_property_map(std::mem_fn(&VertexEx::distance), vex);
    
        auto cap    = get(&EdgeProperty::capacity, g);
        auto rescap = get(&EdgeProperty::residual_capacity, g);
    
        // algorithm
        double flow = boykov_kolmogorov_max_flow(g, cap, rescap, rev, pred, color, dist, idx, 0, 3);
        std::cout << "Flow: " << flow << "\n";
    
        {
            auto& by_id   = edge_idx.left;
            auto& by_desc = edge_idx.right;
    
            for (auto const& e : edge_idx.left) {
                std::cout << "Edge #" << e.first << " is (" << source(e.second, g) << " -> " << target(e.second, g) << ")\n";
            }
            int random = prng() % num_edges(g);
            auto ed = by_id.at(random);
            std::cout << "Random edge #" << random << " is (" << source(ed, g) << " -> " << target(ed, g) << ")\n";
    
            std::cout << "Reverse lookup: " << by_desc.at(ed) << "\n"; // reverse, though not very spectacular
            std::cout << "Classic property lookup: " << g[ed].id << "\n"; // because it can be done using boost easily
        }
    }
    

    Printing

    Flow: 8
    Edge #0 is (0 -> 1)
    Edge #1 is (1 -> 0)
    Edge #2 is (0 -> 2)
    Edge #3 is (2 -> 0)
    Edge #4 is (1 -> 3)
    Edge #5 is (3 -> 1)
    Edge #6 is (2 -> 3)
    Edge #7 is (3 -> 2)
    Random edge #2 is (0 -> 2)
    Reverse lookup: 2
    Classic property lookup: 2
    

    Adjacency Matrix

    Keeps everything the same, except for changing the model:

    #include <boost/graph/adjacency_matrix.hpp>
    typedef adjacency_matrix<directedS, VertexProperty, EdgeProperty> Graph;
    

    And now you get the added capability of lookup by vertices:

    Live On Coliru

    std::cout << "Finding (3, 1) results in Edge #" << by_desc.at(edge(3, 1, g).first) << "\n";
    

    Prints

    Finding (3, 1) results in Edge #5
    
    0 讨论(0)
提交回复
热议问题