I am trying to optimize writing a script that has 2 minimizations, one dependent on the other. My code is a bit bloated, and I find the global parameters that I am solving for depend quite strongly on the initial guesses for the local parameters (which is quite concerning). So while the code works, I'm looking to optimize it, both for speed and robustness (i.e. the global solutions shouldn't vary so much due to local initial guesses).
Let's say you have multiple systems of linear equations:
#system of equations 1
equation1=dF*pF+dO*pO+dC*pC
equation2=dF*pF2+dO*pO2+dC*pC2
equation3=dF*pF3+dO*pO3+dC*pC3
equation4=dF*pF4+dO*pO4+dC*pC4
#system 2
equation5=dF2*pF+dO2*pO+dC2*pC
equation6=dF2*pF2+dO2*pO2+dC2*pC2
equation7=dF2*pF3+dO2*pO3+dC2*pC3
equation8=dF2*pF4+dO2*pO4+dC2*pC4
...
For each system of equations, there are 4 equations, with 15 adjustable parameters (3 of them shared dF, dO, and dC). However, the other 12 are shared across the multiple various systems. There are given experimental values of "equation1, equation2, equation3...." that the above system of equations is minimized against.
These 12 parameters can be determined from 4 global parameters (k,k1,k2,k3) [io is a constant that is given]
pF=(sqrt(k)*sqrt(k1)*sqrt(kk1+8io(1+k1)-kk1)/(4(1+k1))
pF2=(sqrt(k*k2)*sqrt(k1*k2)*sqrt(kk2k1k2+8io(1+k1*k2)-kk2k1k2)/(4(1+k1k2))
pF3=(sqrt(k*k3)*sqrt(k1*k3)*sqrt(kk3k1k3+8io(1+k1k3)-kk3k1k3)/(4(1+k1*k3))
pF4=(sqrt(k*k2*k3)*sqrt(k1*k2*k3)*sqrt(kk2k3k1k2k3+8io(1+k1k2k3)-kk2k3k1k2k3)/(4(1+k1*k2*k3))
#all other ps can be calculated in the same manner, but they have different equations I won't list here for space reasons
So the idea is the 4 global parameters (k,k1,k2,k3) can be used to calculate the p's. Then with given p's, you go through each system of equation individually and solve for dF, dO, dC. You then minimize the global parameters, using the combined chi2 of the "solved/minimized" local parameters (dF, dO, dC).
Here is the code:
from scipy.optimize import minimize
import numpy as np
experimental_data_list=[[117.77, 117.705, 117.843, 117.597], [110.575, 110.258, 110.167, 110.216], [125.691, 125.006, 125.327, 124.481], [107.491, 108.461, 107.804, 109.383], [128.689, 128.383, 128.668, 128.29], [125.969, 126.326, 126.28, 126.257], [122.439, 122.684, 122.859, 122.194], [125.989, 125.998, 125.985, 125.897], [120.916, 120.18, 120.345, 120.567], [126.772, 126.669, 127.006, 127.592], [120.176, 120.153, 119.864, 120.205]]
def local_calculation(d,pF,pO,pC,pF2,pO2,pC2,pF3,pO3,pC3,pF4,pO4,pC4,experimental_data):
equation1=(d[0]*pF)+(d[1]*pO)+(d[2]*pC)
equation2=(d[0]*pF2)+(d[1]*pO2)+(d[2]*pC2)
equation3=(d[0]*pF3)+(d[1]*pO3)+(d[2]*pC3)
equation4=(d[0]*pF4)+(d[1]*pO4)+(d[2]*pC4)
return np.sqrt(np.sum((experimental_data-np.array([equation1,equation2,equation3,equation4]))**2))
def get_populations(k,io):
pF=(((np.sqrt(k[0])*np.sqrt(k[1]))*(np.sqrt((8*io*(k[1]+1))+(k[0]*k[1])))-(k[0]*k[1]))/(4*(k[1]+1)))/io
pO=((k[1]*((-np.sqrt(k[0])*np.sqrt(k[1]))*(np.sqrt((8*io*(k[1]+1))+(k[0]*k[1])))+(k[0]*k[1])+(4*io*(k[1]+1))))/(4*((k[1]+1)**2)))/io
pC=(((-np.sqrt(k[0])*np.sqrt(k[1]))*(np.sqrt((8*io*(k[1]+1))+(k[0]*k[1])))+(k[0]*k[1])+(4*io*(k[1]+1)))/(4*((k[1]+1)**2)))/io
pF2=(((np.sqrt((k[0]*k[2]))*np.sqrt((k[1]*k[2])))*(np.sqrt((8*io*((k[1]*k[2])+1))+((k[0]*k[2])*(k[1]*k[2]))))-((k[0]*k[2])*(k[1]*k[2])))/(4*((k[1]*k[2])+1)))/io
pO2=(((k[1]*k[2])*((-np.sqrt((k[0]*k[2]))*np.sqrt((k[1]*k[2])))*(np.sqrt((8*io*((k[1]*k[2])+1))+((k[0]*k[2])*(k[1]*k[2]))))+((k[0]*k[2])*(k[1]*k[2]))+(4*io*((k[1]*k[2])+1))))/(4*(((k[1]*k[2])+1)**2)))/io
pC2=(((-np.sqrt((k[0]*k[2]))*np.sqrt((k[1]*k[2])))*(np.sqrt((8*io*((k[1]*k[2])+1))+((k[0]*k[2])*(k[1]*k[2]))))+((k[0]*k[2])*(k[1]*k[2]))+(4*io*((k[1]*k[2])+1)))/(4*(((k[1]*k[2])+1)**2)))/io
pF3=(((np.sqrt((k[0]*k[3]))*np.sqrt((k[1]*k[3])))*(np.sqrt((8*io*((k[1]*k[3])+1))+((k[0]*k[3])*(k[1]*k[3]))))-((k[0]*k[3])*(k[1]*k[3])))/(4*((k[1]*k[3])+1)))/io
pO3=(((k[1]*k[3])*((-np.sqrt((k[0]*k[3]))*np.sqrt((k[1]*k[3])))*(np.sqrt((8*io*((k[1]*k[3])+1))+((k[0]*k[3])*(k[1]*k[3]))))+((k[0]*k[3])*(k[1]*k[3]))+(4*io*((k[1]*k[3])+1))))/(4*(((k[1]*k[3])+1)**2)))/io
pC3=(((-np.sqrt((k[0]*k[3]))*np.sqrt((k[1]*k[3])))*(np.sqrt((8*io*((k[1]*k[3])+1))+((k[0]*k[3])*(k[1]*k[3]))))+((k[0]*k[3])*(k[1]*k[3]))+(4*io*((k[1]*k[3])+1)))/(4*(((k[1]*k[3])+1)**2)))/io
pF4=(((np.sqrt((k[0]*k[2]*k[3]))*np.sqrt((k[1]*k[2]*k[3])))*(np.sqrt((8*io*((k[1]*k[2]*k[3])+1))+((k[0]*k[2]*k[3])*(k[1]*k[2]*k[3]))))-((k[0]*k[2]*k[3])*(k[1]*k[2]*k[3])))/(4*((k[1]*k[2]*k[3])+1)))/io
pO4=(((k[1]*k[2]*k[3])*((-np.sqrt((k[0]*k[2]*k[3]))*np.sqrt((k[1]*k[2]*k[3])))*(np.sqrt((8*io*((k[1]*k[2]*k[3])+1))+((k[0]*k[2]*k[3])*(k[1]*k[2]*k[3]))))+((k[0]*k[2]*k[3])*(k[1]*k[2]*k[3]))+(4*io*((k[1]*k[2]*k[3])+1))))/(4*(((k[1]*k[2]*k[3])+1)**2)))/io
pC4=(((-np.sqrt((k[0]*k[2]*k[3]))*np.sqrt((k[1]*k[2]*k[3])))*(np.sqrt((8*io*((k[1]*k[2]*k[3])+1))+((k[0]*k[2]*k[3])*(k[1]*k[2]*k[3]))))+((k[0]*k[2]*k[3])*(k[1]*k[2]*k[3]))+(4*io*((k[1]*k[2]*k[3])+1)))/(4*(((k[1]*k[2]*k[3])+1)**2)))/io
local_chi2=[]
for experimental_data in experimental_data_list:
arguments=(pF,pO,pC,pF2,pO2,pC2,pF3,pO3,pC3,pF4,pO4,pC4,experimental_data)
local_solution=minimize(local_calculation,args=arguments, x0=np.array([120,110,100]),method = 'Nelder-Mead')
local_chi2.append(local_solution.fun)
return sum(local_chi2)
io=280000
global_parameter_solution=minimize(get_populations,args=io, x0=np.array([1000,0.002,8,20]),method = 'Nelder-Mead')