Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework trait system #9

Merged
merged 6 commits into from
Mar 23, 2022
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

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

74 changes: 23 additions & 51 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
mod generator;

use coupe::algorithms::k_means::simplified_k_means;
use coupe::algorithms::recursive_bisection::{axis_sort, rcb};
use coupe::geometry::Point2D;
use coupe::Partition as _;
use coupe::Point2D;
use criterion::{criterion_group, criterion_main};
use criterion::{Criterion, Throughput};
use rayon::prelude::*;

const SAMPLE_SIZE: usize = 5000;
const NUM_ITER: usize = 2;

fn bench_axis_sort_random(c: &mut Criterion) {
c.benchmark_group("axis_sort_random")
.throughput(Throughput::Elements(SAMPLE_SIZE as u64))
.bench_function("axis_sort_random", move |b| {
let sample_points = generator::uniform_rectangle(
Point2D::from([0., 0.]),
Point2D::from([30., 10.]),
SAMPLE_SIZE,
);
let mut permutation: Vec<_> = (0..SAMPLE_SIZE).collect();

b.iter(|| axis_sort(&sample_points, &mut permutation, 0))
});
}

fn bench_raw_pdqsort_random(c: &mut Criterion) {
c.benchmark_group("raw_pdqsort_random")
.throughput(Throughput::Elements(SAMPLE_SIZE as u64))
Expand Down Expand Up @@ -87,20 +71,6 @@ fn bench_parallel_raw_pdqsort_sorted(c: &mut Criterion) {
});
}

fn bench_axis_sort_sorted(c: &mut Criterion) {
c.benchmark_group("axis_sort_sorted")
.throughput(Throughput::Elements(SAMPLE_SIZE as u64))
.bench_function("axis_sort_sorted", move |b| {
let sample_points = generator::already_x_sorted_rectangle(
Point2D::from([0., 0.]),
Point2D::from([30., 10.]),
SAMPLE_SIZE,
);
let mut permutation: Vec<_> = (0..SAMPLE_SIZE).collect();
b.iter(|| axis_sort(&sample_points, &mut permutation, 0))
});
}

fn bench_rcb_random(c: &mut Criterion) {
c.benchmark_group("rcb_random")
.throughput(Throughput::Elements(SAMPLE_SIZE as u64))
Expand All @@ -110,52 +80,54 @@ fn bench_rcb_random(c: &mut Criterion) {
Point2D::from([30., 10.]),
SAMPLE_SIZE,
);
let weights = vec![1.0; sample_points.len()];
let mut ids = vec![0; sample_points.len()];
let weights = vec![1; SAMPLE_SIZE];
let mut ids = vec![0; SAMPLE_SIZE];
b.iter(|| {
use rayon::iter::IntoParallelRefIterator as _;
use rayon::iter::ParallelIterator as _;

let p = sample_points.par_iter().cloned();
let w = weights.par_iter().cloned();
rcb(&mut ids, p, w, NUM_ITER)
coupe::Rcb {
iter_count: NUM_ITER,
}
.partition(&mut ids, (p, w))
.unwrap()
})
});
}

fn bench_simplified_k_means(c: &mut Criterion) {
c.benchmark_group("simplified_k_means")
fn bench_k_means(c: &mut Criterion) {
c.benchmark_group("k_means")
.throughput(Throughput::Elements(SAMPLE_SIZE as u64))
.bench_function("simplified_k_means", move |b| {
.bench_function("k_means", move |b| {
let sample_points = generator::uniform_rectangle(
Point2D::new(0., 0.),
Point2D::new(30., 10.),
SAMPLE_SIZE,
);
let ids: Vec<_> = (0..SAMPLE_SIZE).collect();
let weights: Vec<_> = ids.iter().map(|_| 1.).collect();
let weights = vec![1.0; SAMPLE_SIZE];
b.iter(|| {
simplified_k_means(
&sample_points,
&weights,
2usize.pow(NUM_ITER as u32),
5.,
1000,
true,
)
coupe::KMeans {
part_count: 2_usize.pow(NUM_ITER as u32),
imbalance_tol: 5.0,
delta_threshold: 0.0,
hilbert: true,
..Default::default()
}
.partition(&mut [0; SAMPLE_SIZE], (&sample_points, &weights))
.unwrap()
})
});
}

criterion_group!(
benches,
bench_axis_sort_random,
bench_axis_sort_sorted,
bench_raw_pdqsort_random,
bench_parallel_raw_pdqsort_random,
bench_raw_pdqsort_sorted,
bench_parallel_raw_pdqsort_sorted,
bench_rcb_random,
bench_simplified_k_means
bench_k_means
);
criterion_main!(benches);
20 changes: 1 addition & 19 deletions benches/generator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use coupe::geometry::Point2D;
use itertools::Itertools;
use coupe::Point2D;
use rand::{self, Rng};

pub fn uniform_f64(min: f64, max: f64, num_vals: usize) -> Vec<f64> {
Expand All @@ -18,20 +17,3 @@ pub fn uniform_rectangle(p_min: Point2D, p_max: Point2D, num_points: usize) -> V
})
.collect()
}

pub fn already_x_sorted_rectangle(
p_min: Point2D,
p_max: Point2D,
num_points: usize,
) -> Vec<Point2D> {
let mut rng = rand::thread_rng();
(0..num_points)
.map(|_| {
Point2D::from([
rng.gen_range(p_min.x..p_max.x),
rng.gen_range(p_min.y..p_max.y),
])
})
.sorted_by(|p1, p2| p1.x.partial_cmp(&p2.x).unwrap_or(std::cmp::Ordering::Equal))
.collect()
}
100 changes: 88 additions & 12 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,88 @@
pub mod ckk;
pub mod fiduccia_mattheyses;
pub mod graph_growth;
pub mod greedy;
pub mod hilbert_curve;
pub mod k_means;
pub mod kernighan_lin;
pub mod kk;
pub mod multi_jagged;
pub mod recursive_bisection;
pub mod vn;
pub mod z_curve;
use std::fmt;

mod ckk;
mod fiduccia_mattheyses;
mod graph_growth;
mod greedy;
mod hilbert_curve;
mod k_means;
mod kernighan_lin;
mod kk;
mod multi_jagged;
mod recursive_bisection;
mod vn;
mod z_curve;

pub use ckk::CompleteKarmarkarKarp;
pub use fiduccia_mattheyses::FiducciaMattheyses;
pub use graph_growth::GraphGrowth;
pub use greedy::Greedy;
pub use hilbert_curve::Error as HilbertCurveError;
pub use hilbert_curve::HilbertCurve;
pub use k_means::KMeans;
pub use kernighan_lin::KernighanLin;
pub use kk::KarmarkarKarp;
pub use multi_jagged::MultiJagged;
pub use recursive_bisection::Rcb;
pub use recursive_bisection::Rib;
pub use vn::VnBest;
pub use z_curve::ZCurve;

/// Common errors thrown by algorithms.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// No partition that matches the given criteria could been found.
NotFound,

/// Input sets don't have matching lengths.
InputLenMismatch { expected: usize, actual: usize },
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::NotFound => write!(f, "no partition found"),
Error::InputLenMismatch { expected, actual } => write!(
f,
"input sets don't have the same length (expected {expected} items, got {actual})",
),
}
}
}

impl std::error::Error for Error {}

/// Map elements to parts randomly.
///
/// # Example
///
/// ```rust
/// use coupe::Partition as _;
/// use rand;
///
/// let mut partition = [0; 12];
///
/// coupe::Random { rng: rand::thread_rng(), part_count: 3 }
/// .partition(&mut partition, ())
/// .unwrap();
/// ```
pub struct Random<R> {
pub rng: R,
pub part_count: usize,
}

impl<R> crate::Partition<()> for Random<R>
where
R: rand::Rng,
{
type Metadata = ();
type Error = std::convert::Infallible;

fn partition(&mut self, part_ids: &mut [usize], _: ()) -> Result<Self::Metadata, Self::Error> {
for part_id in part_ids {
*part_id = self.rng.gen_range(0..self.part_count);
}
Ok(())
}
}
55 changes: 51 additions & 4 deletions src/algorithms/ckk.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::Error;
use num::FromPrimitive;
use num::ToPrimitive;
use std::iter::Sum;
Expand Down Expand Up @@ -92,17 +93,22 @@ where
false
}

pub fn ckk_bipart<I, T>(partition: &mut [usize], weights: I, tolerance: f64) -> bool
fn ckk_bipart<I, T>(partition: &mut [usize], weights: I, tolerance: f64) -> Result<(), Error>
where
I: IntoIterator<Item = T>,
T: Sum + Add<Output = T> + Sub<Output = T>,
T: FromPrimitive + ToPrimitive,
T: Ord + Default + Copy,
{
let mut weights: Vec<(T, usize)> = weights.into_iter().zip(0..).collect();
debug_assert_eq!(weights.len(), partition.len());
if weights.len() != partition.len() {
return Err(Error::InputLenMismatch {
expected: partition.len(),
actual: weights.len(),
});
}
if weights.is_empty() {
return true;
return Ok(());
}
weights.sort_unstable();

Expand All @@ -111,7 +117,48 @@ where

let mut steps = Vec::new();

ckk_bipart_rec(partition, &mut weights, tolerance, &mut steps)
if ckk_bipart_rec(partition, &mut weights, tolerance, &mut steps) {
Ok(())
} else {
Err(Error::NotFound)
}
}

/// The exact variant of the [Karmarkar-Karp][crate::KarmarkarKarp] algorithm.
///
/// # Example
///
/// ```rust
/// use coupe::Partition as _;
///
/// let weights: [i32; 4] = [3, 5, 3, 9];
/// let mut partition = [0; 4];
///
/// coupe::CompleteKarmarkarKarp { tolerance: 0.1 }
/// .partition(&mut partition, weights)
/// .unwrap();
/// ```
pub struct CompleteKarmarkarKarp {
pub tolerance: f64,
}

impl<W> crate::Partition<W> for CompleteKarmarkarKarp
where
W: IntoIterator,
W::Item: Sum + Add<Output = W::Item> + Sub<Output = W::Item>,
W::Item: FromPrimitive + ToPrimitive,
W::Item: Ord + Default + Copy,
{
type Metadata = ();
type Error = Error;

fn partition(
&mut self,
part_ids: &mut [usize],
weights: W,
) -> Result<Self::Metadata, Self::Error> {
ckk_bipart(part_ids, weights, self.tolerance)
}
}

// TODO tests
Loading