Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions include/boost/graph/adj_matrix_serialize.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//=======================================================================
// Copyright 2026 Varun Sharma
// Authors: Varun Sharma
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#ifndef BOOST_GRAPH_ADJ_MATRIX_SERIALIZE_HPP
#define BOOST_GRAPH_ADJ_MATRIX_SERIALIZE_HPP

#include <utility>

#include <boost/core/serialization.hpp>
#include <boost/graph/adjacency_matrix.hpp>
#include <boost/pending/property_serialize.hpp>
#include <boost/type_traits/integral_constant.hpp>

namespace boost
{

namespace serialization
{

// Disable tracking for adjacency_matrix, which is not polymorphic. This
// serializer writes graph structure and properties by value and rebuilds
// edges on load; it does not preserve graph-object identity. Use this only
// when pointer identity for graph objects need not be preserved.
template < class T >
struct tracking_level_impl;

template < class Directed, class VP, class EP, class GP, class Allocator >
struct tracking_level_impl<
const boost::adjacency_matrix< Directed, VP, EP, GP, Allocator > >
: boost::integral_constant< int, 0 >
{
};

template < class Archive, class Directed, class VP, class EP, class GP, class Allocator >
inline void save(Archive& ar,
const boost::adjacency_matrix< Directed, VP, EP, GP, Allocator >& graph,
const unsigned int /* version */)
{
const auto V = boost::num_vertices(graph);
const auto E = boost::num_edges(graph);

ar << BOOST_NVP(V);
ar << BOOST_NVP(E);

auto vertex_range = boost::vertices(graph);
for (auto it = vertex_range.first; it != vertex_range.second; ++it)
{
auto&& vertex_property =
boost::get(boost::vertex_all_t(), graph, *it);
ar << BOOST_NVP(vertex_property);
}

auto edge_range = boost::edges(graph);
for (auto it = edge_range.first; it != edge_range.second; ++it)
{
const auto u = boost::source(*it, graph);
const auto v = boost::target(*it, graph);
auto&& edge_property =
boost::get(boost::edge_all_t(), graph, *it);

ar << BOOST_NVP(u);
ar << BOOST_NVP(v);
ar << BOOST_NVP(edge_property);
}

ar << boost::make_nvp(
"graph_property", boost::get_property(graph, boost::graph_all_t()));
}

template < class Archive, class Directed, class VP, class EP, class GP, class Allocator >
inline void load(Archive& ar,
boost::adjacency_matrix< Directed, VP, EP, GP, Allocator >& graph,
const unsigned int /* version */)
{
using Graph = boost::adjacency_matrix< Directed, VP, EP, GP, Allocator >;
using VertexSize = typename Graph::vertices_size_type;
using EdgeSize = typename Graph::edges_size_type;
using Vertex = typename Graph::vertex_descriptor;

VertexSize V = 0;
EdgeSize E = 0;

ar >> BOOST_NVP(V);
ar >> BOOST_NVP(E);

Graph loaded(V);

auto vertex_range = boost::vertices(loaded);
for (auto it = vertex_range.first; it != vertex_range.second; ++it)
{
auto&& vertex_property =
boost::get(boost::vertex_all_t(), loaded, *it);
ar >> BOOST_NVP(vertex_property);
}

for (EdgeSize i = 0; i < E; ++i)
{
Vertex u = 0;
Vertex v = 0;

ar >> BOOST_NVP(u);
ar >> BOOST_NVP(v);

const auto e = boost::add_edge(u, v, loaded).first;
auto&& edge_property = boost::get(boost::edge_all_t(), loaded, e);
ar >> BOOST_NVP(edge_property);
}

ar >> boost::make_nvp("graph_property",
boost::get_property(loaded, boost::graph_all_t()));

graph = std::move(loaded);
}

template < class Archive, class Directed, class VP, class EP, class GP, class Allocator >
inline void serialize(Archive& ar,
boost::adjacency_matrix< Directed, VP, EP, GP, Allocator >& graph,
const unsigned int version)
{
boost::core::split_free(ar, graph, version);
}

} // namespace serialization
} // namespace boost

#endif // BOOST_GRAPH_ADJ_MATRIX_SERIALIZE_HPP
4 changes: 4 additions & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ alias graph_test_regular :
/boost/serialization//boost_serialization
: : : ]

[ run adj_matrix_serialize.cpp
/boost/serialization//boost_serialization
: : : ]

[ compile reverse_graph_cc.cpp ]
[ run sequential_vertex_coloring.cpp ]

Expand Down
103 changes: 103 additions & 0 deletions test/adj_matrix_serialize.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2026 Varun Sharma
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/core/nvp.hpp>
#include <boost/graph/adj_matrix_serialize.hpp>
#include <boost/serialization/tracking.hpp>

#include <sstream>
#include <string>

struct vertex_properties
{
std::string name;

template < class Archive >
void serialize(Archive& ar, const unsigned int /* version */)
{
ar& boost::make_nvp("name", name);
}
};

struct edge_properties
{
int weight;

template < class Archive >
void serialize(Archive& ar, const unsigned int /* version */)
{
ar& boost::make_nvp("weight", weight);
}
};

typedef boost::adjacency_matrix< boost::directedS > DirectedGraph;
typedef boost::adjacency_matrix< boost::undirectedS, vertex_properties,
edge_properties >
UndirectedGraph;

static_assert(boost::serialization::tracking_level< DirectedGraph >::value
== boost::serialization::track_never,
"adjacency_matrix tracking_level must be track_never");
static_assert(boost::serialization::tracking_level< UndirectedGraph >::value
== boost::serialization::track_never,
"adjacency_matrix tracking_level must be track_never");

int main()
{
DirectedGraph directed(3);
boost::add_edge(0, 1, directed);
boost::add_edge(1, 2, directed);

UndirectedGraph undirected(3);
undirected[0].name = "A";
undirected[1].name = "B";
undirected[2].name = "C";

edge_properties first_edge = { 7 };
edge_properties second_edge = { 11 };
boost::add_edge(0, 1, first_edge, undirected);
boost::add_edge(1, 2, second_edge, undirected);

std::stringstream buffer;
{
boost::archive::xml_oarchive archive(buffer);
archive << boost::make_nvp("directed_graph", directed);
archive << boost::make_nvp("undirected_graph", undirected);
}

DirectedGraph restored_directed(0);
UndirectedGraph restored_undirected(0);
{
boost::archive::xml_iarchive archive(buffer);
archive >> boost::make_nvp("directed_graph", restored_directed);
archive >> boost::make_nvp("undirected_graph", restored_undirected);
}

if (boost::num_vertices(restored_directed) != 3 ||
boost::num_edges(restored_directed) != 2 ||
!boost::edge(0, 1, restored_directed).second ||
!boost::edge(1, 2, restored_directed).second ||
boost::edge(1, 0, restored_directed).second)
return 1;

if (boost::num_vertices(restored_undirected) != 3 ||
boost::num_edges(restored_undirected) != 2 ||
restored_undirected[0].name != "A" ||
restored_undirected[1].name != "B" ||
restored_undirected[2].name != "C")
return 1;

const auto first = boost::edge(0, 1, restored_undirected);
const auto second = boost::edge(1, 2, restored_undirected);
if (!first.second || !second.second ||
restored_undirected[first.first].weight != 7 ||
restored_undirected[second.first].weight != 11)
return 1;

return 0;
}
Loading