pythonnoob
@pythonnoob (noob python)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

アニメーションの実装方法について教えてください。

解決したいこと

モデルのアニメーションの実装方法がわからないためご教授いただけると幸いです。
目標としては前回の質問と同じ単位円上を動く振動子のアニメーションです。

発生している問題・エラー

TypeError: unsupported operand type(s) for +: 'PathCollection' and 'PathCollection'

該当するソースコード

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
fig, ax = plt.subplots()
N = 2
K = 5
w1 = np.random.standard_cauchy()
w2 = np.random.standard_cauchy()
T=50
n = 5000
h = T/n
t = np.arange(0,T,h)

f1 = lambda theta1,theta2,t=0 : w1-K*np.sin(theta1-theta2)/N
f2 = lambda theta1,theta2,t=0 : w2-K*np.sin(theta2-theta1)/N


theta1= np.empty(n)
theta2= np.empty(n)
theta1[0]=np.pi/2
theta2[0]=3*np.pi/2

ims = []
for i in range(n-1):
    k1 = h * f1(theta1[i],theta2[i],t[i])
    l1 = h * f2(theta1[i],theta2[i],t[i])
    k2 = h * f1(theta1[i] + k1 /2 , theta2[i]+l1/2,t[i] + h/2 )
    l2 = h * f2(theta1[i]+k1/2, theta2[i]+ l1 /2 ,t[i] + h/2 )
    k3 = h * f1(theta1[i] + k2 /2 , theta2[i]+l2/2,t[i] + h/2 )
    l3 = h * f2(theta1[i]+k2/2, theta2[i]+ l2 /2 ,t[i] + h/2 )
    k4 = h * f1(theta1[i] + k3 , theta2[i]+l3, t[i] + h )
    l4 = h * f2(theta1[i]+k3, theta2[i]+ l3 , t[i] + h )

    theta1[i+1] = theta1[i] + 1/6 * (k1 + 2*k2 + 2*k3 + k4 )
    theta2[i+1] = theta2[i] + 1/6 * (l1 + 2*l2 + 2*l3 + l4 )

    x1=np.cos(theta1)
    y1=np.sin(theta1)
    x2=np.cos(theta2)
    y2=np.sin(theta2)

    im1 = ax.scatter(x1, y1)
    im2 = ax.scatter(x2,y2)
    ims.append(im1+im2)

anim = animation.ArtistAnimation(fig, ims,interval=100)
plt.show()

自分で試したこと

先日、こちらの質問箱で回答をいただき、自分でもコードを書けるようになろうと蔵本モデルをルンゲクッタにて書き表してみました。(N=2からスタートしています)
なんとか実装までは行けた気がするのですがこれをアニメーションにしようとしたところ壁にぶつかりました。
もし、アニメーションの実装に関しておかしな点があればご教授ください。

0

2Answer

im1im2は加算によって図を重ね合わせるような処理はしてくれません。

対応策としては、x1x2のように振動子ごとに描画するのではなく、一度に描画してしまえばよいです。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation

N = 2
K = 5
w1 = np.random.standard_cauchy()
w2 = np.random.standard_cauchy()
T = 50
n = 500
h = T / n
ts = np.arange(0, T, h)

f1 = lambda theta1,theta2,t : w1-K*np.sin(theta1-theta2)/N
f2 = lambda theta1,theta2,t : w2-K*np.sin(theta2-theta1)/N

theta1 = np.pi / 2
theta2 = 3 * np.pi / 2

fig, ax = plt.subplots()
ax.set_aspect("equal")

ims = []
for t in ts:
	# 描画
	x = np.cos([theta1, theta2])
	y = np.sin([theta1, theta2])

	im = ax.scatter(x, y, facecolor="None", edgecolors="r")
	ims.append([im]) # 注意:リストで渡す必要があります。

	# 更新
	k1 = h * f1(theta1, theta2, t)
	l1 = h * f2(theta1, theta2, t)
	k2 = h * f1(theta1 + k1 / 2, theta2 + l1 / 2, t + h / 2)
	l2 = h * f2(theta1 + k1 / 2, theta2 + l1 / 2, t + h / 2)
	k3 = h * f1(theta1 + k2 / 2, theta2 + l2 / 2, t + h / 2)
	l3 = h * f2(theta1 + k2 / 2, theta2 + l2 / 2, t + h / 2)
	k4 = h * f1(theta1 + k3, theta2 + l3, t + h)
	l4 = h * f2(theta1 + k3, theta2 + l3, t + h)

	theta1 += 1/6 * (k1 + 2 * k2 + 2 * k3 + k4 )
	theta2 += 1/6 * (l1 + 2 * l2 + 2 * l3 + l4 )

anim = animation.ArtistAnimation(fig, ims, interval=100)
plt.show()

また、アドバイスとしてnumpyのブロードキャストという機能を理解すると、今後Nが増えていっても対応できると思います。現在は、ルンゲクッタに必要な関数f1,f2,...がNと同じ数だけ必要ですが、ブロードキャスト機能をうまく利用すれば、この関数の定義は一つだけで済ますことができます。

1Like

Comments

  1. @pythonnoob

    Questioner

    こんにちは。
    ご回答いただきありがとうございます。
    なるほど、自分で思い描く処理通りにいかず勉強の毎日です...
    theta全体をまとめて1つのリストに入れているような感覚でよろしいですかね??
    前回の質問の際も丁寧にご回答いただき誠に感謝申し上げます。
    ブロードキャストについても勉強してみます!
  2. @pythonnoob

    Questioner

    続けての質問ですみません。
    ブロードキャストについてなのですが、ルンゲクッタに必要な関数fを1つ決めれば全振動子(i=1~N)に適応することのできるという解釈でよろしいでしょうか??
    コードを組んでみようかと思ったのですが自分では全く歯が立ちませんでした...

@pythonnoob さん
返答遅くなり申し訳ありません。
実装例を以下に載せます。
Nの値を変えるだけで振動子の数を制御できます。
この実装を見て、numpyのブロードキャスト機能の素晴らしさを理解していただけたら幸いです。

余談ですが、numpy.random.standard_cauchyを使用した結果において、振動子の挙動がおかしいような気がします。
自作関数のコーシー分布を使用した場合は、「参考」にあるサイトで見た挙動と同じようになるのですが、numpy.random.standard_cauchyを使用し続けて大丈夫ですか?

  • numpy.random.standard_cauchy

animation_numpy.gif

  • 自作関数rand_cauchy

animation_selfmade.gif

参考:蔵本モデルの高速計算

  • 実装例
import math
import random
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation

# コーシー分布
def rand_cauchy(mu, gamma) :
	arg = math.pi * random.uniform(-0.5, 0.5)
	return mu + gamma * math.tan(arg)

# 微分方程式の右辺
def func(t, theta, **params):
	K = params.get("K", 0.0)
	omega = params.get("omega", np.zeros_like(theta))
	Ti, Tj = np.meshgrid(theta, theta)
	return omega + K * np.average(np.sin(Tj - Ti), axis=0)

# 4次ルンゲクッタ
def rk4(dt, t, Xs, func, **params):
	k1 = dt * func(t, Xs, **params)
	k2 = dt * func(t + dt / 2, Xs + k1 / 2, **params)
	k3 = dt * func(t + dt / 2, Xs + k2 / 2, **params)
	k4 = dt * func(t + dt, Xs + k3, **params)
	# 更新
	new_t = t + dt
	newXs = Xs + (k1 + 2 * k2 + 2 * k3 + k4) / 6
	return new_t, newXs

# パラメータ
N = 100
K = 10
T = 5
dt = 0.01
ts = np.arange(0, T + dt, dt)

# 初期値
# omega = np.array([np.random.standard_cauchy() for _ in range(N)])
omega = np.array([rand_cauchy(4, 1) for _ in range(N)])
theta = np.array([np.random.uniform(0.0, 2 * np.pi) for _ in range(N)])

# 計算の実行
fig, ax = plt.subplots()
ax.set_aspect("equal")

ims = []
for t in ts:
	# 描画
	x = np.cos(theta)
	y = np.sin(theta)

	im = ax.scatter(x, y, facecolor="None", edgecolors="r")
	ims.append([im]) # 注意:リストで渡す必要があります。

	# 更新
	_, theta = rk4(dt, t, theta, func, K=K, omega=omega)

ani = animation.ArtistAnimation(fig, ims, interval=100)
# ani.save("./animation.gif")
plt.show()
0Like

Comments

  1. @pythonnoob

    Questioner

    こんばんは。
    毎度丁寧に答えてくださるので、大変勉学の支えになっております。
    ブロードキャスト機能を使うとこんなにも見やすい実装になるのですね...
    コーシー分布についても私自身違和感がありました。関係なさそうなところで大きく影響してしまっているのですね...
    また、標準コーシー分布を表現したい場合には mu を0に設定すればいいという理解でよろしいでしょうか?
    私の質問で他の回答者様からご教授いただいたことと組み合わせて解析に励みたいと思います。有難うございます!
  2. > また、標準コーシー分布を表現したい場合には mu を0に設定すればいいという理解でよろしいでしょうか?
    mu=0, gamma=1が標準コーシー分布ですね。

    挙動がおかしい理由がわかりました。
    おかしく見えるのは、基準となる角速度が小さいことが原因です。
    numpy.random.cauchyを用いた方では、各振動子の角速度omegaはomega0=0まわりの値となります。同期した(集まった)状態では、Kを含む項はほとんど0となり、0付近の値である角速度omegaで振動子はゆっくり周回します。自作関数ではomega0=4まわりなので、ゆっくりなnumpy.random.cauchyを用いた方がおかしく見えたのです。

    したがって、挙動はおかしくなく、正しく標準コーシー分布に従う挙動をしているわけです。
    よく考えず、間違った認識をしたまま投稿をしてしまって申し訳ありません。



    引き続きがんばってください!
  3. @pythonnoob

    Questioner

    とんでもありません。
    こちらこそ、いろいろご教授くださり本当に感謝です。
    また、些細なことで質問させていただくかもしれませんがよろしくお願いいたします!

Your answer might help someone💌