From 7bd5bc5453808541ad67505a388db0e2be4f6784 Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Sat, 28 Dec 2024 17:33:25 +0100 Subject: [PATCH 1/4] simplify expr of nonlinear constraint if not detected as quadratic - and try to detect as quadratic again, if simplify did a change - need to possibly simplified expr around for writing of QCMATRIX section --- CHANGELOG | 2 ++ src/scip/reader_mps.c | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ce9fb2d116..8d3a47197f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,8 @@ Features -------- +- simplify expressions of nonlinear constraints in MPS writing to increase chance that they are recognized as quadratic + Performance improvements ------------------------ diff --git a/src/scip/reader_mps.c b/src/scip/reader_mps.c index fb26356482..df799d2f95 100644 --- a/src/scip/reader_mps.c +++ b/src/scip/reader_mps.c @@ -3900,6 +3900,7 @@ SCIP_RETCODE SCIPwriteMps( SCIP_CONS** consSOS1; SCIP_CONS** consSOS2; SCIP_CONS** consQuadratic; + SCIP_EXPR** exprQuadratic; /* expressions to quadratic constraints */ int nConsIndicator; int nConsSOS1; int nConsSOS2; @@ -3966,6 +3967,7 @@ SCIP_RETCODE SCIPwriteMps( SCIP_CALL( SCIPallocBufferArray(scip, &consSOS1, nconss) ); SCIP_CALL( SCIPallocBufferArray(scip, &consSOS2, nconss) ); SCIP_CALL( SCIPallocBufferArray(scip, &consQuadratic, nconss) ); + SCIP_CALL( SCIPallocBufferArray(scip, &exprQuadratic, nconss) ); SCIP_CALL( SCIPallocBufferArray(scip, &consIndicator, nconss) ); /* nfixedvars counts all variables with status SCIP_VARSTATUS_FIXED, SCIP_VARSTATUS_AGGREGATED, SCIP_VARSTATUS_MULTAGGR, but not SCIP_VARSTATUS_NEGATED */ @@ -4293,20 +4295,39 @@ SCIP_RETCODE SCIPwriteMps( int j; /* check if it is a quadratic constraint */ - SCIP_CALL( SCIPcheckQuadraticNonlinear(scip, cons, &isquadratic) ); + expr = SCIPgetExprNonlinear(cons); + SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) ); + if( !isquadratic || !SCIPexprAreQuadraticExprsVariables(expr) ) + { + SCIP_Bool changed; + SCIP_Bool infeasible; + SCIP_CALL( SCIPsimplifyExpr(scip, expr, &expr, &changed, &infeasible, NULL, NULL) ); + /* the corresponding releaseExpr is in the writing of the QCMATRIX sections at the end */ + if( changed ) + { + SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) ); + isquadratic &= SCIPexprAreQuadraticExprsVariables(expr); + assert(!infeasible || !isquadratic); + } + } + if( !isquadratic ) { /* unknown constraint type; mark this with SCIPinfinity(scip) */ rhss[c] = SCIPinfinity(scip); - SCIPwarningMessage(scip, "constraint handler <%s> cannot print requested format\n", conshdlrname ); + SCIPwarningMessage(scip, "nonlinear constraint <%s> not recognized as quadratic: cannot print as MPS\n", SCIPconsGetName(cons)); + if( expr != SCIPgetExprNonlinear(cons) ) + { + SCIP_CALL( SCIPreleaseExpr(scip, &expr) ); + } continue; } - /* store constraint */ - consQuadratic[nConsQuadratic++] = cons; - - expr = SCIPgetExprNonlinear(cons); + /* store constraint and corresponding expression (may be the simplified one) */ + consQuadratic[nConsQuadratic] = cons; + exprQuadratic[nConsQuadratic] = expr; + ++nConsQuadratic; /* collect linear coefficients of quadratic part */ SCIPexprGetQuadraticData(expr, &constant, &nlinexprs, &linexprs, &lincoefs, &nquadexprs, NULL, NULL, @@ -4752,7 +4773,7 @@ SCIP_RETCODE SCIPwriteMps( SCIP_EXPR* expr; cons = consQuadratic[c]; - expr = SCIPgetExprNonlinear(cons); + expr = exprQuadratic[c]; SCIPexprGetQuadraticData(expr, NULL, NULL, NULL, NULL, &nconsvars, &nbilin, NULL, NULL); @@ -4828,6 +4849,11 @@ SCIP_RETCODE SCIPwriteMps( printRecord(scip, file, varname, valuestr, maxnamelen); SCIPinfoMessage(scip, file, "\n"); } + + if( expr != SCIPgetExprNonlinear(cons) ) + { + SCIP_CALL( SCIPreleaseExpr(scip, &expr) ); + } } SCIPfreeBufferArray(scip, &namestr); @@ -4906,6 +4932,7 @@ SCIP_RETCODE SCIPwriteMps( /* free buffer arrays for SOS1, SOS2, and quadratic */ SCIPfreeBufferArray(scip, &consIndicator); + SCIPfreeBufferArray(scip, &exprQuadratic); SCIPfreeBufferArray(scip, &consQuadratic); SCIPfreeBufferArray(scip, &consSOS2); SCIPfreeBufferArray(scip, &consSOS1); From 5d777141dbd10c7771591866fe8610051462b71c Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Sat, 28 Dec 2024 17:51:53 +0100 Subject: [PATCH 2/4] simplify expr if not recognized as quadratic when writing lp --- CHANGELOG | 2 +- src/scip/reader_lp.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8d3a47197f..bb5131ee32 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,7 +6,7 @@ Features -------- -- simplify expressions of nonlinear constraints in MPS writing to increase chance that they are recognized as quadratic +- simplify expressions of nonlinear constraints in MPS and LP writing to increase chance that they are recognized as quadratic Performance improvements ------------------------ diff --git a/src/scip/reader_lp.c b/src/scip/reader_lp.c index 6f7ec1958a..20850efa3f 100644 --- a/src/scip/reader_lp.c +++ b/src/scip/reader_lp.c @@ -3888,25 +3888,49 @@ SCIP_RETCODE SCIPwriteLp( else if( strcmp(conshdlrname, "nonlinear") == 0 ) { SCIP_Bool isquadratic; + SCIP_EXPR* expr; /* check whether there is a quadratic representation of the nonlinear constraint */ SCIP_CALL( SCIPcheckQuadraticNonlinear(scip, cons, &isquadratic) ); + if( !isquadratic ) + { + /* simplify expr and check again whether there is a quadratic representation */ + SCIP_Bool changed; + SCIP_Bool infeasible; + + SCIP_CALL( SCIPsimplifyExpr(scip, SCIPgetExprNonlinear(cons), &expr, &changed, &infeasible, NULL, NULL) ); + if( changed ) + { + SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) ); + isquadratic &= SCIPexprAreQuadraticExprsVariables(expr); + assert(!infeasible || !isquadratic); + } + } + else + { + expr = SCIPgetExprNonlinear(cons); + } /* we cannot handle nonlinear constraint that are not quadratically representable */ if( !isquadratic ) { - SCIPwarningMessage(scip, "constraint handler <%s> cannot print constraint\n", SCIPconshdlrGetName(SCIPconsGetHdlr(cons))); + SCIPwarningMessage(scip, "nonlinear constraint <%s> not recognized as quadratic: cannot print as LP\n", SCIPconsGetName(cons)); SCIPinfoMessage(scip, file, "\\ "); SCIP_CALL( SCIPprintCons(scip, cons, file) ); SCIPinfoMessage(scip, file, ";\n"); } else { - SCIP_CALL( printQuadraticCons(scip, file, consname, NULL, NULL, 0, SCIPgetExprNonlinear(cons), + SCIP_CALL( printQuadraticCons(scip, file, consname, NULL, NULL, 0, expr, SCIPgetLhsNonlinear(cons), SCIPgetRhsNonlinear(cons), transformed) ); consExpr[nConsExpr++] = cons; } + + if( expr != SCIPgetExprNonlinear(cons) ) + { + SCIP_CALL( SCIPreleaseExpr(scip, &expr) ); + } } else if( strcmp(conshdlrname, "and") == 0 ) { From f15ae0c3de16b78ff5d3a23baeb77138bcdfd72d Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Sat, 28 Dec 2024 17:56:45 +0100 Subject: [PATCH 3/4] duplicate expr before simplify - a comment in the analogue writing in reader_pip says that it is better to do so --- src/scip/reader_lp.c | 5 ++++- src/scip/reader_mps.c | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/scip/reader_lp.c b/src/scip/reader_lp.c index 20850efa3f..1c76a18b5c 100644 --- a/src/scip/reader_lp.c +++ b/src/scip/reader_lp.c @@ -3895,10 +3895,13 @@ SCIP_RETCODE SCIPwriteLp( if( !isquadratic ) { /* simplify expr and check again whether there is a quadratic representation */ + SCIP_EXPR* exprcopy; SCIP_Bool changed; SCIP_Bool infeasible; - SCIP_CALL( SCIPsimplifyExpr(scip, SCIPgetExprNonlinear(cons), &expr, &changed, &infeasible, NULL, NULL) ); + SCIP_CALL( SCIPduplicateExpr(scip, SCIPgetExprNonlinear(cons), &exprcopy, NULL, NULL, NULL, NULL) ); + SCIP_CALL( SCIPsimplifyExpr(scip, exprcopy, &expr, &changed, &infeasible, NULL, NULL) ); + SCIP_CALL( SCIPreleaseExpr(scip, &exprcopy) ); if( changed ) { SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) ); diff --git a/src/scip/reader_mps.c b/src/scip/reader_mps.c index df799d2f95..72091b9a8d 100644 --- a/src/scip/reader_mps.c +++ b/src/scip/reader_mps.c @@ -4299,9 +4299,13 @@ SCIP_RETCODE SCIPwriteMps( SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) ); if( !isquadratic || !SCIPexprAreQuadraticExprsVariables(expr) ) { + SCIP_EXPR* exprcopy; SCIP_Bool changed; SCIP_Bool infeasible; - SCIP_CALL( SCIPsimplifyExpr(scip, expr, &expr, &changed, &infeasible, NULL, NULL) ); + + SCIP_CALL( SCIPduplicateExpr(scip, expr, &exprcopy, NULL, NULL, NULL, NULL) ); + SCIP_CALL( SCIPsimplifyExpr(scip, exprcopy, &expr, &changed, &infeasible, NULL, NULL) ); + SCIP_CALL( SCIPreleaseExpr(scip, &exprcopy) ); /* the corresponding releaseExpr is in the writing of the QCMATRIX sections at the end */ if( changed ) { From 796df300fd63a21b8be83792725be9b68cd48604 Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Sat, 28 Dec 2024 18:31:43 +0100 Subject: [PATCH 4/4] check quadratic only again if not infeasible - I'm not sure what the simplified expr is if infeasible has been detected - however, if infeasible (=domain error), then it surely is not quadratic --- src/scip/reader_lp.c | 3 +-- src/scip/reader_mps.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/scip/reader_lp.c b/src/scip/reader_lp.c index 1c76a18b5c..a513125c01 100644 --- a/src/scip/reader_lp.c +++ b/src/scip/reader_lp.c @@ -3902,11 +3902,10 @@ SCIP_RETCODE SCIPwriteLp( SCIP_CALL( SCIPduplicateExpr(scip, SCIPgetExprNonlinear(cons), &exprcopy, NULL, NULL, NULL, NULL) ); SCIP_CALL( SCIPsimplifyExpr(scip, exprcopy, &expr, &changed, &infeasible, NULL, NULL) ); SCIP_CALL( SCIPreleaseExpr(scip, &exprcopy) ); - if( changed ) + if( changed && !infeasible ) { SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) ); isquadratic &= SCIPexprAreQuadraticExprsVariables(expr); - assert(!infeasible || !isquadratic); } } else diff --git a/src/scip/reader_mps.c b/src/scip/reader_mps.c index 72091b9a8d..89f778dabf 100644 --- a/src/scip/reader_mps.c +++ b/src/scip/reader_mps.c @@ -4307,11 +4307,10 @@ SCIP_RETCODE SCIPwriteMps( SCIP_CALL( SCIPsimplifyExpr(scip, exprcopy, &expr, &changed, &infeasible, NULL, NULL) ); SCIP_CALL( SCIPreleaseExpr(scip, &exprcopy) ); /* the corresponding releaseExpr is in the writing of the QCMATRIX sections at the end */ - if( changed ) + if( changed && !infeasible ) { SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) ); isquadratic &= SCIPexprAreQuadraticExprsVariables(expr); - assert(!infeasible || !isquadratic); } }