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