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