// Copyright (C) 2015 - IIT Bombay - FOSSEE
//
// This file must be used under the terms of the CeCILL.
// This source file is licensed as described in the file COPYING, which
// you should have received as part of this distribution. The terms
// are also available at
// http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
// Author: Harpreet Singh
// Organization: FOSSEE, IIT Bombay
// Email: toolbox@scilab.in
function [xopt,resnorm,residual,exitflag,output,lambda,gradient] = lsqnonlin (varargin)
// Solves a non linear data fitting problems.
//
// Calling Sequence
// xopt = lsqnonlin(fun,x0)
// xopt = lsqnonlin(fun,x0,lb,ub)
// xopt = lsqnonlin(fun,x0,lb,ub,options)
// [xopt,resnorm] = lsqnonlin( ... )
// [xopt,resnorm,residual] = lsqnonlin( ... )
// [xopt,resnorm,residual,exitflag] = lsqnonlin( ... )
// [xopt,resnorm,residual,exitflag,output,lambda,gradient] = lsqnonlin( ... )
//
// Parameters
// fun : a function, representing the objective function and gradient (if given) of the problem
// x0 : a vector of double, contains initial guess of variables.
// lb : a vector of double, contains lower bounds of the variables.
// ub : a vector of double, contains upper bounds of the variables.
// options : a list containing the parameters to be set.
// xopt : a vector of double, the computed solution of the optimization problem.
// resnorm : a double, objective value returned as the scalar value i.e. sum(fun(x).^2).
// residual : a vector of double, solution of objective function i.e. fun(x).
// exitflag : The exit status. See below for details.
// output : The structure consist of statistics about the optimization. See below for details.
// lambda : The structure consist of the Lagrange multipliers at the solution of problem. See below for details.
// gradient : a vector of doubles, containing the Objective's gradient of the solution.
//
// Description
// Search the minimum of a constrained non-linear least square problem specified by :
//
//
// \begin{eqnarray}
// &\mbox{min}_{x}
// & (f_1(x)^2 + f_2(x)^2 + ... + f_n(x)^2) \\
// & lb \leq x \leq ub \\
// \end{eqnarray}
//
//
// The routine calls fmincon which calls Ipopt for solving the non-linear least square problem, Ipopt is a library written in C++.
//
// The options allows the user to set various parameters of the Optimization problem.
// It should be defined as type "list" and contains the following fields.
//
// Syntax : options= list("MaxIter", [---], "CpuTime", [---],"GradObj", "on");
// MaxIter : a Scalar, containing the Maximum Number of Iteration that the solver should take.
// CpuTime : a Scalar, containing the Maximum amount of CPU Time that the solver should take.
// GradObj : a string, representing the gradient function is on or off.
// Default Values : options = list("MaxIter", [3000], "CpuTime", [600], "GradObj", "off");
//
//
// The exitflag allows to know the status of the optimization which is given back by Ipopt.
//
// exitflag=0 : Optimal Solution Found
// exitflag=1 : Maximum Number of Iterations Exceeded. Output may not be optimal.
// exitflag=2 : Maximum CPU Time exceeded. Output may not be optimal.
// exitflag=3 : Stop at Tiny Step.
// exitflag=4 : Solved To Acceptable Level.
// exitflag=5 : Converged to a point of local infeasibility.
//
//
// For more details on exitflag see the ipopt documentation, go to http://www.coin-or.org/Ipopt/documentation/
//
// The output data structure contains detailed informations about the optimization process.
// It has type "struct" and contains the following fields.
//
// output.iterations: The number of iterations performed during the search
// output.constrviolation: The max-norm of the constraint violation.
//
//
// The lambda data structure contains the Lagrange multipliers at the end
// of optimization. In the current version the values are returned only when the the solution is optimal.
// It has type "struct" and contains the following fields.
//
// lambda.lower: The Lagrange multipliers for the lower bound constraints.
// lambda.upper: The Lagrange multipliers for the upper bound constraints.
//
//
// Examples
// //A simple non-linear least square example taken from leastsq default present in scilab
// function y=yth(t, x)
// y = x(1)*exp(-x(2)*t)
// endfunction
// // we have the m measures (ti, yi):
// m = 10;
// tm = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5]';
// ym = [0.79, 0.59, 0.47, 0.36, 0.29, 0.23, 0.17, 0.15, 0.12, 0.08]';
// // measure weights (here all equal to 1...)
// wm = ones(m,1);
// // and we want to find the parameters x such that the model fits the given
// // data in the least square sense:
// //
// // minimize f(x) = sum_i wm(i)^2 ( yth(tm(i),x) - ym(i) )^2
// // initial parameters guess
// x0 = [1.5 ; 0.8];
// // in the first examples, we define the function fun and dfun
// // in scilab language
// function y=myfun(x, tm, ym, wm)
// y = wm.*( yth(tm, x) - ym )
// endfunction
// // the simplest call
// [xopt,resnorm,residual,exitflag,output,lambda,gradient] = lsqnonlin(myfun,x0)
// // Press ENTER to continue
//
// Examples
// //A basic example taken from leastsq default present in scilab with gradient
// function y=yth(t, x)
// y = x(1)*exp(-x(2)*t)
// endfunction
// // we have the m measures (ti, yi):
// m = 10;
// tm = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5]';
// ym = [0.79, 0.59, 0.47, 0.36, 0.29, 0.23, 0.17, 0.15, 0.12, 0.08]';
// // measure weights (here all equal to 1...)
// wm = ones(m,1);
// // and we want to find the parameters x such that the model fits the given
// // data in the least square sense:
// //
// // minimize f(x) = sum_i wm(i)^2 ( yth(tm(i),x) - ym(i) )^2
// // initial parameters guess
// x0 = [1.5 ; 0.8];
// // in the first examples, we define the function fun and dfun
// // in scilab language
// function [y,dy]=myfun(x, tm, ym, wm)
// y = wm.*( yth(tm, x) - ym )
// v = wm.*exp(-x(2)*tm)
// dy = [v , -x(1)*tm.*v]
// endfunction
// options = list("GradObj", "on")
// [xopt,resnorm,residual,exitflag,output,lambda,gradient] = lsqnonlin(myfun,x0,[],[],options)
// Authors
// Harpreet Singh
//To check the number of input and output argument
[lhs , rhs] = argn();
//To check the number of argument given by user
if ( rhs < 2 | rhs == 3 | rhs > 5 ) then
errmsg = msprintf(gettext("%s: Unexpected number of input arguments : %d provided while should be in the set of [2 4 5]"), "lsqnonlin", rhs);
error(errmsg)
end
// Initializing all the values to empty matrix
_fun = varargin(1);
x0 = varargin(2);
nbVar = size(x0,'*');
if(nbVar == 0) then
errmsg = msprintf(gettext("%s: Cannot determine the number of variables because input initial guess is empty"), "lsqcurvefit");
error(errmsg);
end
if (size(x0,2)~=1) then
errmsg = msprintf(gettext("%s: Initial Guess should be a column matrix"), "lsqcurvefit");
error(errmsg);
end
if ( rhs<3 ) then
lb = repmat(-%inf,nbVar,1);
ub = repmat(%inf,nbVar,1);
else
lb = varargin(3);
ub = varargin(4);
end
if ( rhs<5 | size(varargin(5)) ==0 ) then
param = list();
else
param =varargin(5);
end
if (size(lb,2)==0) then
lb = repmat(-%inf,nbVar,1);
end
if (size(ub,2)==0) then
ub = repmat(%inf,nbVar,1);
end
//Check type of variables
Checktype("lsqnonlin", _fun, "fun", 1, "function")
Checktype("lsqnonlin", x0, "x0", 2, "constant")
Checktype("lsqnonlin", lb, "lb", 3, "constant")
Checktype("lsqnonlin", ub, "ub", 4, "constant")
Checktype("lsqnonlin", param, "param", 5, "list")
if (modulo(size(param),2)) then
errmsg = msprintf(gettext("%s: Size of parameters should be even"), "lsqnonlin");
error(errmsg);
end
options = list( "MaxIter" , [3000], ...
"CpuTime" , [600], ...
"GradObj" , ["off"]);
for i = 1:(size(param))/2
select convstr(param(2*i-1),'l')
case "maxiter" then
options(2) = param(2*i);
Checktype("lsqcurvefit", param(2*i), "maxiter", i, "constant")
case "cputime" then
options(4) = param(2*i);
Checktype("lsqcurvefit", param(2*i), "cputime", i, "constant")
case "gradobj" then
if (convstr(param(2*i))=="on") then
options(6) = "on";
else
errmsg = msprintf(gettext("%s: Unrecognized String [%s] entered for gradobj."), "lsqnonlin",param(2*i));
error(errmsg);
end
else
errmsg = msprintf(gettext("%s: Unrecognized parameter name ''%s''."), "lsqnonlin", param(2*i-1));
error(errmsg)
end
end
// Check if the user gives row vector
// and Changing it to a column matrix
if (size(lb,2)== [nbVar]) then
lb = lb(:);
end
if (size(ub,2)== [nbVar]) then
ub = ub(:);
end
//To check the match between fun (1st Parameter) and x0 (2nd Parameter)
if(execstr('init=_fun(x0)','errcatch')==21) then
errmsg = msprintf(gettext("%s: Objective function and x0 did not match"), "lsqnonlin");
error(errmsg);
end
ierr = execstr('init=_fun(x0)', "errcatch")
if ierr <> 0 then
lamsg = lasterror();
lclmsg = "%s: Error while evaluating the function: ""%s""\n";
error(msprintf(gettext(lclmsg), "lsqnonlin", lamsg));
end
//Check the size of Lower Bound which should be equal to the number of variables
if ( size(lb,1) ~= nbVar) then
errmsg = msprintf(gettext("%s: The Lower Bound is not equal to the number of variables"), "lsqnonlin");
error(errmsg);
end
//Check the size of Upper Bound which should equal to the number of variables
if ( size(ub,1) ~= nbVar) then
errmsg = msprintf(gettext("%s: The Upper Bound is not equal to the number of variables"), "lsqnonlin");
error(errmsg);
end
//Check if the user gives a matrix instead of a vector
if (size(lb,1)~=1)& (size(lb,2)~=1) then
errmsg = msprintf(gettext("%s: Lower Bound should be a vector"), "lsqnonlin");
error(errmsg);
end
if (size(ub,1)~=1)& (size(ub,2)~=1) then
errmsg = msprintf(gettext("%s: Upper Bound should be a vector"), "lsqnonlin");
error(errmsg);
end
for i = 1:nbVar
if(lb(i)>ub(i))
errmsg = msprintf(gettext("%s: Problem has inconsistent variable bounds"), "lsqnonlin");
error(errmsg);
end
end
//Converting it into fmincon Problem
function [y] = __fun(x)
[yVal] = _fun(x);
y = sum(yVal.^2);
endfunction
if (options(6) == "on")
ierr = execstr('[initx initdx]=_fun(x0)', "errcatch")
if ierr <> 0 then
lamsg = lasterror();
lclmsg = "%s: Error while evaluating the function: ""%s""\n";
error(msprintf(gettext(lclmsg), "lsqnonlin", lamsg));
end
function [dy] = __fGrad(x)
[y_user,dy_user] = _fun(x)
dy = 2*y_user'*dy_user;
endfunction
else
function [dy] = __fGrad(x)
y_user = _fun(x);
dy_nd = numderivative(_fun,x);
dy = 2*y_user'*dy_nd;
endfunction
end
options(6) = __fGrad;
function [c, ceq] = nlc(x)
c = [];
ceq = [];
endfunction
[xopt,fopt,exitflag,output,lambda_fmincon,gradient] = fmincon(__fun,x0,[],[],[],[],lb,ub,nlc,options);
lambda = struct("lower", lambda_fmincon.lower,"upper",lambda_fmincon.upper);
resnorm = sum(_fun(xopt).^2);
residual = _fun(xopt);
endfunction