From ef53f2e73e04373dd54e0cfd15156c6ed571ec18 Mon Sep 17 00:00:00 2001 From: Marc Pfetsch Date: Sun, 2 Feb 2025 18:16:37 +0100 Subject: [PATCH 1/9] only output SCIP version when debugging --- tests/src/lpi/bases.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/src/lpi/bases.c b/tests/src/lpi/bases.c index dc4088893e..9213db7f21 100644 --- a/tests/src/lpi/bases.c +++ b/tests/src/lpi/bases.c @@ -497,10 +497,12 @@ Test(more_vars, test1) int basinds[3]; int basicvarpos; +#ifdef SCIP_DEBUG SCIP* scip; SCIP_CALL( SCIPcreate(&scip) ); SCIPprintVersion(scip, 0); SCIP_CALL( SCIPfree(&scip) ); +#endif /* ------------------------------------- */ /* first solve problem */ From 97950205f5d968e156d0c40c68dd7b8e7056a6eb Mon Sep 17 00:00:00 2001 From: Marc Pfetsch Date: Sun, 2 Feb 2025 18:17:10 +0100 Subject: [PATCH 2/9] add unit test for slacks --- tests/src/lpi/bases.c | 234 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) diff --git a/tests/src/lpi/bases.c b/tests/src/lpi/bases.c index 9213db7f21..fabd2ebc5f 100644 --- a/tests/src/lpi/bases.c +++ b/tests/src/lpi/bases.c @@ -573,3 +573,237 @@ Test(more_vars, test1) cr_expect_float_eq(binvarow[2], 0.0, EPS, "BInvARow[%d] = %g != %g\n", 2, binvarow[2], 0.0); cr_expect_float_eq(binvarow[3], -6.0, EPS, "BInvARow[%d] = %g != %g\n", 3, binvarow[3], -6.0); } + + + +/*** TEST SUITE: Check slack variables ***/ +Test(slack_var, test1) +{ + int ncols; + int beg = 0; + int inds[3]; + int nrows; + SCIP_Real vals[3]; + SCIP_Real lb; + SCIP_Real ub; + SCIP_Real obj; + SCIP_Real lhs; + SCIP_Real rhs; + + SCIP_Real M[18] = {2, 1, 1, 1, 0, 0, 4, 1, 2, 0, 1, 0, 3, 4, 2, 0, 0, 1}; + SCIP_Real B[9]; + SCIP_Real binvarow[3]; + SCIP_Real binvacol[3]; + SCIP_Real objval; + int cstats[3]; + int rstats[3]; + int basinds[3]; + int c; + int r; + int i; + int j; + + /* create LPI */ + SCIP_CALL( SCIPlpiCreate(&lpi, NULL, "prob", SCIP_OBJSEN_MAXIMIZE) ); + + /* use the following LP: + * max 5 x1 + 4 x2 + 3 x3 + * 2 x1 + x2 + x3 <= 5 + * 4 x1 + x2 + 2 x3 <= 11 + * 3 x1 + 4 x2 + 2 x3 <= 8 + * x1, x2, x3 >= 0 + */ + + /* add columns */ + lb = 0.0; + ub = SCIPlpiInfinity(lpi); + + obj = 5.0; + SCIP_CALL( SCIPlpiAddCols(lpi, 1, &obj, &lb, &ub, NULL, 0, NULL, NULL, NULL) ); + obj = 4.0; + SCIP_CALL( SCIPlpiAddCols(lpi, 1, &obj, &lb, &ub, NULL, 0, NULL, NULL, NULL) ); + obj = 3.0; + SCIP_CALL( SCIPlpiAddCols(lpi, 1, &obj, &lb, &ub, NULL, 0, NULL, NULL, NULL) ); + + /* add rows */ + lhs = -SCIPlpiInfinity(lpi); + inds[0] = 0; + inds[1] = 1; + inds[2] = 2; + + rhs = 5; + vals[0] = 2.0; + vals[1] = 1.0; + vals[2] = 1.0; + SCIP_CALL( SCIPlpiAddRows(lpi, 1, &lhs, &rhs, NULL, 3, &beg, inds, vals) ); + + rhs = 11; + vals[0] = 4.0; + vals[1] = 1.0; + vals[2] = 2.0; + SCIP_CALL( SCIPlpiAddRows(lpi, 1, &lhs, &rhs, NULL, 3, &beg, inds, vals) ); + + rhs = 8; + vals[0] = 3.0; + vals[1] = 4.0; + vals[2] = 2.0; + SCIP_CALL( SCIPlpiAddRows(lpi, 1, &lhs, &rhs, NULL, 3, &beg, inds, vals) ); + + /* check size */ + SCIP_CALL( SCIPlpiGetNRows(lpi, &nrows) ); + SCIP_CALL( SCIPlpiGetNCols(lpi, &ncols) ); + cr_assert_eq(nrows, 3); + cr_assert_eq(ncols, 3); + +#ifdef SCIP_DEBUG + /* turn on output */ + SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_LPINFO, 1) ); +#endif + + /* ------------------------------------- */ + /* first solve problem */ + SCIP_CALL( SCIPlpiSolvePrimal(lpi) ); + + SCIP_CALL( SCIPlpiGetObjval(lpi, &objval) ); + cr_assert_float_eq(objval, 13.0, EPS); + + /* get basis indices */ + SCIP_CALL( SCIPlpiGetBasisInd(lpi, basinds) ); + + /* find positions of basis index for slack variable of the second constraint (x5) */ + for (j = 0; j < 3; ++j) + { + if( basinds[j] == -2 ) + break; + } + cr_assert(j < 3); /* assert that we found the variable */ + + /* find positions of basis index for x0 */ + for (j = 0; j < 3; ++j) + { + if( basinds[j] == 0 ) + break; + } + cr_assert(j < 3); /* assert that we found the variable */ + + /* find positions of basis index for x2 */ + for (j = 0; j < 3; ++j) + { + if( basinds[j] == 0 ) + break; + } + cr_assert(j < 3); /* assert that we found the variable */ + + /* the optimal basis should be: {x5 = slack for second row, x0, x2 */ + SCIP_CALL( SCIPlpiGetBase(lpi, cstats, rstats) ); + + /* check column status */ + cr_assert(cstats[0] == SCIP_BASESTAT_BASIC); + cr_assert(cstats[1] == SCIP_BASESTAT_LOWER); + cr_assert(cstats[2] == SCIP_BASESTAT_BASIC); + + /* check row status */ + cr_assert(rstats[0] == SCIP_BASESTAT_UPPER); + cr_assert(rstats[1] == SCIP_BASESTAT_BASIC); + cr_assert(rstats[2] == SCIP_BASESTAT_UPPER); + + /* set up basis matrix */ + for (i = 0; i < 3; ++i) + { + for (j = 0; j < 3; ++j) + { + if ( basinds[j] >= 0 ) + { + assert( basinds[j] < 3 ); + B[3 * i + j] = M[6 * i + basinds[j]]; + } + else + { + int rowidx; + rowidx = - (basinds[j] + 1); + assert( 0 <= rowidx && rowidx < 3 ); + if ( i == rowidx ) + B[3 * i + j] = 1.0; + else + B[3 * i + j] = 0.0; + } + } + } + +#ifdef SCIP_DEBUG + printf("Basis matrix:\n"); + for (i = 0; i < 3; ++i) + { + for (j = 0; j < 3; ++j) + printf("%f ", B[3 * i + j]); + printf("\n"); + } + printf("\n"); + + printf("Inverse of basis matrix:\n"); + for (i = 0; i < 3; ++i) + { + SCIP_CALL( SCIPlpiGetBInvRow(lpi, i, binvarow, NULL, NULL) ); + for (j = 0; j < 3; ++j) + printf("%f ", binvarow[j]); + printf("\n"); + } + printf("\n"); + + printf("Product of inverse and basis matrix:\n"); + for (r = 0; r < 3; ++r) + { + SCIP_CALL( SCIPlpiGetBInvRow(lpi, r, binvarow, NULL, NULL) ); + + for (j = 0; j < 3; ++j) + { + SCIP_Real sum = 0.0; + for (i = 0; i < 3; ++i) + sum += binvarow[i] * B[3 * i + j]; + + printf("%f ", sum); + } + printf("\n"); + } +#endif + + /* check product of the inverse basis matrix with the basis matrix */ + for (r = 0; r < 3; ++r) + { + /* get row of basis inverse matrix */ + SCIP_CALL( SCIPlpiGetBInvRow(lpi, r, binvarow, NULL, NULL) ); + + /* check product */ + for (j = 0; j < 3; ++j) + { + SCIP_Real sum = 0.0; + for (i = 0; i < 3; ++i) + sum += binvarow[i] * B[3 * i + j]; + + if ( j == r ) + cr_assert_float_eq(sum, 1.0, EPS); + else + cr_assert_float_eq(sum, 0.0, EPS); + } + } + + /* do the same for the columns */ + for (c = 0; c < 3; ++c) + { + /* get column of basis inverse matrix */ + SCIP_CALL( SCIPlpiGetBInvCol(lpi, c, binvacol, NULL, NULL) ); + + /* check product */ + for (i = 0; i < 3; ++i) + { + SCIP_Real sum = 0.0; + for (j = 0; j < 3; ++j) + sum += B[3 * i + j] * binvacol[j]; + + if ( i == c ) + cr_assert_float_eq(sum, 1.0, EPS); + else + cr_assert_float_eq(sum, 0.0, EPS); + } + } +} From 4ffe0b7dc85bef34ffe8e31e875e4c680b7d32a6 Mon Sep 17 00:00:00 2001 From: Marc Pfetsch Date: Sun, 2 Feb 2025 18:18:13 +0100 Subject: [PATCH 3/9] try to fix sign of slack variable --- src/lpi/lpi_grb.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lpi/lpi_grb.c b/src/lpi/lpi_grb.c index 68ec47ee89..fdaae932a0 100644 --- a/src/lpi/lpi_grb.c +++ b/src/lpi/lpi_grb.c @@ -4791,7 +4791,15 @@ SCIP_RETCODE SCIPlpiGetBInvRow( /* set up rhs */ b.len = 1; ind = r; - val = (x.ind)[r] >= 0 ? 1.0 : -1.0; + val = 1.0; + if ( x.ind[r] < 0 ) + { + /* the sign of the slack variables seems to be 1 for <= inequalities and equations, but -1 for >= inequalities */ + char sense; + CHECK_ZERO( lpi->messagehdlr, GRBgetcharattrarray(lpi->grbmodel, GRB_CHAR_ATTR_SENSE, -(x.ind[r] + 1), 1, &sense) ); + if ( sense == '>' ) + val = -1.0; + } b.ind = &ind; b.val = &val; From 6093f36ba5a01ccdd3e05dd7b0777907a095bfec Mon Sep 17 00:00:00 2001 From: Marc Pfetsch Date: Sun, 2 Feb 2025 18:35:36 +0100 Subject: [PATCH 4/9] have to handle ranged rows --- src/lpi/lpi_grb.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/lpi/lpi_grb.c b/src/lpi/lpi_grb.c index fdaae932a0..8abf334ec4 100644 --- a/src/lpi/lpi_grb.c +++ b/src/lpi/lpi_grb.c @@ -4764,6 +4764,8 @@ SCIP_RETCODE SCIPlpiGetBInvRow( double val; int ind; int status; + int ncols; + int ngrbcols; assert(lpi != NULL); assert(lpi->grbmodel != NULL); @@ -4779,6 +4781,8 @@ SCIP_RETCODE SCIPlpiGetBInvRow( } SCIP_CALL( SCIPlpiGetNRows(lpi, &nrows) ); + SCIP_CALL( SCIPlpiGetNRows(lpi, &ncols) ); + CHECK_ZERO( lpi->messagehdlr, GRBgetintattr(lpi->grbmodel, GRB_INT_ATTR_NUMVARS, &ngrbcols) ); /* set up solution vector */ x.len = 0; @@ -4786,19 +4790,24 @@ SCIP_RETCODE SCIPlpiGetBInvRow( SCIP_ALLOC( BMSallocMemoryArray(&(x.val), nrows) ); /* get basis indices, temporarily using memory of x.ind */ - SCIP_CALL( SCIPlpiGetBasisInd(lpi, x.ind) ); + CHECK_ZERO( lpi->messagehdlr, GRBgetBasisHead(lpi->grbmodel, x.ind) ); /* set up rhs */ b.len = 1; ind = r; val = 1.0; - if ( x.ind[r] < 0 ) + if ( x.ind[r] > ncols ) { - /* the sign of the slack variables seems to be 1 for <= inequalities and equations, but -1 for >= inequalities */ - char sense; - CHECK_ZERO( lpi->messagehdlr, GRBgetcharattrarray(lpi->grbmodel, GRB_CHAR_ATTR_SENSE, -(x.ind[r] + 1), 1, &sense) ); - if ( sense == '>' ) + /* the sign of the slack variables seems to be 1 for <= inequalities, equations, but -1 for >= inequalities and ranged rows */ + if ( x.ind[r] < ngrbcols ) val = -1.0; + else + { + char sense; + CHECK_ZERO( lpi->messagehdlr, GRBgetcharattrarray(lpi->grbmodel, GRB_CHAR_ATTR_SENSE, x.ind[r] - ncols, 1, &sense) ); + if ( sense == '>' ) + val = -1.0; + } } b.ind = &ind; b.val = &val; From a49ca2bbedd51a1df31ceeb776fe475c509b0aa5 Mon Sep 17 00:00:00 2001 From: Marc Pfetsch Date: Sun, 2 Feb 2025 18:50:29 +0100 Subject: [PATCH 5/9] fix SCIPlpiGetBInvCol() --- src/lpi/lpi_grb.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/lpi/lpi_grb.c b/src/lpi/lpi_grb.c index 8abf334ec4..9d838e8d93 100644 --- a/src/lpi/lpi_grb.c +++ b/src/lpi/lpi_grb.c @@ -4879,11 +4879,12 @@ SCIP_RETCODE SCIPlpiGetBInvCol( { SVECTOR x; SVECTOR b; - int* bind; int nrows; double val; int ind; int status; + int ncols; + int ngrbcols; assert(lpi != NULL); assert(lpi->grbmodel != NULL); @@ -4899,16 +4900,34 @@ SCIP_RETCODE SCIPlpiGetBInvCol( } SCIP_CALL( SCIPlpiGetNRows(lpi, &nrows) ); + SCIP_CALL( SCIPlpiGetNRows(lpi, &ncols) ); + CHECK_ZERO( lpi->messagehdlr, GRBgetintattr(lpi->grbmodel, GRB_INT_ATTR_NUMVARS, &ngrbcols) ); /* set up solution vector */ x.len = 0; SCIP_ALLOC( BMSallocMemoryArray(&(x.ind), nrows) ); SCIP_ALLOC( BMSallocMemoryArray(&(x.val), nrows) ); + /* get basis indices, temporarily using memory of x.ind */ + CHECK_ZERO( lpi->messagehdlr, GRBgetBasisHead(lpi->grbmodel, x.ind) ); + /* set up rhs */ b.len = 1; ind = c; val = 1.0; + if ( x.ind[c] > ncols ) + { + /* the sign of the slack variables seems to be 1 for <= inequalities, equations, but -1 for >= inequalities and ranged rows */ + if ( x.ind[c] < ngrbcols ) + val = -1.0; + else + { + char sense; + CHECK_ZERO( lpi->messagehdlr, GRBgetcharattrarray(lpi->grbmodel, GRB_CHAR_ATTR_SENSE, x.ind[c] - ncols, 1, &sense) ); + if ( sense == '>' ) + val = -1.0; + } + } b.ind = &ind; b.val = &val; @@ -4918,10 +4937,6 @@ SCIP_RETCODE SCIPlpiGetBInvCol( /* size should be at most the number of rows */ assert( x.len <= nrows ); - /* get basis indices: entries that correspond to slack variables with coefficient -1 must be negated */ - SCIP_ALLOC( BMSallocMemoryArray(&bind, nrows) ); - SCIP_CALL( SCIPlpiGetBasisInd(lpi, bind) ); - /* check whether we require a dense or sparse result vector */ if ( ninds != NULL && inds != NULL ) { @@ -4935,8 +4950,6 @@ SCIP_RETCODE SCIPlpiGetBInvCol( assert( idx >= 0 && idx < nrows ); inds[i] = idx; coef[idx] = (x.val)[i]; - if( bind[idx] < 0 ) - coef[idx] *= -1.0; } *ninds = x.len; } @@ -4952,13 +4965,10 @@ SCIP_RETCODE SCIPlpiGetBInvCol( idx = (x.ind)[i]; assert( idx >= 0 && idx < nrows ); coef[idx] = (x.val)[i]; - if( bind[idx] < 0 ) - coef[idx] *= -1.0; } } /* free solution space and basis index array */ - BMSfreeMemoryArray(&bind); BMSfreeMemoryArray(&(x.val)); BMSfreeMemoryArray(&(x.ind)); From be983aa383929a09ce91349a4f01f714be17f6de Mon Sep 17 00:00:00 2001 From: Marc Pfetsch Date: Sun, 2 Feb 2025 18:57:06 +0100 Subject: [PATCH 6/9] fix to get number of columns --- src/lpi/lpi_grb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lpi/lpi_grb.c b/src/lpi/lpi_grb.c index 9d838e8d93..1c9bd9e1e8 100644 --- a/src/lpi/lpi_grb.c +++ b/src/lpi/lpi_grb.c @@ -4781,7 +4781,7 @@ SCIP_RETCODE SCIPlpiGetBInvRow( } SCIP_CALL( SCIPlpiGetNRows(lpi, &nrows) ); - SCIP_CALL( SCIPlpiGetNRows(lpi, &ncols) ); + SCIP_CALL( SCIPlpiGetNCols(lpi, &ncols) ); CHECK_ZERO( lpi->messagehdlr, GRBgetintattr(lpi->grbmodel, GRB_INT_ATTR_NUMVARS, &ngrbcols) ); /* set up solution vector */ @@ -4900,7 +4900,7 @@ SCIP_RETCODE SCIPlpiGetBInvCol( } SCIP_CALL( SCIPlpiGetNRows(lpi, &nrows) ); - SCIP_CALL( SCIPlpiGetNRows(lpi, &ncols) ); + SCIP_CALL( SCIPlpiGetNCols(lpi, &ncols) ); CHECK_ZERO( lpi->messagehdlr, GRBgetintattr(lpi->grbmodel, GRB_INT_ATTR_NUMVARS, &ngrbcols) ); /* set up solution vector */ From d1edd6cf1f05b46280e49949ca4136b1d73a9392 Mon Sep 17 00:00:00 2001 From: Marc Pfetsch Date: Sun, 2 Feb 2025 19:07:46 +0100 Subject: [PATCH 7/9] fix row computation --- src/lpi/lpi_grb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lpi/lpi_grb.c b/src/lpi/lpi_grb.c index 1c9bd9e1e8..af92d180bd 100644 --- a/src/lpi/lpi_grb.c +++ b/src/lpi/lpi_grb.c @@ -4804,7 +4804,7 @@ SCIP_RETCODE SCIPlpiGetBInvRow( else { char sense; - CHECK_ZERO( lpi->messagehdlr, GRBgetcharattrarray(lpi->grbmodel, GRB_CHAR_ATTR_SENSE, x.ind[r] - ncols, 1, &sense) ); + CHECK_ZERO( lpi->messagehdlr, GRBgetcharattrarray(lpi->grbmodel, GRB_CHAR_ATTR_SENSE, x.ind[r] - ngrbcols, 1, &sense) ); if ( sense == '>' ) val = -1.0; } @@ -4923,7 +4923,7 @@ SCIP_RETCODE SCIPlpiGetBInvCol( else { char sense; - CHECK_ZERO( lpi->messagehdlr, GRBgetcharattrarray(lpi->grbmodel, GRB_CHAR_ATTR_SENSE, x.ind[c] - ncols, 1, &sense) ); + CHECK_ZERO( lpi->messagehdlr, GRBgetcharattrarray(lpi->grbmodel, GRB_CHAR_ATTR_SENSE, x.ind[c] - ngrbcols, 1, &sense) ); if ( sense == '>' ) val = -1.0; } From 4e951509a24addb8e242412df0e91b4ba2d12684 Mon Sep 17 00:00:00 2001 From: Ambros Gleixner Date: Mon, 3 Feb 2025 15:27:47 +0100 Subject: [PATCH 8/9] add CHANGELOG entry --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 6ae64bfd38..642732d4f4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,8 @@ Fixed bugs ---------- +- fixed definition of slack variables of >= constraints and ranged rows in LP interface to Gurobi (lpi_grb.c) + Build system ------------ From d6d3568d78b8fdf8d93ab3edc8911f2cf6fde5e4 Mon Sep 17 00:00:00 2001 From: Marc Pfetsch Date: Tue, 4 Feb 2025 20:31:40 +0100 Subject: [PATCH 9/9] fix bounds on CPLEX parameters from Version 22.1 --- src/lpi/lpi_cpx.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lpi/lpi_cpx.c b/src/lpi/lpi_cpx.c index f88144522e..1a15bc87a1 100644 --- a/src/lpi/lpi_cpx.c +++ b/src/lpi/lpi_cpx.c @@ -4544,30 +4544,30 @@ SCIP_RETCODE SCIPlpiSetRealpar( { case SCIP_LPPAR_FEASTOL: assert( dval > 0.0 ); - /* 1e-09 <= dval <= 1e-04 */ + /* 1e-09 <= dval <= 1e-01 */ if( dval < 1e-09 ) dval = 1e-09; - else if( dval > 1e-04 ) - dval = 1e-04; + else if( dval > 1e-01 ) + dval = 1e-01; setDblParam(lpi, CPX_PARAM_EPRHS, dval); lpi->feastol = dval; break; case SCIP_LPPAR_DUALFEASTOL: assert( dval > 0.0 ); - /* 1e-09 <= dval <= 1e-04 */ + /* 1e-09 <= dval <= 1e-01 */ if( dval < 1e-09 ) dval = 1e-09; - else if( dval > 1e-04 ) - dval = 1e-04; + else if( dval > 1e-01 ) + dval = 1e-01; setDblParam(lpi, CPX_PARAM_EPOPT, dval); break; case SCIP_LPPAR_BARRIERCONVTOL: - /* 1e-10 <= dval */ + /* 1e-12 <= dval */ assert( dval >= 0.0 ); - if( dval < 1e-10 ) - dval = 1e-10; + if( dval < 1e-12 ) + dval = 1e-12; setDblParam(lpi, CPX_PARAM_BAREPCOMP, dval); break;