Skip to content

Commit 5521e9c

Browse files
committed
WIP: rework Partitioner trait family
This simplifies the family of traits from before into one "Partition" trait.
1 parent d3067fd commit 5521e9c

File tree

16 files changed

+1091
-1398
lines changed

16 files changed

+1091
-1398
lines changed

src/algorithms.rs

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,62 @@
1-
pub mod ckk;
2-
pub mod fiduccia_mattheyses;
3-
pub mod graph_growth;
4-
pub mod greedy;
5-
pub mod hilbert_curve;
6-
pub mod k_means;
7-
pub mod kernighan_lin;
8-
pub mod kk;
9-
pub mod multi_jagged;
10-
pub mod recursive_bisection;
11-
pub mod vn;
12-
pub mod z_curve;
1+
mod ckk;
2+
mod fiduccia_mattheyses;
3+
mod graph_growth;
4+
mod greedy;
5+
mod hilbert_curve;
6+
mod k_means;
7+
mod kernighan_lin;
8+
mod kk;
9+
mod multi_jagged;
10+
mod recursive_bisection;
11+
mod vn;
12+
mod z_curve;
13+
14+
pub use ckk::CompleteKarmarkarKarp;
15+
pub use fiduccia_mattheyses::FiducciaMattheyses;
16+
pub use graph_growth::GraphGrowth;
17+
pub use greedy::Greedy;
18+
pub use hilbert_curve::HilbertCurve;
19+
pub use k_means::KMeans;
20+
pub use kernighan_lin::KernighanLin;
21+
pub use kk::KarmarkarKarp;
22+
pub use multi_jagged::MultiJagged;
23+
pub use recursive_bisection::Rcb;
24+
pub use recursive_bisection::Rib;
25+
pub use vn::VnBest;
26+
pub use z_curve::ZCurve;
27+
28+
/// Map mesh elements to parts randomly.
29+
///
30+
/// # Example
31+
///
32+
/// ```rust
33+
/// use coupe::Partitioner;
34+
/// use coupe::Point2D;
35+
/// use coupe::Random;
36+
/// use rand;
37+
///
38+
/// let num_parts = 3;
39+
/// let points: &[Point2D] = &[Point2D::new(1., 0.), Point2D::new(0., 1.)];
40+
///
41+
/// let r = Random::new(rand::thread_rng(), num_parts);
42+
/// r.partition(points, &[1., 1.]);
43+
/// ```
44+
pub struct Random<R> {
45+
pub rng: R,
46+
pub part_count: usize,
47+
}
48+
49+
impl<R> crate::Partition<()> for Random<R>
50+
where
51+
R: rand::Rng,
52+
{
53+
type Metadata = ();
54+
type Error = std::convert::Infallible;
55+
56+
fn partition(&mut self, part_ids: &mut [usize], _: ()) -> Result<Self::Metadata, Self::Error> {
57+
for part_id in part_ids {
58+
*part_id = self.rng.gen_range(0..self.part_count);
59+
}
60+
Ok(())
61+
}
62+
}

src/algorithms/ckk.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ where
9292
false
9393
}
9494

95-
pub fn ckk_bipart<I, T>(partition: &mut [usize], weights: I, tolerance: f64) -> bool
95+
fn ckk_bipart<I, T>(partition: &mut [usize], weights: I, tolerance: f64) -> bool
9696
where
9797
I: IntoIterator<Item = T>,
9898
T: Sum + Add<Output = T> + Sub<Output = T>,
@@ -114,4 +114,43 @@ where
114114
ckk_bipart_rec(partition, &mut weights, tolerance, &mut steps)
115115
}
116116

117+
/// The exact variant of the [Karmarkar-Karp][crate::KarmarkarKarp] algorithm.
118+
///
119+
/// # Example
120+
///
121+
/// ```rust
122+
/// use coupe::Partitioner;
123+
/// use coupe::Point2D;
124+
/// use coupe::CompleteKarmarkarKarp;
125+
///
126+
/// let tolerance = 0.1;
127+
/// let points: &[Point2D] = &[Point2D::new(1., 0.), Point2D::new(0., 1.)];
128+
///
129+
/// let ckk = CompleteKarmarkarKarp::new(tolerance);
130+
/// ckk.partition(points, &[1., 1.]);
131+
/// ```
132+
pub struct CompleteKarmarkarKarp {
133+
pub tolerance: f64,
134+
}
135+
136+
impl<W> crate::Partition<W> for CompleteKarmarkarKarp
137+
where
138+
W: IntoIterator,
139+
W::Item: Sum + Add<Output = W::Item> + Sub<Output = W::Item>,
140+
W::Item: FromPrimitive + ToPrimitive,
141+
W::Item: Ord + Default + Copy,
142+
{
143+
type Metadata = ();
144+
type Error = std::convert::Infallible;
145+
146+
fn partition(
147+
&mut self,
148+
part_ids: &mut [usize],
149+
weights: W,
150+
) -> Result<Self::Metadata, Self::Error> {
151+
ckk_bipart(part_ids, weights, self.tolerance);
152+
Ok(())
153+
}
154+
}
155+
117156
// TODO tests

src/algorithms/fiduccia_mattheyses.rs

Lines changed: 116 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use itertools::Itertools;
22
use sprs::CsMatView;
33

4-
use crate::partition::Partition;
5-
use crate::PointND;
64
use std::collections::HashMap;
75

8-
pub fn fiduccia_mattheyses<'a, const D: usize>(
9-
initial_partition: &mut Partition<'a, PointND<D>, f64>,
6+
fn fiduccia_mattheyses<'a>(
7+
part_ids: &mut [usize],
8+
weights: &[f64],
109
adjacency: CsMatView<f64>,
1110
max_passes: impl Into<Option<usize>>,
1211
max_flips_per_pass: impl Into<Option<usize>>,
@@ -16,12 +15,11 @@ pub fn fiduccia_mattheyses<'a, const D: usize>(
1615
let max_passes = max_passes.into();
1716
let max_flips_per_pass = max_flips_per_pass.into();
1817
let max_imbalance_per_flip = max_imbalance_per_flip.into();
19-
let (_points, weights, ids) = initial_partition.as_raw_mut();
2018

2119
fiduccia_mattheyses_impl(
20+
part_ids,
2221
weights,
23-
adjacency.view(),
24-
ids,
22+
adjacency,
2523
max_passes,
2624
max_flips_per_pass,
2725
max_imbalance_per_flip,
@@ -30,9 +28,9 @@ pub fn fiduccia_mattheyses<'a, const D: usize>(
3028
}
3129

3230
fn fiduccia_mattheyses_impl(
31+
initial_partition: &mut [usize],
3332
weights: &[f64],
3433
adjacency: CsMatView<f64>,
35-
initial_partition: &mut [usize],
3634
max_passes: Option<usize>,
3735
max_flips_per_pass: Option<usize>,
3836
max_imbalance_per_flip: Option<f64>,
@@ -250,3 +248,113 @@ fn fiduccia_mattheyses_impl(
250248

251249
println!("final cut size: {}", new_cut_size);
252250
}
251+
252+
/// FiducciaMattheyses
253+
///
254+
/// An implementation of the Fiduccia Mattheyses topologic algorithm
255+
/// for graph partitioning. This implementation is an extension of the
256+
/// original algorithm to handle partitioning into more than two parts.
257+
///
258+
/// This algorithm repeats an iterative pass during which a set of graph nodes are assigned to
259+
/// a new part, reducing the overall cutsize of the partition. As opposed to the
260+
/// Kernighan-Lin algorithm, during each pass iteration, only one node is flipped at a time.
261+
/// The algorithm thus does not preserve partition weights balance and may produce an unbalanced
262+
/// partition.
263+
///
264+
/// Original algorithm from "A Linear-Time Heuristic for Improving Network Partitions"
265+
/// by C.M. Fiduccia and R.M. Mattheyses.
266+
///
267+
/// # Example
268+
///
269+
/// ```rust
270+
/// use coupe::Point2D;
271+
/// use coupe::TopologicPartitionImprover;
272+
/// use coupe::partition::Partition;
273+
/// use sprs::CsMat;
274+
///
275+
/// // swap
276+
/// // 0 1 0 1
277+
/// // +--+--+--+
278+
/// // | | | |
279+
/// // +--+--+--+
280+
/// // 0 0 1 1
281+
/// let points = vec![
282+
/// Point2D::new(0., 0.),
283+
/// Point2D::new(1., 0.),
284+
/// Point2D::new(2., 0.),
285+
/// Point2D::new(3., 0.),
286+
/// Point2D::new(0., 1.),
287+
/// Point2D::new(1., 1.),
288+
/// Point2D::new(2., 1.),
289+
/// Point2D::new(3., 1.),
290+
/// ];
291+
///
292+
/// let ids = vec![0, 0, 1, 1, 0, 1, 0, 1];
293+
/// let weights = vec![1.; 8];
294+
///
295+
/// let mut partition = Partition::from_ids(&points, &weights, ids);
296+
///
297+
/// let mut adjacency = CsMat::empty(sprs::CSR, 8);
298+
/// adjacency.reserve_outer_dim(8);
299+
/// eprintln!("shape: {:?}", adjacency.shape());
300+
/// adjacency.insert(0, 1, 1.);
301+
/// adjacency.insert(1, 2, 1.);
302+
/// adjacency.insert(2, 3, 1.);
303+
/// adjacency.insert(4, 5, 1.);
304+
/// adjacency.insert(5, 6, 1.);
305+
/// adjacency.insert(6, 7, 1.);
306+
/// adjacency.insert(0, 4, 1.);
307+
/// adjacency.insert(1, 5, 1.);
308+
/// adjacency.insert(2, 6, 1.);
309+
/// adjacency.insert(3, 7, 1.);
310+
///
311+
/// // symmetry
312+
/// adjacency.insert(1, 0, 1.);
313+
/// adjacency.insert(2, 1, 1.);
314+
/// adjacency.insert(3, 2, 1.);
315+
/// adjacency.insert(5, 4, 1.);
316+
/// adjacency.insert(6, 5, 1.);
317+
/// adjacency.insert(7, 6, 1.);
318+
/// adjacency.insert(4, 0, 1.);
319+
/// adjacency.insert(5, 1, 1.);
320+
/// adjacency.insert(6, 2, 1.);
321+
/// adjacency.insert(7, 3, 1.);
322+
///
323+
/// // 1 iteration
324+
/// let algo = coupe::FiducciaMattheyses::new(None, None, None, 1);
325+
///
326+
/// let partition = algo.improve_partition(partition, adjacency.view());
327+
///
328+
/// let new_ids = partition.into_ids();
329+
/// assert_eq!(new_ids[5], 0);
330+
/// assert_eq!(new_ids[6], 1);
331+
/// ```
332+
#[derive(Debug, Clone, Copy)]
333+
pub struct FiducciaMattheyses {
334+
pub max_passes: Option<usize>,
335+
pub max_flips_per_pass: Option<usize>,
336+
pub max_imbalance_per_flip: Option<f64>,
337+
pub max_bad_move_in_a_row: usize,
338+
}
339+
340+
impl<'a> crate::Partition<(CsMatView<'a, f64>, &'a [f64])> for FiducciaMattheyses {
341+
type Metadata = ();
342+
type Error = std::convert::Infallible;
343+
344+
fn partition(
345+
&mut self,
346+
part_ids: &mut [usize],
347+
(adjacency, weights): (CsMatView<f64>, &'a [f64]),
348+
) -> Result<Self::Metadata, Self::Error> {
349+
fiduccia_mattheyses(
350+
part_ids,
351+
weights,
352+
adjacency,
353+
self.max_passes,
354+
self.max_flips_per_pass,
355+
self.max_imbalance_per_flip,
356+
self.max_bad_move_in_a_row,
357+
);
358+
Ok(())
359+
}
360+
}

src/algorithms/graph_growth.rs

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
use rand::seq::SliceRandom;
22
use sprs::CsMatView;
33

4-
pub fn graph_growth(weights: &[f64], adjacency: CsMatView<f64>, num_parts: usize) -> Vec<usize> {
4+
fn graph_growth(
5+
initial_ids: &mut [usize],
6+
weights: &[f64],
7+
adjacency: CsMatView<f64>,
8+
num_parts: usize,
9+
) {
510
let (shape_x, shape_y) = adjacency.shape();
611
assert_eq!(shape_x, shape_y);
712
assert_eq!(weights.len(), shape_x);
813

9-
let mut initial_ids = vec![0; weights.len()];
1014
// let total_weight = weights.iter().sum::<f64>();
1115
// let weight_per_part = total_weight / num_parts as f64;
1216
let max_expansion_per_pass = 20;
@@ -54,6 +58,92 @@ pub fn graph_growth(weights: &[f64], adjacency: CsMatView<f64>, num_parts: usize
5458
}
5559
}
5660
}
61+
}
62+
63+
/// Graph Growth algorithm
64+
///
65+
/// A topologic algorithm that generates a partition from a topologic mesh.
66+
/// Given a number k of parts, the algorithm selects k nodes randomly and assigns them to a different part.
67+
/// Then, at each iteration, each part is expanded to neighbor nodes that are not yet assigned to a part
68+
///
69+
/// # Example
70+
///
71+
/// ```rust
72+
/// use coupe::Point2D;
73+
/// use coupe::TopologicPartitioner;
74+
/// use coupe::partition::Partition;
75+
/// use sprs::CsMat;
76+
///
77+
/// // +--+--+--+
78+
/// // | | | |
79+
/// // +--+--+--+
80+
///
81+
/// let points = vec![
82+
/// Point2D::new(0., 0.),
83+
/// Point2D::new(1., 0.),
84+
/// Point2D::new(2., 0.),
85+
/// Point2D::new(3., 0.),
86+
/// Point2D::new(0., 1.),
87+
/// Point2D::new(1., 1.),
88+
/// Point2D::new(2., 1.),
89+
/// Point2D::new(3., 1.),
90+
/// ];
91+
///
92+
/// let weights = vec![1.; 8];
93+
///
94+
/// let mut adjacency = CsMat::empty(sprs::CSR, 8);
95+
/// adjacency.reserve_outer_dim(8);
96+
/// eprintln!("shape: {:?}", adjacency.shape());
97+
/// adjacency.insert(0, 1, 1.);
98+
/// adjacency.insert(1, 2, 1.);
99+
/// adjacency.insert(2, 3, 1.);
100+
/// adjacency.insert(4, 5, 1.);
101+
/// adjacency.insert(5, 6, 1.);
102+
/// adjacency.insert(6, 7, 1.);
103+
/// adjacency.insert(0, 4, 1.);
104+
/// adjacency.insert(1, 5, 1.);
105+
/// adjacency.insert(2, 6, 1.);
106+
/// adjacency.insert(3, 7, 1.);
107+
///
108+
/// // symmetry
109+
/// adjacency.insert(1, 0, 1.);
110+
/// adjacency.insert(2, 1, 1.);
111+
/// adjacency.insert(3, 2, 1.);
112+
/// adjacency.insert(5, 4, 1.);
113+
/// adjacency.insert(6, 5, 1.);
114+
/// adjacency.insert(7, 6, 1.);
115+
/// adjacency.insert(4, 0, 1.);
116+
/// adjacency.insert(5, 1, 1.);
117+
/// adjacency.insert(6, 2, 1.);
118+
/// adjacency.insert(7, 3, 1.);
119+
///
120+
/// let gg = coupe::GraphGrowth::new(2);
121+
///
122+
/// let _partition = gg.partition(points.as_slice(), &weights, adjacency.view());
123+
/// ```
124+
#[derive(Debug, Clone, Copy)]
125+
pub struct GraphGrowth {
126+
pub part_count: usize,
127+
}
57128

58-
initial_ids
129+
impl<'a, W> crate::Partition<(CsMatView<'a, f64>, W)> for GraphGrowth
130+
where
131+
W: AsRef<[f64]>,
132+
{
133+
type Metadata = ();
134+
type Error = std::convert::Infallible;
135+
136+
fn partition(
137+
&mut self,
138+
part_ids: &mut [usize],
139+
(adjacency, weights): (CsMatView<f64>, W),
140+
) -> Result<Self::Metadata, Self::Error> {
141+
graph_growth(
142+
part_ids,
143+
weights.as_ref(),
144+
adjacency.view(),
145+
self.part_count,
146+
);
147+
Ok(())
148+
}
59149
}

0 commit comments

Comments
 (0)