Search code examples
pythonscipychemistry

ValueError: object too deep for desired array in optimize.curve_fit


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


Solution

  • 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