如何在耦合微分方程的求解器中更有效地使用特征?

How can I use traits more efficiently in a solver for coupled differential equations?

提问人:exocortex 提问时间:11/4/2022 最后编辑:katnissexocortex 更新时间:11/10/2022 访问量:77

问:

一些背景

我正在编写一个(延迟)耦合微分方程组的求解器,即“积分方程”。类似于 Lorenz-System(无延迟反馈)或 Mackey-Glass 系统(具有延迟反馈),但(可能)有多个这样的系统,它们直接或在一定延迟后相互反馈。反馈是通过复杂的网络拓扑进行的。我开始使用特征来限制如何定义某个系统,以便它可以与我的其余部分代码一起使用(例如,使用 Runge-Kutta 方法)。一般来说,我有两种系统 - 有延迟和无延迟(对于这些系统,我有一些特殊情况,每种情况都想稍微优化集成过程)。现在我觉得我必须把所有东西都写两遍或更多遍,这是低效的,很难理解。 每个“动力系统”都有一个“状态”(想想坐标向量)和一个函数 f(state),它给出了状态的导数。把它想象成一个向量场,将箭头附加到空间中的每个点。 只是一组重要的参数,用于表征给定系统。 我想到的两个特质:Modelf

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 等向量。但我需要做两次——一次是,一次是毫不拖延。StateTModelT

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.
}

这似乎有点多余。更糟糕的是:除了一般情况之外,我还有一些特殊情况需要解决(即优化):

  1. 只有一个动力系统 (states.len() == models.len() == 1)
  2. 具有相同参数的多个动力系统 (states.len() > 1 && models.len() == 1)
  3. 系统之间的反馈,但没有延迟,即我不需要一个复杂的容器来存储延迟值。
  4. 无(延迟-)反馈 我第一次尝试解决 1 和 2 是使用我在 Integrator 的 -method 中设置的函数指针。取决于系统设置的情况(系统数量、模型参数数量)。但是函数指针似乎是一个很慢的黑客。integration_stepnew

问题

您在这里推荐哪些关于重构/更多“DRY”代码的一般建议?我不确定我是否了解特质的全部潜力。尤其是将多个特征组合在一起,而不是将所有特征都写成一个,这对我来说是很难想象的。我可以将其拆分为多个特征,例如 、 、 和 ?我似乎无法使用特征来强制结构成员变量的存在,例如延迟容器。DelayFeedbackInstantFeedbackNoFeedbackSingleSystemMultipleIdenticalSystemsMultipleDistinctSystems

蚀性状 微分方程

评论


答:

2赞 hkBst 11/5/2022 #1

在我看来,a 是 where 的特例。这种观察可能会让你消除这种特征。DynamicalSystemDynamicalDelaySystemDelayT = ()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;
}

另一种可能的方法是创建一个超级特征:DynamicalSystemDynamicalDelaySystem

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;
}

最后,如果没有其他足够好的,您可以使用宏来为您完成代码复制工作。

评论

0赞 exocortex 11/6/2022
这是一个非常酷的解决方案!(第二个)。这感觉与C++的多态性非常相似(如果我没记错的话)。我也会研究第一个解决方案。老实说,我不完全理解。经常在这里提问,可以很好地了解下一步的搜索方向。像这里一样!谢谢。
0赞 hkBst 11/8/2022
@exocortex,第一个解决方案基本上说 a 是一个带有 /empty/missing 延迟信息。DynamicalSystemDynamicalDelaySystemnull
0赞 exocortex 11/9/2022
嗯,我试过了,但我想我不知何故误解了它。你的意思是将实现定义为?DynamicalSystemDelayT()
1赞 hkBst 11/10/2022
@exocortex,我扩展了我的答案,使这个选项更清晰。如果有帮助,请告诉我。