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}