Skip to content

Commit ecec938

Browse files
committed
Add libcoupemetis.so
coupe-metis is an implementation of MeTiS using coupe's algorithms.
1 parent 0c996e0 commit ecec938

File tree

5 files changed

+191
-0
lines changed

5 files changed

+191
-0
lines changed

Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[workspace]
22
members = [
33
".",
4+
"coupe-metis",
45
"ffi",
56
"tools",
67
"tools/mesh-io",

coupe-metis/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "coupe-metis"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
7+
[lib]
8+
name = "coupemetis"
9+
crate-type = ["cdylib", "staticlib"]
10+
11+
12+
[dependencies]
13+
coupe = { version = "0.1", path = ".." }
14+
libc = "0.2"
15+
sprs = { version = "0.11", default-features = false }

coupe-metis/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# libcoupemetis.so
2+
3+
This library is an implementation of [MeTiS] 5.1 using coupe's algorithms.
4+
5+
[MeTiS]: http://glaros.dtc.umn.edu/gkhome/metis/metis/overview
6+
7+
## Building
8+
9+
Build it with cargo. It will produce a shared object and an archive for static
10+
linking.
11+
12+
## Usage
13+
14+
This library is a drop-in replacement for `libmetis.so`. Simply replace
15+
`-lmetis` by `-lcoupemetis` in your linker flags:
16+
17+
```shell
18+
# Change this
19+
cc -o my_program -c main.c -lmetis
20+
21+
# To this
22+
cc -o my_program -c main.c -lcoupemetis
23+
```

coupe-metis/src/lib.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#![allow(clippy::missing_safety_doc)] // See the MeTiS manual
2+
3+
use coupe::Partition as _;
4+
use std::slice;
5+
6+
pub type Idx = i32;
7+
pub type Real = f32;
8+
9+
//const NOPTIONS: usize = 40; // TODO
10+
11+
#[repr(C)]
12+
pub enum RStatus {
13+
Ok = 1,
14+
ErrorInput = -2,
15+
ErrorMemory = -3,
16+
Error = -4,
17+
}
18+
19+
#[allow(clippy::too_many_arguments)]
20+
unsafe fn partition(
21+
nvtxs: *const Idx,
22+
ncon: *const Idx,
23+
xadj: *const Idx,
24+
adjncy: *const Idx,
25+
vwgt: *const Idx,
26+
_vsize: *const Idx, // TODO
27+
adjwgt: *const Idx,
28+
nparts: *const Idx,
29+
_tpwgts: *const Idx, // TODO
30+
_ubvec: *const Real, // TODO
31+
_options: *const Idx, // TODO
32+
objval: *mut Idx,
33+
part: *mut Idx,
34+
) -> RStatus {
35+
let nvtxs = *nvtxs as usize;
36+
let ncon = *ncon as usize;
37+
if ncon != 1 {
38+
// TODO make graph algorithms generic over input collection.
39+
return RStatus::Error;
40+
}
41+
let nparts = *nparts as usize;
42+
43+
// TODO make graph algorithms work over CsMatViewI instead of CsMatView
44+
// to avoid these conversions.
45+
let xadj: Vec<usize> = slice::from_raw_parts(xadj, nvtxs + 1)
46+
.iter()
47+
.map(|i| *i as usize)
48+
.collect();
49+
let adjncy: Vec<usize> = slice::from_raw_parts(adjncy, xadj[xadj.len() - 1] as usize)
50+
.iter()
51+
.map(|i| *i as usize)
52+
.collect();
53+
let adjwgt = if adjwgt.is_null() {
54+
vec![1_i64; adjncy.len()]
55+
} else {
56+
// TODO make graph algorithms generic over weight type
57+
slice::from_raw_parts(adjwgt, adjncy.len())
58+
.iter()
59+
.map(|wgt| *wgt as i64)
60+
.collect()
61+
};
62+
let adjacency = sprs::CsMat::new((nvtxs, nvtxs), xadj, adjncy, adjwgt);
63+
64+
let mut partition = vec![0; nvtxs];
65+
66+
let vwgt = if vwgt.is_null() {
67+
vec![0.0; nvtxs]
68+
} else {
69+
// TODO make graph algorithms generic over weight type
70+
slice::from_raw_parts(vwgt, nvtxs)
71+
.iter()
72+
.map(|wgt| *wgt as f64)
73+
.collect()
74+
};
75+
76+
let ret = coupe::KarmarkarKarp { part_count: nparts }
77+
.partition(&mut partition, vwgt.iter().map(|w| coupe::Real::from(*w)));
78+
if let Err(err) = ret {
79+
println!("error: {}", err);
80+
return RStatus::Error;
81+
}
82+
83+
let ret =
84+
coupe::FiducciaMattheyses::default().partition(&mut partition, (adjacency.view(), &vwgt));
85+
if let Err(err) = ret {
86+
println!("error: {}", err);
87+
return RStatus::Error;
88+
}
89+
90+
let part = slice::from_raw_parts_mut(part, nvtxs);
91+
for (dst, src) in part.iter_mut().zip(&mut partition) {
92+
*dst = *src as Idx;
93+
}
94+
95+
// TODO have fidducia return the cut size.
96+
*objval = 0;
97+
98+
RStatus::Ok
99+
}
100+
101+
#[no_mangle]
102+
pub unsafe extern "C" fn METIS_PartGraphKway(
103+
nvtxs: *const Idx,
104+
ncon: *const Idx,
105+
xadj: *const Idx,
106+
adjncy: *const Idx,
107+
vwgt: *const Idx,
108+
vsize: *const Idx,
109+
adjwgt: *const Idx,
110+
nparts: *const Idx,
111+
tpwgts: *const Idx,
112+
ubvec: *const Real,
113+
options: *const Idx,
114+
objval: *mut Idx,
115+
part: *mut Idx,
116+
) -> RStatus {
117+
partition(
118+
nvtxs, ncon, xadj, adjncy, vwgt, vsize, adjwgt, nparts, tpwgts, ubvec, options, objval,
119+
part,
120+
)
121+
}
122+
123+
#[no_mangle]
124+
pub unsafe extern "C" fn METIS_PartGraphRecursive(
125+
nvtxs: *const Idx,
126+
ncon: *const Idx,
127+
xadj: *const Idx,
128+
adjncy: *const Idx,
129+
vwgt: *const Idx,
130+
vsize: *const Idx,
131+
adjwgt: *const Idx,
132+
nparts: *const Idx,
133+
tpwgts: *const Idx,
134+
ubvec: *const Real,
135+
options: *const Idx,
136+
objval: *mut Idx,
137+
part: *mut Idx,
138+
) -> RStatus {
139+
partition(
140+
nvtxs, ncon, xadj, adjncy, vwgt, vsize, adjwgt, nparts, tpwgts, ubvec, options, objval,
141+
part,
142+
)
143+
}

0 commit comments

Comments
 (0)