Skip to main content

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::SoftScore};
20///
21/// #[derive(Clone)]
22/// struct NQueens {
23///     n: usize,
24///     rows: Vec<Option<usize>>,
25///     score: Option<SoftScore>,
26/// }
27///
28/// impl PlanningSolution for NQueens {
29///     type Score = SoftScore;
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    */
54    fn score(&self) -> Option<Self::Score>;
55
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    */
62    fn is_initialized(&self) -> bool {
63        // Default implementation - can be overridden by derived code
64        true
65    }
66}
67
68/// Marker trait for planning entities.
69///
70/// A planning entity is something that gets planned/optimized.
71/// It contains one or more planning variables that the solver will change.
72///
73/// # Example
74///
75/// ```
76/// use std::any::Any;
77/// use solverforge_core::PlanningEntity;
78///
79/// #[derive(Clone)]
80/// struct Queen {
81///     column: i32,
82///     row: Option<i32>,
83/// }
84///
85/// impl PlanningEntity for Queen {
86///     fn as_any(&self) -> &dyn Any { self }
87///     fn as_any_mut(&mut self) -> &mut dyn Any { self }
88/// }
89/// ```
90///
91/// For complex entities, use the `#[derive(PlanningEntity)]` macro from `solverforge-macros`.
92///
93/// # Pinning
94///
95/// Entities can be "pinned" to prevent the solver from changing them.
96/// Override `is_pinned()` to return true for pinned entities.
97pub trait PlanningEntity: Clone + Send + Sync + Any + 'static {
98    /* Returns true if this entity is pinned (should not be changed).
99
100    Default implementation returns false (entity can be changed).
101    */
102    fn is_pinned(&self) -> bool {
103        false
104    }
105
106    // Cast to Any for dynamic typing support.
107    fn as_any(&self) -> &dyn Any;
108
109    // Cast to mutable Any for dynamic typing support.
110    fn as_any_mut(&mut self) -> &mut dyn Any;
111}
112
113/// Marker trait for problem facts.
114///
115/// Problem facts are immutable input data that define the problem.
116/// They are used by constraints but not changed during solving.
117///
118/// # Example
119///
120/// ```
121/// use std::any::Any;
122/// use solverforge_core::ProblemFact;
123///
124/// #[derive(Clone)]
125/// struct Room {
126///     id: i64,
127///     capacity: usize,
128/// }
129///
130/// impl ProblemFact for Room {
131///     fn as_any(&self) -> &dyn Any { self }
132/// }
133/// ```
134pub trait ProblemFact: Clone + Send + Sync + Any + 'static {
135    // Cast to Any for dynamic typing support.
136    fn as_any(&self) -> &dyn Any;
137}
138
139/// Trait for unique identification of entities and facts.
140///
141/// Used for looking up working copies during solving and rebasing moves.
142///
143/// # Example
144///
145/// ```
146/// use solverforge_core::PlanningId;
147///
148/// #[derive(Clone)]
149/// struct Task {
150///     id: i64,
151///     name: String,
152/// }
153///
154/// impl PlanningId for Task {
155///     type Id = i64;
156///     fn planning_id(&self) -> i64 { self.id }
157/// }
158/// ```
159///
160/// The ID type must be `Eq + Hash + Clone`.
161pub trait PlanningId {
162    // The type of the unique identifier.
163    type Id: Eq + Hash + Clone + Send + Sync + 'static;
164
165    /* Returns the unique identifier for this object.
166
167    This must never return a value that changes during solving.
168    */
169    fn planning_id(&self) -> Self::Id;
170}
171
172/// Trait for solutions with list-based planning variables.
173///
174/// Used for problems like VRP where entities (vehicles) have ordered lists
175/// of elements (visits) that can be inserted, removed, or reordered.
176///
177/// # Examples
178///
179/// ```
180/// use solverforge_core::domain::ListVariableSolution;
181/// use solverforge_core::PlanningSolution;
182/// use solverforge_core::score::SoftScore;
183///
184/// #[derive(Clone)]
185/// struct Vehicle {
186///     visits: Vec<usize>,
187/// }
188///
189/// #[derive(Clone)]
190/// struct VrpSolution {
191///     vehicles: Vec<Vehicle>,
192///     visit_count: usize,
193///     score: Option<SoftScore>,
194/// }
195///
196/// impl PlanningSolution for VrpSolution {
197///     type Score = SoftScore;
198///     fn score(&self) -> Option<Self::Score> { self.score }
199///     fn set_score(&mut self, score: Option<Self::Score>) { self.score = score; }
200/// }
201///
202/// impl ListVariableSolution for VrpSolution {
203///     type Element = usize;
204///
205///     fn entity_count(&self) -> usize {
206///         self.vehicles.len()
207///     }
208///
209///     fn list_len(&self, entity_idx: usize) -> usize {
210///         self.vehicles[entity_idx].visits.len()
211///     }
212///
213///     fn list_get(&self, entity_idx: usize, position: usize) -> Self::Element {
214///         self.vehicles[entity_idx].visits[position]
215///     }
216///
217///     fn list_push(&mut self, entity_idx: usize, elem: Self::Element) {
218///         self.vehicles[entity_idx].visits.push(elem);
219///     }
220///
221///     fn list_insert(&mut self, entity_idx: usize, position: usize, elem: Self::Element) {
222///         self.vehicles[entity_idx].visits.insert(position, elem);
223///     }
224///
225///     fn list_remove(&mut self, entity_idx: usize, position: usize) -> Self::Element {
226///         self.vehicles[entity_idx].visits.remove(position)
227///     }
228///
229///     fn list_reverse(&mut self, entity_idx: usize, start: usize, end: usize) {
230///         self.vehicles[entity_idx].visits[start..end].reverse();
231///     }
232///
233///     fn unassigned_elements(&self) -> Vec<Self::Element> {
234///         use std::collections::HashSet;
235///         let assigned: HashSet<usize> = self.vehicles
236///             .iter()
237///             .flat_map(|v| v.visits.iter().copied())
238///             .collect();
239///         (0..self.visit_count)
240///             .filter(|i| !assigned.contains(i))
241///             .collect()
242///     }
243/// }
244/// ```
245pub trait ListVariableSolution: PlanningSolution {
246    // The type of elements in the list (typically an index or ID).
247    type Element: Copy + Send + Sync;
248
249    fn entity_count(&self) -> usize;
250
251    fn list_len(&self, entity_idx: usize) -> usize;
252
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}