Animating bisection method with matplotlib animation library - python

I am interested in math demonstrations. Currently I am working on visualizing numerical methods in python, in particular the bisection method. Below is the code I have written so far.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
def sgn(x):
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
def bisect(f,a,b):
fa = f(a)
fb = f(b)
p = a+(b-a)/2
fp = f(p)
if sgn(fa) == sgn(fp):
return p, fp, b, fb
else:
return a, fa, p, fp
def f(x):
return x**2-3
a, b = 1, 2
plt.figure()
plt.subplot(111)
a, fa, b, fb = bisect(f,a,b)
vf = np.vectorize(f)
x = np.linspace(a,b)
y = vf(x)
plt.plot(x, y, color='blue')
plt.plot([a,a], [0,fa], color='red', linestyle="--")
plt.plot([b,b], [0,fb], color='red', linestyle="--")
plt.grid()
plt.show()
I have three problems I wish to solve. First, I want to be able to call the bisect function multiple times and each time I would like to redraw the plot with the new data. Second, I would like to restart the animation after applying the bisect function some specified number of times. Third, I would like to retain the original axes of the figure before the bisection method is called i.e. I would like to keep the x-range as [1,2] and the y-range as $[-2,1]$. Any help will be much appreciated.

I found a solution to my problems through much trial and error.
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
def sgn(x):
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
def bisect(f,a,b):
fa = f(a)
fb = f(b)
p = a+(b-a)/2
fp = f(p)
if sgn(fa) == sgn(fp):
return p, b
else:
return a, p
def bisection_method(f,a,b,n):
for i in range(n):
a,b = bisect(f,a,b)
return a,b
def f(x):
return x**2-3
xmin, xmax = 1, 2
yrange = f(xmin), f(xmax)
ymin, ymax = min(yrange), max(yrange)
vf = np.vectorize(f)
x = np.linspace(xmin,xmax)
y = vf(x)
epsilon = 0.1
# Initialize figure
fig = plt.figure()
ax = plt.axes(xlim=(xmin-epsilon,xmax+epsilon), ylim=(ymin,ymax))
curve, = ax.plot([],[], color='blue')
left, = ax.plot([],[],color='red')
right, = ax.plot([],[],color='red')
# Figure reset between frames
def init():
left.set_data([],[])
right.set_data([],[])
curve.set_data([],[])
return left, right, curve,
# Animation of bisection
def animate(i):
a, b = bisection_method(f,xmin,xmax,i)
left.set_data([a,a],[ymin,ymax])
right.set_data([b,b],[ymin,ymax])
curve.set_data(x,y)
return left, right, curve,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=15, interval=700, blit=True)
plt.grid()
plt.show()

You can simply change your code to:
plt.plot([a,a], [0,fa], color='red', linestyle="--",hold=TRUE) which would basically allow you to plot multiple points without resetting the plot and once you have plotted a number of times you can reset using hold=FALSE. Hope this makes sense.

Related

Save Matplotlib animation Tikzplotlib

I have a numerical simulation that I run with a short python code using animation.Funcanimation. Is is possible to use tikzplotlib to produce a .tex file including the whole simulation (and not just one picture) ? This would mean combining tikz and the animate package, for instance. For the moment my sole idea is to export each of the pictures and try to do so something directly with latex (but even this is not completely clear for me !).
Here is an example of short simulation :
import numpy as np
from scipy.integrate import solve_ivp
from matplotlib import pyplot as plt
from matplotlib import animation
# Sites
M = 10
sites = np.array(range(M))
# Particles
N = 3
# Right hand side
def lotka(t,x):
u = x[0:M]
v = x[M:]
dudt = u-u*u-u*v
dvdt = v-u*v-v*v
dxdt = np.concatenate((dudt,dvdt))
return dxdt
# Initial conditions
u0 = np.zeros(M)
v0 = np.zeros(M)
# Segregated
u0[0] = N
v0[-1] = N
if M%2 == 0:
for i in range(int(M/2)):
u0[i] = N
v0[M-1-i] = N
else:
for i in range(int(np.floor(M/2))-1):
u0[i] = N
v0[M-1-i] = N
x0 = np.concatenate((u0,v0))
# Solving the equation
nt = 500
t = np.linspace(0,0.2,nt)
x = solve_ivp(lotka,[0,4],x0,t_eval=t)
# Getting each species from the solution of solve_ivp
u = np.zeros((nt,M))
v = np.zeros((nt,M))
for i in range(nt):
u[i] = x.y.T[i][0:M]
v[i] = x.y.T[i][M:]
# Animation
data = [u, v]
fig = plt.figure()
ax = plt.axes()
ax.grid()
lines = [ax.plot(sites,u[0])[0], ax.plot(sites,v[0])[0]]
time_template = 'time = % s'
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
lines.append(time_text)
def animate(i,lines,data):
lines[0].set_ydata(data[0][i])
lines[1].set_ydata(data[1][i])
lines[2].set_text(time_template % t[i])
return lines
anim = animation.FuncAnimation(fig,
animate,
#frames=200,
fargs=(lines,data),
interval=1,
blit=True)
plt.show()

Monte Carlo simulation programming exercise [duplicate]

I can evaluate the value of pi using different data points by Python. But for each repeat I want to plot the scatter plot like this:
My python code for finding pi using monte carlo method is :
from random import *
from math import sqrt
inside=0
n=10**6
for i in range(0,n):
x=random()
y=random()
if sqrt(x*x+y*y)<=1:
inside+=1
pi=4*inside/n
print (pi)
If you get errors about the backend use this:
import matplotlib as mp
mp.use('Tkagg')
Which will set the backend to TkAgg, which uses the Tkinter user interface toolkit.
import numpy as np
import matplotlib.pyplot as plt
n=1e3
x = 1-2*np.random.random(int(n))
y = 1-2.*np.random.random(int(n))
insideX, insideY = x[(x*x+y*y)<=1],y[(x*x+y*y)<=1]
outsideX, outsideY = x[(x*x+y*y)>1],y[(x*x+y*y)>1]
fig, ax = plt.subplots(1)
ax.scatter(insideX, insideY, c='b', alpha=0.8, edgecolor=None)
ax.scatter(outsideX, outsideY, c='r', alpha=0.8, edgecolor=None)
ax.set_aspect('equal')
fig.show()
To further elaborate Robbie's code:
import numpy as np
import matplotlib.pyplot as plt
n = 1000
xy = np.random.uniform(-1, 1, 2 * n).reshape((2, n))
in_marker = xy[0]**2 + xy[1]**2 <= 1
pi = np.sum(in_marker) / n * 4
in_xy = xy[:, in_marker]
out_xy = xy[:, ~in_marker]
fig, ax = plt.subplots(1)
ax.scatter(*in_xy,c='b')
ax.scatter(*out_xy,c='r')
ax.set_aspect('equal')
fig.show()
building from your code, this may get you started:
import matplotlib.pyplot as plt
from random import random
inside = 0
n = 10**3
x_inside = []
y_inside = []
x_outside = []
y_outside = []
for _ in range(n):
x = random()
y = random()
if x**2+y**2 <= 1:
inside += 1
x_inside.append(x)
y_inside.append(y)
else:
x_outside.append(x)
y_outside.append(y)
pi = 4*inside/n
print(pi)
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.scatter(x_inside, y_inside, color='g', marker='s')
ax.scatter(x_outside, y_outside, color='r', marker='s')
fig.show()
although i prefer this answer that uses numpy from the start...
Here is a variation on hiro protagonist's code, using random.uniform() to allow for random numbers between -1.0 and 1.0, allowing all the points to be plotted, and not just 1/4 of it (not the most elegant code, but it is spelled-out to learn the basics of the Monte Carlo Simulation):
import matplotlib.pyplot as plt
import random
inside = 0
n = 10**3
x_inside = []
y_inside = []
x_outside = []
y_outside = []
for _ in range(n):
x = random.uniform(-1.0,1.0)
y = random.uniform(-1.0,1.0)
if x**2+y**2 <= 1:
inside += 1
x_inside.append(x)
y_inside.append(y)
else:
x_outside.append(x)
y_outside.append(y)
To estimate pi, the points in the circle correspond to the area of the circle enclosing it (pi*radius^2) and the total points correspond to the area of the square enclosing it (2*radius)^2. So this translates into:
(points in the circle)/(total points) = (pi*radius^2)/(2*radius)^2
Solving for pi, the equation becomes:
pi=4*(points in the circle)/(total points)
pi = 4*inside/n
print(pi)
Plot the points inside and outside the circle:
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.scatter(x_inside, y_inside, color='g', marker='s')
ax.scatter(x_outside, y_outside, color='r', marker='s')
fig.show()
Plot of points inside and outside the circle

How to remove the last tick in a polar plot

I want to remove the last tick in a polar plot (the 2π). I've found a way for non-polar plots here, where it said:
yticks[-1].set_visible(False)
which results in:
AttributeError: 'PolarAxesSubplot' object has no attribute 'yticks'
I tried to write rticks instead of yticks but this produced the same error. I've attached an image at the end.
I'm looking for an equivalent method to remove the last tick entry like for a non-polar plot.
import numpy as np
import matplotlib.pyplot as plt
def multiple_formatter(denominator=2, number=np.pi, latex='\pi'):
# produces pi in the axis labels
# https://stackoverflow.com/a/53586826
def gcd(a, b):
while b:
a, b = b, a%b
return a
def _multiple_formatter(x, pos):
den = denominator
num = np.int(np.rint(den*x/number))
com = gcd(num,den)
(num,den) = (int(num/com),int(den/com))
if den==1:
if num==0:
return r'$0$'
if num==1:
return r'$%s$'%latex
elif num==-1:
return r'$-%s$'%latex
else:
return r'$%s%s$'%(num,latex)
else:
if num==1:
return r'$\frac{%s}{%s}$'%(latex,den)
elif num==-1:
return r'$\frac{-%s}{%s}$'%(latex,den)
else:
return r'$\frac{%s%s}{%s}$'%(num,latex,den)
return _multiple_formatter
r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
ax = plt.subplot(111, projection='polar')
ax.plot(theta, r)
ax.set_rticks([0.5, 1, 1.5, 2]) # Less radial ticks
ax.set_rlabel_position(-22.5) # Move radial labels away from plotted line
ax.grid(True)
ax.set_title("A line plot on a polar axis", va='bottom')
ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 4))
ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 12))
ax.xaxis.set_major_formatter(plt.FuncFormatter(multiple_formatter(4)))
plt.show()
The pi labeling comes from here.
Result:
If you change
ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 4))
to
ax.xaxis.set_major_locator(plt.FixedLocator(np.arange(0,2*np.pi,np.pi/4)))
It should meet your requirements.
Full code:
import numpy as np
import matplotlib.pyplot as plt
def multiple_formatter(denominator=2, number=np.pi, latex='\pi'):
# produces pi in the axis labels
# https://stackoverflow.com/a/53586826
def gcd(a, b):
while b:
a, b = b, a%b
return a
def _multiple_formatter(x, pos):
den = denominator
num = np.int(np.rint(den*x/number))
com = gcd(num,den)
(num,den) = (int(num/com),int(den/com))
if den==1:
if num==0:
return r'$0$'
if num==1:
return r'$%s$'%latex
elif num==-1:
return r'$-%s$'%latex
else:
return r'$%s%s$'%(num,latex)
else:
if num==1:
return r'$\frac{%s}{%s}$'%(latex,den)
elif num==-1:
return r'$\frac{-%s}{%s}$'%(latex,den)
else:
return r'$\frac{%s%s}{%s}$'%(num,latex,den)
return _multiple_formatter
r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
ax = plt.subplot(111, projection='polar')
ax.plot(theta, r)
ax.set_rticks([0.5, 1, 1.5, 2]) # Less radial ticks
ax.set_rlabel_position(-22.5) # Move radial labels away from plotted line
ax.grid(True)
ax.set_title("A line plot on a polar axis", va='bottom')
ax.xaxis.set_major_locator(plt.FixedLocator(np.arange(0,2*np.pi,np.pi/4)))
ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 12))
ax.xaxis.set_major_formatter(plt.FuncFormatter(multiple_formatter(4)))
plt.show()

Funcanimation works like time.sleep() to run repeatedly functions?

this is my question: if I get a code with a funcAnimation implementation (financial field, grabbing data from a exchange and live plot every interval of time)..how can I run a function every interval in a optimise manner?
Imagine we have this code as example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(-6, 6)
tmax = 1
tmin = -1
t = np.linspace(-1, 1)
def f(x, t):
term = (np.exp(-1*(x-3*t)**2))*np.sin(3*np.pi*(x-t))
return term
y = f(x, tmax)
lines = plt.plot(x, y)
plt.axis([x[0], x[-1], -1, 1])
plt.xlabel('x')
plt.ylabel('f')
counter = [0]
def animate(ts):
y = f(x, ts)
lines[0].set_ydata(y)
plt.legend(['ts=%4.2f' % ts])
#plt.savefig('tmp_%04d.png' % counter)
counter[0] += 1
anim = animation.FuncAnimation(plt.gcf(), animate, frames=t, interval = 1000)
plt.show()
In this case that maybe is so simple, when I run it the code get stuck to the close loop cause by funcAnimation.
What I do? put the function to run inside animate function. But...that is a good way to proceed?
Here we have a portion of my code and one simple example:
fig = plt.figure(figsize=(16,18),facecolor="#232a3b")
def animate(i):
say(5)
graphData('BTC/USDT','1m',8,13,21)
def say(i):
print('Hello World ' + str(i))
while True:
#pair = input('Crypto pair to plot: ')
#timeframe = input('Enter timeframe: ')
say(5)
ani = animation.FuncAnimation(fig, animate,interval=60000)
plt.show()

Python: Creating a plot inside of a for loop with two different variables

I am trying to plot a function with two parameters. In this case I'd like the function to plot with respect to "yy" in order for it to be in polar coordinates. When I run the program I get 10 figures rather than 1 single plot. Is there a reason this happens? Also, I'm not getting a plot at all.
import scipy.optimize as opt
import matplotlib.pyplot as plt
import pylab as pyl
freq = 9.75e9
lmda = 299792458./freq
k = 2*np.pi/lmda
h1 = 0.25*lmda
def theta(x,y):
th = np.arctan(y,x)
return th
def F(x,y):
f=2*np.abs(np.sin(k*h1*theta(x,y)))
return f
def gain(x,y):
return 10*np.log10(F(x,y)**2)
xx = np.arange(0,2000,200)
yy = np.linspace(0,np.pi/2,1000)
for tval in xx:
plt.rcParams['text.latex.preamble']=[r'\usepackage{amsmath}']
plt.rc('text',usetex=True)
font = {'family':'serif','size':20}
plt.rc('font',**font)
fig, ax=plt.subplots(subplot_kw=dict(projection='polar'))
ticks = np.arange(0,360,45)
ax.set_ylim(-40,10)
ax.set_yticks([-40,-30,-20,-10,0])
ax.set_yticklabels(['','30','20','10',''],verticalalignment='center',horizontalalignment='center')
ax.set_thetagrids(ticks, frac=1.2)
ax.set_xlim(0, np.pi/2)
ax.set_theta_zero_location('N') # changes the orienation of theta
ax.plot(yy,gain(yy,tval)) #dipole elevation plane pattern
plt.tight_layout()
plt.show()
You need a little restructure:
import scipy.optimize as opt
import matplotlib.pyplot as plt
import pylab as pyl
import numpy as np
freq = 9.75e9
lmda = 299792458./freq
k = 2*np.pi/lmda
h1 = 0.25*lmda
def theta(x,y):
th = np.arctan(y,x)
return th
def F(x,y):
f=2*np.abs(np.sin(k*h1*theta(x,y)))
return f
def gain(x,y):
return 10*np.log10(F(x,y)**2)
xx = np.arange(0,2000,200)
yy = np.linspace(0,np.pi/2,1000)
plt.rcParams['text.latex.preamble']=[r'\usepackage{amsmath}']
plt.rc('text',usetex=True)
font = {'family':'serif','size':20}
plt.rc('font',**font)
fig, ax=plt.subplots(subplot_kw=dict(projection='polar'))
ticks = np.arange(0,360,45)
ax.set_ylim(-40,10)
ax.set_yticks([-40,-30,-20,-10,0])
ax.set_yticklabels(['','30','20','10',''],verticalalignment='center',horizontalalignment='center')
ax.set_thetagrids(ticks, frac=1.2)
ax.set_xlim(0, np.pi/2)
ax.set_theta_zero_location('N') # changes the orienation of theta
for tval in xx:
ax.plot(yy,gain(yy,tval)) #dipole elevation plane pattern
plt.tight_layout()
plt.show()
You also should handle the division by zero error.

Categories

Resources