// 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] = lsqlin (varargin)
// Solves a linear quadratic problem.
//
// Calling Sequence
// xopt = lsqlin(C,d,A,b)
// xopt = lsqlin(C,d,A,b,Aeq,beq)
// xopt = lsqlin(C,d,A,b,Aeq,beq,lb,ub)
// xopt = lsqlin(C,d,A,b,Aeq,beq,lb,ub,x0)
// xopt = lsqlin(C,d,A,b,Aeq,beq,lb,ub,x0,param)
// [xopt,resnorm,residual,exitflag,output,lambda] = lsqlin( ... )
//
// 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.
// A : a matrix of double, represents the linear coefficients in the inequality constraints A⋅x ≤ b.
// b : a vector of double, represents the linear coefficients in the inequality constraints A⋅x ≤ b.
// Aeq : a matrix of double, represents the linear coefficients in the equality constraints Aeq⋅x = beq.
// beq : a vector of double, represents the linear coefficients in the equality constraints Aeq⋅x = beq.
// lb : a vector of double, contains lower bounds of the variables.
// ub : a vector of double, contains upper bounds of the variables.
// x0 : a vector of double, contains initial guess of variables.
// param : 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 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
// Search the minimum of a constrained linear least square problem specified by :
//
//
// \begin{eqnarray}
// &\mbox{min}_{x}
// & 1/2||C⋅x - d||_2^2 \\
// & \text{subject to} & A⋅x \leq b \\
// & & Aeq⋅x = beq \\
// & & lb \leq x \leq ub \\
// \end{eqnarray}
//
//
// The routine calls Ipopt for solving the linear least square problem, Ipopt is a library written in C++.
//
// 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.
// lambda.eqlin: The Lagrange multipliers for the linear equality constraints.
// lambda.ineqlin: The Lagrange multipliers for the linear inequality constraints.
//
//
// Examples
// //A simple linear least square example
// C = [ 2 0;
// -1 1;
// 0 2]
// d = [1
// 0
// -1];
// A = [10 -2;
// -2 10];
// b = [4
// -4];
// [xopt,resnorm,residual,exitflag,output,lambda] = lsqlin(C,d,A,b)
// // Press ENTER to continue
//
// Examples
// //A basic example for equality, inequality constraints and variable bounds
// C = [1 1 1;
// 1 1 0;
// 0 1 1;
// 1 0 0;
// 0 0 1]
// d = [89;
// 67;
// 53;
// 35;
// 20;]
// A = [3 2 1;
// 2 3 4;
// 1 2 3];
// b = [191
// 209
// 162];
// Aeq = [1 2 1];
// beq = 10;
// lb = repmat(0.1,3,1);
// ub = repmat(4,3,1);
// [xopt,resnorm,residual,exitflag,output,lambda] = lsqlin(C,d,A,b,Aeq,beq,lb,ub)
// 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 < 4 | rhs == 5 | rhs == 7 | rhs > 10 ) then
errmsg = msprintf(gettext("%s: Unexpected number of input arguments : %d provided while should be in the set of [4 6 8 9 10]"), "lsqlin", rhs);
error(errmsg)
end
// Initializing all the values to empty matrix
C=[];
d=[];
A=[];
b=[];
Aeq=[];
beq=[];
lb=[];
ub=[];
x0=[];
C = varargin(1);
d = varargin(2);
A = varargin(3);
b = varargin(4);
nbVar = size(C,2);
if ( rhs<5 ) then
Aeq = []
beq = []
else
Aeq = varargin(5);
beq = varargin(6);
end
if ( rhs<7 ) then
lb = repmat(-%inf,nbVar,1);
ub = repmat(%inf,nbVar,1);
else
lb = varargin(7);
ub = varargin(8);
end
if ( rhs<9 | size(varargin(9)) ==0 ) then
x0 = repmat(0,nbVar,1)
else
x0 = varargin(9);
end
if ( rhs<10 | size(varargin(10)) ==0 ) then
param = list();
else
param =varargin(10);
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
if (type(param) ~= 15) then
errmsg = msprintf(gettext("%s: param should be a list "), "lsqlin");
error(errmsg);
end
if (modulo(size(param),2)) then
errmsg = msprintf(gettext("%s: Size of parameters should be even"), "lsqlin");
error(errmsg);
end
options = list( "MaxIter" , [3000], ...
"CpuTime" , [600] ...
);
for i = 1:(size(param))/2
select param(2*i-1)
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
nbConInEq = size(A,1);
nbConEq = size(Aeq,1);
// Check if the user gives row vector
// and Changing it to a column matrix
if (size(d,2)== [nbVar]) then
d=d';
end
if (size(lb,2)== [nbVar]) then
lb = lb';
end
if (size(ub,2)== [nbVar]) then
ub = ub';
end
if (size(b,2)==nbConInEq) then
b = b';
end
if (size(beq,2)== nbConEq) then
beq = beq';
end
if (size(x0,2)== [nbVar]) then
x0=x0';
end
//Check the size of d 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"), "lsqlin");
error(errmsg);
end
//Check the size of inequality constraint which should be equal to the number of variables
if ( size(A,2) ~= nbVar & size(A,2) ~= 0) then
errmsg = msprintf(gettext("%s: The number of columns in A must be the same as the number of columns in C"), "lsqlin");
error(errmsg);
end
//Check the size of equality constraint which should be equal to the number of variables
if ( size(Aeq,2) ~= nbVar & size(Aeq,2) ~= 0 ) then
errmsg = msprintf(gettext("%s: The number of columns in Aeq must be the same as the number of columns in C"), "lsqlin");
error(errmsg);
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"), "lsqlin");
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"), "lsqlin");
error(errmsg);
end
//Check the size of constraints of Lower Bound which should equal to the number of constraints
if ( size(b,1) ~= nbConInEq & size(b,1) ~= 0) then
errmsg = msprintf(gettext("%s: The number of rows in A must be the same as the number of elements of b"), "lsqlin");
error(errmsg);
end
//Check the size of constraints of Upper Bound which should equal to the number of constraints
if ( size(beq,1) ~= nbConEq & size(beq,1) ~= 0) then
errmsg = msprintf(gettext("%s: The number of rows in Aeq must be the same as the number of elements of beq"), "lsqlin");
error(errmsg);
end
//Check the size of initial of variables which should equal to the number of variables
if ( size(x0,1) ~= nbVar) then
warnmsg = msprintf(gettext("%s: Ignoring initial guess of variables as it is not equal to the number of variables"), "lsqlin");
warning(warnmsg);
x0 = repmat(0,nbVar,1);
end
//Check if the user gives a matrix instead of a vector
if ((size(d,1)~=1)& (size(d,2)~=1)) then
errmsg = msprintf(gettext("%s: d should be a vector"), "lsqlin");
error(errmsg);
end
if (size(lb,1)~=1)& (size(lb,2)~=1) then
errmsg = msprintf(gettext("%s: Lower Bound should be a vector"), "lsqlin");
error(errmsg);
end
if (size(ub,1)~=1)& (size(ub,2)~=1) then
errmsg = msprintf(gettext("%s: Upper Bound should be a vector"), "lsqlin");
error(errmsg);
end
if (nbConInEq) then
if ((size(b,1)~=1)& (size(b,2)~=1)) then
errmsg = msprintf(gettext("%s: Constraint Lower Bound should be a vector"), "lsqlin");
error(errmsg);
end
end
if (nbConEq) then
if (size(beq,1)~=1)& (size(beq,2)~=1) then
errmsg = msprintf(gettext("%s: Constraint should be a vector"), "lsqlin");
error(errmsg);
end
end
for i = 1:nbConInEq
if (b(i) == -%inf)
errmsg = msprintf(gettext("%s: Value of b can not be negative infinity"), "lsqlin");
error(errmsg);
end
end
for i = 1:nbConEq
if (beq(i) == -%inf)
errmsg = msprintf(gettext("%s: Value of beq can not be negative infinity"), "lsqlin");
error(errmsg);
end
end
for i = 1:nbVar
if(lb(i)>ub(i))
errmsg = msprintf(gettext("%s: Problem has inconsistent variable bounds"), "lsqlin");
error(errmsg);
end
end
//Converting it into Quadratic Programming Problem
H = C'*C;
f = [-C'*d]';
op_add = d'*d;
lb = lb';
ub = ub';
x0 = x0';
conMatrix = [Aeq;A];
nbCon = size(conMatrix,1);
conLB = [beq; repmat(-%inf,nbConInEq,1)]';
conUB = [beq;b]' ;
[xopt,fopt,status,iter,Zl,Zu,lmbda] = solveqp(nbVar,nbCon,H,f,conMatrix,conLB,conUB,lb,ub,x0,options);
xopt = xopt';
residual = d-C*xopt;
resnorm = residual'*residual;
exitflag = status;
output = struct("Iterations" , [], ..
"ConstrViolation" ,[]);
output.Iterations = iter;
output.ConstrViolation = max([0;norm(Aeq*xopt-beq, 'inf');(lb'-xopt);(xopt-ub');(A*xopt-b)]);
lambda = struct("lower" , [], ..
"upper" , [], ..
"eqlin" , [], ..
"ineqlin" , []);
lambda.lower = Zl;
lambda.upper = Zu;
lambda.eqlin = lmbda(1:nbConEq);
lambda.ineqlin = lmbda(nbConEq+1:nbCon);
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