Skip to content

Commit fb4a8fe

Browse files
committed
hypergraph io tests
1 parent ee60c42 commit fb4a8fe

20 files changed

Lines changed: 656 additions & 248 deletions

include/gl/graph.hpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "gl/attributes/force_inline.hpp"
88
#include "gl/constants.hpp"
9+
#include "gl/directional_tags.hpp"
910
#include "gl/graph_traits.hpp"
1011
#include "gl/impl/impl_tags.hpp"
1112
#include "gl/io/graph_fmt_traits.hpp"
@@ -782,8 +783,9 @@ class graph final {
782783
if (with_e_props) {
783784
const auto print_out_edges = [this, &os](const id_type vertex_id) {
784785
for (const auto& edge : this->out_edges(vertex_id)) {
785-
if (edge.source() != vertex_id)
786-
continue; // vertex is not the source
786+
if constexpr (std::same_as<directional_tag, undirected_t>)
787+
if (edge.other(vertex_id) > vertex_id)
788+
continue; // deduplicate edges
787789
os << edge.source() << ' ' << edge.target() << ' ' << edge.properties()
788790
<< '\n';
789791
}
@@ -798,8 +800,9 @@ class graph final {
798800

799801
const auto print_out_edges = [this, &os](const id_type vertex_id) {
800802
for (const auto& edge : this->out_edges(vertex_id)) {
801-
if (edge.source() != vertex_id)
802-
continue; // vertex is not the source
803+
if constexpr (std::same_as<directional_tag, undirected_t>)
804+
if (edge.other(vertex_id) > vertex_id)
805+
continue; // deduplicate edges
803806
os << edge.source() << ' ' << edge.target() << '\n';
804807
}
805808
};

include/gl/io/graph_fio.hpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,26 @@
99
#include <filesystem>
1010
#include <fstream>
1111

12-
namespace gl::io {
12+
namespace gl {
13+
namespace io {
1314

1415
struct write {};
1516

1617
struct append {};
1718

18-
namespace detail {
19+
} // namespace io
20+
21+
namespace traits {
1922

2023
template <typename T>
21-
concept c_io_save_mode = traits::c_one_of<T, write, append>;
24+
concept c_io_save_mode = traits::c_one_of<T, io::write, io::append>;
25+
26+
} // namespace traits
27+
28+
namespace io {
29+
namespace detail {
2230

23-
template <c_io_save_mode Mode>
31+
template <traits::c_io_save_mode Mode>
2432
requires(std::same_as<Mode, write>)
2533
[[nodiscard]] std::ofstream open_outfile(const std::filesystem::path& path) {
2634
if (std::filesystem::exists(path))
@@ -37,7 +45,7 @@ requires(std::same_as<Mode, write>)
3745
return file;
3846
}
3947

40-
template <c_io_save_mode Mode>
48+
template <traits::c_io_save_mode Mode>
4149
requires(std::same_as<Mode, append>)
4250
[[nodiscard]] std::ofstream open_outfile(const std::filesystem::path& path) {
4351
if (not std::filesystem::exists(path))
@@ -87,7 +95,7 @@ requires(std::same_as<Mode, append>)
8795

8896
} // namespace detail
8997

90-
template <traits::c_graph GraphType, detail::c_io_save_mode Mode = write>
98+
template <traits::c_graph GraphType, traits::c_io_save_mode Mode = write>
9199
void save(
92100
const GraphType& graph,
93101
const std::filesystem::path& path = "graph.gsf",
@@ -114,4 +122,5 @@ template <traits::c_graph GraphType>
114122
return graph;
115123
}
116124

117-
} // namespace gl::io
125+
} // namespace io
126+
} // namespace gl

include/hgl/io.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
#pragma once
66

77
#include "hgl/io/core.hpp"
8+
#include "hgl/io/hypergraph_fio.hpp"

include/hgl/io/hypergraph_fio.hpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2024-2026 Jakub Musiał
2+
// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl).
3+
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
4+
5+
#pragma once
6+
7+
#include "gl/io/graph_fio.hpp"
8+
#include "gl/io/options.hpp"
9+
#include "gl/io/options_manip.hpp"
10+
#include "hgl/hypergraph.hpp"
11+
12+
#include <filesystem>
13+
#include <fstream>
14+
#include <initializer_list>
15+
16+
namespace hgl::io {
17+
18+
using gl::io::append;
19+
using gl::io::write;
20+
21+
namespace detail {
22+
23+
using gl::io::detail::open_infile;
24+
using gl::io::detail::open_outfile;
25+
26+
} // namespace detail
27+
28+
template <traits::c_hypergraph HypergraphType, traits::c_io_save_mode Mode = write>
29+
void save(
30+
const HypergraphType& hypergraph,
31+
const std::filesystem::path& path = "hypergraph.hgsf",
32+
const std::initializer_list<gl::io::options_manip>& options = {}
33+
) {
34+
std::ofstream file = detail::open_outfile<Mode>(path);
35+
36+
file << gl::io::spec_fmt;
37+
for (auto option : options)
38+
file << option;
39+
40+
file << hypergraph;
41+
}
42+
43+
template <traits::c_hypergraph HypergraphType>
44+
[[nodiscard]] HypergraphType load(const std::filesystem::path& path = "hypergraph.hgsf") {
45+
std::ifstream file = detail::open_infile(path);
46+
47+
file >> gl::io::spec_fmt;
48+
49+
HypergraphType hypergraph;
50+
file >> hypergraph;
51+
52+
return hypergraph;
53+
}
54+
55+
} // namespace hgl::io
Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
#pragma once
22

3-
namespace gl_testing {
4-
53
template <typename T>
64
void discard_result(T&&) {
75
// do nothing
86
}
9-
10-
} // namespace gl_testing

tests/include/testing/common/io.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "doctest.h"
44

55
#include <concepts>
6+
#include <filesystem>
67
#include <ranges>
78
#include <sstream>
89
#include <string_view>
@@ -23,3 +24,22 @@ struct StringMaker<R> {
2324
};
2425

2526
} // namespace doctest
27+
28+
namespace fs = std::filesystem;
29+
30+
#define GL_REQUIRE_THROWS_FS_ERROR(expr, errc) \
31+
try { \
32+
expr; \
33+
FAIL("Expected `std::filesystem::filesystem_error` but no exception was thrown"); \
34+
} \
35+
catch (const fs::filesystem_error& e) { \
36+
const auto expected_code = std::make_error_code(errc); \
37+
if (e.code() != expected_code) { \
38+
FAIL(std::format( \
39+
"Expected error code {}, but got {}.", expected_code.value(), e.code().value() \
40+
)); \
41+
} \
42+
} \
43+
catch (...) { \
44+
FAIL("Expected `std::filesystem::filesystem_error` but caught a different exception"); \
45+
}

tests/include/testing/hgl/io.hpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#pragma once
2+
3+
#include "doctest.h"
4+
5+
#include <hgl/hypergraph.hpp>
6+
7+
#include <algorithm>
8+
9+
namespace hgl_testing {
10+
11+
template <hgl::traits::c_hypergraph HypergraphType>
12+
void verify_hypergraph_structure(const HypergraphType& actual, const HypergraphType& expected) {
13+
REQUIRE_EQ(actual.order(), expected.order());
14+
REQUIRE_EQ(actual.size(), expected.size());
15+
16+
for (const auto& he_actual : actual.hyperedges()) {
17+
const auto id = he_actual.id();
18+
REQUIRE(expected.has_hyperedge(id));
19+
20+
if constexpr (std::same_as<typename HypergraphType::directional_tag, hgl::undirected_t>) {
21+
auto actual_vs = actual.incident_vertex_ids(id) | std::ranges::to<std::vector>();
22+
auto expected_vs = expected.incident_vertex_ids(id) | std::ranges::to<std::vector>();
23+
CHECK(std::ranges::is_permutation(actual_vs, expected_vs));
24+
}
25+
else if constexpr (std::same_as<
26+
typename HypergraphType::directional_tag,
27+
hgl::bf_directed_t>) {
28+
auto actual_tail = actual.tail_vertex_ids(id) | std::ranges::to<std::vector>();
29+
auto expected_tail = expected.tail_vertex_ids(id) | std::ranges::to<std::vector>();
30+
CHECK(std::ranges::is_permutation(actual_tail, expected_tail));
31+
32+
auto actual_head = actual.head_vertex_ids(id) | std::ranges::to<std::vector>();
33+
auto expected_head = expected.head_vertex_ids(id) | std::ranges::to<std::vector>();
34+
CHECK(std::ranges::is_permutation(actual_head, expected_head));
35+
}
36+
}
37+
}
38+
39+
template <hgl::traits::c_hypergraph HypergraphType>
40+
void verify_vertex_properties(const HypergraphType& actual, const HypergraphType& expected) {
41+
const auto properties_proj = [](const auto& item) { return item.properties(); };
42+
43+
CHECK(std::ranges::equal(
44+
actual.vertices(),
45+
expected.vertices(),
46+
std::ranges::equal_to{},
47+
properties_proj,
48+
properties_proj
49+
));
50+
}
51+
52+
template <hgl::traits::c_hypergraph HypergraphType>
53+
void verify_hyperedge_properties(const HypergraphType& actual, const HypergraphType& expected) {
54+
const auto properties_proj = [](const auto& item) { return item.properties(); };
55+
56+
CHECK(std::ranges::equal(
57+
actual.hyperedges(),
58+
expected.hyperedges(),
59+
std::ranges::equal_to{},
60+
properties_proj,
61+
properties_proj
62+
));
63+
}
64+
65+
} // namespace hgl_testing

tests/source/gl/test_adjacency_list.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
#include "testing/common/functional.hpp"
12
#include "testing/gl/constants.hpp"
2-
#include "testing/gl/functional.hpp"
33

44
#include <gl/graph_traits.hpp>
55
#include <gl/impl/adjacency_list.hpp>

tests/source/gl/test_adjacency_matrix.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "gl/types/core.hpp"
2+
#include "testing/common/functional.hpp"
23
#include "testing/gl/constants.hpp"
3-
#include "testing/gl/functional.hpp"
44

55
#include <gl/graph_traits.hpp>
66
#include <gl/impl/adjacency_matrix.hpp>

0 commit comments

Comments
 (0)