【问题标题】:BOOST Planar Face Traversal not visiting outer edges correctlyBOOST 平面遍历未正确访问外边缘
【发布时间】:2021-05-20 16:37:52
【问题描述】:

我想保存图表的外部节点。为此,我有以下代码(我正在展示我认为与问题相关的部分,我确实有所有#include 标头等......):

typedef adjacency_list <
    setS, 
    vecS, 
    undirectedS, 
    property<vertex_index_t, int>, 
    property<edge_index_t, int> 
    > SimpleGraph;

struct output_visitor : public planar_face_traversal_visitor{
    int c = 0;
    vector<int> vertexOuter;
    vector<int> save_vertexOuter;
    void begin_face(){cout << "New face: ";};
    void end_face(){
        if (c>3){
            save_vertexOuter = vertexOuter; 
        }
        cout << "End face "<< endl;
        vertexOuter.clear();
        c = 0;
    };
};

struct vertex_output_visitor : public output_visitor
{
    template <typename Vertex>

    void next_vertex(Vertex v){
        cout << v << " ";
        vertexOuter.push_back(v);
        c ++;
    }
};

SimpleGraph BuildGraph(int i, vector<vector<int>> structureInfo){
    
    // arguments of the function are not relevant here
    // I am just passing graphs from a dataset

    int j;
    SimpleGraph g;
    vector <int> adjList = structureInfo[i];

    for(int j =0; j<adjList.size(); j++){
        cout << adjList[j] << " ";
    }
    
    for (j = 0; j<adjList.size()-1; j+=2){
        add_edge(adjList[j], adjList[j+1], g);
    }

    return g;
}

SimpleGraph tempGSN = BuildGraph(i, gs_N);
vector<vec_t> embedding(num_vertices(tempGSN));
bool is_planar = boost::boyer_myrvold_planarity_test(tempGSN, boost::boyer_myrvold_params::embedding = &embedding[0]);

if (is_planar){
        
            vertex_output_visitor v_vis;
            planar_face_traversal(tempGSN, &embedding[0], v_vis);
           
            for (int j = 0; j < v_vis.save_vertexOuter.size()-1; j+=1 ){
                 Do something;}
}

到目前为止,代码在小图(节点数

0 3 0 4 0 6 0 7 1 3 1 5 1 6 2 4 2 5 2 6 3 6 3 7 4 6 5 6

返回

New face: 0 3 1 5 2 4 End face 
New face: 6 End face 
New face: 7 End face 

这没有意义。正确的外表面应该是 0 7 3 1 5 2 4。

如果您能帮我找出问题,我将不胜感激。谢谢!

【问题讨论】:

    标签: c++ boost planar-graph


    【解决方案1】:

    将您的图解释为边缘列表(不是邻接列表,因为这似乎没有意义):

    我发现你的 0 7 3 1 5 2 4 序列源于平面图表示:

    所以我同意有些不对劲。我清理/简化了您的代码,并将其扩展为潜在原因的偏移,但没有任何偏移导致行为发生任何变化。

    我获得的唯一见解——从上面的平面图和原始嵌入的输出都可以直观地理解,就是第 7 点造成了问题。

    v = 0: (0,3) (0,7) (0,6) (0,4)
    v = 1: (1,5) (1,6) (3,1)
    v = 2: (2,4) (2,6) (5,2)
    v = 3: (3,1) (3,6) (3,7) (0,3)
    v = 4: (0,4) (4,6) (2,4)
    v = 5: (5,2) (5,6) (1,5)
    v = 6: (0,6) (3,6) (1,6) (5,6) (2,6) (4,6)
    v = 7: (0,7) (3,7)
    

    如果您查看顶点 0 的边的顺序,您会注意到它们的顺时针顺序不一致(再次参考图像)。

    这可能是一个错误,或者我误解了平面性测试算法的全部先决条件。我建议您在 Boost 开发人员处提出这个问题。

    完整清单

    这包括:

    • various docs 中概述的可选 make_connected/make_biconnected_planar/make_maximum_planar 步骤
    • 允许从输入中删除特定顶点的命令行选项
    • 带有原始嵌入调试转储的原始代码
    • 尝试了规范的排序和绘制代码(最终只是确认没有正确生成嵌入)

    Live On Compiler Explorero

    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/planar_face_traversal.hpp>
    #include <boost/graph/boyer_myrvold_planar_test.hpp>
    #include <boost/graph/make_connected.hpp>
    #include <boost/graph/make_biconnected_planar.hpp>
    #include <boost/graph/make_maximal_planar.hpp>
    #include <boost/graph/planar_canonical_ordering.hpp>
    #include <boost/graph/chrobak_payne_drawing.hpp>
    #include <boost/graph/graphviz.hpp>
    #include <boost/fusion/adapted.hpp> // parsing
    #include <boost/spirit/home/x3.hpp> // parsing
    #include <iostream>
    
    using SimpleGraph =
        boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS,
                              boost::property<boost::vertex_index_t, int>,
                              boost::property<boost::edge_index_t, int>>;
    
    using V  = SimpleGraph::vertex_descriptor;
    using E  = SimpleGraph::edge_descriptor;
    using Vs = std::vector<V>;
    using Es = std::vector<E>;
    using Structures = std::vector<Vs>;
    using Embedding  = std::vector<Es>;
    
    struct Point { int x, y; };
    using Drawing = std::vector<Point>;
    
    struct output_visitor : public boost::planar_face_traversal_visitor {
        void begin_traversal() const { std::cout << "Planar traversal:\n"; } 
        void begin_face()            { std::cout << "Face:";               } 
        void end_face()   const      { std::cout << "\n";                  } 
        void next_vertex(V v)        { std::cout << " " << v;              } 
        void next_edge(E e) const    { std::cout << " " << e;              } 
        void end_traversal() const   { std::cout << "Done\n";              } 
    };
    
    SimpleGraph BuildGraph(std::string_view input){
        SimpleGraph g;
    
        namespace x3 = boost::spirit::x3;
        static const auto edge_
            = x3::rule<E, std::pair<V,V> >{}
            = x3::uint_ >> x3::uint_;
    
        std::vector<std::pair<size_t, size_t> > edgeList;
        if (not phrase_parse(input.begin(), input.end(),
                             +edge_ >> x3::eoi, // grammar
                             x3::space,         // skipper
                             edgeList))
            throw std::runtime_error("BuildGraph");
    
        for (auto [src, tgt] : edgeList)
            if (not add_edge(src, tgt, g).second)
                throw std::runtime_error("BuildGraph");
        return g;
    }
    
    auto parse_cli(std::set<std::string_view> cli) {
        auto extract = [&] (std::string_view name) {
            bool yn = cli.count(name);
            cli.erase(name);
            return yn;
        };
    
        struct {
            bool make_bi, make_max, traverse, dump, draw, dot;
            std::set<V, std::greater<V> > to_remove; // descending!
        } parsed{extract("--make_bi"),
            extract("--make_max"),
            extract("--traverse"),
            extract("--dump"),
            extract("--draw"),
            extract("--dot"),
            {}};
    
        extract(""); // prevent zero-length options
        for (auto opt : cli)
            if (opt.front() != '-')
                parsed.to_remove.insert(std::stoul(opt.data()));
    
        return parsed;
    }
    
    int main(int argc, char** argv) {
        auto const opts = parse_cli({ argv+1, argv+argc });
    
        namespace P = boost::boyer_myrvold_params;
    
        SimpleGraph g =
            BuildGraph("0 3 0 4 0 6 0 7 1 3 1 5 1 6 2 4 2 5 2 6 3 6 3 7 4 6 5 6");
    
        // remove some vertices?
        for (auto removal : opts.to_remove) {
            clear_vertex(removal, g);
        }
        for (auto removal: opts.to_remove) { // DESCENDING ORDER
            remove_vertex(removal, g);
        }
    
        std::cout << "Input: ";
        for (auto e : boost::make_iterator_range(edges(g))) {
            std::cout << " " << e;
        }
        std::cout << "\n";
    
        // some optional tests/preprocessing, didn't improve for given inputs
        if (opts.make_bi || opts.make_bi) {
            make_connected(g);
            Embedding embedding(num_vertices(g));
            boyer_myrvold_planarity_test(g, P::embedding = &embedding[0]);
    
            make_biconnected_planar(g, embedding.data());
        }
        if (opts.make_max) {
            Embedding embedding(num_vertices(g));
            boyer_myrvold_planarity_test(g, P::embedding = &embedding[0]);
    
            make_maximal_planar(g, embedding.data());
        }
    
        if (opts.dot) {
            boost::write_graphviz(std::cout, g);
        }
    
        Embedding embedding(num_vertices(g));
        auto emap = boost::make_iterator_property_map(
            embedding.begin(), get(boost::vertex_index, g));
        bool planar = boyer_myrvold_planarity_test(g, P::embedding = emap);
    
        if (planar) {
            if (opts.traverse) {
                output_visitor v_vis;
                planar_face_traversal(g, emap, v_vis);
            }
    
            if (opts.dump) {
                std::cout << "Raw embedding:\n";
                for (auto v : boost::make_iterator_range(vertices(g))) {
                    std::cout << "v = " << v << ":";
                    for (auto e : embedding.at(v)) {
                        std::cout << " " << e;
                    }
                    std::cout << "\n";
                }
            }
    
            if (opts.draw) {
                Vs ordering;
                planar_canonical_ordering(g, emap, std::back_inserter(ordering));
                for (auto v : ordering) {
                    std::cout << " " << v;
                }
                std::cout << "\n";
    
                Drawing drawing(num_vertices(g));
    
                // Compute the straight line drawing
                chrobak_payne_straight_line_drawing(g,
                        emap,
                        ordering.begin(), ordering.end(),
                        &drawing[0]);
    
                std::cout << "The straight line drawing is: " << std::endl;
                
                for (auto v : boost::make_iterator_range(vertices(g))) {
                    auto [x,y] =  drawing.at(v);
                    std::cout << v << " -> (" << x << ", " << y << ")\n";
                }
            }
        } else {
            std::cout << "Not planar\n";
        }
    
        return planar? 0 : 1;
    }
    

    原文:(--dump --traverse --make_bi):

    Input:  (0,3) (0,4) (0,6) (0,7) (1,3) (1,5) (1,6) (2,4) (2,5) (2,6) (3,6) (3,7) (4,6) (5,6)
    Planar traversal:
    Face: 0 (0,3) 3 (3,1) 1 (1,5) 5 (5,2) 2 (2,4) 4 (0,4)
    Face: 6 (0,6)
    Face: 7 (0,7)
    Done
    Raw embedding:
    v = 0: (0,3) (0,7) (0,6) (0,4)
    v = 1: (1,5) (1,6) (3,1)
    v = 2: (2,4) (2,6) (5,2)
    v = 3: (3,1) (3,6) (3,7) (0,3)
    v = 4: (0,4) (4,6) (2,4)
    v = 5: (5,2) (5,6) (1,5)
    v = 6: (0,6) (3,6) (1,6) (5,6) (2,6) (4,6)
    v = 7: (0,7) (3,7)
    

    删除顶点 7:(--traverse 7https://godbolt.org/z/7e3Mfx

    Input:  (0,3) (0,4) (0,6) (1,3) (1,5) (1,6) (2,4) (2,5) (2,6) (3,6) (4,6) (5,6)
    Planar traversal:
    Face: 0 (0,3) 3 (3,1) 1 (1,5) 5 (5,2) 2 (2,4) 4 (0,4)
    Face: 6 (0,6)
    Done
    

    一些高级游览:--traverse --make_max 7 --draw https://godbolt.org/z/bbdba5

    Input:  (0,3) (0,4) (0,6) (1,3) (1,5) (1,6) (2,4) (2,5) (2,6) (3,6) (4,6) (5,6)
    Planar traversal:
    Face: 0 (0,3) 3 (0,3)
    Face: 4 (0,4)
    Face: 6 (0,6)
    Face: 1 (1,3)
    Face: 5 (1,5)
    Face: 2 (2,4)
    Done
     0 1 5 2 4 6 3
    The straight line drawing is: 
    0 -> (0, 0)
    1 -> (10, 0)
    2 -> (6, 2)
    3 -> (5, 5)
    4 -> (5, 3)
    5 -> (7, 1)
    6 -> (5, 4)
    

    【讨论】:

    • 嗨@sehe,非常感谢您的意见和帮助。对“邻接列表”感到抱歉,实际上我的意思是“边缘列表”。我会听从您的建议并写信给 Boost 开发人员。我对 Boost 还是很陌生,我很难理解我是否忽略了代码中的一个明显错误。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-22
    • 1970-01-01
    • 2014-08-09
    • 2013-05-06
    相关资源
    最近更新 更多