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
13 changes: 9 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
".",
"coupe-metis",
"ffi",
"tools",
"tools/mesh-io",
Expand Down
15 changes: 15 additions & 0 deletions coupe-metis/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "coupe-metis"
version = "0.1.0"
edition = "2021"


[lib]
name = "coupemetis"
crate-type = ["cdylib", "staticlib"]


[dependencies]
coupe = { version = "0.1", path = ".." }
libc = "0.2"
sprs = { version = "0.11", default-features = false }
23 changes: 23 additions & 0 deletions coupe-metis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# libcoupemetis.so

This library is an implementation of [MeTiS] 5.1 using coupe's algorithms.

[MeTiS]: http://glaros.dtc.umn.edu/gkhome/metis/metis/overview

## Building

Build it with cargo. It will produce a shared object and an archive for static
linking.

## Usage

This library is a drop-in replacement for `libmetis.so`. Simply replace
`-lmetis` by `-lcoupemetis` in your linker flags:

```shell
# Change this
cc -o my_program -c main.c -lmetis

# To this
cc -o my_program -c main.c -lcoupemetis
```
143 changes: 143 additions & 0 deletions coupe-metis/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#![allow(clippy::missing_safety_doc)] // See the MeTiS manual

use coupe::Partition as _;
use std::slice;

pub type Idx = i32;
pub type Real = f32;

//const NOPTIONS: usize = 40; // TODO

#[repr(C)]
pub enum RStatus {
Ok = 1,
ErrorInput = -2,
ErrorMemory = -3,
Error = -4,
}

#[allow(clippy::too_many_arguments)]
unsafe fn partition(
nvtxs: *const Idx,
ncon: *const Idx,
xadj: *const Idx,
adjncy: *const Idx,
vwgt: *const Idx,
_vsize: *const Idx, // TODO
adjwgt: *const Idx,
nparts: *const Idx,
_tpwgts: *const Idx, // TODO
_ubvec: *const Real, // TODO
_options: *const Idx, // TODO
objval: *mut Idx,
part: *mut Idx,
) -> RStatus {
let nvtxs = *nvtxs as usize;
let ncon = *ncon as usize;
if ncon != 1 {
// TODO make graph algorithms generic over input collection.
return RStatus::Error;
}
let nparts = *nparts as usize;

// TODO make graph algorithms work over CsMatViewI instead of CsMatView
// to avoid these conversions.
let xadj: Vec<usize> = slice::from_raw_parts(xadj, nvtxs + 1)
.iter()
.map(|i| *i as usize)
.collect();
let adjncy: Vec<usize> = slice::from_raw_parts(adjncy, xadj[xadj.len() - 1] as usize)
.iter()
.map(|i| *i as usize)
.collect();
let adjwgt = if adjwgt.is_null() {
vec![1_i64; adjncy.len()]
} else {
// TODO make graph algorithms generic over weight type
slice::from_raw_parts(adjwgt, adjncy.len())
.iter()
.map(|wgt| *wgt as i64)
.collect()
};
let adjacency = sprs::CsMat::new((nvtxs, nvtxs), xadj, adjncy, adjwgt);

let mut partition = vec![0; nvtxs];

let vwgt = if vwgt.is_null() {
vec![0.0; nvtxs]
} else {
// TODO make graph algorithms generic over weight type
slice::from_raw_parts(vwgt, nvtxs)
.iter()
.map(|wgt| *wgt as f64)
.collect()
};

let ret = coupe::KarmarkarKarp { part_count: nparts }
.partition(&mut partition, vwgt.iter().map(|w| coupe::Real::from(*w)));
if let Err(err) = ret {
println!("error: {}", err);
return RStatus::Error;
}

let ret =
coupe::FiducciaMattheyses::default().partition(&mut partition, (adjacency.view(), &vwgt));
if let Err(err) = ret {
println!("error: {}", err);
return RStatus::Error;
}

let part = slice::from_raw_parts_mut(part, nvtxs);
for (dst, src) in part.iter_mut().zip(&mut partition) {
*dst = *src as Idx;
}

// TODO have fidducia return the cut size.
*objval = 0;

RStatus::Ok
}

#[no_mangle]
pub unsafe extern "C" fn METIS_PartGraphKway(
nvtxs: *const Idx,
ncon: *const Idx,
xadj: *const Idx,
adjncy: *const Idx,
vwgt: *const Idx,
vsize: *const Idx,
adjwgt: *const Idx,
nparts: *const Idx,
tpwgts: *const Idx,
ubvec: *const Real,
options: *const Idx,
objval: *mut Idx,
part: *mut Idx,
) -> RStatus {
partition(
nvtxs, ncon, xadj, adjncy, vwgt, vsize, adjwgt, nparts, tpwgts, ubvec, options, objval,
part,
)
}

#[no_mangle]
pub unsafe extern "C" fn METIS_PartGraphRecursive(
nvtxs: *const Idx,
ncon: *const Idx,
xadj: *const Idx,
adjncy: *const Idx,
vwgt: *const Idx,
vsize: *const Idx,
adjwgt: *const Idx,
nparts: *const Idx,
tpwgts: *const Idx,
ubvec: *const Real,
options: *const Idx,
objval: *mut Idx,
part: *mut Idx,
) -> RStatus {
partition(
nvtxs, ncon, xadj, adjncy, vwgt, vsize, adjwgt, nparts, tpwgts, ubvec, options, objval,
part,
)
}
2 changes: 1 addition & 1 deletion tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ default = ["scotch", "metis"]
# Partitioners
coupe = { version = "0.1", path = ".." }
scotch = { version = "0.1", optional = true }
metis = { version = "0.1", optional = true }
metis = { version = "0.1", optional = true, path = "../../metis-rs" }

# Random number generation
rand = { version = "0.8", default-features = false, features = ["std"] }
Expand Down