numpy float128没有给出正确的答案

时间:2019-08-30 03:56:31

标签: numpy floating-point precision ode solver

我用Python创建了一个微分方程求解器(Runge-Kutta 4阶方法)。然后,我决定通过将参数mu设置为0并查看它返回的数值解来检查其结果。

问题是,我知道此解决方案应能产生稳定的振荡,但我得到的是分散的解决方案。

下面显示了代码。我尝试通过使用numpy float128数据类型解决此问题(从浮点精度舍入错误)。但是求解器总是给我错误的答案。

代码是:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

def f(t,x,v):
  f = -k/m*x-mu/m*v
  return(f)

def g(t,x,v):
  g = v
  return(g)

def srunge4(t,x,v,dt):
  k1 = f(t,x,v)
  l1 = g(t,x,v)

  k2 = f(t+dt/2, x+k1*dt/2, v+l1*dt/2)
  l2 = g(t+dt/2, x+k1*dt/2, v+l1*dt/2)

  k3 = f(t+dt/2, x+k2*dt/2, v+l2*dt/2)
  l3 = g(t+dt/2, x+k2*dt/2, v+l2*dt/2)  

  k4 = f(t+dt/2, x+k3*dt, v+l3*dt)
  l4 = g(t+dt/2, x+k3*dt, v+l3*dt)

  v = v + dt/6*(k1+2*k2+2*k3+k4)
  x = x + dt/6*(l1+2*l2+2*l3+l4)
  t = t + dt
  return([t,x,v])

mu = np.float128(0.00); k = np.float128(0.1); m = np.float128(6)

x0 = np.float128(5); v0 = np.float128(-10)

t0 = np.float128(0); tf = np.float128(1000); dt = np.float128(0.05)

def sedol(t, x, v, tf, dt):
  sol = np.array([[t,x,v]], dtype='float128')
  while sol[-1][0]<=tf:
    t,x,v = srunge4(t,x,v,dt)
    sol=np.append(sol,np.float128([[t,x,v]]),axis=0)
  sol = pd.DataFrame(data=sol, columns=['t','x','v'])
  return(sol)

ft_runge = sedol(t0, x0, v0, tf, dt=0.1)

plt.close("all")
graf1 = plt.plot(ft_runge.iloc[:,0],ft_runge.iloc[:,1],'b')
plt.show()

我是否以错误的方式使用了numpy float128?

1 个答案:

答案 0 :(得分:1)

您在srunge4中将klxv的关联混合在一起。根据函数关联和最终求和,关联应为(v,f,k)(x,g,l)。在该方法的各个阶段的更新中必须遵守这一点。


在阶段4中,第一个参数应为t+dt。但是,由于在导数计算中未使用t,因此此错误在这里没有后果。


此外,如果在dt=0.1中以默认float64类型为浮点数设置一个参数,则会破坏float128计算。


经过这些更正和一些简化的代码是

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

mu = np.float128(0.00); k = np.float128(0.1); m = np.float128(6)
x0 = np.float128(5); v0 = np.float128(-10)
t0 = np.float128(0); tf = np.float128(1000); dt = np.float128(0.05)

def f(t,x,v): return -(k*x+mu*v)/m
def g(t,x,v): return v

def srunge4(t,x,v,dt): # should be skutta4, Wilhelm Kutta gave this method in 1901
  k1, l1 = (fg(t,x,v) for fg in (f,g)) 
  # here is the essential corrections, x->l, v->k
  k2, l2 = (fg(t+dt/2, x+l1*dt/2, v+k1*dt/2) for fg in (f,g)) 
  k3, l3 = (fg(t+dt/2, x+l2*dt/2, v+k2*dt/2) for fg in (f,g))
  k4, l4 = (fg(t+dt  , x+l3*dt  , v+k3*dt  ) for fg in (f,g))

  v = v + dt/6*(k1+2*k2+2*k3+k4)
  x = x + dt/6*(l1+2*l2+2*l3+l4)
  t = t + dt
  return([t,x,v])

def sedol(t, x, v, tf, dt):
  sol = [[t,x,v]]
  while t<=tf:
    t,x,v = srunge4(t,x,v,dt)
    sol.append([t,x,v])
  sol = pd.DataFrame(data=np.asarray(sol) , columns=['t','x','v'])
  return(sol)

ft_runge = sedol(t0, x0, v0, tf, dt=2*dt)

plt.close("all")
fig,ax = plt.subplots(1,3)
ft_runge.plot(x='t', y='x', ax=ax[0])
ft_runge.plot(x='t', y='v', ax=ax[1])
ft_runge.plot.scatter(x='x', y='v', s=1, ax=ax[2])
plt.show()

它产生预期的椭圆,而幅度没有视觉上可识别的变化。