symrs/
system.rs

1use std::collections::HashSet;
2
3use itertools::Itertools;
4use log::{debug, info};
5
6use super::*;
7use crate::symbol;
8use lazy_static::lazy_static;
9
10#[derive(Debug, Clone)]
11pub struct System {
12    pub unknowns: Vec<Func>,
13    pub known_unknowns: Vec<Func>,
14    pub knowns: Vec<Func>,
15    pub equations: Vec<Equation>,
16}
17
18lazy_static! {
19    static ref shape_matrixes: [Symbol; 4] = [
20        Symbol::new("M^n"),
21        Symbol::new("A^n"),
22        Symbol::new("M^n,n-1"),
23        Symbol::new("A^n,n-1")
24    ];
25}
26
27impl std::fmt::Display for System {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        write!(
30            f,
31            "System ({}), ({}), ({})\n{}",
32            self.unknowns
33                .iter()
34                .map(|f| f.str())
35                .collect::<Vec<_>>()
36                .join(", "),
37            self.known_unknowns
38                .iter()
39                .map(|f| f.str())
40                .collect::<Vec<_>>()
41                .join(", "),
42            self.knowns
43                .iter()
44                .map(|f| f.str())
45                .collect::<Vec<_>>()
46                .join(", "),
47            self.equations
48                .iter()
49                .map(|e| format!("> {}", e.str()))
50                .collect::<Vec<_>>()
51                .join("\n")
52        )
53    }
54}
55
56#[derive(thiserror::Error, Debug)]
57pub enum SystemError {
58    #[error("failed to simplify system")]
59    SimplificationFailed(#[source] SolvingError),
60}
61
62impl System {
63    pub fn new<
64        'a,
65        T: IntoIterator<Item = &'a str>,
66        U: IntoIterator<Item = &'a str>,
67        V: IntoIterator<Item = &'a Equation>,
68    >(
69        unknowns: T,
70        knowns: U,
71        equations: V,
72    ) -> Self {
73        Self {
74            unknowns: unknowns.into_iter().map(|s| Func::new(s, [])).collect(),
75            knowns: knowns.into_iter().map(|s| Func::new(s, [])).collect(),
76            equations: equations.into_iter().cloned().collect(),
77            known_unknowns: vec![],
78        }
79    }
80
81    pub fn to_first_order_in_time(&self) -> Self {
82        let mut unknowns_with_snd_time_derivatives = HashSet::new();
83        for unknown in &self.unknowns {
84            let snd_time_derivative = unknown.diff("t", 2);
85            for equation in &self.equations {
86                if equation.has(snd_time_derivative.get_ref()) {
87                    unknowns_with_snd_time_derivatives.insert(unknown);
88                    info!(
89                        "Unknown `{}` has a second order time derivative",
90                        unknown.str()
91                    );
92                    break;
93                }
94            }
95        }
96        let mut unknowns = self.unknowns.clone();
97        let mut substitutions = vec![];
98        let mut equations =
99            Vec::with_capacity(self.equations.len() + unknowns_with_snd_time_derivatives.len());
100        if !unknowns_with_snd_time_derivatives.is_empty() {
101            info!("Converting system to first order in time");
102        }
103        for unknown in unknowns_with_snd_time_derivatives {
104            let fst_time_derivative = Func::new(&format!("dt_{}", unknown.name), []);
105
106            substitutions.push([unknown.diff("t", 2), fst_time_derivative.diff("t", 1)]);
107            equations.push(Equation {
108                lhs: fst_time_derivative.clone_box(),
109                rhs: unknown.diff("t", 1),
110            });
111            unknowns.push(fst_time_derivative);
112        }
113
114        equations.extend(
115            self.equations
116                .iter()
117                .map(|e| e.subs(&substitutions).as_eq().expect("equation")),
118        );
119
120        System {
121            unknowns,
122            knowns: self.knowns.clone(),
123            equations,
124            known_unknowns: vec![],
125        }
126    }
127
128    pub fn time_discretized(&self) -> Self {
129        let mut unknowns: Vec<Func> = Vec::with_capacity(self.unknowns.len());
130        let mut known_unknowns: Vec<Func> = Vec::with_capacity(self.unknowns.len());
131        let mut knowns: Vec<Func> = Vec::with_capacity(self.unknowns.len() + self.knowns.len());
132
133        // let substitutions: Vec<_> = iproduct!(self.unknowns.iter(), unknowns.iter()).chain(iproduct!(self.knowns.iter().zip,knowns.iter()).map(|(replaced, replacement)| [replaced, replacement]).collect();
134        let mut substitutions: Vec<[Box<dyn Expr>; 2]> =
135            Vec::with_capacity(self.unknowns.len() + self.knowns.len());
136
137        for f in &self.unknowns {
138            let [prev, curr] = f.time_discretize();
139            known_unknowns.push(prev);
140            unknowns.push(curr);
141        }
142
143        for f in &self.knowns {
144            let [prev, curr] = f.time_discretize();
145            knowns.push(prev);
146            knowns.push(curr);
147        }
148
149        for f in self.unknowns.iter().chain(self.knowns.iter()) {
150            let [prev, curr] = f.time_discretize();
151
152            let curr = &curr.clone_box();
153            let prev = &prev.clone_box();
154
155            let k = &Symbol::new_box("k").clone_box();
156            let theta = &Symbol::new_box("θ").clone_box();
157
158            substitutions.push([f.diff("t", 1).clone_box(), (curr - prev) / k]);
159            substitutions.push([
160                f.clone_box(),
161                theta * curr + (Integer::new_box(1) - theta) * prev,
162            ])
163        }
164
165        let equations: Vec<_> = self
166            .equations
167            .iter()
168            .map(|e| e.subs(&substitutions).as_eq().unwrap())
169            .collect();
170
171        System {
172            unknowns,
173            knowns,
174            equations,
175            known_unknowns,
176        }
177    }
178
179    pub fn simplified(&self) -> Result<Self, SystemError> {
180        info!("Simplifying system so that each equation has one unknown");
181        let mut equations = self.equations.clone();
182        let mut unsolved_unknowns: HashSet<&Func> = HashSet::from_iter(self.unknowns.iter());
183
184        for i in (0..self.equations.len()).rev() {
185            for unknown in self.unknowns.iter().rev() {
186                if !unsolved_unknowns.contains(unknown) {
187                    continue;
188                }
189                if equations[i].has(unknown.get_ref()) {
190                    info!("Solving equation {} for {}", i, unknown.str());
191                    let solved_equation = equations[i]
192                        .solve([unknown.get_ref()])
193                        .map_err(|e| SystemError::SimplificationFailed(e))?
194                        .expand()
195                        .as_eq()
196                        .unwrap();
197                    equations[i] = solved_equation;
198                    unsolved_unknowns.remove(unknown);
199                    if unsolved_unknowns.len() == 0 {
200                        break;
201                    }
202
203                    let substitution =
204                        vec![[equations[i].lhs.clone_box(), equations[i].rhs.clone_box()]];
205                    debug!("Equation {} is now {}", i, equations[i].str());
206                    debug!("substitution: {:?}", substitution);
207
208                    for j in 0..i {
209                        info!(
210                            "Substituting {} in equation {} using equation {}",
211                            equations[i].lhs.str(),
212                            j,
213                            i
214                        );
215                        equations[j] = equations[j].subs(&substitution).as_eq().expect("equation");
216                        equations[j] = equations[j]
217                            .solve(unsolved_unknowns.iter().map(|f| f.get_ref()))
218                            .map_err(|e| SystemError::SimplificationFailed(e))?;
219                    }
220                    break;
221                }
222            }
223        }
224
225        Ok(self.with_equations(equations))
226    }
227
228    pub fn factor(&self) -> Self {
229        let laplacian = symbol!("laplacian");
230        let matrixes = [Symbol::new_box("M^n"), Symbol::new_box("A^n")];
231        let symbols: Vec<Box<dyn Expr>> = self
232            .unknowns
233            .iter()
234            .map(|f| f.get_ref())
235            .chain(self.known_unknowns.iter().map(|f| f.get_ref()))
236            .chain(matrixes.iter().map(|m| m.get_ref()))
237            .chain(self.knowns.iter().map(|f| f.get_ref()))
238            .flat_map(|f| [laplacian * f, f.clone_box()])
239            .chain([Symbol::new_box("k"), Symbol::new_box("theta")])
240            .collect();
241        let symbols: Vec<_> = symbols.iter().map(|e| e.get_ref()).collect();
242        let equations: Vec<_> = self
243            .equations
244            .iter()
245            .map(|eq| eq.factor(&symbols).as_eq().unwrap())
246            .collect();
247
248        self.with_equations(equations)
249    }
250
251    pub fn matrixify(&self) -> Self {
252        let mut knowns: Vec<Func> = Vec::with_capacity(self.knowns.len());
253        let mut known_unknowns: Vec<Func> = Vec::with_capacity(self.known_unknowns.len());
254        let mut unknowns: Vec<Func> = Vec::with_capacity(self.unknowns.len());
255        let laplacian = symbol!("laplacian");
256
257        let mass_mat = symbol!("M^n");
258        let mass_mat_prev = symbol!("M^n,n-1");
259        let laplace_mat = symbol!("A^n");
260        let laplace_mat_prev = symbol!("A^n,n-1");
261
262        let mut substitutions =
263            Vec::with_capacity(2 * knowns.len() + 2 * known_unknowns.len() + unknowns.len());
264
265        for known in &self.knowns {
266            let known_vec = known.to_vector();
267            substitutions.push([known.clone_box(), known_vec.clone_box()]);
268            knowns.push(known_vec);
269        }
270
271        for unknown in &self.unknowns {
272            let unknown_vec = unknown.to_vector();
273            substitutions.push([
274                laplacian * unknown.get_ref(),
275                -laplace_mat * unknown_vec.get_ref(),
276            ]);
277            substitutions.push([unknown.clone_box(), mass_mat * unknown_vec.get_ref()]);
278            unknowns.push(unknown_vec);
279        }
280
281        for unknown in &self.known_unknowns {
282            let unknown_vec = unknown.to_vector();
283            substitutions.push([
284                laplacian * unknown.get_ref(),
285                -laplace_mat_prev * unknown_vec.get_ref(),
286            ]);
287            substitutions.push([unknown.clone_box(), mass_mat_prev * unknown_vec.get_ref()]);
288            known_unknowns.push(unknown_vec);
289        }
290
291        let equations = self
292            .equations
293            .iter()
294            .map(|e| {
295                e.subs(&substitutions)
296                    .factor(&unknowns.iter().map(|e| e.get_ref()).collect::<Vec<_>>())
297                    .as_eq()
298                    .unwrap()
299            })
300            .collect();
301
302        System {
303            unknowns,
304            knowns,
305            known_unknowns,
306            equations,
307        }
308    }
309
310    pub fn to_constant_mesh(&self) -> Self {
311        let mass_mat = Symbol::new_box("M^n");
312        let mass_mat_prev = Symbol::new_box("M^n,n-1");
313        let laplace_mat = Symbol::new_box("A^n");
314        let laplace_mat_prev = Symbol::new_box("A^n,n-1");
315
316        let subs = [[mass_mat_prev, mass_mat], [laplace_mat_prev, laplace_mat]];
317
318        self.with_equations(
319            self.equations
320                .iter()
321                .map(|eq| eq.subs(&subs).as_eq().unwrap())
322                .collect(),
323        )
324        .factor()
325    }
326
327    pub fn to_crank_nikolson(&self) -> Self {
328        info!("Applying a Crank-Nikolson time discretization");
329        let theta = Symbol::new_box("θ");
330        self.subs(&[[theta, Rational::new_box(1, 2)]]).simplify()
331    }
332
333    pub fn to_explicit_euler(&self) -> Self {
334        info!("Applying an explicit Euler time discretization");
335        let theta = Symbol::new_box("θ");
336        self.subs(&[[theta, Integer::new_box(0)]])
337    }
338
339    pub fn to_implicit_euler(&self) -> Self {
340        info!("Applying an implicit Euler time discretization");
341        let theta = Symbol::new_box("θ");
342        self.subs(&[[theta, Integer::new_box(1)]])
343    }
344
345    pub fn subs(&self, substitutions: &[[Box<dyn Expr>; 2]]) -> Self {
346        self.with_equations(
347            self.equations
348                .iter()
349                .map(|e| e.subs(substitutions).as_eq().unwrap())
350                .collect(),
351        )
352    }
353
354    pub fn expand(&self) -> Self {
355        self.with_equations(
356            self.equations
357                .iter()
358                .map(|e| e.expand().as_eq().unwrap())
359                .collect(),
360        )
361    }
362
363    pub fn simplify(&self) -> Self {
364        self.expand().factor().with_equations(
365            self.equations
366                .iter()
367                .map(|expr| expr.simplify().as_eq().unwrap())
368                .collect(),
369        )
370    }
371
372    pub fn with_equations(&self, equations: Vec<Equation>) -> Self {
373        System {
374            known_unknowns: self.known_unknowns.clone(),
375            unknowns: self.unknowns.clone(),
376            knowns: self.knowns.clone(),
377            equations,
378        }
379    }
380
381    pub fn vectors(&self) -> impl Iterator<Item = (&dyn Expr, bool)> {
382        self.unknowns
383            .iter()
384            .map(|u| (u, true))
385            .chain(self.known_unknowns.iter().map(|u| (u, true)))
386            .chain(self.knowns.iter().map(|f| (f, false)))
387            .map(|(f, is_unknown)| (f.get_ref(), is_unknown))
388    }
389
390    pub fn matrixes(&self) -> impl Iterator<Item = &dyn Expr> {
391        shape_matrixes.iter().map(|m| m.get_ref())
392    }
393
394    pub fn num_vectors(&self) -> usize {
395        [&self.unknowns, &self.known_unknowns, &self.knowns]
396            .iter()
397            .map(|v| v.len())
398            .reduce(|acc, e| acc + e)
399            .unwrap()
400    }
401
402    pub fn eqs_in_solving_order(&self) -> impl Iterator<Item = &Equation> {
403        fn get_rhs_unknowns(system: &System, eq: &Equation) -> impl Iterator<Item = String> {
404            system.unknowns.iter().filter_map(|unknown| {
405                if eq.has(unknown) {
406                    Some(unknown.str())
407                } else {
408                    None
409                }
410            })
411        }
412
413        self.equations
414            .iter()
415            .map(|e| (e, get_rhs_unknowns(self, e).count()))
416            .sorted_by_key(|e| e.1)
417            .map(|(e, _)| e)
418    }
419
420    pub fn equation_lhs_unknowns(&self, equation: &Equation) -> impl Iterator<Item = &dyn Expr> {
421        self.unknowns.iter().filter_map(|unknown| {
422            if equation.has(unknown) {
423                Some(unknown.get_ref())
424            } else {
425                None
426            }
427        })
428    }
429}