problemreductions/
polynomial.rs1use crate::types::ProblemSize;
4use std::ops::Add;
5
6#[derive(Clone, Debug, PartialEq)]
8pub struct Monomial {
9 pub coefficient: f64,
10 pub variables: Vec<(&'static str, u8)>,
11}
12
13impl Monomial {
14 pub fn constant(c: f64) -> Self {
15 Self { coefficient: c, variables: vec![] }
16 }
17
18 pub fn var(name: &'static str) -> Self {
19 Self { coefficient: 1.0, variables: vec![(name, 1)] }
20 }
21
22 pub fn var_pow(name: &'static str, exp: u8) -> Self {
23 Self { coefficient: 1.0, variables: vec![(name, exp)] }
24 }
25
26 pub fn scale(mut self, c: f64) -> Self {
27 self.coefficient *= c;
28 self
29 }
30
31 pub fn evaluate(&self, size: &ProblemSize) -> f64 {
32 let var_product: f64 = self.variables.iter()
33 .map(|(name, exp)| {
34 let val = size.get(name).unwrap_or(0) as f64;
35 val.powi(*exp as i32)
36 })
37 .product();
38 self.coefficient * var_product
39 }
40}
41
42#[derive(Clone, Debug, PartialEq)]
44pub struct Polynomial {
45 pub terms: Vec<Monomial>,
46}
47
48impl Polynomial {
49 pub fn zero() -> Self {
50 Self { terms: vec![] }
51 }
52
53 pub fn constant(c: f64) -> Self {
54 Self { terms: vec![Monomial::constant(c)] }
55 }
56
57 pub fn var(name: &'static str) -> Self {
58 Self { terms: vec![Monomial::var(name)] }
59 }
60
61 pub fn var_pow(name: &'static str, exp: u8) -> Self {
62 Self { terms: vec![Monomial::var_pow(name, exp)] }
63 }
64
65 pub fn scale(mut self, c: f64) -> Self {
66 for term in &mut self.terms {
67 term.coefficient *= c;
68 }
69 self
70 }
71
72 pub fn evaluate(&self, size: &ProblemSize) -> f64 {
73 self.terms.iter().map(|m| m.evaluate(size)).sum()
74 }
75}
76
77impl Add for Polynomial {
78 type Output = Self;
79
80 fn add(mut self, other: Self) -> Self {
81 self.terms.extend(other.terms);
82 self
83 }
84}
85
86#[macro_export]
88macro_rules! poly {
89 ($name:ident) => {
91 $crate::polynomial::Polynomial::var(stringify!($name))
92 };
93 ($name:ident ^ $exp:literal) => {
95 $crate::polynomial::Polynomial::var_pow(stringify!($name), $exp)
96 };
97 ($c:literal) => {
99 $crate::polynomial::Polynomial::constant($c as f64)
100 };
101 ($c:literal * $name:ident) => {
103 $crate::polynomial::Polynomial::var(stringify!($name)).scale($c as f64)
104 };
105 ($c:literal * $name:ident ^ $exp:literal) => {
107 $crate::polynomial::Polynomial::var_pow(stringify!($name), $exp).scale($c as f64)
108 };
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_monomial_constant() {
117 let m = Monomial::constant(5.0);
118 let size = ProblemSize::new(vec![("n", 10)]);
119 assert_eq!(m.evaluate(&size), 5.0);
120 }
121
122 #[test]
123 fn test_monomial_variable() {
124 let m = Monomial::var("n");
125 let size = ProblemSize::new(vec![("n", 10)]);
126 assert_eq!(m.evaluate(&size), 10.0);
127 }
128
129 #[test]
130 fn test_monomial_var_pow() {
131 let m = Monomial::var_pow("n", 2);
132 let size = ProblemSize::new(vec![("n", 5)]);
133 assert_eq!(m.evaluate(&size), 25.0);
134 }
135
136 #[test]
137 fn test_polynomial_add() {
138 let p = Polynomial::var("n").scale(3.0)
140 + Polynomial::var("m").scale(2.0);
141
142 let size = ProblemSize::new(vec![("n", 10), ("m", 5)]);
143 assert_eq!(p.evaluate(&size), 40.0); }
145
146 #[test]
147 fn test_polynomial_complex() {
148 let p = Polynomial::var_pow("n", 2)
150 + Polynomial::var("m").scale(3.0);
151
152 let size = ProblemSize::new(vec![("n", 4), ("m", 2)]);
153 assert_eq!(p.evaluate(&size), 22.0); }
155
156 #[test]
157 fn test_poly_macro() {
158 let size = ProblemSize::new(vec![("n", 5), ("m", 3)]);
159
160 assert_eq!(poly!(n).evaluate(&size), 5.0);
161 assert_eq!(poly!(n^2).evaluate(&size), 25.0);
162 assert_eq!(poly!(3 * n).evaluate(&size), 15.0);
163 assert_eq!(poly!(2 * m^2).evaluate(&size), 18.0);
164 }
165
166 #[test]
167 fn test_missing_variable() {
168 let p = Polynomial::var("missing");
169 let size = ProblemSize::new(vec![("n", 10)]);
170 assert_eq!(p.evaluate(&size), 0.0); }
172
173 #[test]
174 fn test_polynomial_zero() {
175 let p = Polynomial::zero();
176 let size = ProblemSize::new(vec![("n", 100)]);
177 assert_eq!(p.evaluate(&size), 0.0);
178 }
179
180 #[test]
181 fn test_polynomial_constant() {
182 let p = Polynomial::constant(42.0);
183 let size = ProblemSize::new(vec![("n", 100)]);
184 assert_eq!(p.evaluate(&size), 42.0);
185 }
186
187 #[test]
188 fn test_monomial_scale() {
189 let m = Monomial::var("n").scale(3.0);
190 let size = ProblemSize::new(vec![("n", 10)]);
191 assert_eq!(m.evaluate(&size), 30.0);
192 }
193
194 #[test]
195 fn test_polynomial_scale() {
196 let p = Polynomial::var("n").scale(5.0);
197 let size = ProblemSize::new(vec![("n", 10)]);
198 assert_eq!(p.evaluate(&size), 50.0);
199 }
200
201 #[test]
202 fn test_monomial_multi_variable() {
203 let m = Monomial {
205 coefficient: 1.0,
206 variables: vec![("n", 1), ("m", 2)],
207 };
208 let size = ProblemSize::new(vec![("n", 2), ("m", 3)]);
209 assert_eq!(m.evaluate(&size), 18.0); }
211}