SciPy 庫創建在 Numpy 庫之上,提供了大量科學算法,主要包括這些主題:php
在本實驗中咱們將瞭解其中一些包的使用方法。html
(ps:因本節只講工具的用法,對這些科學主題不展開討論,因此根據本身所學的知識挑選食用就行了,強迫症不要糾結哈~)python
無需密碼自動登陸,系統用戶名shiyanlounginx
本課程實驗環境使用Spyder。首先打開terminal,而後輸入如下命令:算法
spyder -w scientific-python-lectures
關於Spyder的使用可參考文檔:https://pythonhosted.org/spyder/windows
本實驗基本在控制檯下進行,可關閉其他窗口,只保留控制檯。如須要調出窗口,能夠經過 view->windows and toolbar 調出。好比但願在py文件中編寫代碼,能夠 view->windows and toolbar->Editor 調出編輯器窗口。數組
讓咱們先導入必要的庫ruby
from numpy import * from scipy import *
在計算科學問題時,經常會用到不少特定的函數,SciPy 提供了一個很是普遍的特定函數集合。函數列表可參考:http://docs.scipy.org/doc/scipy/reference/special.html#module-scipy.specialbash
爲了演示特定函數的通常用法咱們拿貝塞爾函數舉例:編輯器
# # The scipy.special module includes a large number of Bessel-functions # Here we will use the functions jn and yn, which are the Bessel functions # of the first and second kind and real-valued order. We also include the # function jn_zeros and yn_zeros that gives the zeroes of the functions jn # and yn. # %matplotlib qt from scipy.special import jn, yn, jn_zeros, yn_zeros import matplotlib.pyplot as plt n = 0 # order x = 0.0 # Bessel function of first kind print "J_%d(%f) = %f" % (n, x, jn(n, x)) x = 1.0 # Bessel function of second kind print "Y_%d(%f) = %f" % (n, x, yn(n, x)) => J_0(0.000000) = 1.000000 Y_0(1.000000) = 0.088257 x = linspace(0, 10, 100) fig, ax = plt.subplots() for n in range(4): ax.plot(x, jn(n, x), label=r"$J_%d(x)$" % n) ax.legend(); fig
# zeros of Bessel functions n = 0 # order m = 4 # number of roots to compute jn_zeros(n, m) => array([ 2.40482556, 5.52007811, 8.65372791, 11.79153444])
被稱做 數值求積,Scipy提供了一些列不一樣類型的求積函數,像是
quad
, dblquad
還有 tplquad
分別對應單積分,雙重積分,三重積分。
from scipy.integrate import quad, dblquad, tplquad
quad
函數有許多參數選項來調整該函數的行爲(詳情見help(quad)
)。
通常用法以下:
# define a simple function for the integrand def f(x): return x x_lower = 0 # the lower limit of x x_upper = 1 # the upper limit of x val, abserr = quad(f, x_lower, x_upper) print "integral value =", val, ", absolute error =", abserr => integral value = 0.5 , absolute error = 5.55111512313e-15
若是咱們須要傳遞額外的參數,能夠使用 args
關鍵字:
def integrand(x, n): """ Bessel function of first kind and order n. """ return jn(n, x) x_lower = 0 # the lower limit of x x_upper = 10 # the upper limit of x val, abserr = quad(integrand, x_lower, x_upper, args=(3,)) print val, abserr => 0.736675137081 9.38925687719e-13
對於簡單的函數咱們能夠直接使用匿名函數:
val, abserr = quad(lambda x: exp(-x ** 2), -Inf, Inf) print "numerical =", val, abserr analytical = sqrt(pi) print "analytical =", analytical => numerical = 1.77245385091 1.42026367809e-08 analytical = 1.77245385091
如例子所示,'Inf' 與 '-Inf' 能夠表示數值極限。
高階積分用法相似:
def integrand(x, y): return exp(-x**2-y**2) x_lower = 0 x_upper = 10 y_lower = 0 y_upper = 10 val, abserr = dblquad(integrand, x_lower, x_upper, lambda x : y_lower, lambda x: y_upper) print val, abserr => 0.785398163397 1.63822994214e-13
注意到咱們爲y積分的邊界傳參的方式,這樣寫是由於y多是關於x的函數。
SciPy 提供了兩種方式來求解常微分方程:基於函數 odeint
的API與基於 ode
類的面相對象的API。一般 odeint
更好上手一些,而 ode
類更靈活一些。
這裏咱們將使用 odeint
函數,首先讓咱們載入它:
from scipy.integrate import odeint, ode
常微分方程組的標準形式以下:
當
爲了求解常微分方程咱們須要知道方程 與初始條件
注意到高階常微分方程經常寫成引入新的變量做爲中間導數的形式。 一旦咱們定義了函數
f
與數組y_0
咱們能夠使用 odeint
函數:
y_t = odeint(f, y_0, t)
咱們將會在下面的例子中看到 Python 代碼是如何實現 f
與 y_0
。
讓咱們思考一個物理學上的例子:雙擺
關於雙擺,參考:http://en.wikipedia.org/wiki/Double_pendulum
Image(url='http://upload.wikimedia.org/wikipedia/commons/c/c9/Double-compound-pendulum-dimensioned.svg')
維基上已給出雙擺的運動方程:
爲了使 Python 代碼更容易實現,讓咱們介紹新的變量名與向量表示法:
g = 9.82 L = 0.5 m = 0.1 def dx(x, t): """ The right-hand side of the pendulum ODE """ x1, x2, x3, x4 = x[0], x[1], x[2], x[3] dx1 = 6.0/(m*L**2) * (2 * x3 - 3 * cos(x1-x2) * x4)/(16 - 9 * cos(x1-x2)**2) dx2 = 6.0/(m*L**2) * (8 * x4 - 3 * cos(x1-x2) * x3)/(16 - 9 * cos(x1-x2)**2) dx3 = -0.5 * m * L**2 * ( dx1 * dx2 * sin(x1-x2) + 3 * (g/L) * sin(x1)) dx4 = -0.5 * m * L**2 * (-dx1 * dx2 * sin(x1-x2) + (g/L) * sin(x2)) return [dx1, dx2, dx3, dx4] # choose an initial state x0 = [pi/4, pi/2, 0, 0] # time coodinate to solve the ODE for: from 0 to 10 seconds t = linspace(0, 10, 250) # solve the ODE problem x = odeint(dx, x0, t) # plot the angles as a function of time fig, axes = plt.subplots(1,2, figsize=(12,4)) axes[0].plot(t, x[:, 0], 'r', label="theta1") axes[0].plot(t, x[:, 1], 'b', label="theta2") x1 = + L * sin(x[:, 0]) y1 = - L * cos(x[:, 0]) x2 = x1 + L * sin(x[:, 1]) y2 = y1 - L * cos(x[:, 1]) axes[1].plot(x1, y1, 'r', label="pendulum1") axes[1].plot(x2, y2, 'b', label="pendulum2") axes[1].set_ylim([-1, 0]) axes[1].set_xlim([1, -1]); fig
咱們將在第四節課看到如何作出更好的演示動畫。
from IPython.display import clear_output
import time fig, ax = plt.subplots(figsize=(4,4)) for t_idx, tt in enumerate(t[:200]): x1 = + L * sin(x[t_idx, 0]) y1 = - L * cos(x[t_idx, 0]) x2 = x1 + L * sin(x[t_idx, 1]) y2 = y1 - L * cos(x[t_idx, 1]) ax.cla() ax.plot([0, x1], [0, y1], 'r.-') ax.plot([x1, x2], [y1, y2], 'b.-') ax.set_ylim([-1.5, 0.5]) ax.set_xlim([1, -1]) display(fig) clear_output() time.sleep(0.1) fig
常微分方程問題在計算物理學中很是重要,因此咱們接下來要看另外一個例子:阻尼諧震子。wiki地址:http://en.wikipedia.org/wiki/Damping
阻尼震子的運動公式:
其中 是震子的位置,
是頻率,
是阻尼係數. 爲了寫二階標準行事的 ODE 咱們引入變量:
:
在這個例子的實現中,咱們會加上額外的參數到 RHS 方程中:
def dy(y, t, zeta, w0): """ The right-hand side of the damped oscillator ODE """ x, p = y[0], y[1] dx = p dp = -2 * zeta * w0 * p - w0**2 * x return [dx, dp] # initial state: y0 = [1.0, 0.0] # time coodinate to solve the ODE for t = linspace(0, 10, 1000) w0 = 2*pi*1.0 # solve the ODE problem for three different values of the damping ratio y1 = odeint(dy, y0, t, args=(0.0, w0)) # undamped y2 = odeint(dy, y0, t, args=(0.2, w0)) # under damped y3 = odeint(dy, y0, t, args=(1.0, w0)) # critial damping y4 = odeint(dy, y0, t, args=(5.0, w0)) # over damped fig, ax = plt.subplots() ax.plot(t, y1[:,0], 'k', label="undamped", linewidth=0.25) ax.plot(t, y2[:,0], 'r', label="under damped") ax.plot(t, y3[:,0], 'b', label=r"critical damping") ax.plot(t, y4[:,0], 'g', label="over damped") ax.legend(); fig
傅立葉變換是計算物理學所用到的通用工具之一。Scipy 提供了使用 NetLib FFTPACK 庫的接口,它是用FORTRAN寫的。Scipy 還另外提供了不少便捷的函數。不過大體上接口都與 NetLib 的接口差很少。
讓咱們加載它:
from scipy.fftpack import *
下面演示快速傅立葉變換,例子使用上節阻尼諧震子的例子:
N = len(t)
dt = t[1]-t[0] # calculate the fast fourier transform # y2 is the solution to the under-damped oscillator from the previous section F = fft(y2[:,0]) # calculate the frequencies for the components in F w = fftfreq(N, dt) fig, ax = plt.subplots(figsize=(9,3)) ax.plot(w, abs(F)); fig
既然信號是實數,同時頻譜是對稱的。那麼咱們只須要畫出正頻率所對應部分的圖:
indices = where(w > 0) # select only indices for elements that corresponds to positive frequencies w_pos = w[indices] F_pos = F[indices] fig, ax = subplots(figsize=(9,3)) ax.plot(w_pos, abs(F_pos)) ax.set_xlim(0, 5); fig
正如預期的那樣,咱們能夠看到頻譜的峯值在1處。1就是咱們在上節例子中所選的頻率。