Skip to content

Commit 8d6e2c2

Browse files
committed
vn/first: multi-criteria support
1 parent 66bc5bb commit 8d6e2c2

File tree

1 file changed

+33
-129
lines changed

1 file changed

+33
-129
lines changed

src/algorithms/vn/first.rs

Lines changed: 33 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,20 @@ where
2929
let (min_load, mut max_load) = part_loads.iter().cloned().minmax().into_option().unwrap();
3030
let mut imbalance = max_load.clone() - min_load;
3131

32-
let mut i = weights.len();
32+
let mut i = 0;
3333
let mut i_last = 0;
3434
let mut algo_iterations = 0;
35-
while i != i_last {
36-
i = (i + 1) % weights.len();
37-
35+
loop {
3836
// loop through the weights.
3937
let p = partition[i];
4038

4139
if part_loads[p] < max_load {
4240
// weight #i is not in the heaviest partition, and thus the move
4341
// will not reduce the max imbalance.
42+
i = (i + 1) % weights.len();
43+
if i == i_last {
44+
break;
45+
}
4446
continue;
4547
}
4648

@@ -56,129 +58,23 @@ where
5658
let (new_min_load, new_max_load) =
5759
part_loads.iter().cloned().minmax().into_option().unwrap();
5860
let new_imbalance = new_max_load.clone() - new_min_load.clone();
59-
if imbalance < new_imbalance {
61+
if new_imbalance <= imbalance {
62+
// The move decreases the partition imbalance.
63+
imbalance = new_imbalance;
64+
max_load = new_max_load;
65+
partition[i] = q;
66+
i_last = i;
67+
} else {
6068
// The move does not decrease the partition imbalance.
6169
part_loads[p] += weights[i].clone();
6270
part_loads[q] = part_loads[p].clone() - weights[i].clone();
6371
continue;
6472
}
65-
imbalance = new_imbalance;
66-
max_load = new_max_load;
67-
partition[i] = q;
68-
i_last = i;
69-
}
70-
71-
algo_iterations += 1;
72-
}
73-
74-
algo_iterations
75-
}
76-
77-
#[allow(dead_code)]
78-
pub fn vn_first<const C: usize, T>(
79-
partition: &mut [usize],
80-
criteria: &[[T; C]],
81-
num_parts: usize,
82-
) -> usize
83-
where
84-
T: AddAssign + SubAssign + Sub<Output = T> + Sum,
85-
T: Zero + One + FromPrimitive,
86-
T: Copy + Ord,
87-
{
88-
if num_parts < 2 || criteria.is_empty() || C == 0 {
89-
return 0;
90-
}
91-
92-
assert_eq!(criteria.len(), partition.len());
93-
94-
let mut part_loads_per_criterion = {
95-
let mut loads = vec![[T::zero(); C]; num_parts];
96-
for (w, weight) in criteria.iter().enumerate() {
97-
for (part_load, criterion) in loads[partition[w]].iter_mut().zip(weight) {
98-
*part_load += *criterion;
99-
}
100-
}
101-
loads
102-
};
103-
let total_weight_per_criterion = {
104-
// TODO replace with .collect() once [_; C] implements FromIterator.
105-
let mut ws = [T::zero(); C];
106-
for c in 0..C {
107-
ws[c] = part_loads_per_criterion[c].iter().cloned().sum();
108-
}
109-
ws
110-
};
111-
if total_weight_per_criterion.contains(&T::zero()) {
112-
return 0;
113-
}
114-
115-
let min_max_loads = |part_loads_per_criterion: &Vec<[T; C]>| -> [(T, T); C] {
116-
// TODO replace with .collect() once [_; C] implements FromIterator.
117-
let mut imbs = [(T::zero(), T::zero()); C];
118-
for c in 0..C {
119-
imbs[c] = part_loads_per_criterion[c]
120-
.iter()
121-
.cloned()
122-
.minmax()
123-
.into_option()
124-
.unwrap();
12573
}
126-
imbs
127-
};
128-
129-
let (global_min_load, mut global_max_load) = *min_max_loads(&part_loads_per_criterion)
130-
.iter()
131-
.max_by_key(|(min_load, max_load)| *max_load - *min_load)
132-
.unwrap();
133-
let mut imbalance = global_max_load - global_min_load;
13474

135-
let mut i = 0;
136-
let mut i_last = 0;
137-
let mut algo_iterations = 0;
138-
while i != i_last {
139-
i = (i + 1) % criteria.len();
140-
141-
// loop through the weights.
142-
let p = partition[i];
143-
144-
if part_loads_per_criterion[p]
145-
.iter()
146-
.all(|criterion_load| *criterion_load < global_max_load)
147-
{
148-
// weight #i is not in the heaviest partition, and thus the move
149-
// will not reduce the max imbalance.
150-
continue;
151-
}
152-
153-
for q in 0..num_parts {
154-
// loop through the parts.
155-
if p == q {
156-
// weight #i is already in partition #q.
157-
continue;
158-
}
159-
160-
for c in 0..C {
161-
part_loads_per_criterion[p][c] -= criteria[i][c];
162-
part_loads_per_criterion[q][c] += criteria[i][c];
163-
}
164-
let (new_global_min_load, new_global_max_load) =
165-
*min_max_loads(&part_loads_per_criterion)
166-
.iter()
167-
.max_by_key(|(min_load, max_load)| *max_load - *min_load)
168-
.unwrap();
169-
let new_imbalance = new_global_max_load - new_global_min_load;
170-
if imbalance < new_imbalance {
171-
// The move does not decrease the partition imbalance.
172-
for c in 0..C {
173-
part_loads_per_criterion[p][c] += criteria[i][c];
174-
part_loads_per_criterion[q][c] -= criteria[i][c];
175-
}
176-
continue;
177-
}
178-
imbalance = new_imbalance;
179-
global_max_load = new_global_max_load;
180-
partition[i] = q;
181-
i_last = i;
75+
i = (i + 1) % weights.len();
76+
if i == i_last {
77+
break;
18278
}
18379

18480
algo_iterations += 1;
@@ -280,18 +176,26 @@ mod tests {
280176
#[test]
281177
fn small() {
282178
const W: [[i32; 2]; 6] = [[1, 2], [2, 4], [3, 6], [8, 4], [10, 5], [12, 6]];
283-
let mut part = [0; W.len()];
179+
let w: Vec<_> = W
180+
.iter()
181+
.map(|w| nalgebra::SVector::<i32, 2>::from(*w))
182+
.collect();
183+
let mut partition = [0; W.len()];
284184

285-
vn_first(&mut part, &W, 1);
185+
vn_first_mono(&mut partition, &w, 2);
186+
println!("partition: {partition:?}");
286187
let imbs_ini: Vec<i32> = (0..W[0].len())
287-
.map(|c| imbalance::max_imbalance(2, &part, W.iter().map(|w| w[c])))
188+
.map(|c| imbalance::max_imbalance(2, &partition, W.iter().map(|w| w[c])))
189+
.collect();
190+
vn_first_mono(&mut partition, &w, 2);
191+
println!("partition: {partition:?}");
192+
let imbs_end: Vec<i32> = (0..W[0].len())
193+
.map(|c| imbalance::max_imbalance(2, &partition, W.iter().map(|w| w[c])))
288194
.collect();
289-
vn_first(&mut part, &W, 2);
290-
let imbs_end =
291-
(0..W[0].len()).map(|c| imbalance::max_imbalance(2, &part, W.iter().map(|w| w[c])));
195+
println!("imbalances: {imbs_end:?} < {imbs_ini:?}");
292196
for (imb_ini, imb_end) in imbs_ini.into_iter().zip(imbs_end) {
197+
println!("imbalance: {imb_end} < {imb_ini}");
293198
assert!(imb_end <= imb_ini);
294-
println!("imbalance : {} < {}", imb_end, imb_ini);
295199
}
296200
}
297201

@@ -303,7 +207,7 @@ mod tests {
303207
(weights, mut partition) in
304208
(2..200_usize).prop_flat_map(|num_weights| {
305209
let weights = prop::collection::vec(
306-
(1..1000_i32, 1..1000_i32).prop_map(|(a, b)| [a, b]),
210+
(1..1000_i32, 1..1000_i32).prop_map(|(a, b)| nalgebra::SVector::<i32, 2>::new(a, b)),
307211
num_weights
308212
);
309213
let partition = prop::collection::vec(0..1_usize, num_weights);
@@ -313,7 +217,7 @@ mod tests {
313217
let imbs_ini: Vec<i32> = (0..C)
314218
.map(|c| imbalance::max_imbalance(2, &partition, weights.iter().map(|w| w[c])))
315219
.collect();
316-
vn_first(&mut partition, &weights, 2);
220+
vn_first_mono(&mut partition, &weights, 2);
317221
let imbs_end: Vec<i32> = (0..C)
318222
.map(|c| imbalance::max_imbalance(2, &partition, weights.iter().map(|w| w[c])))
319223
.collect();

0 commit comments

Comments
 (0)