mathhook_core/calculus/pde/common/
fourier_coefficients.rs1use crate::calculus::integrals::Integration;
10use crate::calculus::pde::types::InitialCondition;
11use crate::core::{Expression, Symbol};
12use crate::expr;
13
14pub fn compute_fourier_coefficients(
49 initial_condition: &InitialCondition,
50 eigenfunctions: &[Expression],
51 domain: &(Expression, Expression),
52 variable: &Symbol,
53) -> Result<Vec<Expression>, String> {
54 let f = match initial_condition {
55 InitialCondition::Value { function } => function,
56 InitialCondition::Derivative { .. } => {
57 return Err(
58 "Fourier coefficient computation from derivative IC not yet implemented".to_owned(),
59 );
60 }
61 };
62
63 let mut coefficients = Vec::new();
64
65 for x_n in eigenfunctions {
66 let coefficient = compute_single_coefficient(f, x_n, domain, variable)?;
67 coefficients.push(coefficient);
68 }
69
70 Ok(coefficients)
71}
72
73fn compute_single_coefficient(
75 f: &Expression,
76 x_n: &Expression,
77 domain: &(Expression, Expression),
78 variable: &Symbol,
79) -> Result<Expression, String> {
80 let (_a, _b) = domain;
81
82 let numerator_integrand = Expression::mul(vec![f.clone(), x_n.clone()]);
83 let denominator_integrand = Expression::mul(vec![x_n.clone(), x_n.clone()]);
84
85 let numerator = numerator_integrand.integrate(variable.clone(), 0);
86 let denominator = denominator_integrand.integrate(variable.clone(), 0);
87
88 if denominator == expr!(0) {
89 return Err("Eigenfunction has zero norm".to_owned());
90 }
91
92 Ok(Expression::mul(vec![
93 numerator,
94 Expression::pow(denominator, Expression::integer(-1)),
95 ]))
96}
97
98pub fn compute_normalization(
102 eigenfunction: &Expression,
103 domain: &(Expression, Expression),
104 variable: &Symbol,
105) -> Result<Expression, String> {
106 let (_a, _b) = domain;
107 let integrand = Expression::mul(vec![eigenfunction.clone(), eigenfunction.clone()]);
108
109 let norm_squared = integrand.integrate(variable.clone(), 0);
110
111 Ok(Expression::function("sqrt", vec![norm_squared]))
112}
113
114pub fn compute_coefficients_analytical(
121 initial_condition: &InitialCondition,
122 eigenfunctions: &[Expression],
123 domain: &(Expression, Expression),
124 variable: &Symbol,
125) -> Result<Option<Vec<Expression>>, String> {
126 let f = match initial_condition {
127 InitialCondition::Value { function } => function,
128 _ => return Ok(None),
129 };
130
131 if is_constant(f) {
132 return compute_constant_coefficients(f, eigenfunctions, domain, variable).map(Some);
133 }
134
135 if is_single_mode(f, eigenfunctions) {
136 return compute_single_mode_coefficients(f, eigenfunctions).map(Some);
137 }
138
139 Ok(None)
140}
141
142fn is_constant(expr: &Expression) -> bool {
144 matches!(expr, Expression::Number(_))
145}
146
147fn is_single_mode(f: &Expression, eigenfunctions: &[Expression]) -> bool {
149 eigenfunctions.iter().any(|x_n| {
150 if let Expression::Mul(terms) = f {
151 terms.iter().any(|term| term == x_n)
152 } else {
153 f == x_n
154 }
155 })
156}
157
158fn compute_constant_coefficients(
160 constant: &Expression,
161 eigenfunctions: &[Expression],
162 domain: &(Expression, Expression),
163 variable: &Symbol,
164) -> Result<Vec<Expression>, String> {
165 let mut coefficients = Vec::new();
166
167 for x_n in eigenfunctions {
168 let (_a, _b) = domain;
169 let integrand = x_n.clone();
170
171 let numerator = integrand.integrate(variable.clone(), 0);
172
173 let norm_integrand = Expression::mul(vec![x_n.clone(), x_n.clone()]);
174 let denominator = norm_integrand.integrate(variable.clone(), 0);
175
176 let coefficient = Expression::mul(vec![
177 constant.clone(),
178 numerator,
179 Expression::pow(denominator, Expression::integer(-1)),
180 ]);
181 coefficients.push(coefficient);
182 }
183
184 Ok(coefficients)
185}
186
187fn compute_single_mode_coefficients(
189 f: &Expression,
190 eigenfunctions: &[Expression],
191) -> Result<Vec<Expression>, String> {
192 let mut coefficients = Vec::new();
193
194 for x_n in eigenfunctions {
195 if f == x_n {
196 coefficients.push(expr!(1));
197 } else if let Expression::Mul(terms) = f {
198 if terms.iter().any(|term| term == x_n) {
199 let const_part: Vec<Expression> =
200 terms.iter().filter(|term| *term != x_n).cloned().collect();
201 if const_part.len() == 1 {
202 coefficients.push(const_part[0].clone());
203 } else {
204 coefficients.push(Expression::mul(const_part));
205 }
206 } else {
207 coefficients.push(expr!(0));
208 }
209 } else {
210 coefficients.push(expr!(0));
211 }
212 }
213
214 Ok(coefficients)
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220 use crate::{expr, symbol};
221
222 #[test]
223 fn test_is_constant() {
224 assert!(is_constant(&expr!(5)));
225 assert!(!is_constant(&symbol!(x).into()));
226 }
227
228 #[test]
229 fn test_is_single_mode_exact_match() {
230 let x = symbol!(x);
231 let sin_x = Expression::function("sin", vec![Expression::symbol(x)]);
232 let eigenfunctions = vec![sin_x.clone()];
233
234 assert!(is_single_mode(&sin_x, &eigenfunctions));
235 }
236
237 #[test]
238 fn test_is_single_mode_with_coefficient() {
239 let x = symbol!(x);
240 let sin_x = Expression::function("sin", vec![Expression::symbol(x)]);
241 let f = Expression::mul(vec![Expression::integer(2), sin_x.clone()]);
242 let eigenfunctions = vec![sin_x];
243
244 assert!(is_single_mode(&f, &eigenfunctions));
245 }
246
247 #[test]
248 fn test_is_single_mode_no_match() {
249 let x = symbol!(x);
250 let sin_x = Expression::function("sin", vec![Expression::symbol(x.clone())]);
251 let cos_x = Expression::function("cos", vec![Expression::symbol(x)]);
252 let eigenfunctions = vec![sin_x];
253
254 assert!(!is_single_mode(&cos_x, &eigenfunctions));
255 }
256
257 #[test]
258 fn test_compute_single_mode_coefficients_exact() {
259 let x = symbol!(x);
260 let sin_x = Expression::function("sin", vec![Expression::symbol(x)]);
261 let eigenfunctions = vec![sin_x.clone(), Expression::function("cos", vec![])];
262
263 let result = compute_single_mode_coefficients(&sin_x, &eigenfunctions);
264 assert!(result.is_ok());
265
266 let coefficients = result.unwrap();
267 assert_eq!(coefficients.len(), 2);
268 assert_eq!(coefficients[0], expr!(1));
269 assert_eq!(coefficients[1], expr!(0));
270 }
271
272 #[test]
273 fn test_compute_single_mode_coefficients_with_constant() {
274 let x = symbol!(x);
275 let sin_x = Expression::function("sin", vec![Expression::symbol(x)]);
276 let f = Expression::mul(vec![Expression::integer(3), sin_x.clone()]);
277 let eigenfunctions = vec![sin_x];
278
279 let result = compute_single_mode_coefficients(&f, &eigenfunctions);
280 assert!(result.is_ok());
281
282 let coefficients = result.unwrap();
283 assert_eq!(coefficients.len(), 1);
284 assert_eq!(coefficients[0], expr!(3));
285 }
286
287 #[test]
288 fn test_compute_fourier_coefficients_single_mode() {
289 let x = symbol!(x);
290 let sin_x = Expression::function("sin", vec![Expression::symbol(x.clone())]);
291 let ic = InitialCondition::value(sin_x.clone());
292 let eigenfunctions = vec![sin_x];
293 let domain = (expr!(0), expr!(pi));
294
295 let result = compute_fourier_coefficients(&ic, &eigenfunctions, &domain, &x);
296 assert!(result.is_ok());
297 }
298
299 #[test]
300 fn test_compute_fourier_coefficients_derivative_ic_error() {
301 let x = symbol!(x);
302 let sin_x = Expression::function("sin", vec![Expression::symbol(x.clone())]);
303 let ic = InitialCondition::derivative(sin_x.clone());
304 let eigenfunctions = vec![sin_x];
305 let domain = (expr!(0), expr!(pi));
306
307 let result = compute_fourier_coefficients(&ic, &eigenfunctions, &domain, &x);
308 assert!(result.is_err());
309 }
310}