oxiz_math/
realclosure_advanced.rs1#[allow(unused_imports)]
19use crate::prelude::*;
20use num_bigint::BigInt;
21use num_rational::BigRational;
22use num_traits::{One, Zero};
23
24pub type Var = u32;
26
27#[derive(Debug, Clone)]
29pub struct AlgebraicNumber {
30 pub polynomial: Vec<BigRational>,
32 pub lower: BigRational,
34 pub upper: BigRational,
36}
37
38impl AlgebraicNumber {
39 pub fn new(polynomial: Vec<BigRational>, lower: BigRational, upper: BigRational) -> Self {
41 Self {
42 polynomial,
43 lower,
44 upper,
45 }
46 }
47
48 pub fn from_rational(value: BigRational) -> Self {
50 Self {
51 polynomial: vec![value.clone(), -BigRational::one()], lower: value.clone(),
53 upper: value,
54 }
55 }
56
57 pub fn is_rational(&self) -> bool {
59 self.lower == self.upper
60 }
61
62 pub fn as_rational(&self) -> Option<BigRational> {
64 if self.is_rational() {
65 Some(self.lower.clone())
66 } else {
67 None
68 }
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Hash)]
74pub struct ThomEncoding {
75 pub signs: Vec<i8>,
78}
79
80impl ThomEncoding {
81 pub fn new(signs: Vec<i8>) -> Self {
83 Self { signs }
84 }
85
86 pub fn is_valid(&self) -> bool {
88 !self.signs.is_empty() && self.signs[0] == 0 }
90}
91
92#[derive(Debug, Clone)]
94pub struct RealClosureAdvancedConfig {
95 pub enable_thom: bool,
97 pub enable_refinement: bool,
99 pub refinement_precision: BigRational,
101}
102
103impl Default for RealClosureAdvancedConfig {
104 fn default() -> Self {
105 Self {
106 enable_thom: true,
107 enable_refinement: true,
108 refinement_precision: BigRational::new(BigInt::from(1), BigInt::from(1000)),
109 }
110 }
111}
112
113#[derive(Debug, Clone, Default)]
115pub struct RealClosureAdvancedStats {
116 pub sign_determinations: u64,
118 pub comparisons: u64,
120 pub thom_encodings: u64,
122 pub refinements: u64,
124}
125
126#[derive(Debug)]
128pub struct RealClosureAdvanced {
129 config: RealClosureAdvancedConfig,
131 stats: RealClosureAdvancedStats,
133}
134
135impl RealClosureAdvanced {
136 pub fn new(config: RealClosureAdvancedConfig) -> Self {
138 Self {
139 config,
140 stats: RealClosureAdvancedStats::default(),
141 }
142 }
143
144 pub fn default_config() -> Self {
146 Self::new(RealClosureAdvancedConfig::default())
147 }
148
149 pub fn sign(&mut self, num: &AlgebraicNumber) -> i8 {
151 self.stats.sign_determinations += 1;
152
153 if num.lower > BigRational::zero() {
154 1
155 } else if num.upper < BigRational::zero() {
156 -1
157 } else if num.is_rational() && num.as_rational() == Some(BigRational::zero()) {
158 0
159 } else {
160 0
162 }
163 }
164
165 pub fn compare(&mut self, a: &AlgebraicNumber, b: &AlgebraicNumber) -> core::cmp::Ordering {
167 self.stats.comparisons += 1;
168
169 if a.upper < b.lower {
171 core::cmp::Ordering::Less
172 } else if a.lower > b.upper {
173 core::cmp::Ordering::Greater
174 } else {
175 core::cmp::Ordering::Equal
177 }
178 }
179
180 pub fn thom_encoding(&mut self, _num: &AlgebraicNumber) -> ThomEncoding {
182 if !self.config.enable_thom {
183 return ThomEncoding::new(vec![0]);
184 }
185
186 self.stats.thom_encodings += 1;
187
188 ThomEncoding::new(vec![0, 1, -1]) }
191
192 pub fn refine_interval(&mut self, num: &mut AlgebraicNumber) {
194 if !self.config.enable_refinement {
195 return;
196 }
197
198 let width = &num.upper - &num.lower;
199 if width <= self.config.refinement_precision {
200 return; }
202
203 self.stats.refinements += 1;
204
205 let mid = (&num.lower + &num.upper) / BigRational::from(BigInt::from(2));
208
209 num.upper = mid;
211 }
212
213 pub fn stats(&self) -> &RealClosureAdvancedStats {
215 &self.stats
216 }
217
218 pub fn reset_stats(&mut self) {
220 self.stats = RealClosureAdvancedStats::default();
221 }
222}
223
224impl Default for RealClosureAdvanced {
225 fn default() -> Self {
226 Self::default_config()
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn test_engine_creation() {
236 let engine = RealClosureAdvanced::default_config();
237 assert_eq!(engine.stats().sign_determinations, 0);
238 }
239
240 #[test]
241 fn test_algebraic_from_rational() {
242 let num = AlgebraicNumber::from_rational(BigRational::from(BigInt::from(3)));
243
244 assert!(num.is_rational());
245 assert_eq!(num.as_rational(), Some(BigRational::from(BigInt::from(3))));
246 }
247
248 #[test]
249 fn test_sign_determination() {
250 let mut engine = RealClosureAdvanced::default_config();
251
252 let positive = AlgebraicNumber::from_rational(BigRational::from(BigInt::from(5)));
253 let negative = AlgebraicNumber::from_rational(BigRational::from(BigInt::from(-3)));
254
255 assert_eq!(engine.sign(&positive), 1);
256 assert_eq!(engine.sign(&negative), -1);
257 assert_eq!(engine.stats().sign_determinations, 2);
258 }
259
260 #[test]
261 fn test_compare() {
262 let mut engine = RealClosureAdvanced::default_config();
263
264 let a = AlgebraicNumber::from_rational(BigRational::from(BigInt::from(3)));
265 let b = AlgebraicNumber::from_rational(BigRational::from(BigInt::from(5)));
266
267 assert_eq!(engine.compare(&a, &b), core::cmp::Ordering::Less);
268 assert_eq!(engine.stats().comparisons, 1);
269 }
270
271 #[test]
272 fn test_thom_encoding() {
273 let mut engine = RealClosureAdvanced::default_config();
274
275 let num = AlgebraicNumber::from_rational(BigRational::zero());
276 let encoding = engine.thom_encoding(&num);
277
278 assert!(encoding.is_valid());
279 assert_eq!(engine.stats().thom_encodings, 1);
280 }
281
282 #[test]
283 fn test_refine_interval() {
284 let mut engine = RealClosureAdvanced::default_config();
285
286 let mut num = AlgebraicNumber::new(
287 vec![
288 BigRational::one(),
289 BigRational::zero(),
290 -BigRational::from(BigInt::from(2)),
291 ], BigRational::one(),
293 BigRational::from(BigInt::from(2)),
294 );
295
296 let initial_width = &num.upper - &num.lower;
297 engine.refine_interval(&mut num);
298 let final_width = &num.upper - &num.lower;
299
300 assert!(final_width < initial_width);
301 assert_eq!(engine.stats().refinements, 1);
302 }
303}