📄 readme.custom
字号:
Custom Modification to lp_solve_2.3This file documents the modifications that I have made to the stockdistributions of lp_solve_2.3. ================ BUG FIXES ================I have fixed a number of actual bugs in lp_solve_2.3:- The comparison of theta against the upper bound in dual case of "solvelp()" did not match the similar condition at the top of "iteration()". (One used Epsb, the other did not.) This resulted in a small window of values where the two routines took incompatible execution paths. This usually resulted in solutions that weren't even feasible!- The "get_reduced_costs" routine did not properly handle problems that were scaled.- Fixed "delete_row_set" routine (see below -- this is a new interface I added) to modify the basis more carefully when deleting rows. ================ Stylistic Changes ================The equates EQ, LE, GE that lp_solve uses to represent constraint typesclashes with equates that are used in my own code to represent the Coperators ==, <=, and >=. It was much easier to change the names ofthese equates in lp_solve than to adjust my own code. ================ NEW INTERFACES ================A number of new interfaces have been added that make it much moreefficient or convenient to construct and modify large problems. add_rows ()Previously, a problem could be initially set up only by calling"add_constraint" once per row, or "add_column" once per variable.Setting up the initial LP matrix for a 200 point Steiner tree problemthis way used to require SEVERAL HUNDRED CPU SECONDS!The sparse array is column oriented, so each call to add_constraintforced a global reorganization of the entire sparse array. Callingadd_column once per variable is not much better, since the sparse arrayis always grown by exactly the amount of space that will be needed forthe new column. Thus the entire matrix is copied each time REALLOC runsinto something else.The new "add_rows ()" routine permits an arbitrary number of rows to beadded in one call. The new rows are also specified in a sparse mannerso that this is also a reasonable way to set up a problem initially. set_bounds ()Stock lp_solve_2.3 provides two separate routines for setting the boundson a variable: "set_upbo ()" and "set_lowbo ()". Unfortunately, thereis no routine provided that will SIMULTANEOUSLY SET BOTH BOUNDS. To seewhy this is important, consider what happens when solving a 0-1 integerprogramming problem via branch-and-bound: suppose integer variable x2has bounds [0..1], and currently has a fractional value. To branch onvariable x2=0 we force the bounds to be [0..0] by calling: set_lowbo (lp, x2, 0.0); set_upbo (lp, x2, 0.0);and the re-optimizing the modified LP. At some later point we want tobranch on x2=1. We now force the bounds to be [1..1] by calling: set_lowbo (lp, x2, 1.0); set_upbo (lp, x2, 1.0);Unfortunately, this call to set_lowbo FAILS because the requested lowerbound exceeds the CURRENTLY SET upper bound! As defined, the set_lowboand set_upbo routines require their caller to be intimately aware of theCURRENT STATE of the bounds and to invoke these routines in an orderthat does not create an invalid intermediate state! This is an almostunusable interface.Solution -- the new "set_bounds ()" routine replaces both the upper andlower bounds with new values that must be consistent. The currentbounds are irrelevant. delete_row_set ()Provides a way of deleting a specified subset of the rows in one fellswoop. This is MUCH more efficient than doing it one row at a time withdel_constraint. lpbinio.c -- dump_lp() and load_lp()This new file contains routines to write LP problem instances out to afile in a binary format that can be loaded back in without causing anychange in the numeric values of the problem.Although such files contain binary data, they are written in"big-endian" byte order so that they are somewhat portable from onemachine to another. For example, I have successfully ported these filesfrom a Sparc to an IBM RS/6000 -- both of which use IEEE floatingpoint.I wrote these as a tool to analyze lp_solve's numeric difficulties. Ifigured that if I could carry a difficult problem instance EXACTLY overto CPLEX then I could use CPLEX to give me some clue as to why theproblem was difficult. Only one problem -- CPLEX solved every one ofthese problems without comment! So far not a single problem thatlp_solve croaks on causes CPLEX any concern whatsoever! ======= Attempts to Make lp_solve More Robust =======I have added a substantial amount of debugging code (most of it #if 0'dout), in an attempt to understand the causes of lp_solve' instability.One of my early theories was degeneracy (this theory appears to beincorrect).The TRUE cause of lp_solve's numeric instability seems to be an almosttotal lack of concern over the values it uses as pivot elements!Essentially, it will pivot blindly on ANY ELEMENT whose magnitude is1e-8 or larger! The use of "my_round" to squish small values into zerois its only real method of avoiding bad pivots. Note also that itsepsilon rounding tolerances are all ABSOLUTE magnitudes, not magnitudesthat are relative to the problem data. (In GeoSteiner, only theobjective row can be badly scaled, so this is less of a problem than itmight seem.)With such little concern over the magnitude of its pivot choices, thereshould be no surprise that it behaves so erratically.Consider that the primal simplex algorithm permits ANY column havingnegative reduced cost to be chosen. Once you pick the column, however,you have NO CHOICE about which row to pivot in (modulo ties, that is).lp_solve ALWAYS picks the column having the MOST-NEGATIVE reduced cost-- NO MATTER HOW BAD A PIVOT ELEMENT RESULTS! Stability should improvedramatically if such pivots are rejected by CHOOSING A DIFFERENT COLUMNto pivot in -- until you get a decent pivot element!Similarly in the dual simplex algorithm, you can choose any infeasiblerow. Once you pick the row, however, you have little choice over thecolumn. Once again, lp_solve ALWAYS picks the MOST-INFEASIBLE row -- NOMATTER HOW BAD A PIVOT ELEMENT RESULTS! Stability should improvedramatically if such pivots are rejected by choosing a different row topivot in -- until you get a decent pivot element.Two other issues involve loss of primal (and perhaps dual?)feasibility, and failure of matrix inversion. I have modified the codeto address both of these issues. After every primal reinversion, thecode now verifies that the basis is still primal feasible. If not, itswitches back to the dual. The algorithm fails if this happens 5times.Similarly, attempts to reinvert a singular basis (column all zeros ineach of the remaining pivot rows) are handled by simply leaving theoffending column out of the basis (substituting one of the slack columnsinstead). The resulting basis may not even be feasible, so the codechecks feasibility and reenters either the primal or dual asappropriate. In all probability you have also lost much progress on theobjective function -- but at least it represents SOMETHING to continuewith! The algorithm fails when this happens 5 times.Of course, picking better pivots in the first place would do much tosteer you away from such singular bases.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -