123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862 |
- /* glpnpp03.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: <mao@gnu.org>.
- *
- * 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 <http://www.gnu.org/licenses/>.
- ***********************************************************************/
- #include "glpnpp.h"
- /***********************************************************************
- * NAME
- *
- * npp_empty_row - process empty row
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_empty_row(NPP *npp, NPPROW *p);
- *
- * DESCRIPTION
- *
- * The routine npp_empty_row processes row p, which is empty, i.e.
- * coefficients at all columns in this row are zero:
- *
- * L[p] <= sum 0 x[j] <= U[p], (1)
- *
- * where L[p] <= U[p].
- *
- * RETURNS
- *
- * 0 - success;
- *
- * 1 - problem has no primal feasible solution.
- *
- * PROBLEM TRANSFORMATION
- *
- * If the following conditions hold:
- *
- * L[p] <= +eps, U[p] >= -eps, (2)
- *
- * where eps is an absolute tolerance for row value, the row p is
- * redundant. In this case it can be replaced by equivalent redundant
- * row, which is free (unbounded), and then removed from the problem.
- * Otherwise, the row p is infeasible and, thus, the problem has no
- * primal feasible solution.
- *
- * RECOVERING BASIC SOLUTION
- *
- * See the routine npp_free_row.
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * See the routine npp_free_row.
- *
- * RECOVERING MIP SOLUTION
- *
- * None needed. */
- int npp_empty_row(NPP *npp, NPPROW *p)
- { /* process empty row */
- double eps = 1e-3;
- /* the row must be empty */
- xassert(p->ptr == NULL);
- /* check primal feasibility */
- if (p->lb > +eps || p->ub < -eps)
- return 1;
- /* replace the row by equivalent free (unbounded) row */
- p->lb = -DBL_MAX, p->ub = +DBL_MAX;
- /* and process it */
- npp_free_row(npp, p);
- return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_empty_col - process empty column
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_empty_col(NPP *npp, NPPCOL *q);
- *
- * DESCRIPTION
- *
- * The routine npp_empty_col processes column q:
- *
- * l[q] <= x[q] <= u[q], (1)
- *
- * where l[q] <= u[q], which is empty, i.e. has zero coefficients in
- * all constraint rows.
- *
- * RETURNS
- *
- * 0 - success;
- *
- * 1 - problem has no dual feasible solution.
- *
- * PROBLEM TRANSFORMATION
- *
- * The row of the dual system corresponding to the empty column is the
- * following:
- *
- * sum 0 pi[i] + lambda[q] = c[q], (2)
- * i
- *
- * from which it follows that:
- *
- * lambda[q] = c[q]. (3)
- *
- * If the following condition holds:
- *
- * c[q] < - eps, (4)
- *
- * where eps is an absolute tolerance for column multiplier, the lower
- * column bound l[q] must be active to provide dual feasibility (note
- * that being preprocessed the problem is always minimization). In this
- * case the column can be fixed on its lower bound and removed from the
- * problem (if the column is integral, its bounds are also assumed to
- * be integral). And if the column has no lower bound (l[q] = -oo), the
- * problem has no dual feasible solution.
- *
- * If the following condition holds:
- *
- * c[q] > + eps, (5)
- *
- * the upper column bound u[q] must be active to provide dual
- * feasibility. In this case the column can be fixed on its upper bound
- * and removed from the problem. And if the column has no upper bound
- * (u[q] = +oo), the problem has no dual feasible solution.
- *
- * Finally, if the following condition holds:
- *
- * - eps <= c[q] <= +eps, (6)
- *
- * dual feasibility does not depend on a particular value of column q.
- * In this case the column can be fixed either on its lower bound (if
- * l[q] > -oo) or on its upper bound (if u[q] < +oo) or at zero (if the
- * column is unbounded) and then removed from the problem.
- *
- * RECOVERING BASIC SOLUTION
- *
- * See the routine npp_fixed_col. Having been recovered the column
- * is assigned status GLP_NS. However, if actually it is not fixed
- * (l[q] < u[q]), its status should be changed to GLP_NL, GLP_NU, or
- * GLP_NF depending on which bound it was fixed on transformation stage.
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * See the routine npp_fixed_col.
- *
- * RECOVERING MIP SOLUTION
- *
- * See the routine npp_fixed_col. */
- struct empty_col
- { /* empty column */
- int q;
- /* column reference number */
- char stat;
- /* status in basic solution */
- };
- static int rcv_empty_col(NPP *npp, void *info);
- int npp_empty_col(NPP *npp, NPPCOL *q)
- { /* process empty column */
- struct empty_col *info;
- double eps = 1e-3;
- /* the column must be empty */
- xassert(q->ptr == NULL);
- /* check dual feasibility */
- if (q->coef > +eps && q->lb == -DBL_MAX)
- return 1;
- if (q->coef < -eps && q->ub == +DBL_MAX)
- return 1;
- /* create transformation stack entry */
- info = npp_push_tse(npp,
- rcv_empty_col, sizeof(struct empty_col));
- info->q = q->j;
- /* fix the column */
- if (q->lb == -DBL_MAX && q->ub == +DBL_MAX)
- { /* free column */
- info->stat = GLP_NF;
- q->lb = q->ub = 0.0;
- }
- else if (q->ub == +DBL_MAX)
- lo: { /* column with lower bound */
- info->stat = GLP_NL;
- q->ub = q->lb;
- }
- else if (q->lb == -DBL_MAX)
- up: { /* column with upper bound */
- info->stat = GLP_NU;
- q->lb = q->ub;
- }
- else if (q->lb != q->ub)
- { /* double-bounded column */
- if (q->coef >= +DBL_EPSILON) goto lo;
- if (q->coef <= -DBL_EPSILON) goto up;
- if (fabs(q->lb) <= fabs(q->ub)) goto lo; else goto up;
- }
- else
- { /* fixed column */
- info->stat = GLP_NS;
- }
- /* process fixed column */
- npp_fixed_col(npp, q);
- return 0;
- }
- static int rcv_empty_col(NPP *npp, void *_info)
- { /* recover empty column */
- struct empty_col *info = _info;
- if (npp->sol == GLP_SOL)
- npp->c_stat[info->q] = info->stat;
- return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_implied_value - process implied column value
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_implied_value(NPP *npp, NPPCOL *q, double s);
- *
- * DESCRIPTION
- *
- * For column q:
- *
- * l[q] <= x[q] <= u[q], (1)
- *
- * where l[q] < u[q], the routine npp_implied_value processes its
- * implied value s[q]. If this implied value satisfies to the current
- * column bounds and integrality condition, the routine fixes column q
- * at the given point. Note that the column is kept in the problem in
- * any case.
- *
- * RETURNS
- *
- * 0 - column has been fixed;
- *
- * 1 - implied value violates to current column bounds;
- *
- * 2 - implied value violates integrality condition.
- *
- * ALGORITHM
- *
- * Implied column value s[q] satisfies to the current column bounds if
- * the following condition holds:
- *
- * l[q] - eps <= s[q] <= u[q] + eps, (2)
- *
- * where eps is an absolute tolerance for column value. If the column
- * is integral, the following condition also must hold:
- *
- * |s[q] - floor(s[q]+0.5)| <= eps, (3)
- *
- * where floor(s[q]+0.5) is the nearest integer to s[q].
- *
- * If both condition (2) and (3) are satisfied, the column can be fixed
- * at the value s[q], or, if it is integral, at floor(s[q]+0.5).
- * Otherwise, if s[q] violates (2) or (3), the problem has no feasible
- * solution.
- *
- * Note: If s[q] is close to l[q] or u[q], it seems to be reasonable to
- * fix the column at its lower or upper bound, resp. rather than at the
- * implied value. */
- int npp_implied_value(NPP *npp, NPPCOL *q, double s)
- { /* process implied column value */
- double eps, nint;
- xassert(npp == npp);
- /* column must not be fixed */
- xassert(q->lb < q->ub);
- /* check integrality */
- if (q->is_int)
- { nint = floor(s + 0.5);
- if (fabs(s - nint) <= 1e-5)
- s = nint;
- else
- return 2;
- }
- /* check current column lower bound */
- if (q->lb != -DBL_MAX)
- { eps = (q->is_int ? 1e-5 : 1e-5 + 1e-8 * fabs(q->lb));
- if (s < q->lb - eps) return 1;
- /* if s[q] is close to l[q], fix column at its lower bound
- rather than at the implied value */
- if (s < q->lb + 1e-3 * eps)
- { q->ub = q->lb;
- return 0;
- }
- }
- /* check current column upper bound */
- if (q->ub != +DBL_MAX)
- { eps = (q->is_int ? 1e-5 : 1e-5 + 1e-8 * fabs(q->ub));
- if (s > q->ub + eps) return 1;
- /* if s[q] is close to u[q], fix column at its upper bound
- rather than at the implied value */
- if (s > q->ub - 1e-3 * eps)
- { q->lb = q->ub;
- return 0;
- }
- }
- /* fix column at the implied value */
- q->lb = q->ub = s;
- return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_eq_singlet - process row singleton (equality constraint)
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_eq_singlet(NPP *npp, NPPROW *p);
- *
- * DESCRIPTION
- *
- * The routine npp_eq_singlet processes row p, which is equiality
- * constraint having the only non-zero coefficient:
- *
- * a[p,q] x[q] = b. (1)
- *
- * RETURNS
- *
- * 0 - success;
- *
- * 1 - problem has no primal feasible solution;
- *
- * 2 - problem has no integer feasible solution.
- *
- * PROBLEM TRANSFORMATION
- *
- * The equality constraint defines implied value of column q:
- *
- * x[q] = s[q] = b / a[p,q]. (2)
- *
- * If the implied value s[q] satisfies to the column bounds (see the
- * routine npp_implied_value), the column can be fixed at s[q] and
- * removed from the problem. In this case row p becomes redundant, so
- * it can be replaced by equivalent free row and also removed from the
- * problem.
- *
- * Note that the routine removes from the problem only row p. Column q
- * becomes fixed, however, it is kept in the problem.
- *
- * RECOVERING BASIC SOLUTION
- *
- * In solution to the original problem row p is assigned status GLP_NS
- * (active equality constraint), and column q is assigned status GLP_BS
- * (basic column).
- *
- * Multiplier for row p can be computed as follows. In the dual system
- * of the original problem column q corresponds to the following row:
- *
- * sum a[i,q] pi[i] + lambda[q] = c[q] ==>
- * i
- *
- * sum a[i,q] pi[i] + a[p,q] pi[p] + lambda[q] = c[q].
- * i!=p
- *
- * Therefore:
- *
- * 1
- * pi[p] = ------ (c[q] - lambda[q] - sum a[i,q] pi[i]), (3)
- * a[p,q] i!=q
- *
- * where lambda[q] = 0 (since column[q] is basic), and pi[i] for all
- * i != p are known in solution to the transformed problem.
- *
- * Value of column q in solution to the original problem is assigned
- * its implied value s[q].
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * Multiplier for row p is computed with formula (3). Value of column
- * q is assigned its implied value s[q].
- *
- * RECOVERING MIP SOLUTION
- *
- * Value of column q is assigned its implied value s[q]. */
- struct eq_singlet
- { /* row singleton (equality constraint) */
- int p;
- /* row reference number */
- int q;
- /* column reference number */
- double apq;
- /* constraint coefficient a[p,q] */
- double c;
- /* objective coefficient at x[q] */
- NPPLFE *ptr;
- /* list of non-zero coefficients a[i,q], i != p */
- };
- static int rcv_eq_singlet(NPP *npp, void *info);
- int npp_eq_singlet(NPP *npp, NPPROW *p)
- { /* process row singleton (equality constraint) */
- struct eq_singlet *info;
- NPPCOL *q;
- NPPAIJ *aij;
- NPPLFE *lfe;
- int ret;
- double s;
- /* the row must be singleton equality constraint */
- xassert(p->lb == p->ub);
- xassert(p->ptr != NULL && p->ptr->r_next == NULL);
- /* compute and process implied column value */
- aij = p->ptr;
- q = aij->col;
- s = p->lb / aij->val;
- ret = npp_implied_value(npp, q, s);
- xassert(0 <= ret && ret <= 2);
- if (ret != 0) return ret;
- /* create transformation stack entry */
- info = npp_push_tse(npp,
- rcv_eq_singlet, sizeof(struct eq_singlet));
- info->p = p->i;
- info->q = q->j;
- info->apq = aij->val;
- info->c = q->coef;
- info->ptr = NULL;
- /* save column coefficients a[i,q], i != p (not needed for MIP
- solution) */
- if (npp->sol != GLP_MIP)
- { for (aij = q->ptr; aij != NULL; aij = aij->c_next)
- { if (aij->row == p) continue; /* skip a[p,q] */
- lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE));
- lfe->ref = aij->row->i;
- lfe->val = aij->val;
- lfe->next = info->ptr;
- info->ptr = lfe;
- }
- }
- /* remove the row from the problem */
- npp_del_row(npp, p);
- return 0;
- }
- static int rcv_eq_singlet(NPP *npp, void *_info)
- { /* recover row singleton (equality constraint) */
- struct eq_singlet *info = _info;
- NPPLFE *lfe;
- double temp;
- if (npp->sol == GLP_SOL)
- { /* column q must be already recovered as GLP_NS */
- if (npp->c_stat[info->q] != GLP_NS)
- { npp_error();
- return 1;
- }
- npp->r_stat[info->p] = GLP_NS;
- npp->c_stat[info->q] = GLP_BS;
- }
- if (npp->sol != GLP_MIP)
- { /* compute multiplier for row p with formula (3) */
- temp = info->c;
- for (lfe = info->ptr; lfe != NULL; lfe = lfe->next)
- temp -= lfe->val * npp->r_pi[lfe->ref];
- npp->r_pi[info->p] = temp / info->apq;
- }
- return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_implied_lower - process implied column lower bound
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_implied_lower(NPP *npp, NPPCOL *q, double l);
- *
- * DESCRIPTION
- *
- * For column q:
- *
- * l[q] <= x[q] <= u[q], (1)
- *
- * where l[q] < u[q], the routine npp_implied_lower processes its
- * implied lower bound l'[q]. As the result the current column lower
- * bound may increase. Note that the column is kept in the problem in
- * any case.
- *
- * RETURNS
- *
- * 0 - current column lower bound has not changed;
- *
- * 1 - current column lower bound has changed, but not significantly;
- *
- * 2 - current column lower bound has significantly changed;
- *
- * 3 - column has been fixed on its upper bound;
- *
- * 4 - implied lower bound violates current column upper bound.
- *
- * ALGORITHM
- *
- * If column q is integral, before processing its implied lower bound
- * should be rounded up:
- *
- * ( floor(l'[q]+0.5), if |l'[q] - floor(l'[q]+0.5)| <= eps
- * l'[q] := < (2)
- * ( ceil(l'[q]), otherwise
- *
- * where floor(l'[q]+0.5) is the nearest integer to l'[q], ceil(l'[q])
- * is smallest integer not less than l'[q], and eps is an absolute
- * tolerance for column value.
- *
- * Processing implied column lower bound l'[q] includes the following
- * cases:
- *
- * 1) if l'[q] < l[q] + eps, implied lower bound is redundant;
- *
- * 2) if l[q] + eps <= l[q] <= u[q] + eps, current column lower bound
- * l[q] can be strengthened by replacing it with l'[q]. If in this
- * case new column lower bound becomes close to current column upper
- * bound u[q], the column can be fixed on its upper bound;
- *
- * 3) if l'[q] > u[q] + eps, implied lower bound violates current
- * column upper bound u[q], in which case the problem has no primal
- * feasible solution. */
- int npp_implied_lower(NPP *npp, NPPCOL *q, double l)
- { /* process implied column lower bound */
- int ret;
- double eps, nint;
- xassert(npp == npp);
- /* column must not be fixed */
- xassert(q->lb < q->ub);
- /* implied lower bound must be finite */
- xassert(l != -DBL_MAX);
- /* if column is integral, round up l'[q] */
- if (q->is_int)
- { nint = floor(l + 0.5);
- if (fabs(l - nint) <= 1e-5)
- l = nint;
- else
- l = ceil(l);
- }
- /* check current column lower bound */
- if (q->lb != -DBL_MAX)
- { eps = (q->is_int ? 1e-3 : 1e-3 + 1e-6 * fabs(q->lb));
- if (l < q->lb + eps)
- { ret = 0; /* redundant */
- goto done;
- }
- }
- /* check current column upper bound */
- if (q->ub != +DBL_MAX)
- { eps = (q->is_int ? 1e-5 : 1e-5 + 1e-8 * fabs(q->ub));
- if (l > q->ub + eps)
- { ret = 4; /* infeasible */
- goto done;
- }
- /* if l'[q] is close to u[q], fix column at its upper bound */
- if (l > q->ub - 1e-3 * eps)
- { q->lb = q->ub;
- ret = 3; /* fixed */
- goto done;
- }
- }
- /* check if column lower bound changes significantly */
- if (q->lb == -DBL_MAX)
- ret = 2; /* significantly */
- else if (q->is_int && l > q->lb + 0.5)
- ret = 2; /* significantly */
- else if (l > q->lb + 0.30 * (1.0 + fabs(q->lb)))
- ret = 2; /* significantly */
- else
- ret = 1; /* not significantly */
- /* set new column lower bound */
- q->lb = l;
- done: return ret;
- }
- /***********************************************************************
- * NAME
- *
- * npp_implied_upper - process implied column upper bound
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_implied_upper(NPP *npp, NPPCOL *q, double u);
- *
- * DESCRIPTION
- *
- * For column q:
- *
- * l[q] <= x[q] <= u[q], (1)
- *
- * where l[q] < u[q], the routine npp_implied_upper processes its
- * implied upper bound u'[q]. As the result the current column upper
- * bound may decrease. Note that the column is kept in the problem in
- * any case.
- *
- * RETURNS
- *
- * 0 - current column upper bound has not changed;
- *
- * 1 - current column upper bound has changed, but not significantly;
- *
- * 2 - current column upper bound has significantly changed;
- *
- * 3 - column has been fixed on its lower bound;
- *
- * 4 - implied upper bound violates current column lower bound.
- *
- * ALGORITHM
- *
- * If column q is integral, before processing its implied upper bound
- * should be rounded down:
- *
- * ( floor(u'[q]+0.5), if |u'[q] - floor(l'[q]+0.5)| <= eps
- * u'[q] := < (2)
- * ( floor(l'[q]), otherwise
- *
- * where floor(u'[q]+0.5) is the nearest integer to u'[q],
- * floor(u'[q]) is largest integer not greater than u'[q], and eps is
- * an absolute tolerance for column value.
- *
- * Processing implied column upper bound u'[q] includes the following
- * cases:
- *
- * 1) if u'[q] > u[q] - eps, implied upper bound is redundant;
- *
- * 2) if l[q] - eps <= u[q] <= u[q] - eps, current column upper bound
- * u[q] can be strengthened by replacing it with u'[q]. If in this
- * case new column upper bound becomes close to current column lower
- * bound, the column can be fixed on its lower bound;
- *
- * 3) if u'[q] < l[q] - eps, implied upper bound violates current
- * column lower bound l[q], in which case the problem has no primal
- * feasible solution. */
- int npp_implied_upper(NPP *npp, NPPCOL *q, double u)
- { int ret;
- double eps, nint;
- xassert(npp == npp);
- /* column must not be fixed */
- xassert(q->lb < q->ub);
- /* implied upper bound must be finite */
- xassert(u != +DBL_MAX);
- /* if column is integral, round down u'[q] */
- if (q->is_int)
- { nint = floor(u + 0.5);
- if (fabs(u - nint) <= 1e-5)
- u = nint;
- else
- u = floor(u);
- }
- /* check current column upper bound */
- if (q->ub != +DBL_MAX)
- { eps = (q->is_int ? 1e-3 : 1e-3 + 1e-6 * fabs(q->ub));
- if (u > q->ub - eps)
- { ret = 0; /* redundant */
- goto done;
- }
- }
- /* check current column lower bound */
- if (q->lb != -DBL_MAX)
- { eps = (q->is_int ? 1e-5 : 1e-5 + 1e-8 * fabs(q->lb));
- if (u < q->lb - eps)
- { ret = 4; /* infeasible */
- goto done;
- }
- /* if u'[q] is close to l[q], fix column at its lower bound */
- if (u < q->lb + 1e-3 * eps)
- { q->ub = q->lb;
- ret = 3; /* fixed */
- goto done;
- }
- }
- /* check if column upper bound changes significantly */
- if (q->ub == +DBL_MAX)
- ret = 2; /* significantly */
- else if (q->is_int && u < q->ub - 0.5)
- ret = 2; /* significantly */
- else if (u < q->ub - 0.30 * (1.0 + fabs(q->ub)))
- ret = 2; /* significantly */
- else
- ret = 1; /* not significantly */
- /* set new column upper bound */
- q->ub = u;
- done: return ret;
- }
- /***********************************************************************
- * NAME
- *
- * npp_ineq_singlet - process row singleton (inequality constraint)
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_ineq_singlet(NPP *npp, NPPROW *p);
- *
- * DESCRIPTION
- *
- * The routine npp_ineq_singlet processes row p, which is inequality
- * constraint having the only non-zero coefficient:
- *
- * L[p] <= a[p,q] * x[q] <= U[p], (1)
- *
- * where L[p] < U[p], L[p] > -oo and/or U[p] < +oo.
- *
- * RETURNS
- *
- * 0 - current column bounds have not changed;
- *
- * 1 - current column bounds have changed, but not significantly;
- *
- * 2 - current column bounds have significantly changed;
- *
- * 3 - column has been fixed on its lower or upper bound;
- *
- * 4 - problem has no primal feasible solution.
- *
- * PROBLEM TRANSFORMATION
- *
- * Inequality constraint (1) defines implied bounds of column q:
- *
- * ( L[p] / a[p,q], if a[p,q] > 0
- * l'[q] = < (2)
- * ( U[p] / a[p,q], if a[p,q] < 0
- *
- * ( U[p] / a[p,q], if a[p,q] > 0
- * u'[q] = < (3)
- * ( L[p] / a[p,q], if a[p,q] < 0
- *
- * If these implied bounds do not violate current bounds of column q:
- *
- * l[q] <= x[q] <= u[q], (4)
- *
- * they can be used to strengthen the current column bounds:
- *
- * l[q] := max(l[q], l'[q]), (5)
- *
- * u[q] := min(u[q], u'[q]). (6)
- *
- * (See the routines npp_implied_lower and npp_implied_upper.)
- *
- * Once bounds of row p (1) have been carried over column q, the row
- * becomes redundant, so it can be replaced by equivalent free row and
- * removed from the problem.
- *
- * Note that the routine removes from the problem only row p. Column q,
- * even it has been fixed, is kept in the problem.
- *
- * RECOVERING BASIC SOLUTION
- *
- * Note that the row in the dual system corresponding to column q is
- * the following:
- *
- * sum a[i,q] pi[i] + lambda[q] = c[q] ==>
- * i
- * (7)
- * sum a[i,q] pi[i] + a[p,q] pi[p] + lambda[q] = c[q],
- * i!=p
- *
- * where pi[i] for all i != p are known in solution to the transformed
- * problem. Row p does not exist in the transformed problem, so it has
- * zero multiplier there. This allows computing multiplier for column q
- * in solution to the transformed problem:
- *
- * lambda~[q] = c[q] - sum a[i,q] pi[i]. (8)
- * i!=p
- *
- * Let in solution to the transformed problem column q be non-basic
- * with lower bound active (GLP_NL, lambda~[q] >= 0), and this lower
- * bound be implied one l'[q]. From the original problem's standpoint
- * this then means that actually the original column lower bound l[q]
- * is inactive, and active is that row bound L[p] or U[p] that defines
- * the implied bound l'[q] (2). In this case in solution to the
- * original problem column q is assigned status GLP_BS while row p is
- * assigned status GLP_NL (if a[p,q] > 0) or GLP_NU (if a[p,q] < 0).
- * Since now column q is basic, its multiplier lambda[q] is zero. This
- * allows using (7) and (8) to find multiplier for row p in solution to
- * the original problem:
- *
- * 1
- * pi[p] = ------ (c[q] - sum a[i,q] pi[i]) = lambda~[q] / a[p,q] (9)
- * a[p,q] i!=p
- *
- * Now let in solution to the transformed problem column q be non-basic
- * with upper bound active (GLP_NU, lambda~[q] <= 0), and this upper
- * bound be implied one u'[q]. As in the previous case this then means
- * that from the original problem's standpoint actually the original
- * column upper bound u[q] is inactive, and active is that row bound
- * L[p] or U[p] that defines the implied bound u'[q] (3). In this case
- * in solution to the original problem column q is assigned status
- * GLP_BS, row p is assigned status GLP_NU (if a[p,q] > 0) or GLP_NL
- * (if a[p,q] < 0), and its multiplier is computed with formula (9).
- *
- * Strengthening bounds of column q according to (5) and (6) may make
- * it fixed. Thus, if in solution to the transformed problem column q is
- * non-basic and fixed (GLP_NS), we can suppose that if lambda~[q] > 0,
- * column q has active lower bound (GLP_NL), and if lambda~[q] < 0,
- * column q has active upper bound (GLP_NU), reducing this case to two
- * previous ones. If, however, lambda~[q] is close to zero or
- * corresponding bound of row p does not exist (this may happen if
- * lambda~[q] has wrong sign due to round-off errors, in which case it
- * is expected to be close to zero, since solution is assumed to be dual
- * feasible), column q can be assigned status GLP_BS (basic), and row p
- * can be made active on its existing bound. In the latter case row
- * multiplier pi[p] computed with formula (9) will be also close to
- * zero, and dual feasibility will be kept.
- *
- * In all other cases, namely, if in solution to the transformed
- * problem column q is basic (GLP_BS), or non-basic with original lower
- * bound l[q] active (GLP_NL), or non-basic with original upper bound
- * u[q] active (GLP_NU), constraint (1) is inactive. So in solution to
- * the original problem status of column q remains unchanged, row p is
- * assigned status GLP_BS, and its multiplier pi[p] is assigned zero
- * value.
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * First, value of multiplier for column q in solution to the original
- * problem is computed with formula (8). If lambda~[q] > 0 and column q
- * has implied lower bound, or if lambda~[q] < 0 and column q has
- * implied upper bound, this means that from the original problem's
- * standpoint actually row p has corresponding active bound, in which
- * case its multiplier pi[p] is computed with formula (9). In other
- * cases, when the sign of lambda~[q] corresponds to original bound of
- * column q, or when lambda~[q] =~ 0, value of row multiplier pi[p] is
- * assigned zero value.
- *
- * RECOVERING MIP SOLUTION
- *
- * None needed. */
- struct ineq_singlet
- { /* row singleton (inequality constraint) */
- int p;
- /* row reference number */
- int q;
- /* column reference number */
- double apq;
- /* constraint coefficient a[p,q] */
- double c;
- /* objective coefficient at x[q] */
- double lb;
- /* row lower bound */
- double ub;
- /* row upper bound */
- char lb_changed;
- /* this flag is set if column lower bound was changed */
- char ub_changed;
- /* this flag is set if column upper bound was changed */
- NPPLFE *ptr;
- /* list of non-zero coefficients a[i,q], i != p */
- };
- static int rcv_ineq_singlet(NPP *npp, void *info);
- int npp_ineq_singlet(NPP *npp, NPPROW *p)
- { /* process row singleton (inequality constraint) */
- struct ineq_singlet *info;
- NPPCOL *q;
- NPPAIJ *apq, *aij;
- NPPLFE *lfe;
- int lb_changed, ub_changed;
- double ll, uu;
- /* the row must be singleton inequality constraint */
- xassert(p->lb != -DBL_MAX || p->ub != +DBL_MAX);
- xassert(p->lb < p->ub);
- xassert(p->ptr != NULL && p->ptr->r_next == NULL);
- /* compute implied column bounds */
- apq = p->ptr;
- q = apq->col;
- xassert(q->lb < q->ub);
- if (apq->val > 0.0)
- { ll = (p->lb == -DBL_MAX ? -DBL_MAX : p->lb / apq->val);
- uu = (p->ub == +DBL_MAX ? +DBL_MAX : p->ub / apq->val);
- }
- else
- { ll = (p->ub == +DBL_MAX ? -DBL_MAX : p->ub / apq->val);
- uu = (p->lb == -DBL_MAX ? +DBL_MAX : p->lb / apq->val);
- }
- /* process implied column lower bound */
- if (ll == -DBL_MAX)
- lb_changed = 0;
- else
- { lb_changed = npp_implied_lower(npp, q, ll);
- xassert(0 <= lb_changed && lb_changed <= 4);
- if (lb_changed == 4) return 4; /* infeasible */
- }
- /* process implied column upper bound */
- if (uu == +DBL_MAX)
- ub_changed = 0;
- else if (lb_changed == 3)
- { /* column was fixed on its upper bound due to l'[q] = u[q] */
- /* note that L[p] < U[p], so l'[q] = u[q] < u'[q] */
- ub_changed = 0;
- }
- else
- { ub_changed = npp_implied_upper(npp, q, uu);
- xassert(0 <= ub_changed && ub_changed <= 4);
- if (ub_changed == 4) return 4; /* infeasible */
- }
- /* if neither lower nor upper column bound was changed, the row
- is originally redundant and can be replaced by free row */
- if (!lb_changed && !ub_changed)
- { p->lb = -DBL_MAX, p->ub = +DBL_MAX;
- npp_free_row(npp, p);
- return 0;
- }
- /* create transformation stack entry */
- info = npp_push_tse(npp,
- rcv_ineq_singlet, sizeof(struct ineq_singlet));
- info->p = p->i;
- info->q = q->j;
- info->apq = apq->val;
- info->c = q->coef;
- info->lb = p->lb;
- info->ub = p->ub;
- info->lb_changed = (char)lb_changed;
- info->ub_changed = (char)ub_changed;
- info->ptr = NULL;
- /* save column coefficients a[i,q], i != p (not needed for MIP
- solution) */
- if (npp->sol != GLP_MIP)
- { for (aij = q->ptr; aij != NULL; aij = aij->c_next)
- { if (aij == apq) continue; /* skip a[p,q] */
- lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE));
- lfe->ref = aij->row->i;
- lfe->val = aij->val;
- lfe->next = info->ptr;
- info->ptr = lfe;
- }
- }
- /* remove the row from the problem */
- npp_del_row(npp, p);
- return lb_changed >= ub_changed ? lb_changed : ub_changed;
- }
- static int rcv_ineq_singlet(NPP *npp, void *_info)
- { /* recover row singleton (inequality constraint) */
- struct ineq_singlet *info = _info;
- NPPLFE *lfe;
- double lambda;
- if (npp->sol == GLP_MIP) goto done;
- /* compute lambda~[q] in solution to the transformed problem
- with formula (8) */
- lambda = info->c;
- for (lfe = info->ptr; lfe != NULL; lfe = lfe->next)
- lambda -= lfe->val * npp->r_pi[lfe->ref];
- if (npp->sol == GLP_SOL)
- { /* recover basic solution */
- if (npp->c_stat[info->q] == GLP_BS)
- { /* column q is basic, so row p is inactive */
- npp->r_stat[info->p] = GLP_BS;
- npp->r_pi[info->p] = 0.0;
- }
- else if (npp->c_stat[info->q] == GLP_NL)
- nl: { /* column q is non-basic with lower bound active */
- if (info->lb_changed)
- { /* it is implied bound, so actually row p is active
- while column q is basic */
- npp->r_stat[info->p] =
- (char)(info->apq > 0.0 ? GLP_NL : GLP_NU);
- npp->c_stat[info->q] = GLP_BS;
- npp->r_pi[info->p] = lambda / info->apq;
- }
- else
- { /* it is original bound, so row p is inactive */
- npp->r_stat[info->p] = GLP_BS;
- npp->r_pi[info->p] = 0.0;
- }
- }
- else if (npp->c_stat[info->q] == GLP_NU)
- nu: { /* column q is non-basic with upper bound active */
- if (info->ub_changed)
- { /* it is implied bound, so actually row p is active
- while column q is basic */
- npp->r_stat[info->p] =
- (char)(info->apq > 0.0 ? GLP_NU : GLP_NL);
- npp->c_stat[info->q] = GLP_BS;
- npp->r_pi[info->p] = lambda / info->apq;
- }
- else
- { /* it is original bound, so row p is inactive */
- npp->r_stat[info->p] = GLP_BS;
- npp->r_pi[info->p] = 0.0;
- }
- }
- else if (npp->c_stat[info->q] == GLP_NS)
- { /* column q is non-basic and fixed; note, however, that in
- in the original problem it is non-fixed */
- if (lambda > +1e-7)
- { if (info->apq > 0.0 && info->lb != -DBL_MAX ||
- info->apq < 0.0 && info->ub != +DBL_MAX ||
- !info->lb_changed)
- { /* either corresponding bound of row p exists or
- column q remains non-basic with its original lower
- bound active */
- npp->c_stat[info->q] = GLP_NL;
- goto nl;
- }
- }
- if (lambda < -1e-7)
- { if (info->apq > 0.0 && info->ub != +DBL_MAX ||
- info->apq < 0.0 && info->lb != -DBL_MAX ||
- !info->ub_changed)
- { /* either corresponding bound of row p exists or
- column q remains non-basic with its original upper
- bound active */
- npp->c_stat[info->q] = GLP_NU;
- goto nu;
- }
- }
- /* either lambda~[q] is close to zero, or corresponding
- bound of row p does not exist, because lambda~[q] has
- wrong sign due to round-off errors; in the latter case
- lambda~[q] is also assumed to be close to zero; so, we
- can make row p active on its existing bound and column q
- basic; pi[p] will have wrong sign, but it also will be
- close to zero (rarus casus of dual degeneracy) */
- if (info->lb != -DBL_MAX && info->ub == +DBL_MAX)
- { /* row lower bound exists, but upper bound doesn't */
- npp->r_stat[info->p] = GLP_NL;
- }
- else if (info->lb == -DBL_MAX && info->ub != +DBL_MAX)
- { /* row upper bound exists, but lower bound doesn't */
- npp->r_stat[info->p] = GLP_NU;
- }
- else if (info->lb != -DBL_MAX && info->ub != +DBL_MAX)
- { /* both row lower and upper bounds exist */
- /* to choose proper active row bound we should not use
- lambda~[q], because its value being close to zero is
- unreliable; so we choose that bound which provides
- primal feasibility for original constraint (1) */
- if (info->apq * npp->c_value[info->q] <=
- 0.5 * (info->lb + info->ub))
- npp->r_stat[info->p] = GLP_NL;
- else
- npp->r_stat[info->p] = GLP_NU;
- }
- else
- { npp_error();
- return 1;
- }
- npp->c_stat[info->q] = GLP_BS;
- npp->r_pi[info->p] = lambda / info->apq;
- }
- else
- { npp_error();
- return 1;
- }
- }
- if (npp->sol == GLP_IPT)
- { /* recover interior-point solution */
- if (lambda > +DBL_EPSILON && info->lb_changed ||
- lambda < -DBL_EPSILON && info->ub_changed)
- { /* actually row p has corresponding active bound */
- npp->r_pi[info->p] = lambda / info->apq;
- }
- else
- { /* either bounds of column q are both inactive or its
- original bound is active */
- npp->r_pi[info->p] = 0.0;
- }
- }
- done: return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_implied_slack - process column singleton (implied slack variable)
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * void npp_implied_slack(NPP *npp, NPPCOL *q);
- *
- * DESCRIPTION
- *
- * The routine npp_implied_slack processes column q:
- *
- * l[q] <= x[q] <= u[q], (1)
- *
- * where l[q] < u[q], having the only non-zero coefficient in row p,
- * which is equality constraint:
- *
- * sum a[p,j] x[j] + a[p,q] x[q] = b. (2)
- * j!=q
- *
- * PROBLEM TRANSFORMATION
- *
- * (If x[q] is integral, this transformation must not be used.)
- *
- * The term a[p,q] x[q] in constraint (2) can be considered as a slack
- * variable that allows to carry bounds of column q over row p and then
- * remove column q from the problem.
- *
- * Constraint (2) can be written as follows:
- *
- * sum a[p,j] x[j] = b - a[p,q] x[q]. (3)
- * j!=q
- *
- * According to (1) constraint (3) is equivalent to the following
- * inequality constraint:
- *
- * L[p] <= sum a[p,j] x[j] <= U[p], (4)
- * j!=q
- *
- * where
- *
- * ( b - a[p,q] u[q], if a[p,q] > 0
- * L[p] = < (5)
- * ( b - a[p,q] l[q], if a[p,q] < 0
- *
- * ( b - a[p,q] l[q], if a[p,q] > 0
- * U[p] = < (6)
- * ( b - a[p,q] u[q], if a[p,q] < 0
- *
- * From (2) it follows that:
- *
- * 1
- * x[q] = ------ (b - sum a[p,j] x[j]). (7)
- * a[p,q] j!=q
- *
- * In order to eliminate x[q] from the objective row we substitute it
- * from (6) to that row:
- *
- * z = sum c[j] x[j] + c[q] x[q] + c[0] =
- * j!=q
- * 1
- * = sum c[j] x[j] + c[q] [------ (b - sum a[p,j] x[j])] + c0 =
- * j!=q a[p,q] j!=q
- *
- * = sum c~[j] x[j] + c~[0],
- * j!=q
- * a[p,j] b
- * c~[j] = c[j] - c[q] ------, c~0 = c0 - c[q] ------ (8)
- * a[p,q] a[p,q]
- *
- * are values of objective coefficients and constant term, resp., in
- * the transformed problem.
- *
- * Note that column q is column singleton, so in the dual system of the
- * original problem it corresponds to the following row singleton:
- *
- * a[p,q] pi[p] + lambda[q] = c[q]. (9)
- *
- * In the transformed problem row (9) would be the following:
- *
- * a[p,q] pi~[p] + lambda[q] = c~[q] = 0. (10)
- *
- * Subtracting (10) from (9) we have:
- *
- * a[p,q] (pi[p] - pi~[p]) = c[q]
- *
- * that gives the following formula to compute multiplier for row p in
- * solution to the original problem using its value in solution to the
- * transformed problem:
- *
- * pi[p] = pi~[p] + c[q] / a[p,q]. (11)
- *
- * RECOVERING BASIC SOLUTION
- *
- * Status of column q in solution to the original problem is defined
- * by status of row p in solution to the transformed problem and the
- * sign of coefficient a[p,q] in the original inequality constraint (2)
- * as follows:
- *
- * +-----------------------+---------+--------------------+
- * | Status of row p | Sign of | Status of column q |
- * | (transformed problem) | a[p,q] | (original problem) |
- * +-----------------------+---------+--------------------+
- * | GLP_BS | + / - | GLP_BS |
- * | GLP_NL | + | GLP_NU |
- * | GLP_NL | - | GLP_NL |
- * | GLP_NU | + | GLP_NL |
- * | GLP_NU | - | GLP_NU |
- * | GLP_NF | + / - | GLP_NF |
- * +-----------------------+---------+--------------------+
- *
- * Value of column q is computed with formula (7). Since originally row
- * p is equality constraint, its status is assigned GLP_NS, and value of
- * its multiplier pi[p] is computed with formula (11).
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * Value of column q is computed with formula (7). Row multiplier value
- * pi[p] is computed with formula (11).
- *
- * RECOVERING MIP SOLUTION
- *
- * Value of column q is computed with formula (7). */
- struct implied_slack
- { /* column singleton (implied slack variable) */
- int p;
- /* row reference number */
- int q;
- /* column reference number */
- double apq;
- /* constraint coefficient a[p,q] */
- double b;
- /* right-hand side of original equality constraint */
- double c;
- /* original objective coefficient at x[q] */
- NPPLFE *ptr;
- /* list of non-zero coefficients a[p,j], j != q */
- };
- static int rcv_implied_slack(NPP *npp, void *info);
- void npp_implied_slack(NPP *npp, NPPCOL *q)
- { /* process column singleton (implied slack variable) */
- struct implied_slack *info;
- NPPROW *p;
- NPPAIJ *aij;
- NPPLFE *lfe;
- /* the column must be non-integral non-fixed singleton */
- xassert(!q->is_int);
- xassert(q->lb < q->ub);
- xassert(q->ptr != NULL && q->ptr->c_next == NULL);
- /* corresponding row must be equality constraint */
- aij = q->ptr;
- p = aij->row;
- xassert(p->lb == p->ub);
- /* create transformation stack entry */
- info = npp_push_tse(npp,
- rcv_implied_slack, sizeof(struct implied_slack));
- info->p = p->i;
- info->q = q->j;
- info->apq = aij->val;
- info->b = p->lb;
- info->c = q->coef;
- info->ptr = NULL;
- /* save row coefficients a[p,j], j != q, and substitute x[q]
- into the objective row */
- for (aij = p->ptr; aij != NULL; aij = aij->r_next)
- { if (aij->col == q) continue; /* skip a[p,q] */
- lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE));
- lfe->ref = aij->col->j;
- lfe->val = aij->val;
- lfe->next = info->ptr;
- info->ptr = lfe;
- aij->col->coef -= info->c * (aij->val / info->apq);
- }
- npp->c0 += info->c * (info->b / info->apq);
- /* compute new row bounds */
- if (info->apq > 0.0)
- { p->lb = (q->ub == +DBL_MAX ?
- -DBL_MAX : info->b - info->apq * q->ub);
- p->ub = (q->lb == -DBL_MAX ?
- +DBL_MAX : info->b - info->apq * q->lb);
- }
- else
- { p->lb = (q->lb == -DBL_MAX ?
- -DBL_MAX : info->b - info->apq * q->lb);
- p->ub = (q->ub == +DBL_MAX ?
- +DBL_MAX : info->b - info->apq * q->ub);
- }
- /* remove the column from the problem */
- npp_del_col(npp, q);
- return;
- }
- static int rcv_implied_slack(NPP *npp, void *_info)
- { /* recover column singleton (implied slack variable) */
- struct implied_slack *info = _info;
- NPPLFE *lfe;
- double temp;
- if (npp->sol == GLP_SOL)
- { /* assign statuses to row p and column q */
- if (npp->r_stat[info->p] == GLP_BS ||
- npp->r_stat[info->p] == GLP_NF)
- npp->c_stat[info->q] = npp->r_stat[info->p];
- else if (npp->r_stat[info->p] == GLP_NL)
- npp->c_stat[info->q] =
- (char)(info->apq > 0.0 ? GLP_NU : GLP_NL);
- else if (npp->r_stat[info->p] == GLP_NU)
- npp->c_stat[info->q] =
- (char)(info->apq > 0.0 ? GLP_NL : GLP_NU);
- else
- { npp_error();
- return 1;
- }
- npp->r_stat[info->p] = GLP_NS;
- }
- if (npp->sol != GLP_MIP)
- { /* compute multiplier for row p */
- npp->r_pi[info->p] += info->c / info->apq;
- }
- /* compute value of column q */
- temp = info->b;
- for (lfe = info->ptr; lfe != NULL; lfe = lfe->next)
- temp -= lfe->val * npp->c_value[lfe->ref];
- npp->c_value[info->q] = temp / info->apq;
- return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_implied_free - process column singleton (implied free variable)
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_implied_free(NPP *npp, NPPCOL *q);
- *
- * DESCRIPTION
- *
- * The routine npp_implied_free processes column q:
- *
- * l[q] <= x[q] <= u[q], (1)
- *
- * having non-zero coefficient in the only row p, which is inequality
- * constraint:
- *
- * L[p] <= sum a[p,j] x[j] + a[p,q] x[q] <= U[p], (2)
- * j!=q
- *
- * where l[q] < u[q], L[p] < U[p], L[p] > -oo and/or U[p] < +oo.
- *
- * RETURNS
- *
- * 0 - success;
- *
- * 1 - column lower and/or upper bound(s) can be active;
- *
- * 2 - problem has no dual feasible solution.
- *
- * PROBLEM TRANSFORMATION
- *
- * Constraint (2) can be written as follows:
- *
- * L[p] - sum a[p,j] x[j] <= a[p,q] x[q] <= U[p] - sum a[p,j] x[j],
- * j!=q j!=q
- *
- * from which it follows that:
- *
- * alfa <= a[p,q] x[q] <= beta, (3)
- *
- * where
- *
- * alfa = inf(L[p] - sum a[p,j] x[j]) =
- * j!=q
- *
- * = L[p] - sup sum a[p,j] x[j] = (4)
- * j!=q
- *
- * = L[p] - sum a[p,j] u[j] - sum a[p,j] l[j],
- * j in Jp j in Jn
- *
- * beta = sup(L[p] - sum a[p,j] x[j]) =
- * j!=q
- *
- * = L[p] - inf sum a[p,j] x[j] = (5)
- * j!=q
- *
- * = L[p] - sum a[p,j] l[j] - sum a[p,j] u[j],
- * j in Jp j in Jn
- *
- * Jp = {j != q: a[p,j] > 0}, Jn = {j != q: a[p,j] < 0}. (6)
- *
- * Inequality (3) defines implied bounds of variable x[q]:
- *
- * l'[q] <= x[q] <= u'[q], (7)
- *
- * where
- *
- * ( alfa / a[p,q], if a[p,q] > 0
- * l'[q] = < (8a)
- * ( beta / a[p,q], if a[p,q] < 0
- *
- * ( beta / a[p,q], if a[p,q] > 0
- * u'[q] = < (8b)
- * ( alfa / a[p,q], if a[p,q] < 0
- *
- * Thus, if l'[q] > l[q] - eps and u'[q] < u[q] + eps, where eps is
- * an absolute tolerance for column value, column bounds (1) cannot be
- * active, in which case column q can be replaced by equivalent free
- * (unbounded) column.
- *
- * Note that column q is column singleton, so in the dual system of the
- * original problem it corresponds to the following row singleton:
- *
- * a[p,q] pi[p] + lambda[q] = c[q], (9)
- *
- * from which it follows that:
- *
- * pi[p] = (c[q] - lambda[q]) / a[p,q]. (10)
- *
- * Let x[q] be implied free (unbounded) variable. Then column q can be
- * only basic, so its multiplier lambda[q] is equal to zero, and from
- * (10) we have:
- *
- * pi[p] = c[q] / a[p,q]. (11)
- *
- * There are possible three cases:
- *
- * 1) pi[p] < -eps, where eps is an absolute tolerance for row
- * multiplier. In this case, to provide dual feasibility of the
- * original problem, row p must be active on its lower bound, and
- * if its lower bound does not exist (L[p] = -oo), the problem has
- * no dual feasible solution;
- *
- * 2) pi[p] > +eps. In this case row p must be active on its upper
- * bound, and if its upper bound does not exist (U[p] = +oo), the
- * problem has no dual feasible solution;
- *
- * 3) -eps <= pi[p] <= +eps. In this case any (either lower or upper)
- * bound of row p can be active, because this does not affect dual
- * feasibility.
- *
- * Thus, in all three cases original inequality constraint (2) can be
- * replaced by equality constraint, where the right-hand side is either
- * lower or upper bound of row p, and bounds of column q can be removed
- * that makes it free (unbounded). (May note that this transformation
- * can be followed by transformation "Column singleton (implied slack
- * variable)" performed by the routine npp_implied_slack.)
- *
- * RECOVERING BASIC SOLUTION
- *
- * Status of row p in solution to the original problem is determined
- * by its status in solution to the transformed problem and its bound,
- * which was choosen to be active:
- *
- * +-----------------------+--------+--------------------+
- * | Status of row p | Active | Status of row p |
- * | (transformed problem) | bound | (original problem) |
- * +-----------------------+--------+--------------------+
- * | GLP_BS | L[p] | GLP_BS |
- * | GLP_BS | U[p] | GLP_BS |
- * | GLP_NS | L[p] | GLP_NL |
- * | GLP_NS | U[p] | GLP_NU |
- * +-----------------------+--------+--------------------+
- *
- * Value of row multiplier pi[p] (as well as value of column q) in
- * solution to the original problem is the same as in solution to the
- * transformed problem.
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * Value of row multiplier pi[p] in solution to the original problem is
- * the same as in solution to the transformed problem.
- *
- * RECOVERING MIP SOLUTION
- *
- * None needed. */
- struct implied_free
- { /* column singleton (implied free variable) */
- int p;
- /* row reference number */
- char stat;
- /* row status:
- GLP_NL - active constraint on lower bound
- GLP_NU - active constraint on upper bound */
- };
- static int rcv_implied_free(NPP *npp, void *info);
- int npp_implied_free(NPP *npp, NPPCOL *q)
- { /* process column singleton (implied free variable) */
- struct implied_free *info;
- NPPROW *p;
- NPPAIJ *apq, *aij;
- double alfa, beta, l, u, pi, eps;
- /* the column must be non-fixed singleton */
- xassert(q->lb < q->ub);
- xassert(q->ptr != NULL && q->ptr->c_next == NULL);
- /* corresponding row must be inequality constraint */
- apq = q->ptr;
- p = apq->row;
- xassert(p->lb != -DBL_MAX || p->ub != +DBL_MAX);
- xassert(p->lb < p->ub);
- /* compute alfa */
- alfa = p->lb;
- if (alfa != -DBL_MAX)
- { for (aij = p->ptr; aij != NULL; aij = aij->r_next)
- { if (aij == apq) continue; /* skip a[p,q] */
- if (aij->val > 0.0)
- { if (aij->col->ub == +DBL_MAX)
- { alfa = -DBL_MAX;
- break;
- }
- alfa -= aij->val * aij->col->ub;
- }
- else /* < 0.0 */
- { if (aij->col->lb == -DBL_MAX)
- { alfa = -DBL_MAX;
- break;
- }
- alfa -= aij->val * aij->col->lb;
- }
- }
- }
- /* compute beta */
- beta = p->ub;
- if (beta != +DBL_MAX)
- { for (aij = p->ptr; aij != NULL; aij = aij->r_next)
- { if (aij == apq) continue; /* skip a[p,q] */
- if (aij->val > 0.0)
- { if (aij->col->lb == -DBL_MAX)
- { beta = +DBL_MAX;
- break;
- }
- beta -= aij->val * aij->col->lb;
- }
- else /* < 0.0 */
- { if (aij->col->ub == +DBL_MAX)
- { beta = +DBL_MAX;
- break;
- }
- beta -= aij->val * aij->col->ub;
- }
- }
- }
- /* compute implied column lower bound l'[q] */
- if (apq->val > 0.0)
- l = (alfa == -DBL_MAX ? -DBL_MAX : alfa / apq->val);
- else /* < 0.0 */
- l = (beta == +DBL_MAX ? -DBL_MAX : beta / apq->val);
- /* compute implied column upper bound u'[q] */
- if (apq->val > 0.0)
- u = (beta == +DBL_MAX ? +DBL_MAX : beta / apq->val);
- else
- u = (alfa == -DBL_MAX ? +DBL_MAX : alfa / apq->val);
- /* check if column lower bound l[q] can be active */
- if (q->lb != -DBL_MAX)
- { eps = 1e-9 + 1e-12 * fabs(q->lb);
- if (l < q->lb - eps) return 1; /* yes, it can */
- }
- /* check if column upper bound u[q] can be active */
- if (q->ub != +DBL_MAX)
- { eps = 1e-9 + 1e-12 * fabs(q->ub);
- if (u > q->ub + eps) return 1; /* yes, it can */
- }
- /* okay; make column q free (unbounded) */
- q->lb = -DBL_MAX, q->ub = +DBL_MAX;
- /* create transformation stack entry */
- info = npp_push_tse(npp,
- rcv_implied_free, sizeof(struct implied_free));
- info->p = p->i;
- info->stat = -1;
- /* compute row multiplier pi[p] */
- pi = q->coef / apq->val;
- /* check dual feasibility for row p */
- if (pi > +DBL_EPSILON)
- { /* lower bound L[p] must be active */
- if (p->lb != -DBL_MAX)
- nl: { info->stat = GLP_NL;
- p->ub = p->lb;
- }
- else
- { if (pi > +1e-5) return 2; /* dual infeasibility */
- /* take a chance on U[p] */
- xassert(p->ub != +DBL_MAX);
- goto nu;
- }
- }
- else if (pi < -DBL_EPSILON)
- { /* upper bound U[p] must be active */
- if (p->ub != +DBL_MAX)
- nu: { info->stat = GLP_NU;
- p->lb = p->ub;
- }
- else
- { if (pi < -1e-5) return 2; /* dual infeasibility */
- /* take a chance on L[p] */
- xassert(p->lb != -DBL_MAX);
- goto nl;
- }
- }
- else
- { /* any bound (either L[p] or U[p]) can be made active */
- if (p->ub == +DBL_MAX)
- { xassert(p->lb != -DBL_MAX);
- goto nl;
- }
- if (p->lb == -DBL_MAX)
- { xassert(p->ub != +DBL_MAX);
- goto nu;
- }
- if (fabs(p->lb) <= fabs(p->ub)) goto nl; else goto nu;
- }
- return 0;
- }
- static int rcv_implied_free(NPP *npp, void *_info)
- { /* recover column singleton (implied free variable) */
- struct implied_free *info = _info;
- if (npp->sol == GLP_SOL)
- { if (npp->r_stat[info->p] == GLP_BS)
- npp->r_stat[info->p] = GLP_BS;
- else if (npp->r_stat[info->p] == GLP_NS)
- { xassert(info->stat == GLP_NL || info->stat == GLP_NU);
- npp->r_stat[info->p] = info->stat;
- }
- else
- { npp_error();
- return 1;
- }
- }
- return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_eq_doublet - process row doubleton (equality constraint)
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * NPPCOL *npp_eq_doublet(NPP *npp, NPPROW *p);
- *
- * DESCRIPTION
- *
- * The routine npp_eq_doublet processes row p, which is equality
- * constraint having exactly two non-zero coefficients:
- *
- * a[p,q] x[q] + a[p,r] x[r] = b. (1)
- *
- * As the result of processing one of columns q or r is eliminated from
- * all other rows and, thus, becomes column singleton of type "implied
- * slack variable". Row p is not changed and along with column q and r
- * remains in the problem.
- *
- * RETURNS
- *
- * The routine npp_eq_doublet returns pointer to the descriptor of that
- * column q or r which has been eliminated. If, due to some reason, the
- * elimination was not performed, the routine returns NULL.
- *
- * PROBLEM TRANSFORMATION
- *
- * First, we decide which column q or r will be eliminated. Let it be
- * column q. Consider i-th constraint row, where column q has non-zero
- * coefficient a[i,q] != 0:
- *
- * L[i] <= sum a[i,j] x[j] <= U[i]. (2)
- * j
- *
- * In order to eliminate column q from row (2) we subtract from it row
- * (1) multiplied by gamma[i] = a[i,q] / a[p,q], i.e. we replace in the
- * transformed problem row (2) by its linear combination with row (1).
- * This transformation changes only coefficients in columns q and r,
- * and bounds of row i as follows:
- *
- * a~[i,q] = a[i,q] - gamma[i] a[p,q] = 0, (3)
- *
- * a~[i,r] = a[i,r] - gamma[i] a[p,r], (4)
- *
- * L~[i] = L[i] - gamma[i] b, (5)
- *
- * U~[i] = U[i] - gamma[i] b. (6)
- *
- * RECOVERING BASIC SOLUTION
- *
- * The transformation of the primal system of the original problem:
- *
- * L <= A x <= U (7)
- *
- * is equivalent to multiplying from the left a transformation matrix F
- * by components of this primal system, which in the transformed problem
- * becomes the following:
- *
- * F L <= F A x <= F U ==> L~ <= A~x <= U~. (8)
- *
- * The matrix F has the following structure:
- *
- * ( 1 -gamma[1] )
- * ( )
- * ( 1 -gamma[2] )
- * ( )
- * ( ... ... )
- * ( )
- * F = ( 1 -gamma[p-1] ) (9)
- * ( )
- * ( 1 )
- * ( )
- * ( -gamma[p+1] 1 )
- * ( )
- * ( ... ... )
- *
- * where its column containing elements -gamma[i] corresponds to row p
- * of the primal system.
- *
- * From (8) it follows that the dual system of the original problem:
- *
- * A'pi + lambda = c, (10)
- *
- * in the transformed problem becomes the following:
- *
- * A'F'inv(F')pi + lambda = c ==> (A~)'pi~ + lambda = c, (11)
- *
- * where:
- *
- * pi~ = inv(F')pi (12)
- *
- * is the vector of row multipliers in the transformed problem. Thus:
- *
- * pi = F'pi~. (13)
- *
- * Therefore, as it follows from (13), value of multiplier for row p in
- * solution to the original problem can be computed as follows:
- *
- * pi[p] = pi~[p] - sum gamma[i] pi~[i], (14)
- * i
- *
- * where pi~[i] = pi[i] is multiplier for row i (i != p).
- *
- * Note that the statuses of all rows and columns are not changed.
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * Multiplier for row p in solution to the original problem is computed
- * with formula (14).
- *
- * RECOVERING MIP SOLUTION
- *
- * None needed. */
- struct eq_doublet
- { /* row doubleton (equality constraint) */
- int p;
- /* row reference number */
- double apq;
- /* constraint coefficient a[p,q] */
- NPPLFE *ptr;
- /* list of non-zero coefficients a[i,q], i != p */
- };
- static int rcv_eq_doublet(NPP *npp, void *info);
- NPPCOL *npp_eq_doublet(NPP *npp, NPPROW *p)
- { /* process row doubleton (equality constraint) */
- struct eq_doublet *info;
- NPPROW *i;
- NPPCOL *q, *r;
- NPPAIJ *apq, *apr, *aiq, *air, *next;
- NPPLFE *lfe;
- double gamma;
- /* the row must be doubleton equality constraint */
- xassert(p->lb == p->ub);
- xassert(p->ptr != NULL && p->ptr->r_next != NULL &&
- p->ptr->r_next->r_next == NULL);
- /* choose column to be eliminated */
- { NPPAIJ *a1, *a2;
- a1 = p->ptr, a2 = a1->r_next;
- if (fabs(a2->val) < 0.001 * fabs(a1->val))
- { /* only first column can be eliminated, because second one
- has too small constraint coefficient */
- apq = a1, apr = a2;
- }
- else if (fabs(a1->val) < 0.001 * fabs(a2->val))
- { /* only second column can be eliminated, because first one
- has too small constraint coefficient */
- apq = a2, apr = a1;
- }
- else
- { /* both columns are appropriate; choose that one which is
- shorter to minimize fill-in */
- if (npp_col_nnz(npp, a1->col) <= npp_col_nnz(npp, a2->col))
- { /* first column is shorter */
- apq = a1, apr = a2;
- }
- else
- { /* second column is shorter */
- apq = a2, apr = a1;
- }
- }
- }
- /* now columns q and r have been chosen */
- q = apq->col, r = apr->col;
- /* create transformation stack entry */
- info = npp_push_tse(npp,
- rcv_eq_doublet, sizeof(struct eq_doublet));
- info->p = p->i;
- info->apq = apq->val;
- info->ptr = NULL;
- /* transform each row i (i != p), where a[i,q] != 0, to eliminate
- column q */
- for (aiq = q->ptr; aiq != NULL; aiq = next)
- { next = aiq->c_next;
- if (aiq == apq) continue; /* skip row p */
- i = aiq->row; /* row i to be transformed */
- /* save constraint coefficient a[i,q] */
- if (npp->sol != GLP_MIP)
- { lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE));
- lfe->ref = i->i;
- lfe->val = aiq->val;
- lfe->next = info->ptr;
- info->ptr = lfe;
- }
- /* find coefficient a[i,r] in row i */
- for (air = i->ptr; air != NULL; air = air->r_next)
- if (air->col == r) break;
- /* if a[i,r] does not exist, create a[i,r] = 0 */
- if (air == NULL)
- air = npp_add_aij(npp, i, r, 0.0);
- /* compute gamma[i] = a[i,q] / a[p,q] */
- gamma = aiq->val / apq->val;
- /* (row i) := (row i) - gamma[i] * (row p); see (3)-(6) */
- /* new a[i,q] is exact zero due to elimnation; remove it from
- row i */
- npp_del_aij(npp, aiq);
- /* compute new a[i,r] */
- air->val -= gamma * apr->val;
- /* if new a[i,r] is close to zero due to numeric cancelation,
- remove it from row i */
- if (fabs(air->val) <= 1e-10)
- npp_del_aij(npp, air);
- /* compute new lower and upper bounds of row i */
- if (i->lb == i->ub)
- i->lb = i->ub = (i->lb - gamma * p->lb);
- else
- { if (i->lb != -DBL_MAX)
- i->lb -= gamma * p->lb;
- if (i->ub != +DBL_MAX)
- i->ub -= gamma * p->lb;
- }
- }
- return q;
- }
- static int rcv_eq_doublet(NPP *npp, void *_info)
- { /* recover row doubleton (equality constraint) */
- struct eq_doublet *info = _info;
- NPPLFE *lfe;
- double gamma, temp;
- /* we assume that processing row p is followed by processing
- column q as singleton of type "implied slack variable", in
- which case row p must always be active equality constraint */
- if (npp->sol == GLP_SOL)
- { if (npp->r_stat[info->p] != GLP_NS)
- { npp_error();
- return 1;
- }
- }
- if (npp->sol != GLP_MIP)
- { /* compute value of multiplier for row p; see (14) */
- temp = npp->r_pi[info->p];
- for (lfe = info->ptr; lfe != NULL; lfe = lfe->next)
- { gamma = lfe->val / info->apq; /* a[i,q] / a[p,q] */
- temp -= gamma * npp->r_pi[lfe->ref];
- }
- npp->r_pi[info->p] = temp;
- }
- return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_forcing_row - process forcing row
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_forcing_row(NPP *npp, NPPROW *p, int at);
- *
- * DESCRIPTION
- *
- * The routine npp_forcing row processes row p of general format:
- *
- * L[p] <= sum a[p,j] x[j] <= U[p], (1)
- * j
- *
- * l[j] <= x[j] <= u[j], (2)
- *
- * where L[p] <= U[p] and l[j] < u[j] for all a[p,j] != 0. It is also
- * assumed that:
- *
- * 1) if at = 0 then |L[p] - U'[p]| <= eps, where U'[p] is implied
- * row upper bound (see below), eps is an absolute tolerance for row
- * value;
- *
- * 2) if at = 1 then |U[p] - L'[p]| <= eps, where L'[p] is implied
- * row lower bound (see below).
- *
- * RETURNS
- *
- * 0 - success;
- *
- * 1 - cannot fix columns due to too small constraint coefficients.
- *
- * PROBLEM TRANSFORMATION
- *
- * Implied lower and upper bounds of row (1) are determined by bounds
- * of corresponding columns (variables) as follows:
- *
- * L'[p] = inf sum a[p,j] x[j] =
- * j
- * (3)
- * = sum a[p,j] l[j] + sum a[p,j] u[j],
- * j in Jp j in Jn
- *
- * U'[p] = sup sum a[p,j] x[j] =
- * (4)
- * = sum a[p,j] u[j] + sum a[p,j] l[j],
- * j in Jp j in Jn
- *
- * Jp = {j: a[p,j] > 0}, Jn = {j: a[p,j] < 0}. (5)
- *
- * If L[p] =~ U'[p] (at = 0), solution can be primal feasible only when
- * all variables take their boundary values as defined by (4):
- *
- * ( u[j], if j in Jp
- * x[j] = < (6)
- * ( l[j], if j in Jn
- *
- * Similarly, if U[p] =~ L'[p] (at = 1), solution can be primal feasible
- * only when all variables take their boundary values as defined by (3):
- *
- * ( l[j], if j in Jp
- * x[j] = < (7)
- * ( u[j], if j in Jn
- *
- * Condition (6) or (7) allows fixing all columns (variables x[j])
- * in row (1) on their bounds and then removing them from the problem
- * (see the routine npp_fixed_col). Due to this row p becomes redundant,
- * so it can be replaced by equivalent free (unbounded) row and also
- * removed from the problem (see the routine npp_free_row).
- *
- * 1. To apply this transformation row (1) should not have coefficients
- * whose magnitude is too small, i.e. all a[p,j] should satisfy to
- * the following condition:
- *
- * |a[p,j]| >= eps * max(1, |a[p,k]|), (8)
- * k
- * where eps is a relative tolerance for constraint coefficients.
- * Otherwise, fixing columns may be numerically unreliable and may
- * lead to wrong solution.
- *
- * 2. The routine fixes columns and remove bounds of row p, however,
- * it does not remove the row and columns from the problem.
- *
- * RECOVERING BASIC SOLUTION
- *
- * In the transformed problem row p being inactive constraint is
- * assigned status GLP_BS (as the result of transformation of free
- * row), and all columns in this row are assigned status GLP_NS (as the
- * result of transformation of fixed columns).
- *
- * Note that in the dual system of the transformed (as well as original)
- * problem every column j in row p corresponds to the following row:
- *
- * sum a[i,j] pi[i] + a[p,j] pi[p] + lambda[j] = c[j], (9)
- * i!=p
- *
- * from which it follows that:
- *
- * lambda[j] = c[j] - sum a[i,j] pi[i] - a[p,j] pi[p]. (10)
- * i!=p
- *
- * In the transformed problem values of all multipliers pi[i] are known
- * (including pi[i], whose value is zero, since row p is inactive).
- * Thus, using formula (10) it is possible to compute values of
- * multipliers lambda[j] for all columns in row p.
- *
- * Note also that in the original problem all columns in row p are
- * bounded, not fixed. So status GLP_NS assigned to every such column
- * must be changed to GLP_NL or GLP_NU depending on which bound the
- * corresponding column has been fixed. This status change may lead to
- * dual feasibility violation for solution of the original problem,
- * because now column multipliers must satisfy to the following
- * condition:
- *
- * ( >= 0, if status of column j is GLP_NL,
- * lambda[j] < (11)
- * ( <= 0, if status of column j is GLP_NU.
- *
- * If this condition holds, solution to the original problem is the
- * same as to the transformed problem. Otherwise, we have to perform
- * one degenerate pivoting step of the primal simplex method to obtain
- * dual feasible (hence, optimal) solution to the original problem as
- * follows. If, on problem transformation, row p was made active on its
- * lower bound (case at = 0), we change its status to GLP_NL (or GLP_NS)
- * and start increasing its multiplier pi[p]. Otherwise, if row p was
- * made active on its upper bound (case at = 1), we change its status
- * to GLP_NU (or GLP_NS) and start decreasing pi[p]. From (10) it
- * follows that:
- *
- * delta lambda[j] = - a[p,j] * delta pi[p] = - a[p,j] pi[p]. (12)
- *
- * Simple analysis of formulae (3)-(5) shows that changing pi[p] in the
- * specified direction causes increasing lambda[j] for every column j
- * assigned status GLP_NL (delta lambda[j] > 0) and decreasing lambda[j]
- * for every column j assigned status GLP_NU (delta lambda[j] < 0). It
- * is understood that once the last lambda[q], which violates condition
- * (11), has reached zero, multipliers lambda[j] for all columns get
- * valid signs. Such column q can be determined as follows. Let d[j] be
- * initial value of lambda[j] (i.e. reduced cost of column j) in the
- * transformed problem computed with formula (10) when pi[p] = 0. Then
- * lambda[j] = d[j] + delta lambda[j], and from (12) it follows that
- * lambda[j] becomes zero if:
- *
- * delta lambda[j] = - a[p,j] pi[p] = - d[j] ==>
- * (13)
- * pi[p] = d[j] / a[p,j].
- *
- * Therefore, the last column q, for which lambda[q] becomes zero, can
- * be determined from the following condition:
- *
- * |d[q] / a[p,q]| = max |pi[p]| = max |d[j] / a[p,j]|, (14)
- * j in D j in D
- *
- * where D is a set of columns j whose, reduced costs d[j] have invalid
- * signs, i.e. violate condition (11). (Thus, if D is empty, solution
- * to the original problem is the same as solution to the transformed
- * problem, and no correction is needed as was noticed above.) In
- * solution to the original problem column q is assigned status GLP_BS,
- * since it replaces column of auxiliary variable of row p (becoming
- * active) in the basis, and multiplier for row p is assigned its new
- * value, which is pi[p] = d[q] / a[p,q]. Note that due to primal
- * degeneracy values of all columns having non-zero coefficients in row
- * p remain unchanged.
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * Value of multiplier pi[p] in solution to the original problem is
- * corrected in the same way as for basic solution. Values of all
- * columns having non-zero coefficients in row p remain unchanged.
- *
- * RECOVERING MIP SOLUTION
- *
- * None needed. */
- struct forcing_col
- { /* column fixed on its bound by forcing row */
- int j;
- /* column reference number */
- char stat;
- /* original column status:
- GLP_NL - fixed on lower bound
- GLP_NU - fixed on upper bound */
- double a;
- /* constraint coefficient a[p,j] */
- double c;
- /* objective coefficient c[j] */
- NPPLFE *ptr;
- /* list of non-zero coefficients a[i,j], i != p */
- struct forcing_col *next;
- /* pointer to another column fixed by forcing row */
- };
- struct forcing_row
- { /* forcing row */
- int p;
- /* row reference number */
- char stat;
- /* status assigned to the row if it becomes active:
- GLP_NS - active equality constraint
- GLP_NL - inequality constraint with lower bound active
- GLP_NU - inequality constraint with upper bound active */
- struct forcing_col *ptr;
- /* list of all columns having non-zero constraint coefficient
- a[p,j] in the forcing row */
- };
- static int rcv_forcing_row(NPP *npp, void *info);
- int npp_forcing_row(NPP *npp, NPPROW *p, int at)
- { /* process forcing row */
- struct forcing_row *info;
- struct forcing_col *col = NULL;
- NPPCOL *j;
- NPPAIJ *apj, *aij;
- NPPLFE *lfe;
- double big;
- xassert(at == 0 || at == 1);
- /* determine maximal magnitude of the row coefficients */
- big = 1.0;
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- if (big < fabs(apj->val)) big = fabs(apj->val);
- /* if there are too small coefficients in the row, transformation
- should not be applied */
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- if (fabs(apj->val) < 1e-7 * big) return 1;
- /* create transformation stack entry */
- info = npp_push_tse(npp,
- rcv_forcing_row, sizeof(struct forcing_row));
- info->p = p->i;
- if (p->lb == p->ub)
- { /* equality constraint */
- info->stat = GLP_NS;
- }
- else if (at == 0)
- { /* inequality constraint; case L[p] = U'[p] */
- info->stat = GLP_NL;
- xassert(p->lb != -DBL_MAX);
- }
- else /* at == 1 */
- { /* inequality constraint; case U[p] = L'[p] */
- info->stat = GLP_NU;
- xassert(p->ub != +DBL_MAX);
- }
- info->ptr = NULL;
- /* scan the forcing row, fix columns at corresponding bounds, and
- save column information (the latter is not needed for MIP) */
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- { /* column j has non-zero coefficient in the forcing row */
- j = apj->col;
- /* it must be non-fixed */
- xassert(j->lb < j->ub);
- /* allocate stack entry to save column information */
- if (npp->sol != GLP_MIP)
- { col = dmp_get_atom(npp->stack, sizeof(struct forcing_col));
- col->j = j->j;
- col->stat = -1; /* will be set below */
- col->a = apj->val;
- col->c = j->coef;
- col->ptr = NULL;
- col->next = info->ptr;
- info->ptr = col;
- }
- /* fix column j */
- if (at == 0 && apj->val < 0.0 || at != 0 && apj->val > 0.0)
- { /* at its lower bound */
- if (npp->sol != GLP_MIP)
- col->stat = GLP_NL;
- xassert(j->lb != -DBL_MAX);
- j->ub = j->lb;
- }
- else
- { /* at its upper bound */
- if (npp->sol != GLP_MIP)
- col->stat = GLP_NU;
- xassert(j->ub != +DBL_MAX);
- j->lb = j->ub;
- }
- /* save column coefficients a[i,j], i != p */
- if (npp->sol != GLP_MIP)
- { for (aij = j->ptr; aij != NULL; aij = aij->c_next)
- { if (aij == apj) continue; /* skip a[p,j] */
- lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE));
- lfe->ref = aij->row->i;
- lfe->val = aij->val;
- lfe->next = col->ptr;
- col->ptr = lfe;
- }
- }
- }
- /* make the row free (unbounded) */
- p->lb = -DBL_MAX, p->ub = +DBL_MAX;
- return 0;
- }
- static int rcv_forcing_row(NPP *npp, void *_info)
- { /* recover forcing row */
- struct forcing_row *info = _info;
- struct forcing_col *col, *piv;
- NPPLFE *lfe;
- double d, big, temp;
- if (npp->sol == GLP_MIP) goto done;
- /* initially solution to the original problem is the same as
- to the transformed problem, where row p is inactive constraint
- with pi[p] = 0, and all columns are non-basic */
- if (npp->sol == GLP_SOL)
- { if (npp->r_stat[info->p] != GLP_BS)
- { npp_error();
- return 1;
- }
- for (col = info->ptr; col != NULL; col = col->next)
- { if (npp->c_stat[col->j] != GLP_NS)
- { npp_error();
- return 1;
- }
- npp->c_stat[col->j] = col->stat; /* original status */
- }
- }
- /* compute reduced costs d[j] for all columns with formula (10)
- and store them in col.c instead objective coefficients */
- for (col = info->ptr; col != NULL; col = col->next)
- { d = col->c;
- for (lfe = col->ptr; lfe != NULL; lfe = lfe->next)
- d -= lfe->val * npp->r_pi[lfe->ref];
- col->c = d;
- }
- /* consider columns j, whose multipliers lambda[j] has wrong
- sign in solution to the transformed problem (where lambda[j] =
- d[j]), and choose column q, whose multipler lambda[q] reaches
- zero last on changing row multiplier pi[p]; see (14) */
- piv = NULL, big = 0.0;
- for (col = info->ptr; col != NULL; col = col->next)
- { d = col->c; /* d[j] */
- temp = fabs(d / col->a);
- if (col->stat == GLP_NL)
- { /* column j has active lower bound */
- if (d < 0.0 && big < temp)
- piv = col, big = temp;
- }
- else if (col->stat == GLP_NU)
- { /* column j has active upper bound */
- if (d > 0.0 && big < temp)
- piv = col, big = temp;
- }
- else
- { npp_error();
- return 1;
- }
- }
- /* if column q does not exist, no correction is needed */
- if (piv != NULL)
- { /* correct solution; row p becomes active constraint while
- column q becomes basic */
- if (npp->sol == GLP_SOL)
- { npp->r_stat[info->p] = info->stat;
- npp->c_stat[piv->j] = GLP_BS;
- }
- /* assign new value to row multiplier pi[p] = d[p] / a[p,q] */
- npp->r_pi[info->p] = piv->c / piv->a;
- }
- done: return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_analyze_row - perform general row analysis
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * int npp_analyze_row(NPP *npp, NPPROW *p);
- *
- * DESCRIPTION
- *
- * The routine npp_analyze_row performs analysis of row p of general
- * format:
- *
- * L[p] <= sum a[p,j] x[j] <= U[p], (1)
- * j
- *
- * l[j] <= x[j] <= u[j], (2)
- *
- * where L[p] <= U[p] and l[j] <= u[j] for all a[p,j] != 0.
- *
- * RETURNS
- *
- * 0x?0 - row lower bound does not exist or is redundant;
- *
- * 0x?1 - row lower bound can be active;
- *
- * 0x?2 - row lower bound is a forcing bound;
- *
- * 0x0? - row upper bound does not exist or is redundant;
- *
- * 0x1? - row upper bound can be active;
- *
- * 0x2? - row upper bound is a forcing bound;
- *
- * 0x33 - row bounds are inconsistent with column bounds.
- *
- * ALGORITHM
- *
- * Analysis of row (1) is based on analysis of its implied lower and
- * upper bounds, which are determined by bounds of corresponding columns
- * (variables) as follows:
- *
- * L'[p] = inf sum a[p,j] x[j] =
- * j
- * (3)
- * = sum a[p,j] l[j] + sum a[p,j] u[j],
- * j in Jp j in Jn
- *
- * U'[p] = sup sum a[p,j] x[j] =
- * (4)
- * = sum a[p,j] u[j] + sum a[p,j] l[j],
- * j in Jp j in Jn
- *
- * Jp = {j: a[p,j] > 0}, Jn = {j: a[p,j] < 0}. (5)
- *
- * (Note that bounds of all columns in row p are assumed to be correct,
- * so L'[p] <= U'[p].)
- *
- * Analysis of row lower bound L[p] includes the following cases:
- *
- * 1) if L[p] > U'[p] + eps, where eps is an absolute tolerance for row
- * value, row lower bound L[p] and implied row upper bound U'[p] are
- * inconsistent, ergo, the problem has no primal feasible solution;
- *
- * 2) if U'[p] - eps <= L[p] <= U'[p] + eps, i.e. if L[p] =~ U'[p],
- * the row is a forcing row on its lower bound (see description of
- * the routine npp_forcing_row);
- *
- * 3) if L[p] > L'[p] + eps, row lower bound L[p] can be active (this
- * conclusion does not account other rows in the problem);
- *
- * 4) if L[p] <= L'[p] + eps, row lower bound L[p] cannot be active, so
- * it is redundant and can be removed (replaced by -oo).
- *
- * Analysis of row upper bound U[p] is performed in a similar way and
- * includes the following cases:
- *
- * 1) if U[p] < L'[p] - eps, row upper bound U[p] and implied row lower
- * bound L'[p] are inconsistent, ergo the problem has no primal
- * feasible solution;
- *
- * 2) if L'[p] - eps <= U[p] <= L'[p] + eps, i.e. if U[p] =~ L'[p],
- * the row is a forcing row on its upper bound (see description of
- * the routine npp_forcing_row);
- *
- * 3) if U[p] < U'[p] - eps, row upper bound U[p] can be active (this
- * conclusion does not account other rows in the problem);
- *
- * 4) if U[p] >= U'[p] - eps, row upper bound U[p] cannot be active, so
- * it is redundant and can be removed (replaced by +oo). */
- int npp_analyze_row(NPP *npp, NPPROW *p)
- { /* perform general row analysis */
- NPPAIJ *aij;
- int ret = 0x00;
- double l, u, eps;
- xassert(npp == npp);
- /* compute implied lower bound L'[p]; see (3) */
- l = 0.0;
- for (aij = p->ptr; aij != NULL; aij = aij->r_next)
- { if (aij->val > 0.0)
- { if (aij->col->lb == -DBL_MAX)
- { l = -DBL_MAX;
- break;
- }
- l += aij->val * aij->col->lb;
- }
- else /* aij->val < 0.0 */
- { if (aij->col->ub == +DBL_MAX)
- { l = -DBL_MAX;
- break;
- }
- l += aij->val * aij->col->ub;
- }
- }
- /* compute implied upper bound U'[p]; see (4) */
- u = 0.0;
- for (aij = p->ptr; aij != NULL; aij = aij->r_next)
- { if (aij->val > 0.0)
- { if (aij->col->ub == +DBL_MAX)
- { u = +DBL_MAX;
- break;
- }
- u += aij->val * aij->col->ub;
- }
- else /* aij->val < 0.0 */
- { if (aij->col->lb == -DBL_MAX)
- { u = +DBL_MAX;
- break;
- }
- u += aij->val * aij->col->lb;
- }
- }
- /* column bounds are assumed correct, so L'[p] <= U'[p] */
- /* check if row lower bound is consistent */
- if (p->lb != -DBL_MAX)
- { eps = 1e-3 + 1e-6 * fabs(p->lb);
- if (p->lb - eps > u)
- { ret = 0x33;
- goto done;
- }
- }
- /* check if row upper bound is consistent */
- if (p->ub != +DBL_MAX)
- { eps = 1e-3 + 1e-6 * fabs(p->ub);
- if (p->ub + eps < l)
- { ret = 0x33;
- goto done;
- }
- }
- /* check if row lower bound can be active/forcing */
- if (p->lb != -DBL_MAX)
- { eps = 1e-9 + 1e-12 * fabs(p->lb);
- if (p->lb - eps > l)
- { if (p->lb + eps <= u)
- ret |= 0x01;
- else
- ret |= 0x02;
- }
- }
- /* check if row upper bound can be active/forcing */
- if (p->ub != +DBL_MAX)
- { eps = 1e-9 + 1e-12 * fabs(p->ub);
- if (p->ub + eps < u)
- { /* check if the upper bound is forcing */
- if (p->ub - eps >= l)
- ret |= 0x10;
- else
- ret |= 0x20;
- }
- }
- done: return ret;
- }
- /***********************************************************************
- * NAME
- *
- * npp_inactive_bound - remove row lower/upper inactive bound
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * void npp_inactive_bound(NPP *npp, NPPROW *p, int which);
- *
- * DESCRIPTION
- *
- * The routine npp_inactive_bound removes lower (if which = 0) or upper
- * (if which = 1) bound of row p:
- *
- * L[p] <= sum a[p,j] x[j] <= U[p],
- *
- * which (bound) is assumed to be redundant.
- *
- * PROBLEM TRANSFORMATION
- *
- * If which = 0, current lower bound L[p] of row p is assigned -oo.
- * If which = 1, current upper bound U[p] of row p is assigned +oo.
- *
- * RECOVERING BASIC SOLUTION
- *
- * If in solution to the transformed problem row p is inactive
- * constraint (GLP_BS), its status is not changed in solution to the
- * original problem. Otherwise, status of row p in solution to the
- * original problem is defined by its type before transformation and
- * its status in solution to the transformed problem as follows:
- *
- * +---------------------+-------+---------------+---------------+
- * | Row | Flag | Row status in | Row status in |
- * | type | which | transfmd soln | original soln |
- * +---------------------+-------+---------------+---------------+
- * | sum >= L[p] | 0 | GLP_NF | GLP_NL |
- * | sum <= U[p] | 1 | GLP_NF | GLP_NU |
- * | L[p] <= sum <= U[p] | 0 | GLP_NU | GLP_NU |
- * | L[p] <= sum <= U[p] | 1 | GLP_NL | GLP_NL |
- * | sum = L[p] = U[p] | 0 | GLP_NU | GLP_NS |
- * | sum = L[p] = U[p] | 1 | GLP_NL | GLP_NS |
- * +---------------------+-------+---------------+---------------+
- *
- * RECOVERING INTERIOR-POINT SOLUTION
- *
- * None needed.
- *
- * RECOVERING MIP SOLUTION
- *
- * None needed. */
- struct inactive_bound
- { /* row inactive bound */
- int p;
- /* row reference number */
- char stat;
- /* row status (if active constraint) */
- };
- static int rcv_inactive_bound(NPP *npp, void *info);
- void npp_inactive_bound(NPP *npp, NPPROW *p, int which)
- { /* remove row lower/upper inactive bound */
- struct inactive_bound *info;
- if (npp->sol == GLP_SOL)
- { /* create transformation stack entry */
- info = npp_push_tse(npp,
- rcv_inactive_bound, sizeof(struct inactive_bound));
- info->p = p->i;
- if (p->ub == +DBL_MAX)
- info->stat = GLP_NL;
- else if (p->lb == -DBL_MAX)
- info->stat = GLP_NU;
- else if (p->lb != p->ub)
- info->stat = (char)(which == 0 ? GLP_NU : GLP_NL);
- else
- info->stat = GLP_NS;
- }
- /* remove row inactive bound */
- if (which == 0)
- { xassert(p->lb != -DBL_MAX);
- p->lb = -DBL_MAX;
- }
- else if (which == 1)
- { xassert(p->ub != +DBL_MAX);
- p->ub = +DBL_MAX;
- }
- else
- xassert(which != which);
- return;
- }
- static int rcv_inactive_bound(NPP *npp, void *_info)
- { /* recover row status */
- struct inactive_bound *info = _info;
- if (npp->sol != GLP_SOL)
- { npp_error();
- return 1;
- }
- if (npp->r_stat[info->p] == GLP_BS)
- npp->r_stat[info->p] = GLP_BS;
- else
- npp->r_stat[info->p] = info->stat;
- return 0;
- }
- /***********************************************************************
- * NAME
- *
- * npp_implied_bounds - determine implied column bounds
- *
- * SYNOPSIS
- *
- * #include "glpnpp.h"
- * void npp_implied_bounds(NPP *npp, NPPROW *p);
- *
- * DESCRIPTION
- *
- * The routine npp_implied_bounds inspects general row (constraint) p:
- *
- * L[p] <= sum a[p,j] x[j] <= U[p], (1)
- *
- * l[j] <= x[j] <= u[j], (2)
- *
- * where L[p] <= U[p] and l[j] <= u[j] for all a[p,j] != 0, to compute
- * implied bounds of columns (variables x[j]) in this row.
- *
- * The routine stores implied column bounds l'[j] and u'[j] in column
- * descriptors (NPPCOL); it does not change current column bounds l[j]
- * and u[j]. (Implied column bounds can be then used to strengthen the
- * current column bounds; see the routines npp_implied_lower and
- * npp_implied_upper).
- *
- * ALGORITHM
- *
- * Current column bounds (2) define implied lower and upper bounds of
- * row (1) as follows:
- *
- * L'[p] = inf sum a[p,j] x[j] =
- * j
- * (3)
- * = sum a[p,j] l[j] + sum a[p,j] u[j],
- * j in Jp j in Jn
- *
- * U'[p] = sup sum a[p,j] x[j] =
- * (4)
- * = sum a[p,j] u[j] + sum a[p,j] l[j],
- * j in Jp j in Jn
- *
- * Jp = {j: a[p,j] > 0}, Jn = {j: a[p,j] < 0}. (5)
- *
- * (Note that bounds of all columns in row p are assumed to be correct,
- * so L'[p] <= U'[p].)
- *
- * If L[p] > L'[p] and/or U[p] < U'[p], the lower and/or upper bound of
- * row (1) can be active, in which case such row defines implied bounds
- * of its variables.
- *
- * Let x[k] be some variable having in row (1) coefficient a[p,k] != 0.
- * Consider a case when row lower bound can be active (L[p] > L'[p]):
- *
- * sum a[p,j] x[j] >= L[p] ==>
- * j
- *
- * sum a[p,j] x[j] + a[p,k] x[k] >= L[p] ==>
- * j!=k
- * (6)
- * a[p,k] x[k] >= L[p] - sum a[p,j] x[j] ==>
- * j!=k
- *
- * a[p,k] x[k] >= L[p,k],
- *
- * where
- *
- * L[p,k] = inf(L[p] - sum a[p,j] x[j]) =
- * j!=k
- *
- * = L[p] - sup sum a[p,j] x[j] = (7)
- * j!=k
- *
- * = L[p] - sum a[p,j] u[j] - sum a[p,j] l[j].
- * j in Jp\{k} j in Jn\{k}
- *
- * Thus:
- *
- * x[k] >= l'[k] = L[p,k] / a[p,k], if a[p,k] > 0, (8)
- *
- * x[k] <= u'[k] = L[p,k] / a[p,k], if a[p,k] < 0. (9)
- *
- * where l'[k] and u'[k] are implied lower and upper bounds of variable
- * x[k], resp.
- *
- * Now consider a similar case when row upper bound can be active
- * (U[p] < U'[p]):
- *
- * sum a[p,j] x[j] <= U[p] ==>
- * j
- *
- * sum a[p,j] x[j] + a[p,k] x[k] <= U[p] ==>
- * j!=k
- * (10)
- * a[p,k] x[k] <= U[p] - sum a[p,j] x[j] ==>
- * j!=k
- *
- * a[p,k] x[k] <= U[p,k],
- *
- * where:
- *
- * U[p,k] = sup(U[p] - sum a[p,j] x[j]) =
- * j!=k
- *
- * = U[p] - inf sum a[p,j] x[j] = (11)
- * j!=k
- *
- * = U[p] - sum a[p,j] l[j] - sum a[p,j] u[j].
- * j in Jp\{k} j in Jn\{k}
- *
- * Thus:
- *
- * x[k] <= u'[k] = U[p,k] / a[p,k], if a[p,k] > 0, (12)
- *
- * x[k] >= l'[k] = U[p,k] / a[p,k], if a[p,k] < 0. (13)
- *
- * Note that in formulae (8), (9), (12), and (13) coefficient a[p,k]
- * must not be too small in magnitude relatively to other non-zero
- * coefficients in row (1), i.e. the following condition must hold:
- *
- * |a[p,k]| >= eps * max(1, |a[p,j]|), (14)
- * j
- *
- * where eps is a relative tolerance for constraint coefficients.
- * Otherwise the implied column bounds can be numerical inreliable. For
- * example, using formula (8) for the following inequality constraint:
- *
- * 1e-12 x1 - x2 - x3 >= 0,
- *
- * where x1 >= -1, x2, x3, >= 0, may lead to numerically unreliable
- * conclusion that x1 >= 0.
- *
- * Using formulae (8), (9), (12), and (13) to compute implied bounds
- * for one variable requires |J| operations, where J = {j: a[p,j] != 0},
- * because this needs computing L[p,k] and U[p,k]. Thus, computing
- * implied bounds for all variables in row (1) would require |J|^2
- * operations, that is not a good technique. However, the total number
- * of operations can be reduced to |J| as follows.
- *
- * Let a[p,k] > 0. Then from (7) and (11) we have:
- *
- * L[p,k] = L[p] - (U'[p] - a[p,k] u[k]) =
- *
- * = L[p] - U'[p] + a[p,k] u[k],
- *
- * U[p,k] = U[p] - (L'[p] - a[p,k] l[k]) =
- *
- * = U[p] - L'[p] + a[p,k] l[k],
- *
- * where L'[p] and U'[p] are implied row lower and upper bounds defined
- * by formulae (3) and (4). Substituting these expressions into (8) and
- * (12) gives:
- *
- * l'[k] = L[p,k] / a[p,k] = u[k] + (L[p] - U'[p]) / a[p,k], (15)
- *
- * u'[k] = U[p,k] / a[p,k] = l[k] + (U[p] - L'[p]) / a[p,k]. (16)
- *
- * Similarly, if a[p,k] < 0, according to (7) and (11) we have:
- *
- * L[p,k] = L[p] - (U'[p] - a[p,k] l[k]) =
- *
- * = L[p] - U'[p] + a[p,k] l[k],
- *
- * U[p,k] = U[p] - (L'[p] - a[p,k] u[k]) =
- *
- * = U[p] - L'[p] + a[p,k] u[k],
- *
- * and substituting these expressions into (8) and (12) gives:
- *
- * l'[k] = U[p,k] / a[p,k] = u[k] + (U[p] - L'[p]) / a[p,k], (17)
- *
- * u'[k] = L[p,k] / a[p,k] = l[k] + (L[p] - U'[p]) / a[p,k]. (18)
- *
- * Note that formulae (15)-(18) can be used only if L'[p] and U'[p]
- * exist. However, if for some variable x[j] it happens that l[j] = -oo
- * and/or u[j] = +oo, values of L'[p] (if a[p,j] > 0) and/or U'[p] (if
- * a[p,j] < 0) are undefined. Consider, therefore, the most general
- * situation, when some column bounds (2) may not exist.
- *
- * Let:
- *
- * J' = {j : (a[p,j] > 0 and l[j] = -oo) or
- * (19)
- * (a[p,j] < 0 and u[j] = +oo)}.
- *
- * Then (assuming that row upper bound U[p] can be active) the following
- * three cases are possible:
- *
- * 1) |J'| = 0. In this case L'[p] exists, thus, for all variables x[j]
- * in row (1) we can use formulae (16) and (17);
- *
- * 2) J' = {k}. In this case L'[p] = -oo, however, U[p,k] (11) exists,
- * so for variable x[k] we can use formulae (12) and (13). Note that
- * for all other variables x[j] (j != k) l'[j] = -oo (if a[p,j] < 0)
- * or u'[j] = +oo (if a[p,j] > 0);
- *
- * 3) |J'| > 1. In this case for all variables x[j] in row [1] we have
- * l'[j] = -oo (if a[p,j] < 0) or u'[j] = +oo (if a[p,j] > 0).
- *
- * Similarly, let:
- *
- * J'' = {j : (a[p,j] > 0 and u[j] = +oo) or
- * (20)
- * (a[p,j] < 0 and l[j] = -oo)}.
- *
- * Then (assuming that row lower bound L[p] can be active) the following
- * three cases are possible:
- *
- * 1) |J''| = 0. In this case U'[p] exists, thus, for all variables x[j]
- * in row (1) we can use formulae (15) and (18);
- *
- * 2) J'' = {k}. In this case U'[p] = +oo, however, L[p,k] (7) exists,
- * so for variable x[k] we can use formulae (8) and (9). Note that
- * for all other variables x[j] (j != k) l'[j] = -oo (if a[p,j] > 0)
- * or u'[j] = +oo (if a[p,j] < 0);
- *
- * 3) |J''| > 1. In this case for all variables x[j] in row (1) we have
- * l'[j] = -oo (if a[p,j] > 0) or u'[j] = +oo (if a[p,j] < 0). */
- void npp_implied_bounds(NPP *npp, NPPROW *p)
- { NPPAIJ *apj, *apk;
- double big, eps, temp;
- xassert(npp == npp);
- /* initialize implied bounds for all variables and determine
- maximal magnitude of row coefficients a[p,j] */
- big = 1.0;
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- { apj->col->ll.ll = -DBL_MAX, apj->col->uu.uu = +DBL_MAX;
- if (big < fabs(apj->val)) big = fabs(apj->val);
- }
- eps = 1e-6 * big;
- /* process row lower bound (assuming that it can be active) */
- if (p->lb != -DBL_MAX)
- { apk = NULL;
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- { if (apj->val > 0.0 && apj->col->ub == +DBL_MAX ||
- apj->val < 0.0 && apj->col->lb == -DBL_MAX)
- { if (apk == NULL)
- apk = apj;
- else
- goto skip1;
- }
- }
- /* if a[p,k] = NULL then |J'| = 0 else J' = { k } */
- temp = p->lb;
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- { if (apj == apk)
- /* skip a[p,k] */;
- else if (apj->val > 0.0)
- temp -= apj->val * apj->col->ub;
- else /* apj->val < 0.0 */
- temp -= apj->val * apj->col->lb;
- }
- /* compute column implied bounds */
- if (apk == NULL)
- { /* temp = L[p] - U'[p] */
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- { if (apj->val >= +eps)
- { /* l'[j] := u[j] + (L[p] - U'[p]) / a[p,j] */
- apj->col->ll.ll = apj->col->ub + temp / apj->val;
- }
- else if (apj->val <= -eps)
- { /* u'[j] := l[j] + (L[p] - U'[p]) / a[p,j] */
- apj->col->uu.uu = apj->col->lb + temp / apj->val;
- }
- }
- }
- else
- { /* temp = L[p,k] */
- if (apk->val >= +eps)
- { /* l'[k] := L[p,k] / a[p,k] */
- apk->col->ll.ll = temp / apk->val;
- }
- else if (apk->val <= -eps)
- { /* u'[k] := L[p,k] / a[p,k] */
- apk->col->uu.uu = temp / apk->val;
- }
- }
- skip1: ;
- }
- /* process row upper bound (assuming that it can be active) */
- if (p->ub != +DBL_MAX)
- { apk = NULL;
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- { if (apj->val > 0.0 && apj->col->lb == -DBL_MAX ||
- apj->val < 0.0 && apj->col->ub == +DBL_MAX)
- { if (apk == NULL)
- apk = apj;
- else
- goto skip2;
- }
- }
- /* if a[p,k] = NULL then |J''| = 0 else J'' = { k } */
- temp = p->ub;
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- { if (apj == apk)
- /* skip a[p,k] */;
- else if (apj->val > 0.0)
- temp -= apj->val * apj->col->lb;
- else /* apj->val < 0.0 */
- temp -= apj->val * apj->col->ub;
- }
- /* compute column implied bounds */
- if (apk == NULL)
- { /* temp = U[p] - L'[p] */
- for (apj = p->ptr; apj != NULL; apj = apj->r_next)
- { if (apj->val >= +eps)
- { /* u'[j] := l[j] + (U[p] - L'[p]) / a[p,j] */
- apj->col->uu.uu = apj->col->lb + temp / apj->val;
- }
- else if (apj->val <= -eps)
- { /* l'[j] := u[j] + (U[p] - L'[p]) / a[p,j] */
- apj->col->ll.ll = apj->col->ub + temp / apj->val;
- }
- }
- }
- else
- { /* temp = U[p,k] */
- if (apk->val >= +eps)
- { /* u'[k] := U[p,k] / a[p,k] */
- apk->col->uu.uu = temp / apk->val;
- }
- else if (apk->val <= -eps)
- { /* l'[k] := U[p,k] / a[p,k] */
- apk->col->ll.ll = temp / apk->val;
- }
- }
- skip2: ;
- }
- return;
- }
- /* eof */
|