Mixing solutions of sympy equation - sympy

I'm solving an time depend equation that has the following form (finding the roots for \lambda):
import sympy as sp
import matplotlib.pyplot as plt
t = sp.symbols(r't', real=True, positive=True)
eq = ...
print(repr(eq))
-\lambda**3 - 2*\lambda**2*t + 4*\lambda*t**4 - 8*\lambda*t**3 + 8*\lambda*t**2 - 8*\lambda*t + 4*\lambda + 8*t**3 - 16*t**2 + 8*t
Solving the equation and saving the roots to a list:
sol = sp.solve(eq)
e_list = [list(sol[i].values())[0] for i in range(len(sol))]
Showing their evolution explicitly:
x =e_list[0]
lam_x = sp.lambdify(t, x, modules=['numpy'])
x_vals = np.linspace(0.01, .9999, 1000, dtype=complex)
y_vals = np.around(lam_x(x_vals),decimals=5)
plt.plot(np.real(x_vals), np.real(y_vals),label='r1')
x =e_list[1]
lam_x = sp.lambdify(t, x, modules=['numpy'])
x_vals = np.linspace(0.01, .9999, 1000, dtype=complex)
y_vals = np.around(lam_x(x_vals),decimals=5)
plt.plot(np.real(x_vals), np.real(y_vals),label='r2')
x =e_list[2]
lam_x = sp.lambdify(t, x, modules=['numpy'])
x_vals = np.linspace(0.01, .9999, 1000, dtype=complex)
y_vals = np.around(lam_x(x_vals),decimals=5)
plt.plot(np.real(x_vals), np.real(y_vals),label='r3')
plt.legend()
plt.show()
But for some reason the solutions for the the two firsts roots are mixed before ~ .72. I have no idea how to correct this, make then not mix

That behavior is to be expected with Numpy, as you are going trough a complex branch cut. In this case you should use Mpmath as the evaluation module for lambdify, which deals with branch cuts differently. For example:
import numpy as np
import matplotlib.pyplot as plt
x =e_list[0]
lam_x = lambdify(t, x, modules=['mpmath'])
x_vals = np.linspace(0.01, .9999, 1000, dtype=complex)
y_vals = []
for _x in x_vals:
y_vals.append(complex(lam_x(_x)))
plt.figure()
plt.plot(np.real(x_vals), np.real(y_vals),label='r1')
x =e_list[1]
lam_x = lambdify(t, x, modules=['mpmath'])
x_vals = np.linspace(0.01, .9999, 1000, dtype=complex)
y_vals = []
for _x in x_vals:
y_vals.append(complex(lam_x(_x)))
plt.plot(np.real(x_vals), np.real(y_vals),label='r2')
x =e_list[2]
lam_x = lambdify(t, x, modules=['numpy'])
x_vals = np.linspace(0.01, .9999, 1000, dtype=complex)
y_vals = np.around(lam_x(x_vals),decimals=5)
plt.plot(np.real(x_vals), np.real(y_vals),label='r3')
plt.legend()
plt.show()

Related

How to find and draw the intersection points of contour shapes

I'm trying to find the points of intersection between the line passing through point V and conic. The conic graph is not solvable relative to y ( or x), so it was depicted using contour. Is there a method for finding the intersection points of contour graphs?
enter image description here
Here is the code:
import numpy as np
import matplotlib.pyplot as plt
p = (input("choose point P on axis OX: "))
print(p)
q = (input("choose point Q on axis OX: "))
print(q)
v = (input("choose point V on axis OX: "))
print(v)
k=3
X = np.arange(-50, 50, 0.05)
Y = k*X
plt.plot(X,Y)
plt.plot(0,0)
plt.scatter(0.0, 0.0, color='white', marker='o')
plt.text(0.0, 0.0, "O", horizontalalignment="center")
plt.plot(-v,0)
plt.scatter(-v, 0, color='red', marker='o')
plt.text(-v, 0.8, "V", horizontalalignment="center")
xmin= -10
xmax= 10
ymin= -10
ymax= 10
ax = plt.gca()
ax.get_xlim()
ax.set_xlim([xmin,xmax])
ax.set_ylim([ymin,ymax])
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
#Create random point B1
b1=4
plt.plot(0.0,b1)
plt.scatter(0.0, b1, color='blue', marker='o')
plt.text(0.8, b1, "B1", horizontalalignment="center")
x, y = np.meshgrid(X, X)
#Create VB1
l3 = b1*x+b1*v - v*y
vb = plt.contour(x,y, l3, [0], colors='k')
# l3 = b1*X/v + b1
# plt.plot(X,l3)
#Create conic
conic = x*x*b1*2*p*k-x*x*b1*2*q*k+x*x*k*k+y*y-b1*2*y+2*b1*q*x*y
cnc = plt.contour(x, y, (conic), [0], colors='k')
I tried to do something like that:
c = cnc.intersection(vb)
print(c)
or
# https://stackoverflow.com/questions/28766692/intersection-of-two-graphs-in-python-find-the-x-value
idx = np.argwhere(np.diff(np.sign(cnc - vb))).flatten()
plt.plot(x[idx], y[idx], 'ro')
My last attempt:
import numpy as np
import matplotlib.pyplot as plt
p,q,v,k,b=5,7,2,3,4
X = np.arange(-50, 50, 0.05)
plt.plot(-v,0)
plt.scatter(-v, 0, color='red', marker='o')
plt.text(-v, 0.8, "V", horizontalalignment="center")
xmin,xmax,ymin,ymax=-10,10,-10,10
ax = plt.gca()
ax.get_xlim()
ax.set_xlim([xmin,xmax])
ax.set_ylim([ymin,ymax])
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.plot(0.0,b)
plt.scatter(0.0, 1, color='blue', marker='o')
x, y = np.meshgrid(X, X)
l = b*x+b*v-v*y
vb = plt.contour(x,y, l, [0], colors='k')
conic = x*x*b*2*p*k-x*x*b*2*q*k+x*x*k*k+y*y-b*2*y+2*b*q*x*y
cnc = plt.contour(x, y, (conic), [0], colors='k')
c = cnc.collections[0].get_paths()[1]
v = c.vertices
x1 = v[:,0]
y1 = v[:,1]
plt.plot(x1,y1)
vb1 = vb.collections[0].get_paths()[0]
v1 = vb1.vertices
x2 = v1[:,0]
y2 = v1[:,1]
plt.plot(x2,y2,color='red')
# def find_roots(x,y):
# s = np.abs(np.diff(np.sign(y))).astype(bool)
# return x[:-1][s] + np.diff(x)[s]/(np.abs(y[1:][s]/y[:-1][s])+1)
#
# z = find_roots(x1-x2,y1-y2)
# plt.plot(z, np.zeros(len(z)), marker="o", ls="", ms=4)
plt.show()
enter image description here
It's a little more complicated. The problem is that (a) the points in the contour are not necessarily sorted and (b) the two contours do not have a common support.
So one would need to (a) sort the points along x, and (b) create an array of common values, on which to interpolate first.
import numpy as np
import matplotlib.pyplot as plt
p,q,v,k,b=5,7,2,3,4
X = np.arange(-50, 50, 0.05)
plt.plot(-v,0)
plt.scatter(-v, 0, color='red', marker='o')
plt.text(-v, 0.8, "V", horizontalalignment="center")
xmin,xmax,ymin,ymax=-10,10,-10,10
ax = plt.gca()
ax.get_xlim()
ax.set_xlim([xmin,xmax])
ax.set_ylim([ymin,ymax])
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
x, y = np.meshgrid(X, X)
l = b*x+b*v-v*y
vb = plt.contour(x,y, l, [0], colors='red')
conic = x*x*b*2*p*k-x*x*b*2*q*k+x*x*k*k+y*y-b*2*y+2*b*q*x*y
cnc = plt.contour(x, y, (conic), [0], colors='blue')
c = cnc.collections[0].get_paths()[1]
v = c.vertices
x1 = np.sort(v[:,0])
y1 = v[np.argsort(v[:,0]),1]
vb1 = vb.collections[0].get_paths()[0]
v1 = vb1.vertices
x2 = np.sort(v1[:,0])
y2 = v1[np.argsort(v1[:,0]),1]
def find_roots(x,y):
s = np.abs(np.diff(np.sign(y))).astype(bool)
return x[:-1][s] + np.diff(x)[s]/(np.abs(y[1:][s]/y[:-1][s])+1)
x = np.linspace(max(x1.min(), x2.min()), min(x1.max(), x2.max()), 1000)
y1i = np.interp(x, x1, y1 ) # blue
y2i = np.interp(x, x2, y2 ) # red
x_intersect = find_roots(x,y2i-y1i)
y_intersect = np.interp(x_intersect, x, y2i)
plt.plot(x_intersect, y_intersect, marker="X", ms=5, color="limegreen")
plt.show()
The point of intersection is the green dot.
Of course one needs to do the same for the other arm of the conic contour (cnc.collections[0].get_paths()[0]) if desired.

Plot a 3D bar histogram with python

I have some x and y data, with which I would like to generate a 3D histogram, with a color gradient (bwr or whatever).
I have written a script which plot the interesting values, in between -2 and 2 for both x and y abscesses:
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
# To generate some test data
x = np.random.randn(500)
y = np.random.randn(500)
XY = np.stack((x,y),axis=-1)
def selection(XY, limitXY=[[-2,+2],[-2,+2]]):
XY_select = []
for elt in XY:
if elt[0] > limitXY[0][0] and elt[0] < limitXY[0][1] and elt[1] > limitXY[1][0] and elt[1] < limitXY[1][1]:
XY_select.append(elt)
return np.array(XY_select)
XY_select = selection(XY, limitXY=[[-2,+2],[-2,+2]])
heatmap, xedges, yedges = np.histogram2d(XY_select[:,0], XY_select[:,1], bins = 7, range = [[-2,2],[-2,2]])
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
plt.figure("Histogram")
#plt.clf()
plt.imshow(heatmap.T, extent=extent, origin='lower')
plt.show()
And give this correct result:
Now, I would like to turn this into a 3D histogram. Unfortunatly I don't success to plot it correctly with bar3d because it takes by default the length of x and y for abscisse.
I am quite sure that there is a very easy way to plot this in 3D with imshow. Like an unknow option...
I finaly succeded in doing it. I am almost sure there is a better way to do it, but at leat it works:
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
# To generate some test data
x = np.random.randn(500)
y = np.random.randn(500)
XY = np.stack((x,y),axis=-1)
def selection(XY, limitXY=[[-2,+2],[-2,+2]]):
XY_select = []
for elt in XY:
if elt[0] > limitXY[0][0] and elt[0] < limitXY[0][1] and elt[1] > limitXY[1][0] and elt[1] < limitXY[1][1]:
XY_select.append(elt)
return np.array(XY_select)
XY_select = selection(XY, limitXY=[[-2,+2],[-2,+2]])
xAmplitudes = np.array(XY_select)[:,0]#your data here
yAmplitudes = np.array(XY_select)[:,1]#your other data here
fig = plt.figure() #create a canvas, tell matplotlib it's 3d
ax = fig.add_subplot(111, projection='3d')
hist, xedges, yedges = np.histogram2d(x, y, bins=(7,7), range = [[-2,+2],[-2,+2]]) # you can change your bins, and the range on which to take data
# hist is a 7X7 matrix, with the populations for each of the subspace parts.
xpos, ypos = np.meshgrid(xedges[:-1]+xedges[1:], yedges[:-1]+yedges[1:]) -(xedges[1]-xedges[0])
xpos = xpos.flatten()*1./2
ypos = ypos.flatten()*1./2
zpos = np.zeros_like (xpos)
dx = xedges [1] - xedges [0]
dy = yedges [1] - yedges [0]
dz = hist.flatten()
cmap = cm.get_cmap('jet') # Get desired colormap - you can change this!
max_height = np.max(dz) # get range of colorbars so we can normalize
min_height = np.min(dz)
# scale each z to [0,1], and get their rgb values
rgba = [cmap((k-min_height)/max_height) for k in dz]
ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color=rgba, zsort='average')
plt.title("X vs. Y Amplitudes for ____ Data")
plt.xlabel("My X data source")
plt.ylabel("My Y data source")
plt.savefig("Your_title_goes_here")
plt.show()
I use this example, but I modified it, because it introduced an offset. The result is this:
You can generate the same result using something as simple as the following:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-2, 2, 7)
y = np.linspace(-2, 2, 7)
xx, yy = np.meshgrid(x, y)
z = xx*0+yy*0+ np.random.random(size=[7,7])
plt.imshow(z, interpolation='nearest', cmap=plt.cm.viridis, extent=[-2,2,2,2])
plt.show()
from mpl_toolkits.mplot3d import Axes3D
ax = Axes3D(plt.figure())
ax.plot_surface(xx, yy, z, cmap=plt.cm.viridis, cstride=1, rstride=1)
plt.show()
The results are given below:

Python: Data fitting with scipy.optimize.curve_fit with sigma = 0

I'm trying to fit a curve with scipy.optimize.curve_fit and it works pretty good so far, except in the case that a value in my sigma array is zero. I understand that the algorithm can't handle this, as I divide by zero in this case. From the scipy documentation:
sigma : None or M-length sequence, optional
If not None, the uncertainties in the ydata array. These are used as weights in the least-squares problem i.e. minimising np.sum( ((f(xdata, *popt) - ydata) / sigma)**2 ) If None, the uncertainties are assumed to be 1.
Here's what my code looks like:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
x = [0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875, 2.125, 2.375, 2.625, 2.875, 3.125, 3.375, 3.625, 3.875, 4.125, 4.375]
y_para = [0, 0, 0.0414, 0.2164, 0.2616, 0.4254, 0.5698, 0.5921, 0.6286, 0.6452, 0.5879, 0.6032, 0.6667, 0.6325, 0.7629, 0.7164, 0.7091, 0.7887]
err = [0, 0, 0.0391, 0.0331, 0.0943, 0.0631, 0.1219, 0.1063, 0.0912, 0.0516, 0.0365, 0.0327, 0.0227, 0.103, 0.1344, 0.0697, 0.0114, 0.0465]
def logistic_growth(x, A1, A2, x_0, p):
return A2 + (A1-A2)/(1+(x/x_0)**p)
x_plot = np.linspace(0, 4.5, 100)
bounds_para = ([0.,0,-np.inf,-np.inf],[0.0000000001, 1,np.inf,np.inf])
paras, paras_cov = curve_fit(logistic_growth, x, y_para, bounds = bounds_para, sigma = err, absolute_sigma=True)
para_curve = logistic_growth(x_plot, *paras)
plt.figure()
plt.errorbar(x,y_para, err, color = 'b', fmt = 'o', label = "Data")
plt.plot(x_plot, para_curve, color = 'b', label = "Fit")
plt.show()
Executing this without the sigma-option in curve_fit works fine, but including it raises:
ValueError: Residuals are not finite in the initial point.
, which results from the zeros in the err-array.
Does anyone know a way to work around this?
Why not just drop the variable? If it has zero variance it cannot contribute in any meaningful way to your analysis.
This is what the scipy doc says about the curve_fit sigma parameter: 'These are used as weights in the least-squares problem ...' Then, in my opinion, they should be inverse to the errors. Here's what I suggest.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
x = [0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875, 2.125, 2.375, 2.625, 2.875, 3.125, 3.375, 3.625, 3.875, 4.125, 4.375]
y_para = [0, 0, 0.0414, 0.2164, 0.2616, 0.4254, 0.5698, 0.5921, 0.6286, 0.6452, 0.5879, 0.6032, 0.6667, 0.6325, 0.7629, 0.7164, 0.7091, 0.7887]
err = [0, 0, 0.0391, 0.0331, 0.0943, 0.0631, 0.1219, 0.1063, 0.0912, 0.0516, 0.0365, 0.0327, 0.0227, 0.103, 0.1344, 0.0697, 0.0114, 0.0465]
weights = [1/max(_,0.001) for _ in err]
print (weights)
def logistic_growth(x, A1, A2, x_0, p):
return A2 + (A1-A2)/(1+(x/x_0)**p)
x_plot = np.linspace(0, 4.5, 100)
bounds_para = ([0.,0,-np.inf,-np.inf],[0.0000000001, 1,np.inf,np.inf])
paras, paras_cov = curve_fit(logistic_growth, x, y_para, bounds = bounds_para,
absolute_sigma=True,
sigma = weights)
para_curve = logistic_growth(x_plot, *paras)
plt.figure()
plt.errorbar(x,y_para, err, color = 'b', fmt = 'o', label = "Data")
plt.plot(x_plot, para_curve, color = 'b', label = "Fit")
plt.show()
This results in the following plot, where those initial data points are made to lie very close to the fitted line.

Axes3D plot taking a lot of time and using a lot of RAM

I have the following code to create a 3D plot of a Bivariate Gaussian Distribution:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
class Data(object):
data = None
columns = 0
rows = 0
def __init__(self, path='file.txt'):
self.data = np.loadtxt(path, delimiter=' ', dtype='float32')
self.rows, self.columns = self.data.shape
def _pdf(self, x, mu, cov):
part1 = 1 / ( ((2* np.pi)**(len(mu)/2)) * (np.linalg.det(cov)**(1/2)) )
part2 = (-1/2) * ((x-mu).T.dot(np.linalg.inv(cov))).dot((x-mu))
return float(part1 * np.exp(part2))
def compute_Z(self):
mu = np.array([[2.99413181],[3.05209659]], dtype="float")
cov = np.array([[1.01023423, 0.02719138], [0.02719138, 2.93782296]], dtype="float")
Z = []
for i, j in zip(X, Y):
x = np.array([i,j]).reshape(2,1)
Z.append(self._pdf(x, mu, cov))
return np.array(Z)
if __name__ == "__main__":
data = Data()
X = data.data[:, 0]
Y = data.data[:, 1]
Z = data.compute_Z()
X, Y = np.meshgrid(X, Y)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, color='0.9', alpha=0.9, linewidth=1)
plt.show()
But this is taking a lot of time and also using a lot of RAM. Is there some way to reduce it? Or there is a better method to create this plot?
Thanks!
matplotlib 3D plotting isn't very good for large amount of data.
You can use mayavi, which has very similar interface using mlab.
from mayavi import mlab
mlab.figure()
mlab.surf(X, Y, Z)
mlab.show()

Python -- Matplotlib for elliptic curve with sympy solve()

I have an elliptic curve plotted. I want to draw a line along a P,Q,R (where P and Q will be determined independent of this question). The main problem with the P is that sympy solve() returns another equation and it needs to instead return a value so it can be used to plot the x-value for P. As I understood it, solve() should return a value, so I'm clearly doing something wrong here that I'm just totally not seeing. For reference, here's how P+Q=R should look:
I've been going over the docs and other material and this is as far as I've been able to get myself into trouble:
from mpl_toolkits.axes_grid.axislines import SubplotZero
from pylab import *
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches
from matplotlib import rc
import random
from sympy.solvers import solve
from sympy import *
def plotGraph():
fig = plt.figure(1)
#ax = SubplotZero(fig, 111)
#fig.add_subplot(ax)
#for direction in ["xzero", "yzero"]:
#ax.axis[direction].set_axisline_style("-|>")
#ax.axis[direction].set_visible(True)
#ax.axis([-10,10,-10,10])
a = -2; b = 1
y, x = np.ogrid[-10:10:100j, -10:10:100j]
xlist = x.ravel(); ylist = y.ravel()
elliptic_curve = pow(y, 2) - pow(x, 3) - x * a - b
plt.contour(xlist, ylist, elliptic_curve, [0])
#rand = random.uniform(-5,5)
randmid = random.randint(30,70)
#y = ylist[randmid]; x = xlist[randmid]
xsym, ysym = symbols('x ylist[randmid]')
x_result = solve(pow(ysym, 2) - pow(xsym, 3) - xsym * a - b, xsym) # 11/5/13 needs to return a value
plt.plot([-1.5,5], [-1,8], color = "c", linewidth=1) # plot([x1,x2,x3,...],[y1,y2,y3,...])
plt.plot([xlist[randmid],5], [ylist[randmid],8], color = "m", linewidth=1)
#rc('text', usetex=True)
text(-9,6,' size of xlist: %s \n size of ylist: %s \n x_coord: %s \n random_y: %s'
%(len(xlist),len(ylist),x_result,ylist[randmid]),
fontsize=10, color = 'blue',bbox=dict(facecolor='tan', alpha=0.5))
plt.annotate('$P+Q=R$', xy=(2, 1), xytext=(3, 1.5),arrowprops=dict(facecolor='black', shrink=0.05))
## verts = [(-5, -10),(5, 10)] # [(x,y)startpoint,(x,y)endpoint] #,(0, 0)]
## codes = [Path.MOVETO,Path.LINETO] # related to verts[] #,Path.STOP]
## path = Path(verts, codes)
## patch = patches.PathPatch(path, facecolor='none', lw=2)
## ax.add_patch(patch)
plt.grid(True)
plt.show()
def main():
plotGraph()
if __name__ == '__main__':
main()
Ultimately, I'd like to draw a line to show P+Q=R, so if someone also has something to add on how to code to get the Q that would be greatly appreciated. I'm teaching myself about Python and elliptic curves so I'm sure that any entry-level programmer can figure out in 2 minutes what I've been on for some time already.
I don't know what are you calculating, but here is the code that can plot the graph:
import numpy as np
import pylab as pl
Y, X = np.mgrid[-10:10:100j, -10:10:100j]
def f(x):
return x**3 -3*x + 5
px = -2.0
py = -np.sqrt(f(px))
qx = 0.5
qy = np.sqrt(f(qx))
k = (qy - py)/(qx - px)
b = -px*k + py
poly = np.poly1d([-1, k**2, 2*k*b+3, b**2-5])
x = np.roots(poly)
y = np.sqrt(f(x))
pl.contour(X, Y, Y**2 - f(X), levels=[0])
pl.plot(x, y, "o")
pl.plot(x, -y, "o")
x = np.linspace(-5, 5)
pl.plot(x, k*x+b)
graph:
based on HYRY's answer, I just update some details to make it better:
import numpy as np
import pylab as pl
Y, X = np.mgrid[-10:10:100j, -10:10:100j]
def f(x, a, b):
return x**3 + a*x + b
a = -2
b = 4
# the 1st point: 0, -2
x1 = 0
y1 = -np.sqrt(f(x1, a, b))
print(x1, y1)
# the second point
x2 = 3
y2 = np.sqrt(f(x2, a, b))
print(x2, y2)
# line: y=kl*x+bl
kl = (y2 - y1)/(x2 - x1)
bl = -x1*kl + y1 # bl = -x2*kl + y2
# y^2=x^3+ax+b , y=kl*x+bl => [-1, kl^2, 2*kl*bl, bl^2-b]
poly = np.poly1d([-1, kl**2, 2*kl*bl-a, bl**2-b])
# the roots of the poly
x = np.roots(poly)
y = np.sqrt(f(x, a, b))
print(x, y)
pl.contour(X, Y, Y**2 - f(X, a, b), levels=[0])
pl.plot(x, y, "o")
pl.plot(x, -y, "o")
x = np.linspace(-5, 5)
pl.plot(x, kl*x+bl)
And we got the roots of this poly:
[3. 2.44444444 0. ] [5. 3.7037037 2. ]