solverforge_solver/phase/partitioned/
partitioner.rs1use std::fmt::Debug;
8
9use solverforge_core::domain::PlanningSolution;
10
11pub trait SolutionPartitioner<S: PlanningSolution>: Send + Sync + Debug {
27 fn partition(&self, solution: &S) -> Vec<S>;
42
43 fn merge(&self, original: &S, partitions: Vec<S>) -> S;
58
59 fn recommended_partition_count(&self) -> Option<usize> {
65 None
66 }
67}
68
69pub struct FunctionalPartitioner<S, PF, MF>
74where
75 S: PlanningSolution,
76 PF: Fn(&S) -> Vec<S> + Send + Sync,
77 MF: Fn(&S, Vec<S>) -> S + Send + Sync,
78{
79 partition_fn: PF,
80 merge_fn: MF,
81 recommended_count: Option<usize>,
82 _phantom: std::marker::PhantomData<fn() -> S>,
83}
84
85impl<S, PF, MF> FunctionalPartitioner<S, PF, MF>
86where
87 S: PlanningSolution,
88 PF: Fn(&S) -> Vec<S> + Send + Sync,
89 MF: Fn(&S, Vec<S>) -> S + Send + Sync,
90{
91 pub fn new(partition_fn: PF, merge_fn: MF) -> Self {
92 Self {
93 partition_fn,
94 merge_fn,
95 recommended_count: None,
96 _phantom: std::marker::PhantomData,
97 }
98 }
99
100 pub fn with_recommended_count(mut self, count: usize) -> Self {
101 self.recommended_count = Some(count);
102 self
103 }
104}
105
106impl<S, PF, MF> Debug for FunctionalPartitioner<S, PF, MF>
107where
108 S: PlanningSolution,
109 PF: Fn(&S) -> Vec<S> + Send + Sync,
110 MF: Fn(&S, Vec<S>) -> S + Send + Sync,
111{
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 f.debug_struct("FunctionalPartitioner")
114 .field("recommended_count", &self.recommended_count)
115 .finish()
116 }
117}
118
119impl<S, PF, MF> SolutionPartitioner<S> for FunctionalPartitioner<S, PF, MF>
120where
121 S: PlanningSolution,
122 PF: Fn(&S) -> Vec<S> + Send + Sync,
123 MF: Fn(&S, Vec<S>) -> S + Send + Sync,
124{
125 fn partition(&self, solution: &S) -> Vec<S> {
126 (self.partition_fn)(solution)
127 }
128
129 fn merge(&self, original: &S, partitions: Vec<S>) -> S {
130 (self.merge_fn)(original, partitions)
131 }
132
133 fn recommended_partition_count(&self) -> Option<usize> {
134 self.recommended_count
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
140pub enum ThreadCount {
141 #[default]
143 Auto,
144 Unlimited,
146 Specific(usize),
148}
149
150impl ThreadCount {
151 pub fn resolve(&self, partition_count: usize) -> usize {
161 match self {
162 ThreadCount::Auto => {
163 let cpus = std::thread::available_parallelism()
164 .map(|p| p.get())
165 .unwrap_or(1);
166 std::cmp::min(cpus, partition_count)
167 }
168 ThreadCount::Unlimited => std::thread::available_parallelism()
169 .map(|p| p.get())
170 .unwrap_or(1),
171 ThreadCount::Specific(n) => std::cmp::min(*n, partition_count),
172 }
173 }
174}
175
176impl std::fmt::Display for ThreadCount {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 match self {
179 ThreadCount::Auto => write!(f, "Auto"),
180 ThreadCount::Unlimited => write!(f, "Unlimited"),
181 ThreadCount::Specific(n) => write!(f, "{}", n),
182 }
183 }
184}
185
186#[cfg(test)]
187#[path = "partitioner_tests.rs"]
188mod tests;