$title CNS test - holdfixed and singular models (cns03,SEQ=93)

$onText
  This model is singular if it is square.
  The way it is reported depends on
  whether holdfixed is turned on or off.

$offText

maxexecerror = 1;
$if not set TESTTOL $set TESTTOL 1e-6
scalar tol / %TESTTOL% /;
$if not set SLOWOK $set SLOWOK 0
scalar slowOK 'slow solves are OK: just abort.noerror in this case' / %SLOWOK% /;
scalar havesol / 0 /;

variable x1,x2,x3;
equation e1,e2;

e1 .. x1 =e= 5;
e2 .. x1 + x2 +x3 =e= 7;

x1.fx = 5;

model m / all /;

* Case 1: with holdfixed=0, GAMS passes on a square 2x2 model
*         in x2 and x3 - x1 is ignored since it is fixed
m.holdfixed = 0;
solve m using cns;
abort.noError$[slowOK and %solveStat.resourceInterrupt% = m.solvestat] 'Solve too slow';
abort$(m.solvestat <> %solveStat.normalCompletion%) 'bad solvestat', m.solvestat;
if   { (m.modelstat = %modelStat.locallyInfeasible%),
* locally infeasible, should report a row that way
  abort$(m.numinfes < 1)                            'wrong .numinfes';
* this next check may be too ambitious - we'll see how it flies
  abort$(m.numdepnd < 1)                            'wrong .numdepnd';
elseif (m.modelstat = %modelStat.solvedSingular%),
* solved singular, should indicate that one dependency exists
  havesol = 1;
  abort$(m.numdepnd <> 1)                           'wrong .numdepnd';
else
  havesol = 1;
  abort$(m.modelstat <> %modelStat.solved%) 'bad modelstat', m.modelstat;
};
if {havesol,
  abort$(abs(x2.l+x3.l-2) > tol)     'bad x2.l+x3.l';
  abort$(abs(e1.l-5) > tol)          'bad e1.l';
  abort$(abs(e2.l-7) > tol)          'bad e2.l';
};

* The following test is about specific behavior with respect to "holdFixed".
* Since this is reset with asyncronous solves, exit early in the following cases
$ifE %GAMS.solveLink%=%solveLink.asyncGrid%       $exit
$ifE %GAMS.solveLink%=%solveLink.asyncSimulate%   $exit
$ifE %GAMS.solveLink%=%solveLink.aSyncThreads%    $exit
$ifE %GAMS.solveLink%=%solveLink.threadsSimulate% $exit


* Case 2: with holdfixed=1, GAMS reduces the model to something that
*         is not square. It takes out x1 and e1 and is left with 2
*         variables (x2 & x3) and 1 equation.
m.holdfixed = 1;
solve m using cns;
abort.noerror$[slowOK and %solvestat.ResourceInterrupt% = m.solvestat] 'Solve too slow';
abort$(execerror=0) 'previous solve should have given exec errors';
abort$(m.solvestat <> %solveStat.solveProcessingSkipped% or m.modelstat <> %modelStat.noSolutionReturned%)
   'bad return codes', m.solvestat, m.modelstat;
execerror = 0;
