I woul like to solve an n-dimensional optimisation problem using iminuit.
So my approach is the following.
I am trying to figure out how to extend this:
def f(x,y,z):
return (x-1.)**2 + (y-2*x)**2 + (z-3.*x)**2 -1.
to a variable "x" that is a numpy.array.
I would like to do something like this:
x = [1,2,3,4,5]
y = [2,4,6,8,10]# y=2x
class StraightLineChi2:
def __init__(self,x,y):
self.x = x
self.y = y
def __call__(self,m,c): #lets try to find slope and intercept
chi2 = sum((y - m*x+c)**2 for x,y in zip(self.x,self.y))
return chi2
but in my case x is my unknown, and it is an array. Like in many optimization/minimization problems, the function is a f=f(x1,...,xn) where n can be big. x1,...,xn are the unknowns of the problem.
(These examples are taken from here)
Something similar is achieved "hacking" pyminuit2, like described here
For your example I recommend you using iminuit and probfit. Having an argument as a list of parameter is not exactly what you want to do since you will get confused which parameter is what very soon.
Here is an example taken straight from probfit tutorial. Also see the documentation
import iminuit
import probfit
x = np.linspace(0, 10, 20)
y = 3 * x + 15 + np.random.randn(len(x))
err = np.ones(len(x))
def line(x, m, c): # define it to be parabolic or whatever you like
return m * x + c
chi2 = probfit.Chi2Regression(line, x, y, err)
minuit = iminuit.Minuit(chi2)
minuit.migrad();
print(minuit.values) #{'c': 16.137947520534624, 'm': 2.8862774144823855}
Related
I am trying to solve this differential equation as part of my assignment. I am not able to understand on how can i put the condition for u in the code. In the code shown below, i arbitrarily provided
u = 5.
2dx(t)dt=−x(t)+u(t)
5dy(t)dt=−y(t)+x(t)
u=2S(t−5)
x(0)=0
y(0)=0
where S(t−5) is a step function that changes from zero to one at t=5. When it is multiplied by two, it changes from zero to two at that same time, t=5.
def model(x,t,u):
dxdt = (-x+u)/2
return dxdt
def model2(y,x,t):
dydt = -(y+x)/5
return dydt
x0 = 0
y0 = 0
u = 5
t = np.linspace(0,40)
x = odeint(model,x0,t,args=(u,))
y = odeint(model2,y0,t,args=(u,))
plt.plot(t,x,'r-')
plt.plot(t,y,'b*')
plt.show()
I do not know the SciPy Library very well, but regarding the example in the documentation I would try something like this:
def model(x, t, K, PT)
"""
The model consists of the state x in R^2, the time in R and the two
parameters K and PT regarding the input u as step function, where K
is the infimum of u and PT is the delay of the step.
"""
x1, x2 = x # Split the state into two variables
u = K if t>=PT else 0 # This is the system input
# Here comes the differential equation in vectorized form
dx = [(-x1 + u)/2,
(-x2 + x1)/5]
return dx
x0 = [0, 0]
K = 2
PT = 5
t = np.linspace(0,40)
x = odeint(model, x0, t, args=(K, PT))
plt.plot(t, x[:, 0], 'r-')
plt.plot(t, x[:, 1], 'b*')
plt.show()
You have a couple of issues here, and the step function is only a small part of it. You can define a step function with a simple lambda and then simply capture it from the outer scope without even passing it to your function. Because sometimes that won't be the case, we'll be explicit and pass it.
Your next problem is the order of arguments in the function to integrate. As per the docs (y,t,...). Ie, First the function, then the time vector, then the other args arguments. So for the first part we get:
u = lambda t : 2 if t>5 else 0
def model(x,t,u):
dxdt = (-x+u(t))/2
return dxdt
x0 = 0
y0 = 0
t = np.linspace(0,40)
x = odeint(model,x0,t,args=(u,))
Moving to the next part, the trouble is, you can't feed x as an arg to y because it's a vector of values for x(t) for particular times and so y+x doesn't make sense in the function as you wrote it. You can follow your intuition from math class if you pass an x function instead of the x values. Doing so requires that you interpolate the x values using the specific time values you are interested in (which scipy can handle, no problem):
from scipy.interpolate import interp1d
xfunc = interp1d(t.flatten(),x.flatten(),fill_value="extrapolate")
#flatten cuz the shape is off , extrapolate because odeint will go out of bounds
def model2(y,t,x):
dydt = -(y+x(t))/5
return dydt
y = odeint(model2,y0,t,args=(xfunc,))
Then you get:
#Sven's answer is more idiomatic for vector programming like scipy/numpy. But I hope my answer provides a clearer path from what you know already to a working solution.
import numpy
import matplotlib.pyplot as plt
from scipy import integrate
def f(x,y):
return x*y + x**2
def integral(x,y):
I = integrate.quad(f, 0, x, args=(y,))[0]
return I
def gau(x,y):
return (1+x)*integral(x,y)
xlist = numpy.linspace(-3.0, 3.0, 100)
ylist = numpy.linspace(-3.0, 3.0, 100)
X, Y = numpy.meshgrid(xlist, ylist)
Z = gau(2, Y)
print(Z)
I keep on getting the error message "Supplied function does not return a valid float." , I think the problem is that I try to pass an array to the quad function. I thought about evaluating the integral for every entry of the array with something like that:
yi=numpy.linspace(-3.0,3.0,100)
for i, item in enumerate(yi):
return integral[i]=integrate.quad(f,0,x,args=(yi,))[0]
It doesn't work but is it the right way? Any other/better suggestions?
You could use a universal function (see https://docs.scipy.org/doc/numpy/reference/ufuncs.html) which operates on arrays element-by-element. You can create these universal functions from any function using the frompyfunc function (https://docs.scipy.org/doc/numpy/reference/generated/numpy.frompyfunc.html):
ugau = numpy.frompyfunc(gau,2,1)
Z=ugau(X,Y)
It if your f() that does not provide a valid float when passed an array, not the scipy.integral itself;
why do you pass an array to your f() ?
You can use quadpy (one of my projects). quadpy is fully vectorized with respect to the dimensionality of the function range and the domains, so you can plug in a function that returns a vector and integrate that function over many intervals at once. You just have to make sure that the input function deals with vectorized input correctly. In your case, that would be
import numpy
import quadpy
def f(x, y):
return numpy.multiply.outer(y, x) + numpy.multiply.outer(numpy.ones_like(y), x ** 2)
def integral(x, y):
scheme = quadpy.line_segment.gauss_legendre(5)
intervals = numpy.array([numpy.zeros_like(x), x])
out = scheme.integrate(lambda t: f(t, y), intervals)
return out
def gau(x, y):
return (1 + x) * integral(x, y)
xlist = numpy.linspace(-3.0, 3.0, 100)
ylist = numpy.linspace(-3.0, 3.0, 100)
Z = gau(2, ylist)
print(Z)
You also insert xlist instead of 2 here to compute it all at once.
I can't find a reason why calculating the correlation between two series A and B using numpy.correlate gives me different results than the ones I obtain using statsmodels.tsa.stattools.ccf
Here's an example of this difference I mention:
import numpy as np
from matplotlib import pyplot as plt
from statsmodels.tsa.stattools import ccf
#Calculate correlation using numpy.correlate
def corr(x,y):
result = numpy.correlate(x, y, mode='full')
return result[result.size/2:]
#This are the data series I want to analyze
A = np.array([np.absolute(x) for x in np.arange(-1,1.1,0.1)])
B = np.array([x for x in np.arange(-1,1.1,0.1)])
#Using numpy i get this
plt.plot(corr(B,A))
#Using statsmodels i get this
plt.plot(ccf(B,A,unbiased=False))
The results seem qualitatively different, where does this difference come from?
statsmodels.tsa.stattools.ccf is based on np.correlate but does some additional things to give the correlation in the statistical sense instead of the signal processing sense, see cross-correlation on Wikipedia. What happens exactly you can see in the source code, it's very simple.
For easier reference I copied the relevant lines below:
def ccovf(x, y, unbiased=True, demean=True):
n = len(x)
if demean:
xo = x - x.mean()
yo = y - y.mean()
else:
xo = x
yo = y
if unbiased:
xi = np.ones(n)
d = np.correlate(xi, xi, 'full')
else:
d = n
return (np.correlate(xo, yo, 'full') / d)[n - 1:]
def ccf(x, y, unbiased=True):
cvf = ccovf(x, y, unbiased=unbiased, demean=True)
return cvf / (np.std(x) * np.std(y))
I am trying out lmfit and using as an example problem below. In this example, I am simply solving for x in a system Ax = y. Here A is a 3*2 array, y is a 3*1 array. I have declared all of them as arrays.
import numpy as np
from lmfit import minimize, Parameters
A = np.array([1,2,-1,3,-2,5])
A = A.reshape(3,2)
y = np.array([12, 13, 21])
def residual(params, A, y, eps_y=1):
x = params['x'].value
y_hat = np.dot(A, x)
return (y - y_hat)/eps_y
x = np.array([0,0])
params = Parameters()
params.add('x', x)
out = minimize(residual, params, args=(A,y))
print out.value
When running this I get an error: "ValueError: object too deep for desired array".
I have found instances of similar problems researching here and on web. In general, most often reason cited is that A, x and y should be arrays and not matrix. Also in some solutions, x and y are asked to be a kept as a vector with shape (len(v),). Above is already in compliance with these suggestions but I am still getting "ValueError: object too deep for desired array".
I have wasted quite a bit of time trying to solve this problem and am stumped now. Any help on this will be very welcome.
The documentation for Parameter is here:
http://newville.github.io/lmfit-py/parameters.html#Parameter
It specifically states that the value of a parameter must be a numerical value, and not an array of any kind. So instead of doing:
x = np.array([0,0])
params.add('x', x)
do:
params.add('x0', 0)
params.add('x1', 0)
and then change the residuals function to:
def residual(params, A, y, eps_y=1):
x0 = params['x0'].value
x1 = params['x1'].value
y_hat = np.dot(A, [x0, x1])
return (y - y_hat)/eps_y
I am a beginner/intermediate in Python. I have coded a 4th-order Runge-Kutta method (RK4) into Python. It is basically solving a pendulum, but that is not the point here.
I want to improve the RK4 method in the following way: I want to be able to pass the function f directly to the RK4 function, i.e. RK4(y_0, n, h) should become RK4(f,y_0,n,h). This would have the great advantage that I could use RK4 for other f functions that describe other systems, not just this one pendulum.
I have played around with just passing simple functions to RK4, but I am doing something wrong. How do I do this in Python?
import numpy as np
def RK4(y_0, n, h):
#4th order Runge-Kutta solver, takes as input
#initial value y_0, the number of steps n and stepsize h
#returns solution vector y and time vector t
#right now function f is defined below
t = np.linspace(0,n*h,n,endpoint = False) #create time vector t
y = np.zeros((n,len(y_0))) #create solution vector y
y[0] = y_0 #assign initial value to first position in y
for i in range(0,n-1):
#compute Runge-Kutta weights k_1 till k_4
k_1 = f(t[i],y[i])
k_2 = f(t[i] + 0.5*h, y[i] + 0.5*h*k_1)
k_3 = f(t[i] + 0.5*h, y[i] + 0.5*h*k_2)
k_4 = f(t[i] + 0.5*h, y[i] + h*k_3)
#compute next y
y[i+1] = y[i] + h / 6. * (k_1 + 2.*k_2 + 2.*k_3 + k_4)
return t,y
def f(t,vec):
theta=vec[0]
omega = vec[1]
omegaDot = -np.sin(theta) - omega + np.cos(t)
result = np.array([omega,omegaDot])
return result
test = np.array([0,0.5])
t,y = RK4(test,10,0.1)
Python functions are objects too. You can pass them around like any other object:
>>> def foo(): print 'Hello world!'
...
>>> foo
<function foo at 0x10c4685f0>
>>> foo()
Hello world!
>>> bar = foo
>>> bar()
Hello world!
Simply pass a function as an extra parameter to your RK4 function and use that as a local variable.
You can pass a function to a function in Python just as you might expect:
def call_function(f):
f()
def my_function():
print "OK"
call_function(my_function) # Prints OK
Maybe you should post your failing code?
It's very simple. Change the definition of the RK4 function like so:
def RK4(f, y_0, n, h):
Here, I have added an extra argument, the function.
Then, when you call RK4, pass the function:
t, y = RK4(f, test, 10, 0.1)
And now, of course, you can substitute different functions without having to re-write the integration code.
Functions in Python are just another kind of object. You can pass them around just as you do more prosaic objects.