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