Skip to content

Commit

Permalink
WIP: rework Partitioner trait family
Browse files Browse the repository at this point in the history
This simplifies the family of traits from before into one "Partition"
trait.
  • Loading branch information
hhirtz committed Mar 17, 2022
1 parent d3067fd commit 5521e9c
Show file tree
Hide file tree
Showing 16 changed files with 1,091 additions and 1,398 deletions.
74 changes: 62 additions & 12 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,62 @@
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;
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::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;

/// Map mesh elements to parts randomly.
///
/// # Example
///
/// ```rust
/// use coupe::Partitioner;
/// use coupe::Point2D;
/// use coupe::Random;
/// use rand;
///
/// let num_parts = 3;
/// let points: &[Point2D] = &[Point2D::new(1., 0.), Point2D::new(0., 1.)];
///
/// let r = Random::new(rand::thread_rng(), num_parts);
/// r.partition(points, &[1., 1.]);
/// ```
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(())
}
}
41 changes: 40 additions & 1 deletion src/algorithms/ckk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ 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) -> bool
where
I: IntoIterator<Item = T>,
T: Sum + Add<Output = T> + Sub<Output = T>,
Expand All @@ -114,4 +114,43 @@ where
ckk_bipart_rec(partition, &mut weights, tolerance, &mut steps)
}

/// The exact variant of the [Karmarkar-Karp][crate::KarmarkarKarp] algorithm.
///
/// # Example
///
/// ```rust
/// use coupe::Partitioner;
/// use coupe::Point2D;
/// use coupe::CompleteKarmarkarKarp;
///
/// let tolerance = 0.1;
/// let points: &[Point2D] = &[Point2D::new(1., 0.), Point2D::new(0., 1.)];
///
/// let ckk = CompleteKarmarkarKarp::new(tolerance);
/// ckk.partition(points, &[1., 1.]);
/// ```
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 = std::convert::Infallible;

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

// TODO tests
124 changes: 116 additions & 8 deletions src/algorithms/fiduccia_mattheyses.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use itertools::Itertools;
use sprs::CsMatView;

use crate::partition::Partition;
use crate::PointND;
use std::collections::HashMap;

pub fn fiduccia_mattheyses<'a, const D: usize>(
initial_partition: &mut Partition<'a, PointND<D>, f64>,
fn fiduccia_mattheyses<'a>(
part_ids: &mut [usize],
weights: &[f64],
adjacency: CsMatView<f64>,
max_passes: impl Into<Option<usize>>,
max_flips_per_pass: impl Into<Option<usize>>,
Expand All @@ -16,12 +15,11 @@ pub fn fiduccia_mattheyses<'a, const D: usize>(
let max_passes = max_passes.into();
let max_flips_per_pass = max_flips_per_pass.into();
let max_imbalance_per_flip = max_imbalance_per_flip.into();
let (_points, weights, ids) = initial_partition.as_raw_mut();

fiduccia_mattheyses_impl(
part_ids,
weights,
adjacency.view(),
ids,
adjacency,
max_passes,
max_flips_per_pass,
max_imbalance_per_flip,
Expand All @@ -30,9 +28,9 @@ pub fn fiduccia_mattheyses<'a, const D: usize>(
}

fn fiduccia_mattheyses_impl(
initial_partition: &mut [usize],
weights: &[f64],
adjacency: CsMatView<f64>,
initial_partition: &mut [usize],
max_passes: Option<usize>,
max_flips_per_pass: Option<usize>,
max_imbalance_per_flip: Option<f64>,
Expand Down Expand Up @@ -250,3 +248,113 @@ fn fiduccia_mattheyses_impl(

println!("final cut size: {}", new_cut_size);
}

/// FiducciaMattheyses
///
/// An implementation of the Fiduccia Mattheyses topologic algorithm
/// for graph partitioning. This implementation is an extension of the
/// original algorithm to handle partitioning into more than two parts.
///
/// This algorithm repeats an iterative pass during which a set of graph nodes are assigned to
/// a new part, reducing the overall cutsize of the partition. As opposed to the
/// Kernighan-Lin algorithm, during each pass iteration, only one node is flipped at a time.
/// The algorithm thus does not preserve partition weights balance and may produce an unbalanced
/// partition.
///
/// Original algorithm from "A Linear-Time Heuristic for Improving Network Partitions"
/// by C.M. Fiduccia and R.M. Mattheyses.
///
/// # Example
///
/// ```rust
/// use coupe::Point2D;
/// use coupe::TopologicPartitionImprover;
/// use coupe::partition::Partition;
/// use sprs::CsMat;
///
/// // swap
/// // 0 1 0 1
/// // +--+--+--+
/// // | | | |
/// // +--+--+--+
/// // 0 0 1 1
/// let points = vec![
/// Point2D::new(0., 0.),
/// Point2D::new(1., 0.),
/// Point2D::new(2., 0.),
/// Point2D::new(3., 0.),
/// Point2D::new(0., 1.),
/// Point2D::new(1., 1.),
/// Point2D::new(2., 1.),
/// Point2D::new(3., 1.),
/// ];
///
/// let ids = vec![0, 0, 1, 1, 0, 1, 0, 1];
/// let weights = vec![1.; 8];
///
/// let mut partition = Partition::from_ids(&points, &weights, ids);
///
/// let mut adjacency = CsMat::empty(sprs::CSR, 8);
/// adjacency.reserve_outer_dim(8);
/// eprintln!("shape: {:?}", adjacency.shape());
/// adjacency.insert(0, 1, 1.);
/// adjacency.insert(1, 2, 1.);
/// adjacency.insert(2, 3, 1.);
/// adjacency.insert(4, 5, 1.);
/// adjacency.insert(5, 6, 1.);
/// adjacency.insert(6, 7, 1.);
/// adjacency.insert(0, 4, 1.);
/// adjacency.insert(1, 5, 1.);
/// adjacency.insert(2, 6, 1.);
/// adjacency.insert(3, 7, 1.);
///
/// // symmetry
/// adjacency.insert(1, 0, 1.);
/// adjacency.insert(2, 1, 1.);
/// adjacency.insert(3, 2, 1.);
/// adjacency.insert(5, 4, 1.);
/// adjacency.insert(6, 5, 1.);
/// adjacency.insert(7, 6, 1.);
/// adjacency.insert(4, 0, 1.);
/// adjacency.insert(5, 1, 1.);
/// adjacency.insert(6, 2, 1.);
/// adjacency.insert(7, 3, 1.);
///
/// // 1 iteration
/// let algo = coupe::FiducciaMattheyses::new(None, None, None, 1);
///
/// let partition = algo.improve_partition(partition, adjacency.view());
///
/// let new_ids = partition.into_ids();
/// assert_eq!(new_ids[5], 0);
/// assert_eq!(new_ids[6], 1);
/// ```
#[derive(Debug, Clone, Copy)]
pub struct FiducciaMattheyses {
pub max_passes: Option<usize>,
pub max_flips_per_pass: Option<usize>,
pub max_imbalance_per_flip: Option<f64>,
pub max_bad_move_in_a_row: usize,
}

impl<'a> crate::Partition<(CsMatView<'a, f64>, &'a [f64])> for FiducciaMattheyses {
type Metadata = ();
type Error = std::convert::Infallible;

fn partition(
&mut self,
part_ids: &mut [usize],
(adjacency, weights): (CsMatView<f64>, &'a [f64]),
) -> Result<Self::Metadata, Self::Error> {
fiduccia_mattheyses(
part_ids,
weights,
adjacency,
self.max_passes,
self.max_flips_per_pass,
self.max_imbalance_per_flip,
self.max_bad_move_in_a_row,
);
Ok(())
}
}
96 changes: 93 additions & 3 deletions src/algorithms/graph_growth.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use rand::seq::SliceRandom;
use sprs::CsMatView;

pub fn graph_growth(weights: &[f64], adjacency: CsMatView<f64>, num_parts: usize) -> Vec<usize> {
fn graph_growth(
initial_ids: &mut [usize],
weights: &[f64],
adjacency: CsMatView<f64>,
num_parts: usize,
) {
let (shape_x, shape_y) = adjacency.shape();
assert_eq!(shape_x, shape_y);
assert_eq!(weights.len(), shape_x);

let mut initial_ids = vec![0; weights.len()];
// let total_weight = weights.iter().sum::<f64>();
// let weight_per_part = total_weight / num_parts as f64;
let max_expansion_per_pass = 20;
Expand Down Expand Up @@ -54,6 +58,92 @@ pub fn graph_growth(weights: &[f64], adjacency: CsMatView<f64>, num_parts: usize
}
}
}
}

/// Graph Growth algorithm
///
/// A topologic algorithm that generates a partition from a topologic mesh.
/// Given a number k of parts, the algorithm selects k nodes randomly and assigns them to a different part.
/// Then, at each iteration, each part is expanded to neighbor nodes that are not yet assigned to a part
///
/// # Example
///
/// ```rust
/// use coupe::Point2D;
/// use coupe::TopologicPartitioner;
/// use coupe::partition::Partition;
/// use sprs::CsMat;
///
/// // +--+--+--+
/// // | | | |
/// // +--+--+--+
///
/// let points = vec![
/// Point2D::new(0., 0.),
/// Point2D::new(1., 0.),
/// Point2D::new(2., 0.),
/// Point2D::new(3., 0.),
/// Point2D::new(0., 1.),
/// Point2D::new(1., 1.),
/// Point2D::new(2., 1.),
/// Point2D::new(3., 1.),
/// ];
///
/// let weights = vec![1.; 8];
///
/// let mut adjacency = CsMat::empty(sprs::CSR, 8);
/// adjacency.reserve_outer_dim(8);
/// eprintln!("shape: {:?}", adjacency.shape());
/// adjacency.insert(0, 1, 1.);
/// adjacency.insert(1, 2, 1.);
/// adjacency.insert(2, 3, 1.);
/// adjacency.insert(4, 5, 1.);
/// adjacency.insert(5, 6, 1.);
/// adjacency.insert(6, 7, 1.);
/// adjacency.insert(0, 4, 1.);
/// adjacency.insert(1, 5, 1.);
/// adjacency.insert(2, 6, 1.);
/// adjacency.insert(3, 7, 1.);
///
/// // symmetry
/// adjacency.insert(1, 0, 1.);
/// adjacency.insert(2, 1, 1.);
/// adjacency.insert(3, 2, 1.);
/// adjacency.insert(5, 4, 1.);
/// adjacency.insert(6, 5, 1.);
/// adjacency.insert(7, 6, 1.);
/// adjacency.insert(4, 0, 1.);
/// adjacency.insert(5, 1, 1.);
/// adjacency.insert(6, 2, 1.);
/// adjacency.insert(7, 3, 1.);
///
/// let gg = coupe::GraphGrowth::new(2);
///
/// let _partition = gg.partition(points.as_slice(), &weights, adjacency.view());
/// ```
#[derive(Debug, Clone, Copy)]
pub struct GraphGrowth {
pub part_count: usize,
}

initial_ids
impl<'a, W> crate::Partition<(CsMatView<'a, f64>, W)> for GraphGrowth
where
W: AsRef<[f64]>,
{
type Metadata = ();
type Error = std::convert::Infallible;

fn partition(
&mut self,
part_ids: &mut [usize],
(adjacency, weights): (CsMatView<f64>, W),
) -> Result<Self::Metadata, Self::Error> {
graph_growth(
part_ids,
weights.as_ref(),
adjacency.view(),
self.part_count,
);
Ok(())
}
}
Loading

0 comments on commit 5521e9c

Please sign in to comment.