solverforge_core/domain/traits.rs
1//! Core domain traits
2
3use std::any::Any;
4use std::hash::Hash;
5
6use crate::score::Score;
7
8/// Marker trait for planning solutions.
9///
10/// A planning solution represents both the problem definition and the
11/// (potentially partial) solution. It contains:
12/// - Problem facts: Immutable input data
13/// - Planning entities: Things to be optimized
14/// - Score: The quality of the current solution
15///
16/// # Example
17///
18/// ```
19/// use solverforge_core::{PlanningSolution, score::SimpleScore};
20///
21/// #[derive(Clone)]
22/// struct NQueens {
23/// n: usize,
24/// rows: Vec<Option<usize>>,
25/// score: Option<SimpleScore>,
26/// }
27///
28/// impl PlanningSolution for NQueens {
29/// type Score = SimpleScore;
30///
31/// fn score(&self) -> Option<Self::Score> {
32/// self.score
33/// }
34///
35/// fn set_score(&mut self, score: Option<Self::Score>) {
36/// self.score = score;
37/// }
38/// }
39/// ```
40///
41/// For complex solutions, use the `#[derive(PlanningSolution)]` macro from `solverforge-macros`.
42///
43/// # Thread Safety
44///
45/// Planning solutions must be `Send + Sync` to support multi-threaded solving.
46pub trait PlanningSolution: Clone + Send + Sync + 'static {
47 /// The score type used to evaluate this solution.
48 type Score: Score;
49
50 /// Returns the current score of this solution, if calculated.
51 ///
52 /// Returns `None` if the solution has not been scored yet.
53 fn score(&self) -> Option<Self::Score>;
54
55 /// Sets the score of this solution.
56 fn set_score(&mut self, score: Option<Self::Score>);
57
58 /// Returns true if this solution is fully initialized.
59 ///
60 /// A solution is initialized when all planning variables have been assigned.
61 fn is_initialized(&self) -> bool {
62 // Default implementation - can be overridden by derived code
63 true
64 }
65}
66
67/// Marker trait for planning entities.
68///
69/// A planning entity is something that gets planned/optimized.
70/// It contains one or more planning variables that the solver will change.
71///
72/// # Example
73///
74/// ```
75/// use std::any::Any;
76/// use solverforge_core::PlanningEntity;
77///
78/// #[derive(Clone)]
79/// struct Queen {
80/// column: i32,
81/// row: Option<i32>,
82/// }
83///
84/// impl PlanningEntity for Queen {
85/// fn as_any(&self) -> &dyn Any { self }
86/// fn as_any_mut(&mut self) -> &mut dyn Any { self }
87/// }
88/// ```
89///
90/// For complex entities, use the `#[derive(PlanningEntity)]` macro from `solverforge-macros`.
91///
92/// # Pinning
93///
94/// Entities can be "pinned" to prevent the solver from changing them.
95/// Override `is_pinned()` to return true for pinned entities.
96pub trait PlanningEntity: Clone + Send + Sync + Any + 'static {
97 /// Returns true if this entity is pinned (should not be changed).
98 ///
99 /// Default implementation returns false (entity can be changed).
100 fn is_pinned(&self) -> bool {
101 false
102 }
103
104 /// Cast to Any for dynamic typing support.
105 fn as_any(&self) -> &dyn Any;
106
107 /// Cast to mutable Any for dynamic typing support.
108 fn as_any_mut(&mut self) -> &mut dyn Any;
109}
110
111/// Marker trait for problem facts.
112///
113/// Problem facts are immutable input data that define the problem.
114/// They are used by constraints but not changed during solving.
115///
116/// # Example
117///
118/// ```
119/// use std::any::Any;
120/// use solverforge_core::ProblemFact;
121///
122/// #[derive(Clone)]
123/// struct Room {
124/// id: i64,
125/// capacity: usize,
126/// }
127///
128/// impl ProblemFact for Room {
129/// fn as_any(&self) -> &dyn Any { self }
130/// }
131/// ```
132pub trait ProblemFact: Clone + Send + Sync + Any + 'static {
133 /// Cast to Any for dynamic typing support.
134 fn as_any(&self) -> &dyn Any;
135}
136
137/// Trait for unique identification of entities and facts.
138///
139/// Used for looking up working copies during solving and rebasing moves.
140///
141/// # Example
142///
143/// ```
144/// use solverforge_core::PlanningId;
145///
146/// #[derive(Clone)]
147/// struct Task {
148/// id: i64,
149/// name: String,
150/// }
151///
152/// impl PlanningId for Task {
153/// type Id = i64;
154/// fn planning_id(&self) -> i64 { self.id }
155/// }
156/// ```
157///
158/// The ID type must be `Eq + Hash + Clone`.
159pub trait PlanningId {
160 /// The type of the unique identifier.
161 type Id: Eq + Hash + Clone + Send + Sync + 'static;
162
163 /// Returns the unique identifier for this object.
164 ///
165 /// This must never return a value that changes during solving.
166 fn planning_id(&self) -> Self::Id;
167}
168
169/// Trait for solutions with list-based planning variables.
170///
171/// Used for problems like VRP where entities (vehicles) have ordered lists
172/// of elements (visits) that can be inserted, removed, or reordered.
173///
174/// # Examples
175///
176/// ```
177/// use solverforge_core::domain::ListVariableSolution;
178/// use solverforge_core::PlanningSolution;
179/// use solverforge_core::score::SimpleScore;
180///
181/// #[derive(Clone)]
182/// struct Vehicle {
183/// visits: Vec<usize>,
184/// }
185///
186/// #[derive(Clone)]
187/// struct VrpSolution {
188/// vehicles: Vec<Vehicle>,
189/// visit_count: usize,
190/// score: Option<SimpleScore>,
191/// }
192///
193/// impl PlanningSolution for VrpSolution {
194/// type Score = SimpleScore;
195/// fn score(&self) -> Option<Self::Score> { self.score }
196/// fn set_score(&mut self, score: Option<Self::Score>) { self.score = score; }
197/// }
198///
199/// impl ListVariableSolution for VrpSolution {
200/// type Element = usize;
201///
202/// fn entity_count(&self) -> usize {
203/// self.vehicles.len()
204/// }
205///
206/// fn list_len(&self, entity_idx: usize) -> usize {
207/// self.vehicles[entity_idx].visits.len()
208/// }
209///
210/// fn list_get(&self, entity_idx: usize, position: usize) -> Self::Element {
211/// self.vehicles[entity_idx].visits[position]
212/// }
213///
214/// fn list_push(&mut self, entity_idx: usize, elem: Self::Element) {
215/// self.vehicles[entity_idx].visits.push(elem);
216/// }
217///
218/// fn list_insert(&mut self, entity_idx: usize, position: usize, elem: Self::Element) {
219/// self.vehicles[entity_idx].visits.insert(position, elem);
220/// }
221///
222/// fn list_remove(&mut self, entity_idx: usize, position: usize) -> Self::Element {
223/// self.vehicles[entity_idx].visits.remove(position)
224/// }
225///
226/// fn list_reverse(&mut self, entity_idx: usize, start: usize, end: usize) {
227/// self.vehicles[entity_idx].visits[start..end].reverse();
228/// }
229///
230/// fn unassigned_elements(&self) -> Vec<Self::Element> {
231/// use std::collections::HashSet;
232/// let assigned: HashSet<usize> = self.vehicles
233/// .iter()
234/// .flat_map(|v| v.visits.iter().copied())
235/// .collect();
236/// (0..self.visit_count)
237/// .filter(|i| !assigned.contains(i))
238/// .collect()
239/// }
240/// }
241/// ```
242pub trait ListVariableSolution: PlanningSolution {
243 /// The type of elements in the list (typically an index or ID).
244 type Element: Copy + Send + Sync;
245
246 /// Returns the number of entities (list owners).
247 fn entity_count(&self) -> usize;
248
249 /// Returns the length of the list for the given entity.
250 fn list_len(&self, entity_idx: usize) -> usize;
251
252 /// Returns the element at the given position in the entity's list.
253 fn list_get(&self, entity_idx: usize, position: usize) -> Self::Element;
254
255 /// Appends an element to the end of the entity's list.
256 fn list_push(&mut self, entity_idx: usize, elem: Self::Element);
257
258 /// Inserts an element at the given position in the entity's list.
259 fn list_insert(&mut self, entity_idx: usize, position: usize, elem: Self::Element);
260
261 /// Removes and returns the element at the given position.
262 fn list_remove(&mut self, entity_idx: usize, position: usize) -> Self::Element;
263
264 /// Reverses the elements in the range [start, end) for the entity's list.
265 fn list_reverse(&mut self, entity_idx: usize, start: usize, end: usize);
266
267 /// Returns all elements not currently assigned to any entity.
268 fn unassigned_elements(&self) -> Vec<Self::Element>;
269}