scipy.optimize.curve_fit how to limite params' relative bounds - scipy

my function has 3 parameters :f(x, param1,param2,param3)
I used curve_fit to optimize parameters with x,ydata:
opt, pcov = curve_fit(f, xdata, ydata,bounds)
question:
I want to limit param1<param2<param3, but it seems that bounds only can be given values.

Related

How to pass variable constrains while minimizing function with multiple variables and return the variables?

Assume I have a function with 5 variables, and each one have a range constrains. I want to find the minimum of a function, as well as the values of those 5 variables which are needed to obtain that minimum value in that function. I am using the fminsearch.
func = #(x, y, z, k, m) (--some-function-which-depends-to-those-5-variable);
Assume I have above function that I want to minimize.
range_x = [12, 24];
range_y = [13.3, 30.2];
range_z = [1.4, 4.7];
range_k = [1.2, 1.4];
range_m = [4.12, 12.2];
and the above ranges.
??? = fminsearch(#(x) func(x(1), x(2), x(3), x(4), x(5)), ???)
I am currently using fminsearch function. However, I stucked the point that how can I use ranges and how can I extract the min value / and all those 5 variables which gives this result.
Thanks in advance.
fminsearch as per the documentation, is for unconstrained minimization, i.e. you don't put limits on the variables.
fmincon instead, is for constrained minimization.

Error lsqnonlin reaching values using nested functions

I want to fit two parameters using lsqnonlin. I have set up my system of ODE and want to solve them with my new parameters that are found after I performed the lsqnonlin.
function [dcdt] = CSTRSinSeries(t,c,par)
for i=1:par.n
if i==1
dcdt(i) = 1/par.tau_per_tank * (par.c0-c(i))+par.kf*(par.c0_meth-c(i))- ...
par.kb*c(i).^2;
else
dcdt(i) = 1/par.tau_per_tank * (c(i-1)-c(i))+par.k*(par.c0_meth-c(i))- ...
par.kb*c(i).^2;
end
end
dcdt = dcdt';
end
% fitcrit function
function error = fitcrit(curve_fit_parameters,time_exp,conc_exp, par, init)
[time_model_fit,conc_model_fit] = ode45(#(t,c) CSTRSinSeries(t,c,par),time_exp,init,[]);
error = (conc_exp-conc_model_fit);
end
I think the problem has to do with the fact that my parameters are in parameter struct par and that I don't want to fit on all of these parameters, but just on basis of two of those.
This is my main script for performing the curve fitting:
% initial guesses for model parameters, no. of indeces is number of fitted % parameters
k0 = [0.028 0.002];
% lower and upper bounds for model parameters, this can be altered
LB = [0.00 0.00];
UB = [Inf Inf];
% Set up fitting options
options = optimset('TolX',1.0E-6,'MaxFunEvals',1000);
% Perform nonlinear least squares fit (note that we store much more
% statistics than just the final fitted parameters)
[curve_fit_parameters,RESNORM,RESIDUAL,EXITFLAG,OUTPUT,LAMBDA,JACOBIAN] = ...
lsqnonlin(#(k) fitcrit(k,time_exp, conc_exp, par, init),k0,LB,UB,options);
The code now does not give an error, however the output of my curve_fit_parameters is now the same as my initial values (also when I change my initial value, it stays the same).
The error is:
>> PackedBed
Initial point is a local minimum.
Optimization completed because the size of the gradient at the initial point
is less than the default value of the optimality tolerance.
<stopping criteria details>
The stopping criteria gives a relative first-order optimality 0.00+00, so I think that the parameters lsqnonlin changes have no influence on my error.
I think that the mistake is the lsqnonlin function, where I refer to 'k' instead of 'par.kb and par.kf'. However, I don't know how to refer to these as these are nested functions. Replacing 'k' by 'par.kb, par.kf' gives me the error: Unexpected matlab operator.
Could anyone help me with my problem?
As suggested by Adriaan in the comments, you can easily make a copy of the par struct in your fitcrit function, and assign the parameters to optimize to this par_tmp struct. The fitcrit function then becomes:
function error = fitcrit(curve_fit_parameters,time_exp,conc_exp, par, init)
par_tmp = par;
par_tmp.kf = curve_fit_parameters(1); % change to parameters you need to fit
par_tmp.kb = curve_fit_parameters(2);
[time_model_fit,conc_model_fit] = ode45(#(t,c) odefun(t,c,par_tmp),time_exp,init,[]); % pass par_tmp to ODE solver
error = (conc_exp-conc_model_fit);
end
In the script calling lsqnonlin, you will have to change the par struct to include the converged solution of the parameter subset to optimize:
% initial guesses for model parameters, no. of indeces is number of fitted % parameters
k0 = [0.028 0.002];
% lower and upper bounds for model parameters, this can be altered
LB = [0.00 0.00];
UB = [Inf Inf];
% Set up fitting options
options = optimset('TolX',1.0E-6,'MaxFunEvals',1000);
% Perform nonlinear least squares fit (note that we store much more
% statistics than just the final fitted parameters)
[curve_fit_parameters,RESNORM,RESIDUAL,EXITFLAG,OUTPUT,LAMBDA,JACOBIAN] = ...
lsqnonlin(#(k) fitcrit(k,time_exp, conc_exp, par, init),k0,LB,UB,options);
% make par_final
par_final = par;
par_final.kf = curve_fit_parameters(1);
par_final.kb = curve_fit_parameters(2);
```

Optimizing a piecewise linear regression

I have written a function that, given parameters, can apply a piecewise linear fit, with arbitrarily many piecewise sections, to some data.
I am trying to fit the function to my data using scipy.optimize.curve_fit, but I am receiving an "OptimizeWarning: Covariance of the parameters could not be estimated" error. I believe this may be because of the nested lambda functions I am using to define the piecewise sections.
Is there an easy way to tweak my code to get round this, or a different scipy optimisation function that might be more suitable?
#The piecewise function
def piecewise_linear(x, *params):
N=len(params)/2
if N.is_integer():N=int(N)
else:raise(ValueError())
c=params[0]
xbounds=params[1:N]
grads=params[N:]
#First we define our conditions, which are true if x is a member of a given
#bin.
conditions=[]
#first and last bins are a special case:
cond0=lambda x: x<xbounds[0]
condl=lambda x: x>=xbounds[-1]
conditions.append(cond0(x))
for i in range(len(xbounds)-1):
cond=lambda x : (x >= xbounds[i]) & (x < xbounds[i+1])
conditions.append(cond(x))
conditions.append(condl(x))
#Next we define our linear regression function for each bin. The offset
#for each bin depends on where the previous bin ends, so we define
#the regression functions recursively:
functions=[]
func0 = lambda x: grads[0]*x +c
functions.append(func0)
for i in range(len(grads)-1):
func = (lambda j: lambda x: grads[j+1]*(x-xbounds[j])\
+functions[j](xbounds[j]))(i)
functions.append(func)
return np.piecewise(x,conditions,functions)
#Some data
x=np.arange(100)
y=np.array([*np.arange(0,19,1),*np.arange(20,59,2),\
*np.arange(60,20,-1),*np.arange(21,42,1)]) + np.random.randn(100)
#A first guess of parameters
cguess=0
boundguess=[20,30,50]
gradguess=[1,1,1,1]
p0=[cguess,*boundguess,*gradguess]
fit=scipy.optimize.curve_fit(piecewise_linear,x,y,p0=p0)
Here is example code that fits two straight lines to a curved data set with a breakpoint, where the line parameters and breakpoint are all fitted. This example uses scipy's Differential Evolution genetic algorithm to determine initial parameter estimates for the regression. That module uses the Latin Hypercube algorithm to ensure a thorough search of parameter space, which requires bounds within which to search. In this example those search bounds are derived from the data itself. Note that it is much easier to find ranges for the initial parameter estimates than to give specific values.
import numpy, scipy, matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import differential_evolution
import warnings
xData = numpy.array([19.1647, 18.0189, 16.9550, 15.7683, 14.7044, 13.6269, 12.6040, 11.4309, 10.2987, 9.23465, 8.18440, 7.89789, 7.62498, 7.36571, 7.01106, 6.71094, 6.46548, 6.27436, 6.16543, 6.05569, 5.91904, 5.78247, 5.53661, 4.85425, 4.29468, 3.74888, 3.16206, 2.58882, 1.93371, 1.52426, 1.14211, 0.719035, 0.377708, 0.0226971, -0.223181, -0.537231, -0.878491, -1.27484, -1.45266, -1.57583, -1.61717])
yData = numpy.array([0.644557, 0.641059, 0.637555, 0.634059, 0.634135, 0.631825, 0.631899, 0.627209, 0.622516, 0.617818, 0.616103, 0.613736, 0.610175, 0.606613, 0.605445, 0.603676, 0.604887, 0.600127, 0.604909, 0.588207, 0.581056, 0.576292, 0.566761, 0.555472, 0.545367, 0.538842, 0.529336, 0.518635, 0.506747, 0.499018, 0.491885, 0.484754, 0.475230, 0.464514, 0.454387, 0.444861, 0.437128, 0.415076, 0.401363, 0.390034, 0.378698])
def func(xArray, breakpoint, slopeA, offsetA, slopeB, offsetB):
returnArray = []
for x in xArray:
if x < breakpoint:
returnArray.append(slopeA * x + offsetA)
else:
returnArray.append(slopeB * x + offsetB)
return returnArray
# function for genetic algorithm to minimize (sum of squared error)
def sumOfSquaredError(parameterTuple):
warnings.filterwarnings("ignore") # do not print warnings by genetic algorithm
val = func(xData, *parameterTuple)
return numpy.sum((yData - val) ** 2.0)
def generate_Initial_Parameters():
# min and max used for bounds
maxX = max(xData)
minX = min(xData)
maxY = max(yData)
minY = min(yData)
slope = 10.0 * (maxY - minY) / (maxX - minX) # times 10 for safety margin
parameterBounds = []
parameterBounds.append([minX, maxX]) # search bounds for breakpoint
parameterBounds.append([-slope, slope]) # search bounds for slopeA
parameterBounds.append([minY, maxY]) # search bounds for offsetA
parameterBounds.append([-slope, slope]) # search bounds for slopeB
parameterBounds.append([minY, maxY]) # search bounds for offsetB
result = differential_evolution(sumOfSquaredError, parameterBounds, seed=3)
return result.x
# by default, differential_evolution completes by calling curve_fit() using parameter bounds
geneticParameters = generate_Initial_Parameters()
# call curve_fit without passing bounds from genetic algorithm
fittedParameters, pcov = curve_fit(func, xData, yData, geneticParameters)
print('Parameters:', fittedParameters)
print()
modelPredictions = func(xData, *fittedParameters)
absError = modelPredictions - yData
SE = numpy.square(absError) # squared errors
MSE = numpy.mean(SE) # mean squared errors
RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
Rsquared = 1.0 - (numpy.var(absError) / numpy.var(yData))
print()
print('RMSE:', RMSE)
print('R-squared:', Rsquared)
print()
##########################################################
# graphics output section
def ModelAndScatterPlot(graphWidth, graphHeight):
f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
axes = f.add_subplot(111)
# first the raw data as a scatter plot
axes.plot(xData, yData, 'D')
# create data for the fitted equation plot
xModel = numpy.linspace(min(xData), max(xData))
yModel = func(xModel, *fittedParameters)
# now the model as a line plot
axes.plot(xModel, yModel)
axes.set_xlabel('X Data') # X axis data label
axes.set_ylabel('Y Data') # Y axis data label
plt.show()
plt.close('all') # clean up after using pyplot
graphWidth = 800
graphHeight = 600
ModelAndScatterPlot(graphWidth, graphHeight)

Matlab fit function error : Function value and YDATA sizes are not equal

I'm trying to use the fit function to estimate a 4 parameters model(P B A R) and meet the error following message and I dont know what does it mean.
Error using fit>iFit (line 367)
Function value and YDATA sizes are not equal.
Error in fit (line 108)
[fitobj, goodness, output, convmsg] = iFit( xdatain, ydatain, fittypeobj, ...
The basic function is
function c1 = c1(x,T,P,B,A,R)
if T == 0
c1=0;
else
G = #(t) 0.5*erfc((P./(4*B*R*t)).^0.5.*(B*R*x-t))...
-1/2*(1+P*x+P*t/(B*R))*exp(P*x).*erfc((P./(4*B*R*t)).^0.5.*(B*R*x+t))...
+(P*t/(pi*B*R)).^0.5.*exp(-P*(B*R*x-t).^2./(4*B*R*t)); %first term in the solution
u = #(t) A*t/(B*R);%.
v = #(t) A*(T-t)/(1-B)/R; %.
e = #(t) 2*(u(t.*v(t))).^0.5; %.
H1 = #(t) exp(-u(t)-v(t)).*(besseli(0,e(t))/B+besseli(1,e(t)).*((u(t)./v(t)).^0.5)/(1-B));
GH = #(t) G(t).*H1(t);
c1 = G(T).*exp(-A*T/(B*R))+A/R*integral(GH,0,T); %int((g*H1),0,T);
end
and another function that based on the foregoing function c1 is
function cm = cm(x,time,P,B,A,R,T1)
for i=1:length(time);
if time(i)<T1
cm(i)=c1(x,time(i),P,B,A,R);
else
cm(i)=c1(x,time(i),P,B,A,R)-c1(x,time(i)-T1,P,B,A,R);
end
end
This function mainly divide the data into two parts for different calculation.
I tried to give a reasonable arbitrary four parameters to run cm to obtain a set of time-c data, use the following code
x=2;
time=0.1:0.1:10;
T1=2;
c=cm(x,time,0.8,0.8,0.8,0.8,T1);
and it works well
after that I tried to use fit function to fit the set of data to obtain the four parameters, using the following code
ft = fittype('cm(x,time,P,B,A,R,T1)','independent','time','problem','x'); % independent variable is time, fixed parameter x
>> [f, gof] = fit( time', c', ft, 'Lower', [0, 0, 0, 1,2], 'Upper', [1, 1, 1, 1,2],'problem',x);
thats when I met the error
Error using fit>iFit (line 367)
Function value and YDATA sizes are not equal.
I checked the input time-c data that obtained from function cm, they have the same size, so I don'k see anything wrong with the input data. I suspect it is the problem with the function that fit function does not work.
Can anyone help me with this problem? Besides, what does it mean by YDATA?
Thank you in advance !

surface(2d) fit in MATLAB with anonymous function

I want to use fit in MATLAB for two dimensions.
I defined function separately and then called it with fittype
x has two columns!
f=fittype('#(x)myfun(beta1, beta2,beta3, x)')
and then customize in options my start point and algorithm.
then use [results, goodness]=fit(x, zdata,f, options), but I have a error
??? Too many inputs to FITTYPE function.
Error in ==> fit at 443
errstr = handleerr( errid, errmsg, suppresserr );
I also tried with [results, goodness]=fit([x(:,1), x(:,2)], zdata,f, options),
and still have the same problem.
I used fit -all
XDATA must be a matrix with one to two columns.
Error in ==> fit at 115
errstr = handleerr('curvefit:fit:xDataMustBeColumnVector', ...
for me sounds meaningles , since I have my x in two columns!!!!
and then which fit -all
/Applications/matlab/MATLAB_R2010a.app/toolbox/curvefit/curvefit/fit.m
/Applications/matlab/MATLAB_R2010a.app/toolbox/stats/#ProbDistUnivParam/fit.m % ProbDistUnivParam method
/Applications/matlab/MATLAB_R2010a.app/toolbox/stats/#NaiveBayes/fit.m % NaiveBayes method
/Applications/matlab/MATLAB_R2010a.app/toolbox/stats/#gmdistribution/fit.m % gmdistribution method
could you please help me to use fit and fittype to fit my 2 dimension data?
{please don't introduce me meshgrid and other commands.}
You need to add the parameter 'numindep' = 2 which indicates that your fit is for a surface (i.e has two independent variables).
Here's an example using your function with the Franke data using a string:
load franke
ft = fittype('myfun(beta1, beta2, beta3, [x, y])', 'numindep', 2)
[results, goodness] = fit([x, y], z, ft)
Here's an example using your function with the Franke data using an anonymous function:
load franke
ft = fittype(#(beta1,beta2,beta3, x, y)myfun(beta1, beta2,beta3, [x, y]), 'numindep', 2)
[results, goodness] = fit([x, y], z, ft)