nlpec03.gms : NLPEC test suite - loop over option combos

Description

This test is similar in nature to nlpec01.

This model takes a set of NLPEC option combinations and runs the NLPEC solver
on the mpec01 model from testlib for each combination.  Any errors
(e.g. internal to NLPEC, in the syntax or semantics of the
reforumulated NLP model, etc.) should get flagged because mpec01
includes internal tests that a correct solution has been obtained and
will abort otherwise.

The options combinations to test are chosen strategically so that:
 1. there are not so many to test with
 2. they give good test coverage
 3. they can be easily extended

The set of models to test is only mpec01 in this file, but that can be
changed easily so this model is good for use during development.

Contributor: Steven Dirkse, Aug 2023


Small Model of Type : GAMS


Category : GAMS Test library


Main file : nlpec03.gms

$TITLE 'NLPEC test suite - options test' (NLPEC03,SEQ=943)

$ontext
This test is similar in nature to nlpec01.

This model takes a set of NLPEC option combinations and runs the NLPEC solver
on the mpec01 model from testlib for each combination.  Any errors
(e.g. internal to NLPEC, in the syntax or semantics of the
reforumulated NLP model, etc.) should get flagged because mpec01
includes internal tests that a correct solution has been obtained and
will abort otherwise.

The options combinations to test are chosen strategically so that:
 1. there are not so many to test with
 2. they give good test coverage
 3. they can be easily extended

The set of models to test is only mpec01 in this file, but that can be
changed easily so this model is good for use during development.

Contributor: Steven Dirkse, Aug 2023
$offtext

scalar errCount;

$if set VERBOSE $setEnv VERBOSE true
$onEmbeddedCode Python:
# verify normal operation of NLPEC
#
# Run NLPEC over a wide range of combinations of reformulation options

import os, sys
import subprocess
import gams.transfer as gt

pySysdir = os.environ.get("PYSYSDIR")
if None == pySysdir:
    gamscmd = os.path.join(r"%gams.sysdir% ".strip(), "gams")
else:
    gamscmd = os.path.join(pySysdir, "gams")

optFmt = "{:<12} {:>12} {:>12}\n"

# create an option file for NLPEC
def makeOptFile(fname,sname,optS,optD,*args):
    with open(fname,'w') as f:
        f.write("dotGams {}\n".format(sname))
        f.write("\n")
        f.write(optFmt.format("refType"   ,optS[0],optD[0]))
        f.write(optFmt.format("slack"     ,optS[1],optD[1]))
        f.write(optFmt.format("constraint",optS[2],optD[2]))
        f.write(optFmt.format("aggregate" ,optS[3],optD[3]))
        f.write(optFmt.format("NCPBounds" ,optS[4],optD[4]))
        for t in args:
            for o in t:
                f.write("{}\n".format(o))
    # print ("")
    # print ("makeOptFile: #args = {}".format(len(args)))
    return

# for some reformulations we need additional args 
def getXtraArgs(optS,optD):
    xx = []
    s_initMU = None
    d_initMU = None
    if optS[0] in ["fCMfx", "fCMxf"]:
        s_initMU = "5e-6"
    if optS[0] in ["CMfx", "CMxf"]:
        s_initMU = "8e-3"
    if optS[0] in ["fVUsin", "fVUpow"]:
        s_initMU = "1e-3"
    if optS[0] in ["penalty"]:
        s_initMU = "5e-3"
    if optD[0] in ["fCMfx", "fCMxf"]:
        d_initMU = "5e-6"
    if optD[0] in ["CMfx", "CMxf"]:
        d_initMU = "8e-3"
    if optD[0] in ["fVUsin", "fVUpow"]:
        d_initMU = "1e-3"
    if optD[0] in ["penalty"]:
        d_initMU = "5e-3"
    if (s_initMU is not None) or (d_initMU is not None):
        if s_initMU is None:
            s_initMU = "*"
        if d_initMU is None:
            d_initMU = "*"
        xx.append(optFmt.format("initMU",s_initMU,d_initMU))
        
    return tuple(xx)


# solve model <gms> using optfile=<opt> with NLPEC
# return true if the solve completes successfully
def processGMS(gms,opt):
    # verify the options file exists
    assert(opt>=100)
    optFileOld = "nlpec.{}".format(opt)
    assert(os.path.isfile(optFileOld))
    assert(os.path.isfile(gms))

    # run NLPEC
    cmd = [ gamscmd, gms, "solver=nlpec", "optfile={}".format(opt)]
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        print ("failure in processGMS({},{}): NLPEC run failed".format(gms,opt))
        print ("cmd: ", cmd)
        print ("rc: ", p.returncode)
        with open("stdout.txt","w") as sss:
            sss.write(p.stdout)
        print ("stdout available in file 'stdout.txt'")
    assert(p.returncode == 0)
    return True


def getCombos(row):
    return [ row.iloc[2*k] for k in range(5) ] , [ row.iloc[2*k+1] for k in range(5) ] 


if __name__ == "__main__":
    verbose = False
    eCount = 0
    try:
        if None == pySysdir:
            m = gt.Container("nlpec03_opts.gdx", system_directory=r"%gams.sysdir% ".strip())
        else:
            m = gt.Container("nlpec03_opts.gdx", system_directory=pySysdir)
        df = m['valid10'].records
        verbose = (None != os.environ.get("VERBOSE"))
        # rS, rD = getCombos(df.iloc[0])

        nOptFiles = 0
        nTests = 0
        nDeltas = 0
        gmsfiles = [ "mpec01.gms" ]
        for idx, row in df.iterrows():
            rS, rD = getCombos(row)
            if (nOptFiles >= 999999):  # easy way to be quick and successful
                continue
            nOptFiles += 1
            if verbose:
                print ("Testing row {}: {}".format(idx,row.to_list()))
            xargs = getXtraArgs(rS,rD)
            makeOptFile("nlpec.100","scalar100.gms",rS,rD,xargs)

            for gms in gmsfiles:
                nTests += 1
                # print ("GAMS file to process: {}".format(gms))
                brc = processGMS(gms,100)
                if verbose:
                    print ("  GAMS file: {}  result: {}".format(gms,brc))
                if not brc:
                    nDeltas += 1

        if verbose:
            print ("")
            print ("ALL DONE: {} option files, {} comparisons, {} deltas".format(nOptFiles,nTests,nDeltas))
        assert(0 == nDeltas)

    except Exception as e:
        print(repr(e))
        eCount = eCount + 1
    gams.set('errCount', [eCount])
$offEmbeddedCode errCount

display errCount;
abort$errCount "There were errors in the test run", errCount;