Pythonプログラミング(ステップ7・配列・線形代数・二次曲面)

このページでは、NumPyライブラリの線形演算機能を用いる例として、二次曲面とその描画について考えてみる。

球面の描画

三次元座標を (x,y,z) で表したとき、原点を中心とした半径 r の球を表す方程式が (1)x2+y2+z2=r2 と書けることは、すでに高校までに学んだはずである。 メルカトル図法の世界地図をながめるとわかるように、球面上の点は、「北極」と「南極」を除けば、平面(矩形領域)に対応させることができる。 そこで、z軸を南北方向に取ることにして、 北極から測った角度(緯度)をθで、経度をϕで表してみる(極座標)。 すると、球面上の点の座標は (2)x(θ,ϕ)=rsinθcosϕy(θ,ϕ)=rsinθsinϕz(θ,ϕ)=rcosθ となる。

緯度(θ)と経度(ϕ)を、三次元座標に対応付ける方法。x,y,z座標のそれぞれについて、(θ,ϕ)の各点の値を表(配列)によって与える。

matplotlibライブラリのplot_surface()関数は、 このように、メルカトル図法的に座標点を与えることで、三次元内の曲面を描画することができる。

# coding: utf-8
from mpl_toolkits.mplot3d import Axes3D

import matplotlib.pyplot as plt
import numpy as np
import math

ndiv = 100
phi = np.linspace(0, 2 * math.pi, ndiv)
theta = np.linspace(0, math.pi, ndiv)

r = 1
x = np.empty((ndiv,ndiv),dtype='float32')
y = np.empty((ndiv,ndiv),dtype='float32')
z = np.empty((ndiv,ndiv),dtype='float32')

for i in range(ndiv):
    t = theta[i]
    for j in range(ndiv):
        p = phi[j]
        x[i,j] = r * math.sin(t) * math.cos(p)
        y[i,j] = r * math.sin(t) * math.sin(p)
        z[i,j] = r * math.cos(t)

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x, y, z, color='red', antialiased=True, shade=True)
plt.show()

二次曲面の描画

以上を踏まえて、さらに一般的に (3)ax2+by2+cz2+2dxy+2eyz+2fzx=C のような式で表される曲面を描く方法について考えてみよう。

一般の二次曲面は上式にx,y,zの一次の項も加えた表式となるが、簡単のためここでは考えない。

上式は (4)(xyz)(adedbfefc)(xyz)=C とも書けるので、行列 AA=(adedbfefc) ベクトル xx=(xyz) と定義し直せば (5)xAx=C と、すっきりまとめることができる。

ここで、線形代数の知識を思い出すと、 Aは対称行列であるから、対角化が可能で、固有値は全て実数である。 すなわち、対角化行列をPと置けば (6)P1AP=(λ1000λ2000λ3) とできる。 またこのときPは直交行列(P1=P)となる。 そこで、 (7)x=Pu のように変換した座標 (8)u=(uvw) を用いると、 (9)xAx=(Pu)A(Pu)=uPAPu=u(λ1000λ2000λ3)u すなわち、 (10)λ1u2+λ2v2+λ3w2=C となる。

固有値が全て正の場合:楕円体の描画

固有値が全て正の場合は、 変数をスケール変換して (11)u=λ1u,v=λ2v,w=λ3w とすれば、 (12)u2+v2+w2=C のように、「球」に帰着できる。

以上を踏まえると、Aの全ての固有値が正の場合の二次曲面を描画するには、以下の手順で、球の描画を少し改変すれば良い:

  1. Aの固有値と固有ベクトルを求め、Pを得る
  2. 半径C球面の座標 (u,v,w)を生成する。
  3. それぞれの座標軸を固有値の平方根倍だけスケールして、u=(u,v,w) を得る。
  4. x=Pu で得られたデータ点について(球と同様に)描画する。
# coding: utf-8
from mpl_toolkits.mplot3d import Axes3D

import matplotlib.pyplot as plt
import numpy as np
import math

ndiv = 30
phi = np.linspace(0, 2 * math.pi, ndiv)
theta = np.linspace(0, math.pi, ndiv)


u = np.empty((ndiv,ndiv),dtype='float32')
v = np.empty((ndiv,ndiv),dtype='float32')
w = np.empty((ndiv,ndiv),dtype='float32')

x = np.empty((ndiv,ndiv),dtype='float32')
y = np.empty((ndiv,ndiv),dtype='float32')
z = np.empty((ndiv,ndiv),dtype='float32')

A = np.array([
        [   1, 0.5,-0.5],
        [ 0.5, 0.7, 0.2],
        [-0.5, 0.2, 1.3]
        ], dtype='float32') 
C = 1

eig,P = np.linalg.eig(A)

print('eigen values=',eig)
print('P=\n',P)

for i in range(ndiv):
    t = theta[i]
    for j in range(ndiv):
        p = phi[j]
        u[i,j] = math.sqrt(C/eig[0]) * math.sin(t) * math.cos(p)
        v[i,j] = math.sqrt(C/eig[1]) * math.sin(t) * math.sin(p)
        w[i,j] = math.sqrt(C/eig[2]) * math.cos(t)

for i in range(ndiv):
    for j in range(ndiv):
        x[i,j] = P[0,0] * u[i,j] + P[0,1] * v[i,j] + P[0,2] * w[i,j]
        y[i,j] = P[1,0] * u[i,j] + P[1,1] * v[i,j] + P[1,2] * w[i,j]
        z[i,j] = P[2,0] * u[i,j] + P[2,1] * v[i,j] + P[2,2] * w[i,j]

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.view_init(elev=45, azim=45)
ax.plot_surface(x, y, z, color='red', antialiased=True, shade=True)
plt.show()

上のコードの出力結果の例を以下に示す。

ax.view_init(elev=45, azim=45)

の値(45)を変えることで、視点の向きを調整することができる。

双曲面の描画

C>0 で、固有値のうち2つが正で一つが負の場合を考える: x2+y2z2=C この式で与えられる曲面は(一葉)双曲面と呼ばれている。

z軸に沿った「高さ」をξ、「経度」をϕとおいて、二次元の矩形領域と三次元座標との対応関係を x(ξ,ϕ)=C+ξ2cosϕy(ξ,ϕ)=C+ξ2sinϕz(ξ,ϕ)=ξ で与え、 双曲面を描画するコードの例を以下に示す:

# coding: utf-8
from mpl_toolkits.mplot3d import Axes3D

import matplotlib.pyplot as plt
import numpy as np
import math

ndiv = 50
phi = np.linspace(0, 2 * math.pi, ndiv)
xi = np.linspace(-2, 2, ndiv)

x = np.empty((ndiv,ndiv),dtype='float32')
y = np.empty((ndiv,ndiv),dtype='float32')
z = np.empty((ndiv,ndiv),dtype='float32')

C = 1

for i in range(ndiv):
    t = xi[i]
    for j in range(ndiv):
        p = phi[j]
        r = math.sqrt(C + t**2)
        x[i,j] = r * math.cos(p)
        y[i,j] = r * math.sin(p)
        z[i,j] = t

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.view_init(elev=30, azim=45)
ax.plot_surface(x, y, z, color='red', linewidth=1, antialiased=True, shade=True)
plt.show()

icon-pc 練習:二葉双曲面

C<0で、1つの固有値が負、2つが正の場合 x2+y2z2=C について、曲面(の片側のみで良い)を描画するコードを作成してみなさい。この曲面は二葉双曲面と呼ばれている。

icon-pc 発展練習:二次曲面

三次元中の二次曲面のうち、 ax2+by2+cz2+2dxy+2eyz+2fzx=C で表されるものを描画する(a,b,c,d,e,fを入力すると、対応する図形が表示できる)Pythonコードを作成してみなさい

icon-hint ヒント

C>0の場合、全ての固有値が正、2つが正で一つが負、1つが正で2つが負、の3通りの曲面の可能性がある。 C<0の場合は、式全体の符号を反転させれば、C>0の場合に帰着される。 (x,y,z)(u,v,w)の対応関係を、固有値の大きさの順で統一することで、場合分けの回数が減らせる。