ruvector_math/optimization/
polynomial.rs1use std::collections::HashMap;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub struct Monomial {
11 pub powers: Vec<(usize, usize)>,
13}
14
15impl Monomial {
16 pub fn one() -> Self {
18 Self { powers: vec![] }
19 }
20
21 pub fn var(i: usize) -> Self {
23 Self {
24 powers: vec![(i, 1)],
25 }
26 }
27
28 pub fn new(mut powers: Vec<(usize, usize)>) -> Self {
30 powers.sort_by_key(|&(i, _)| i);
32
33 let mut merged = Vec::new();
35 for (i, p) in powers {
36 if p == 0 {
37 continue;
38 }
39 if let Some(&mut (last_i, ref mut last_p)) = merged.last_mut() {
40 if last_i == i {
41 *last_p += p;
42 continue;
43 }
44 }
45 merged.push((i, p));
46 }
47
48 Self { powers: merged }
49 }
50
51 pub fn degree(&self) -> usize {
53 self.powers.iter().map(|&(_, p)| p).sum()
54 }
55
56 pub fn is_constant(&self) -> bool {
58 self.powers.is_empty()
59 }
60
61 pub fn max_var(&self) -> Option<usize> {
63 self.powers.last().map(|&(i, _)| i)
64 }
65
66 pub fn mul(&self, other: &Monomial) -> Monomial {
68 let mut combined = self.powers.clone();
69 combined.extend(other.powers.iter().copied());
70 Monomial::new(combined)
71 }
72
73 pub fn eval(&self, x: &[f64]) -> f64 {
75 let mut result = 1.0;
76 for &(i, p) in &self.powers {
77 if i < x.len() {
78 result *= x[i].powi(p as i32);
79 }
80 }
81 result
82 }
83
84 pub fn divides(&self, other: &Monomial) -> bool {
86 let mut j = 0;
87 for &(i, p) in &self.powers {
88 while j < other.powers.len() && other.powers[j].0 < i {
90 j += 1;
91 }
92 if j >= other.powers.len() || other.powers[j].0 != i || other.powers[j].1 < p {
93 return false;
94 }
95 j += 1;
96 }
97 true
98 }
99}
100
101impl std::fmt::Display for Monomial {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 if self.powers.is_empty() {
104 write!(f, "1")
105 } else {
106 let parts: Vec<String> = self
107 .powers
108 .iter()
109 .map(|&(i, p)| {
110 if p == 1 {
111 format!("x{}", i)
112 } else {
113 format!("x{}^{}", i, p)
114 }
115 })
116 .collect();
117 write!(f, "{}", parts.join("*"))
118 }
119 }
120}
121
122#[derive(Debug, Clone)]
124pub struct Term {
125 pub coeff: f64,
127 pub monomial: Monomial,
129}
130
131impl Term {
132 pub fn new(coeff: f64, powers: Vec<(usize, usize)>) -> Self {
134 Self {
135 coeff,
136 monomial: Monomial::new(powers),
137 }
138 }
139
140 pub fn constant(c: f64) -> Self {
142 Self {
143 coeff: c,
144 monomial: Monomial::one(),
145 }
146 }
147
148 pub fn degree(&self) -> usize {
150 self.monomial.degree()
151 }
152}
153
154#[derive(Debug, Clone)]
156pub struct Polynomial {
157 terms: HashMap<Monomial, f64>,
159 degree: usize,
161 num_vars: usize,
163}
164
165impl Polynomial {
166 pub fn zero() -> Self {
168 Self {
169 terms: HashMap::new(),
170 degree: 0,
171 num_vars: 0,
172 }
173 }
174
175 pub fn constant(c: f64) -> Self {
177 if c == 0.0 {
178 return Self::zero();
179 }
180 let mut terms = HashMap::new();
181 terms.insert(Monomial::one(), c);
182 Self {
183 terms,
184 degree: 0,
185 num_vars: 0,
186 }
187 }
188
189 pub fn var(i: usize) -> Self {
191 let mut terms = HashMap::new();
192 terms.insert(Monomial::var(i), 1.0);
193 Self {
194 terms,
195 degree: 1,
196 num_vars: i + 1,
197 }
198 }
199
200 pub fn from_terms(term_list: Vec<Term>) -> Self {
202 let mut terms = HashMap::new();
203 let mut degree = 0;
204 let mut num_vars = 0;
205
206 for term in term_list {
207 if term.coeff.abs() < 1e-15 {
208 continue;
209 }
210
211 degree = degree.max(term.degree());
212 if let Some(max_v) = term.monomial.max_var() {
213 num_vars = num_vars.max(max_v + 1);
214 }
215
216 *terms.entry(term.monomial).or_insert(0.0) += term.coeff;
217 }
218
219 terms.retain(|_, &mut c| c.abs() >= 1e-15);
221
222 Self {
223 terms,
224 degree,
225 num_vars,
226 }
227 }
228
229 pub fn degree(&self) -> usize {
231 self.degree
232 }
233
234 pub fn num_variables(&self) -> usize {
236 self.num_vars
237 }
238
239 pub fn num_terms(&self) -> usize {
241 self.terms.len()
242 }
243
244 pub fn is_zero(&self) -> bool {
246 self.terms.is_empty()
247 }
248
249 pub fn coeff(&self, m: &Monomial) -> f64 {
251 *self.terms.get(m).unwrap_or(&0.0)
252 }
253
254 pub fn terms(&self) -> impl Iterator<Item = (&Monomial, &f64)> {
256 self.terms.iter()
257 }
258
259 pub fn eval(&self, x: &[f64]) -> f64 {
261 self.terms.iter().map(|(m, &c)| c * m.eval(x)).sum()
262 }
263
264 pub fn add(&self, other: &Polynomial) -> Polynomial {
266 let mut terms = self.terms.clone();
267
268 for (m, &c) in &other.terms {
269 *terms.entry(m.clone()).or_insert(0.0) += c;
270 }
271
272 terms.retain(|_, &mut c| c.abs() >= 1e-15);
273
274 let degree = terms.keys().map(|m| m.degree()).max().unwrap_or(0);
275 let num_vars = terms
276 .keys()
277 .filter_map(|m| m.max_var())
278 .max()
279 .map(|v| v + 1)
280 .unwrap_or(0);
281
282 Polynomial {
283 terms,
284 degree,
285 num_vars,
286 }
287 }
288
289 pub fn sub(&self, other: &Polynomial) -> Polynomial {
291 self.add(&other.neg())
292 }
293
294 pub fn neg(&self) -> Polynomial {
296 Polynomial {
297 terms: self.terms.iter().map(|(m, &c)| (m.clone(), -c)).collect(),
298 degree: self.degree,
299 num_vars: self.num_vars,
300 }
301 }
302
303 pub fn scale(&self, s: f64) -> Polynomial {
305 if s.abs() < 1e-15 {
306 return Polynomial::zero();
307 }
308
309 Polynomial {
310 terms: self
311 .terms
312 .iter()
313 .map(|(m, &c)| (m.clone(), s * c))
314 .collect(),
315 degree: self.degree,
316 num_vars: self.num_vars,
317 }
318 }
319
320 pub fn mul(&self, other: &Polynomial) -> Polynomial {
322 let mut terms = HashMap::new();
323
324 for (m1, &c1) in &self.terms {
325 for (m2, &c2) in &other.terms {
326 let m = m1.mul(m2);
327 *terms.entry(m).or_insert(0.0) += c1 * c2;
328 }
329 }
330
331 terms.retain(|_, &mut c| c.abs() >= 1e-15);
332
333 let degree = terms.keys().map(|m| m.degree()).max().unwrap_or(0);
334 let num_vars = terms
335 .keys()
336 .filter_map(|m| m.max_var())
337 .max()
338 .map(|v| v + 1)
339 .unwrap_or(0);
340
341 Polynomial {
342 terms,
343 degree,
344 num_vars,
345 }
346 }
347
348 pub fn square(&self) -> Polynomial {
350 self.mul(self)
351 }
352
353 pub fn pow(&self, n: usize) -> Polynomial {
355 if n == 0 {
356 return Polynomial::constant(1.0);
357 }
358 if n == 1 {
359 return self.clone();
360 }
361
362 let mut result = self.clone();
363 for _ in 1..n {
364 result = result.mul(self);
365 }
366 result
367 }
368
369 pub fn monomials_up_to_degree(num_vars: usize, max_degree: usize) -> Vec<Monomial> {
371 let mut result = vec![Monomial::one()];
372
373 if max_degree == 0 || num_vars == 0 {
374 return result;
375 }
376
377 fn generate(
379 var: usize,
380 num_vars: usize,
381 remaining_degree: usize,
382 current: Vec<(usize, usize)>,
383 result: &mut Vec<Monomial>,
384 ) {
385 if var >= num_vars {
386 result.push(Monomial::new(current));
387 return;
388 }
389
390 for p in 0..=remaining_degree {
391 let mut next = current.clone();
392 if p > 0 {
393 next.push((var, p));
394 }
395 generate(var + 1, num_vars, remaining_degree - p, next, result);
396 }
397 }
398
399 for d in 1..=max_degree {
400 generate(0, num_vars, d, vec![], &mut result);
401 }
402
403 result.sort_by(|a, b| {
405 a.degree()
406 .cmp(&b.degree())
407 .then_with(|| a.powers.cmp(&b.powers))
408 });
409 result.dedup();
410
411 let const_count = result.iter().filter(|m| m.is_constant()).count();
413 if const_count > 1 {
414 let mut seen_const = false;
415 result.retain(|m| {
416 if m.is_constant() {
417 if seen_const {
418 return false;
419 }
420 seen_const = true;
421 }
422 true
423 });
424 }
425
426 result
427 }
428}
429
430impl std::fmt::Display for Polynomial {
431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432 if self.terms.is_empty() {
433 return write!(f, "0");
434 }
435
436 let mut sorted: Vec<_> = self.terms.iter().collect();
437 sorted.sort_by(|a, b| {
438 a.0.degree()
439 .cmp(&b.0.degree())
440 .then_with(|| a.0.powers.cmp(&b.0.powers))
441 });
442
443 let parts: Vec<String> = sorted
444 .iter()
445 .map(|(m, &c)| {
446 if m.is_constant() {
447 format!("{:.4}", c)
448 } else if (c - 1.0).abs() < 1e-10 {
449 format!("{}", m)
450 } else if (c + 1.0).abs() < 1e-10 {
451 format!("-{}", m)
452 } else {
453 format!("{:.4}*{}", c, m)
454 }
455 })
456 .collect();
457
458 write!(f, "{}", parts.join(" + "))
459 }
460}
461
462#[cfg(test)]
463mod tests {
464 use super::*;
465
466 #[test]
467 fn test_monomial() {
468 let m1 = Monomial::var(0);
469 let m2 = Monomial::var(1);
470 let m3 = m1.mul(&m2);
471
472 assert_eq!(m3.degree(), 2);
473 assert_eq!(m3.powers, vec![(0, 1), (1, 1)]);
474 }
475
476 #[test]
477 fn test_polynomial_eval() {
478 let p = Polynomial::from_terms(vec![
480 Term::new(1.0, vec![(0, 2)]),
481 Term::new(2.0, vec![(0, 1), (1, 1)]),
482 Term::new(1.0, vec![(1, 2)]),
483 ]);
484
485 assert!((p.eval(&[1.0, 1.0]) - 4.0).abs() < 1e-10);
487
488 assert!((p.eval(&[2.0, 3.0]) - 25.0).abs() < 1e-10);
490 }
491
492 #[test]
493 fn test_polynomial_mul() {
494 let x = Polynomial::var(0);
496 let y = Polynomial::var(1);
497 let sum = x.add(&y);
498 let squared = sum.square();
499
500 assert!((squared.coeff(&Monomial::new(vec![(0, 2)])) - 1.0).abs() < 1e-10);
501 assert!((squared.coeff(&Monomial::new(vec![(0, 1), (1, 1)])) - 2.0).abs() < 1e-10);
502 assert!((squared.coeff(&Monomial::new(vec![(1, 2)])) - 1.0).abs() < 1e-10);
503 }
504
505 #[test]
506 fn test_monomials_generation() {
507 let monoms = Polynomial::monomials_up_to_degree(2, 2);
508
509 assert!(monoms.len() >= 6);
511 }
512}