2

I am trying to fit a kinetic model for population growth and decay of four variables A,B,C,D in a chemical system. I am trying to solve the following set of equations, which I have attached in matrix form:

Matrix form of equations

where t is a time step and k1,k2,k3 are constants in an exponential function. I want to fit curves based on these equations to solve for k1,k2, and k3 given my populations of A,B,C,D.

For this I am using optimize.curve_fit, t is the time step in a (1000,) array, X is a (4,1000) matrix and where u and w are the two matrices:

from scipy import optimize

def func(t,X,k1,k2,k3):

    u = np.array([[1,0,0],
                  [-k1/(k1+k2-k3),k1/(k1+k2-k3),0],
                  [(k1*k3)/((k1+k2-k3)*(k1+k2)),-k1/(k1+k2k3),k1/(k1+k2)],
                  [-k2/(k1+k2),0,k2/(k2+k1)]],dtype=float)

    w = np.array([[np.exp(-t*(k1+k2))],
                 [np.exp(-t*k3)],
                 [1]])

    return X*np.dot(u,w)


X = np.array([A,B,C,D]) # A,B,C,D are (1000,) arrays
# X.shape = (4, 1000)
# t.shape = (1000,)

optimize.curve_fit(func,t,X,method='lm')

When I run this piece of code, I get the following output:

ValueError: object too deep for desired array

error: Result from function call is not a proper array of floats.

I have seen in a similar post that the shapes of the arrays in important, but as far as I can tell these are correct.

Could anyone suggest where the problem may be in this code and how I can best go about solving for k1,k2,k3 using the curve fit function?

Thanks

  • 1
    There are at least a couple of problems: `func` does not need `X` parameter, your `t` is the x param. Also, the 3rd element in matrix `w` is of length one, it should be of length 1,000 to match the other 2 elements above – Brenlla Jul 25 '18 at 15:11
  • Ah yes - `X` is a bad name for a variable here ; I should call it ``Y, so that my x param is `t ` and my y param is `Y`, where `Y` is the matrix of shape (4,1000). For the third element of matrix `w`, I just want it to be a constant, so that when the dot product is taken with matrix u, the third column in `u` is returned as a constant. – acleveronlinename Jul 25 '18 at 15:22
  • Your function signature is `func(t,X,k1,k2,k3)`. `curve_fit` treats *all* parameters after the first as parameters to be fit. So it tries to fit `X`, `k1`, `k2`, and `k3`. – Warren Weckesser Jul 25 '18 at 15:38
  • Does this answer your question? [What does "ValueError: object too deep for desired array" mean and how to fix it?](https://stackoverflow.com/questions/15923081/what-does-valueerror-object-too-deep-for-desired-array-mean-and-how-to-fix-it) – mkrieger1 Feb 02 '22 at 13:01

1 Answers1

2

As I mentioned in my comment, you don't need to pass X onto func. @WarrenWeckesser briefly explains why. So here is how func should be:

def func(t,k1,k2,k3):

    u = np.array([[1,0,0],
                  [-k1/(k1+k2-k3),k1/(k1+k2-k3),0],
                  [(k1*k3)/((k1+k2-k3)*(k1+k2)),-k1/(k1+k2*k3),k1/(k1+k2)],
                  [-k2/(k1+k2),0,k2/(k2+k1)]],dtype=float)

    w = np.array([np.exp(-t*(k1+k2)),
                 np.exp(-t*k3),
                 np.ones_like(t)]) # must match shapes with above 

    return np.dot(u,w).flatten()

The output at the end is flattened because otherwise it would give an error with curve_fit. Now we test it:

from scipy.optimize import curve_fit
t = np.arange(1000)*0.01
data = func(t, *[0.5, 2, 1])
data +=np.random.normal(size=data.shape)*0.01 # add some noise
po, pcov = curve_fit(func,t, data.flatten(), method='lm') #data must also be flattened
print(po)
#[ 0.50036411  2.00393807  0.99694513]
plt.plot(t, data.reshape(4,-1).T, t, func(t, *po).reshape(4,-1).T)

The optimised values are pretty close to the original ones and the fit seems good fit

Brenlla
  • 1,471
  • 1
  • 11
  • 23
  • Thanks Brenlla! I guess I got a bit confused with the curve fit function - thank you for you clear explanation and brilliant code example! It works perfectly on my data. Thanks – acleveronlinename Jul 26 '18 at 08:24