Matlab自动微分

3
如果你们中的某人能帮我或指引我方向,那就太好了。
我有下面这个公式,它有8个参数,每个受试者的值都不同。该方程描述了一种随时间t而变化的生长模式,其中y是生长量,m1m8是每个受试者不同的参数。
y = m1*(1-1/(1+(m2*(t+m8))^m5+(m3*(t+m8))^m6+(m4*(t+m8))^m7))

我想做的是插入这个公式并计算(数值上)速度曲线。 我希望从这些信息中获得以下内容:
- 最大速度及其对应的年龄和高度 - 达到最大速度之前的最小速度。
我只需要提供所有受试者及其参数的列表,然后就能收到我的结果,你能帮我吗?
我完全没有编程和数学背景,但我想能够做到这一点。 我有Matlab可用。 我正在尝试弄清楚事情的运作方式,但我无法理解它。 作为一名生物学家,明年我将参加编程课程 ;-)
我已经从Excel导出了一个制表符分隔的文件,并具有以下结构:
A列:受试者的ID
B到I列:不同参数m1 ... m8的值(如方程中所示)
每行是具有不同参数的不同受试者。
我选择了“导入选择”,然后选择“生成函数”(或者我应该选择“生成脚本”)。
如果我打开这个文件,我会看到一个新的编号选项卡。 第一行是一个函数[ID,m1,...,m8] = importfile。 然后是112行。
我复制了dan给出的文本(感谢dan),从第113行开始。
tmin = 0; tmax = 20; dt = 1/12; t = timn:dt:tmax; y = m1.(1-1./(1+(m2.(t+m8)).^m5+(m3.(t+m8)).^m6+(m4.(t+m8)).^m7)); dy = diff(y)./dt; max(dy); min(dy); imax = find(dy==max(dy))+1; imin = find(dy==min(dy))+1; t(imax); t(imin); y(imax); y(imin);
接下来我点击了“运行”,并被要求保存文件,我已经完成了。
现在我再次点击“运行”,但出现错误。
有人能指点我吗?
非常感谢。

3
方程在哪里? - m_power
编辑后的内容包括函数。 - user1719126
你的方程式中t在哪里? - Dan
那是我的错误。 - user1719126
3个回答

1
如果你的方程式是这样的形式:y = f(t,...,... ...),并且所有其他参数在每个受试者的所有时间内保持不变,那么你可以按照以下方式操作。
首先,确定一个时间范围(我选择了10年,但完全取决于你的工作)。
tmin = 0;
tmax = 10;

然后,您必须选择一个时间分辨率(我选择了每月一次,但您可能需要更细的分辨率):

dt = 1/12;

现在创建一个t向量并数值求解y:
t = timn:dt:tmax;
y = m1.*(1-1./(1+(m2.*(t+m8)).^m5+(m3.*(t+m8)).^m6+(m4.*(t+m8)).^m7));

一阶数值微分就是找到y中每两个相邻点之间的斜率。即 (y(i) - y(i - 1)) / (t(i) - t(i - 1)),注意 t(i) - t(i - 1) 总是等于 dt。因此在 matlab 中我们可以这样做:

dy = diff(y)./dt;

但现在dydt少一个元素,请记住这一点。
因此,要找到最大和最小速度:
max(dy);
min(dy);

并找到相应的时间和高度:
imax = find(dy==max(dy)) + 1;
imin = find(dy==min(dy)) + 1;

t(imax);
t(imin);
y(imax);
y(imin);

1
实际上,min和max的位置作为第二个输出。因此不需要使用find函数。 - bdecaf
我已经完成了以下工作(尽管还没有成功,但我希望我正在朝着正确的方向努力):
  1. 我将一个Excel文件保存为txt制表符分隔文件。其中A列是主题编号。B到I列是参数m1、...、m8的不同值。
  2. 我使用Matlab中的导入数据功能打开了这个文件,并获得了一个电子表格。
- user1719126
@user1719126 顺便说一下,既然我看到了你的方程式,我会将它添加到我的解决方案中。你会注意到有许多额外的“.”,这使得运算符逐个元素进行,对于这个应用程序非常必要。 - Dan
我应该把这个公式放在哪里?并且能否在相邻的列中获取最大速度和相应的时间? - user1719126
看看我的答案。我不是在炫耀,只是想让你了解数值微分的有限差分方法。 - Rody Oldenhuis
显示剩余2条评论

1

首先,这是一个可复制的解决方案。我的答案剩下的部分是解释 :)

(你需要从 FEX 下载 FindRealRoots 才能运行此程序)。

trange = [tstart tend] % <your range in t>

% define all intermediate functions
a = m2^m5;
b = m3^m6;
c = m4^m7;
X = @(t) t(:).' + m8;

g   = @(t) 1 + [a b c] * bsxfun(@power, X(t), [m5; m6; m7]);
gp  = @(t) ([m5 m6 m7].*[a b c]) * bsxfun(@power, X(t), [m5-1; m6-1; m7-1]);
gpp = @(t) (([m5 m6 m7]-1).*[m5 m6 m7].*[a b c]) * ...
           bsxfun(@power, X(t), [m5-1; m6-1; m7-1]);
sgn = @(t) m1*(2*(gp(t)).^2 - g(t).^2*gpp(t) );

y   = @(t) m1*(1 + 1./g(t));   
yp  = @(t) -m1*gp(t) ./ g(t).^2;

% Find the roots of the derivative
R = FindRealRoots(gp, tstart , tend, 2*ceil(max([m5 m6 m7])));

% values at end-points
y0   = y(tstart);
yend = y(tend);

% if we have some roots, separate minima/maxima and sort
if ~isempty(R)

    extrema = y(R);
    signs   = sgn(R);

    % Find maximum
    [maximum, Imax] = max(extrema(signs < 0));
    tmax = R(Imax);

    if isempty(tmax) % no maximum in interval; use endpoint
        [~,ind] = max([y0, yend]);
        tmax = trange(ind);
    end

    ymax = y(tmax);


    % Find minimum *before* maximum
    [minima, Imin] = extrema(signs > 0); 
    tmin = R(Imin);

    if isempty(tmin) % no minimum on interval; use endpoint
        [~,ind] = min([y0, yend]);
        tmin = trange(ind);
    end

    if any(tmin < tmax)
        % make sure to take only one minimum; the one 
        % just before the maximum
        tmin = tmin(find(tmin < tmax, 1, 'last')); 
    else
        [~,ind] = min([y0, yend]);  % no minima before the maximum; use end-point
        tmin = trange(ind);
    end

    ymin = y(tmin);


else % no roots found; just order the end-points

    [ymax,ind] = max([y0, yend]); 
    tmax = trange(ind);

    [ymin,ind] = min([y0, yend]); 
    tmin = trange(ind);

end

现在,解释一下。
虽然Dan的答案是一个不错的第一步,但我觉得我必须在一些地方进行更正。
首先,数值导数只在以下情况下按照Dan所描述的方式计算:
- 您有网格数据(在许多不同点t处的y(t)的值) - 已知底层模型函数y(t)不允许插值
而这在您的情况下并不正确。当您有一个可以在任意点评估的显式函数时,最好使用有限差分来计算数值导数,通常仅在无法通过分析方法找到显式导数时使用。
例如,在x=π/4处(使用h=0.001的中心差分)的cos(x)的数值导数:
  z'(π/4) ≅ ( z(x+h)-z(x-h) ) / 2h                      
          = ( cos(π/4 + 0.001) - cos(π/4 - 0.001) ) / 0.002
          = -0.7071066633353995...
-sin(π/4) = -0.7071067811865475...   (value of analytical derivative)

说了这些,我们转向更重要的一点;在您不得不求助于数值方法之前,可以将数学推进得比Dan更远。这样做可能会更加繁琐,但它将改善您对问题、MATLAB、数学的理解,并提高结果的准确性和稳健性。
对于您的函数 y = f(t),您声明有三个目标:
  1. 找到速度曲线
  2. 找到最大值 y(tmax) = ymax
  3. 找到发生在最大值 ymax 之前的最小值 ymintmin
当翻译成数学术语时:
  1. 求导数 y' = dy/dt
  2. (和3.) 找出所有 y' 的根 R,并在所有 R 处计算 yy'',还要在初始时间 t0 处计算 y

让我们从目标1开始。您有一个关于时间的显式函数 y = f(t),这基本上是有理函数的一种变形形式。

    y(t) = a + f(t)/g(t)  

为了简洁起见,我们可以省略(t)部分。这样写起来更容易,而且可以理解y、f和g都依赖于t。所以,

y = a + f/g

你有一个优势,即 f(t) = <constant> = a = m1,这在后面的事情中会更加方便。这种函数的导数表达式采用以下形式(即商规则):derivative
y' = (f'·g - f·g') / g²

由于 f(t) = m1 = <constant>,所以它对时间的导数为

y' = -m1·g' / g²

现在,我们需要找到g的导数。函数g是多项式: g = 1 + (m2·(t+m8))m5+ (m3·(t+m8))m6+ (m4·(t+m8))m7 让我们定义X = t + m8a = m2m5b = m3m6c = m4m7。这样,g看起来会少吓人很多: g = 1 + a·Xm5+ b·Xm6+ c·Xm7 Differentiation of polynomials follows the elementary power rule (and chain rule, but that's not really relevant here because X' = 1):

g' = m5·a·Xm5-1+ m6·b·Xm6-1+ m7·c·Xm7-1

因此,

y' = -m1·g' / g² = -m1 · (m5·a·Xm5-1+ m6·b·Xm6-1+ m7·c·Xm7-1) / (1 + a·Xm5+ b·Xm6+ c·Xm7

目标1已完成!您现在可以在任何地方使用完全准确的速度曲线。下面是如何在MATLAB中实现此函数:

a = m2^m5;
b = m3^m6;
c = m4^m7;
X = @(t) t + m8;
dydt = @(t) -m1*( [m5*a m6*b m7*c]*X(t).^[m5-1; m6-1; m7-1] ) ./ ...
            ( 1 + [a b c]*X(t).^[m5; m6; m7] ).^2;

现在是目标2:

  1. 找到y'=0的所有
  2. 在这些根上评估y
  3. 在这些根上评估y''
  4. t区间的端点上评估y

步骤3是必要的,以确定y'=0的根R是否描述了y最大值最小值。如果y''(R) < 0,则处理的是最大值,如果y'' > 0,则处理的是最小值,如果y''(R) = 0,则处理的是一个拐点,可以安全地忽略此问题。

第四步是必要的,因为在函数的最大值之前,可能存在没有最小值的情况,直到超过终点。在这种情况下,终点本身就是您应该使用的最小值。

现在,第一步。如果您观察这个方程的一般形状,

y' = -m1·g' / g² = 0

很明显,这只有在g' = 0的情况下才可能实现。因此,我们必须解决。
g' = 0  => 

m5·a·Xm5-1+ m6·b·Xm6-1+ m7·c·Xm7-1= 0

现在我们在数学上遇到了困难。这个方程有一个微不足道的解 (X=0(t=-m8))。然而,只有在特定参数m5-7的值下才存在一般解决方案。对于那些参数的所有其他值,不存在一般解决方案,您必须通过数值方法找到解决方案。

因此,我建议您使用:

对于第三步,您需要再次重复取导的过程;

   y'  = -m1·g' / g²
=> y'' = m1·( 2·(g')² - g²·g'' ) / (g²)²

在这里

g'' = (m5-1)·m5·a·Xm5-2+ (m6-1)·m6·b·Xm6-2+ (m7-1)·m7·c·Xm7-2。注意,您只需要知道它的符号,因此只需使用分子即可。

sgn(y'') = sgn( m1·( 2·(g')² - g²·g'' ) )

这是如何在MATLAB中实现的:

g   = @(t) 1 + [a b c]*X(t).^[m5; m6; m7];
gp  = @(t) ([m5 m6 m7].*[a b c]) * X(t).^[m5-1; m6-1; m7-1];
gpp = @(t) (([m5 m6 m7]-1).*[m5 m6 m7].*[a b c]) * X(t).^[m5-1; m6-1; m7-1];
sgn = @(t) m1*(2*(gp(t)).^2 - g(t).^2*gpp(t) );

然后,使用之前找到的所有根R,您只需评估所有内容:
extrema = y(R);
signs   = sgn(R);
y0      = y(t0);

现在,您已经获得了所有根,所有在根和端点处的y值以及二阶导数的所有符号....

目标2已完成!


我已经理解了其中的原因,但由于某些原因它无法工作,我无法弄清楚我的错误在哪里。我已将脚本粘贴到命令窗口中,并在工作区上传了我的文件,在那里我插入了所需的列:第一列是名称,第2至8列是参数。 或者我需要编写一个脚本并点击运行? - user1719126

1

由于您的问题比较笼统,我只能提供一些指引。

  • 对于公式,您可以将其存储在 [function][1][function handle][2] 中——还有一个符号数学工具箱可供使用(在这种情况下,您可以使用符号微分来查找极值)

  • 接下来基本上是循环(参见 forwhile)——这将取决于您如何将数据读入 Matlab。

  • 最后是找到极值(我想您不知道它们的确切时间)——查找 fmin 命令以进行数值最小化(用于寻找极小值——但对于极大值,只需使用 - 您的函数作为目标)——如果有多个,您可能需要稍微调整一下设置。

  • 别忘了一些输出。我认为 xlswrite 最方便。但帮助文件会指向其他替代品。


作为附注,您应该尝试手动进行区分,以检查期望的解决方案数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接