Skip to main content

simular/edd/
equation.rs

1//! Governing equation definitions and traits for EDD.
2//!
3//! Every simulation in the EDD framework must be grounded in a mathematically
4//! verified governing equation. This module provides the traits and types
5//! for defining, documenting, and validating these equations.
6
7use std::fmt;
8
9/// Classification of equation types for domain organization.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum EquationClass {
12    /// Conservation laws (mass, energy, momentum)
13    Conservation,
14    /// Queueing theory equations
15    Queueing,
16    /// Statistical mechanics / thermodynamics
17    Statistical,
18    /// Inventory and supply chain
19    Inventory,
20    /// Optimization and control
21    Optimization,
22    /// Machine learning / inference
23    MachineLearning,
24}
25
26impl fmt::Display for EquationClass {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        match self {
29            Self::Conservation => write!(f, "Conservation"),
30            Self::Queueing => write!(f, "Queueing Theory"),
31            Self::Statistical => write!(f, "Statistical Mechanics"),
32            Self::Inventory => write!(f, "Inventory Management"),
33            Self::Optimization => write!(f, "Optimization"),
34            Self::MachineLearning => write!(f, "Machine Learning"),
35        }
36    }
37}
38
39/// A variable in a governing equation with its metadata.
40#[derive(Debug, Clone)]
41pub struct EquationVariable {
42    /// Symbol used in the equation (e.g., "λ", "L", "W")
43    pub symbol: String,
44    /// Human-readable name
45    pub name: String,
46    /// Physical units (e.g., "items/time", "seconds")
47    pub units: String,
48    /// Description of what this variable represents
49    pub description: String,
50    /// Valid range constraints
51    pub constraints: Option<VariableConstraints>,
52}
53
54/// Constraints on variable values.
55#[derive(Debug, Clone)]
56pub struct VariableConstraints {
57    /// Minimum value (inclusive)
58    pub min: Option<f64>,
59    /// Maximum value (inclusive)
60    pub max: Option<f64>,
61    /// Must be strictly positive
62    pub positive: bool,
63    /// Must be an integer
64    pub integer: bool,
65}
66
67impl EquationVariable {
68    /// Create a new equation variable.
69    #[must_use]
70    pub fn new(symbol: &str, name: &str, units: &str) -> Self {
71        Self {
72            symbol: symbol.to_string(),
73            name: name.to_string(),
74            units: units.to_string(),
75            description: String::new(),
76            constraints: None,
77        }
78    }
79
80    /// Add a description.
81    #[must_use]
82    pub fn with_description(mut self, desc: &str) -> Self {
83        self.description = desc.to_string();
84        self
85    }
86
87    /// Add constraints.
88    #[must_use]
89    pub fn with_constraints(mut self, constraints: VariableConstraints) -> Self {
90        self.constraints = Some(constraints);
91        self
92    }
93}
94
95/// A citation for the governing equation's source.
96#[derive(Debug, Clone)]
97pub struct Citation {
98    /// List of author names
99    pub authors: Vec<String>,
100    /// Publication title
101    pub title: String,
102    /// Journal or conference name
103    pub venue: String,
104    /// Publication year
105    pub year: u32,
106    /// DOI if available
107    pub doi: Option<String>,
108    /// Page numbers if applicable
109    pub pages: Option<String>,
110}
111
112impl Citation {
113    /// Create a new citation.
114    #[must_use]
115    pub fn new(authors: &[&str], venue: &str, year: u32) -> Self {
116        Self {
117            authors: authors.iter().map(|s| (*s).to_string()).collect(),
118            title: String::new(),
119            venue: venue.to_string(),
120            year,
121            doi: None,
122            pages: None,
123        }
124    }
125
126    /// Add the publication title.
127    #[must_use]
128    pub fn with_title(mut self, title: &str) -> Self {
129        self.title = title.to_string();
130        self
131    }
132
133    /// Add DOI.
134    #[must_use]
135    pub fn with_doi(mut self, doi: &str) -> Self {
136        self.doi = Some(doi.to_string());
137        self
138    }
139
140    /// Add page numbers.
141    #[must_use]
142    pub fn with_pages(mut self, pages: &str) -> Self {
143        self.pages = Some(pages.to_string());
144        self
145    }
146}
147
148impl fmt::Display for Citation {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        let authors = self.authors.join(", ");
151        write!(
152            f,
153            "{authors} ({}) \"{}\", {}",
154            self.year, self.title, self.venue
155        )?;
156        if let Some(ref pages) = self.pages {
157            write!(f, ", pp. {pages}")?;
158        }
159        if let Some(ref doi) = self.doi {
160            write!(f, ", doi:{doi}")?;
161        }
162        Ok(())
163    }
164}
165
166/// Trait for governing equations that ground simulations.
167///
168/// Every simulation in the EDD framework must implement this trait
169/// to provide mathematical foundation and falsifiability.
170pub trait GoverningEquation {
171    /// Get the LaTeX representation of the equation.
172    fn latex(&self) -> &str;
173
174    /// Get the equation's classification.
175    fn class(&self) -> EquationClass;
176
177    /// Get the primary citation for this equation.
178    fn citation(&self) -> Citation;
179
180    /// Get all variables in this equation.
181    fn variables(&self) -> Vec<EquationVariable>;
182
183    /// Get a human-readable description.
184    fn description(&self) -> &str;
185
186    /// Get the equation name.
187    fn name(&self) -> &'static str;
188
189    /// Validate that a set of values satisfies the equation within tolerance.
190    ///
191    /// Returns `Ok(())` if the equation holds, `Err` with explanation otherwise.
192    ///
193    /// # Errors
194    /// Returns error message if the values don't satisfy the equation within tolerance.
195    fn validate_consistency(&self, values: &[(&str, f64)], tolerance: f64) -> Result<(), String>;
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_equation_variable_builder() {
204        let var = EquationVariable::new("λ", "arrival_rate", "items/hour")
205            .with_description("Rate at which items arrive to the system")
206            .with_constraints(VariableConstraints {
207                min: Some(0.0),
208                max: None,
209                positive: true,
210                integer: false,
211            });
212
213        assert_eq!(var.symbol, "λ");
214        assert_eq!(var.name, "arrival_rate");
215        assert_eq!(var.units, "items/hour");
216        assert!(!var.description.is_empty());
217        assert!(var.constraints.is_some());
218    }
219
220    #[test]
221    fn test_citation_builder() {
222        let cite = Citation::new(&["Little, J.D.C."], "Operations Research", 1961)
223            .with_title("A Proof for the Queuing Formula: L = λW")
224            .with_doi("10.1287/opre.9.3.383");
225
226        assert_eq!(cite.authors.len(), 1);
227        assert_eq!(cite.year, 1961);
228        assert!(cite.doi.is_some());
229    }
230
231    #[test]
232    fn test_equation_class_display() {
233        assert_eq!(EquationClass::Queueing.to_string(), "Queueing Theory");
234        assert_eq!(EquationClass::Conservation.to_string(), "Conservation");
235    }
236
237    #[test]
238    fn test_all_equation_classes_display() {
239        assert_eq!(
240            EquationClass::Statistical.to_string(),
241            "Statistical Mechanics"
242        );
243        assert_eq!(EquationClass::Inventory.to_string(), "Inventory Management");
244        assert_eq!(EquationClass::Optimization.to_string(), "Optimization");
245        assert_eq!(
246            EquationClass::MachineLearning.to_string(),
247            "Machine Learning"
248        );
249    }
250
251    #[test]
252    fn test_citation_display() {
253        let cite = Citation::new(&["Author A", "Author B"], "Test Journal", 2020)
254            .with_title("Test Title")
255            .with_pages("1-10");
256        let display = format!("{cite}");
257        assert!(display.contains("Author A"));
258        assert!(display.contains("Author B"));
259        assert!(display.contains("2020"));
260        assert!(display.contains("Test Title"));
261        assert!(display.contains("1-10"));
262    }
263
264    #[test]
265    fn test_citation_display_with_doi() {
266        let cite = Citation::new(&["Author"], "Journal", 2021)
267            .with_title("Title")
268            .with_doi("10.1234/test");
269        let display = format!("{cite}");
270        assert!(display.contains("doi:10.1234/test"));
271    }
272
273    #[test]
274    fn test_citation_with_pages() {
275        let cite = Citation::new(&["Test"], "Venue", 2022).with_pages("100-200");
276        assert!(cite.pages.is_some());
277        assert_eq!(cite.pages.as_deref(), Some("100-200"));
278    }
279
280    #[test]
281    fn test_variable_constraints() {
282        let constraints = VariableConstraints {
283            min: Some(-5.0),
284            max: Some(5.0),
285            positive: false,
286            integer: true,
287        };
288        assert_eq!(constraints.min, Some(-5.0));
289        assert_eq!(constraints.max, Some(5.0));
290        assert!(!constraints.positive);
291        assert!(constraints.integer);
292    }
293
294    #[test]
295    fn test_equation_variable_without_constraints() {
296        let var = EquationVariable::new("x", "variable", "units");
297        assert!(var.constraints.is_none());
298        assert!(var.description.is_empty());
299    }
300}