diff -r d59bea55db9b -r c445c931472f src/glpnpp05.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnpp05.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,809 @@ +/* glpnpp05.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpnpp.h" + +/*********************************************************************** +* NAME +* +* npp_clean_prob - perform initial LP/MIP processing +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_clean_prob(NPP *npp); +* +* DESCRIPTION +* +* The routine npp_clean_prob performs initial LP/MIP processing that +* currently includes: +* +* 1) removing free rows; +* +* 2) replacing double-sided constraint rows with almost identical +* bounds, by equality constraint rows; +* +* 3) removing fixed columns; +* +* 4) replacing double-bounded columns with almost identical bounds by +* fixed columns and removing those columns; +* +* 5) initial processing constraint coefficients (not implemented); +* +* 6) initial processing objective coefficients (not implemented). */ + +void npp_clean_prob(NPP *npp) +{ /* perform initial LP/MIP processing */ + NPPROW *row, *next_row; + NPPCOL *col, *next_col; + int ret; + xassert(npp == npp); + /* process rows which originally are free */ + for (row = npp->r_head; row != NULL; row = next_row) + { next_row = row->next; + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) + { /* process free row */ +#ifdef GLP_DEBUG + xprintf("1"); +#endif + npp_free_row(npp, row); + /* row was deleted */ + } + } + /* process rows which originally are double-sided inequalities */ + for (row = npp->r_head; row != NULL; row = next_row) + { next_row = row->next; + if (row->lb != -DBL_MAX && row->ub != +DBL_MAX && + row->lb < row->ub) + { ret = npp_make_equality(npp, row); + if (ret == 0) + ; + else if (ret == 1) + { /* row was replaced by equality constraint */ +#ifdef GLP_DEBUG + xprintf("2"); +#endif + } + else + xassert(ret != ret); + } + } + /* process columns which are originally fixed */ + for (col = npp->c_head; col != NULL; col = next_col) + { next_col = col->next; + if (col->lb == col->ub) + { /* process fixed column */ +#ifdef GLP_DEBUG + xprintf("3"); +#endif + npp_fixed_col(npp, col); + /* column was deleted */ + } + } + /* process columns which are originally double-bounded */ + for (col = npp->c_head; col != NULL; col = next_col) + { next_col = col->next; + if (col->lb != -DBL_MAX && col->ub != +DBL_MAX && + col->lb < col->ub) + { ret = npp_make_fixed(npp, col); + if (ret == 0) + ; + else if (ret == 1) + { /* column was replaced by fixed column; process it */ +#ifdef GLP_DEBUG + xprintf("4"); +#endif + npp_fixed_col(npp, col); + /* column was deleted */ + } + } + } + return; +} + +/*********************************************************************** +* NAME +* +* npp_process_row - perform basic row processing +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_process_row(NPP *npp, NPPROW *row, int hard); +* +* DESCRIPTION +* +* The routine npp_process_row performs basic row processing that +* currently includes: +* +* 1) removing empty row; +* +* 2) removing equality constraint row singleton and corresponding +* column; +* +* 3) removing inequality constraint row singleton and corresponding +* column if it was fixed; +* +* 4) performing general row analysis; +* +* 5) removing redundant row bounds; +* +* 6) removing forcing row and corresponding columns; +* +* 7) removing row which becomes free due to redundant bounds; +* +* 8) computing implied bounds for all columns in the row and using +* them to strengthen current column bounds (MIP only, optional, +* performed if the flag hard is on). +* +* Additionally the routine may activate affected rows and/or columns +* for further processing. +* +* RETURNS +* +* 0 success; +* +* GLP_ENOPFS primal/integer infeasibility detected; +* +* GLP_ENODFS dual infeasibility detected. */ + +int npp_process_row(NPP *npp, NPPROW *row, int hard) +{ /* perform basic row processing */ + NPPCOL *col; + NPPAIJ *aij, *next_aij, *aaa; + int ret; + /* row must not be free */ + xassert(!(row->lb == -DBL_MAX && row->ub == +DBL_MAX)); + /* start processing row */ + if (row->ptr == NULL) + { /* empty row */ + ret = npp_empty_row(npp, row); + if (ret == 0) + { /* row was deleted */ +#ifdef GLP_DEBUG + xprintf("A"); +#endif + return 0; + } + else if (ret == 1) + { /* primal infeasibility */ + return GLP_ENOPFS; + } + else + xassert(ret != ret); + } + if (row->ptr->r_next == NULL) + { /* row singleton */ + col = row->ptr->col; + if (row->lb == row->ub) + { /* equality constraint */ + ret = npp_eq_singlet(npp, row); + if (ret == 0) + { /* column was fixed, row was deleted */ +#ifdef GLP_DEBUG + xprintf("B"); +#endif + /* activate rows affected by column */ + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + npp_activate_row(npp, aij->row); + /* process fixed column */ + npp_fixed_col(npp, col); + /* column was deleted */ + return 0; + } + else if (ret == 1 || ret == 2) + { /* primal/integer infeasibility */ + return GLP_ENOPFS; + } + else + xassert(ret != ret); + } + else + { /* inequality constraint */ + ret = npp_ineq_singlet(npp, row); + if (0 <= ret && ret <= 3) + { /* row was deleted */ +#ifdef GLP_DEBUG + xprintf("C"); +#endif + /* activate column, since its length was changed due to + row deletion */ + npp_activate_col(npp, col); + if (ret >= 2) + { /* column bounds changed significantly or column was + fixed */ + /* activate rows affected by column */ + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + npp_activate_row(npp, aij->row); + } + if (ret == 3) + { /* column was fixed; process it */ +#ifdef GLP_DEBUG + xprintf("D"); +#endif + npp_fixed_col(npp, col); + /* column was deleted */ + } + return 0; + } + else if (ret == 4) + { /* primal infeasibility */ + return GLP_ENOPFS; + } + else + xassert(ret != ret); + } + } +#if 0 + /* sometimes this causes too large round-off errors; probably + pivot coefficient should be chosen more carefully */ + if (row->ptr->r_next->r_next == NULL) + { /* row doubleton */ + if (row->lb == row->ub) + { /* equality constraint */ + if (!(row->ptr->col->is_int || + row->ptr->r_next->col->is_int)) + { /* both columns are continuous */ + NPPCOL *q; + q = npp_eq_doublet(npp, row); + if (q != NULL) + { /* column q was eliminated */ +#ifdef GLP_DEBUG + xprintf("E"); +#endif + /* now column q is singleton of type "implied slack + variable"; we process it here to make sure that on + recovering basic solution the row is always active + equality constraint (as required by the routine + rcv_eq_doublet) */ + xassert(npp_process_col(npp, q) == 0); + /* column q was deleted; note that row p also may be + deleted */ + return 0; + } + } + } + } +#endif + /* general row analysis */ + ret = npp_analyze_row(npp, row); + xassert(0x00 <= ret && ret <= 0xFF); + if (ret == 0x33) + { /* row bounds are inconsistent with column bounds */ + return GLP_ENOPFS; + } + if ((ret & 0x0F) == 0x00) + { /* row lower bound does not exist or redundant */ + if (row->lb != -DBL_MAX) + { /* remove redundant row lower bound */ +#ifdef GLP_DEBUG + xprintf("F"); +#endif + npp_inactive_bound(npp, row, 0); + } + } + else if ((ret & 0x0F) == 0x01) + { /* row lower bound can be active */ + /* see below */ + } + else if ((ret & 0x0F) == 0x02) + { /* row lower bound is a forcing bound */ +#ifdef GLP_DEBUG + xprintf("G"); +#endif + /* process forcing row */ + if (npp_forcing_row(npp, row, 0) == 0) +fixup: { /* columns were fixed, row was made free */ + for (aij = row->ptr; aij != NULL; aij = next_aij) + { /* process column fixed by forcing row */ +#ifdef GLP_DEBUG + xprintf("H"); +#endif + col = aij->col; + next_aij = aij->r_next; + /* activate rows affected by column */ + for (aaa = col->ptr; aaa != NULL; aaa = aaa->c_next) + npp_activate_row(npp, aaa->row); + /* process fixed column */ + npp_fixed_col(npp, col); + /* column was deleted */ + } + /* process free row (which now is empty due to deletion of + all its columns) */ + npp_free_row(npp, row); + /* row was deleted */ + return 0; + } + } + else + xassert(ret != ret); + if ((ret & 0xF0) == 0x00) + { /* row upper bound does not exist or redundant */ + if (row->ub != +DBL_MAX) + { /* remove redundant row upper bound */ +#ifdef GLP_DEBUG + xprintf("I"); +#endif + npp_inactive_bound(npp, row, 1); + } + } + else if ((ret & 0xF0) == 0x10) + { /* row upper bound can be active */ + /* see below */ + } + else if ((ret & 0xF0) == 0x20) + { /* row upper bound is a forcing bound */ +#ifdef GLP_DEBUG + xprintf("J"); +#endif + /* process forcing row */ + if (npp_forcing_row(npp, row, 1) == 0) goto fixup; + } + else + xassert(ret != ret); + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) + { /* row became free due to redundant bounds removal */ +#ifdef GLP_DEBUG + xprintf("K"); +#endif + /* activate its columns, since their length will change due + to row deletion */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + npp_activate_col(npp, aij->col); + /* process free row */ + npp_free_row(npp, row); + /* row was deleted */ + return 0; + } +#if 1 /* 23/XII-2009 */ + /* row lower and/or upper bounds can be active */ + if (npp->sol == GLP_MIP && hard) + { /* improve current column bounds (optional) */ + if (npp_improve_bounds(npp, row, 1) < 0) + return GLP_ENOPFS; + } +#endif + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_improve_bounds - improve current column bounds +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_improve_bounds(NPP *npp, NPPROW *row, int flag); +* +* DESCRIPTION +* +* The routine npp_improve_bounds analyzes specified row (inequality +* or equality constraint) to determine implied column bounds and then +* uses these bounds to improve (strengthen) current column bounds. +* +* If the flag is on and current column bounds changed significantly +* or the column was fixed, the routine activate rows affected by the +* column for further processing. (This feature is intended to be used +* in the main loop of the routine npp_process_row.) +* +* NOTE: This operation can be used for MIP problem only. +* +* RETURNS +* +* The routine npp_improve_bounds returns the number of significantly +* changed bounds plus the number of column having been fixed due to +* bound improvements. However, if the routine detects primal/integer +* infeasibility, it returns a negative value. */ + +int npp_improve_bounds(NPP *npp, NPPROW *row, int flag) +{ /* improve current column bounds */ + NPPCOL *col; + NPPAIJ *aij, *next_aij, *aaa; + int kase, ret, count = 0; + double lb, ub; + xassert(npp->sol == GLP_MIP); + /* row must not be free */ + xassert(!(row->lb == -DBL_MAX && row->ub == +DBL_MAX)); + /* determine implied column bounds */ + npp_implied_bounds(npp, row); + /* and use these bounds to strengthen current column bounds */ + for (aij = row->ptr; aij != NULL; aij = next_aij) + { col = aij->col; + next_aij = aij->r_next; + for (kase = 0; kase <= 1; kase++) + { /* save current column bounds */ + lb = col->lb, ub = col->ub; + if (kase == 0) + { /* process implied column lower bound */ + if (col->ll.ll == -DBL_MAX) continue; + ret = npp_implied_lower(npp, col, col->ll.ll); + } + else + { /* process implied column upper bound */ + if (col->uu.uu == +DBL_MAX) continue; + ret = npp_implied_upper(npp, col, col->uu.uu); + } + if (ret == 0 || ret == 1) + { /* current column bounds did not change or changed, but + not significantly; restore current column bounds */ + col->lb = lb, col->ub = ub; + } + else if (ret == 2 || ret == 3) + { /* current column bounds changed significantly or column + was fixed */ +#ifdef GLP_DEBUG + xprintf("L"); +#endif + count++; + /* activate other rows affected by column, if required */ + if (flag) + { for (aaa = col->ptr; aaa != NULL; aaa = aaa->c_next) + { if (aaa->row != row) + npp_activate_row(npp, aaa->row); + } + } + if (ret == 3) + { /* process fixed column */ +#ifdef GLP_DEBUG + xprintf("M"); +#endif + npp_fixed_col(npp, col); + /* column was deleted */ + break; /* for kase */ + } + } + else if (ret == 4) + { /* primal/integer infeasibility */ + return -1; + } + else + xassert(ret != ret); + } + } + return count; +} + +/*********************************************************************** +* NAME +* +* npp_process_col - perform basic column processing +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_process_col(NPP *npp, NPPCOL *col); +* +* DESCRIPTION +* +* The routine npp_process_col performs basic column processing that +* currently includes: +* +* 1) fixing and removing empty column; +* +* 2) removing column singleton, which is implied slack variable, and +* corresponding row if it becomes free; +* +* 3) removing bounds of column, which is implied free variable, and +* replacing corresponding row by equality constraint. +* +* Additionally the routine may activate affected rows and/or columns +* for further processing. +* +* RETURNS +* +* 0 success; +* +* GLP_ENOPFS primal/integer infeasibility detected; +* +* GLP_ENODFS dual infeasibility detected. */ + +int npp_process_col(NPP *npp, NPPCOL *col) +{ /* perform basic column processing */ + NPPROW *row; + NPPAIJ *aij; + int ret; + /* column must not be fixed */ + xassert(col->lb < col->ub); + /* start processing column */ + if (col->ptr == NULL) + { /* empty column */ + ret = npp_empty_col(npp, col); + if (ret == 0) + { /* column was fixed and deleted */ +#ifdef GLP_DEBUG + xprintf("N"); +#endif + return 0; + } + else if (ret == 1) + { /* dual infeasibility */ + return GLP_ENODFS; + } + else + xassert(ret != ret); + } + if (col->ptr->c_next == NULL) + { /* column singleton */ + row = col->ptr->row; + if (row->lb == row->ub) + { /* equality constraint */ + if (!col->is_int) +slack: { /* implied slack variable */ +#ifdef GLP_DEBUG + xprintf("O"); +#endif + npp_implied_slack(npp, col); + /* column was deleted */ + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) + { /* row became free due to implied slack variable */ +#ifdef GLP_DEBUG + xprintf("P"); +#endif + /* activate columns affected by row */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + npp_activate_col(npp, aij->col); + /* process free row */ + npp_free_row(npp, row); + /* row was deleted */ + } + else + { /* row became inequality constraint; activate it + since its length changed due to column deletion */ + npp_activate_row(npp, row); + } + return 0; + } + } + else + { /* inequality constraint */ + if (!col->is_int) + { ret = npp_implied_free(npp, col); + if (ret == 0) + { /* implied free variable */ +#ifdef GLP_DEBUG + xprintf("Q"); +#endif + /* column bounds were removed, row was replaced by + equality constraint */ + goto slack; + } + else if (ret == 1) + { /* column is not implied free variable, because its + lower and/or upper bounds can be active */ + } + else if (ret == 2) + { /* dual infeasibility */ + return GLP_ENODFS; + } + } + } + } + /* column still exists */ + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_process_prob - perform basic LP/MIP processing +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_process_prob(NPP *npp, int hard); +* +* DESCRIPTION +* +* The routine npp_process_prob performs basic LP/MIP processing that +* currently includes: +* +* 1) initial LP/MIP processing (see the routine npp_clean_prob), +* +* 2) basic row processing (see the routine npp_process_row), and +* +* 3) basic column processing (see the routine npp_process_col). +* +* If the flag hard is on, the routine attempts to improve current +* column bounds multiple times within the main processing loop, in +* which case this feature may take a time. Otherwise, if the flag hard +* is off, improving column bounds is performed only once at the end of +* the main loop. (Note that this feature is used for MIP only.) +* +* The routine uses two sets: the set of active rows and the set of +* active columns. Rows/columns are marked by a flag (the field temp in +* NPPROW/NPPCOL). If the flag is non-zero, the row/column is active, +* in which case it is placed in the beginning of the row/column list; +* otherwise, if the flag is zero, the row/column is inactive, in which +* case it is placed in the end of the row/column list. If a row/column +* being currently processed may affect other rows/columns, the latters +* are activated for further processing. +* +* RETURNS +* +* 0 success; +* +* GLP_ENOPFS primal/integer infeasibility detected; +* +* GLP_ENODFS dual infeasibility detected. */ + +int npp_process_prob(NPP *npp, int hard) +{ /* perform basic LP/MIP processing */ + NPPROW *row; + NPPCOL *col; + int processing, ret; + /* perform initial LP/MIP processing */ + npp_clean_prob(npp); + /* activate all remaining rows and columns */ + for (row = npp->r_head; row != NULL; row = row->next) + row->temp = 1; + for (col = npp->c_head; col != NULL; col = col->next) + col->temp = 1; + /* main processing loop */ + processing = 1; + while (processing) + { processing = 0; + /* process all active rows */ + for (;;) + { row = npp->r_head; + if (row == NULL || !row->temp) break; + npp_deactivate_row(npp, row); + ret = npp_process_row(npp, row, hard); + if (ret != 0) goto done; + processing = 1; + } + /* process all active columns */ + for (;;) + { col = npp->c_head; + if (col == NULL || !col->temp) break; + npp_deactivate_col(npp, col); + ret = npp_process_col(npp, col); + if (ret != 0) goto done; + processing = 1; + } + } +#if 1 /* 23/XII-2009 */ + if (npp->sol == GLP_MIP && !hard) + { /* improve current column bounds (optional) */ + for (row = npp->r_head; row != NULL; row = row->next) + { if (npp_improve_bounds(npp, row, 0) < 0) + { ret = GLP_ENOPFS; + goto done; + } + } + } +#endif + /* all seems ok */ + ret = 0; +done: xassert(ret == 0 || ret == GLP_ENOPFS || ret == GLP_ENODFS); +#ifdef GLP_DEBUG + xprintf("\n"); +#endif + return ret; +} + +/**********************************************************************/ + +int npp_simplex(NPP *npp, const glp_smcp *parm) +{ /* process LP prior to applying primal/dual simplex method */ + int ret; + xassert(npp->sol == GLP_SOL); + xassert(parm == parm); + ret = npp_process_prob(npp, 0); + return ret; +} + +/**********************************************************************/ + +int npp_integer(NPP *npp, const glp_iocp *parm) +{ /* process MIP prior to applying branch-and-bound method */ + NPPROW *row, *prev_row; + NPPCOL *col; + NPPAIJ *aij; + int count, ret; + xassert(npp->sol == GLP_MIP); + xassert(parm == parm); + /*==============================================================*/ + /* perform basic MIP processing */ + ret = npp_process_prob(npp, 1); + if (ret != 0) goto done; + /*==============================================================*/ + /* binarize problem, if required */ + if (parm->binarize) + npp_binarize_prob(npp); + /*==============================================================*/ + /* identify hidden packing inequalities */ + count = 0; + /* new rows will be added to the end of the row list, so we go + from the end to beginning of the row list */ + for (row = npp->r_tail; row != NULL; row = prev_row) + { prev_row = row->prev; + /* skip free row */ + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) continue; + /* skip equality constraint */ + if (row->lb == row->ub) continue; + /* skip row having less than two variables */ + if (row->ptr == NULL || row->ptr->r_next == NULL) continue; + /* skip row having non-binary variables */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col = aij->col; + if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) + break; + } + if (aij != NULL) continue; + count += npp_hidden_packing(npp, row); + } + if (count > 0) + xprintf("%d hidden packing inequaliti(es) were detected\n", + count); + /*==============================================================*/ + /* identify hidden covering inequalities */ + count = 0; + /* new rows will be added to the end of the row list, so we go + from the end to beginning of the row list */ + for (row = npp->r_tail; row != NULL; row = prev_row) + { prev_row = row->prev; + /* skip free row */ + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) continue; + /* skip equality constraint */ + if (row->lb == row->ub) continue; + /* skip row having less than three variables */ + if (row->ptr == NULL || row->ptr->r_next == NULL || + row->ptr->r_next->r_next == NULL) continue; + /* skip row having non-binary variables */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col = aij->col; + if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) + break; + } + if (aij != NULL) continue; + count += npp_hidden_covering(npp, row); + } + if (count > 0) + xprintf("%d hidden covering inequaliti(es) were detected\n", + count); + /*==============================================================*/ + /* reduce inequality constraint coefficients */ + count = 0; + /* new rows will be added to the end of the row list, so we go + from the end to beginning of the row list */ + for (row = npp->r_tail; row != NULL; row = prev_row) + { prev_row = row->prev; + /* skip equality constraint */ + if (row->lb == row->ub) continue; + count += npp_reduce_ineq_coef(npp, row); + } + if (count > 0) + xprintf("%d constraint coefficient(s) were reduced\n", count); + /*==============================================================*/ +#ifdef GLP_DEBUG + routine(npp); +#endif + /*==============================================================*/ + /* all seems ok */ + ret = 0; +done: return ret; +} + +/* eof */