// 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] = lsqnonneg (varargin)
// Solves nonnegative least-squares curve fitting problems.
//
// Calling Sequence
// xopt = lsqnonneg(C,d)
// xopt = lsqnonneg(C,d,param)
// [xopt,resnorm,residual,exitflag,output,lambda] = lsqnonneg( ... )
//
// Parameters
// C : a matrix of double, represents the multiplier of the solution x in the expression C⋅x - d. Number of columns in C is equal to the number of elements in x.
// d : a vector of double, represents the additive constant term in the expression C⋅x - d. Number of elements in d is equal to the number of rows in C matrix.
// xopt : a vector of double, the computed solution of the optimization problem.
// resnorm : a double, objective value returned as the scalar value norm(C⋅x-d)^2.
// residual : a vector of double, solution residuals returned as the vector d-C⋅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.
//
// Description
// Solves nonnegative least-squares curve fitting problems specified by :
//
//
// \begin{eqnarray}
// &\mbox{min}_{x}
// & 1/2||C⋅x - d||_2^2 \\
// & & x \geq 0 \\
// \end{eqnarray}
//
//
// The routine calls Ipopt for solving the nonnegative least-squares curve fitting problems, 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", [---]);
// 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.
// Default Values : options = list("MaxIter", [3000], "CpuTime", [600]);
//
//
// 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 basic lsqnonneg problem
// C = [1 1 1;
// 1 1 0;
// 0 1 1;
// 1 0 0;
// 0 0 1]
// d = [89;
// 67;
// 53;
// 35;
// 20;]
// [xopt,resnorm,residual,exitflag,output,lambda] = lsqnonneg(C,d)
// 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 ) then
errmsg = msprintf(gettext("%s: Unexpected number of input arguments : %d provided while should be in the set of [2 3]"), "lsqnonneg", rhs);
error(errmsg)
end
C = varargin(1);
d = varargin(2);
nbVar = size(C,2);
if(nbVar == 0) then
errmsg = msprintf(gettext("%s: Cannot determine the number of variables because input objective coefficients is empty"), "lsqnonneg");
error(errmsg);
end
if ( rhs<3 | size(varargin(3)) ==0 ) then
param = list();
else
param =varargin(3);
end
if (type(param) ~= 15) then
errmsg = msprintf(gettext("%s: param should be a list "), "lsqnonneg");
error(errmsg);
end
//Check type of variables
Checktype("lsqnonneg", C, "C", 1, "constant")
Checktype("lsqnonneg", d, "d", 2, "constant")
if (modulo(size(param),2)) then
errmsg = msprintf(gettext("%s: Size of parameters should be even"), "lsqnonneg");
error(errmsg);
end
options = list( "MaxIter" , [3000], ...
"CpuTime" , [600] ...
);
for i = 1:(size(param))/2
select convstr(param(2*i-1),'l')
case "maxiter" then
options(2*i) = param(2*i);
case "cputime" then
options(2*i) = param(2*i);
else
errmsg = msprintf(gettext("%s: Unrecognized parameter name ''%s''."), "lsqlin", param(2*i-1));
error(errmsg)
end
end
// Check if the user gives row vector
// and Changing it to a column matrix
if (size(d,2)== [nbVar]) then
d=d';
end
//Check the size of f which should equal to the number of variable
if ( size(d,1) ~= size(C,1)) then
errmsg = msprintf(gettext("%s: The number of rows in C must be equal the number of elements of d"), "lsqnonneg");
error(errmsg);
end
//Converting it into Quadratic Programming Problem
Q = C'*C;
p = [-C'*d]';
op_add = d'*d;
lb = repmat(0,1,nbVar);
ub = repmat(%inf,1,nbVar);
x0 = repmat(0,1,nbVar);;
conMatrix = [];
nbCon = size(conMatrix,1);
conLB = [];
conUB = [] ;
[xopt,fopt,status,iter,Zl,Zu,lmbda] = solveqp(nbVar,nbCon,Q,p,conMatrix,conLB,conUB,lb,ub,x0,options);
xopt = xopt';
residual = -1*(C*xopt-d);
resnorm = residual'*residual;
exitflag = status;
output = struct("Iterations" , [], ..
"ConstrViolation" ,[]);
output.Iterations = iter;
output.ConstrViolation = max([0;(lb'-xopt);(xopt-ub')]);
lambda = struct("lower" , [], ..
"upper" , []);
lambda.lower = Zl;
lambda.upper = Zu;
select status
case 0 then
printf("\nOptimal Solution Found.\n");
case 1 then
printf("\nMaximum Number of Iterations Exceeded. Output may not be optimal.\n");
case 2 then
printf("\nMaximum CPU Time exceeded. Output may not be optimal.\n");
case 3 then
printf("\nStop at Tiny Step\n");
case 4 then
printf("\nSolved To Acceptable Level\n");
case 5 then
printf("\nConverged to a point of local infeasibility.\n");
case 6 then
printf("\nStopping optimization at current point as requested by user.\n");
case 7 then
printf("\nFeasible point for square problem found.\n");
case 8 then
printf("\nIterates diverging; problem might be unbounded.\n");
case 9 then
printf("\nRestoration Failed!\n");
case 10 then
printf("\nError in step computation (regularization becomes too large?)!\n");
case 12 then
printf("\nProblem has too few degrees of freedom.\n");
case 13 then
printf("\nInvalid option thrown back by Ipopt\n");
case 14 then
printf("\nNot enough memory.\n");
case 15 then
printf("\nINTERNAL ERROR: Unknown SolverReturn value - Notify Ipopt Authors.\n");
else
printf("\nInvalid status returned. Notify the Toolbox authors\n");
break;
end
endfunction