cns01.gms : Test how bounds change the 1-variable CNS model sqr(x)=4

Description

Single equation square nonlinear system with bounds varying
from free over exactly at the solution to a fixed variable.

The model without bounds has two solutions, x = 2 and x = -2,
and the one that is returned may depend on the initial value.
Bounds may of course exclude one of the solutions.


Small Model of Type : CNS


Category : GAMS Test library


Main file : cns01.gms

$title Test how bounds change the 1-variable CNS model sqr(x)=4 (cns01,SEQ=91)

$onText
  Single equation square nonlinear system with bounds varying
  from free over exactly at the solution to a fixed variable.

  The model without bounds has two solutions, x = 2 and x = -2,
  and the one that is returned may depend on the initial value.
  Bounds may of course exclude one of the solutions.

$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% /;

variable x;
equation f;
model m / f /;

f .. x*x =e= 4;

option limrow = 0, limcol = 0;


* Case 1: either x = -2 or x = 2 is OK.
x.l = 8;
solve m using cns;
abort.noError$[slowOK and %solveStat.resourceInterrupt% = m.solvestat] 'Solve too slow';
abort$(m.solvestat <> %solveStat.normalCompletion% or m.modelstat <> %modelStat.solved%)
   'bad return codes', m.solvestat, m.modelstat;
abort$(abs(sqr(x.l)-4) > tol)                  'x.l should be + or -2',x.l;

* Case 2: either x = -2 or x = 2 is OK.
x.l = -8;
solve m using cns;
abort.noError$[slowOK and %solveStat.resourceInterrupt% = m.solvestat] 'Solve too slow';
abort$(m.solvestat <> %solveStat.normalCompletion% or m.modelstat <> %modelStat.solved%)
   'bad return codes', m.solvestat, m.modelstat;
abort$(abs(sqr(x.l)-4) > tol)                  'x.l should be + or -2',x.l;

* Case 3: The Jacobian is singular in the initial point, but OK at
*         the two solutions. The return will be solver specific.
x.l = 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';
else
  abort$(m.modelstat <> %modelStat.solved%) 'bad modelstat', m.modelstat;
  abort$(abs(sqr(x.l)-4) > tol)                'x.l should be + or -2',x.l;
};

* Case 4: Must return x = 2 because of the bounds.
x.lo = 1; x.up = 3; x.l = 8;
solve m using cns;
abort.noError$[slowOK and %solveStat.resourceInterrupt% = m.solvestat] 'Solve too slow';
abort$(m.solvestat <> %solveStat.normalCompletion% or
       (m.modelstat <> %modelStat.solvedUnique% eqv m.modelstat <> %modelStat.solved%))
   'bad return codes', m.solvestat, m.modelstat;
abort$(abs(x.l-2) > tol)  'x.l should be 2',x.l;

* Case 5: Must return x = 2 because of the bounds. Note that the
*         solution is at the bound, but the bound is not violated.
x.lo = 2; x.up = 3; x.l = 8;
solve m using cns;
abort.noError$[slowOK and %solveStat.resourceInterrupt% = m.solvestat] 'Solve too slow';
abort$(m.solvestat <> %solveStat.normalCompletion% or
       (m.modelstat <> %modelStat.solvedUnique% eqv m.modelstat <> %modelStat.solved%))
   'bad return codes', m.solvestat, m.modelstat;
abort$(abs(x.l-2) > tol)  'x.l should be 2',x.l;

* The following tests are all about specific behavior with respect to "holdFixed".
* Since this is ignored 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 6a: The model is nonsquare because there are no non-fixed
*          variables left but there is one equation.   How this is treated
*          depends on the holdfixed setting
*          If holdfixed is 0 GAMS rejects this model as nonsquare
*          - it never gets to the solver
m.holdfixed = 0; x.lo = 2; x.up = 2; x.l = 8; 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;

* Case 6b: If holdfixed = 1 GAMS accepts the model and the solver
*          should give a reasonable return.   N.B. GAMS' acceptance
*          of this model happens somewhat by chance - it will not always
*          be the case that setting holdfixed=1 will recover
*          squareness.  In this case, the number of equations that
*          drop out is equal to the number of fixed variables, so we
*          stay square
m.holdfixed = 1; x.lo = 2; x.up = 2; x.l = 8; solve m using cns;
abort.noError$[slowOK and %solveStat.resourceInterrupt% = m.solvestat] 'Solve too slow';
abort$(m.solvestat <> %solveStat.normalCompletion%) 'bad solvestat', m.solvestat;
abort$(m.modelstat <> %modelStat.solvedUnique% and m.modelstat <> %modelStat.solved%) 'bad modelstat', m.modelstat;

* Case 8a: Same checks as 6a and 6b, but the variable is fixed to an
*          infeasible point
m.holdfixed = 0; x.lo = 3; x.up = 3; x.l = 8; 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;

* Case 8b: If holdfixed = 1 GAMS will see a square model, but
*          the removed row will look like 9 =e= 4.  GAMS will
*          give an error on model generation.
m.holdfixed = 1; x.lo = 3; x.up = 3; x.l = 8; 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;