2015-05-20 資料

力学系のシミュレーションをするのに最低限必要なこと

  • 関数を定義すること
  • 関数を任意の回数繰り返し適用すること

関数定義

ポイント

  • def
  • 4つの空白
In [1]:
def f(x):
    return x ** 2

関数呼び出し

In [2]:
f(10)
Out[2]:
100
In [3]:
def g(x):
    return x * 2
In [6]:
# Integer -> Integer
g(10)
Out[6]:
20
In [5]:
# Float -> Float
g(2.0)
Out[5]:
4.0
In [7]:
# List -> List (期待通りではない)
g([1, 2, 3])
Out[7]:
[1, 2, 3, 1, 2, 3]

for ループ

リストに対する操作は forループを使う (numpyを使わないならば ... )

In [8]:
x = [1, 2, 3]
for i in x:
    print(i * 2)
2
4
6

リストを返すようにするにはこうする

In [11]:
y = []
for i in x:
    y.append(i * 2)
y
Out[11]:
[2, 4, 6]

もっとよい方法

In [12]:
[i * 2 for i in x]
Out[12]:
[2, 4, 6]

for は色々な局面で現れるのでマスターしておく。ただし数値計算をする場合には頼りすぎないこと

モジュール

自分で作った関数をファイルに保存しておく方法。IPythonから呼び出す場合は, IPython のカレントディレクトリをファイルがある場所に移動しておく。

In [14]:
# カレントディレクトリへのパスを表示
%pwd 
Out[14]:
'/Users/kenjisato/Dropbox/Lectures/2015/kobe-u/py/2015-05-20'
In [15]:
# カレントディレクトリのファイルをリストアップ
%ls 
Untitled.ipynb  __pycache__/    duck.py         klass.py        note.py
In [18]:
# ファイルの内容を表示
%cat note.py
# Generators

def logistic(x):
    """Logistic map"""
    return 4 * x * (1 - x)
    
def logistic_path(x, length):
    y = [x]
    for _ in range(length - 1):
        y.append(logistic(y[-1]))
    return y

def logistic_gen(x, length):
    for _ in range(length):
        x1 = logistic(x)
        yield x
        x = x1
        

    
In [19]:
# ファイルを実行
%run note.py
In [20]:
logistic(0.2)
Out[20]:
0.6400000000000001
In [21]:
logistic_path(0.2, 5)
Out[21]:
[0.2,
 0.6400000000000001,
 0.9215999999999999,
 0.28901376000000045,
 0.8219392261226504]
In [22]:
for x in logistic_gen(0.2, 5):
    print(x)
0.2
0.6400000000000001
0.9215999999999999
0.28901376000000045
0.8219392261226504

ジェネレータ

上で定義されている logistic_gen はジェネレータ関数と呼ばれるもの. 関数定義の中で return の代わりに yield を使っている. ループの中でyield を使うと, yield文が実行されるたびに関数の実行が一時停止する. デザイン上の理由でたくさんのデータを返す関数を作るよりも必要なデータをひとつずつ出力するほうが望ましい場合に使う

In [36]:
def pm_gen(maxnum):
    n = 0
    while n < maxnum:
        if n % 2 == 0:
            yield 1
        else:
            yield -1
        n += 1
In [37]:
list(pm_gen(10))
Out[37]:
[1, -1, 1, -1, 1, -1, 1, -1, 1, -1]

クラス

In [39]:
%cat klass.py
import numpy as np

class System:
    def __init__(self, dim):
        self.dim = dim
        
    def forward(self, x):
        pass
        
class Logistic(System):
    def __init__(self, a):
        self.a = a
        super().__init__(dim=1)
        # System.__init__(dim=1)
        
    def forward(self, x):
        return self.a * x * (1 - x)
    
class Tent(System):
    def __init__(self, a):
        self.a = a
        super().__init__(dim=1)
        
    def forward(self, x):
        return self.a * min(x, 1 - x)
    

class Linear(System):
    def __init__(self, A):
        self.A = A
        super().__init__(dim=A.shape[0])
    
    def forward(self, x):
        return np.dot(self.A, x)

class NoUse:
    def forward(self, x):
        return 2 * x     
        
def run(system, x, steps):
    for i in range(steps):
        x1 = system.forward(x)
        yield x
        x = x1
        
        
        
        
        
In [41]:
run klass
In [42]:
A = np.array([[0.9, 0.0], [0.0, -0.5]])
In [43]:
ct = Linear(A)
In [44]:
x = np.array([2, 3])
In [45]:
for y in run(ct, x, 10):
    print(y)
[2 3]
[ 1.8 -1.5]
[ 1.62  0.75]
[ 1.458 -0.375]
[ 1.3122  0.1875]
[ 1.18098 -0.09375]
[ 1.062882  0.046875]
[ 0.9565938 -0.0234375]
[ 0.86093442  0.01171875]
[ 0.77484098 -0.00585938]

ダックタイピング

特定のクラスのインスタンスに対して動作する関数を定義したとする. 他のクラスのインスタンスが, その関数が正常に動作するのに必要な性質を備えていれば, クラスが違っても関数が正常に実行される

In [46]:
nu = NoUse()
In [47]:
list(run(nu, 1, 10))
Out[47]:
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
In [ ]: