curve_fit: calculations with x before fit - python

I'm using the curve_fit function of the scipy package to fit some experimental data. I wrote my objective function like that:
def binding21(G0, eps1, eps2, K1, K2):
A = K1 * K2
B = K1 * (2 * K2 * H0 - K2 * G0 + 1)
C = K1 * (H0 - G0) + 1
D = G0
roots = np.roots([A, B, C, -D])
roots = [value for value in roots if np.isreal(value)]
G = max(roots)
y = (eps1 * H0 * K1 * G + eps2 * H0 * K1 * K2 * G**2) / \
(1 + K1 * G + K1 * K2 * G**2)
return y
G0 is my array of x values. However, the model I use in my objective function is not defined as y=f(G0), but instead as y=f(G=f(G0)).
I need to find the roots of G=f(G0) before I can calculate y: I have to choose the smallest positive value of the roots returned, and assign it to G.
For now, the previous code returns:
Traceback (most recent call last):
File "/home/djipey/Working/Data_boss/21.py", line 80, in <module>
result = mod.fit(y, G0=x)
File "/usr/lib/python3.5/site-packages/lmfit/model.py", line 506, in fit
output.fit(data=data, weights=weights)
File "/usr/lib/python3.5/site-packages/lmfit/model.py", line 710, in fit
self.init_fit = self.model.eval(params=self.params, **self.userkws)
File "/usr/lib/python3.5/site-packages/lmfit/model.py", line 372, in eval
result = self.func(**self.make_funcargs(params, kwargs))
File "/home/djipey/Working/Data_boss/21.py", line 38, in binding21
roots = np.roots([A, B, C, -D])
File "/usr/lib/python3.5/site-packages/numpy/lib/polynomial.py", line 207, in roots
p = atleast_1d(p)
File "/usr/lib/python3.5/site-packages/numpy/core/shape_base.py", line 50, in atleast_1d
ary = asanyarray(ary)
File "/usr/lib/python3.5/site-packages/numpy/core/numeric.py", line 525, in asanyarray
return array(a, dtype, copy=False, order=order, subok=True)
ValueError: setting an array element with a sequence.
Because I'm trying to find the roots using G0, that is not a single value but an array.
Could you help me to solve this problem ?
EDIT:
I'm basically trying to fit this equation:
With:
Epsilon_HG, Epsilon_HG2, K1 and K2 are the parameters. H0 is a known constant. G is the variable in this model. And G depends on G0, which is the "x" values I give to the model.
I have several experimental data points where Y=f(G0). But I need to fit Y=f(G). G can be obtained only by solving the cubic equation above.

I finally found the solution, which is in fact very simple:
G = []
def binding21(G0, eps1, eps2, K1, K2):
global G
G = []
for value in G0:
A = K1 * K2
B = K1 * (2 * K2 * H0 - K2 * value + 1)
C = K1 * (H0 - value) + 1
D = value
roots = np.roots([A, B, C, -D])
roots = [float(value) for value in roots if np.isreal(value)]
G.append(max(roots))
G = np.array(G)
y = (eps1 * H0 * K1 * G + eps2 * H0 * K1 * K2 * G**2) / \
(1 + K1 * G + K1 * K2 * G**2)
return y
I basically re-calculate G at each iteration of curve_fit. But I have to do it for the whole list, I can't use one particular value. So, I just do it for the entire list :)
And as I need the final value of G, I had to use a global variable. I know it's evil, but I couldn't find a better solution in that case.
For the chemists out there, this snippet is used to fit titrations for binding experiments (model 1:2).

Related

Differents results from create function in a different way - only length-1 arrays can be converted to Python scalars

I have defined the following functions in python:
from math import *
import numpy as np
import cmath
def BSM_CF(u, s0, T, r, sigma):
realp = -0.5*u**2*sigma**2*T
imagp = u*(s0+(r-0.5*sigma**2)*T)
zc = complex(realp, imagp)
return cmath.exp(zc)
def BSM_characteristic_function(v, x0, T, r, sigma):
cf_value = np.exp(((x0 / T + r - 0.5 * sigma ** 2) * 1j * v -
0.5 * sigma ** 2 * v ** 2) * T)
return cf_value
Parameters:
alpha = 1.5
K = 90
S0 = 100
T = 1
r = 0.05
sigma = 0.2
k = np.log(K / S0)
s0 = np.log(S0 / S0)
g = 1 # factor to increase accuracy
N = 2 ** 2
eta = 0.15
eps = (2*np.pi)/(N*eta)
b = 0.5 * N * eps - k
u = np.arange(1, N + 1, 1)
vo = eta * (u - 1)
v = vo - (alpha + 1) * 1j
BSMCF = BSM_characteristic_function(v, s0, T, r, sigma)
BSMCF_v2 = BSM_CF(0, s0, T, r, sigma)
print(BSMCF)
print(BSMCF_v2)
Both are the same functions. But, I get different results. How can I fix the function BSM_CF to get the same result from the function BSM_characteristic_function? The idea is get an array with len 4 values as in the funtion BSM_characteristic_function
Your calls are not identical. You are passing v in the first call and 0 in the second call. If I pass 0 for both, the results are identical. If I pass v, it complains because you can't call complex on a vector.
Numeric computation is Not always identical to symbolic algebra. For the first formula, you use complex computation as an alternative, which could result rounding errors in complex part. I came across such mistakes quite often as I used Mathematica, which loves to transfer a real formula to a complex one before doing the numeric computation.

How to solve the differential equation?

I want to solve the deferential equation
dydt = r * (Y ** p) * (1 - (Y / K) ** alpha)
I tried to write the code like :
def func(Y, r, p, K, alpha):
dydt = r * (Y ** p) * (1 - (Y / K) ** alpha)
return dydt
t = np.linspace(0, len(df), len(df))
# I used 1 to initialize my parameters ( is there a better way ?)
r = 1; p = 1; K = 1; alpha = 1
y0 = r,p,K,alpha
ret = odeint(func, y0, t)
but when I try to execute the third block I get
TypeError: func() missing 3 required positional arguments: 'p', 'K', and 'alpha'
However I tried to use ret = odeint(func, y0, t, args=(p,K, alpha))
but this resulted in a three straight lines, when the equation is supposed to return a logistic curve.
how can I try to put r in the argument and why I need to specify the arguments? how can I get the final shape (logistic curve)
Note: to understand the parameters: Y represents the cumulative number of cases at time t, r is the growth rateat the early stage, and K is the final epidemic size.𝑝∈[0,1]is a parameter that allows the model to capture different growth profiles including the constant incidence (𝑝=0), sub-exponential growth (0<𝑝<1)and exponential growth (𝑝=1).
def func(Y, t, r, p, K, alpha):
return r * (Y ** p) * (1 - (Y / K) ** alpha)
You must add the t parameter in the ODEINT method.
y0 = 0.5 # Your initial condition.
params = (1, 1, 1, 1) # r, p, K, alpha
sol = odeint(func, y0, t, args=params)
From the source! Scipy ODEINT

How to use positional arguments appropriately for a complex problem

I am revisiting a school project, which I did not complete to my satisfaction. Namely, I wrote an algorithm that takes an ALMOST arbitrary size set of equations and solves them iteratively. The problem being the "almost" part. Essentially, it must have at least two equations, and will not solve for a single one. This is because, I believe, that I don't understand how to use positional arguments correctly.
Below, in the main method, I define two functions y_prime and z_prime. If I pass them both, I get a beautiful graph of my solutions. But, if I only pass y_prime along with its initial conditions and the solution vector to the rungekutta() function, things go haywire:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
def rungekutta(dt, y, t, *funcs):
"""
The following code was written in order to
reproduce the classic 4th order Runge-Kutta numerical
method of solving a system of differential equations.
The aim was to not only apply this to the budworm deforestation
model developed by Ludwig et al, but also to create an algorithm
that is generic enough to accept a wide range of ODEs and
systems of ODEs.
:param dt: time step "Delta t"
:param y: The solution vector at the last time step
:param t: The time at the last time step
:param funcs: the vector field dy/dt = f(t,y)
:return: The solution vector for the next time step
"""
k1 = [dt * f(*y, t) for f in funcs]
args = [y_n + 0.5 * k_1 for y_n, k_1 in zip((*y, t), (*k1, dt))]
k2 = [dt * f(*args) for f in funcs]
args = [y_n + 0.5 * k_2 for y_n, k_2 in zip((*y, t), (*k2, dt))]
k3 = [dt * f(*args) for f in funcs]
args = [y_n + k_3 for y_n, k_3 in zip((*y, t), (*k3, dt))]
k4 = [dt * f(*args) for f in funcs]
return [y_n + (k_1 + 2 * k_2 + 2 * k_3 + k_4) / 6 for y_n, k_1, k_2, k_3, k_4 in
zip(y, k1, k2, k3, k4)]
if __name__ == '__main__':
def y_prime(y, z, t):
return -t * y
def z_prime(y, z, t):
return z
t_0 = -10
t_n = 10
dt = .05
steps = int((t_n - t_0) / dt)
y_soln = [0] * steps
z_soln = [0] * steps
time = np.arange(t_0, t_n, dt)
y_soln[0] = 1.928749848e-22
z_soln[0] = .0000453999297625
for i in np.arange(1, steps):
y_soln[i] = rungekutta(dt, y_soln[i-1], time[i-1], y_prime)
The first error I received, when trying to pass a single equation was:
Traceback (most recent call last):
File "C:/Users/wesle/PycharmProjects/Budworms/RK4v2.py", line 57, in <module>
y_soln[i] = rungekutta(dt, y_soln[i-1], time[i-1], y_prime, z_prime)
File "C:/Users/wesle/PycharmProjects/Budworms/RK4v2.py", line 23, in rungekutta
k1 = [dt * f(*y, t) for f in funcs]
File "C:/Users/wesle/PycharmProjects/Budworms/RK4v2.py", line 23, in <listcomp>
k1 = [dt * f(*y, t) for f in funcs]
TypeError: y_prime() argument after * must be an iterable, not float
This was because, I think, I have "y_soln" as a positional argument, but now there is only one and it is no longer iterable. So, I made it a tuple of 1 when I passed it in the main method:
for i in np.arange(1, steps):
y_soln[i] = rungekutta(dt, (y_soln[i-1],), time[i-1], y_prime)
That bit me in the butt, however, because now I am passing a tuple into my y_prime equation, when what it really needs is a float:
Traceback (most recent call last):
File "C:/Users/wesle/PycharmProjects/Budworms/RK4v2.py", line 57, in <module>
y_soln[i] = rungekutta(dt, (y_soln[i-1],), time[i-1], y_prime)
File "C:/Users/wesle/PycharmProjects/Budworms/RK4v2.py", line 23, in rungekutta
k1 = [dt * f(*y, t) for f in funcs]
File "C:/Users/wesle/PycharmProjects/Budworms/RK4v2.py", line 23, in <listcomp>
k1 = [dt * f(*y, t) for f in funcs]
File "C:/Users/wesle/PycharmProjects/Budworms/RK4v2.py", line 38, in y_prime
return -t * y
TypeError: can't multiply sequence by non-int of type 'numpy.float64'
My only work-around so far has been to solve an extra, random equation like $y= y'$ in addition to whatever equation I'm interested in. This seems pretty inefficient though.
So, it seems like I'm damned if I do, or damned if I don't. Is there any remedy to this?
EDIT If you want to see the code actually work, replace this:
for i in np.arange(1, steps):
y_soln[i] = rungekutta(dt, (y_soln[i-1],), time[i-1], y_prime)
with the instance where I pass both equations and their solution vectors to the function:
for i in np.arange(1, steps):
y_soln[i], z_soln[i] = rungekutta(dt, (y_soln[i-1], z_soln[i-1]), time[i-1], y_prime, z_prime)
My solution ended up being to convert all lists to numpy arrays, which allowed me to take advantage of the built in element-wise scalar addition and multiplication. This made computing the "k" values much less cumbersome and convoluted:
def rk4(dt, t, field, y_0):
"""
:param dt: float - the timestep
:param t: array - the time mesh
:param field: method - the vector field y' = f(t, y)
:param y_0: array - contains initial conditions
:return: ndarray - solution
"""
# Initialize solution matrix. Each row is the solution to the system
# for a given time step. Each column is the full solution for a single
# equation.
y = np.asarray(len(t) * [y_0])
for i in np.arange(len(t) - 1):
k1 = dt * field(t[i], y[i])
k2 = dt * field(t[i] + 0.5 * dt, y[i] + 0.5 * k1)
k3 = dt * field(t[i] + 0.5 * dt, y[i] + 0.5 * k2)
k4 = dt * field(t[i] + dt, y[i] + k3)
y[i + 1] = y[i] + (k1 + 2 * k2 + 2 * k3 + k4) / 6
return y

scipy.integrate.quad gives TypeError: integer argument expected, got float

I am trying to do Fourier series with numpy. I am trying to write functions as they're defined here
But I am having trouble already at defining a0.
# "M1(t)" function definition.
def M1(t, *args):
tau, M0 = args
omega = 2 * np.pi / tau
return (2 * M0 + M0 * np.sin(omega * t - 2 / 3 * np.pi) +
M0 * np.sin(omega * t - 4/ 3 * np.pi))
# "M2(t)" function definition.
def M2(t, *args):
tau, M0 = args
omega = 2 * np.pi / tau
return (3 * M0 + M0 * np.sin(omega * t) + M0 * np.sin(omega * t - 2 / 3 * np.pi) +
M0 * np.sin(omega * t - 4/ 3 * np.pi))
def a0(tau, *args):
# limits of integrals; a = lower of 1st integral;
# b = higher of 1st and lower od 2nd integral;
# c = higher of 2nd integral
a, b, c = 0, tau / 2, tau
i1, err1 = quad(M1, a, b, *args)
i2, err2 = quad(M2, b, c, *args)
return 2 / tau * (i1 + i2)
When I run this code I get the following error:
TypeError: integer argument expected, got float
As requested, traceback of error:
Traceback (most recent call last):
File "C:/Users/Alex/Documents/Faks/Magisterij/1. letnik/VD/2. seminar/periodicno_vzbujanje.py", line 86, in <module>
a0 = a0(parameters[0], *parameters)
File "C:/Users/Alex/Documents/Faks/Magisterij/1. letnik/VD/2. seminar/periodicno_vzbujanje.py", line 41, in a0
i1, err1 = quad(M1, a, b, *args)
File "C:\Users\Alex\Anaconda3\lib\site-packages\scipy\integrate\quadpack.py", line 315, in quad
points)
File "C:\Users\Alex\Anaconda3\lib\site-packages\scipy\integrate\quadpack.py", line 380, in _quad
return _quadpack._qagse(func,a,b,args,full_output,epsabs,epsrel,limit)
TypeError: integer argument expected, got float
Extra question: how can I pass combined function to quad? For example: M1(t, *args) * np.cos(omega * t)? Do I have to define it as a new function and then pass it in or is there quicker way? Because I feel it's kind of redundant to type 4 extra functions.
UPDATE:
I realized I've been passing aditional arguments wrong the whole time.
I changed i2, err2 = quad(M2, b, c, *args) to i2, err2 = quad(M2, b, c, args). However now I get the following error:
ValueError: not enough values to unpack (expected 2, got 1).
With your M1
In [202]: M1(0,1,1)
Out[202]: 1.9999999999999996
In [203]: integrate.quad(M1,0,1,(1,1))
Out[203]: (2.0, 2.220446049250313e-14)
In [204]: M1(0,*(1,1))
Out[204]: 1.9999999999999996
The args tuple should look like what you'd pass to M1 with the *() syntax.

Error when using sympy's solver on polynomials with complex coefficients (4th deg)

Trying to solve a 4th degree polynomial equation with sympy, I arrived at some difficulties. My code and the equation i'm trying to solve:
import sympy as sym
from sympy import I
sym.init_printing()
k = sym.Symbol('k')
t, sigma ,k0, L , V = sym.symbols('t, sigma, k0, L,V')
x4 = ( -t**2 + 2*I * t / sigma**2 + 1/sigma**4)
x3 = ( -2*I * t * k0 / sigma**2 - 2*k0 / sigma**4)
x2 = ( L**2 + k0 **2 / sigma **4 + t**2 * V - 2 * I * t * V / sigma**2 -V/sigma**4)
x1 = (2*I * V * k0 / sigma**2 + 2*k0 * V / sigma **4)
x0 = (2*I*k0*t*V / sigma**2 - k0 **2 *V / sigma**4)
expr = x4 * k**4 + x3 * k**3 + x2 * k**2 + x1 * k + x0
expr2 = expr.subs({k0 :2 , sigma : .2 , L : 1, V:1})
sym.solvers.solve(expr2,k)
Output:
Traceback (most recent call last):
File "<ipython-input-4-e1ce7d8c9531>", line 1, in <module>
sols = sym.solvers.solve(expr2,k)
File "/usr/local/lib/python2.7/dist-packages/sympy/solvers /solvers.py", line 1125, in solve
solution = nfloat(solution, exponent=False)
File "/usr/local/lib/python2.7/dist-packages/sympy/core/function.py", line 2465, in nfloat
return type(expr)([nfloat(a, n, exponent) for a in expr])
File "/usr/local/lib/python2.7/dist-packages/sympy/core/function.py", line 2499, in nfloat
lambda x: isinstance(x, Function)))
File "/usr/local/lib/python2.7/dist-packages/sympy/core/basic.py", line 1087, in xreplace
value, _ = self._xreplace(rule)
File "/usr/local/lib/python2.7/dist-packages/sympy/core/basic.py", line 1095, in _xreplace
return rule[self], True
File "/usr/local/lib/python2.7/dist-packages/sympy/core/rules.py", line 59, in __getitem__
return self._transform(key)
File "/usr/local/lib/python2.7/dist-packages/sympy/core/function.py", line 2498, in <lambda>
lambda x: x.func(*nfloat(x.args, n, exponent)),
File "/usr/local/lib/python2.7/dist-packages/sympy/core/function.py", line 2465, in nfloat
return type(expr)([nfloat(a, n, exponent) for a in expr])
File "/usr/local/lib/python2.7/dist-packages/sympy/core/function.py", line 2465, in nfloat
return type(expr)([nfloat(a, n, exponent) for a in expr])
TypeError: __new__() takes exactly 3 arguments (2 given)
And I really can't make anything out of it. I am not so sure what's causing this, I "tested" this solver for more compact polynomials and it worked well.
Looks like you can work around the issue by using solve(expr2, k, rational=False).

Categories