提问人:exocortex 提问时间:11/4/2022 最后编辑:katnissexocortex 更新时间:11/10/2022 访问量:77
如何在耦合微分方程的求解器中更有效地使用特征?
How can I use traits more efficiently in a solver for coupled differential equations?
问:
一些背景
我正在编写一个(延迟)耦合微分方程组的求解器,即“积分方程”。类似于 Lorenz-System(无延迟反馈)或 Mackey-Glass 系统(具有延迟反馈),但(可能)有多个这样的系统,它们直接或在一定延迟后相互反馈。反馈是通过复杂的网络拓扑进行的。我开始使用特征来限制如何定义某个系统,以便它可以与我的其余部分代码一起使用(例如,使用 Runge-Kutta 方法)。一般来说,我有两种系统 - 有延迟和无延迟(对于这些系统,我有一些特殊情况,每种情况都想稍微优化集成过程)。现在我觉得我必须把所有东西都写两遍或更多遍,这是低效的,很难理解。
每个“动力系统”都有一个“状态”(想想坐标向量)和一个函数 f(state),它给出了状态的导数。把它想象成一个向量场,将箭头附加到空间中的每个点。 只是一组重要的参数,用于表征给定系统。
我想到的两个特质:Model
f
pub trait DynamicalSystem {
type StateT;
type ModelT; // the model parameters
fn f(state: &StateT, model: &ModelT) -> StateT;
}
pub trait DynamicalDelaySystem {
type StateT;
type ModelT;
type DelayT;
fn f(state: &StateT, model: &ModelT, delay: &DelayT) -> StateT;
fn keep_delay(state: &StateT) -> DelayT; // keep *something* for the feedback
}
如果我将其与欧拉方法集成,我需要定义两个集成:
euler(state: &mut StateT, model: &ModelT, dt: &f64, f: fn(&StateT, &ModelT) -> StateT) {
*state += f(&state, &model) * dt;
}
euler_delay(state: &mut StateT, model: &ModelT, delay: &DelayT, dt: &f64, f: fn(&StateT, &ModelT) -> StateT, keep_delay: fn(&StateT) -> DelayT) {
*state += f(&state, &model, &delay) * dt;
keep_delay(state); // some function that puts the delay into some container e.g. a ringbuffer for later use
}
我的想法是编写一个“积分器”结构,它保留了 's、's 等向量。但我需要做两次——一次是,一次是毫不拖延。StateT
ModelT
struct Integrator<SystemT> {
dt: f64,
states: Vec<SystemT::StateT>,
models: Vec<SystemT::ModelT>,
}
impl<SystemT> Integrator<SystemT> {
fn integration_step(&mut self) {
for (s, m) in &mut self.states.iter_mut().zip(self.models) {
euler_step(&mut s, &m, &dt);
}
struct DelayIntegrator<SystemT> {
// the same as `Integrator<SystemT>`
history: Vec<Vec<SystemT::DelayT>>,
}
impl<SystemT> DelayIntegrator<SystemT> {
// everything again, but retrieve delayed values and save new values after integration step.
}
这似乎有点多余。更糟糕的是:除了一般情况之外,我还有一些特殊情况需要解决(即优化):
- 只有一个动力系统 (states.len() == models.len() == 1)
- 具有相同参数的多个动力系统 (states.len() > 1 && models.len() == 1)
- 系统之间的反馈,但没有延迟,即我不需要一个复杂的容器来存储延迟值。
- 无(延迟-)反馈
我第一次尝试解决 1 和 2 是使用我在 Integrator 的 -method 中设置的函数指针。取决于系统设置的情况(系统数量、模型参数数量)。但是函数指针似乎是一个很慢的黑客。
integration_step
new
问题:
您在这里推荐哪些关于重构/更多“DRY”代码的一般建议?我不确定我是否了解特质的全部潜力。尤其是将多个特征组合在一起,而不是将所有特征都写成一个,这对我来说是很难想象的。我可以将其拆分为多个特征,例如 、 、 和 ?我似乎无法使用特征来强制结构成员变量的存在,例如延迟容器。DelayFeedback
InstantFeedback
NoFeedback
SingleSystem
MultipleIdenticalSystems
MultipleDistinctSystems
答:
在我看来,a 是 where 的特例。这种观察可能会让你消除这种特征。DynamicalSystem
DynamicalDelaySystem
DelayT = ()
DynamicalSystem
pub trait DynamicalSystem {
type StateT;
type ModelT;
type DelayT;
fn next(state: &Self::StateT, model: &Self::ModelT, delay: &Self::DelayT) -> Self::StateT;
fn keep_delay(state: &Self::StateT) -> Self::DelayT;
}
impl DynamicalSystem for usize {
type StateT = usize;
type ModelT = usize;
type DelayT = (); // there is no delay
fn next(state: &Self::StateT, model: &Self::ModelT, delay: &Self::DelayT) -> Self::StateT {
*state
}
fn keep_delay(state: &Self::StateT) -> Self::DelayT {
()
}
}
或者尝试实验性特征别名功能:
#![feature(trait_alias)]
pub trait DynamicalSystem = DynamicalDelaySystem<DelayT = ()>;
pub trait DynamicalDelaySystem {
type StateT;
type ModelT;
type DelayT;
fn next(state: &Self::StateT, model: &Self::ModelT, delay: &Self::DelayT) -> Self::StateT;
fn keep_delay(state: &Self::StateT) -> Self::DelayT;
}
另一种可能的方法是创建一个超级特征:DynamicalSystem
DynamicalDelaySystem
pub trait DynamicalSystem {
type StateT;
type ModelT;
fn next(state: &Self::StateT, model: &Self::ModelT) -> Self::StateT;
}
pub trait DynamicalDelaySystem: DynamicalSystem {
type DelayT;
fn next_with_delay(
state: &Self::StateT,
model: &Self::ModelT,
delay: &Self::DelayT,
) -> Self::StateT;
fn keep_delay(state: &Self::StateT) -> Self::DelayT;
}
最后,如果没有其他足够好的,您可以使用宏来为您完成代码复制工作。
评论
DynamicalSystem
DynamicalDelaySystem
null
DynamicalSystem
DelayT
()
评论