I am writing a function to calculate three nonlinear equations. I have the function to calculate them, but the thing is I give initial guess to them - which works fine. But now I have a constraint for x between (1/4 and 1/3). For that, I am making 2 for lops. One is for ptinting 13 different values, the other one for the constraint.
However the code does not give me the result at all:
import numpy as np
import math
from scipy.optimize import fsolve
def equations(vars):
x, y, z = vars
eq1 = ((x / (1 - x)) - (((2.5*np.cos(z)) / (8 * np.pi * np.sin(z) ** 2)) * (1 + (design_ratio * np.tan(z)))))
eq2 = ((y / (1 + y)) - (2.5 / (8 * np.pi * np.cos(z))) * (1 -design_ratio *( (1 / np.tan(z)))))
eq3 = np.tan(z) - ((1-x) /( 1.40 * (1+y)))
return [eq1, eq2, eq3]
n=13
for i in range(0, n):
for j in range(25555, 33333):
x = 0.00001 *x
x, y, z = fsolve(equations, (0.328, 0.048, 28))
print(x, y, z)
Frankly, using a loop is a terrible approach to handle the box constraint 1/4 <= x <= 1/3. Since fsolve doesn't support (box) constraints, you can rewrite the problem
Solve F(x,y,z) = 0 with 1/4 <= x <= 1/3
as an equivalent minimization problem
min np.sum(F(x,y,z)**2) s.t. 1/4 <= x <= 1/3
and solve it by means of scipy.optimize.minimize like this:
import numpy as np
from scipy.optimize import minimize
def F(vars):
x, y, z = vars
eq1 = ((x / (1 - x)) - (((2.5*np.cos(z)) / (8 * np.pi * np.sin(z) ** 2)) * (1 + (design_ratio * np.tan(z)))))
eq2 = ((y / (1 + y)) - (2.5 / (8 * np.pi * np.cos(z))) * (1 -design_ratio *( (1 / np.tan(z)))))
eq3 = np.tan(z) - ((1-x) /( 1.40 * (1+y)))
return np.array([eq1, eq2, eq3])
bounds = [(1./4, 1./3), (None, None), (None, None)]
res = minimize(lambda vars: np.sum(F(vars)**2), x0=(0.328, 0.048, 28), bounds=bounds)
Related
I am new to Python. I am sharing a code snippet which is a part of a code to read data from a file and make the needed calculations. I have 3 nonlinear eqs with 3 unknowns. I wrote the following code to solve it, but the code gives me an error of:
File "<ipython-input-46-bc67ad1b6153>", line 4
eq2 = (y/(1-y) - (5/(8*np.pi * np.cos(z))) * (1 - (np.cot(z)))
^
SyntaxError: invalid syntax
I do not think the syntax is wrong, but I am not sure if this kind of solution works in my case. Also, my variable "x" should be in the rage between 1/4 and 1/3
My code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
from scipy.optimize import fsolve
def equations(vars):
x, y, z = vars
eq1 = (x/(1-x) - (5/(8*np.pi*np.sin(z)**2) * (1 + 4*(np.tan(z))
eq2 = (y/(1-y) - (5/(8*np.pi * np.cos(z))) * (1 - (np.cot(z)))
eq3 = np.tan(z) - ((1-x) / 3 * (1+y))
return [eq1, eq2, eq3]
x, y,z = fsolve(equations, (1, 1, 1))
print(x, y, z)
I encountered some errors in your code:
there's no np.cot function you maybe want to use 1/np.tan(z) in equation 3.
you forgot some parentheses (eq1 and eq2) so it will never work (I might put some but i'm just guessing. you might to correct that part
your initial guess (1,1,1) causes a singularity in eq1 and eq2
so here a proposal (check parentheses)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
from scipy.optimize import fsolve
def equations(vars):
x, y, z = vars
eq1 = (x/(1-x)) - (5/(8*np.pi*np.sin(z)**2)) * (1 + 4*(np.tan(z)))
eq2 = (y/(1-y)) - (5/(8*np.pi * np.cos(z))) * (1 - (1/np.tan(z)))
eq3 = np.tan(z) - ((1-x) / 3 * (1+y))
return [eq1, eq2, eq3]
x, y,z = fsolve(equations, (2, 2, 2))
print(x, y, z)
#prints: 1.4489745262423956 26.230116359918174 1.8114278561902486
You have wrong number of parenthesis in several lines of code. Also there is no np.cot(), use 1 / np.tan() instead. Fixed working code below:
Try it online!
import numpy as np
import math
from scipy.optimize import fsolve
def equations(vars):
x, y, z = vars
eq1 = (x / (1 - x) - (5 / (8 * np.pi * np.sin(z) ** 2) * (1 + 4 * np.tan(z))))
eq2 = (y / (1 - y) - (5 / (8 * np.pi * np.cos(z))) * (1 - (1 / np.tan(z))))
eq3 = np.tan(z) - ((1-x) / 3 * (1+y))
return [eq1, eq2, eq3]
x, y,z = fsolve(equations, (1, 1, 1))
print(x, y, z)
Output:
1.0 1.0 1.0
I'm trying to integrate an equation using sympy, but the evaluation keeps erroring out with:
TypeError: cannot add <class 'sympy.matrices.immutable.ImmutableDenseMatrix'> and <class 'sympy.core.numbers.Zero'>
However, when I integrate the same equation in Mathematica, I get the result I'm looking for. I'm hoping someone can explain what is happening under the hood with sympy and how its integration differs from mathematica's.
Because there are a lot of equations involved, I am only going to post the Python representation of the equations which are used to make up the variables called in the integration. To know for certain that the integration is an issue, I have independently checked each equation involved and made sure the results match between Python and Mathematica. I can confirm that only the integration fails.
Integration variable setup in Python:
import numpy as np
import sympy as sym
from sympy import I, Matrix, integrate
from sympy.integrals import Integral
from sympy.functions import exp
from sympy.physics.vector import cross, dot
c_speed = 299792458
lambda_u = 0.01
k_u = (2 * np.pi)/lambda_u
K_0 = 0.2
gamma_0 = 2.0
beta_0 = sym.sqrt(1 - 1 / gamma_0**2)
t = sym.symbols('t')
t_start = (-2 * lambda_u) / c_speed
t_end = (3 * lambda_u) / c_speed
beta_x_of_t = sym.Piecewise( (0.0, sym.Or(t < t_start, t > t_end)),
((-1 * K_0)/gamma_0 * sym.sin(k_u * c_speed * t), sym.And(t_start <= t, t <= t_end)) )
beta_z_of_t = sym.Piecewise( (beta_0, sym.Or(t < t_start, t > t_end)),
(beta_0 * (1 - K_0**2/ (4 * beta_0 * gamma_0**2)) + K_0**2 / (4 * beta_0 * gamma_0**2) * sym.sin(2 * k_u * c_speed * t), sym.And(t_start <= t, t <= t_end)) )
beta_xp_of_t = sym.diff(beta_x_of_t, t)
beta_zp_of_t = sym.diff(beta_z_of_t, t)
Python Integration:
n = Matrix([(0, 0, 1)])
beta = Matrix([(beta_x_of_t, 0, beta_z_of_t)])
betap = Matrix([(beta_xp_of_t, 0, beta_zp_of_t)])
def rad(n, beta, betap):
return integrate(n.cross((n-beta).cross(betap)), (t, t_start, t_end))
rad(n, beta, betap)
# Output is the error above
Mathematica integration:
rad[n_, beta_, betap_] :=
NIntegrate[
Cross[n, Cross[n - beta, betap]]
, {t, tstart, tend}, AccuracyGoal -> 3]
rad[{0, 0, 1}, {betax[t], 0, betaz[t]}, {betaxp[t], 0, betazp[t]}]
# Output is {0.00150421, 0., 0.}
I did see similar question such as this one and this question, but I'm not entirely sure if they are relevant here (the second link seems to be a closer match, but not quite a fit).
As you're working with imprecise floats (and even use np.pi instead of sym.pi), while sympy tries to find exact symbolic solutions, sympy's expressions get rather wild with constants like 6.67e-11 mixed with much larger values.
Here is an attempt to use more symbolic expressions, and only integrate the x-coordinate of the function.
import sympy as sym
from sympy import I, Matrix, integrate, S
from sympy.integrals import Integral
from sympy.functions import exp
from sympy.physics.vector import cross, dot
c_speed = 299792458
lambda_u = S(1) / 100
k_u = (2 * sym.pi) / lambda_u
K_0 = S(2) / 100
gamma_0 = 2
beta_0 = sym.sqrt(1 - S(1) / gamma_0 ** 2)
t = sym.symbols('t')
t_start = (-2 * lambda_u) / c_speed
t_end = (3 * lambda_u) / c_speed
beta_x_of_t = sym.Piecewise((0, sym.Or(t < t_start, t > t_end)),
((-1 * K_0) / gamma_0 * sym.sin(k_u * c_speed * t), sym.And(t_start <= t, t <= t_end)))
beta_z_of_t = sym.Piecewise((beta_0, sym.Or(t < t_start, t > t_end)),
(beta_0 * (1 - K_0 ** 2 / (4 * beta_0 * gamma_0 ** 2)) + K_0 ** 2 / (
4 * beta_0 * gamma_0 ** 2) * sym.sin(2 * k_u * c_speed * t),
sym.And(t_start <= t, t <= t_end)))
beta_xp_of_t = sym.diff(beta_x_of_t, t)
beta_zp_of_t = sym.diff(beta_z_of_t, t)
n = Matrix([(0, 0, 1)])
beta = Matrix([(beta_x_of_t, 0, beta_z_of_t)])
betap = Matrix([(beta_xp_of_t, 0, beta_zp_of_t)])
func = n.cross((n - beta).cross(betap))[0]
print(integrate(func, (t, t_start, t_end)))
This doesn't give an error, and outputs just zero.
Lambdify can be used to convert the function to numpy, and plot it.
func_np = sym.lambdify(t, func)
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import quad
t_start_np = float(t_start.evalf())
t_end_np = float(t_end.evalf())
ts = np.linspace(t_start_np, t_end_np, 100000)
plt.plot(ts, func_np(ts))
print("approximate integral (np.trapz):", np.trapz(ts, func_np(ts)))
print("approximate integral (scipy's quad):", quad(func_np, t_start_np, t_end_np))
This outputs:
approximate integral (np.trapz): 0.04209721470548062
approximate integral (scipy's quad): (-2.3852447794681098e-18, 5.516333374450447e-10)
Note the huge values on the y-axis, and the small values on the x-axis. These values, as well as matematica's, could well be rounding errors.
So I've been trying to fit to an exponentially modified gaussian function (if interested, https://en.wikipedia.org/wiki/Exponentially_modified_Gaussian_distribution)
import numpy as np
import scipy.optimize as sio
import scipy.special as sps
def exp_gaussian(x, h, u, c, t):
z = 1.0/sqrt(2.0) * (c/t - (x-u)/c) #not important
k1 = k2 = h * c / t * sqrt(pi / 2) #not important
n1 = 1/2 * (c / t)**2 - (x-u)/t #not important
n2 = -1 / 2 * ((x - u) / c)**2 #not important
y = np.zeros(len(x))
y += (k1 * np.exp(n1) * sps.erfc(z)) * (z < 0)
y += (k2 * np.exp(n2) * sps.erfcx(z)) * (z >= 0)
return y
In order to prevent overflow problems, one of two equivilent functions must be used depending on whether z is positive or negative (see Alternative forms for computation from previous wikipedia page).
The problem I am having is this: The line y += (k2 * np.exp(n2) * sps.erfcx(z)) * (z >= 0)is only supposed to add to y when z is positive. But if z is, say, -30, sps.erfcx(-30) is inf, and inf * False is NaN. Therefore, instead of leaving y untouched, the resulting y is clustered with NaN. Example:
x = np.linspace(400, 1000, 1001)
y = exp_gaussian(x, 100, 400, 10, 5)
y
array([ 84.27384586, 86.04516723, 87.57518493, ..., nan,
nan, nan])
I tried the replacing the line in question with the following:
y += numpy.nan_to_num((k2 * np.exp(n2) * sps.erfcx(z)) * (z >= 0))
But doing this ran into serious runtime issues. Is there a way to only evaluate (k2 * np.exp(n2) * sps.erfcx(z)) on the condition that (z >= 0) ? Is there some other way to solve this without sacrificing runtime?
Thanks!
EDIT: After Rishi's advice, the following code seems to work much better:
def exp_gaussian(x, h, u, c, t):
z = 1.0/sqrt(2.0) * (c/t - (x-u)/c)
k1 = k2 = h * c / t * sqrt(pi / 2)
n1 = 1/2 * (c / t)**2 - (x-u)/t
n2 = -1 / 2 * ((x - u) / c)**2
return = np.where(z >= 0, k2 * np.exp(n2) * sps.erfcx(z), k1 * np.exp(n1) * sps.erfc(z))
How about using numpy.where with something like: np.where(z >= 0, sps.erfcx(z), sps.erfc(z)). I'm no numpy expert, so don't know if it's efficient. Looks elegant at least!
One thing you could do is create a mask and reuse it so it wouldn't need to be evaluated twice. Another idea is to use the nan_to_num only once at the end
mask = (z<0)
y += (k1 * np.exp(n1) * sps.erfc(z)) * (mask)
y += (k2 * np.exp(n2) * sps.erfcx(z)) * (~mask)
y = numpy.nan_yo_num(y)
Try and see if this helps...
Consider following equation system:
1623.66790917 * x ** 2 + 468.829686367 * x * y + 252.762128419 * y ** 2 + -1027209.42116 * x + -301192.975791 * y + 188804356.212 = 0
11154.1759415 * x ** 2 + 31741.0229155 * x * y + 32933.5622632 * y ** 2 + -16226174.4037 * x + -26323622.7497 * y + 6038609721.67 = 0
As you see there are two pairs of complex solutions to the system. I tried sympy but it was not a success. I want to know how to figure it out in Python. BTW I don't have a nice initial guess to use numeric methods.
You can solve those equations numerically using mpmath's findroot(). As far as I know there isn't a way to tell findroot() to find multiple roots, but we can get around that restriction: first, find a solution pair (xa, ya), then divide the equations by (x - xa)*(y - ya). You do have to supply an initial approximate solution, but I managed to find something that worked in only a few tries.
from mpmath import mp
mp.dps = 30
prec = 20
f_1 = lambda x, y: 1623.66790917 * x ** 2 + 468.829686367 * x * y + 252.762128419 * y ** 2 + -1027209.42116 * x + -301192.975791 * y + 188804356.212
f_2 = lambda x, y: 11154.1759415 * x ** 2 + 31741.0229155 * x * y + 32933.5622632 * y ** 2 + -16226174.4037 * x + -26323622.7497 * y + 6038609721.67
def solve(f1, f2, initxy):
return mp.findroot([f1, f2], initxy, solver='muller')
def show(x, y):
print 'x=', mp.nstr(x, prec)
print 'y=', mp.nstr(y, prec)
print mp.nstr(f_1(x, y), prec)
print mp.nstr(f_2(x, y), prec)
print
f1a = f_1
f2a = f_2
xa, ya = solve(f1a, f2a, (240+40j, 265-85j))
show(xa, ya)
f1b = lambda x, y: f1a(x, y) / ((x - xa) * (y - ya))
f2b = lambda x, y: f2a(x, y) / ((x - xa) * (y - ya))
xb, yb = solve(f1b, f2b, (290+20j, 270+30j))
show(xb, yb)
output
x= (246.82064795986653023 + 42.076841530787279711j)
y= (261.83565021239842638 - 81.555049135736951496j)
(0.0 + 3.3087224502121106995e-24j)
(0.0 + 0.0j)
x= (289.31873055121622967 + 20.548128321524345062j)
y= (272.23440694481666637 + 29.381152413744722108j)
(0.0 + 3.3087224502121106995e-24j)
(0.0 + 0.0j)
How can I do a maximum likelihood regression using scipy.optimize.minimize? I specifically want to use the minimize function here, because I have a complex model and need to add some constraints. I am currently trying a simple example using the following:
from scipy.optimize import minimize
def lik(parameters):
m = parameters[0]
b = parameters[1]
sigma = parameters[2]
for i in np.arange(0, len(x)):
y_exp = m * x + b
L = sum(np.log(sigma) + 0.5 * np.log(2 * np.pi) + (y - y_exp) ** 2 / (2 * sigma ** 2))
return L
x = [1,2,3,4,5]
y = [2,3,4,5,6]
lik_model = minimize(lik, np.array([1,1,1]), method='L-BFGS-B', options={'disp': True})
When I run this, convergence fails. Does anyone know what is wrong with my code?
The message I get running this is 'ABNORMAL_TERMINATION_IN_LNSRCH'. I am using the same algorithm that I have working using optim in R.
Thank you Aleksander. You were correct that my likelihood function was wrong, not the code. Using a formula I found on wikipedia I adjusted the code to:
import numpy as np
from scipy.optimize import minimize
def lik(parameters):
m = parameters[0]
b = parameters[1]
sigma = parameters[2]
for i in np.arange(0, len(x)):
y_exp = m * x + b
L = (len(x)/2 * np.log(2 * np.pi) + len(x)/2 * np.log(sigma ** 2) + 1 /
(2 * sigma ** 2) * sum((y - y_exp) ** 2))
return L
x = np.array([1,2,3,4,5])
y = np.array([2,5,8,11,14])
lik_model = minimize(lik, np.array([1,1,1]), method='L-BFGS-B')
plt.scatter(x,y)
plt.plot(x, lik_model['x'][0] * x + lik_model['x'][1])
plt.show()
Now it seems to be working.
Thanks for the help!