Skip to content

Commit 0121c38

Browse files
hmetis format hypergraph reader
1 parent 92369ce commit 0121c38

3 files changed

Lines changed: 236 additions & 1 deletion

File tree

apps/test_suite_runner/PartitioningTestSuiteRunner.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ limitations under the License.
2121
#include "AbstractTestSuiteRunner.hpp"
2222
#include "StringToScheduler/run_partitioner.hpp"
2323
#include "osp/auxiliary/io/mtx_hypergraph_file_reader.hpp"
24+
#include "osp/auxiliary/io/hmetis_hypergraph_file_reader.hpp"
2425
#include "osp/bsp/model/BspSchedule.hpp"
2526
#include "osp/graph_implementations/adj_list_impl/computational_dag_vector_impl.hpp"
2627
#include "osp/partitioning/model/partitioning.hpp"
@@ -134,6 +135,9 @@ int PartitioningTestSuiteRunner<GraphType>::Run(int argc, char *argv[]) {
134135
graphStatus = file_reader::ReadHypergraphMartixMarketFormat(
135136
filenameGraph, instance.GetHypergraph(), file_reader::MatrixToHypergraphFormat::ROW_NET);
136137

138+
} else if (fileEnding == "hmetis") {
139+
graphStatus = file_reader::ReadHypergraphMetisFormat(filenameGraph, instance.GetHypergraph());
140+
137141
} else {
138142
graphStatus = file_reader::ReadGraph(filenameGraph, dag);
139143
instance.SetHypergraph(ConvertFromCdagAsHyperdag<HypergraphT, GraphType>(dag));
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
Copyright 2024 Huawei Technologies Co., Ltd.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
@author Toni Boehnlein, Christos Matzoros, Benjamin Lozes, Pal Andras Papp, Raphael S. Steiner
17+
*/
18+
19+
#pragma once
20+
21+
#include <filesystem>
22+
#include <fstream>
23+
#include <iostream>
24+
#include <limits>
25+
#include <sstream>
26+
#include <string>
27+
#include <vector>
28+
29+
#include "osp/auxiliary/io/filepath_checker.hpp"
30+
#include "osp/partitioning/model/hypergraph.hpp"
31+
32+
namespace osp {
33+
namespace file_reader {
34+
35+
// reads a Hypergraph from a file in hMetis format
36+
template <typename IndexType, typename WorkwType, typename MemwType, typename CommwType>
37+
bool ReadHypergraphMetisFormat(std::ifstream &infile,
38+
Hypergraph<IndexType, WorkwType, MemwType, CommwType> &hgraph) {
39+
std::string line;
40+
41+
// Skip comments or empty lines (robustly)
42+
while (std::getline(infile, line)) {
43+
if (line.empty() || line[0] == '%') {
44+
continue;
45+
}
46+
47+
// Null byte check
48+
if (line.find('\0') != std::string::npos) {
49+
std::cerr << "Error: Null byte detected in header line.\n";
50+
return false;
51+
}
52+
53+
if (line.size() > MAX_LINE_LENGTH) {
54+
std::cerr << "Error: Line too long, possible malformed or malicious file.\n";
55+
return false;
56+
}
57+
break; // We found the actual header line
58+
}
59+
60+
if (infile.eof()) {
61+
std::cerr << "Error: Unexpected end of file while reading header.\n";
62+
return false;
63+
}
64+
65+
IndexType numVertices, numHyperedges, weightCode = 0;
66+
67+
std::istringstream headerStream(line);
68+
if (!(headerStream >> numHyperedges >> numVertices) || numVertices <= 0 || numHyperedges <= 0) {
69+
std::cerr << "Error: Invalid header.\n";
70+
return false;
71+
}
72+
73+
headerStream >> weightCode;
74+
bool hasVertexWeights = false, hasHyperedgeWeights = false;
75+
76+
switch(weightCode) {
77+
case 0:
78+
break;
79+
case 1:
80+
hasHyperedgeWeights = true;
81+
break;
82+
case 10:
83+
hasVertexWeights = true;
84+
break;
85+
case 11:
86+
hasHyperedgeWeights = true;
87+
hasVertexWeights = true;
88+
break;
89+
default:
90+
std::cerr << "Error: Invalid weight code in header (must be 0, 1, 10 or 11).\n";
91+
return false;
92+
}
93+
94+
hgraph.Reset(numVertices, 0);
95+
96+
IndexType edgesRead = 0;
97+
while (edgesRead < numHyperedges && std::getline(infile, line)) {
98+
if (line.empty() || line[0] == '%') {
99+
continue;
100+
}
101+
if (line.size() > MAX_LINE_LENGTH) {
102+
std::cerr << "Error: Line too long.\n";
103+
return false;
104+
}
105+
106+
std::istringstream edgeStream(line);
107+
108+
CommwType weight = 1;
109+
if (hasHyperedgeWeights && !(edgeStream >> weight)) {
110+
std::cerr << "Error: Malformed hyperedge row (weight).\n";
111+
return false;
112+
}
113+
114+
std::vector<IndexType> verticesInHyperedge;
115+
IndexType vertex;
116+
while (edgeStream >> vertex) {
117+
--vertex; // Convert to 0-based
118+
if (vertex < 0 || vertex >= numVertices) {
119+
std::cerr << "Error: Malformed hyperedge row (vertex entry).\n";
120+
return false;
121+
}
122+
verticesInHyperedge.push_back(vertex);
123+
}
124+
125+
if (verticesInHyperedge.empty()) {
126+
std::cerr << "Error: Empty hyperedge in file.\n";
127+
return false;
128+
}
129+
130+
std::sort(verticesInHyperedge.begin(), verticesInHyperedge.end());
131+
for (unsigned index = 0; index < verticesInHyperedge.size() - 1; ++index) {
132+
if (verticesInHyperedge[index] == verticesInHyperedge[index + 1]) {
133+
std::cerr << "Error: Malformed hyperedge row (same vertex appears multiple times).\n";
134+
return false;
135+
}
136+
}
137+
138+
hgraph.AddHyperedge(verticesInHyperedge, weight);
139+
++edgesRead;
140+
}
141+
142+
if (edgesRead != numHyperedges) {
143+
std::cerr << "Error: Incomplete hyperedge entries.\n";
144+
return false;
145+
}
146+
147+
if (hasVertexWeights) {
148+
IndexType vertexWeightsRead = 0;
149+
while (vertexWeightsRead < numVertices && std::getline(infile, line)) {
150+
if (line.empty() || line[0] == '%') {
151+
continue;
152+
}
153+
if (line.size() > MAX_LINE_LENGTH) {
154+
std::cerr << "Error: Line too long.\n";
155+
return false;
156+
}
157+
158+
std::istringstream weightStream(line);
159+
WorkwType weight = 1;
160+
if (!(weightStream >> weight)) {
161+
std::cerr << "Error: Malformed vertex weight row.\n";
162+
return false;
163+
}
164+
165+
hgraph.SetVertexWorkWeight(vertexWeightsRead, weight);
166+
167+
++vertexWeightsRead;
168+
}
169+
170+
if (vertexWeightsRead != numVertices) {
171+
std::cerr << "Error: Incomplete vertex weight lines.\n";
172+
return false;
173+
}
174+
175+
}
176+
177+
while (std::getline(infile, line)) {
178+
if (!line.empty() && line[0] != '%') {
179+
std::cerr << "Error: Extra data after hypergraph content.\n";
180+
return false;
181+
}
182+
}
183+
184+
return true;
185+
}
186+
187+
template <typename IndexType, typename WorkwType, typename MemwType, typename CommwType>
188+
bool ReadHypergraphMetisFormat(const std::string &filename,
189+
Hypergraph<IndexType, WorkwType, MemwType, CommwType> &hgraph) {
190+
// Ensure the file is .hmetis format
191+
if (std::filesystem::path(filename).extension() != ".hmetis") {
192+
std::cerr << "Error: Only .hmetis files are accepted.\n";
193+
return false;
194+
}
195+
196+
if (!IsPathSafe(filename)) {
197+
std::cerr << "Error: Unsafe file path (potential traversal attack).\n";
198+
return false;
199+
}
200+
201+
if (std::filesystem::is_symlink(filename)) {
202+
std::cerr << "Error: Symbolic links are not allowed.\n";
203+
return false;
204+
}
205+
206+
if (!std::filesystem::is_regular_file(filename)) {
207+
std::cerr << "Error: Input is not a regular file.\n";
208+
return false;
209+
}
210+
211+
std::ifstream infile(filename);
212+
if (!infile.is_open()) {
213+
std::cerr << "Error: Failed to open file.\n";
214+
return false;
215+
}
216+
217+
return ReadHypergraphMetisFormat(infile, hgraph);
218+
}
219+
220+
} // namespace file_reader
221+
222+
} // namespace osp

tests/hypergraph_and_partition.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ limitations under the License.
2424

2525
#include "osp/auxiliary/io/hdag_graph_file_reader.hpp"
2626
#include "osp/auxiliary/io/mtx_hypergraph_file_reader.hpp"
27+
#include "osp/auxiliary/io/hmetis_hypergraph_file_reader.hpp"
2728
#include "osp/auxiliary/io/partitioning_file_writer.hpp"
2829
#include "osp/graph_implementations/adj_list_impl/computational_dag_vector_impl.hpp"
2930
#include "osp/partitioning/model/hypergraph_utility.hpp"
@@ -60,12 +61,20 @@ BOOST_AUTO_TEST_CASE(HypergraphAndPartitionTest) {
6061
BOOST_CHECK_EQUAL(hgraph.NumHyperedges(), 16);
6162

6263
// Matrix format, columns are vertices, rows are hyperedges
63-
status = file_reader::ReadHypergraphMartixMarketFormat((cwd / "data/mtx_tests/ErdosRenyi_8_19_A.mtx").string(), hgraph,
64+
status = file_reader::ReadHypergraphMartixMarketFormat((cwd / "data/mtx_tests/ErdosRenyi_8_19_A.mtx").string(), hgraph,
6465
file_reader::MatrixToHypergraphFormat::ROW_NET);
6566
BOOST_CHECK(status);
6667
BOOST_CHECK_EQUAL(hgraph.NumVertices(), 8);
6768
BOOST_CHECK_EQUAL(hgraph.NumHyperedges(), 8);
6869

70+
// Hmetis format for hypergraphs
71+
status = file_reader::ReadHypergraphMetisFormat((cwd / "data/hmetis/example01.hmetis").string(), hgraph);
72+
BOOST_CHECK(status);
73+
BOOST_CHECK_EQUAL(hgraph.NumVertices(), 8);
74+
BOOST_CHECK_EQUAL(hgraph.NumHyperedges(), 6);
75+
BOOST_CHECK_EQUAL(hgraph.GetVertexWorkWeight(0), 1);
76+
BOOST_CHECK_EQUAL(hgraph.GetHyperedgeWeight(0), 1);
77+
6978
// DAG format, all hyperedges have size 2
7079
hgraph = ConvertFromCdagAsDag<HypergraphImpl, Graph>(dag);
7180
BOOST_CHECK_EQUAL(dag.NumVertices(), hgraph.NumVertices());

0 commit comments

Comments
 (0)