How to copy graph properties of one boost graph into another boost graph?

70 views Asked by At

I have a boost graph with vertex , edge and graph properties. I want to make a copy of boost braph. I copied vertex and edge properties ( using copy_graph) of one boost graph into another one but could not able to copy graph properties.

using BGType = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
                                     // Vertex Properties...
                                     vertexProps,
                                     // Edge Propereties...
                                     edgeProps,
                                     // Graph Properties
                                     graphProps>;

I have graph properties :

class graphProps {
   public:
    explicit graphProps(std::string *name = nullptr) { _name = name ? *name : ""; };

    std::string _name;
    std::map<std::string, std::tuple<std::vector<schPinInfo *>,  // input Pins
                                     std::vector<schPinInfo *>,  // inout pins
                                     std::vector<schPinInfo *>>  // output pins
             >
      _modInfo;
    std::map<std::string, std::vector<std::string>> _altNames;
    std::map<std::string, schSymbol> _modSymbol;
}
struct CustomVertexCopy {
    BGType const& g1;
    BGType&       g2;

    void operator()(BGType::vertex_descriptor v1, BGType::vertex_descriptor v2) const {
        vertexProps const& p1 = g1[v1];
        vertexProps&       p2 = g2[v2];

        p2._refPtr     = p1._refPtr;
        p2._moduleName = p1._moduleName;
        p2._name       = p1._name;
    }
};

struct CustomEdgeCopy {
    BGType const& g1;
    BGType&       g2;

    void operator()(BGType::edge_descriptor e1, BGType::edge_descriptor e2) const {
        g2[e2]._name = g1[e1]._name;
    }
};

my copy_graph is like this :

OnClick(BGType* bGraph)
{
   // some code
   BGType* oldBg = new BGType;
   boost::copy_graph(*bGraph, *oldBg,
                      boost::vertex_copy(CustomVertexCopy{*bGraph,*oldBg})
                            .edge_copy(CustomEdgeCopy{*bGraph, *oldBg}));
   // some code
}

How to copy grap properties ?

1

There are 1 answers

8
sehe On

Simply put

  • boost::copy_graph doesn't support it.

  • just copy assigning it will be fine:

    boost::get_property(g2) = boost::get_property(g1)
    

Adapted from your previous question with demo:

Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <fmt/ranges.h>

class vertexProps {
  public:
    vertexProps(vertexProps const&)            = default;
    vertexProps& operator=(vertexProps const&) = default;

    explicit vertexProps(std::string mn = {}, std::string n = {}, long refPtr = 0)
        : _moduleName(std::move(mn))
        , _name(std::move(n))
        , _refPtr(refPtr) {}

    std::string _moduleName;
    std::string _name;
    long        _refPtr;
};

class edgeProps {
  public:
    edgeProps(edgeProps const&)            = default;
    edgeProps& operator=(edgeProps const&) = default;
    explicit edgeProps(std::string name = "") : _name(name){};

    std::string _name;
};

struct schPinInfo { std::string id; };
schPinInfo const wellknownPins[] = {
    {"i1"}, {"i2"}, {"i3"}, {"o4"}, {"o5"}, {"i6"}, {"i7"}, {"i8"}, {"o9"}, {"o10"},
};

static schPinInfo const* const i1  = wellknownPins + 0;
static schPinInfo const* const i2  = wellknownPins + 1;
static schPinInfo const* const i3  = wellknownPins + 2;
static schPinInfo const* const o4  = wellknownPins + 3;
static schPinInfo const* const o5  = wellknownPins + 4;
static schPinInfo const* const i6  = wellknownPins + 5;
static schPinInfo const* const i7  = wellknownPins + 6;
static schPinInfo const* const i8  = wellknownPins + 7;
static schPinInfo const* const o9  = wellknownPins + 8;
static schPinInfo const* const o10 = wellknownPins + 9;

using Pins = std::vector<schPinInfo const*>;
struct Layout {
    Pins input, inout, output;
};
struct schSymbol {};

struct graphProps {
    graphProps& operator=(graphProps const&) = default;
    explicit graphProps(std::string name = {}) : _name(std::move(name)) {}

    std::string                                     _name;
    std::map<std::string, Layout>                   _modInfo;
    std::map<std::string, std::vector<std::string>> _altNames;
    std::map<std::string, schSymbol>                _modSymbol;
};

using BGType = boost::adjacency_list<                //
    boost::vecS, boost::vecS, boost::bidirectionalS, //
    vertexProps, edgeProps, graphProps>;

int main() {
    BGType g2;

    {
        BGType g1{3, graphProps{"name"}};
        {
            boost::get_property(g1)._modInfo = {
                {"mod1", Layout{{i1, i2, i3}, {}, {o4, o5}}},
                {"mod2", Layout{{i6, i7, i8}, {}, {o9, o10}}},
            };
            boost::get_property(g1)._altNames = {
                {"mod1", {"MOD1", "MOD_1"}},
                {"mod2", {"MOD2", "MOD_2"}},
            };
            boost::get_property(g1)._modSymbol = {
                {"mod1", schSymbol{}},
                {"mod2", schSymbol{}},
            };

            g1[0] = vertexProps{"Hello", "world", 111};
            g1[1] = vertexProps{"Goodbye", "moon", 222};
            g1[2] = vertexProps{"Greetings", "Cosmos", 333};

            add_edge(0, 1, edgeProps{"one"}, g1);
            add_edge(2, 0, edgeProps{"two"}, g1);
        }

        g2 = g1; // FULL COPY
        // copy_graph(g1, g2); // doesn't copy graph property
    }

    boost::dynamic_properties dp;
    dp.property("node_id", get(boost::vertex_index, g2));
    dp.property("moduleName", get(&vertexProps::_moduleName, g2));
    dp.property("name", get(&vertexProps::_name, g2));
    dp.property("ref", get(&vertexProps::_refPtr, g2));
    dp.property("name", get(&edgeProps::_name, g2));
    dp.property("name", get(&edgeProps::_name, g2));
    write_graphviz_dp(std::cout, g2, dp);

    fmt::print("Copied _altNames: {}\n", boost::get_property(g2)._altNames);
}

Prints

digraph G {
0 [moduleName=Hello, name=world, ref=111];
1 [moduleName=Goodbye, name=moon, ref=222];
2 [moduleName=Greetings, name=Cosmos, ref=333];
0->1  [name=one, name=one];
2->0  [name=two, name=two];
}
Copied _altNames: {"mod1": ["MOD1", "MOD_1"], "mod2": ["MOD2", "MOD_2"]}

Deep Cloning Dynamic Data

What if you expected the schPinInfo objects to be deep copied as well? To correctly do object tracking so that repeated pins will not be (de)serialized more than once, I suggest Boost Serialization:

Live On Coliru

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/graph/adj_list_serialize.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <fmt/ranges.h>

class vertexProps {
  public:
    vertexProps(vertexProps const&)            = default;
    vertexProps& operator=(vertexProps const&) = default;

    explicit vertexProps(std::string mn = {}, std::string n = {}, long refPtr = 0)
        : _moduleName(std::move(mn))
        , _name(std::move(n))
        , _refPtr(refPtr) {}

    std::string _moduleName;
    std::string _name;
    long        _refPtr;
};

class edgeProps {
  public:
    edgeProps(edgeProps const&)            = default;
    edgeProps& operator=(edgeProps const&) = default;
    explicit edgeProps(std::string name = "") : _name(name){};

    std::string _name;
};

struct schPinInfo { std::string id; };
schPinInfo const wellknownPins[] = {
    {"i1"}, {"i2"}, {"i3"}, {"o4"}, {"o5"}, {"i6"}, {"i7"}, {"i8"}, {"o9"}, {"o10"},
};

static schPinInfo const* const i1  = wellknownPins + 0;
static schPinInfo const* const i2  = wellknownPins + 1;
static schPinInfo const* const i3  = wellknownPins + 2;
static schPinInfo const* const o4  = wellknownPins + 3;
static schPinInfo const* const o5  = wellknownPins + 4;
static schPinInfo const* const i6  = wellknownPins + 5;
static schPinInfo const* const i7  = wellknownPins + 6;
static schPinInfo const* const i8  = wellknownPins + 7;
static schPinInfo const* const o9  = wellknownPins + 8;
static schPinInfo const* const o10 = wellknownPins + 9;

using Pins = std::vector<schPinInfo const*>;
struct Layout {
    Pins input, inout, output;
};
struct schSymbol {};

struct graphProps {
    graphProps& operator=(graphProps const&) = default;
    explicit graphProps(std::string name = {}) : _name(std::move(name)) {}

    std::string                                     _name;
    std::map<std::string, Layout>                   _modInfo;
    std::map<std::string, std::vector<std::string>> _altNames;
    std::map<std::string, schSymbol>                _modSymbol;
};

using BGType = boost::adjacency_list<                //
    boost::vecS, boost::vecS, boost::bidirectionalS, //
    vertexProps, edgeProps, graphProps>;

void serialize(auto& ar, vertexProps& p, unsigned) { ar& p._moduleName& p._name& p._refPtr;              } 
void serialize(auto& ar, edgeProps& p, unsigned)   { ar& p._name;                                        } 
void serialize(auto& ar, graphProps& p, unsigned)  { ar& p._name& p._modInfo& p._altNames& p._modSymbol; } 
void serialize(auto&, schSymbol&, unsigned)        {                                                     } 
void serialize(auto& ar, schPinInfo& i, unsigned)  { ar& i.id;                                           } 
void serialize(auto& ar, Layout& l, unsigned)      { ar& l.input& l.inout& l.output;                     } 

BGType deep_copy(BGType const& g1) {
    std::stringstream ss;
    boost::archive::binary_oarchive(ss) << g1;

    BGType g2;
    boost::archive::binary_iarchive(ss) >> g2; 
    return g2;
}

int main() {
    BGType g2;

    {
        BGType g1{3, graphProps{"name"}};
        {
            boost::get_property(g1)._modInfo = {
                {"mod1", Layout{{i1, i2, i3}, {}, {o4, o5}}},
                {"mod2", Layout{{i6, i7, i8}, {}, {o9, o10}}},
            };
            boost::get_property(g1)._altNames = {
                {"mod1", {"MOD1", "MOD_1"}},
                {"mod2", {"MOD2", "MOD_2"}},
            };
            boost::get_property(g1)._modSymbol = {
                {"mod1", schSymbol{}},
                {"mod2", schSymbol{}},
            };

            g1[0] = vertexProps{"Hello", "world", 111};
            g1[1] = vertexProps{"Goodbye", "moon", 222};
            g1[2] = vertexProps{"Greetings", "Cosmos", 333};

            add_edge(0, 1, edgeProps{"one"}, g1);
            add_edge(2, 0, edgeProps{"two"}, g1);

            assert(boost::get_property(g1)._modInfo["mod2"].input.at(0) == i6);
            assert(boost::get_property(g1)._modInfo["mod2"].input.at(1) == i7);
            assert(boost::get_property(g1)._modInfo["mod2"].input.at(2) == i8);
            assert(boost::get_property(g1)._modInfo["mod2"].output.at(0) == o9);
            assert(boost::get_property(g1)._modInfo["mod2"].output.at(1) == o10);
        }

        g2 = deep_copy(g1); // even deeply copied schPinInfo const* objects!

        assert(boost::get_property(g2)._modInfo["mod2"].input.at(0) != i6);
        assert(boost::get_property(g2)._modInfo["mod2"].input.at(1) != i7);
        assert(boost::get_property(g2)._modInfo["mod2"].input.at(2) != i8);
        assert(boost::get_property(g2)._modInfo["mod2"].output.at(0) != o9);
        assert(boost::get_property(g2)._modInfo["mod2"].output.at(1) != o10);
    }

    boost::dynamic_properties dp;
    dp.property("node_id", get(boost::vertex_index, g2));
    dp.property("moduleName", get(&vertexProps::_moduleName, g2));
    dp.property("name", get(&vertexProps::_name, g2));
    dp.property("ref", get(&vertexProps::_refPtr, g2));
    dp.property("name", get(&edgeProps::_name, g2));
    dp.property("name", get(&edgeProps::_name, g2));
    write_graphviz_dp(std::cout, g2, dp);

    fmt::print("Copied _altNames: {}\n", boost::get_property(g2)._altNames);

    // note the deeply allocated schPinInfo are leaked!
}

Printing

digraph G {
0 [moduleName=Hello, name=world, ref=111];
1 [moduleName=Goodbye, name=moon, ref=222];
2 [moduleName=Greetings, name=Cosmos, ref=333];
0->1  [name=one, name=one];
2->0  [name=two, name=two];
}
Copied _altNames: {"mod1": ["MOD1", "MOD_1"], "mod2": ["MOD2", "MOD_2"]}

Notes:

  • the assertions all pass - making the deepcloned schPinInfo tangible.

  • the deserialized instanced of schPinInfo are now leaked, which is why you should not be using raw pointers:

    ==794912==ERROR: LeakSanitizer: detected memory leaks
    
    Direct leak of 320 byte(s) in 10 object(s) allocated from:
        #0 0x7f11ffec21e7 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
        #1 0x55698a066997 in boost::archive::detail::heap_allocation<schPinInfo>::doesnt_have_new_operator::invoke_new() /home/sehe/custom/superboost/boost/archive/detail/iserializer.hpp:243
        #2 0x55698a066997 in boost::archive::detail::heap_allocation<schPinInfo>::invoke_new() /home/sehe/custom/superboost/boost/archive/detail/iserializer.hpp:257
        #3 0x55698a066997 in boost::archive::detail::heap_allocation<schPinInfo>::heap_allocation() /home/sehe/custom/superboost/boost/archive/detail/iserializer.hpp:270
        #4 0x55698a066997 in boost::archive::detail::pointer_iserializer<boost::archive::binary_iarchive, schPinInfo>::heap_allocation() const /home/sehe/custom/superboost/boost/archive/detail/iserializer.hpp:295
    
    SUMMARY: AddressSanitizer: 320 byte(s) leaked in 10 allocation(s).