I'm trying to make a bifurcation diagram for the following iterated map:
x_n+1 = x_n * e^(r(1-x_n)).
First I defined the map:
def newmap(x,r):
return x*math.exp(r*(1-x))
Then I tried this:
def bifurcation_diagram(rmin=0, rmax=4, r_N=2000, N_min=4000, N = 1000):
rspace = np.linspace(rmin, rmax, r_N)
x = 0
rset = []
xset = []
for r in rspace:
for i in range(N_min + N):
x = newmap(x,r)
if i > N_min:
rset.append(r)
xset.append(x)
plt.figure(figsize=(16,7))
plt.xlim((rmin,rmax))
plt.ylim((0,5))
plt.scatter(rset,xset,s=0.3,c='C0',linewidth=0)
plt.xlabel(r'r', fontsize=20)
plt.ylabel(r'$x_{end}$', fontsize=29, rotation=0)
plt.show()
When I try bifurcation_diagram() I get a blank plot.
I'm not sure where I'm going wrong here.
The problem is that x=0 is a fixed point and so is x=1. If you switch x=0 to x=0.1 but otherwise leave it where it is, the first r value drives x to (what is for those values) the attracting fixed point 1. You need to put x=0.1 inside of the main loop:
for r in rspace:
x = 0.01
(with everything else as before).
Related
I am writing a code to plot several projectile trajectories of various theta values in Python.
theta = np.arange(np.pi/6, np.pi/3)
t = np.linspace(0,2,num=100)
while y0>=0:
for i in theta:
x = []
y = []
for k in t:
x0= v_0*np.cos(i)*k
y0= v_0*np.sin(i)*k - 1/2*g*(k**2)
x.append(x0)
x.append(y0)
After forming the arrays and putting in the necessary conditions for projectile, I have used a while loop to put the terminating instruction in the program. I think, I am missing a crucial point. Thanks!
I think you want your terminating condition inside your inner-most loop. See below, where I also defined a couple of missing constants (v_0, g) and fixed one x to y. also printing the results
theta = np.arange(np.pi/6, np.pi/3)
t = np.linspace(0,2,num=100)
v_0 = 1
g=10
for i in theta:
x = []
y = []
for k in t:
x0= v_0*np.cos(i)*k
y0= v_0*np.sin(i)*k - 1/2*g*(k**2)
x.append(x0)
y.append(y0)
if y0 < 0: # the main change here. Stop looping when y_0 below zero
break
print(f'theta:{i}')
print(f'x:{x}')
print(f'y:{y}')
produces
theta:0.5235987755982988
x:[0.0, 0.017495462702715934, 0.03499092540543187, 0.052486388108147805, 0.06998185081086374, 0.08747731351357968]
y:[0.0, 0.008060401999795939, 0.012039587797163551, 0.011937557392102841, 0.007754310784613805, -0.0005101520253035577]
Plotting it (y vs x), looks reasonable
It is also worth noting that your definition of theta = np.arange(np.pi/6, np.pi/3) looks rather strange, what are you trying to achieve here?
I have used the following code to produce a heatmap for a given function. Here it is calculating the global lyapunov exponent for a variation of the logistic map - I've added the parameter of p. Where this is fine, I don't actually care about what the exact value of the global lyapunov exponent is, but rather if it is positive or negative.
Here is the code I've been using:
# Logistic Function
def p_logistic(A, x, p):
return (A/4) * (((p+1)**(p+1))/(p**p)) * (x**p) * (1-x)
# Lyapunov Exponent
def p_lyap(A, x, p):
return np.log(abs((A/4) * (((p+1)**(p+1))/(p**p)) * (p*(x**(p-1)) - (p+1)*(x**p))))
n = 500
A = np.linspace(2, 4, n)
p = np.linspace(0.5, 5, n)
def F(A, p):
A, p = np.meshgrid(A, p)
lyapunov = 0
x = 0.9
N = 100
for i in range(0,N):
lyapunov = lyapunov + p_lyap(A, x, p)
x = p_logistic(A, x, p)
global_lyapunov = lyapunov/N
return global_lyapunov
z = F(A, p)
plt.figure(figsize=(8,8))
xlabels = ['{:3.1f}'.format(x) for x in A]
ylabels = ['{:3.1f}'.format(y) for y in p]
ax = sns.heatmap(z, xticklabels = A, yticklabels = p)
I have tried using if statements to return the global lyapunov exponent as 1 if it is greater than 0, and -1 if it is less than 0, but it returned the error
The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Essentially I would like only 3 colours in my heat map, one for when the global lyapunov exponent is negative, equal to 0 and positive. is there any way this is possible?
assuming I'm interpreting you correctly, I'd just do something like:
zd = np.zeros_like(z, dtype=int)
zd[z > 0] = 1
zd[z < 0] = -1
and then plot zd instead of z
also note, you might be better off using imshow from directly from matplotlib that way you can get nicer axis labels, e.g:
plt.figure(figsize=(8,8))
plt.imshow(zd, extent=(2, 4, 5, 0.5), aspect='auto')
plt.colorbar()
imshow doesn't doesn't do any rescaling of values that heatmap does, which is probably better for your use case
also there seem to be a lot of values that aren't defined, so it might be worth treating them specially, e.g:
zd[~np.isfinite(z)] = 0
giving a final plot of:
but I'm not sure if I got the axes the right way around
numpy.sign is an easy way to get the data you want. Also, seems you intended to use xlabel and ylabel:
sns.heatmap(np.sign(z), xticklabels=xlabel, yticklabels=ylabel)
I'm a beginner and I don't speak english very well so sorry about that.
I'd like to draw the bifurcation diagram of the sequence :
x(n+1)=ux(n)(1-x(n)) with x(0)=0.7 and u between 0.7 and 4.
I am supposed to get something like this :
So, for each value of u, I'd like to calculate the accumulation points of this sequence. That's why I'd like to code something that could display every points (u;x1001),(u;x1002)...(u;x1050) for each value of u.
I did this :
import matplotlib.pyplot as plt
import numpy as np
P=np.linspace(0.7,4,10000)
m=0.7
Y=[m]
l=np.linspace(1000,1050,51)
for u in P:
X=[u]
for n in range(1001):
m=(u*m)*(1-m)
break
for l in range(1051):
m=(u*m)*(1-m)
Y.append(m)
plt.plot(X,Y)
plt.show()
And, I get a blank graphic.
This is the first thing I try to code and I don't know anything yet in Python so I need help please.
There are a few issues in your code. Although the problem you have is a code review problem, generating bifurcation diagrams is a problem of general interest (it might need a relocation on scicomp but I don't know how to request that formally).
import matplotlib.pyplot as plt
import numpy as np
P=np.linspace(0.7,4,10000)
m=0.7
# Initialize your data containers identically
X = []
Y = []
# l is never used, I removed it.
for u in P:
# Add one value to X instead of resetting it.
X.append(u)
# Start with a random value of m instead of remaining stuck
# on a particular branch of the diagram
m = np.random.random()
for n in range(1001):
m=(u*m)*(1-m)
# The break is harmful here as it prevents completion of
# the loop and collection of data in Y
for l in range(1051):
m=(u*m)*(1-m)
# Collection of data in Y must be done once per value of u
Y.append(m)
# Remove the line between successive data points, this renders
# the plot illegible. Use a small marker instead.
plt.plot(X, Y, ls='', marker=',')
plt.show()
Also, X is useless here as it contains a copy of P.
To save bifurcation diagram in png format, you can try this simple code.
# Bifurcation diagram of the logistic map
import math
from PIL import Image
imgx = 1000
imgy = 500
image = Image.new("RGB", (imgx, imgy))
xa = 2.9
xb = 4.0
maxit = 1000
for i in range(imgx):
r = xa + (xb - xa) * float(i) / (imgx - 1)
x = 0.5
for j in range(maxit):
x = r * x * (1 - x)
if j > maxit / 2:
image.putpixel((i, int(x * imgy)), (255, 255, 255))
image.save("Bifurcation.png", "PNG")
I got a list of points by extracting the edge of a image, like that:But it is not well ordered, so if I connect it as a line, it will be:
Thus I want to sort this list if points. Like, start with point_0, find which one has the shortest distance with it, say, point_3, then find which one's closest to point_3 then continue...
To sort the points, I wrote this:
import matplotlib.pyplot as plt
import numpy as np
import math
def dist(now, seek):
return math.sqrt((now[0] - seek[0])**2 + (now[1] - seek[1])**2)
def sortNearest(x, y):
if len(x) != len(y):
raise Exception('Error! Array length do not match!')
return False
xNew = []; yNew = []
nearest = 0 #record which point is nearest
now = [x[0], y[0]] #start point index
seekValue = 0
while len(x) > 0:
distance = (max(x) - min(x)) + (max(y) - min(y))
for seek in range(len(x)): # other
temp = dist(now, [x[seek], y[seek]])
if temp < distance and temp != 0.0:
distance = temp
seekValue = x[seek]
xNew.append(now[0]);
yNew.append(now[1]);
if len(x) > 0:
x.remove(now[0])
y.remove(now[1])
if len(x) > 0:
nearest = x.index(seekValue)
now = [x[nearest], y[nearest]]
x = list(xNew); y = list(yNew)
return xNew, yNew
x, y = getBorder('large.png', maxRes = 125)
x, y = sortNearest(x, y)
But that doesn't work well, I came up with this:
Which is obviously incorrect, if I zoom in, see:
If my code runs what I want, point_644 should connect 620 or 675, any but 645... What's wrong with it?
Well, point 644 cannot connect to point 620, because 620 is already part of your path.
As for why it connects to 645 instead of the closer 675: in your loop, you aren't actually remembering the index of the closest point, you're only remembering its x coordinate. After the loop, you then locate an arbitrary point with the same x coordinate - it could be anywhere on a vertical line going through the desired point.
I don't know how I would do this in python 3.x, so please forgive changes that I have not made from python 2.7. You'll also want to figure out what point you'd like to start with:
def find_distance(point1, point2):
distance = sqrt(square(point1[0]-point2[0]) + square(point1[1] - point2[1]))
return distance
x, y = getBorder('large.png', maxRes = 125)
points_in_border = [(i,j) for i, j in zip(x,y)]
current_point = points_in_border.pop([0])
points_in_order = [current_point]
while len(points_in_border) > 0:
min_distance = 10000
for point in points_in_border:
if find_distance(current_point, point) < min_distance:
closest_point = point
min_distance = find_distance(current_point, point)
points_in_border.remove(closest_point)
current_point = closest_point
points_in_order.append(closest_point)
I think what you want to do can be optimized with numpy and scipy:
import numpy as np
import scipy.spatial.distance as distance
import matplotlib.pyplot as plt
points = np.random.random((6,2))
dists =distance.pdist(points)
m=np.argsort(distance.squareform(dists))[:,1:]
order = [0,m[0,0]]
next_point = order[-1]
while len(order)<len(points):
row = m[next_point]
i = 0
while row[i] in order:
i += 1
order.append(row[i])
next_point = order[-1]
order.append(0)
ordered=points[order]
plt.plot(ordered[:,0], ordered[:,1], 'o-')
The idea underlying this code is the following. First you calculate all the distances. Then you use argsort to get the indices that would order each row. You can remove the first column, as each point is closest to itself. We know that. Then you look which is the next closest point and you add it to the list order if the point is not there yet. You then go to the row corresponding to this point, and look for the next point. And so on.
If what you are only interested in is just sorting the enclosing set of points, you can use ConvexHull to find them:
ch = ConvexHull(points)
plt.plot(points[ch.vertices,0], points[ch.vertices,1], 'o-')
print "Enter the amount of points you would like to generate to estimate Pi"
n = raw_input()
plt.xlim((0,1))
plt.ylim((0,1))
x = numpy.linspace(0,1,500)
y = numpy.sqrt(1-x**2)
for i in range(int(n)):
xcoordinate = random.random()
ycoordinate = random.random()
plt.scatter(xcoordinate, ycoordinate)
plt.plot(x,y,'g')
plt.show()
I'm trying to get n random points scattered across this graph. Instead I only get a single random point.
How can I fix this?
You can turn xcoordinate and xcoordinate into a list instead, and append random points in it.
xcoordinate=[]
ycoordinate=[]
for i in range(int(n)):
xcoordinate.append(random.random())
ycoordinate.append(random.random())
The rest of your code remains unchanged.