I currently have a function that take a lot of parameters (let's call them
*lot, a, b), do a lot of stuff and return 3 float (x, y , z).
What I'm trying to do is to minimize x by varying a and b and puting some constraint to a, b, y and z (a and b are limited with a min and a max value while y and z just have a min value).
If I just explore my search area I can see that the surface that form all the x is in fact a bowl, so my result is linear (or am I that wrong?) if my result is linear I can just use a simplex to find my minimum allowed with my constraint (or Am i wrong here?).
Now my problem: In python, using optimize.minimize from scipy it seems that I can't use constraint with simplex (except maybe min and max on a and b but that's not the biggest issue here) so did I misunderstand completly the minimize function? Do I really need something else than simplex or is there another solution than the optimize function?
Here is some kind of code snippet that can emulate my function:
# coding=utf-8
def bowl(a,b):
y = a*b
z = a/b
x = (a*a)/(y*y) + (b*b)/(z*z)
return x,y,z
def surface(min_a, max_a, min_b, max_b):
for a in range(min_a,max_a):
buffer = list()
for b in range(min_b, max_b):
buffer.append(bowl(a,b))
print(buffer)
surface(1,10,1,10)
Related
I'm sorry if this is an unusual way to write a question here, since the scope of it seems quite large (to me). I'd be happy to be directed to pre-defined packages that already do what I'm needing, indeed I hope there is (should be?) a standardised solution to my question. I was wondering if there is any help out there? I'm still learning Python through a project I'm doing, and I feel I'm slightly weak on certain points...
Ok so here goes: I would like to invert a 2D --> 2D function in python, but none of my efforts have succeeded yet.
Let's say I have two relationships in a (non-linear) systems of equations, so
a = f(x,y)
b = g(x,y)
where both f and g are continuous and invertible, and x and y have a certain pre-defined rectangular domain. a and b also have their own rectangular domain, but it is different from that of x and y.
Some extra info on f and g: One of the functions will be linear, let's call this f. So, a = f(x,y) = qx + py + r (where q, p and r are known constants). In Python terms, I guess you would write a[ i, j ] = qx[ i ] + py[ j ]. The other function, g, has no analytic expression but looks similar to ksin(x) + lsin(y), for x and y between 0 and pi/2.
Moreover, the overall "mother-function" that I wish to make a 3D surface plot of, takes a and b as arguments. Calling the mother function M, we then have that M = M(a,b) = M(f(x,y),g(x,y)). So far so good.
The essence of the problem is that I need to first choose a pair (a,b) on the "mother-grid", then find the corresponding pair (x,y) that gives rise to this particular (a,b). f and g do not have any analytically "simple" inverses however, and I need to find these numerically.
So the basic question is, "given a[ i ] and b[ j ] as two sorted lists, and given x[ ii ] and y [ jj ] that are used to obtain each a and b, how do I find the two inverse functions x = inv1 (a,b) and y = inv2 (a,b)?"
PS. I have tried the "cheap way" of circumventing this problem by first choosing a (x,y) pair, calculating a tentative (A,B) pair and then interpolate this into the pre-defined (a,b) mesh as best as I could. However, since the (x,y) mesh and the (a,b) meshes are (quite) different, the corresponding "fitting error" always make the end result come out jagged and messy (I have a control scenario where I know what the end result should look like, before doing my own cases). This is because I am essentially forcing the values of A and B onto the height of the M function at position (a,b) if that makes sense. I've tried averaging and smoothing "cheats" to this, but it is still not passable imo. Hence, I really need to choose an (a,b) pair FIRST, and then only finding the relevant (x,y) pair after that.
Note: Some parameters in the M-function depends directly on x and y, hence the need for knowing the exact values of x and y.
Thanks for the additional information! I think you can solve this directly and then analytically.
Starting with your final result (a, b):
First solve against a to find your x-y line, e.g.:
a = qx + py + r
y = (qx + r - a) / -p
Since you said it is monotonically increasing, I just solve for y for simplicity.
Next, plug this into your non-analytic g using a simple binary search across x:
def invert(a, b):
def get_y(x):
return (Q * x + R - a) / -P
def g_constrained(x):
return g(x, func(x))
x = binary_search(g_constrained, b, min_x, max_x, guess_x)
y = get_y(x)
return x, y
Note that your function is not guaranteed to have a single solution in general, consider a planar solution for g that looks like an arc, since f is a line, you can have two intersections. You will need to decide what you want to do with this information.
Previous concerns
I am suspicious of your claim that a = f(x,y) is continuous and invertible.
Put succinctly: if your function z = f(x, y) doesn't intersect the plane z = K in exactly one point for every K, it is not invertible.
For a detailed example:
Consider some point, and 4 points around it - here I use (0,0) and unit length, for convenience.
z = f(0,0)
a = f(-1,0)
b = f(1,0)
p = f(0,-1)
q = f(0,1)
If f provides a scalar value (or anything where x < y and y < z implies x < z), then we have a problem.
Since f is continuous and invertible, either a < z < b or b < z < a, and likewise for p and q. So f-inv(z+) will map to two different values, one on the line (-1, 0) -> (1, 0) and one on the line (0, -1) -> (0, 1).
I'm trying to implement the following formula in python for X and Y points
I have tried following approach
def f(c):
"""This function computes the curvature of the leaf."""
tt = c
n = (tt[0]*tt[3] - tt[1]*tt[2])
d = (tt[0]**2 + tt[1]**2)
k = n/d
R = 1/k # Radius of Curvature
return R
There is something incorrect as it is not giving me correct result. I think I'm making some mistake while computing derivatives in first two lines. How can I fix that?
Here are some of the points which are in a data frame:
pts = pd.DataFrame({'x': x, 'y': y})
x y
0.089631 97.710199
0.089831 97.904541
0.090030 98.099313
0.090229 98.294513
0.090428 98.490142
0.090627 98.686200
0.090827 98.882687
0.091026 99.079602
0.091225 99.276947
0.091424 99.474720
0.091623 99.672922
0.091822 99.871553
0.092022 100.070613
0.092221 100.270102
0.092420 100.470020
0.092619 100.670366
0.092818 100.871142
0.093017 101.072346
0.093217 101.273979
0.093416 101.476041
0.093615 101.678532
0.093814 101.881451
0.094013 102.084800
0.094213 102.288577
pts_x = np.gradient(x_c, t) # first derivatives
pts_y = np.gradient(y_c, t)
pts_xx = np.gradient(pts_x, t) # second derivatives
pts_yy = np.gradient(pts_y, t)
After getting the derivatives I am putting the derivatives x_prim, x_prim_prim, y_prim, y_prim_prim in another dataframe using the following code:
d = pd.DataFrame({'x_prim': pts_x, 'y_prim': pts_y, 'x_prim_prim': pts_xx, 'y_prim_prim':pts_yy})
after having everything in the data frame I am calling function for each row of the data frame to get curvature at that point using following code:
# Getting the curvature at each point
for i in range(len(d)):
temp = d.iloc[i]
c_temp = f(temp)
curv.append(c_temp)
You do not specify exactly what the structure of the parameter pts is. But it seems that it is a two-dimensional array where each row has two values x and y and the rows are the points in your curve. That itself is problematic, since the documentation is not quite clear on what exactly is returned in such a case.
But you clearly are not getting the derivatives of x or y. If you supply only one array to np.gradient then numpy assumes that the points are evenly spaced with a distance of one. But that is probably not the case. The meaning of x' in your formula is the derivative of x with respect to t, the parameter variable for the curve (which is separate from the parameters to the computer functions). But you never supply the values of t to numpy. The values of t must be the second parameter passed to the gradient function.
So to get your derivatives, split the x, y, and t values into separate one-dimensional arrays--lets call them x and y and t. Then get your first and second derivatives with
pts_x = np.gradient(x, t) # first derivatives
pts_y = np.gradient(y, t)
pts_xx = np.gradient(pts_x, t) # second derivatives
pts_yy = np.gradient(pts_y, t)
Then continue from there. You no longer need the t values to calculate the curvatures, which is the point of the formula you are using. Note that gradient is not really designed to calculate the second derivatives, and it absolutely should not be used to calculate third or higher-order derivatives. More complex formulas are needed for those. Numpy's gradient uses "second order accurate central differences" which are pretty good for the first derivative, poor for the second derivative, and worthless for higher-order derivatives.
I think your problem is that x and y are arrays of double values.
The array x is the independent variable; I'd expect it to be sorted into ascending order. If I evaluate y[i], I expect to get the value of the curve at x[i].
When you call that numpy function you get an array of derivative values that are the same shape as the (x, y) arrays. If there are n pairs from (x, y), then
y'[i] gives the value of the first derivative of y w.r.t. x at x[i];
y''[i] gives the value of the second derivative of y w.r.t. x at x[i].
The curvature k will also be an array with n points:
k[i] = abs(x'[i]*y''[i] -y'[i]*x''[i])/(x'[i]**2 + y'[i]**2)**1.5
Think of x and y as both being functions of a parameter t. x' = dx/dt, etc. This means curvature k is also a function of that parameter t.
I like to have a well understood closed form solution available when I program a solution.
y(x) = sin(x) for 0 <= x <= pi
y'(x) = cos(x)
y''(x) = -sin(x)
k = sin(x)/(1+(cos(x))**2)**1.5
Now you have a nice formula for curvature as a function of x.
If you want to parameterize it, use
x(t) = pi*t for 0 <= t <= 1
x'(t) = pi
x''(t) = 0
See if you can plot those and make your Python solution match it.
I have an equation in 4 variables x, v, eta(e), beta(b). I want to substitute x and v with various values and create an equation by multiplying all the obtained terms.
Image of Code in sympy
Using SYMPY, How to solve the obtained equation in 2 variables(e and b) taking a log and differentiating with respect to e and then b?
I am taking 'ln' so that the multiplicative terms get simplified to additive terms and hence exponential terms get eliminated. But, it is not happening. It just writes the word log in front but doesn't expand using the property ln(mn) = ln(m) + ln(n)
Following is the output in console:
Output in console when I run code shown in previous image
You may need to set your various symbols as positive, like x = symbols('x', positive=True). Identities like log(x*y) == log(x) + log(y) and log(exp(x)) == x are not true in general, but they are true when the variables are positive.
I want to solve this kind of problem:
dy/dt = 0.01*y*(1-y), find t when y = 0.8 (0<t<3000)
I've tried the ode function in Python, but it can only calculate y when t is given.
So are there any simple ways to solve this problem in Python?
PS: This function is just a simple example. My real problem is so complex that can't be solve analytically. So I want to know how to solve it numerically. And I think this problem is more like an optimization problem:
Objective function y(t) = 0.8, Subject to dy/dt = 0.01*y*(1-y), and 0<t<3000
PPS: My real problem is:
objective function: F(t) = 0.85,
subject to: F(t) = sqrt(x(t)^2+y(t)^2+z(t)^2),
x''(t) = (1/F(t)-1)*250*x(t),
y''(t) = (1/F(t)-1)*250*y(t),
z''(t) = (1/F(t)-1)*250*z(t)-10,
x(0) = 0, y(0) = 0, z(0) = 0.7,
x'(0) = 0.1, y'(0) = 1.5, z'(0) = 0,
0<t<5
This differential equation can be solved analytically quite easily:
dy/dt = 0.01 * y * (1-y)
rearrange to gather y and t terms on opposite sides
100 dt = 1/(y * (1-y)) dy
The lhs integrates trivially to 100 * t, rhs is slightly more complicated. We can always write a product of two quotients as a sum of the two quotients * some constants:
1/(y * (1-y)) = A/y + B/(1-y)
The values for A and B can be worked out by putting the rhs on the same denominator and comparing constant and first order y terms on both sides. In this case it is simple, A=B=1. Thus we have to integrate
1/y + 1/(1-y) dy
The first term integrates to ln(y), the second term can be integrated with a change of variables u = 1-y to -ln(1-y). Our integrated equation therefor looks like:
100 * t + C = ln(y) - ln(1-y)
not forgetting the constant of integration (it is convenient to write it on the lhs here). We can combine the two logarithm terms:
100 * t + C = ln( y / (1-y) )
In order to solve t for an exact value of y, we first need to work out the value of C. We do this using the initial conditions. It is clear that if y starts at 1, dy/dt = 0 and the value of y never changes. Thus plug in the values for y and t at the beginning
100 * 0 + C = ln( y(0) / (1 - y(0) )
This will give a value for C (assuming y is not 0 or 1) and then use y=0.8 to get a value for t. Note that because of the logarithm and the factor 100 multiplying t y will reach 0.8 within a relatively short range of t values, unless the initial value of y is incredibly small. It is of course also straightforward to rearrange the equation above to express y in terms of t, then you can plot the function as well.
Edit: Numerical integration
For a more complexed ODE which cannot be solved analytically, you will have to try numerically. Initially we only know the value of the function at zero time y(0) (we have to know at least that in order to uniquely define the trajectory of the function), and how to evaluate the gradient. The idea of numerical integration is that we can use our knowledge of the gradient (which tells us how the function is changing) to work out what the value of the function will be in the vicinity of our starting point. The simplest way to do this is Euler integration:
y(dt) = y(0) + dy/dt * dt
Euler integration assumes that the gradient is constant between t=0 and t=dt. Once y(dt) is known, the gradient can be calculated there also and in turn used to calculate y(2 * dt) and so on, gradually building up the complete trajectory of the function. If you are looking for a particular target value, just wait until the trajectory goes past that value, then interpolate between the last two positions to get the precise t.
The problem with Euler integration (and with all other numerical integration methods) is that its results are only accurate when its assumptions are valid. Because the gradient is not constant between pairs of time points, a certain amount of error will arise for each integration step, which over time will build up until the answer is completely inaccurate. In order to improve the quality of the integration, it is necessary to use more sophisticated approximations to the gradient. Check out for example the Runge-Kutta methods, which are a family of integrators which remove progressive orders of error term at the cost of increased computation time. If your function is differentiable, knowing the second or even third derivatives can also be used to reduce the integration error.
Fortunately of course, somebody else has done the hard work here, and you don't have to worry too much about solving problems like numerical stability or have an in depth understanding of all the details (although understanding roughly what is going on helps a lot). Check out http://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.ode.html#scipy.integrate.ode for an example of an integrator class which you should be able to use straightaway. For instance
from scipy.integrate import ode
def deriv(t, y):
return 0.01 * y * (1 - y)
my_integrator = ode(deriv)
my_integrator.set_initial_value(0.5)
t = 0.1 # start with a small value of time
while t < 3000:
y = my_integrator.integrate(t)
if y > 0.8:
print "y(%f) = %f" % (t, y)
break
t += 0.1
This code will print out the first t value when y passes 0.8 (or nothing if it never reaches 0.8). If you want a more accurate value of t, keep the y of the previous t as well and interpolate between them.
As an addition to Krastanov`s answer:
Aside of PyDSTool there are other packages, like Pysundials and Assimulo which provide bindings to the solver IDA from Sundials. This solver has root finding capabilites.
Use scipy.integrate.odeint to handle your integration, and analyse the results afterward.
import numpy as np
from scipy.integrate import odeint
ts = np.arange(0,3000,1) # time series - start, stop, step
def rhs(y,t):
return 0.01*y*(1-y)
y0 = np.array([1]) # initial value
ys = odeint(rhs,y0,ts)
Then analyse the numpy array ys to find your answer (dimensions of array ts matches ys). (This may not work first time because I am constructing from memory).
This might involve using the scipy interpolate function for the ys array, such that you get a result at time t.
EDIT: I see that you wish to solve a spring in 3D. This should be fine with the above method; Odeint on the scipy website has examples for systems such as coupled springs that can be solved for, and these could be extended.
What you are asking for is a ODE integrator with root finding capabilities. They exist and the low-level code for such integrators is supplied with scipy, but they have not yet been wrapped in python bindings.
For more information see this mailing list post that provides a few alternatives: http://mail.scipy.org/pipermail/scipy-user/2010-March/024890.html
You can use the following example implementation which uses backtracking (hence it is not optimal as it is a bolt-on addition to an integrator that does not have root finding on its own): https://github.com/scipy/scipy/pull/4904/files
I have a nasty discontinuous 2 dimensional integral I(k,k''; J,Jp,a,b,c,d) that has 4 variational parameters (a,b,c,d) and 2 fixed constants (J,Jp). The procedure to finding the integral isn't straightforward and has a first step.
I need to find the root (mu) to a one-dimensional integral from -pi
to pi
A = Integrate [ 1/(exp(E(k; a,b,c,d)-mu)+1 ] dk/2pi = 0.5,
where E is a complicated function consisting of square roots and cosines.
Having found mu, I need to find the 4D (global) minimum (a,b,c,d) values to this two-dimensional integral (same -pi to pi limits) with J, Jp supplied.
result(J,Jp) = Minimum[ Integrate [ I(J,Jp;k,k''; a,b,c,d,mu) ] dk/2pi dk''/2pi ]
The complicated function I basically looks like
I(J,Jp;k,k''; a,b,c,d,mu) = A(k)*A(k'')*f(a,b,c,d)*[J cos(k+k'') + Jp cos(k-k'')]
I have done the first step to finding mu with assumed values of a,b,c,d, but am unsure how to go about with arbitrary values of them. Is there another way besides nesting all the lambda functions? Even so, how do I go about nesting lambda functions to accomplish what I need?
beta=100.0
a=1.2
b=1.5
c=0.1
d=0.5
findmu = lambda mu: integrate.quad(lambda k:1.0/(2.0*pi)*1.0/(exp(beta*(0.5*(c+d-2.0*(1.0+b)*cos(k)-sqrt(32.0*(b*cos(k/2.0))**2.0+(c-d-2.0*(1-b)*cos(k))**2.0))-mu))+1.0)-0.5/(2.0*pi), -pi,pi)
mu0 = optimize.fsolve(findmu,0.0)
I have the procedure written in Mathematica, but it's taking too long to obtain the minimum, and are sometimes wrong. I would like to try porting it over to Python, which I am currently learning. Thanks!
EDIT: More information on the physical system: The first step is finding the Fermi level of a quantum system by imposing the filling of the system. With the Fermi level, one can find what variational parameters minimize the ground state energy of this Hartree-Fock system.
I have not looked at your equation at all, but I can give you the information on how to nest the lambdas. You can simply add lambda a, b, c, d: to the front of your current lambda, then findmu(a, b, c, d) would return a function that you would pass into optimize.fsolve(), for example:
beta=100.0
findmu = lambda a, b, c, d: lambda mu: integrate.quad(lambda k:1.0/(2.0*pi)*1.0/(exp(beta*(0.5*(c+d-2.0*(1.0+b)*cos(k)-sqrt(32.0*(b*cos(k/2.0))**2.0+(c-d-2.0*(1-b)*cos(k))**2.0))-mu))+1.0)-0.5/(2.0*pi), -pi,pi)
mu0 = optimize.fsolve(findmu(1.2, 1.5, 0.1, 0.5),0.0)
# now just tweak the values for the arguments to findmu in subsequent calls
This makes a closure for the values of a, b, c, and d that you pass into findmu(), it might make more sense to rename findmu to make_findmu or something similar.
It might make the code more readable to use an actual function definition rather than lambdas here:
def make_findmu(a, b, c, d):
beta = 100.0
return lambda mu: integrate.quad(lambda k:1.0/(2.0*pi)*1.0/(exp(beta*(0.5*(c+d-2.0*(1.0+b)*cos(k)-sqrt(32.0*(b*cos(k/2.0))**2.0+(c-d-2.0*(1-b)*cos(k))**2.0))-mu))+1.0)-0.5/(2.0*pi), -pi,pi)
mu0 = optimize.fsolve(make_findmu(1.2, 1.5, 0.1, 0.5),0.0)