Solve nonlinear equation in python - python

I am trying to find the fundamental TE mode of the dielectric waveguide. The way I try to solve it is to compute two function and try to find their intersection on graph. However, I am having trouble get the intersect point on the plot.
My code:
def LHS(w):
theta = 2*np.pi*1.455*10*10**(-6)*np.cos(np.deg2rad(w))/(900*10**(-9))
if(theta>(np.pi/2) or theta < 0):
y1 = 0
else:
y1 = np.tan(theta)
return y1
def RHS(w):
y = ((np.sin(np.deg2rad(w)))**2-(1.440/1.455)**2)**0.5/np.cos(np.deg2rad(w))
return y
x = np.linspace(80, 90, 500)
LHS_vals = [LHS(v) for v in x]
RHS_vals = [RHS(v) for v in x]
# plot
fig, ax = plt.subplots(1, 1, figsize=(6,3))
ax.plot(x,LHS_vals)
ax.plot(x,RHS_vals)
ax.legend(['LHS','RHS'],loc='center left', bbox_to_anchor=(1, 0.5))
plt.ylim(0,20)
plt.xlabel('degree')
plt.ylabel('magnitude')
plt.show()
I got plot like this:
The intersect point is around 89 degree, however, I am having trouble to compute the exact value of x. I have tried fsolve, solve to find the solution but still in vain. It seems not able to print out solution if it is not the only solution. Is it possible to only find solution that x is in certain range? Could someone give me any suggestion here? Thanks!
edit:
the equation is like this (m=0):
and I am trying to solve theta here by finding the intersection point
edit:
One of the way I tried is as this:
from scipy.optimize import fsolve
def f(wy):
w, y = wy
z = np.array([y - LHS(w), y - RHS(w)])
return z
fsolve(f,[85, 90])
However it gives me the wrong answer.
I also tried something like this:
import matplotlib.pyplot as plt
x = np.arange(85, 90, 0.1)
f = LHS(x)
g = RHS(x)
plt.plot(x, f, '-')
plt.plot(x, g, '-')
idx = np.argwhere(np.diff(np.sign(f - g)) != 0).reshape(-1) + 0
plt.plot(x[idx], f[idx], 'ro')
print(x[idx])
plt.show()
But it shows:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

First, you need to make sure that the function can actually handle numpy arrays. Several options for defining piecewise functions are shown in
Plot Discrete Distribution using np.linspace(). E.g.
def LHS(w):
theta = 2*np.pi*1.455*10e-6*np.cos(np.deg2rad(w))/(900e-9)
y1 = np.select([theta < 0, theta <= np.pi/2, theta>np.pi/2], [np.nan, np.tan(theta), np.nan])
return y1
This already allows to use the second approach, plotting a point at the index which is closest to the minimum of the difference between the two curves.
import numpy as np
import matplotlib.pyplot as plt
def LHS(w):
theta = 2*np.pi*1.455*10e-6*np.cos(np.deg2rad(w))/(900e-9)
y1 = np.select([theta < 0, theta <= np.pi/2, theta>np.pi/2], [np.nan, np.tan(theta), np.nan])
return y1
def RHS(w):
y = ((np.sin(np.deg2rad(w)))**2-(1.440/1.455)**2)**0.5/np.cos(np.deg2rad(w))
return y
x = np.linspace(82.1, 89.8, 5000)
f = LHS(x)
g = RHS(x)
plt.plot(x, f, '-')
plt.plot(x, g, '-')
idx = np.argwhere(np.diff(np.sign(f - g)) != 0).reshape(-1) + 0
plt.plot(x[idx], f[idx], 'ro')
plt.ylim(0,40)
plt.show()
One may then also use scipy.optimize.fsolve to get the actual solution.
idx = np.argwhere(np.diff(np.sign(f - g)) != 0)[-1]
from scipy.optimize import fsolve
h = lambda x: LHS(x)-RHS(x)
sol = fsolve(h,x[idx])
plt.plot(sol, LHS(sol), 'ro')
plt.ylim(0,40)
plt.show()

Something quick and (very) dirty that seems to work (at least it gave theta value of ~89 for your parameters) - add the following to your code before the figure, after RHS_vals = [RHS(v) for v in x] line:
# build a list of differences between the values of RHS and LHS
diff = [abs(r_val- l_val) for r_val, l_val in zip(RHS_vals, LHS_vals)]
# find the minimum of absolute values of the differences
# find the index of this minimum difference, then find at which angle it occured
min_diff = min(diff)
print "Minimum difference {}".format(min_diff)
print "Theta = {}".format(x[diff.index(min_diff)])
I looked in range of 85-90:
x = np.linspace(85, 90, 500)

Related

How to find crossection of hline and a function in python?

I have created a plot like in the figure below, red plot is original data, blue is a fitted function, the horizontal lines are different levels. I need to find the both intersection points with each line. do you have any suggestions ? Thanks In advance.
An easy way to do this numerically is to subtract the y-value of each horizontal line from your fit and then to solve the equation
fit(x) - y = 0 for x.
For this, scipy.optimize.fsolve can be used as follows (https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fsolve.html):
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve # To find the zeros
from scipy.stats import cauchy
def my_fit(x):
# dummy function for this example
return cauchy().pdf(x)
horizontal_lines = [0.05, 0.15, 0.25]
colors = ['r', 'g', 'm']
x = np.linspace(-5, 5, 1000)
plt.plot(x, my_fit(x))
plt.hlines(horizontal_lines, -5, 5, ls="--", color=colors)
for i, y in enumerate(horizontal_lines):
x0 = fsolve(lambda x: my_fit(x) - y, -1)
x1 = fsolve(lambda x: my_fit(x) - y, 1)
print(f"Intersection points for {y=}: {x0=} {x1=}")
plt.scatter(x0, my_fit(x0), color=colors[i])
plt.scatter(x1, my_fit(x1), color=colors[i])
Output:
Intersection points for y=0.05: x0=array([-2.3165055]) x1=array([2.3165055])
Intersection points for y=0.15: x0=array([-1.05927612]) x1=array([1.05927612])
Intersection points for y=0.25: x0=array([-0.5227232]) x1=array([0.5227232])
A simple solution to this would be to just calculate the intersection points knowing the functions of the lines.
Example:
Line 1 : y = 2x
Line 2 : y = x^2
Intersection points:
2x = x^2
0 = x^2 - 2x
x1 = 2
x2 = 0
For y just substitute x in one of the functions,
y1 = 2x y1 = 2*(2) y1 = 4
y2 = 2x y2 = 2*(0) y2 = 0
Intersection point 1 of line 1 and line 2:
Itersection point 1: (2, 4)
Intersection point 2: (0,0)
enter image description here
Hope this helps.
The simplest non-mathematical solution would be:
take all points with y > y0
take the largest and smallest x from the remaining list.
The result is approximately correct for high point density.

given percentiles find distribution function python

From https://stackoverflow.com/a/30460089/2202107, we can generate CDF of a normal distribution:
import numpy as np
import matplotlib.pyplot as plt
N = 100
Z = np.random.normal(size = N)
# method 1
H,X1 = np.histogram( Z, bins = 10, normed = True )
dx = X1[1] - X1[0]
F1 = np.cumsum(H)*dx
#method 2
X2 = np.sort(Z)
F2 = np.array(range(N))/float(N)
# plt.plot(X1[1:], F1)
plt.plot(X2, F2)
plt.show()
Question: How do we generate the "original" normal distribution, given only x (eg X2) and y (eg F2) coordinates?
My first thought was plt.plot(x,np.gradient(y)), but gradient of y was all zero (data points are evenly spaced in y, but not in x) These kind of data is often met in percentile calculations. The key is to get the data evenly space in x and not in y, using interpolation:
x=X2
y=F2
num_points=10
xinterp = np.linspace(-2,2,num_points)
yinterp = np.interp(xinterp, x, y)
# for normalizing that sum of all bars equals to 1.0
tot_val=1.0
normalization_factor = tot_val/np.trapz(np.ones(len(xinterp)),yinterp)
plt.bar(xinterp, normalization_factor * np.gradient(yinterp), width=0.2)
plt.show()
output looks good to me:
I put my approach here for examination. Let me know if my logic is flawed.
One issue is: when num_points is large, the plot looks bad, but it's a issue in discretization, not sure how to avoid it.
Related posts:
I failed to understand why the answer was so complicated in https://stats.stackexchange.com/a/6065/131632
I also didn't understand why my approach was different than Generate distribution given percentile ranks

How can I make the contours of quadratic form using matplotlib?

I have the following quadratic form f(x) = x^T A x - b^T x and i've used numpy to define my matrices A, b:
A = np.array([[4,3], [3,7]])
b = np.array([3,-7])
So we're talking about 2 dimensions here, meaning that the contour plot will have the axes x1 and x2 and I want these to span from -4 to 4.
I've tried to experiment by doing
u = np.linspace(-4,4,100)
x, y = np.meshgrid(u,u)
in order to create the 2 axis x1 and x2 but then I dont know how to define my function f(x) and if I do plt.contour(x,y,f) it won't work because the function f(x) is defined with only x as an argument.
Any ideas would be greatly appreciated. Thanks !
EDIT : I managed to "solve" the problem by doing the operations between the quadratic form , for example x^T A x, and ended up with a function of x1,x2 where these are the components of x vector. After that I did
u = np.linspace(-4,4,100)
x, y = np.meshgrid(u,u)
z = 1.5*(x**2) + 3*(y**2) - 2*x + 8*y + 2*x*y #(thats the function i ended up with)
plt.contour(x, y, z)
If Your transformation matrices A, b look like
A = np.array([[4,3], [3,7]])
b = np.array([3,-7])
and Your data look like
u = np.linspace(-4,4,100)
x, y = np.meshgrid(u,u)
x.shape
x and y will have the shapes (100,100).
You can define f(x) as
def f(x):
return np.dot(np.dot(x.T,A),x) - np.dot(b,x)
to then input anything with the shape (2, N) into the function f.
I am unfortunately not sure, which values You want to feed into it.
But one example would be: [(-4:4), (-4:4)]
plt.contour(x, y, f(x[0:2,:]))
update
If the visualization of the contour plot does not fit Your purpose, You can use other plots, e.g. 3D visualizations.
from mpl_toolkits.mplot3d import Axes3D # This import has side effects required for the kwarg projection='3d' in the call to fig.add_subplot
fig = plt.figure(figsize=(40,20))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x,y, f(x[0:2,:]))
plt.show()
If You expect other values in the z-dimension, the projection f might be off.
For other 3d plots see: https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html
you could try something like this:
import numpy as np
import matplotlib.pyplot as plt
A = np.array([[4,3], [3,7]])
n_points = 100
u = np.linspace(-4, 4, n_points)
x, y = np.meshgrid(u, u)
X = np.vstack([x.flatten(), y.flatten()])
f_x = np.dot(np.dot(X.T, A), X)
f_x = np.diag(f_x).reshape(n_points, n_points)
plt.figure()
plt.contour(x, y, f_x)
Another alternative is to compute f_x as follows.
f_x = np.zeros((n_points, n_points))
for i in range(n_points):
for j in range(n_points):
in_v = np.array([[x[i][j]], [y[i][j]]])
f_x[i][j] = np.dot(np.dot(in_v.T, A), in_v)

Plot a piecewise function in 3D

I am new to python and trying to 3d plot a piecewise function. I am trying to 3d plot the 'mainformula' function below on the z-axis as it varies with x and y ranging from 0 to 10, and constant = 1. But I can't quite seem to figure out the plotting method here.
from sympy import *
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
def mainformula(x,y,constant):
return Piecewise((subformula1(x,y,constant), y >= 0 and y < 3),(subformula2(x,y,constant),y>=3 and y <= 10))
def subformula1(x,y,constant):
return x + y + constant
def subformula2(x,y,constant):
return x - y - constant
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 10, 0.25)
Y = np.arange(0, 10, 0.25)
constant = 1
X, Y = np.meshgrid(X, Y)
Z = mainformula(X,Y,constant)
surf = ax.plot_surface(X, Y, Z)
plt.show()
The error I get when I run that code is: "ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
You are working on arrays so it's never going to work to use array > 3 in a boolean context (for example with and) this will always give you the error you received. But you can always define your conditions as boolean masks and operate your formula on the appropriate elements:
def mainformula(x,y,constant):
z = np.zeros_like(x)
# Condition 1 indexes all elements where subformula 1 is valid
condition1 = np.logical_and(y >= 0, y < 3)
# condition1 = (y >= 0) & (y < 3) # is another way of writing it
z[condition1] = x[condition1] + y[condition1] + constant
# now do it in the range where subformula 2 is valid
condition2 = np.logical_and(y >= 3, y <= 10)
# condition1 = (y >= 3) & (y <= 10) # is another way of writing it
z[condition2] = x[condition2] - y[condition2] - constant
return z
This doesn't use sympy.Piecewise but works alright when only trying to plot. If you want seperate functions instead of doing it all in the main formula you need to change it a bit:
z[condition1] = subformula1(x[condition1], y[condition1], constant)
and similar for condition2.
The problem is you are trying to make logic assertions into arrays that do not have a direct one. It's difficult for Python to assert what this is:
y >= 0 and y < 3
So you'll have to change somewhat your code into something it can understand:
def mainformula(x,y,constant):
y2 = y[y>=0]
y2 = y2[y2<3]
y3 = y[y>=3]
y3 = y2[y2<=10]
return Piecewise((subformula1(x,y,constant), y2),(subformula2(x,y,constant),y3))
The problem is Piecewise function also does not seem to accept some arrays into one of the arguments you have. You'll have to rethink your problem, perhaps by building a loop for the Piecewise function in order to be launched at every element.

Parametric plot of solution of 2x2 diff. system in python, Mathematica

I've implemented a solution to the following system of equations
dy/dt = -t*y(t) - x(t)
dx/dt = 2*x(t) - y(t)^3
y(0) = x(0) = 1.
0 <= t <= 20
firstly in Mathematica and afterwards in Python.
My code in Mathematica:
s = NDSolve[
{x'[t] == -t*y[t] - x[t], y'[t] == 2 x[t] - y[t]^3, x[0] == y[0] == 1},
{x, y}, {t, 20}]
ParametricPlot[Evaluate[{x[t], y[t]} /. s], {t, 0, 20}]
From that I get the following plot: Plot1 (if it gives a 403 Forbidden message please press enter inside the url field)
Later on I coded the same into python:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
g = lambda t: t
def f(z,t):
xi = z[0]
yi = z[1]
gi = z[2]
f1 = -gi*yi-xi
f2 = 2*xi-yi**3
return [f1,f2]
# Initial Conditions
x0 = 1.
y0 = 1.
g0 = g(0)
z0 = [x0,y0,g0]
t= np.linspace(0,20.,1000)
# Solve the ODEs
soln = odeint(f,z0,t)
x = soln[:,0]
y = soln[:,1]
plt.plot(x,y)
plt.show()
And this is the plot I get:
Plot2 (if it gives a 403 Forbidden message please press enter inside the url field)
If one plots again the Mathematica solution in a smaller field:
ParametricPlot[Evaluate[{x[t], y[t]} /. s], {t, 0, 6}]
he will get a similar result to the python solution. Only the axis' will be misplaced.
Why is there such a big difference in the plots? What am I doing wrong?
I suspect that my python implementation of the model is wrong, especially where f1 is calculated. Or maybe the plot() function isn't handy at all for plotting parametric equations as in this case.
Thanks.
ps: sorry for making your life hard by not slapping the images inside the text; I don't have enough reputation yet.
You're using t as your third parameter in the input vector, not as a separate parameter. The t in f(z,t) is never used; instead, you use z[2], which will not equal the range of t as you define before (t=np.linspace(0,20.,1000)). The lambda function for g won't help here: you only use it once to set a t0, but never after.
Simplify your code, and remove that third parameter from your input vector (as well as the lambda function). For example:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def f(z,t):
xi = z[0]
yi = z[1]
f1 = -t*yi-xi
f2 = 2*xi-yi**3
return [f1,f2]
# Initial Conditions
x0 = 1.
y0 = 1.
#t= np.linspace(0,20.,1000)
t = np.linspace(0, 10., 100)
# Solve the ODEs
soln = odeint(f,[x0,y0],t)
x = soln[:,0]
y = soln[:,1]
ax = plt.axes()
#plt.plot(x,y)
plt.plot(t,x)
# Put those axes at their 0 value position
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
#plt.axis([-0.085, 0.085, -0.05, 0.07])
plt.show()
I have commented out the actual plot you want, and instead I'm plotting x versus t, what you have in the comments, since I feel that makes it easier to see things are correct now. The figure I get:

Categories