From 277de626187e76659635d0f14886b176a745103b Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Tue, 7 Feb 2023 06:16:49 +0100 Subject: [PATCH] improve writing of ampl starting point - allow to write d segment - allow to specify which initial values to pass on to AMPL --- src/amplsolver/amplsolver.c | 17 ++++++ src/amplsolver/convert_nl.c | 102 +++++++++++++++++++++++++++---- src/amplsolver/convert_nl.h | 9 +++ src/amplsolver/optamplsolver.cpp | 17 ++++++ 4 files changed, 133 insertions(+), 12 deletions(-) diff --git a/src/amplsolver/amplsolver.c b/src/amplsolver/amplsolver.c index 384b77b0..693cb4f0 100644 --- a/src/amplsolver/amplsolver.c +++ b/src/amplsolver/amplsolver.c @@ -41,6 +41,8 @@ typedef struct int stublen; char solver[GMS_SSSIZE]; int nlbinary; + char initprimal[GMS_SSSIZE]; + char initdual[GMS_SSSIZE]; } amplsolver; @@ -166,6 +168,9 @@ int processOptions( as->nlbinary = optGetIntStr(opt, "nlbinary"); + optGetStrStr(opt, "initprimal", as->initprimal); + optGetStrStr(opt, "initdual", as->initdual); + rc = 0; TERMINATE: @@ -194,6 +199,18 @@ void writeNL( writeopts.filename = as->filename; writeopts.binary = as->nlbinary; + writeopts.primalstart = convert_initall; + if( strcmp(as->initprimal, "none") == 0 ) + writeopts.primalstart = convert_initnone; + else if( strcmp(as->initprimal, "nondefault") == 0 ) + writeopts.primalstart = convert_initnondefault; + + writeopts.dualstart = convert_initall; + if( strcmp(as->initdual, "none") == 0 ) + writeopts.dualstart = convert_initnone; + else if( strcmp(as->initdual, "nondefault") == 0 ) + writeopts.dualstart = convert_initnondefault; + if( convertWriteNL(as->gmo, writeopts) == RETURN_ERROR ) { gmoSolveStatSet(as->gmo, gmoSolveStat_Capability); diff --git a/src/amplsolver/convert_nl.c b/src/amplsolver/convert_nl.c index 3c29308e..af61b7ef 100644 --- a/src/amplsolver/convert_nl.c +++ b/src/amplsolver/convert_nl.c @@ -738,32 +738,110 @@ RETURN writeNLVarBounds( return RETURN_OK; } -/** write the x segment */ +/** write the x and d segments */ static RETURN writeNLInitialPoint( struct gmoRec* gmo, convertWriteNLopts writeopts ) { - int nnz = 0; + int nnz; int i; assert(gmo != NULL); - /* count nonzeros */ - for( i = 0; i < gmoN(gmo); ++i ) - if( gmoGetVarLOne(gmo, i) != 0.0 ) - ++nnz; + if( writeopts.dualstart != convert_initnone ) + { + /* count marginal values to be printed */ + if( writeopts.dualstart == convert_initall ) + { + nnz = gmoM(gmo); + } + else + { + nnz = 0; + for( i = 0; i < gmoM(gmo); ++i ) + if( gmoGetEquMOne(gmo, i) != 0.0 ) + ++nnz; + } - if( nnz == 0 ) - return RETURN_OK; + CHECK( writeNLPrintf(writeopts, "d%d\n", nnz) ); + + /* write marginal values */ + for( i = 0; i < gmoM(gmo); ++i ) + if( writeopts.dualstart == convert_initall || gmoGetEquMOne(gmo, i) != 0.0 ) + { + CHECK( writeNLPrintf(writeopts, "%d %g\n", i, gmoGetEquMOne(gmo, i)) ); + } + } - CHECK( writeNLPrintf(writeopts, "x%d\n", nnz) ); + if( writeopts.primalstart != convert_initnone ) + { + /* count level values to be printed */ + if( writeopts.primalstart == convert_initall ) + { + nnz = gmoN(gmo); + } + else + { + nnz = 0; + for( i = 0; i < gmoN(gmo); ++i ) + { + double val = gmoGetVarLOne(gmo, i); + + /* check whether variable level is at default (0 projected onto bounds) */ + double lb = gmoGetVarLowerOne(gmo, i); + double ub = gmoGetVarUpperOne(gmo, i); + if( gmoGetVarTypeOne(gmo, i) == gmovar_SC || gmoGetVarTypeOne(gmo, i) == gmovar_SI ) + { + /* correct to actual bounds if semi-cont/integral */ + if( lb != gmoMinf(gmo) && lb > 0.0 ) + lb = 0.0; + if( ub != gmoPinf(gmo) && ub < 0.0 ) + ub = 0.0; + } + if( lb > 0.0 && val == lb ) + continue; + if( ub < 0.0 && val == ub ) + continue; + if( val == 0.0 ) + continue; + + ++nnz; + } + } + + CHECK( writeNLPrintf(writeopts, "x%d\n", nnz) ); + + /* write level values */ + for( i = 0; i < gmoN(gmo); ++i ) + { + double val = gmoGetVarLOne(gmo, i); + + if( writeopts.primalstart == convert_initnondefault ) + { + /* check whether variable level is at default (0 projected onto bounds) */ + double lb = gmoGetVarLowerOne(gmo, i); + double ub = gmoGetVarUpperOne(gmo, i); + if( gmoGetVarTypeOne(gmo, i) == gmovar_SC || gmoGetVarTypeOne(gmo, i) == gmovar_SI ) + { + /* correct to actual bounds if semi-cont/integral */ + if( lb != gmoMinf(gmo) && lb > 0.0 ) + lb = 0.0; + if( ub != gmoPinf(gmo) && ub < 0.0 ) + ub = 0.0; + } + if( lb > 0.0 && val == lb ) + continue; + if( ub < 0.0 && val == ub ) + continue; + if( val == 0.0 ) + continue; + } - /* write nonzero level values */ - for( i = 0; i < gmoN(gmo); ++i ) - if( gmoGetVarLOne(gmo, i) != 0.0 ) CHECK( writeNLPrintf(writeopts, "%d %g\n", i, gmoGetVarLOne(gmo, i)) ); + } + } return RETURN_OK; } diff --git a/src/amplsolver/convert_nl.h b/src/amplsolver/convert_nl.h index d565e321..2cefa2e3 100644 --- a/src/amplsolver/convert_nl.h +++ b/src/amplsolver/convert_nl.h @@ -11,6 +11,13 @@ #include "def.h" +/** which initial values to write to .nl file */ +typedef enum { + convert_initnone = 0, /**< don't write any initial values */ + convert_initnondefault = 1, /**< write only values that are not at default */ + convert_initall = 2 /**< write all values */ +} convert_initvalues; + typedef struct { /* parameters */ @@ -18,6 +25,8 @@ typedef struct int binary; /**< whether to print binary .nl */ int comments; /**< whether to print many comments to .nl (text only) */ int shortfloat; /**< whether to print float as short as possible or like AMPL (text only) */ + convert_initvalues primalstart; /**< which of the variable level values to write into x-section */ + convert_initvalues dualstart; /**< which of the equation marginal values to write into d-section */ /* private */ FILE* f; /**< nl file stream */ diff --git a/src/amplsolver/optamplsolver.cpp b/src/amplsolver/optamplsolver.cpp index 20c617ce..6a44d83e 100644 --- a/src/amplsolver/optamplsolver.cpp +++ b/src/amplsolver/optamplsolver.cpp @@ -28,6 +28,23 @@ int main(int argc, char** argv) gmsopt.collect("nlbinary", "Whether .nl file should be written in binary form", "", true); + GamsOption::EnumVals initvals; + initvals.append("none", "pass no values"); + initvals.append("nondefault", "pass only values that are not at GAMS default"); + initvals.append("all", "pass on all values"); + + gmsopt.collect("initprimal", "Which initial variable level values to pass to AMPL solver", "", + "all", initvals); + + // reusing initvals would give a double-free at the end; should fix that someday... + GamsOption::EnumVals initvals2; + initvals2.append("none", "pass no values"); + initvals2.append("nondefault", "pass only values that are not at GAMS default"); + initvals2.append("all", "pass on all values"); + + gmsopt.collect("initdual", "Which initial equation marginal values to pass to AMPL solver", "", + "all", initvals2); + gmsopt.finalize(); gmsopt.writeDef();