# HG changeset patch # User Alpar Juttner # Date 1291637361 -3600 # Node ID c445c931472fc809a9c19902829d209a64ebca61 # Parent d59bea55db9be53b53a3e2f3716174e47a50777c Import glpk-4.45 - Generated files and doc/notes are removed diff -r d59bea55db9b -r c445c931472f AUTHORS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AUTHORS Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,33 @@ +The GLPK package was developed and programmed by Andrew Makhorin, +Department for Applied Informatics, Moscow Aviation Institute, Moscow, +Russia. + +E-mail: + +Paper mail: 125871, Russia, Moscow, Volokolamskoye sh., 4, + Moscow Aviation Institute, Andrew O. Makhorin + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.2.2 (MingW32) + +mQGiBD8UdT8RBAC6yWYxoa1b7U973J0jBuQpgZnhXlGJJpMZgAW9efDBD17vhkJm +hPVOQBKRUeOOLcW3/a7NMoNLMdmF1Rgz1FPVy3RgBDsYj4Sp4RBCsX/o0xXh+jxe +gncr4bdN0Ruk03pezVtLi9oYygdxfI51SsjZ2vwYP6BhMwv+xrIgcnc4qwCgoCit +26mTd0FoGOmsQuipo6X5LaUD/1l7NqFIjiGdWthyG3TqsiK5Ew7xF3fLEABXKPjb +PMRTMucX8XXHmW8RUD1vP1uGDnEn6s+fjc3/RtaqKjqGMdLt4XgHQkImaVguNpSS +IxN3LaK600BgAbwSd1bomRqWNlczAM7469VvGG9ASpCBveUUrqwerHZcUbvngL62 +pIcqA/41dO0xYrOTqMRhuguYMgHL2xbwB2Aj2TqRwBm697DIS25B9nE+8UsbjGRx +q3EmeuHeZ5kN5RbESXkGUB8whIcYxvH16HRNmM1ZjmFoBVL2Z6S2gpa2ZUqsq7BZ +s+hriElm3dfOQCt79/o852uKWu5bSjw2yiemVA2T8tG4OoN6DrQjQW5kcmV3IE1h +a2hvcmluIDxtYW9AbWFpMi5yY25ldC5ydT6IWwQTEQIAGwUCPxR1PwYLCQgHAwID +FQIDAxYCAQIeAQIXgAAKCRDRe/IwWYHoGKpHAJ44MmzWKr8OiTc0Bb6/RD56aekp +3wCdGznQMCfWFkehQPbeNaB5yFIs+8a5AQ0EPxR1UBAEAO3U3H5M0iYv06C4kKty +6ReWyYH4CzMAfp2lPVUKzRSjPtoAJ6SkrBSKMT+U+DahxZ4K4HbinvHq3uvlwWax +xw0wKxJl4oY6EGE1Jqn3B//Ak47RaNMnrs9V739WT1YNRpQvh4wOCeMekBzksf43 +hm4dSV4PMQkLmrEeG2+BYaZnAAMFA/4tVHhjGRkxzcTcfHCB+Yo3PXeIQMuIs00c +VKCrNReLni/3BWZC0w9tFzZSdz+URXefPWDGuAC16vLCVOD06NcIQGutPe189dUn +Kf9Bl6qc9DyWsxSTdF/PbLqcLfEe9g7fHhIwdY+w/hSq2n3NEURMzHiMT1U3CvHd +As5IzV/yD4hGBBgRAgAGBQI/FHVQAAoJENF78jBZgegYRZEAmwReJkMSrbs0EQs2 +wjyTCMd5KDh3AKCR2/RvVad9RT3ShYnUiPPYTL2/Nw== +=OfLQ +-----END PGP PUBLIC KEY BLOCK----- diff -r d59bea55db9b -r c445c931472f COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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. + + This program 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 this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff -r d59bea55db9b -r c445c931472f ChangeLog --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ChangeLog Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,2356 @@ +Sun Dec 05 12:00:00 2010 Andrew Makhorin + + * GLPK 4.45 (30:0:30) has been released + + * glplpx01.c + A bug (it_cnt) in routine reset_parms was fixed. + Thanks to Ali Baharev for report. + + * glpmpl03.c + A bug (print "text\") was fixed. + Thanks to Xypron for report. + + * glpsql.c + A precision bug was fixed. + Thanks to Xypron . + + * glpk.tex + Some typos were corrected. + Thanks to Robbie Morrison . + +Thu Jun 03 12:00:00 2010 Andrew Makhorin + + * GLPK 4.44 (29:0:29) has been released + + * glpapi14.c glpmpl.h glpmpl01.c glpmpl03.c glpmpl04.c + Implemented suffixes for variables and constraints. + + * glpmpl06.c + Made changes to allow comment records in CSV files. + + * glpapi17.c + Added and documented new API routine glp_cpp to solve Critical + Path Problem. + +Sat Feb 20 12:00:00 2010 Andrew Makhorin + + * GLPK 4.43 (28:0:28) has been released + + * glplib.h, glplib.c, glpenv.h, glpenv.c + The module glpenv was split into two modules glpenv and glplib. + + * glpenv01.c, glpenv03.c, glpenv04.c, glpenv06.c + The following new API routines were added and documented: + glp_init_env, glp_free_env, glp_open_tee, glp_close_tee, + glp_error (macro), glp_difftime. + + * glpapi16.c + New API routine glp_top_sort (topological sorting of ayclic + digraph) was added and documented. + + * glpapi17.c + A serious bug was fixed in the routine glp_asn_prob_hall. + + * glpnpp05.c + A bug was fixed in the LP/MIP preprocessor (hidden covering + inequalities). + + * glpsql.c + Some improvements were made in the table driver (NULL data). + Thanks to Xypron for contribution. + + * configure.ac + Changes were made to use .dylib rather than .so under Mac OS. + Thanks to Noli Sicad for testing + +Wed Jan 13 12:00:00 2010 Andrew Makhorin + + * GLPK 4.42 (27:0:27) has been released + + * glpapi01.c, glpapi11.c, glpapi12.c, glpdmx.c + The following new API routines were added and documented: + glp_check_dup (check for duplicate elements in sparse matrix); + glp_sort_matrix (sort elements of the constraint matrix); + glp_read_prob (read problem data in GLPK format); + glp_write_prob (write problem data in GLPK format); + glp_analyze_bound (analyze active bound of non-basic variable); + glp_analyze_coef (analyze obj. coefficient at basic variable); + glp_print_ranges (print sensitivity analysis report; replaces + lpx_print_sens_bnds). + + * glpapi20.c + New command-line options were added to glpsol: + --glp (read problem data in GLPK format); + --wglp (write problem data in GLPK format); + --lp (replaces --cpxlp); + --wlp (replaces --wcpxlp); + --ranges (print sensitivity analysis report). + + * glpapi06.c + In the routine glp_init_smcp default value of the parameter + out_frq was changed to 500 (it was 200). + + * glpipp.h, glpipp01.c, glpipp02.c + The old MIP preprocessor module was removed. + + * glpapi09.c + Now the MIP solver uses the new MIP preprocessor (NPP). + + * glplpx03.c + lpx_write_opb was disabled due to replacing IPP with NPP. + + * glpnet09.c + Kellerman's heuristic to cover edges by cliques was added. + + * glplib08.c + Recognition of special filenames "/dev/stdin", "/dev/stdout", + and "/dev/stderr" was added. + + * glpk.tex + Chapter "Graph and network routines" was carried out from the + reference manual as a separate document. + +Mon Dec 21 12:00:00 2009 Andrew Makhorin + + * GLPK 4.41 (26:0:26) has been released + + * glpapi12.c + The following new API routines were added: + glp_transform_row (replaces lpx_transform_row); + glp_transform_col (replaces lpx_transform_col); + glp_prim_rtest (replaces lpx_prim_ratio_test); + glp_dual_rtest (replaces lpx_dual_ratio_test). + Note that values returned by glp_prim_rtest and glp_dual_rtest + differ from the ones retutned by the deprecated routines. + + * glpnpp*.* + The LP/MIP preprocessor was essentially re-implemented. + + * glpios03.c + The feature to remove inactive cuts from the active subproblem + was implemented. + + * glpios11.c + The feature processing cuts stored in the cut pool was improved + (now it uses estimation of objective degradation). + + * glpscg.* + Obsolete implemetation of the conflict graph was removed. + + * glpmpl.h, glpmpl03.c, glpmpl04.c + FILE was replaced by XFILE to allow using GLPK I/O routines. + + * glpsql.c, examples/sql, doc/tables.tex + The SQL table driver was changed to allow multiple arguments + separated by semicolon in SQL statements. Thanks to Xypron + . + + * glpk.h, glpapi14.c + New API routine glp_time was added (not documented yet). + + * glpapi20.c + Two new options were added to glpsol: --seed value (initialize + pseudo-random number generator used in MathProg model with + specified seed value), and --ini filename (use as initial basis + previously saved with -w option). + + * examples/xyacfs.mod + Thanks to Nigel Galloway for + contribution. + + * examples/dbf/*.* + Thanks to Noli Sicad for contribution. + + * w32/*.*, w64/*.* + Scripts to build GLPK with Microsoft Visual Studio 2010 were + added. Thanks to Xypron for contribution + and testing. + +Tue Nov 03 12:00:00 2009 Andrew Makhorin + + * GLPK 4.40 (25:0:25) has been released + + * glpdmx.c + Two new API routines were added: + glp_read_ccdata (read graph in DIMACS clique/coloring format); + glp_write_ccdata (write graph in DIMACS clique/coloring format). + Also an example file examples/sample.col was added. + + * glpapi19.c, glpnet08.c + New API routine glp_wclique_exact was added. It is intended to + find a maximum weight clique with the exact algorithm developed + by Prof. P. Ostergard. + + * glpnpp02.c + A bug was fixed in the LP preprocessor (routine npp_empty_col). + Thanks to Stefan Vigerske for the + bug report. + + * glpios10.c + A bug was fixed and some improvements were made in the FPUMP + heuristic module. Thanks to Xypron . + + * glpapi12.c + A bug was fixed in the API routine glp_warm_up (dual + feasibility test was incorrect in maximization case). Thanks to + Uday Venkatadri for the bug report. + + * glpapi16.c + Two new API routines were added: + glp_del_vertices (remove vertices from graph); + glp_del_arc (remove arc from graph). + + * glpios09.c + The hybrid pseudocost branching heuristic was included in the + MIP solver. It is available on API level (iocp.br_tech should + be set to GLP_BR_PCH) and in the stand-alone solver glpsol + (via the command-line option --pcost). This heuristic may be + useful on solving hard MIP instances. + + * glpios03.c + The branching heuristic by Driebeck and Tomlin (used in the + MIP solver by default) was changed to switch to branching on + most fractional variable if an lower bound of degradation of + the objective is close to zero for all branching candidates. + +Sun Jul 26 12:00:00 2009 Andrew Makhorin + + * GLPK 4.39 (24:0:24) has been released + + * glpsdf.c + New API routines to read plain data files were added. + + * glpcpx.h, glpini.h, glpscl.h + These headers were removed. + + * glpcpx.c + API routines glp_read_lp and glp_write_lp to read/write files + in CPLEX LP format were re-implemented. Now glp_write_lp + correctly writes double-bounded (ranged) rows by introducing + slack variables rather than by duplicating the rows. The data + structure glp_cpxcp and routine glp_init_cpxcp were added. + + * amd/* + The 'xfree(NULL)' bug was fixed in the AMD routines. Thanks to + Niels Klitgord for the bug report. + + * glpapi16.c + New API routines glp_set_vertex_name, glp_create_v_index, + glp_find_vertex, and glp_delete_v_index were added. + + * glpdmx.c + New API routines glp_read_asnprob, glp_write_asnprob, + glp_read_ccformat, and glp_write_ccformat were added (the two + latter routines are not documented yet). + + * glpapi18.c + New API routines glp_check_asnprob, glp_asnprob_lp, + glp_asnprob_okalg, and glp_asnprob_hall were added. + + * glpini01.c, glpini02.c + The message "Crashing..." was changed to "Constructing initial + basis..." due to suggestion by Thomas Kahle . + + * glpapi14.c + New API routines glp_printf, glp_vprintf, glp_malloc, + glp_calloc, glp_free, and glp_assert were added. + + * glplpp.h, glplpp01.c, glplpp02.c + Old LP presolver routines were removed. Now glp_simplex uses + new preprocessing routines (see glpnpp). + + * glpapi12.c + New API routine glp_warm_up was added; it replaces the routine + lpx_warm_up. + +Sat May 02 12:00:00 2009 Andrew Makhorin + + * GLPK 4.38 (23:0:23) has been released + + * glpmps.c + API routines to read/write MPS files were re-implemented. + + * glpspx02.c + Some improvements were made in the dual simplex routine. + + * glpk.h + New structure glp_iptcp was added. + + * glpnpp.h, glpnpp01.c, glpnpp02.c + New LP/MIP preprocessor. Currently it includes only some basic + routines and used only in the interior-point solver. + + * glpapi08.c + API routine glp_interior was replaced by an improved version + (new LP/MIP preprocessor, new ordering algorithms). + + New API routine glp_init_iptcp was added. + + API routine glp_ipt_status may return two new statuses due to + changes in glp_interior. + + * glpsol.c + New command-line options were added (ordering algorithm used in + the interior-point solver). + + * amd/*.*, colamd/*.* + Two external software modules AMD and COLAMD/SYMAMD used in the + interior-point solver were included in the distribution. + + For details see amd/README and colamd/README. + + * glpnet03.c, glpnet04.c, glpnet05.c + A minor bug was fixed (_G => G_). Thanks to Nelson H. F. Beebe + for bug report. + +Sun Mar 29 12:00:00 2009 Andrew Makhorin + + * GLPK 4.37 (22:0:22) has been released + + * glpk.h + iocp.fp_heur was added to enable/disable fpump heuristic. + + * glpios10.c + ios_feas_pump was added (feasibility pump heuristic). + + * glpsol.c + --fpump command-line option was added. + + * glpsds.c + Plain data set routines were added to facilitate reading plain + data in application programs. Currently these routines are not + in API, though declared in glpk.h. + + * glpapi08.c + A bug was fixed in the internal routine restore. (Due to this + bug dual solution components were computed incorrectly if the + problem was scaled.) + + * glpapi10.c, glpapi11.c + The following new API routines were added: + glp_print_sol (replaces lpx_print_sol); + glp_print_ipt (replaces lpx_print_ips); + glp_print_mip (replaces lpx_print_mip); + _glp_check_kkt (replaces lpx_check_kkt, lpx_check_int). + Now the routine lpx_print_prob (deprecated) is equivalent to + the routine glp_write_lp. + + * glpapi18.c, glpapi19.c + The following new API routines were added: + glp_read_graph (read graph from plain text file); + glp_write_graph (write graph to plain text file); + glp_weak_comp (find all weakly connected components); + glp_strong_comp (find all strongly connected components). + + * configure.ac, Makefile.am + Changes were made: (a) to allow using autoreconf/autoheader; + (b) to allow building glpk in a directory other than its source + directory. Thanks to Marco Atzeri for + bug report. + + * examples/shiftcover.mod + An example model in MathProg was added. + Thanks to Larry D'Agostino + for contribution. + +Fri Feb 06 12:00:00 2009 Andrew Makhorin + + * GLPK 4.36 (21:0:21) has been released + + * glpnet06.c, glpnet07.c, glpapi19.c + The following new API routines were added: + glp_mincost_okalg find minimum-cost flow with out-of-kilter + algorithm + glp_maxflow_ffalg find maximal flow with Ford-Fulkerson + algorithm + + * glpsol.c + Two new command-line options were added: + --mincost read min-cost flow data in DIMACS format + --maxflow read maximum flow data in DIMACS format + + * doc/glpk.* + New edition of the reference manual was included. + + * glpk.h + Duplicate symbols were removed to allow using swig. + Thanks to Kelly Westbrooks and + Nigel Galloway for suggestion. + + * glpcpx.c + A minor defect was fixed in the routine glp_write_lp. + Thanks to Sebastien Briais for bug report. + + * glpsql.c + A minor bug was fixed. Thanks to Xypron + for patch. + + * examples/hashi.mod, examples/shikaku.mod + Two example models in MathProg were added. Thanks to Sebastian + Nowozin for contribution. + + * examples/qfit.mod, examples/yacfs.mod + Two example models in MathProg were added. Thanks to Nigel + Galloway for contribution. + +Fri Jan 09 12:00:00 2009 Andrew Makhorin + + * GLPK 4.35 (20:0:20) has been released + + * glpk.h, glpapi.c, glpnet.c + The following new API routines were added: + glp_create_graph create graph + glp_set_graph_name assign (change) graph name + glp_add_vertices add new vertices to graph + glp_add_arc add new arc to graph + glp_erase_graph erase graph content + glp_delete_graph delete graph + glp_read_mincost read minimum cost flow problem data in + DIMACS format + glp_write_mincost write minimum cost flow problem data in + DIMACS format + glp_mincost_lp convert minimum cost flow problem to LP + glp_netgen Klingman's network problem generator + glp_gridgen grid-like network problem generator + glp_read_maxflow read maximum flow problem data in DIMACS + format + glp_write_maxflow write maximum flow problem data in DIMACS + format + glp_maxflow_lp convert maximum flow problem to LP + glp_rmfgen Goldfarb's maximum flow problem generator + + * doc/glpk.* + New edition of the reference manual was included. + + * examples/sample.min, examples/sample.max + Two example data files in DIMACS format were added. + + * glplib04.c + The statement "if (c = '\n') fflush(stdout)" was added to the + internal routine xputc to provide "real-time" terminal output. + Thanks to Luiz Bettoni for + suggestion. + + * glpmpl05.c + A minor bug was fixed in the internal routine mpl_fn_time2str. + Thanks to Stefan Vigerske for bug report. + + * w32/makefile, w64/makefile + The flag -O2 (/O2) was added to some makefiles. + +Thu Dec 04 12:00:00 2008 Andrew Makhorin + + * GLPK 4.34 (19:0:19) has been released + + * src/glpios03.c + A bug was fixed in the internal routine branch_on. Thanks to + Nigel Galloway for bug report. + + * src/glpmpl05.c + Three new MathProg functions were included: + gmtime obtaining current calendar time + str2time converting character string to calendar time + time2str converting calendar time to character string + Thanks to Xypron . + + * doc/glpk.*, doc/gmpl.* + A new edition of the GLPK reference manual and GNU MathProg + language description were included. + +Thu Oct 30 12:00:00 2008 Andrew Makhorin + + * GLPK 4.33 (18:0:18) has been released + + * glpapi*.* + The following new API routines were added: + glp_copy_prob copy problem object content + glp_exact solve LP in exact arithmetic + (makes lpx_exact deprecated) + glp_get_unbnd_ray determine variable causing unboundedness + (makes lpx_get_ray_info deprecated) + glp_interior solve LP with interior-point method + (makes lpx_interior deprecated) + + * glpapi*.* + The following new API routines for processing models written in + the GNU Mathprog language were added to the package: + glp_mpl_alloc_wksp allocate the translator workspace + glp_mpl_read_model read and translate model section + glp_mpl_read_data read and translate data section + glp_mpl_generate generate the model + glp_mpl_build_prob build LP/MIP instance from the model + glp_mpl_postsolve postsolve the model + glp_mpl_free_wksp deallocate the translator workspace + (These routines make lpx_read_model deprecated.) + + * src/glpapi17.c, examples/glpsol.c + The stand-alone solver glpsol was re-implemented with new API + routines. + + * src/glpsql.c + Some bugs were fixed in the SQL table driver. Thanks to Xypron + . + + * examples/cplex/*.* + A crude implementation of CPLEX-like interface to GLPK API was + added to the package. See examples/cplex/README. + +Fri Oct 03 12:00:00 2008 Andrew Makhorin + + * GLPK 4.32 (17:0:17) has been released + + * glpmpl01.c + A bug was fixed. Due to this bug iterated expressions having + an indexing expression whose dummy indices are bound to some + values, i.e. like sum{(i+1,j,k-1) in E} x[i,j,k] are evaluated + incorrectly. Namely, current value of such expressions is not + invalidated when corresponding dummy indices (like i and k in + the example above) are changed, that erroneously results in the + same value evaluated for the first time. + + * glpios03.c + Euclidean reduction of the local objective bound was added in + the routine glpios03.c. + + * glpapi11.c + The following new branch-and-cut API routines were added: + glp_ios_row_attr determine additional row attributes; + glp_ios_pool_size determine current size of the cut pool; + glp_ios_add_row add constraint to the cut pool; + glp_ios_del_row delete constraint from the cut pool; + glp_ios_clear_pool delete all constraints from the cut pool. + + * glpapi08.c + The following new features were included in the branch-and-cut + solver (the API routine glp_intopt): + MIP presolver; + mixed cover cut generator; + clique cut generator. + Due to the MIP presolver glp_intopt may additionally return + GLP_ENOPFS and GLP_ENODFS, if primal or dual infeasibility of + LP relaxation is detected by the presolver. Also the return + code GLP_EMIPGAP was introduced to correctly indicate that the + mip gap tolerance is reached. + + * glplpx01.c + Now the obsolete API routines lpx_integer and lpx_intopt are + completely superseded by the API routine glp_intopt that makes + them deprecated. + + * glpmpl05.c + Now the table driver name "iODBC" can be specified as "ODBC". + + * glpmpl03.c + A bug fixed in the routine free_dca. + Thanks to Xypron . + + * glpsql.c + A bug was fixed in the SQL table driver. + Thanks to Xypron . + + * examples/glpsol.c + Changes were made to allow multiple MathProg data files. + + * doc/glpk.* + A new edition of the GLPK reference manual was included. + + * doc/tables.* + A new edition of the supplement "Using data tables in the GNU + MathProg language" was included. + +Tue Sep 02 12:00:00 2008 Andrew Makhorin + + * GLPK 4.31 (16:0:16) has been released + + * glpspx.h, glpspx01.c, glpspx02.c, glpapi06.c + The dual simplex solver (spx_dual_opt) was replaced by a new + implementation of the two-phase dual simplex method (spx_dual). + Old simplex method routines (spx_prim_opt, spx_prim_feas, and + spx_dual_opt) were removed from the package. + + * glpk.h, glpscl.h, glpscl.c, glpapi04.c + New API routine glp_scale_prob was added. It replaces routine + lpx_scale_prob which is deprecated. + + * glpk.h, glpini.h, glpini01.c, glpini02.c, glpapi05.c + New API routines glp_std_basis, glp_adv_basis, glp_cpx_basis + were added. They replace routines lpx_std_basis, lpx_adv_basis, + lpx_cpx_basis which are deprecated. + + * glpdmp.c + 8-byte data alignment was added to the module (sufficient for + both ILP32 and LP64 environments). + + * glplib07.c + 16-byte data alignment was added to the module to provide + compatibility with LP64 environment (8-byte is not sufficient + due to jmp_buf; thanks to Xypron for investigation). + + * glplpx16.c + New version of the routine lpx_write_pb was added. Thanks to + Oscar Gustafsson for the contribution. + + * w32/VC9, w64/VC9 + Makefiles and batch files were added to build GLPK under 32- + and 64-bit Windows with Microsoft Visual Studio Express 2008. + Thanks to Heinrich Schuchardt for + the contribution and testing. + + * w32/DM, w32/OWC + Makefiles and batch files were added to build GLPK with Digital + Mars C/C++ 8.50 and Open Watcom C/C++ 1.6 (32-bit Windows). + +Wed Aug 13 12:00:00 2008 Andrew Makhorin + + * GLPK 4.30 (15:0:15) has been released + + * glpspx.h, glpspx03.c, glpapi06.c + The primal simplex solver (spx_prim_opt, spx_prim_feas) was + replaced by a new implementation (spx_primal), which currently + provides the same features as the old version. + + * glpmpl01.c, glpmpl03.c + Some changes were made in the MathProg translator to allow <, + <=, >=, and > on comparing symbolic values. Thanks to Heinrich + Schuchardt for patches. + + * glplpx10.c + Internal routine set_d_eps in the exact LP solver was changed + to prevent approximation errors in case of integral data. + Thanks to Markus Pilz for bug report. + +XXX XXX XX 12:00:00 2008 Andrew Makhorin + + * GLPK 4.29 (14:0:14) has been released + + * configure.ac + The configure script was changed to disable optional features + by default. For details see file INSTALL. + + * glpipp02.c + A bug was fixed in the internal routine reduce_bounds. Thanks + to Anne-Laurence Putz for + the bug report. + + * glpapi01.c + New API routine glp_erase_prob was added. + + * glpapi13.c + New API routines glp_read_mps and glp_write_mps were added. + They replace API routines lpx_read_mps, lpx_read_freemps, + lpx_write_mps, and lpx_write_freemps, which are deprecated. + + * glpapi14.c + New API routines glp_read_lp and glp_write_lp were added. They + replace API routines lpx_read_cpxlp and lpx_write_cpxlp, which + are deprecated. + + * glpsql.c + Minor bug was fixed. Thanks to Xypron for + the bug report. + +Tue Mar 25 12:00:00 2008 Andrew Makhorin + + * GLPK 4.28 (13:0:13) has been released + + * glplib.h, glplib.c + Three wrapper routines xdlopen, xdlsym, and xdlclose, which + provide the shared library support, were added. A particular + version of these routines depends on the option --enable-dl + passed to the configure script (see file INSTALL for details). + Thanks to Rafael Laboissiere for useful + advices concerning the shared library support. + + * glpsql.c + A static linking to iODBC and MySQL libraries used in the + MathProg table drivers was replaced by a dynamic linking to + corresponding shared libraries. + Many thanks to Heinrich Schuchardt + for the contribution and to Vijay Patil + for testing this feature under Windows XP. + + * glpgmp.h, glpgmp.c + A bug (which appeared only on 64-bit platforms) was fixed. + Thanks to Axel Simon for the bug report. + + * glpapi.c + A bug was fixed in the api routine glp_add_cols. (If the basis + is valid, adding column keeps it valid, however, col->bind was + set to -1 rather to 0.) + Thanks to Cedric[FR] for the bug report. + + * glplib.c + 64-bit unsigned int type glp_ulong and corresponding routines + were replaced by 64-bit signed int type xlong_t. + + * glpk.h, glpapi.c + The type glp_ulong was replaced by glp_long. This affects only + the api routine glp_mem_usage. + + * glplib.c + Compressed data file support was added. This feature requires + the zlib data compression libraries and allows compressing and + decompressing .gz files "on the fly". + + * glpcli.h, glpcli.c + Command-line interface routines were added. (This feature is + incomplete so far.) + +Sun Mar 02 12:00:00 2008 Andrew Makhorin + + * GLPK 4.27 (12:0:12) has been released + + * glpsql.h, glpsql.c + Two MathProg table drivers for iODBC and MySQL contributed by + Heinrich Schuchardt were added to + the package. + + * glpmpl05.c + Mathprog table driver for xBASE was added to the package. + + * glpmpl03.c + A minor was fixed in the MathProg translator (if some field + specified in the table statement is missing in corresponding + input table, the bug causes abnormal termination). Thanks to + Heinrich Schuchardt for the bug + report. + + * glpmpl.h, glpmpl.c + STRING data type was replaced by plain character strings. + +Sun Feb 17 12:00:00 2008 Andrew Makhorin + + * GLPK 4.26 (11:0:11) has been released + + * glpmpl.h, glpmpl01.c, glpmpl03.c, glpmpl05.c + The table statement was implemented. Description of this new + feature is given in file doc/tables.txt. + + * glpios03.c + A bug causing zero divide error on computing euclidean norm of + the cut coefficient vector was fixed. + +Wed Dec 19 12:00:00 2007 Andrew Makhorin + + * GLPK 4.25 (10:0:10) has been released + + * glpapi10.c + Routines lpx_eval_tab_row and lpx_eval_tab_col were replaced by + glp_eval_tab_row and glp_eval_tab_col. + + * glpios03.c, glpios05.c + Gomory's mixed integer cuts were implemented. + + * glpscs.h, glpscs.c + Segmented character string routines are no longer used and were + removed from the package. + +Wed Nov 21 12:00:00 2007 Andrew Makhorin + + * GLPK 4.24 (9:0:9) has been released + + * src/glplpx16.c + A bug was fixed in the routine lpx_write_cpxlp. If a variable + x has upper bound and no lower bound, it should appear in the + bounds section as "-inf <= x <= u", not as "x <= u". Thanks to + Enric Rodriguez for the bug report. + + * src/glpios03.c, src/glpios04.c, src/glpios05.c + MIR (mixed integer rounding) cuts were implemented. + +Sun Oct 28 12:00:00 2007 Andrew Makhorin + + * GLPK 4.23 (8:0:8) has been released + + * src/glplib05.c, configure.ac + Check for vsnprintf was added. + + * include/glppds.h, src/glppds.c + A module to scan plain data was added. + + * src/glpapi09.c + The following new API routines were added: + glp_read_sol read basic solution from text file; + glp_write_sol write basic solution to text file; + glp_read_ipt read interior-point solution from text file; + glp_write_ipt write interior-point solution to text file; + glp_read_mip read MIP solution from text file; + glp_write_mip write MIP solution to text file. + + * src/glpapi12.c + Advanced API routine glp_free_env was added. + + * examples/glpsol.c + The following three command-line options were added: + --mipgap tol set relative MIP gap tolerance + -r filename read solution from filename + -w filename write solution to filename + +Wed Sep 19 12:00:00 2007 Andrew Makhorin + + * GLPK 4.22 (7:0:7) has been released + + * src/glpios02.c + A bug was fixed in the MIP preprocessor (ios_preprocess_node). + Thanks to Roberto Bagnara (Department of + Mathematics, University of Parma, Italy) for the bug report. + + * src/glpios02.c + A bug was fixed in the MIP preprocessor (col_implied_bounds), + due to which constraint coefficients with small magnitude could + lead to wrong implied bounds of structural variables. + + * src/glpipp02.c + A similar bug was fixed in the routine reduce_bounds. + + * src/glpapi01.c + A bug was fixed in the routines glp_set_mat_row and + glp_set_mat_col. (The bug appeared due to incorrect removing + zero elements from the row/column lists.) + + * src/glplpx14.c + A bug was fixed in the API routines lpx_read_mps and + lpx_read_freemps, due to which bounds of type LI specified in + BOUNDS section were incorrectly processed. + + * src/glplib05.c + A call to standard function vsprintf was replaced by a call to + vsnprintf for security reasons. Many thanks to Peter T. Breuer + and Rafael Laboissiere . + +Tue Aug 28 12:00:00 2007 Andrew Makhorin + + * GLPK 4.21 (6:0:6) has been released + + * glpscg.h, glpscg.c + Routines to maintain sparse cliqued graph were added. + + * glpios02.c + MIP preprocessing routines were added. + + * glpk.h, glpios.h, glpios03.c + New reasons for calling the callback routine were introduced + in the MIP solver. + + * glpapi08.c + Default backtracking strategy was changed to best local bound. + + * glpapi11.c + New API routine glp_term_out to enable/disable terminal output + was added. + + * glprng.h, glprng02.c + Two routines to generate uniformly distributed pseudo-random + floating-point numbers were added. + +Thu Jul 26 12:00:00 2007 Andrew Makhorin + + * GLPK 4.20 (5:0:5) has been released + + * glpk.h, glpapi08.c + The routine lpx_integer was replaced by an equivalent routine + glp_intopt. Also new API routine glp_init_iocp was added. + + * glpiet.h, glpiet.c + Routines implementing the implicit enumeration tree are + no longer used and therefore were removed from the package. + + * glpios.h, glpios01.c, glpios02, glpios03 + Routines implementing the integer optimization suite being + replaced by a new version were removed from the package. + + * glpmip.h, glpmip01.c, glpmip02.c + + Routines implementing the B&B method being replaced by a new + version were removed from the package. + + * glpios.h, glpios01.c, glpios02.c + + Routines implementing a new version of the integer optimization + suite (IOS) based on the B&B method were added to the package. + + * glpk.h, glpapi10.c + Branch-and-bound interface routines were added to the package. + + * examples/tspsol.c + The TSP solver based on old version of the integer optimization + suite is no more supported and was removed from the package. + + * glpipp02.c + An error in the routine reduce_bounds was fixed; thanks to + Graham Rockwell for the bug report. + + * glpk.latex + A new edition of the reference manual was included. + +Thu Jul 05 12:00:00 2007 Andrew Makhorin + + * GLPK 4.19 (4:0:4) has been released + + The principal change is upgrading to GPLv3. + + * glpapi01.c + A serious bug in the routine glp_del_cols was fixed; thanks to + Cedric[FR] for the bug report. The bug + appeared because on deleting non-basic columns the basis header + remained valid, however, contained invalid (old) column ordinal + numbers. + + * glpapi10.c + A new advanced API routine glp_mem_limit was added. + + * glplpx01.c + The case GLP_EBOUND was added to the routine lpx_simplex. + Thanks to Cameron Kellough for the + bug report. + + * glplpx19.c + An API routine lpx_write_pb to write the problem instance in + OPB (pseudo boolean) format format was added. Thanks to Oscar + Gustafsson for the contribution. + + * glpsol.c + Two new options --wpb and --wnpb were added to glpsol to write + the problem instance in OPB format. + +Mon Jun 25 12:00:00 2007 Andrew Makhorin + + * GLPK 4.18 (3:0:3) has been released + + * glplib.h + Type names ulong_t and uldiv_t were changed to glp_ulong and + glp_uldiv to avoid conflicts with standard type names on some + platforms. Thanks to Boris Wirtz + for the bug report. + + * glpbfd.*, glpfhv.*, glplpf.* + LP basis factorization routines were made tidy. + + * glpk.h, glpapi04.c + The following API routines were added: + glp_set_rii, glp_set_sjj, glp_get_rii, glp_get_sjj. + + * glpk.h, glpapi06.c + The routine lpx_simplex was replaced by an equivalent routine + glp_simplex. Also new API routine glp_init_smcp was added. + + * glpk.h, glpapi09.c + The following advanced API routines were added: + glp_bf_exists, glp_factorize, glp_bf_updated, glp_get_bfcp, + glp_set_bfcp, glp_get_bhead, glp_get_row_bind, glp_get_col_bind, + glp_ftran, glp_btran. + + * glpk.latex + A new edition of the reference manual was included. + + * examples/dea.mod, examples/food.mod, examples/food2.mod + Three examples in the MathProg language were added. + Thanks to Sebastian Nowozin . + +Sat May 26 12:00:00 2007 Andrew Makhorin + + * GLPK 4.17 (2:0:2) has been released + + * glpdmp.h, glpdmp.c + Memory pool routines were replaced by a new version. + + * glpscs.h, glpscs.c + Segmented string routines were replaced by a new version. + + * glplpx08.c, glplpx09.c + Now the MIP problem may have no integer columns. + + * glpapi01.c + The routines glp_set_mat_row, glp_set_mat_col, and glp_load_mat + were modified to allow zero elements (which are not stored in + the constraint matrix). + + * glpscf.h, glpscf.c + Schur complement factorization routines were implemented. + + * glplpf.h, glplpf.c + LP basis factorization routines based on LU-factorization and + Schur complement were implemented. + + * glplpx02.c, glplpx03.c + New control parameter LPX_K_BFTYPE was introduced to choose the + basis factorization type used by the simplex method routines. + + * glpsol.c + Three new command-line options were added to choose the basis + factorization type used by the simplex method routines: --luf, + --cbg, and --cgr. + + * glpk.latex + A new edition of the reference manual was included. + +Sat May 05 12:00:00 2007 Andrew Makhorin + + * GLPK 4.16 (1:0:1) has been released + + * glpk.h, glpapi.c, glplpx01.c, glplpx02.c + Names of a number basic api routines were changed and now have + the prefix 'glp_'. To keep backward compatibility these routines + are also available via their old names prefixed with 'lpx_'. + + * glplpx19.c + Three new api routines were added: glp_version, glp_term_hook, + and glp_mem_usage. + + * glpk.latex, gmpl.texi + A new edition of the reference manuals was included. + + * lpglpk40.c + This example program is no longer supported and therefore was + removed from the package. + +Sun Feb 18 12:00:00 2007 Andrew Makhorin + + * GLPK 4.15 (0:0:0) has been released + + * configure.ac, Makefile.am + Autotools specification files were changed to use GNU Libtool + that allows building the static as well as shared GLPK library. + Thanks to Rafael Laboissiere . + +Mon Feb 05 08:00:00 2007 Andrew Makhorin + + * GLPK 4.14 has been released + Now GLPK conforms to ILP32, LLP64, and LP64 programming models + (the latter seems to be the ultimate choice regarding 64-bit + architectures). Note that GLPK itself is a 32-bit application, + and the conformity only means that the package works correctly + on all these arenae. Nevertheless, on 64-bit platforms it is + possible to use more than 4GB of memory, if necessary. + + * Makefile + Starting from this release only the header glpk.h is needed to + be installed. + + * glplib01.c + Two routines bigmul and bigdiv which performs multiplication + and division of unsigned integers of arbitrary precision were + added. + + * glplib02.c + A set of 64-bit arithmetic routines were added. + + * glplib04.c + Some low-level library routines were improved and renamed. + + * glpcfg.h + The macro GLP_TM_SPEC were introduced to specify a version of + the time routine depending on the host environment. + +Mon Nov 13 12:00:00 2006 Andrew Makhorin + + * GLPK 4.13 has been released + + * configure.in + '-lm' bug was fixed. + + * glpbfx.h, glpbfx.c + Basis factorization interface routines based on exact (bignum) + arithmetic were implemented. + + * glpssx.h, glpssx1.c, glpssx2.c + Simplex method routines based on exact (bignum) arithmetic were + implemented. + + * glplpx6e.c + The routine lpx_exact, which is an easy-to-use driver to the + exact simplex method, was added. + + * glpsol.c + Two command-line options were added: '--exact' and '--xcheck'. + +Wed Nov 08 12:00:00 2006 Andrew Makhorin + + * GLPK 4.12 has been released + + * glpcfg.h + The package configuration file was added. + + * glplib2.c + Alternative version of the routines umalloc, ucalloc, and ufree + was provided. It does not limit the amount of allocated memory + to INT_MAX bytes and therefore can be used on platforms where + sizeof(void *) > sizeof(int). To enable this version one should + define the preprocessor variable GLP_HUGE_MEM. + + * glprng.c + The routine rng_create_rand was changed to initialize the + generator using seed = 1, not 0, to conform ISO C requirements. + + * glpgmp.h, glpgmp.c + A set of bignum arithmetic routines implementing operations on + integers and rationals was added. These routines are compatible + with the GNU MP library. + + NOTE: To attain a much better performance it is recommended to + use, if possible, the original GNU MP library rather than the + GLPK version, by defining the preprocessor variable GLP_USE_GMP. + + * glplux.h, glplux.c + A tentative implementation of sparse LU-factorization based on + exact (bignum) arithmetic was added. + + * glpssx.h, glpssx.c + A tentative implementation of some simplex method routines based + on exact (bignum) arithmetic was added. + + * glplpx6f.c + A preliminary implementation of the routine lpx_exact_check was + added. This routine checks the current basis for primal and dual + feasibility using exact (bignum) arithmetic. + + * examples/glpsol.c + The command-line option '--xcheck' was introduced to check the + current basis for feasibility using exact (bignum) arithmetic. + +Tue Jul 25 12:00:00 2006 Andrew Makhorin + + * GLPK 4.11 has been released. + + * include/glpbfi.h, src/glpbfi.c + Basis factorization interface routines were added. + + * include/glpluf.h, src/glpluf1.c + Hypersparse solution routines were added. + + * include/glpinv.h, src/glpinv1.c + Hypersparse solution routines (fake version) were added. + + * include/glpmpl.h, src/glpmpl.c + Built-in functions card, length, and substr were implemented. + Output redirection in the printf statement was implemented. + + * examples/graph.mod, examples/crypto.mod + Two example models illustrating new features of the modeling + language were included. + +Thu May 11 12:00:00 2006 Andrew Makhorin + + * GLPK 4.10 has been released. + + * src/glplpx8a.c + A fragment was added to the routines lpx_read_mps and + lpx_read_freemps to accept LI bound type (it is similar to LO, + however, additionally marks the column as integer). + + * include/glpbfi.h, src/glpbfi.c + The module glpbfi which implements the basis factorization + interface (BFI) was added. + + * src/glplpx7a.c + The routine lpx_cover_cut to generate mixed cover cuts was + added. + + * src/glplpx7b.c + The routine lpx_clique_cut to generate clique cuts and related + routines to maintain the conflict graph were added. + + * include/glplpx.h, src/glplpx5.c + The routine lpx_cpx_basis implementing Bixby's algorithm to + construct an initial LP basis was added. + + * examples/glpsol.c + Command-line option '--bib' was added which allows building + an initial LP basis using Bixby's algorithm. + Default command-line option '--mps' was changed to '--freemps'. + + * examples/cf12a.mod, examples/cf12b.mod + Two examples in MathProg (curve fitting problem) were added. + Thanks to Dr. Harley Mackenzie . + +Tue Jan 17 12:00:00 2006 Andrew Makhorin + + * GLPK 4.9 has been released. + + * glpipp.h, glpipp1.c, glpipp2.c + A MIP presolver were implemented (currently incomplete). It is + used internally in the routine lpx_intopt (see below). + + * glplpx6d.c, glplpx7a.c + An advanced branch-and-bound solver (the routine lpx_intopt) + were implemented. + + * glplpx6c.c + The routine lpx_check_int to check MIP feasibility conditions + was added. + + * glplpx8a.c + The routine lpx_print_mip was changed to print MIP feasibility + conditions. + + * glpmpl.h, glpmpl1.c, glpmpl3.c + The built-in functions sin, cos, atan, and atan2 were added to + the MathProg language. + + * doc/lang.* + Some typos were fixed. + Thanks to Minh Ha Duong (CIRED, CNRS). + +Wed Jan 12 12:00:00 2005 Andrew Makhorin + + * GLPK 4.8 has been released. + + * glpspx.h, glpspx1.c, glpspx2.c, glplpx6a.c + Simplex method routines were changed due to a new format of the + constraint matrix. + + * glpmat.h, glpmat.c + Sparse matrix routines were re-implemented using storage-by-rows + format. + + * glpipm.h, glpipm.c, glplpx6b.c + Interior-point method routines were changed due to a new format + of sparse matrices. + + * glpchol.h, glpchol.c + Old version of Cholesky factorization routines being replaced by + a new one (see glpmat.c) was removed from the package. + + * glplpx8c.c + Minor bug was fixed in api routine lpx_read_cpxlp. + +Mon Aug 23 12:00:00 2004 Andrew Makhorin + + * GLPK 4.7 has been released. + + * glplpx.h, glplpx1.c + New core API routines were added (but not documented yet): + lpx_order_matrix, lpx_create_index, lpx_find_row, lpx_find_col, + lpx_delete_index. + + * glplpx8a.c + API routine lpx_read_mps was re-implemented, and two new API + routines lpx_read_freemps and lpx_write_freemps were added to + support free MPS format. + + * glplpx8c.c + Two API routines lpx_read_cpxlp and lpx_write_cpxlp (formerly + named lpx_read_lpt and lpx_write_lpt) were re-implemented. + + * glpmps.h, glpmps.c + This module formerly used in lpx_read_mps was removed from the + package. + + * glplpt.h, glplpt.c + This module formerly used in lpx_read_lpt was removed from the + package. + + * glpmip.h, glpmip1.h, glpmip2.h + New MIP routines mip_best_node and mip_relative_gap were added + due to suggestion of Brady Hunsaker . + + * glpsol.c + The following new command-options were added: + --freemps to read problem data in free MPS format + --wfreemps to write problem data in free MPS format + --cpxlp to read problem data in CPLEX LP format + --wcpxlp to write problem data in CPLEX LP format + --bas to read LP basis from a text file in MPS format + --wbas to write LP basis to a text file in MPS format + --mostf to use "most fractional" branching heuristic + --bestb to use "best bound" backtracking heuristic + + * contrib/deli/*.* + GLPK Delphi interface module was temporarily removed from the + distribution due to licensing problems. + + * contrib/glpkmex/*.* + GLPK Matlab interface module was temporarily removed from the + distribution due to licensing problems. + + * contrib/jni/*.* + GLPK Java interface module was temporarily removed from the + distribution due to licensing problems. + +Wed Aug 04 12:00:00 2004 Andrew Makhorin + + * GLPK 4.6 has been released. + + * glpmpl.h, glpmpl1.c, glpmpl2.c, glpmpl3.c, glpmpl4.c + Three new statements were implemented in the GNU MathProg + language: solve, printf, and for. Also some bugs were fixed. + + * glplpx.h, glplpx8e.c + Two API routines were added: lpx_read_prob and lpx_write_prob, + which allow reading and writing problem data in GNU LP format. + + * glpsol.c + Three new command-line options were added: --glp (to read + problem data in GNU LP format), --wglp (to write problem data + in GNU LP format), and --name (to change problem name). + + * glprng.h, glprng.c + A portable pseudo-random number generator was implemented as a + separate module. + + * glplib4.c + The old implementation of a pseudo-random number generator was + removed from the package. + + * doc/lang.*, doc/refman.* + New edition of the GLPK documentation was included. + + * contrib/glpkmex/*.* + A new version of GLPKMEX was included in the distribution. For + more details see contrib/glpkmex/ChangeLog. + +Mon Jul 19 12:00:00 2004 Andrew Makhorin + + * GLPK 4.5 has been released. + + * glpmip.h, glpmip1.c, glpmip2.c, glplpx6c.c + New implementation of the branch-and-bound method was added. + It replaces the old implementation, which was removed from the + package. + + * glpies.h, glpies1.c, glpies2.c, glpies3.c + Modules used in the old implementation of the branch-and-bound + method were removed from the package. + + * glplib2.c + Now if the preprocessor variable GLPHUGEMEM is defined, other + version of the routines umalloc, ucalloc, and ufree is used on + compiling the package. This allows avoiding memory allocation + problems on platforms where sizeof(void *) > sizeof(int), for + example, where addresses are 64-bit while integers are 32-bit. + The modification was made due to a bug report provided by Karel + Zimmermann and Christophe Caron + . + +Sat Jan 17 12:00:00 2004 Andrew Makhorin + + * GLPK 4.4 has been released. + + * glplpx.h, glplpx*.c + All API routines were re-implemented using new data structures. + Some new API routines were added and some existing API routines + became obsolete as shown below: + + Obsolete API routine Equivalent new API routine + lpx_check_name (no more supported) + lpx_set_obj_c0 lpx_set_obj_coef + lpx_set_row_coef (no more supported) + lpx_set_col_coef lpx_set_obj_coef + lpx_load_mat (no more supported) + lpx_load_mat3 lpx_load_matrix + lpx_unmark_all (no more supported) + lpx_mark_row (no more supported) + lpx_mark_col (no more supported) + lpx_clear_mat (no more supported) + lpx_del_items lpx_del_rows, lpx_del_cols + lpx_get_row_bnds lpx_get_row_type, lpx_get_row_lb, + lpx_get_row_ub + lpx_get_col_bnds lpx_get_col_type, lpx_get_col_lb, + lpx_get_col_ub + lpx_get_obj_c0 lpx_get_obj_coef + lpx_get_row_coef (no more supported) + lpx_get_col_coef lpx_get_obj_coef + lpx_get_row_mark (no more supported) + lpx_get_col_mark (no more supported) + lpx_get_row_info lpx_get_row_stat, lpx_get_row_prim, + lpx_get_row_dual + lpx_get_col_info lpx_get_col_stat, lpx_get_col_prim, + lpx_get_col_dual + lpx_get_ips_stat lpx_ipt_status + lpx_get_ips_row lpx_ipt_row_prim, lpx_ipt_row_dual + lpx_get_ips_col lpx_ipt_col_prim, lpx_ipt_col_dual + lpx_get_ips_obj lpx_ipt_obj_val + lpx_get_mip_stat lpx_mip_status + lpx_get_mip_row lpx_mip_row_val + lpx_get_mip_col lpx_mip_col_val + lpx_get_mip_obj lpx_mip_obj_val + + Obsolete API routines were kept for backward compatibility, + however, they will be removed in the future. + + * doc/refman.* + New edition of the GLPK reference manual containing description + of all new API routines was included. + + * contrib/glpkmex/*.* + GLPKMEX, a Matlab MEX interface to GLPK package, contributed by + Nicolo Giorgetti was included. + + * doc/GLPK_FAQ.txt + GLPK FAQ contributed by Harley Mackenzie was + included. + +Fri Dec 12 12:00:00 2003 Andrew Makhorin + + * GLPK 4.3 has been released. + + * configure.in + The bug, due to which the standard math library is not linked on + some platforms, was fixed. + + * glpmpl3.c + The bug (0 ** y) was fixed in the routine fp_power. + + * glpmpl.h, glpmpl1.c, glpmpl3.c + Some new built-in functions (round, trunc, Irand224, Uniform01, + Uniform, Normal01, Normal) were added to the MathProg language. + + * glpmpl1.c + The MathProg syntax was changed to allow writing 'subj to'. + + * glplpx.h, glplpx1.c, glplpx2.c + The new api routine lpx_get_ray_info was added. + + * glplpx8a.c + The api routine lpx_print_sol was changed to print the number of + non-basic variable, which causes primal unboundness. + + * glpmps.c + The code was changed to avoid errors on compiling the package on + Mac OS X. Thanks to Andre Girard for + the bug report. + + * doc/lang.*, doc/refman.* + Several typos were fixed and some new material was added in the + glpk documentation. + +Fri Nov 14 12:00:00 2003 Andrew Makhorin + + * GLPK 4.2 has been released. + + * glpiet.h, glpiet.c, glpios.h, glpios1.c, glpios2.c, glpios3.c + A preliminary implementation of the Integer Optimization Suite + (IOS) was included in the package. Eventually IOS will replace + the Implicit Enumeration Suite (IES). + + * glplpx.h, glplpx6d.c + A dummy version of the integer optimization routine lpx_intopt + was included in the package. Later this routine will replace the + routine lpx_integer. + + * examples/glpsol.c + A new command-line option --int-opt was added to the solver to + call lpx_intopt rather than lpx_integer. + + * glpbcs.h, glpbcs1.c, glpbcs2.c + Being replaced by IOS routines (see above) the Branch-and-Cut + Framework (BCS) routines were removed from the package. + + * examples/tspsol.c + Stand-alone Symmetric TSP solver was completely re-programmed + using IOS routines. + + * glplib.h, glplib2.c, glplib4.c + The random-number generator was implemented. It is based on the + module GB_FLIB from the Stanford GraphBase originally developed + by Donald Knuth. + + * glphbsm.c, glplpx8a.c, glpmps.c + All calls to fopen/fclose were replaced by corresponding calls + to ufopen/ufclose due to bug reports provided by Morten Welinder + and . + + * glpmps.c + The code was made re-entrant. + + * glplpx8b.c + API routine lpx_print_sens_bnds for bounds sensitivity analysis + contributed by Brady Hunsaker was added + to the package. This feature is also available in glpsol via the + command-line option --bounds. + + * contrib/jni/*.* + New version of GLPK JNI (Java Native Interface) contributed by + Chris Rosebrugh was added to the package. + + * contrib/deli/*.* + GLPK DELI (Delphi Interface) contributed by Ivo van Baren + was added to the package. + + * glplpx3.c + Default method to scale the problem was changed to equilibration + scaling (lp->scale = 1 in lpx_reset_parms). + + * glplpx6a.c + Two minor (non-critical) typos were fixed due to report provided + by Andrew Hamilton-Wright . + + * glplpp2.c + An untested case (line 941) had been tested due to bug report + provided by Jiri Spitz . + + * w32bc5.mak, w32vc6.mak, w32vc6d.mak, d32dmc.mak + Several makefiles were added to allow building GLPK library for + some non-GNU 32-bit platforms. + +Sat Aug 23 12:00:00 2003 Andrew Makhorin + + * GLPK 4.1 has been released. + + * glpmpl1.c, glpmpl3.c + Some bugs were fixed in the MathProg translator due to the bug + reports provided by Giles Thompson : + conditional set expressions were incorrectly parsed; + dimen attribute was not set by default when a set was used + recursively in its own declaration; + logical expressions ... in if ... then ... else ... did not + work; + displaying set expressions did not free memory allocated for + temporary results. + + * glpmpl3.c (reduce_terms) + Implementation of summation of linear forms over domain was + improved to reduce complexity of that operation from O(n*n) to + O(n*log n). The improvement was made due to a report provided + by Sebastien de Menten . + + * glplpx6a.c (line 1056), glpmip1.c (line 641) + Two minor bugs were fixed due to the bug report provided by + Kendall Demaree . + + * glplpx.h, glplpx6a.c + The method of one artificial variable implemented in the routine + lpx_prim_art and used on the phase I in the glpk simplex solver + has a serious defect: for some lp instances it erroneously + reports that the problem has no primal feasible solution. This + error appears when the column of the artificial variable, which + enters the basis to make it primal feasible, has large + constraint coefficients, that leads to small reduced costs of + non-basic variables and premature termination of the search, + i.e. to wrong conclusion that the problem has no primal feasible + solution. To avoid this defect the routine lpx_prim_feas was + included. It implements the method of implicit artifical + variables (based on minimization of the sum of infeasibilities), + which is a bit slower but much more robust. The routine + lpx_prim_feas having the same functionality now is used instead + the routine lpx_prim_art. + + * glpinv.h, glpinv.c + The test used in the routine inv_update to detect low accuracy + after updating LU-factorization of the basis matrix was replaced + by a new, more robust test. + + * glplpx6c.c + Selecting an active node to be solved next in the routine + btrack_bestp was changed. Now, if any integer feasible solution + has not been found yet, the routine chooses an active node which + has the minimal sum of integer infeasibilities. + + * glpmip.h, glpmip1.c + The additional flag int_obj was included in the structure + MIPTREE used by the branch-and-bound. This flag is set in the + routine mip_create_tree and used in the routine is_better. It + means that the objective is integral, i.e. depends only on + integer variables with integer objective coefficients. The test + used in the routine check_integrality was also replaced by a + new, more reasonable one. + + * glplpx1.c + A minor bug was fixed in the routine lpx_check_name. + + * glpmpl.h, glpmpl4.c, glplpx8d.c + The flag skip_data was added to the parameter list of the + routine mpl_read_model. If this flag is set, the data section + in the model file is ignored. Corresponding change was made in + the routine lpx_read_model. Now, if both model and data files + are specified, the data section in the model file is ignored. + + * glplpx8c.c + A minor bug (wrong format used for writing free columns) in the + routine lpx_write_lpt was fixed due to the bug report provided + by Bernhard Schmidt + + * sample/glpsol.c + The command-line parameter --tmlim, which allows limiting the + solution time, was added. + + * doc/lang.*, doc/refman.* + New edition of the GLPK documentation was included. + + * java-binding/*.* + New version of the GLPK JNI (Java Native Interface) package was + included in the distribution. + + * sample/lpglpk40.c + A non-trivial example was added. It allows using GLPK as a base + LP solver for Concorde, a program for solving Traveling Salesman + Problem (TSP). For details see comments in lpglpk40.c. + + * sample/*.mod + Some examples of LP and MIP models written in GNU MathProg were + added. + +Tue May 06 12:00:00 2003 Andrew Makhorin + + * GLPK 4.0 has been released. + + * glpmpl.h, glpmpl1.c, glpmpl2.c, glpmpl3.c, glpmpl4.c + The model translator for the GNU MathProg modeling language was + implemented and included in the package. + + * glplpx.h, glplpx8d.c + The api routine lpx_read_model, which is an interface to the + MathProg translator, was included in the package. + + * glplpx.h, glplpx8a.c + The api routine lpx_print_prob for writing LP/MIP problem data + in plain text format was included in the package. + + * sample/glpsol.c + New version of the GLPK stand-alone LP/MIP solver that supports + the GNU MathProg modeling language was implemented. + + * doc/lang.latex, doc/lang.dvi, doc/lang.ps + The document "GLPK: Modeling Language GNU MathProg" was included + in the package. + + * doc/refman.latex, doc/refman.dvi, doc/refman.ps + New edition of the GLPK Reference Manual was included in the + package. + + * glplpx8c.c + A bug in the api routine lpx_write_lpt was fixed. Due to that + bug an addressing error occured in the routine if the objective + function has the non-zero constant term. + + * glplan.h, glplan1.c, glplan2.c, glplan3.c, glplan4.c, + * glplan5.c, glplan6.c, glplan7.c, glplan8.c, glplpx8b.c + All modules of the translator for the GLPK/L modeling language + were removed from the package, because GLPK/L being completely + superseded by GNU MathProg is no more supported. + +Tue Mar 25 12:00:00 2003 Andrew Makhorin + + * GLPK 3.3 has been released. + + * glplpp.h, glplpp1.c, glplpp2.c + An implementation of the built-in LP presolver was added to the + package. + + * glplpx.h + The flag presol was added to the structure LPX. This flag tells + the lpx_simplex whether the built-in LP presolver should be used + or not. By default this flag is off. Also three macros (namely + LPX_E_NOPFS, LPX_E_NODFS, and LPX_K_PRESOL) that concern using + the LP presolver were introduced. + + * glplpx3.c, glplpx6a.c + These modules was changed to use the built-in LP presolver. + + * sample/glpsol.c + Command line options --presol and --nopresol that concern using + the LP presolver were added to the stand-alone LP/MIP solver. + + * glplan1.c + This module was changed to allow declaring sets like A[1:10] in + the models written in the GLPK/L modeling language. + + * doc/refman.latex, doc/lang.latex + New editions of the documents "GLPK User's Guide" and "GLPK/L + Modeling Language" were included in the distribution. + + * java-binding/*.* + The package GLPK JNI (Java Native Interface) implementing Java + binding for GLPK was included in the distribution. This package + was developed and programmed by Yuri Victorovich . + +Tue Feb 18 12:00:00 2003 Andrew Makhorin + + * GLPK 3.2.4 has been released. + + * glplpx6b.c + The code was changed to allow auxiliary variables have non-zero + objective coefficients. + + Also a minor bug was fixed (the constant term was not considered + on displaying the objective function value). + + * sample/glpsol.c + The code was changed to fix a bug (the command-line option 'bfs' + was not recognized). The bug was fixed due to report provided by + Olivier . + + * glplpt.c + The code was changed to fix a bug (binary variables were treated + erroneously as integer ones). + + * glplpx6b.c + The code was changed to fix a bug (variables that have no lower + bounds were incorrectly processed on converting to the standard + formulation). The bug was fixed due to report kindly provided by + Kjell Eikland . + +Mon Nov 11 12:00:00 2002 Andrew Makhorin + + * GLPK 3.2.3 has been released. + + * glpmip.h, glpmip1.c + A preliminary implementation of the branch-and-bound driver + based on the implicit enumeration suite (glpies) was added to + the package. This module is not documented yet. + + * glplpx6c.c + A new implementation of the api routine lpx_integer which now + is based on the b&b driver (see glpmip above) was included in + the package. This new implementation has exactly the same + functionality as the old version and therefore all changes are + transparent to the api user. + + * glpbbm.h, glpbbm.c + * glprsm.h, glprsm1.c, glprsm2.c + * glplp.h, glplp.c + These modules were removed from the package, because they were + used only in the old version of the routine lpx_integer, which + was replaced by the new version (see glplpx6c above). + + * glplpx.h, glplpx6a.c + The api routine lpx_check_kkt was included in the package and + its description was added in the reference manual. This routine + allows checking Karush-Kuhn-Tucker optimality conditions for an + LP solution. + + * glplpx.h, glplpx8a.c + Now the api routine lpx_print_sol also prints information about + "solution quality" obtained via the api routine lpx_check_kkt. + + * glplpx.h, glplpx8a.c + New api routines lpx_read_bas and lpx_write_bas were included + in the package and documented. The routine lpx_write_bas allows + writing a current basis from an LP object to a text file in the + MPS format. The routine lpx_read_bas allows reading a basis + prepared in the MPS format from a text file into an LP object. + + * glplpt.c + The parsing routine which reads LP problem data prepared in the + CPLEX LP format was modified to allow specifying lower bounds + of variables also in the form 'variable >= lower bound' (in the + bounds section). This modification was made due to a notice + provided by Ivan Luzzi . + + * glplpx.h, glplpx8c.c + The api routine lpx_write_lpt which allows writing LP problem + data from an LP object to a text file using the CPLEX LP format + was included in the package and documented. + + * glplpx.h, glplpx3.c + The control parameter LPX_K_LPTORIG that affects the behavior + of the api routine lpx_write_lpt was introduced. + + * glplan6.c + The semantics of the language GLPK/L was changed to allow + selection in case when not all mute letters of a predicate (the + operand that follows the keyword 'where') are presented in a + parameter (the operand that precedes the keyword 'where'), i.e. + to allow writing something like this: + y[j] := sum(i, x[i] where p[i,j]); + The paragraph "Selection" in the langauge description (page 25) + was also correspondingly changed. This change of the language + semantics was undertaken due to a notice provided by Peter Lee + . + + * sample/hwd.lpm + A nice example of LP model written in GLPK/L and contributed by + Peter Lee was included in the package. + + * glplpx6b.c + The api routine lpx_interior was modified: a) to compute dual + values for all structural as well as auxiliary variables; b) to + allow specifying non-zero objective coefficients at auxiliary + variables. + + * sample/glpsol.c + Three new command-line options were added to the solver, which + are: --plain, --orig, and --wrlpt. + +Mon Oct 14 12:00:00 2002 Andrew Makhorin + + * GLPK 3.2.2 has been released. + + * glplpt.h, glplpt.c + A module that reads LP/MIP problem data in CPLEX LP format was + implemented. + + * glplpx8c.c + An api routine lpx_read_lpt that reads LP/MIP problem data in + CPLEX LP format was implemented. + + * sample/glpsol.c, sample/plan.lpt + A new command-line option '--lpt' that allows reading LP/MIP + problem data in CPLEX LP format was added to the solver. + + * doc/refman.latex, doc/refman.dvi, doc/refman.ps + A new edition of the Reference Manual was included. + + * source/*.c + Casting to (unsigned char) was added in some calls to the + classification functions (isalpha, etc.). The bug was fixed due + to report provided by Morten Welinder . + + * glplpx8a.c + The local routine mps_numb used in the routine lpx_write_mps + was modified to correctly format floating-point numbers that + have two digits in the decimal exponent. The bug was fixed due + to report provided by Vlahos Kiriakos . + + * glplan.h, glplan1.c, ..., glplan8.c + Several serious bugs were fixed in the language processor due + to reports provided by : + (a) a static search tree used to find sparse array elements was + sometimes overwritten that caused the message 'assertion failed' + to appear; the bug was fixed by creating separate search trees + in parsing routines; (b) a variable declared using the + predicate-controlled variable declaration statement had wrong + order of domain sets, because the variable array was built as + a copy of the predicate array; the bug was fixed by using the + internal routine transpose that coordinates mute letters (and + therefore domain sets) on copying sparse arrays; (c) sometimes + assignment statements like x[#a,#b,#c] := ... was incorrectly + processed; the bug was fixed by including an appropriate check + into the internal routine assign_stmt. + + * glp_simplex.c + An additional check to see if all lower bounds are not greater + than corresponding upper bounds was included in the routine to + prevent wrong results to appear. Such incorrectness sometimes + was not detected, namely, when variables with such bounds were + non-basic and never entered the basis. + + * glpspx1.c + Maximal number of simplex iterations before reinversion was + decreased from 100 to 50. This allowed to improve accuracy and, + that is more important, to reduce the solution time for many + serial lp problems approximately 1.5--2 times. + + * glpspx2.c + A check to see if all elements in the column chosen to enter + the basis are close to zero in the routine spx_prim_chuzr was + temporarily removed because this check gave wrong conclusion in + case when the corresponding non-basic variable had zero column + in the constraint matrix. An analogous check to see if all + elements in the row chosen to leave the basis are close to zero + in the routine spx_dual_chuzc was also temporarily removed on + the same reason. The bug was fixed due to reports provided by + Flavio Keidi Miyazawa and Vlahos Kiriakos + . + +Mon Aug 12 12:00:00 2002 Andrew Makhorin + + * GLPK 3.2.1 has been released. + + * glpbcs.h, glpbcs1.c, glpbcs2.c + * glpies.h, glpies1.c, glpies2.c, glpies3.c + A preliminary implementation of the branch-and-cut framework + was included in the package. + + * doc/brcut.txt + The document "GLPK: A Preliminary Implementation of the + Branch-And-Cut Framework" was included in the distribution. + + * sample/tspsol.c + An illustrative program for solving symmetric TSP based on the + branch-and-cut method was included in the package. + + * glpdmp.h, glpdmp.c + A new, re-enterable version of routines for managing dynamic + memory pools was included in the package. + + * glpavl.h, glpavl.c + A new, re-enterable version of routines for managing AVL search + trees was included in the package. + + * glplib.h, glplib2.c + Two new low-level routines ufopen and ufclose were included in + the package. + + * glplpx.h, glplpx7.c + The following new api routines were added: lpx_eval_activity, + lpx_eval_red_cost, lpx_reduce_form, lpx_mixed_gomory. + + * glptsp.h, glptsp.c + A module for reading TSP data using TSPLIB format was included + in the package. + +Mon Jul 15 12:00:00 2002 Andrew Makhorin + + * GLPK 3.2 has been released. + + * glplpx.h, glplpx1.c, glplpx2.c + The identifier 'class' (used as a member name in the structure + LPX and as an argument name in the routine lpx_set_class) was + changed to 'clss' in order to avoid conflicts with C++ reserved + words. + + * glpk.h, glplpx.h, glplpx1.c, glplpx2.c, glplpx6a.c, + * glplpx6b.c, glplpx6c.c, glplpx7.c, glplpx8.c + The following new api routines were added: lpx_set_obj_name, + lpx_get_obj_name, lpx_get_row_mark, lpx_get_col_mark, + lpx_transform_row, lpx_transform_col, lpx_prim_ratio_test, + lpx_dual_ratio_test, lpx_interior, lpx_get_ips_stat, + lpx_get_ips_row, lpx_get_ips_col, lpx_get_ips_obj, lpx_read_lpm, + lpx_write_mps, lpx_print_ips. + + * glpsol.c + The solver was completely re-programmed using new api routines. + + * lang.latex, lang.dvi, lang.ps + New edition of the document "GLPK: Modeling Language GLPK/L" + was included in the distribution. + + * refman.latex, refman.dvi, refman.ps + New edition of the document "GLPK: Reference Manual" (which + contains descriptions of all new api routines) was included in + the distribution. + + * glpapi.h, glpapi1.c, glpapi2.c, glpapi3.c, glpapi4.c + These files (which contain old api routines) were removed from + the package. + + * glpipm1.c, glpipm2.c + The file glpipm1.c was renamed to glpipm.c. The file glpipm2.c + was used only by old api routines and therefore was removed from + the package. + + * language.texinfo + Old version of the document "GLPK: Modeling Language GLPK/L" was + removed from the distribution. + +Mon May 27 12:00:00 2002 Andrew Makhorin + + * GLPK 3.1 has been released. + + * glplpx.h, glplpx1.c, glplpx2.c, glplpx3.c, glplpx4.c, + * glplpx5.c, glplpx6.c, glplpx7.c, glplpx8.c + A preliminary implementation of new API routines was completed. + + * refman.latex, refman.dvi, refman.ps + A draft edition of the document "GLPK Reference Manual", which + describes new API routines, was included. + + * glplib3.c + A bug in measuring long time intervals was fixed up. + + * glprsm3.c + This module contains some obsolete routines not longer used and + therefore it was removed from the package (into the subdirectory + 'oldsrc'). + + * glprsm.h + Some declarations related to the module 'glprsm3.c' (see above) + were removed. + + * guide.texinfo + The document "GLPK User's Guide" describing old API routines was + removed from the package (into the subdirectory 'oldsrc'). + + * newapi.txt + The document "New GLPK API Routines" was removed at all, because + it is superseded by the new reference manual (see above). + +Mon May 13 12:00:00 2002 Andrew Makhorin + + * GLPK 3.0.8 has been released. + + * glplpx.h, glplpx1.c, glplpx2.c, glplpx3.c, glplpx4.c, + * glplpx5.c, glplpx6.c, glplpx7.c + A preliminary (currently incomplete) implementation of new api + routines was included. + + * sample/newsamp.c + A sample program for the new api routines was included. + + * newapi.txt + A draft of the document "New GLPK API Routines" was included. + + * glpapi2.c, glpapi5.c, glpapi6.c + These modules (which contain the api routines glp_call_rsm1, + glp_simplex1, glp_pivot_in, glp_pivot_out) were removed from the + package (to the subdirectory 'oldsrc') since these routines are + functionally superseded by the new api routines. + + * glpk.h, glpapi2.c, glpapi3.c, glpapi4.c + The api routines glp_simplex2, glp_call_ipm1, glp_call_bbm1 were + renamed to glp_simplex, glp_interior, glp_integer, respectively. + + * sample/glpsol.c + Some command-line options (which got obsolete due to the recent + changes in api) were excluded. + + * doc/guide.texinfo + New edition of the document "GLPK User's Guide" was included in + the distribution to reflect the changes in some api routines. + + * doc/libref.texinfo + This document was removed from the package (to the subdirectory + 'oldsrc') since it describes the library routines, most of which + got obsolete and no longer used. + + * Makefile.in + A minor bug was fixed up due to bug report from Hans Schwengeler + . + +Mon Apr 22 12:00:00 2002 Andrew Makhorin + + * GLPK 3.0.7 has been released. + + * glpduff.h, glpduff.c, glpspx.h, glpspx1.c, glpspx2.c, + * glpapi7.c + These modules were replaced by a new implementation of the + simplex method and therefore they were removed from the package + (however they still can be found in the subdirectory 'oldsrc'). + + * glprsm1.c + The routine crash_aa was replaced by a new implementation and + therefore it was removed from the file 'glprsm1.c'. + + * glplpx.h, glplpx.c, glpspx.h, glpspx1.c, glpspx2.c, glpspx3.c, + * glpspx4.c, glpapi7.c + New (currently incomplete) implementation of the simplex method + components was included in the package. + +Thu Mar 28 12:00:00 2002 Andrew Makhorin + + * GLPK 3.0.6 has been released. + + * glpluf.h, glpluf.c, glpinv.h, glpinv.c + New version of LU-factorization and basis maintenance routines + (based on Forrest-Tomlin updating technique) was implemented. + + * glpeta.h, glpeta.c, glpfhv.h, glpfhv.c, glpgel.h, glpgel.c, + * glppfi.h, glppfi.c, glprfi.h, glprfi.c + These routines implement some other forms of the basis matrix. + Now they became obsolete being functionally superseded by the + new version of basis maintenance routines (see above) and were + removed from the package (however they still can be found in the + subdirectory 'oldsrc'). + + * glpbbm.c, glprsm.h, glprsm1.h, glprsm2.h, glpspx.h, glpspx2.c, + * glprsm2.c, glpsol.c + Necessary changes were made in order to use the new version of + basis maintenance routines. + +Tue Jan 29 12:00:00 2002 Andrew Makhorin + + * GLPK 3.0.5 has been released. + Structure of the package was re-organized in order to simplify + its maintenance. + + * doc/guide.texinfo + New edition of the document "GLPK User's Guide" was included in + the distribution. Now the document includes descriptions of some + additional API routines recently added to the package. + + * doc/newapi.txt + The document "Additional GLPK API Routines" was removed from the + distribution, because the corresponding material was included in + the user's guide (see above). + +Mon Dec 10 12:00:00 2001 Andrew Makhorin + + * GLPK 3.0.4 has been released. + + * glpspx.h, glpspx1.c, glpspx2.c, glpapi/glp_simplex2.h + A new, more efficient version of the two-phase primal simplex + method was implemented (advanced initial basis, projected + steepest edge, recursive computations of solution components). + + * glpapi/glp_call_bbm1.c + Now LP relaxation can be solved either using rsm1_driver(), or + using glp_simplex2(). The choice is controlled by the parameter + 'meth' (a member of struct bbm1). + + * sample/glpsol.c + The new implementation of the simplex method is now used by + default. The old version is available via --old-sim option. + + * glpmat/gm_scaling.c + Now this routine displays only two lines: an initial "quality" + and a final "quality". + + * glplp/prepro_lp.c + Identifiers 'fmin' and 'fmax' renamed to 'f_min' and 'f_max' in + order to avoid conflict with . The bug was fixed due to + report provided by Sami Farin . + +Wed Oct 03 12:00:00 2001 Andrew Makhorin + + * GLPK 3.0.3 has been released. + + * glprsm/harris_row.c, glprsm/harris_col.c + The relative tolerance used on the first pass of the two-pass + ratio test was replaced by the absolute tolerance. + + * glprsm/rsm_primal.c, glprsm/rsm_feas.c, glprsm/rsm_dual.c + The absolute tolerance passed to the two-pass ratio test routine + was decaresed (for both primal and dual simplex). + + These changes were made in order to improve numerical stability + of the simplex method. + + * glprsm/glp_call_rsm1.c, glprsm/glp_call_bbm1.c, + * glprsm/glp_simplex1, glprsm/glp_pivoting.c + Default form of the inverse was changed from RFI to AFI. + +Mon Sep 24 12:00:00 2001 Andrew Makhorin + + * GLPK 3.0.2 has been released. + + * glpfhv.h, glpfhv.c + New version of the basis maintaining routines was implemented. + These routines, which are based on so called FHV-factorization + (a variety of LU-factorization) and Gustavson's data structures, + perform the main operations on the basis matrix faster at the + expense of some worsening numerical accuracy. + + * glprsm.h, glprsm/afi.c + The routines, which implement AFI (Advanced Form of the + Inverse) based on FHV-factorization, were added to the package. + This new form is available via the parameter form = 3 (on API + level) or via the option --afi (in GLPSOL solver). + + * EFI was renamed to PFI + In order to correct terminology the acronym EFI (Elimination + Form of the Inverse) was replaced by PFI (Product Form of the + Inverse) everywhere in the source code and the documentation. + + * glpset/umalloc.c, glpset/ucalloc.c + * glpset/get_atom.c, glpset/get_atomv.c + These memory management routines were changed in order *not* to + clear allocated memory blocks by binary zeros. + +Wed Aug 01 12:00:00 2001 Andrew Makhorin + + * GLPK 3.0.1 has been released. + + * glpapi/old_api.c, glplp/extract_lp.c, store_lpsol.c + Old API routines were deleted from the package. + + * include/glpk.h, include/glpapi.h, include/glplp.h + Specifications of old API routines and data structures were + removed from the headers. + + * sample/glpsol.c + New version of the stand-alone solver GLPSOL that now uses new + API routines was implemented. + + * glpapi/glp_set_row_fctr.c, glpapi/glp_set_col_fctr.c, + * glpapi/glp_get_row_fctr.c, glpapi/glp_get_col_fctr.c, + * glpapi/glp_scale_prob.c + Scaling routines were added. + + * glpapi/glp_write_mps.c + The routine for writing problem data in MPS format was added. + + * glpapi/glp_simplex1.c + Comprehensive driver to the simplex method was added. + + * glpapi/glp_pivoting.c + The routines glp_pivot_in() and glp_pivot_out() intended for + basis maintaining were added. + + * glprsm/create_rsm.c, glprsm/delete_rsm.c, glprsm/scale_rsm.c, + * glprsm/build_basis.c + Additional low level routines related to the simplex method + were added. + + * glpk.h, glpapi.h, glprsm.h + Additional specifications for new routines and data structures + were added. + + * sample/lpglpk30.c + A non-trivial example was added. It allows using GLPK as a base + LP solver for Concorde, a program for solving Traveling Salesman + Problem (TSP). For details see comments in 'lpglpk30.c'. + + * doc/newapi.txt + The document "Additional GLPK API Routines" that describes some + new API routines was included. + +Thu Jul 19 12:00:00 2001 Andrew Makhorin + + * GLPK 3.0 has been released. + + Now GLPK is provided with new API, which is intended for using + the package in more complex algorithmic schemes. + + * glpapi/old_api.c + All routines related to old API were gathered in one file named + 'old_api.c'. + + * glpapi/*.c + These routines that implement new API were added to the package. + + * include/glpk.h, include/glpapi.h + Specifications of new API routines and data structures were + added to these headers. Specifications of old API routines and + data structures were locked by #ifdef GLP_OLD_API directive. + + * doc/guide.texinfo + New edition of the document "GLPK User's Guide" that correspond + to new API was included. + +Thu Jun 14 12:00:00 2001 Andrew Makhorin + + * GLPK 2.4.1 has been released. + + * doc/glpk_ml.texinfo + The new document "Modeling Language GLPK/L" was included. + + * doc/glpk_ug.texinfo + New edition of the document "GLPK User's Guide" was included. + + * doc/language.txt + The preliminary document "GLPK/L Modeling Language: A Brief + description" was removed from the distribution, because it has + been replaced by the new document "Modeling Language GLPK/L". + + * glplang/l_spar.c + The routine comparison() was re-programmed in order to + implement the relation operation as specified in the language + description. + + * glpmip.h, glpmip/*.c + The partition 'glpmip' was renamed to 'glpbbm'. + +Thu May 10 12:00:00 2001 Andrew Makhorin + + * GLPK 2.4 has been released. + + Now GLPK includes an implementation of a preliminary version of + the GLPK/L modeling language. + + * glplang.h, glplang/*.c + The header 'glplang.h' and a set of routines that implements + the GLPK/L language processor (the partition 'glplang') were + added to the package. + + * doc/language.txt + The document "GLPK/L Modeling Language: A Brief Description + (Supplement to GLPK User's Guide)" in plain text format was + included in the package (see the file 'language.txt' in the + subdirectory 'doc' of the distribution). + + * ex/model1.lpm, ex/model2.lpm + Two examples of model descriptions written in GLPK/L were added + to the package. + + * sample/glpsol.c + This program was modified in order: a) to allow processing + model description written in GLPK/L; b) to allow solving pure + LP problem using the interior point method. + + * sample/glpipm.c + This program was removed from the package, because its function + was passed to the GLPSOL solver. + + * Makefile.in + This file was changed in order to install the GLPSOL solver + executable. + +Mon Apr 09 12:00:00 2001 Andrew Makhorin + + * GLPK 2.3 has been released. + + * glpmip.h, glpmip/*.c + These routines (that implement the branch-and-bound method) were + re-programmed in order to improve robustness of implementation. + In particular, heuristic routines were carried out from the main + driver routine. + + Additional GLPK API routines were documented. + + New edition of the document "GLPK User's Guide" was included in + the package. + + The preliminary document "Mixed Integer Programming Using GLPK + Version 2.2 (Supplement to GLPK User's Guide)" was removed from + the package, because this material was included in GLPK User's + Guide. + +Thu Mar 15 12:00:00 2001 Andrew Makhorin + + * GLPK 2.2 has been released. + + Now GLPK includes a tentative implementation of the + branch-and-bound procedure based on the dual simplex method for + mixed integer linear programming (MIP). + + The preliminary document "Mixed Integer Programming Using GLPK + Version 2.2 (Supplement to GLPK User's Guide)" was included into + the package in plain text format (see the file 'mip.txt' in the + subdirectory 'doc' of the distribution). + + * glpmip.h, glpmip/*.c, glpapi/glp_integer.c + These routines (that implement the branch-and-bound method) were + added to the package. + + * sample/glpsol.c + This program was modified in order to allow solving LP and MIP + problems. + + * glprsm/rsm_primal.c, glprsm/rsm_dual.c, glprsm/rsm_feas.c, + * glprsm/rsm1_driver.c + These routines (which are drivers to basic components of the + revised simplex method) were added to the package. + +Mon Feb 19 12:00:00 2001 Andrew Makhorin + + * GLPK 2.1 has been released. + + * glprsm.h, glprsm/*.c + These routines (that implement components of the revised simplex + method) were re-programmed and documented. + + The document "GLPK Implementation of the Revised Simplex Method" + was included into the package. + +Thu Jan 25 12:00:00 2001 Andrew Makhorin + + * GLPK 2.0 has been released. + + Now GLPK includes a tentative implementation of the primal-dual + interior point method for large-scale linear programming (for + more details see the file `NEWS' in the distribution). A number + of routines related to the interior point method were added to + the package. + + * insist.c + The routine `insist' and the macro of the same name were + introduced into the package in order to replace the standard + macro `assert'. Some routines require the expression specified + in the `assert' macro to be evaluated, but compiling the package + with NDEBUG option prevents from that. This bug was fixed due to + bug report provided by Peter A. Huegler . + + * Makefile.in + Minor bug was fixed due to a patch provided by Alexandre Oliva + . + +Wed Jan 10 12:00:00 2001 Andrew Makhorin + + * GLPK 1.1.2 has been released. + + * umalloc.c, ufree.c, create_pool.c, get_atom.c, get_atomv.c + These routines were changed in order to fix a bug due to + report provided by Andrew Hood . Because of + this bug data alignment error occured on the Sparc computer. + +Tue Dec 14 12:00:00 2000 Andrew Makhorin + + * GLPK 1.1.1 has been released. + + Minor bug was fixed in `Makefile.in'. + + GLPK Library Reference was included. + +Mon Nov 27 12:00:00 2000 Andrew Makhorin + + * GLPK 1.1 has been released. + + Minor changes were made in order to co-ordinate GLPK routines + and their descriptions. + + GLPK User's Guide was included. + +Fri Oct 20 12:00:00 2000 Andrew Makhorin + + * GLPK 1.0 has been released. diff -r d59bea55db9b -r c445c931472f INSTALL --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/INSTALL Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,209 @@ +INSTALLING GLPK ON YOUR COMPUTER +******************************** + +Unpacking the distribution file +------------------------------- +The GLPK package (like all other GNU software) is distributed in the +form of a packed archive. It is one file named `glpk-X.Y.tar.gz', where +`X' is the major version number and `Y' is the minor version number; +for example, the archive name might be `glpk-4.15.tar.gz'. + +In order to prepare the distribution for installation you should: + +1. Copy the GLPK distribution file to a working directory. + +2. Unpack the distribution file with the following command: + + gzip -d glpk-X.Y.tar.gz + + After unpacking the distribution file is automatically renamed to + `glpk-X.Y.tar'. + +3. Unarchive the distribution file with the following command: + + tar -x < glpk-X.Y.tar + + It automatically creates the subdirectory `glpk-X.Y' containing the + GLPK distribution. + +Configuring the package +----------------------- +After unpacking and unarchiving the GLPK distribution you should +configure the package, i.e. automatically tune it for your platform. + +Normally, you should just `cd' to the directory `glpk-X.Y' and run the +`configure' script, e.g. + + ./configure + +The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It also creates file `config.h' containing platform-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + +Running `configure' takes about a few seconds. While it is running, it +displays some messages that tell you what it is doing. If you don't want +to see the messages, run `configure' with its standard output redirected +to `dev/null'; for example, `./configure > /dev/null'. + +By default both static and shared versions of the GLPK library will be +compiled. Compilation of the shared librariy can be turned off by +specifying the `--disable-shared' option to `configure', e.g. + + ./configure --disable-shared + +If you encounter problems building the library try using the above +option, because some platforms do not support shared libraries. + +The GLPK package has some optional features listed below. By default +all these features are disabled. To enable a feature the corresponding +option should be passed to the configure script. + +--with-gmp Enable using the GNU MP bignum library + + This feature allows the exact simplex solver to use the GNU MP + bignum library. If it is disabled, the exact simplex solver uses the + GLPK bignum module, which provides the same functionality as GNU MP, + however, it is much less efficient. + + For details about the GNU MP bignum library see its web page at + . + +--with-zlib Enable using the zlib data compression library + + This feature allows GLPK API routines and the stand-alone solver to + read and write compressed data files performing compression and + decompression "on the fly" (compressed data files are recognized by + suffix `.gz' in the file name). It may be useful in case of large + MPS files to save the disk space. + + For details about the zlib compression library see its web page at + . + +--enable-dl The same as --enable-dl=ltdl +--enable-dl=ltdl Enable shared library support (GNU) +--enable-dl=dlfcn Enable shared library support (POSIX) + + Currently this feature is only needed to provide dynamic linking to + ODBC and MySQL shared libraries (see below). + + For details about the GNU shared library support see the manual at + . + +--enable-odbc Enable using ODBC table driver (libiodbc) +--enable-odbc=unix Enable using ODBC table driver (libodbc) + + This feature allows transmitting data between MathProg model objects + and relational databases accessed through ODBC. + + For more details about this feature see the supplement "Using Data + Tables in the GNU MathProg Modeling Language" (doc/tables.*). + +--enable-mysql Enable using MySQL table driver (libmysql) + + This feature allows transmitting data between MathProg model objects + and MySQL relational databases. + + For more details about this feature see the supplement "Using Data + Tables in the GNU MathProg Modeling Language" (doc/tables.*). + +Compiling the package +--------------------- +Normally, you can compile (build) the package by typing the command: + + make + +It reads `Makefile' generated by `configure' and performs all necessary +jobs. + +If you want, you can override the `make' variables CFLAGS and LDFLAGS +like this: + + make CFLAGS=-O2 LDFLAGS=-s + +To compile the package in a different directory from the one containing +the source code, you must use a version of `make' that supports `VPATH' +variable, such as GNU `make'. `cd' to the directory where you want the +object files and executables to go and run the `configure' script. +`configure' automatically checks for the source code in the directory +that `configure' is in and in `..'. If for some reason `configure' is +not in the source code directory that you are configuring, then it will +report that it can't find the source code. In that case, run `configure' +with the option `--srcdir=DIR', where DIR is the directory that contains +the source code. + +Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Here are the `make' variables that you might want to override with +environment variables when running `configure'. + +For these variables, any value given in the environment overrides the +value that `configure' would choose: + +CC: C compiler program. The default is `cc'. + +INSTALL: Program used to install files. The default value is `install' + if you have it, otherwise `cp'. + +For these variables, any value given in the environment is added to the +value that `configure' chooses: + +DEFS: Configuration options, in the form `-Dfoo -Dbar ...'. + +LIBS: Libraries to link with, in the form `-lfoo -lbar ...'. + +Checking the package +-------------------- +To check the package, i.e. to run some tests included in the package, +you can use the following command: + + make check + +Installing the package +---------------------- +Normally, to install the GLPK package you should type the following +command: + + make install + +By default, `make install' will install the package's files in +`usr/local/bin', `usr/local/lib', etc. You can specify an installation +prefix other than `/usr/local' by giving `configure' the option +`--prefix=PATH'. Alternately, you can do so by consistently giving a +value for the `prefix' variable when you run `make', e.g. + + make prefix=/usr/gnu + make prefix=/usr/gnu install + +After installing you can remove the program binaries and object files +from the source directory by typing `make clean'. To remove all files +that `configure' created (`Makefile', `config.status', etc.), just type +`make distclean'. + +The file `configure.ac' is used to create `configure' by a program +called `autoconf'. You only need it if you want to remake `configure' +using a newer version of `autoconf'. + +Uninstalling the package +------------------------ +To uninstall the GLPK package, i.e. to remove all the package's files +from the system places, you can use the following command: + + make uninstall + +======================================================================== diff -r d59bea55db9b -r c445c931472f Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.am Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,7 @@ +## Process this file with automake to produce Makefile.in ## + +ACLOCAL_AMFLAGS=-I m4 + +SUBDIRS = include src examples + +## eof ## diff -r d59bea55db9b -r c445c931472f Makefile_MMIX --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile_MMIX Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,149 @@ +# Build GLPK for MMIX with GCC cross-compiler + +#**********************************************************************# +# You can use this Makefile to build GLPK with GCC cross-compiler for # +# MMIX. No configuring is needed. # +# # +# MMIX is a 64-bit RISC computer for the third millennium designed by # +# Prof. Donald Knuth. For details see: # +# # +# http://www-cs-faculty.stanford.edu/~knuth/mmix.html # +# http://en.wikipedia.org/wiki/MMIX # +# http://www.malgil.com/mmix # +# # +# The GNU MMIX compiler tool-suite (GCC backend, binutils, and newlib) # +# was developed by Hans-Peter Nilsson. You may follow his instructions # +# to build and install the MMIX tools; see: # +# # +# http://bitrange.com/mmix/install.html # +#**********************************************************************# + +GCC = mmix-gcc +AR = mmix-ar +MMIX = mmix +CFLAGS = -O2 + +OBJSET = \ +src/glpapi01.o \ +src/glpapi02.o \ +src/glpapi03.o \ +src/glpapi04.o \ +src/glpapi05.o \ +src/glpapi06.o \ +src/glpapi07.o \ +src/glpapi08.o \ +src/glpapi09.o \ +src/glpapi10.o \ +src/glpapi11.o \ +src/glpapi12.o \ +src/glpapi13.o \ +src/glpapi14.o \ +src/glpapi15.o \ +src/glpapi16.o \ +src/glpapi17.o \ +src/glpapi18.o \ +src/glpapi19.o \ +src/glpavl.o \ +src/glpbfd.o \ +src/glpbfx.o \ +src/glpcpx.o \ +src/glpdmp.o \ +src/glpdmx.o \ +src/glpenv01.o \ +src/glpenv02.o \ +src/glpenv03.o \ +src/glpenv04.o \ +src/glpenv05.o \ +src/glpenv06.o \ +src/glpenv07.o \ +src/glpenv08.o \ +src/glpfhv.o \ +src/glpgmp.o \ +src/glphbm.o \ +src/glpini01.o \ +src/glpini02.o \ +src/glpios01.o \ +src/glpios02.o \ +src/glpios03.o \ +src/glpios04.o \ +src/glpios05.o \ +src/glpios06.o \ +src/glpios07.o \ +src/glpios08.o \ +src/glpios09.o \ +src/glpios10.o \ +src/glpios11.o \ +src/glpios12.o \ +src/glpipm.o \ +src/glplib01.o \ +src/glplib02.o \ +src/glplib03.o \ +src/glplpf.o \ +src/glplpx01.o \ +src/glplpx02.o \ +src/glplpx03.o \ +src/glpluf.o \ +src/glplux.o \ +src/glpmat.o \ +src/glpmpl01.o \ +src/glpmpl02.o \ +src/glpmpl03.o \ +src/glpmpl04.o \ +src/glpmpl05.o \ +src/glpmpl06.o \ +src/glpmps.o \ +src/glpnet01.o \ +src/glpnet02.o \ +src/glpnet03.o \ +src/glpnet04.o \ +src/glpnet05.o \ +src/glpnet06.o \ +src/glpnet07.o \ +src/glpnet08.o \ +src/glpnet09.o \ +src/glpnpp01.o \ +src/glpnpp02.o \ +src/glpnpp03.o \ +src/glpnpp04.o \ +src/glpnpp05.o \ +src/glpqmd.o \ +src/glprgr.o \ +src/glprng01.o \ +src/glprng02.o \ +src/glpscf.o \ +src/glpscl.o \ +src/glpsdf.o \ +src/glpspm.o \ +src/glpspx01.o \ +src/glpspx02.o \ +src/glpsql.o \ +src/glpssx01.o \ +src/glpssx02.o \ +src/glptsp.o \ +src/amd/amd_1.o \ +src/amd/amd_2.o \ +src/amd/amd_aat.o \ +src/amd/amd_control.o \ +src/amd/amd_defaults.o \ +src/amd/amd_dump.o \ +src/amd/amd_info.o \ +src/amd/amd_order.o \ +src/amd/amd_post_tree.o \ +src/amd/amd_postorder.o \ +src/amd/amd_preprocess.o \ +src/amd/amd_valid.o \ +src/colamd/colamd.o + +.c.o: + $(GCC) $(CFLAGS) -Iinclude -Isrc -o $@ -c $< + +all: libglpk.a glpsol.mmo + +libglpk.a: $(OBJSET) + $(AR) cru libglpk.a $(OBJSET) + +glpsol.mmo: examples/glpsol.o libglpk.a + $(GCC) $(CFLAGS) -o glpsol.mmo examples/glpsol.o libglpk.a -lm + +check: glpsol.mmo + $(MMIX) glpsol.mmo --mps examples/plan.mps diff -r d59bea55db9b -r c445c931472f NEWS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NEWS Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1532 @@ +GLPK 4.45 (release date: Dec 05, 2010) + + This is a bug-fix release. + + Several bugs/typos were fixed. Thanks to + Xypron , + Robbie Morrison , and + Ali Baharev for reports. + + Some glpk documents was re-formatted and merged into a single + document. Now the glpk documentation consists of the following + three main documents (all included in the distribution): + + GLPK: Reference Manual + + GLPK: Graph and Network Routines + + Modeling Language GNU MathProg: Language Reference + +GLPK 4.44 (release date: Jun 03, 2010) + + The following suffixes for variables and constraints were + implemented in the MathProg language: + + .lb (lower bound), + .ub (upper bound), + .status (status in the solution), + .val (primal value), and + .dual (dual value). + + Thanks to Xypron for draft implementation + and testing. + + Now the MathProg language allows comment records (marked by + '#' in the very first position) in CSV data files read with the + table statements. Note that the comment records may appear only + in the beginning of a CSV data file. + + The API routine glp_cpp to solve the Critical Path Problem was + added and documented. + +GLPK 4.43 (release date: Feb 20, 2010) + + This is a maintainer release. + + `configure.ac' was changed to allow building the package under + Mac OS and Darwin with ODBC support. + Thanks to Xypron for suggestions and Noli + Sicad for testing. + + The SQL table driver was improved to process NULL data. Thanks + to Xypron . + + Some bugs were fixed in the LP/MIP preprocessor. + +GLPK 4.42 (release date: Jan 13, 2010) + + The following new API routines were added: + + glp_check_dup check for duplicate elements in sparse + matrix + glp_sort_matrix sort elements of the constraint matrix + glp_read_prob read problem data in GLPK format + glp_write_prob write problem data in GLPK format + glp_analyze_bound analyze active bound of non-basic variable + glp_analyze_coef analyze objective coefficient at basic + variable + glp_print_ranges print sensitivity analysis report (this + routine replaces lpx_print_sens_bnds and + makes it deprecated) + + For description of these new routines and the GLPK LP/MIP + format see a new edition of the reference manual included in + the distribution. (Chapter "Graph and network API routines" was + carried out from the main reference manual and included in the + distribution as a separate document.) + + The following new command-line options were added to the stand- + alone solver glpsol: + --glp filename read problem data in GLPK format + --wglp filename write problem data in GLPK format + --ranges filename print sensitivity analysis report (this + option replaces --bounds) + + Now all GLPK routines performing file I/O support special + filenames "/dev/stdin", "/dev/stdout", and "/dev/stderr", which + can be specified in the same way as regular filenames. This + feature is plaform-independent. + +GLPK 4.41 (release date: Dec 21, 2009) + + The following new API routies were added: + + glp_transform_row transform explicitly specified row + glp_transform_col transform explicitly specified column + glp_prim_rtest perform primal ratio test + glp_dual_rtest perform dual ratio test + + For description of these new routines see a new edition of the + reference manual included in the distribution. + + The following API routines are deprecated: lpx_transform_row, + lpx_transform_col, lpx_prim_ratio_test, lpx_dual_ratio_test. + + Some improvements were made in the MIP solver (glp_intopt). + + The SQL table driver used to read/write data in MathProg models + was changed to allow multiple arguments separated by semicolon + in SQL statements. Thanks to Xypron . + + Two new options were added to the glpsol stand-alone solver: + --seed value (to initialize the pseudo-random number generator + used in MathProg models with specified value), and + --ini filename (to use a basis previously saved with -w option + as an initial basis on solving similar LP's). + + Two new MathProg example models were included. Thanks to + Nigel Galloway and Noli Sicad + for contribution. + + Scripts to build GLPK with Microsoft Visual Studio 2010 for + both 32-bit and 64-bit Windows were included. Thanks to Xypron + for contribution and testing. + +GLPK 4.40 (release date: Nov 03, 2009) + + The following new API routines were added: + + glp_del_vertices remove vertices from graph + glp_del_arc remove arc from graph + glp_wclique_exact find maximum weight clique with the exact + algorithm developed by Prof. P. Ostergard + glp_read_ccdata read graph in DIMACS clique/coloring + format + glp_write_ccdata write graph in DIMACS clique/coloring + format + + For description of these new routines see a new edition of the + reference manual included in the distribution. + + The hybrid pseudocost branching heuristic was included in the + MIP solver. It is available on API level (iocp.br_tech should + be set to GLP_BR_PCH) and in the stand-alone solver glpsol + (via the command-line option --pcost). This heuristic may be + useful on solving hard MIP instances. + + The branching heuristic by Driebeck and Tomlin (used in the + MIP solver by default) was changed to switch to branching on + most fractional variable if an lower bound of degradation of + the objective is close to zero for all branching candidates. + + A bug was fixed in the LP preprocessor (routine npp_empty_col). + Thanks to Stefan Vigerske for the + bug report. + + A bug was fixed and some improvements were made in the FPUMP + heuristic module. Thanks to Xypron . + + A bug was fixed in the API routine glp_warm_up (dual + feasibility test was incorrect in maximization case). Thanks to + Uday Venkatadri for the bug report. + +GLPK 4.39 (release date: Jul 26, 2009) + + The following new API routines were added: + + glp_warm_up "warm up" LP basis + glp_set_vertex_name assign (change) vertex name + glp_create_v_index create vertex name index + glp_find_vertex find vertex by its name + glp_delete_v_index delete vertex name index + glp_read_asnprob read assignment problem data in DIMACS + format + glp_write_asnprob write assignment problem data in DIMACS + format + glp_check_asnprob check correctness of assignment problem + data + glp_asnprob_lp convert assignment problem to LP + glp_asnprob_okalg solve assignment problem with the + out-of-kilter algorithm + glp_asnprob_hall find bipartite matching of maxumum + cardinality with Hall's algorithm + + Also were added some API routines to read plain data files. + + The API routines glp_read_lp and glp_write_lp to read/write + files in CPLEX LP format were re-implemented. Now glp_write_lp + correctly writes double-bounded (ranged) rows by introducing + slack variables rather than by duplicating the rows. + + For description of these new routines see a new edition of the + reference manual included in the distribution. + + The 'xfree(NULL)' bug was fixed in the AMD routines. Thanks to + Niels Klitgord for bug report. + + The message "Crashing..." was changed to "Constructing initial + basis..." due to suggestion by Thomas Kahle . + + Some typos were corrected in glpsol output messages. Thanks to + Xypron for patch. + +GLPK 4.38 (release date: May 02, 2009) + + API routines glp_read_mps and glp_write_mps were improved. + + Some improvements were made in the dual simplex routines. + + Two external software modules AMD and COLAMD were included in + the distribution (for more details please see src/amd/README + and src/colamd/README). Now they are used in the interior-point + solver to reorder the matrix prior to Cholesky factorization. + + API routine glp_ipt_status may return two new statuses due to + changes in the routine glp_interior. For details please see the + reference manual included in the distribution. + + A minor bug was fixed in the graph/network routines. Thanks to + Nelson H. F. Beebe for bug report. + +GLPK 4.37 (release date: Mar 29, 2009) + + The 0-1 Feasibility Pump heuristic was included in the GLPK + integer optimizer glp_intopt. On API level the heuristic can be + enabled by setting the parameter fp_heur in glp_iocp to GLP_ON. + This feature is also available in the solver glpsol through + command-line option '--fpump'. For more details please see the + reference manual included in the distribution. + + The following new API routines were added: + + glp_print_sol write basic solution in printable format + glp_print_ipt write interior-point solution in printable + format + glp_print_mip write MIP solution in printable format + glp_read_graph read (di)graph from plain text file + glp_write_graph write (di)graph to plain text file + glp_weak_comp find all weakly connected components + glp_strong_comp find all strongly connected components + + The following API routines are deprecated: lpx_print_sol, + lpx_print_ips, lpx_print_mip, lpx_print_prob (the latter is + equivalent to glp_write_lp). + + A bug was fixed in the interior-point solver (glp_interior) to + correctly compute dual solution components when the problem is + scaled. + + The files configure.ac and Makefile.am were changed: + (a) to allow using autoreconf/autoheader; + (b) to allow building the package in a directory other than its + source directory. + Thanks to Marco Atzeri for bug report. + + An example model in the GNU MathProg language was added. + Thanks to Larry D'Agostino for + contribution. + +GLPK 4.36 (release date: Feb 06, 2009) + + The following new API routines were added to the package: + + glp_mincost_okalg find minimum-cost flow with out-of-kilter + algorithm + glp_maxflow_ffalg find maximal flow with Ford-Fulkerson + algorithm + + For detailed description of these new routines and related data + structures see chapter "Graph and Network API Routines" in a new + edition of the reference manual included in the distribution. + + The following two new command-line options were added to the + solver glpsol: + + --mincost read min-cost flow data in DIMACS format + --maxflow read maximum flow data in DIMACS format + + Duplicate symbols in the header glpk.h were removed to allow + using swig. + Thanks to Kelly Westbrooks and + Nigel Galloway for suggestion. + + A minor defect was fixed in the routine glp_write_lp. + Thanks to Sebastien Briais for bug report. + + A minor bug was fixed in the SQL module. + Thanks to Xypron for patch. + + Some new example models in the GNU MathProg modeling language + were added. Thanks to Sebastian Nowozin and + Nigel Galloway for contribution. + +GLPK 4.35 (release date: Jan 09, 2009) + + The following new API routines were added to the package: + + glp_create_graph create graph + glp_set_graph_name assign (change) graph name + glp_add_vertices add new vertices to graph + glp_add_arc add new arc to graph + glp_erase_graph erase graph content + glp_delete_graph delete graph + glp_read_mincost read minimum cost flow problem data in + DIMACS format + glp_write_mincost write minimum cost flow problem data in + DIMACS format + glp_mincost_lp convert minimum cost flow problem to LP + glp_netgen Klingman's network problem generator + glp_gridgen grid-like network problem generator + glp_read_maxflow read maximum flow problem data in DIMACS + format + glp_write_maxflow write maximum flow problem data in DIMACS + format + glp_maxflow_lp convert maximum flow problem to LP + glp_rmfgen Goldfarb's maximum flow problem generator + + For detailed description of these new routines and related data + structures see chapter "Graph and Network API Routines" in a new + edition of the reference manual included in the distribution. + + A minor change were made in the internal routine xputc. Thanks + to Luiz Bettoni for suggestion. + + A minor bug was fixed in the internal routine mpl_fn_time2str. + Thanks to Stefan Vigerske for bug report. + +GLPK 4.34 (release date: Dec 04, 2008) + + The GNU MathProg modeling language was supplemented with three + new built-in functions: + + gmtime obtaining current calendar time + str2time converting character string to calendar time + time2str converting calendar time to character string + + (Thanks to Xypron .) + + For detailed description of these functions see Appendix A in + the document "Modeling Language GNU MathProg", a new edition of + which was included in the distribution. + + A bug was fixed in the MIP solver. Thanks to Nigel Galloway + for bug report. + + A new makefile was added to build the GLPK DLL with Microsoft + Visual Studio Express 2008 for 64-bit Windows. Thanks to Xypron + for contribution and testing. + +GLPK 4.33 (release date: Oct 30, 2008) + + The following new API routines were added to the package: + glp_copy_prob copy problem object content + glp_exact solve LP in exact arithmetic + (makes lpx_exact deprecated) + glp_get_unbnd_ray determine variable causing unboundedness + (makes lpx_get_ray_info deprecated) + glp_interior solve LP with interior-point method + (makes lpx_interior deprecated) + + The following new API routines for processing models written in + the GNU Mathprog language were added to the package: + glp_mpl_alloc_wksp allocate the translator workspace + glp_mpl_read_model read and translate model section + glp_mpl_read_data read and translate data section + glp_mpl_generate generate the model + glp_mpl_build_prob build LP/MIP instance from the model + glp_mpl_postsolve postsolve the model + glp_mpl_free_wksp deallocate the translator workspace + (These routines make lpx_read_model deprecated.) + + For description of all these new API routines see the reference + manual included in the distribution. + + A crude implementation of CPLEX-like interface to GLPK API was + added to the package. Currently it allows using GLPK as a core + LP solver for Concorde, a well known computer code for solving + the symmetric TSP. For details see examples/cplex/README. + + Some bugs were fixed in the SQL table driver. Thanks to Xypron + . + +GLPK 4.32 (release date: Oct 03, 2008) + + The following new features were included in the MIP solver + (the API routine glp_intopt): + + * MIP presolver + * mixed cover cut generator + * clique cut generator + * Euclidean reduction of the objective value + + Due to changes the routine glp_intopt may additionally return + GLP_ENOPFS, GLP_ENODFS, and GLP_EMIPGAP. + + The API routines lpx_integer are lpx_intopt are deprecated, + since they are completely superseded by glp_intopt. + + The following new branch-and-cut API routines were added: + glp_ios_row_attr determine additional row attributes + glp_ios_pool_size determine current size of the cut pool + glp_ios_add_row add constraint to the cut pool + glp_ios_del_row delete constraint from the cut pool + glp_ios_clear_pool delete all constraints from the cut pool + + For description of these new routines see the reference manual + included in the distribution. + + The stand-alone solver glpsol was changed to allow multiple + data files. + + A new edition of the supplement "Using Data Tables in the GNU + MathProg Modeling Language" was included. + + As usual, some bugs were fixed (in the MathProg translator). + Thanks to Xypron . + +GLPK 4.31 (release date: Sep 02, 2008) + + The core LP solver based on the dual simplex method was + re-implemented and now it provides both phases I and II. + + The following new API routines were added: + glp_scale_prob automatic scaling of problem data + glp_std_basis construct standard initial LP basis + glp_adv_basis construct advanced initial LP basis + glp_cpx_basis construct Bixby's initial LP basis + + For description of these new routines see the reference manual + included in the distribution. + + The following API routines are deprecated: + lpx_scale_prob, lpx_std_basis, lpx_adv_basis, lpx_cpx_basis. + + Necessary changes were made in memory allocation routines to + resolve portability issues for 64-bit platforms. + + New version of the routine lpx_write_pb to write problem data + in OPB (pseudo boolean format) was added to the package. Thanks + to Oscar Gustafsson for the contribution. + + Two new makefiles were added to build the package for 32- and + 64-bit Windows with Microsoft Visual Studio Express 2008. + Thanks to Heinrich Schuchardt (aka + Xypron) for the contribution and testing. + + Two new makefiles were added to build the package with Digital + Mars C/C++ 8.50 and Open Watcom C/C++ 1.6 (for 32-bit Windows). + +GLPK 4.30 (release date: Aug 13, 2008) + + The core LP solver based on the primal simplex method was + re-implemented to allow its further improvements. Currently the + new version provides the same features as the old one, however, + it is a bit faster and more numerically stable. + + Some changes were made in the MathProg translator to allow <, + <=, >=, and > on comparing symbolic values. Thanks to Heinrich + Schuchardt for patches. + + Internal routine set_d_eps in the exact LP solver was changed + to prevent approximation errors in case of integral data. + Thanks to Markus Pilz for bug report. + +GLPK 4.29 (release date: Jul 06, 2008) + + The configure script was changed to disable all optional + features by default. For details please see file INSTALL. + + The following new API routines were added: + glp_erase_prob erase problem object content + glp_read_mps read problem data in MPS format + glp_write_mps write problem data in MPS format + glp_read_lp read problem data in CPLEX LP format + glp_write_lp write problem data in CPLEX LP format + + For description of these new routines see the reference manual + included in the distribution. + + The following API routines are deprecated: + lpx_read_mps, lpx_read_freemps, lpx_write_mps, + lpx_write_freemps, lpx_read_cpxlp, and lpx_write_cpxlp. + + Two bugs were fixed. Thanks to + Anne-Laurence Putz and + Xypron for bug report. + +GLPK 4.28 (release date: Mar 25, 2008) + + The iODBC and MySQL table drivers, which allows transmitting + data between MathProg model objects and relational databases, + were re-implemented to replace a static linking by a dynamic + linking to corresponding shared libraries. + Many thanks to Heinrich Schuchardt + for the contribution, Rafael Laboissiere + for useful advices concerning the shared library support under + GNU/Linux, and Vijay Patil for testing + this feature under Windows XP. + + A new optional feature was added to the package. This feature + is based on the zlib data compression library and allows GLPK + API routines and the stand-alone solver to read and write + compressed data files performing compression/decompression "on + the fly" (compressed data files are recognized by suffix `.gz' + in the file name). It may be useful in case of large MPS files + to save the disk space (up to ten times). + + The `configure' script was re-implemented. Now it supports the + following specific options: + + --with-gmp Enable using the GNU MP bignum library + --without-gmp Disable using the GNU MP bignum library + --with-zlib Enable using the zlib data compression + library + --without-zlib Disable using the zlib data compression + library + --enable-dl Enable shared library support (auto check) + --enable-dl=ltdl Enable shared library support (GNU) + --enable-dl=dlfcn Enable shared library support (POSIX) + --disable-dl Disable shared library support + --enable-odbc Enable using ODBC table driver + --disable-odbc Disable using ODBC table driver + --enable-mysql Enable using MySQL table driver + --disable-mysql Disable using MySQL table driver + + For more details please see file INSTALL. + +GLPK 4.27 (release date: Mar 02, 2008) + + Three new table drivers were added to the MathProg translator: + + xBASE built-in table driver, which allows reading and writing + data in .dbf format (only C and N fields are supported); + + MySQL table driver, which provides connection to a MySQL + database; + + iODBC table driver, which provides connection to a database + through ODBC. + + The MySQL and iODBC table drivers were contributed to GLPK by + Heinrich Schuchardt . + + The table driver is a program module which allows transmitting + data between MathProg model objects and external data tables. + + For detailed description of the table statement and table + drivers see the document "Using Data Tables in the GNU MathProg + Modeling Language" (file doc/tables.txt) included in the + distribution. Some examples which demonstrate using MySQL and + iODBC table drivers can be found in subdirectory examples/sql. + +GLPK 4.26 (release date: Feb 17, 2008) + + The table statement was implemented in the GNU MathProg + modeling language. This new feature allows reading data from + external tables into model objects such as sets and parameters + as well as writing results of computations to external tables. + + A table is a (unordered) set of records, where each record + consists of the same number of fields, and each field is + provided with a unique symbolic name called the field name. + + Currently the GLPK package has the only built-in table driver, + which supports tables in the CSV (comma-separated values) file + format. This format is very simple and supported by almost all + spreadsheets and database management systems. + + Detailed description of the table statement and CSV format can + be found in file doc/tables.txt, included in the distribution. + +GLPK 4.25 (release date: Dec 19, 2007) + + A tentative implementation of Gomory's mixed integer cuts was + included in the branch-and-cut solver. To enable generating + Gomory's cuts the control parameter gmi_cuts passed to the + routine glp_intopt should be set to GLP_ON. This feature is + also available in the solver glpsol through command-line option + '--gomory'. For more details please see the reference manual + included in the distribution. + +GLPK 4.24 (release date: Nov 21, 2007) + + A tentative implementation of MIR (mixed integer rounding) cuts + was included in the MIP solver. To enable generating MIR cuts + the control parameter mir_cuts passed to the routine glp_intopt + should be set to GLP_ON. This feature is also available in the + stand-alone solver glpsol via command-line option '--mir'. For + more details please see the reference manual included in the + distribution. + + The implementation is mainly based on the following two papers: + + 1. H. Marchand and L. A. Wolsey. Aggregation and mixed integer + rounding to solve MIPs. CORE discussion paper 9839, CORE, + Universite catholique de Louvain, June 1998. + + 2. G. Andreello, A. Caprara, and M. Fischetti. Embedding cuts + in a Branch&Cut framework. Preliminary draft, October 2003. + + MIR cuts can be generated on any level of the search tree that + makes the GLPK MIP solver to be a real branch-and-cut solver. + + A bug was fixed in the routine lpx_write_cpxlp. If a variable + x has upper bound and no lower bound, it should appear in the + bounds section as "-inf <= x <= u", not as "x <= u". Thanks to + Enric Rodriguez for the bug report. + +GLPK 4.23 (release date: Oct 28, 2007) + + The following new API routines were added: + + glp_read_sol read basic solution from text file + glp_write_sol write basic solution to text file + glp_read_ipt read interior-point solution from text file + glp_write_ipt write interior-point solution to text file + glp_read_mip read MIP solution from text file + glp_write_mip write MIP solution to text file + + For description of these routines and corresponding file + formats see Chapter "API Routines", Section "Utility routines" + in the reference manual included in the distribution. + + Advanced API routine glp_free_env was added. It may be used by + the application program to free all resources allocated by GLPK + routines. + + The following three new command-line options were added to the + solver glpsol: + + --mipgap tol set relative MIP gap tolerance + -r filename read solution from filename + -w filename write solution to filename + +GLPK 4.22 (release date: Sep 19, 2007) + + This is a maintainer release. + + A bug was fixed in the MIP preprocessor (ios_preprocess_node). + Thanks to Roberto Bagnara (Department of + Mathematics, University of Parma, Italy) for the bug report. + + A bug was fixed in the MIP preprocessor (col_implied_bounds), + due to which constraint coefficients with small magnitude could + lead to wrong implied bounds of structural variables. + + A similar bug was fixed in the routine reduce_bounds. + + A bug was fixed in the routines glp_set_mat_row and + glp_set_mat_col. (The bug appeared due to incorrect removing + zero elements from the row/column lists.) + + A bug was fixed in the API routines lpx_read_mps and + lpx_read_freemps, due to which bounds of type LI specified in + BOUNDS section were incorrectly processed. + + A call to standard function vsprintf was replaced by a call to + vsnprintf for security reasons. Many thanks to Peter T. Breuer + and Rafael Laboissiere . + +GLPK 4.21 (release date: Aug 28, 2007) + + Additional reasons for calling the callback routine used in the + MIP solver (glp_intopt) were introduced. Currently the following + reasons are supported: + + * request for subproblem selection + * request for preprocessing + * request for row generation + * request for heuristic solution + * request for cut generation + * request for branching + * better integer solution found + + A basic preprocessing component used to improve subproblem + formulations by tightening bounds of variables was included in + the MIP solver. Depending on the control parameter pp_tech + passed to the routine glp_intopt the preprocessing can be + performed either on the root level or on all levels (default) + or can be disabled. + + Backtracking heuristic used by default in the MIP solver was + changed to the "best local bound". + + For more details see Chapter "Advanced API routines", Section + "Branch-and-bound interface routines" in a new edition of the + reference manual included in the distribution. + +GLPK 4.20 (release date: Jul 26, 2007) + + API routine lpx_integer was replaced by API routine glp_intopt, + which provides equivalent functionality and additionally allows + the application to control the solution process by means of the + user-written callback routine, which is called by the solver at + various points of the branch-and-bound algorithm. Besides, the + new MIP solver allows generating "lazy" constraints and cutting + planes on all levels of the branch-and-bound tree, not only on + the root level. The routine lpx_integer is also still available + for the backward compatibility. + + The following new advanced API routines, which may be called + from the B&B callback routine, were included in the package: + + glp_ios_reason determine reason for calling callback + routine + glp_ios_get_prob access the problem object + glp_ios_tree_size determine size of the branch-and-bound tree + glp_ios_curr_node determine current active subproblem + glp_ios_next_node determine next active subproblem + glp_ios_prev_node determine previous active subproblem + glp_ios_up_node determine parent subproblem + glp_ios_node_level determine subproblem level + glp_ios_node_bound determine subproblem local bound + glp_ios_mip_gap compute relative MIP gap + glp_ios_heur_sol provide solution found by heuristic + glp_ios_terminate terminate the solution process + + For description of these routines see Chapter "Advanced API + routines", Section "Branch-and-bound interface routines" in a + new edition of the reference manual, which was included in the + distribution. + + Old version of the integer optimization suite (IOS) as well as + TSP solver tspsol based on it are no longer supported and were + removed from the package. + + A minor error in the MIP presolver was fixed; thanks to Graham + Rockwell for the bug report. + +GLPK 4.19 (release date: Jul 05, 2007) + + The principal change is upgrading to GPLv3. + + A serious bug in the routine glp_del_cols was fixed; thanks to + Cedric[FR] for the bug report. The bug + appeared because on deleting non-basic columns the basis header + remained valid, however, contained invalid (old) column ordinal + numbers. + + A new advanced API routine glp_mem_limit was added. + + The case GLP_EBOUND was added to the routine lpx_simplex. + Thanks to Cameron Kellough for the + bug report. + + An API routine lpx_write_pb to write the problem instance in + OPB (pseudo boolean) format format was added. Thanks to Oscar + Gustafsson for the contribution. + + Two new options --wpb and --wnpb were added to glpsol to write + the problem instance in OPB format. + +GLPK 4.18 (release date: Jun 25, 2007) + + The following new API routines were added: + + glp_set_rii set (change) row scale factor + glp_set_sjj set (change) column scale factor + glp_get_rii retrieve row scale factor + glp_get_sjj retrieve column scale factor + glp_simplex solve LP problem with the simplex method + (this routine replaces lpx_simplex, which is + also available for backward compatibility) + glp_init_smcp initialize simplex method control params + glp_bf_exists check if the basis factorization exists + glp_factorize compute the basis factorization + glp_bf_updated check if the basis factorization has been + updated + glp_get_bfcp retrieve basis factorization control params + glp_set_bfcp change basis factorization control params + glp_get_bhead retrieve the basis header information + glp_get_row_bind retrieve row index in the basis header + glp_get_col_bind retrieve column index in the basis header + glp_ftran perform forward transformation + glp_btran perform backward transformation + + For description of all these routines see a new edition of the + reference manual included in the distribution. + + Type names ulong_t and uldiv_t were changed to glp_ulong and + glp_uldiv to avoid conflicts with standard type names on some + platforms. Thanks to Boris Wirtz + for the bug report. + + Some new examples in the MathProg language were added. Thanks + to Sebastian Nowozin . + +GLPK 4.17 (release date: May 26, 2007) + + API routines glp_set_mat_row, glp_set_mat_col, and glp_load_mat + were modified to allow zero constraint coefficients (which are + not stored in the constraint matrix). Note that constraint + coefficients with duplicate row/column indices are not allowed. + + Another form of LP basis factorization was implemented in the + package. It is based on LU-factorization of an initial basis + and Schur complement to reflect changes in the basis. Currently + the implementation is incomplete and provides only updating the + factorization on replacing a column of the basis matrix. On API + level the user can set the control parameter LPX_K_BFTYPE to + choose between the folloiwng forms of LP basis factorization to + be used in the simplex method routines: + 1) LU + Forrest-Tomlin update; + 2) LU + Schur complement + Bartels-Golub update; + 3) LU + Schur complement + Givens rotation update. + The GLPK implementation is similar to LUSOL/LUMOD developed by + Michael A. Saunders. + + The user can choose the form of LP basis factorzation used by + the simplex method routines by specifying the folloiwng options + of glpsol: --luf, --cbg, --cgr. + +GLPK 4.16 (release date: May 05, 2007) + + A number of basic GLPK API routines, which now are in the + stable stable, were renamed to be prefixed with 'glp_'. Note + that all these routines are available via their old names + prefixed with 'lpx_' that keeps the downward compatibility with + older versions of the package. + + Three new GLPK API routines were added to the package: + glp_version, glp_term_hook, and glp_mem_usage; for more details + see a new edition of the GLPK reference manual included in the + distribution. The routine glp_version reports the actual version + of the GLPK library and also can be used (along with the header + glpk.h) in Autotools specification files to check if the GLPK + library has been installed. + + The header glpk.h was changed to conform to C++ environment. + +GLPK 4.15 (release date: Feb 18, 2007) + + Autotools specification files (configure.ac, Makefile.am) were + changed to use GNU Libtool. This allows building the static as + well as shared GLPK library. + +GLPK 4.14 (release date: Feb 05, 2007) + + Now GLPK conforms to ILP32, LLP64, and LP64 programming models + (the latter seems to be the ultimate choice regarding 64-bit + architectures). Note that GLPK itself is a 32-bit application, + and the conformity only means that the package works correctly + on all these arenae. Nevertheless, on 64-bit platforms it is + possible to use more than 4GB of memory, if necessary. + +GLPK 4.13 (release date: Nov 13, 2006) + + A tentative implementation of the "exact" simplex method based + on bignum (rational) arithmetic was included in the package. + + On API level this new feature is available through the routine + lpx_exact, which is similar to the routine lpx_simplex. + + In the solver glpsol this feature is available through two new + command-line options: --exact and --xcheck. If the '--exact' + option is specified, glpsol solves LP instance using the exact + simplex method; in case of MIP it is used to obtain optimal + solution of LP relaxation. If the --xcheck option is specified, + LP instance (or LP relaxation) is solved using the standard + (floating-point) simplex method, however, then glpsol calls the + exact simplex routine to make sure that the final LP basis is + exactly optimal, and if it is not, to perform some additional + simplex iterations in exact arithmetic. + +GLPK 4.12 (release date: Nov 08, 2006) + + A tentative implementation of some simplex method routines + based on exact (bignum) arithmetic was included in the package. + Currently these routines provide computing LU-factorization of + the basis matrix and computing components of basic solution. + + These routines were used to implement a routine, which checks + primal and dual feasibility of basic solution exactly, i.e. in + rational numbers, without round-off errors. In glpsol this + feature is available through the command-line option --xcheck. + + GLPK has its own low-level routines implementing operations on + integer and rational numbers that makes it independent on other + software packages. However, to attain a much better performance + it is highly recommended to install (before configuring GLPK) + the GNU Multiple Precision Arithmetic Library (GMP). Using GMP + makes computations 100-200 times faster. + +GLPK 4.11 (release date: Jul 25, 2006) + + Three new built-in functions in the modeling language were + implemented: card (cardinality of set), length (length of + character string), and substr (substring of character string). + Another improvement concerns the printf statement which now + allows redirecting its output to a specified file. These new + features are illustrated in example models crypto.mod and + graph.mod included in the distribution. For more details see + the document "Modeling Language GNU MathProg". + + Four batch files (along with corresponding makefiles) were + included in the distribution to simplify building GLPK under + MS Windows; see them in subdirectory 'w32'. + +GLPK 4.10 (release date: May 11, 2006) + + Cutting planes of two new classes were implemented: mixed cover + cuts and clique cuts. On API level this feature can be enabled + by setting control parameter LPX_K_USECUTS passed to the routine + lpx_intopt. In glpsol this feature is available through the + command-line options --cover and --clique. For more details see + the reference manual. + + Now the routines lpx_read_mps and lpx_read_freemps support LI + bound type. It is similar to LO, however, indicates the column + as of integer kind. + +GLPK 4.9 (release date: Jan 17, 2006) + + An advanced MIP solver was implemented. It includes: + + - basic presolving technique (removing free, singleton and + redundant rows, improving bounds of columns, removing fixed + columns, reducing constraint coefficents); + + - generating cutting planes to improve LP relaxation (currently + only Gomory's mixed integer cuts are implemented); + + - using the branch-and-bound method to solve resultant MIP; + + - recovering solution of the original MIP. + + The solver is available on API level via the routine lpx_intopt + (see the reference manual). It is similar to the routine + lpx_integer, however, does not require initial solution of LP + relaxation. + + The solver is also available in the command-line utility glpsol + via two options: --intopt (only presolving) and --cuts (assumes + --intopt plus generating cuts). + + Note that efficiency of the MIP solver strongly depends on the + internal structure of the problem to be solved. For some hard + instances it is very efficient, but for other instances it may + be significantly worse than the standard branch-and-bound. + + For some comparative benchmarks see doc/bench1.txt. + + Well, what else... + + Three built-in functions were added to MathProg: sin, cos, and + atan (the latter allows one or two arguments). + + Some bugs were fixed. + + Several new examples in MathProg were included: color.mod + (graph coloring problem), tsp.mod (traveling salesman problem), + and pbn.mod (paint-by-numbers puzzle). + +GLPK 4.8 (release date: Jan 12, 2005) + + Core simplex method and interior-point method routines were + re-implemented and now they use a new, "storage-by-rows" sparse + matrix format (unlike previous versions where linked lists were + used to represent sparse matrices). For details see ChangeLog. + + Also a minor bug was fixed in API routine lpx_read_cpxlp. + +GLPK 4.7 (release date: Aug 23, 2004) + + Now GLPK supports free MPS format. Two new API routines + lpx_read_freemps (to read problem data in free MPS format) and + lpx_write_freemps (to write problem data in free MPS format) + were added. This feature is also available in the solver glpsol + via new command-line options --freemps and --wfreemps. For more + details see the GLPK reference manual. + + API routines lpx_read_cpxlp and lpx_write_cpxlp for reading and + writing problem data in CPLEX LP format were re-implemented to + allow long symbolic names (up to 255 characters). + + The following three modules were temporarily removed from the + GLPK distribution due to licensing problems: DELI (an interface + module to Delphi), GLPKMEX (an interface module to Matlab), and + JNI (an interface module to Java). + +GLPK 4.6 (release date: Aug 04, 2004) + + Three new statements were implemented in the GNU MathProg + language: solve, printf, and for. Their detailed description can + be found in the GLPK documentation included in the distribution. + (See also a sample model, examples/queens.mod, which illustrates + using these new statements.) + + Two new API routines were added to the package: lpx_read_prob + and lpx_write_prob. They allow reading/writing problem data in + GNU LP low-level text format. + + Three new command-line options were implemented in the LP/MIP + solver glpsol: --glp (to read problem data in GNU LP format), + --wglp (to write problem data in GNU LP format), and --name (to + change problem name). Now glpsol also supports processing models + where the new statements (see above) are used. + + A new version of GLPKMEX, a Matlab MEX interface to GLPK, was + included. For more details see contrib/glpkmex/ChangeLog. + +GLPK 4.5 (release date: Jul 19, 2004) + + The branch-and-bound solver was completely re-implemented. + + Some modifications were made in memory allocation routines that + allows using the package on 64-bit platforms. + + For more details see ChangeLog. + +GLPK 4.4 (release date: Jan 17, 2004) + + All API routines were re-implemented using new data structures. + The new implementation provides the same specifications and + functionality of API routines as the old one, however, it has + some important advantages, in particular: + * linked lists are used everywhere that allows creating and + modifying the problem object as efficiently as possible + * all data stored in the problem object are non-scaled (even if + the internal scaling is used) that prevents distortion of the + original problem data + * solution components obtained by the solver remain available + even if the problem object has been modified + * no solver-specific data are used in the new data structures + that allows attaching any external lp/mip solver using GLPK + API as an uniform interface + Note that some API routines became obsolete being replaced by + new, more convenient routines. These obsolete routines are kept + for backward compatibility, however, they will be removed in + the future. For more details please see ChangeLog and the GLPK + Reference Manual. + + New edition of the GLPK Reference Manual was included in the + distribution. + + GLPKMEX, a Matlab MEX interface to GLPK package, contributed by + Nicolo Giorgetti was included in the + distribution. + + GLPK FAQ contributed by Harley Mackenzie was + included in the distribution. + +GLPK 4.3 (release date: Dec 12, 2003) + + The bug, due to which the standard math library is not linked + on building the package on some platforms, was fixed. + + The following new built-in functions were added to the MathProg + language: round, trunc, Irand224, Uniform01, Uniform, Normal01, + Normal. For details see the language description. + + The MathProg syntax was changed to allow writing 'subj to' that + means 'subject to'. + + The new api routine lpx_get_ray_info was added. It is intended + to determine which (non-basic) variable causes unboundness. For + details see the reference manual. + + The module glpmps.c was changed to avoid compilation errors on + building the package on Mac OS X. + + Several typos was fixed and some new material was added to the + GLPK documentation. + +GLPK 4.2 (release date: Nov 14, 2003) + + A preliminary implementation of the Integer Optimization Suite + (IOS) was included in the package. The Branch-and-Cut Framework + being completely superseded by IOS was removed from the package. + + New API routine lpx_print_sens_bnds intended for bounds + sensitivity analysis was contributed to GLPK by Brady Hunsaker + . This function is also available in + the solver glpsol (via command-line option --bounds). + + An improved version of GLPK JNI (Java Native Interface) was + contributed by Chris Rosebrugh . + + GLPK DELI (Delphi Interface) was contributed by Ivo van Baren + . + + Several makefiles were added to allow compiling GLPK on some + non-GNU 32-bit platforms: + * Windows single-threaded static library, Visual C++ 6.0 + * Windows multi-threaded dynamic library, Visual C++ 6.0 + * Windows single-threaded static library, Borland C++ 5.2 + * DOS single-threaded static library, Digital Mars C++ 7.50 + + And, of course, some bugs were fixed. + + For more details see ChangeLog. + +GLPK 4.1 (release date: Aug 23, 2003) + + Some improvements were made in the lp/mip solver routines and + several bugs were fixed in the model translator. + + For more details see ChangeLog. + +GLPK 4.0 (release date: May 06, 2003) + + Now GLPK supports the GNU MathProg modeling language, which is + a subset of the AMPL modeling language. + + The document "GLPK: Modeling Language GNU MathProg" included in + the distribution is a complete description of GNU MathProg. (See + the files lang.latex, lang.dvi, and lang.ps in the subdirectory + 'doc'. See also some examples in the subdirectory 'sample'.) + + New version of the solver glpsol, which supports models written + in GNU MathProg, was implemented. (Brief instructions how to use + glpsol can be found in the GNU MathProg documentation.) + + The GLPK/L modeling language is no more supported. The reason is + that GNU MathProg being much more powerful completely supersedes + all features of GLPK/L. + +GLPK 3.3 (release date: Mar 25, 2003) + + LP PRESOLVER + ------------ + + Now the routine lpx_simplex (which is a driver to the simplex + method for solving LP) is provided with the built-in LP + presolver, which is a program that transforms the original LP + problem to an equivalent LP problem, which may be easier for + solving with the simplex method than the original one. Once the + transformed LP has been solver, the presolver transforms its + basic solution back to a corresponding basic solution of the + original problem. For details about this feature please see the + GLPK reference manual. + + Currently the LP presolver implements the following features: + * removing empty rows; + * removing empty columns; + * removing free rows; + * removing fixed columns; + * removing row singletons, which have the form of equations; + * removing row singletons, which have the form of inequalities; + * removing column singletons, which are implied slack variables; + * fixing and removing column singletons, which are implied free + variables; + * removing forcing rows that involves fixing and removing the + corresponding columns; + * checking for primal and dual infeasibilities. + + The LP presolver is also used by default in the stand-alone + program glpsol. In order *not* to use it, the option --nopresol + should be specified in the command-line. + + CHANGES IN GLPK/L + ----------------- + + The syntax and semantics of the GLPK/L modeling language was + changed to allow declaration of "interval" sets. This means that + now the user can declare a set, for example, as: + + set task = [8:11]; + + that is exactly equivalent to the following declaration: + + set task = (task_8, task_9, task_10, task_11); + + For details see the language description. + + JAVA INTERFACE + -------------- + + Now GLPK includes the package GLPK JNI (Java Native Interface) + that implements Java binding for GLPK. It allows Java programs + to utilize GLPK in solving LP and MIP problems. For details see + a brief user's guide in the subdirectory contrib/java-binding. + This package was developed and programmed by Yuri Victorovich + , who contributed it to GLPK. + +GLPK 3.2.4 (release date: Feb 18, 2003) + + This is a bug-fix release. For details see ChangeLog. + +GLPK 3.2.3 (release date: Nov 11, 2002) + + A new implementation of the api routine lpx_integer which now + is based on the b&b driver (which is based on the implicit + enumeration suite) was included in the package. This new + implementation has exactly the same functionality as the old + version, so all changes are transparent to the api user. + + Four new api routines were included in the package: + lpx_check_kkt checks Karush-Kuhn-Tucker optmality conditions; + lpx_read_bas reads predifined basis in MPS format; + lpx_write_bas writes current basis in MPS format; + lpx_write_lpt writes problem data in CPLEX LP format. + + Also other minor improvements were made (for details see the + file 'ChangeLog'). + +GLPK 3.2.2 (release date: Oct 14, 2002) + + The api routine lpx_read_lpt was included in the package. It + is similar to the routine lpx_read_mps and intended to read + LP/MIP data prepared in CPLEX LP format. Description of this + format is given in the GLPK reference manual, a new edition of + which was also included in the distribution (see the files + 'refman.latex', 'refman.dvi', 'refman.ps' in the subdirectory + 'doc'). In order to use data files in CPLEX LP format with the + solver glpsol the option '--lpt' should be specified in the + command line. + + Several bugs were fixed and some minor improvements were made + (for details see the file 'ChangeLog'). + +GLPK 3.2.1 (release date: Aug 12, 2002) + + Now GLPK includes a preliminary implementation of the + branch-and-cut framework, which is a set of data structures and + routines intended for developing branch-and-cut methods for + solving mixed-integer and combinatorial optimization problems. + + Detailed decsription of the branch-and-cut framework is given in + the document "GLPK: A Preliminary Implementation of the + Branch-And-Cut Framework" included in the distribution (see the + file 'brcut.txt' in the subdirectory 'doc'). + + In order to illustrate how the GLPK branch-and-cut framework + can be used for solving a particular optimization problem there + is an example included in the package. This is a stand-alone + program, TSPSOL, which is intended for solving to optimality the + symmetric Traveling Salesman Problem (TSP), a classical problem + of the combinatorial optimization (see the file 'tspsol.c' in + the subdirectory 'sample'). + +GLPK 3.2 (release date: Jul 15, 2002) + + New edition of the document "GLPK: Reference Manual" was + included (see the files 'refman.latex', 'refman.dvi', and + 'refman.ps' in the subdirectory 'doc'). + + New edition of the document "GLPK: Modeling Language GLPK/L" was + included (see the files 'lang.latex', 'lang.dvi', and 'lang.ps' + in the subdirectory 'doc'). + + The following new API routines were added to the package: + + lpx_transform_row (transform explicitly specified row); + lpx_transform_col (transform explicitly specified column); + lpx_prim_ratio_test (perform primal ratio test); + lpx_dual_ratio_test (perform dual ratio test); + lpx_interior (solve LP problem using interior point method); + lpx_get_ips_stat (query status of interior point solution); + lpx_get_ips_row (obtain row interior point solution); + lpx_get_ips_col (obtain column interior point solution); + lpx_get_ips_obj (obtain interior point value of obj.func.); + lpx_read_lpm (read LP/MIP model written in GLPK/L); + lpx_write_mps (write problem data using MPS format); + lpx_print_ips (print interior point solution). + + Detailed description of all these new API routines are given in + the new edition of the reference manual. + + New version of the stand-alone solver glpsol (which is based on + the new API) was implemented. + + So long as the new API (introduced in glpk 3.0) now provides + all the functions, which were provided by the old API, the old + API routines were removed from the package at all. + +GLPK 3.1 (release date: May 27, 2002) + + A preliminary implementation of new API routines was completed + and included in the package. + + These new API routines provide much more flexible interaction + between the application program, LP/MIP problem instances, and + solver routines. Based on completely changed data structures + they are, however, similar to the API routines and provide the + same functionality. Please note that three routines, namely, + solving LPs using interior point method, reading model written + in the GLPK/L modeling language, and writing problem data in + the MPS format, are not implemented in the new API, however, + these routines are planned to be implemented in the next version + of the package. + + A description of the new API routines is given in the document + "GLPK Reference Manual", a draft edition of which is included + in the package (see the files 'refman.latex', 'refman.dvi', and + 'refman.ps' in the subdirectory 'doc'). + + Although the old API routines are kept in the package, they are + no longer supported and will be removed in the future. + +GLPK 3.0.8 (release date: May 13, 2002) + + A preliminary implementation of new API routines was included + in the package. These new API routines are intended to provide + much more flexible interaction between the application program, + LP/MIP problem and solver routines. See the document "New GLPK + API Routines" (the file 'newapi.txt' in the subdirectory 'doc') + also included in the package. + + The api routines glp_simplex2, glp_call_ipm1, glp_call_bbm1 were + renamed, respectively, to glp_simplex, glp_interior, glp_integer + in order to reflect changes in implementation. The api routines + glp_call_rsm1, glp_simplex1, glp_pivot_in, glp_pivout_out were + removed from the package since they are completely supreseded by + the new API routines (however, these routines still can be found + in the subdirectory 'oldsrc'). Please consult a new edition of + the document "GLPK User's Guide" about all these changes in the + existing api routines. + + The document "GLPK Library Reference" was removed from the + package (into the subdirectory 'oldsrc') since it describes the + obsolete library routines, most of which are no longer used. + +GLPK 3.0.7 (release date: Apr 22, 2002) + + A new, more efficient implementation of the primal/dual simplex + method was included in the package. Due to some improvements the + simplex-based solver allows solving many LP problems faster and + provides more reliable results. Note that the new implementation + is currently incomplete and available only via the api routine + glp_simplex2. + + All the changes are transparent on API level. + +GLPK 3.0.6 (release date: Mar 28, 2002) + + New version of LU-factorization and basis maintenance routines + (based on Forrest-Tomlin updating technique) was implemented. + Since these new routines functionally supersede some routines + (which implement other forms of the basis matrix) and make them + obsolete, the latter were removed from the package (they still + can be found in the subdirectory 'oldsrc'). + + All the changes are transparent on API level. + +GLPK 3.0.5 (release date: Jan 29, 2002) + + New edition of the document "GLPK User's Guide" was included in + the distribution. Now it describes all additional API routines, + which were recently added to the package. + + Structure of the package was re-organized in order to make its + maintenance easier (all small files in the subdurectory 'source' + were merged in bigger units). These changes are transparent for + the user. + +GLPK 3.0.4 (release date: Dec 10, 2001) + + A new, more efficient implementation of the two-phase primal + simplex method was included in the package. Due to some new + features (an advanced initial basis, projected steepest edge, + recursive updating values and reduced costs) the new LP solver + is faster and numerically more stable than the old one. + + The new LP solver is available as API routine glp_simplex2 and + has the same purpose as API routine glp_call_rsm1. For detailed + specification see the file 'newapi.txt' in the directory 'doc'. + + Now the new LP solver is also used by default to solve an + initial LP problem in the branch-and-bound routine glp_call_bbm1 + instead the routine rsm1_driver. Note that the branch-and-bound + procedure itself is still based on rsm1_driver. + + The new LP solver is also used as default solver in GLPSOL for + solving LP and MIP problems. In order to choose the old solver + the option '--old-sim' can be specified in the command line. + +GLPK 3.0.3 (release date: Oct 03, 2001) + + Some minor changes were made in the simplex method routines in + order to improve numerical stability of the method. + +GLPK 3.0.2 (release date: Sep 24, 2001) + + A new implementation of the basis maintaining routines was + included in the package. These routines, which are based on so + called FHV-factorization (a variety of LU-factorization) of the + basis matrix and Gustavson's data structures, allows performing + the main operations faster at the expense of some worsening + numerical accuracy. + + AFI (Advanced Form of the Inverse), which is the form of the + basis matrix based on FHV-factorization, is available via the + parameter form = 3 (on API level) or via the option --afi (in + GLPSOL solver). + +GLPK 3.0.1 (release date: Aug 01, 2001) + + Old GLPK API routines have been removed from the package. + + New GLPK API routines were added: + + - scaling routines; + + - a routine for writing problem data in MPS format; + + - a comprehensive driver to the simplex method; + + - basis maintaining routines. + + A description of the new API routines is given in the document + "Additional GLPK API Routines". This document is included into + the distribution in plain text format (see the file 'newapi.txt' + in the subdirectory 'doc'). + + Now the distribution includes a non-trivial example of using + GLPK as a base LP solver for Concorde, a well known program that + solves Traveling Salesman Problem (TSP). For further details see + comments in the file 'sample/lpglpk30.c'. + +GLPK 3.0 (release date: Jul 19, 2001) + + Now GLPK is provided with new API, which being more flexible + can be used in more complex algorithmic schemes. + + New edition of the document "GLPK User's Guide" is included in + the distribution. Now it completely corresponds to the new GLPK + API routines. + + Old API routines are not removed yet from the package, however + they became obsolete and therefore should not be used. Since now + the header glpk.h corresponds to new API, in order to compile + existing programs that use old GLPK API routines the statement + + #define GLP_OLD_API + + should be inserted before the statement + + #include "glpk.h" + +GLPK 2.4.1 (release date: Jun 14, 2001) + + The document "Modeling language GLPK/L" is included into the + distribution in texinfo format. + + New edition of the document "GLPK User's Guide" is included in + the distribution. Now it describes all additional API routines + which were recently added to the package. + +GLPK 2.4 (release date: May 10, 2001) + + Now GLPK includes an implementation of a preliminary version + of the GLPK/L modeling language. This language is intended for + writing mathematcal programming models. The name GLPK/L is + derived from GNU Linear Programming Kit Language. + + A brief description of the GLPK/L language is given in the + document "GLPK/L Modeling Language: A Brief Description". This + document is included into the distribution in plain text format + (see the file 'language.txt' in the subdirectory 'doc'). + + The language processor (which is a program that analyzes model + description written in GLPK/L and translates it to internal data + structures) is available as the GLPK API routine. + + The stand-alone solver GLPSOL now is able: a) to process model + descriptions written in the GLPK/L language; b) to solve pure LP + problems using the interior point method (therefore the program + GLPIPM was removed from the package). + +GLPK 2.3 (release date: Apr 09, 2001) + + New edition of the document "GLPK User's Guide" is included in + the distribution. Now it describes all additional API routines + which were recently added to the package. + + The MIP solver was fully re-programmed in order to improve its + robustness and performance. In particular, a basis recovering + procedure was implemented (this procedure allows switching to + the primal simplex method in case when the dual simplex method + fails). + +GLPK 2.2 (release date: Mar 15, 2001) + + Now GLPK includes a tentative implementation of the + branch-and-bound procedure based on the dual simplex method for + mixed integer linear programming (MIP). + + Complete description of this new feature of the package is given + in the preliminary document "Mixed Integer Linear Programming + Using GLPK Version 2.2 (Supplement to GLPK User's Guide)". This + document is included into the distribution in plain text format + (see the file 'mip.txt' in the subdirectory 'doc'). + + The MIP solver (glp_integer) can be used as GLPK API routine in + the same way as the pure LP solver (glp_simplex). + + The stand-alone program 'glpsol' is now able to solve LP as well + as MIP problems. + + Note that the current version of GLPK MIP solver is based on + easiest heuristics for branching and backtrackng. Therefore the + solver is fit mainly for MIP problems which are not very hard + and have few integer variables. + +GLPK 2.1 (release date: Feb 19, 2001) + + The document "GLPK Implementation of the Revised Simplex Method" + is included into the distribution. This document describes most + of routines related to the revised simplex method. + +GLPK 2.0 (release date: Jan 25, 2001) + + Now GLPK includes a tentative implementation of the primal-dual + interior point method for large-scale linear programming. + + The interior point solver can be used as GLPK API routine in the + same manner as the simplex method solver (glp_simplex): + + ret = glp_interior(); + + Note that currently the interior point solver implemented in + GLPK doesn't include many important features, in particular: + + * it can't process dense columns; therefore if the problem has + dense columns, the solving will be extremely inefficient; + + * it has no special features against numerical unstability; + some problems may cause premature termination of the solving + when the matrix A*D*A' becomes ill-conditioned; + + * it computes only values of primal (auxiliary and structural) + variables and doesn't compute values of dual variables (i.e. + reduced costs) which are just set to zero; + + * it doesn't identify optimal basis corresponding to the found + interior point solution; all variables in the found solution + are just marked as basic variables. + + GLPK also includes a stand-alone program 'glpipm' which is a + demo based on the interior point method. It may be used in the + same way as the program 'glpsol' that is based on the simplex + method. diff -r d59bea55db9b -r c445c931472f README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,39 @@ + Olga K. gewidmet + +GLPK (GNU Linear Programming Kit) Version 4.45 + +Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +2009, 2010 Andrew Makhorin, Department for Applied Informatics, +Moscow Aviation Institute, Moscow, Russia. All rights reserved. +E-mail: . + +GLPK is part of the GNU Project released under the aegis of GNU. + +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. + +See the file COPYING for the GNU General Public License. + +See the file INSTALL for compilation and installation instructions. + +The GLPK package is a set of routines written in ANSI C and organized +in the form of a callable library. This package is intended for solving +large-scale linear programming (LP), mixed integer linear programming +(MIP), and other related problems. + +The GLPK package includes the following main components: + +* implementation of the simplex method; +* implementation of the exact simplex method based on bignum (rational) + arithmetic; +* implementation of the primal-dual interior-point method; +* implementation of the branch-and-cut method; +* application program interface (API); +* GNU MathProg modeling language (a subset of AMPL); +* GLPSOL, a stand-alone LP/MIP solver. + +See GLPK webpage . + +Please report bugs to . diff -r d59bea55db9b -r c445c931472f THANKS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/THANKS Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,183 @@ +Noli Sicad for testing glpk under Mac OS. + +Pietro Scionti for report typos found in +the reference manual. + +Noli Sicad for example model in MathProg. + +Nigel Galloway for example model in +MathProg. + +Xypron for contribution and testing batch scripts +to build Glpk with MS Visual Studio 2010. + +Xypron for improving the SQL table driver. + +Stefan Vigerske for bug report. + +Uday Venkatadri for bug report. + +Xypron for bug fixing and some improvments in the +FPUMP module. + +Niels Klitgord for bug report. + +Thomas Kahle for some suggestions. + +Jonathan Senning for bug report. + +Xypron for testing GLPK on 64-bit platforms and +for maintaining Windows versions of the package. + +Nelson H. F. Beebe for bug report. + +Larry D'Agostino for example model in +MathProg. + +Marco Atzeri for bug report. + +Robert Wood for example model in MathProg. + +Sebastien Briais for bug report. + +Kelly Westbrooks for suggestions. + +Nigel Galloway for example models in +MathProg. + +Xypron for bug patch. + +Sebastian Nowozin for example models in MathProg. + +Stefan Vigerske for bug report. + +Luiz Bettoni for some suggestions. + +Nigel Galloway for bug report. + +Peter Ingerfeld for bug report. + +Heinrich Schuchardt for contribution of +makefiles and testing the package under 64-bit Windows. + +Oscar Gustafsson for contribution of a new version +of the routine to write data in OPB (pseudo boolean) format. + +Heinrich Schuchardt for some patches for +the MathProg translator. + +Markus Pilz for bug report. + +Anne-Laurence Putz for bug +report. + +Robbie Morrison for correcting the glpk manual. + +Axel Simon for bug report. + +Vijay Patil for testing the package under +Windows XP. + +Heinrich Schuchardt for contribution of +two MathProg table drivers for iODBC and MySQL. + +Rafael Laboissiere for useful advices concerning +shared library support under GNU/Linux. + +Nigel Galloway for an example program +in C#. + +Enric Rodriguez for bug report. + +Nigel Galloway for an example MathProg +model. + +Roberto Bagnara (Department of Mathematics, +University of Parma, Italy) for bug report. + +Peter T. Breuer for bug report. + +Graham Rockwell for bug report. + +Cedric[FR] for bug report. + +Cameron Kellough for bug report. + +Oscar Gustafsson for contribution of a routine to +write data in OPB (pseudo boolean) format. + +Boris Wirtz for bug report. + +Sebastian Nowozin for three example MathProg +models. + +Rafael Laboissiere for useful advices concerning +shared library support. + +Dr. Harley Mackenzie for two example MathProg +models (curve fitting problem). + +Minh Ha Duong for fixing doc typos. + +Brady Hunsaker for some suggestions concerning +MIP routines. + +Karel Zimmermann for bug report. + +Christophe Caron for bug report. + +Nicolo Giorgetti for contribution of GLPKMEX, +a Matlab MEX interface. + +Harley Mackenzie for contribution of GLPK FAQ. + +Andre Girard for bug report. + +Morten Welinder for bug report. + +Brady Hunsaker for contribution of a routine +for bounds sensitivity analysis. + +Chris Rosebrugh for contribution of a new version of +GLPK JNI (Java Native Interface). + +Ivo van Baren for contribution of GLPK DELI +(Delphi Interface). + +Andrew Hamilton-Wright for bug report. + +Jiri Spitz for bug report. + +Giles Thompson for bug report. + +Sebastien de Menten for bug report. + +Kendall Demaree for bug report. + +Bernhard Schmidt for bug report. + +Yuri Victorovich for contribution of GLPK Java binding. + +Olivier for bug report. + +Kjell Eikland for bug report. + +Ivan Luzzi for comments concerning CPLEX LP format. + +Peter Lee for example LP model and bug report. + +Morten Welinder for bug report. + +Vlahos Kiriakos for bug report. + +Flavio Keidi Miyazawa for bug report. + +Hans Schwengeler for bug report. + +Sami Farin for bug report. + +Peter A. Huegler for bug report. + +Alexandre Oliva for bug report. + +Andrew Hood for bug report. diff -r d59bea55db9b -r c445c931472f configure.ac --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configure.ac Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,159 @@ +dnl Process this file with autoconf to produce a configure script + +AC_INIT([GLPK], [4.45], [bug-glpk@gnu.org]) + +AC_CONFIG_SRCDIR([include/glpk.h]) + +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE + +AC_CONFIG_HEADERS([config.h]) + +AC_ARG_WITH(gmp, +AC_HELP_STRING([--with-gmp], + [use GNU MP bignum library [[default=no]]]), + [case $withval in + yes | no) ;; + *) AC_MSG_ERROR([invalid value `$withval' for --with-gmp]);; + esac], + [with_gmp=no]) + +AC_ARG_WITH(zlib, +AC_HELP_STRING([--with-zlib], + [use zlib data compression library [[default=no]]]), + [case $withval in + yes | no) ;; + *) AC_MSG_ERROR([invalid value `$withval' for --with-zlib]);; + esac], + [with_zlib=no]) + +AC_ARG_ENABLE(dl, +AC_HELP_STRING([--enable-dl], + [enable shared library support [[default=no]]]), + [case $enableval in + yes | ltdl | dlfcn | no) ;; + *) AC_MSG_ERROR([invalid value `$enableval' for --enable-dl]);; + esac], + [enable_dl=no]) + +AC_ARG_ENABLE(odbc, +AC_HELP_STRING([--enable-odbc], + [enable MathProg ODBC support [[default=no]]]), + [case $enableval in + yes | unix | no) ;; + *) AC_MSG_ERROR([invalid value `$enableval' for --enable-odbc]);; + esac], + [enable_odbc=no]) + +AC_ARG_ENABLE(mysql, +AC_HELP_STRING([--enable-mysql], + [enable MathProg MySQL support [[default=no]]]), + [case $enableval in + yes | no) ;; + *) AC_MSG_ERROR([invalid value `$enableval' for --enable-mysql]);; + esac], + [enable_mysql=no]) + +dnl Disable unnecessary libtool tests +define([AC_LIBTOOL_LANG_CXX_CONFIG], [:]) +define([AC_LIBTOOL_LANG_F77_CONFIG], [:]) +define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:]) + +dnl Check for programs +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LIBTOOL + +dnl Check for math library +AC_CHECK_LIB([m], [exp]) + +dnl Check for header +AC_CHECK_HEADER([sys/time.h], + AC_DEFINE([HAVE_SYS_TIME_H], [1], [N/A])) + +dnl Check for gettimeofday function +AC_CHECK_FUNC([gettimeofday], + AC_DEFINE([HAVE_GETTIMEOFDAY], [1], [N/A])) + +AC_MSG_CHECKING([whether to use GNU MP bignum library]) +if test "$with_gmp" = "yes"; then + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_GMP], [1], [N/A]) + LIBS="-lgmp $LIBS" +else + AC_MSG_RESULT([no]) +fi + +AC_MSG_CHECKING([whether to use zlib data compression library]) +if test "$with_zlib" = "yes"; then + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_ZLIB], [1], [N/A]) + LIBS="-lz $LIBS" +else + AC_MSG_RESULT([no]) +fi + +AC_MSG_CHECKING([whether to enable shared library support]) +if test "$enable_dl" = "yes"; then + AC_MSG_RESULT([ltdl]) + AC_DEFINE([HAVE_LTDL], [1], [N/A]) + LIBS="-lltdl $LIBS" +elif test "$enable_dl" = "ltdl"; then + AC_MSG_RESULT([ltdl]) + AC_DEFINE([HAVE_LTDL], [1], [N/A]) + LIBS="-lltdl $LIBS" +elif test "$enable_dl" = "dlfcn"; then + AC_MSG_RESULT([dlfcn]) + AC_DEFINE([HAVE_DLFCN], [1], [N/A]) +else + AC_MSG_RESULT([no]) +fi + +case $host_os in + darwin* | macosx*) + LIBIODBC="libiodbc.dylib" + LIBODBC="libodbc.dylib" + LIBMYSQL="libmysqlclient.dylib" + ;; + *) + LIBIODBC="libiodbc.so" + LIBODBC="libodbc.so" + LIBMYSQL="libmysqlclient.so" + ;; +esac + +AC_MSG_CHECKING([whether to enable MathProg ODBC support]) +if test "$enable_odbc" = "yes"; then + if test "$enable_dl" = "no"; then + AC_MSG_ERROR([--enable-odbc requires --enable-dl]) + fi + AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED([ODBC_DLNAME], ["$LIBIODBC"], [N/A]) +elif test "$enable_odbc" = "unix"; then + if test "$enable_dl" = "no"; then + AC_MSG_ERROR([--enable-odbc requires --enable-dl]) + fi + AC_MSG_RESULT([unix]) + AC_DEFINE_UNQUOTED([ODBC_DLNAME], ["$LIBODBC"], [N/A]) +else + AC_MSG_RESULT([no]) +fi + +AC_MSG_CHECKING([whether to enable MathProg MySQL support]) +if test "$enable_mysql" = "yes"; then + if test "$enable_dl" = "no"; then + AC_MSG_ERROR([--enable-mysql requires --enable-dl]) + fi + AC_MSG_RESULT([yes]) + CPPFLAGS="-I/usr/include/mysql $CPPFLAGS" + AC_DEFINE_UNQUOTED([MYSQL_DLNAME], ["$LIBMYSQL"], [N/A]) +else + AC_MSG_RESULT([no]) +fi + +AC_CONFIG_FILES( + [include/Makefile src/Makefile examples/Makefile Makefile]) +AC_OUTPUT + +dnl eof diff -r d59bea55db9b -r c445c931472f doc/glpk.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,128 @@ +%* glpk.tex *% + +%*********************************************************************** +% This code is part of GLPK (GNU Linear Programming Kit). +% +% Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +% 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +% Moscow Aviation Institute, Moscow, Russia. All rights reserved. +% E-mail: . +% +% GLPK is free software: you can redistribute it and/or modify it +% under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% GLPK is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +% or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License +% along with GLPK. If not, see . +%*********************************************************************** + +\documentclass[11pt]{report} +\usepackage{amssymb} +\usepackage{lscape} +\usepackage[dvipdfm,linktocpage,colorlinks,linkcolor=blue, +urlcolor=blue]{hyperref} +\usepackage[all]{xy} + +\renewcommand\contentsname{\sf\bfseries Contents} +\renewcommand\chaptername{\sf\bfseries Chapter} +\renewcommand\appendixname{\sf\bfseries Appendix} + +\begin{document} + +\thispagestyle{empty} + +\begin{center} + +\vspace*{1in} + +\begin{huge} +\sf\bfseries GNU Linear Programming Kit +\end{huge} + +\vspace{0.5in} + +\begin{LARGE} +\sf Reference Manual +\end{LARGE} + +\vspace{0.5in} + +\begin{LARGE} +\sf for GLPK Version 4.45 +\end{LARGE} + +\vspace{0.5in} +\begin{Large} +\sf (DRAFT, December 2010) +\end{Large} +\end{center} + +\newpage + +\vspace*{1in} + +\vfill + +\noindent +The GLPK package is part of the GNU Project released under the aegis of +GNU. + +\medskip \noindent +Copyright \copyright{} 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. + +\medskip \noindent +Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +02110-1301, USA. + +\medskip \noindent +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +\medskip \noindent +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +entire resulting derived work is distributed under the terms of +a permission notice identical to this one. + +\medskip \noindent +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. + +\tableofcontents + +\include{glpk01} + +\include{glpk02} + +\include{glpk03} + +\include{glpk04} + +\include{glpk05} + +\include{glpk06} + +\appendix + +\include{glpk07} + +\include{glpk08} + +\include{glpk09} + +\include{glpk10} + +\include{glpk11} + +\include{glpk12} + +\end{document} diff -r d59bea55db9b -r c445c931472f doc/glpk01.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk01.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,348 @@ +%* glpk01.tex *% + +\chapter{Introduction} + +GLPK (\underline{G}NU \underline{L}inear \underline{P}rogramming +\underline{K}it) is a set of routines written in the ANSI C programming +language and organized in the form of a callable library. It is intended +for solving linear programming (LP), mixed integer programming (MIP), +and other related problems. + +\section{LP problem} +\label{seclp} + +GLPK assumes the following formulation of {\it linear programming (LP)} +problem: + +\medskip + +\noindent +\hspace{.5in} minimize (or maximize) +$$z = c_1x_{m+1} + c_2x_{m+2} + \dots + c_nx_{m+n} + c_0 \eqno (1.1)$$ +\hspace{.5in} subject to linear constraints +$$ +\begin{array}{r@{\:}c@{\:}r@{\:}c@{\:}r@{\:}c@{\:}r} +x_1&=&a_{11}x_{m+1}&+&a_{12}x_{m+2}&+ \dots +&a_{1n}x_{m+n} \\ +x_2&=&a_{21}x_{m+1}&+&a_{22}x_{m+2}&+ \dots +&a_{2n}x_{m+n} \\ +\multicolumn{7}{c} +{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .} \\ +x_m&=&a_{m1}x_{m+1}&+&a_{m2}x_{m+2}&+ \dots +&a_{mn}x_{m+n} \\ +\end{array} \eqno (1.2) +$$ +\hspace{.5in} and bounds of variables +$$ +\begin{array}{r@{\:}c@{\:}c@{\:}c@{\:}l} +l_1&\leq&x_1&\leq&u_1 \\ +l_2&\leq&x_2&\leq&u_2 \\ +\multicolumn{5}{c}{.\ \ .\ \ .\ \ .\ \ .}\\ +l_{m+n}&\leq&x_{m+n}&\leq&u_{m+n} \\ +\end{array} \eqno (1.3) +$$ +where: $x_1, x_2, \dots, x_m$ are auxiliary variables; +$x_{m+1}, x_{m+2}, \dots, x_{m+n}$ are\linebreak structural variables; +$z$ is the objective function; +$c_1, c_2, \dots, c_n$ are objective coefficients; +$c_0$ is the constant term (``shift'') of the objective function; +$a_{11}, a_{12}, \dots, a_{mn}$ are constraint coefficients; +$l_1, l_2, \dots, l_{m+n}$ are lower bounds of variables; +$u_1, u_2, \dots, u_{m+n}$ are upper bounds of variables. + +Auxiliary variables are also called {\it rows}, because they correspond +to rows of the constraint matrix (i.e. a matrix built of the constraint +coefficients). Similarly, structural variables are also called +{\it columns}, because they correspond to columns of the constraint +matrix. + +Bounds of variables can be finite as well as infinite. Besides, lower +and upper bounds can be equal to each other. Thus, the following types +of variables are possible: +\begin{center} +\begin{tabular}{r@{}c@{}ll} +\multicolumn{3}{c}{Bounds of variable} & Type of variable \\ +\hline +$-\infty <$ &$\ x_k\ $& $< +\infty$ & Free (unbounded) variable \\ +$l_k \leq$ &$\ x_k\ $& $< +\infty$ & Variable with lower bound \\ +$-\infty <$ &$\ x_k\ $& $\leq u_k$ & Variable with upper bound \\ +$l_k \leq$ &$\ x_k\ $& $\leq u_k$ & Double-bounded variable \\ +$l_k =$ &$\ x_k\ $& $= u_k$ & Fixed variable \\ +\end{tabular} +\end{center} +\noindent +Note that the types of variables shown above are applicable to +structural as well as to auxiliary variables. + +To solve the LP problem (1.1)---(1.3) is to find such values of all +structural and auxiliary variables, which: + +$\bullet$ satisfy to all the linear constraints (1.2), and + +$\bullet$ are within their bounds (1.3), and + +$\bullet$ provide the smallest (in case of minimization) or the largest +(in case of maximization) value of the objective function (1.1). + +\section{MIP problem} + +{\it Mixed integer linear programming (MIP)} problem is LP problem in +which some variables are additionally required to be integer. + +GLPK assumes that MIP problem has the same formulation as ordinary +(pure) LP problem (1.1)---(1.3), i.e. includes auxiliary and structural +variables, which may have lower and/or upper bounds. However, in case of +MIP problem some variables may be required to be integer. This +additional constraint means that a value of each {\it integer variable} +must be only integer number. (Should note that GLPK allows only +structural variables to be of integer kind.) + +\section{Using the package} + +\subsection{Brief example} + +In order to understand what GLPK is from the user's standpoint, +consider the following simple LP problem: + +\medskip + +\noindent +\hspace{.5in} maximize +$$z = 10 x_1 + 6 x_2 + 4 x_3$$ +\hspace{.5in} subject to +$$ +\begin{array}{r@{\:}c@{\:}r@{\:}c@{\:}r@{\:}c@{\:}r} +x_1 &+&x_2 &+&x_3 &\leq 100 \\ +10 x_1 &+& 4 x_2 & +&5 x_3 & \leq 600 \\ +2 x_1 &+& 2 x_2 & +& 6 x_3 & \leq 300 \\ +\end{array} +$$ +\hspace{.5in} where all variables are non-negative +$$x_1 \geq 0, \ x_2 \geq 0, \ x_3 \geq 0$$ + +At first this LP problem should be transformed to the standard form +(1.1)---(1.3). This can be easily done by introducing auxiliary +variables, by one for each original inequality constraint. Thus, the +problem can be reformulated as follows: + +\medskip + +\noindent +\hspace{.5in} maximize +$$z = 10 x_1 + 6 x_2 + 4 x_3$$ +\hspace{.5in} subject to +$$ +\begin{array}{r@{\:}c@{\:}r@{\:}c@{\:}r@{\:}c@{\:}r} +p& = &x_1 &+&x_2 &+&x_3 \\ +q& = &10 x_1 &+& 4 x_2 &+& 5 x_3 \\ +r& = &2 x_1 &+& 2 x_2 &+& 6 x_3 \\ +\end{array} +$$ +\hspace{.5in} and bounds of variables +$$ +\begin{array}{ccc} +\nonumber -\infty < p \leq 100 && 0 \leq x_1 < +\infty \\ +\nonumber -\infty < q \leq 600 && 0 \leq x_2 < +\infty \\ +\nonumber -\infty < r \leq 300 && 0 \leq x_3 < +\infty \\ +\end{array} +$$ +where $p, q, r$ are auxiliary variables (rows), and $x_1, x_2, x_3$ are +structural variables (columns). + +The example C program shown below uses GLPK API routines in order to +solve this LP problem.\footnote{If you just need to solve LP or MIP +instance, you may write it in MPS or CPLEX LP format and then use the +GLPK stand-alone solver to obtain a solution. This is much less +time-consuming than programming in C with GLPK API routines.} + +\newpage + +\begin{verbatim} +/* sample.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *lp; + int ia[1+1000], ja[1+1000]; + double ar[1+1000], z, x1, x2, x3; +s1: lp = glp_create_prob(); +s2: glp_set_prob_name(lp, "sample"); +s3: glp_set_obj_dir(lp, GLP_MAX); +s4: glp_add_rows(lp, 3); +s5: glp_set_row_name(lp, 1, "p"); +s6: glp_set_row_bnds(lp, 1, GLP_UP, 0.0, 100.0); +s7: glp_set_row_name(lp, 2, "q"); +s8: glp_set_row_bnds(lp, 2, GLP_UP, 0.0, 600.0); +s9: glp_set_row_name(lp, 3, "r"); +s10: glp_set_row_bnds(lp, 3, GLP_UP, 0.0, 300.0); +s11: glp_add_cols(lp, 3); +s12: glp_set_col_name(lp, 1, "x1"); +s13: glp_set_col_bnds(lp, 1, GLP_LO, 0.0, 0.0); +s14: glp_set_obj_coef(lp, 1, 10.0); +s15: glp_set_col_name(lp, 2, "x2"); +s16: glp_set_col_bnds(lp, 2, GLP_LO, 0.0, 0.0); +s17: glp_set_obj_coef(lp, 2, 6.0); +s18: glp_set_col_name(lp, 3, "x3"); +s19: glp_set_col_bnds(lp, 3, GLP_LO, 0.0, 0.0); +s20: glp_set_obj_coef(lp, 3, 4.0); +s21: ia[1] = 1, ja[1] = 1, ar[1] = 1.0; /* a[1,1] = 1 */ +s22: ia[2] = 1, ja[2] = 2, ar[2] = 1.0; /* a[1,2] = 1 */ +s23: ia[3] = 1, ja[3] = 3, ar[3] = 1.0; /* a[1,3] = 1 */ +s24: ia[4] = 2, ja[4] = 1, ar[4] = 10.0; /* a[2,1] = 10 */ +s25: ia[5] = 3, ja[5] = 1, ar[5] = 2.0; /* a[3,1] = 2 */ +s26: ia[6] = 2, ja[6] = 2, ar[6] = 4.0; /* a[2,2] = 4 */ +s27: ia[7] = 3, ja[7] = 2, ar[7] = 2.0; /* a[3,2] = 2 */ +s28: ia[8] = 2, ja[8] = 3, ar[8] = 5.0; /* a[2,3] = 5 */ +s29: ia[9] = 3, ja[9] = 3, ar[9] = 6.0; /* a[3,3] = 6 */ +s30: glp_load_matrix(lp, 9, ia, ja, ar); +s31: glp_simplex(lp, NULL); +s32: z = glp_get_obj_val(lp); +s33: x1 = glp_get_col_prim(lp, 1); +s34: x2 = glp_get_col_prim(lp, 2); +s35: x3 = glp_get_col_prim(lp, 3); +s36: printf("\nz = %g; x1 = %g; x2 = %g; x3 = %g\n", + z, x1, x2, x3); +s37: glp_delete_prob(lp); + return 0; +} + +/* eof */ +\end{verbatim} + +The statement \verb|s1| creates a problem object. Being created the +object is initially empty. The statement \verb|s2| assigns a symbolic +name to the problem object. + +The statement \verb|s3| calls the routine \verb|glp_set_obj_dir| in +order to set the optimization direction flag, where \verb|GLP_MAX| means +maximization. + +The statement \verb|s4| adds three rows to the problem object. + +The statement \verb|s5| assigns the symbolic name `\verb|p|' to the +first row, and the statement \verb|s6| sets the type and bounds of the +first row, where \verb|GLP_UP| means that the row has an upper bound. +The statements \verb|s7|, \verb|s8|, \verb|s9|, \verb|s10| are used in +the same way in order to assign the symbolic names `\verb|q|' and +`\verb|r|' to the second and third rows and set their types and bounds. + +The statement \verb|s11| adds three columns to the problem object. + +The statement \verb|s12| assigns the symbolic name `\verb|x1|' to the +first column, the statement \verb|s13| sets the type and bounds of the +first column, where \verb|GLP_LO| means that the column has an lower +bound, and the statement \verb|s14| sets the objective coefficient for +the first column. The statements \verb|s15|---\verb|s20| are used in the +same way in order to assign the symbolic names `\verb|x2|' and +`\verb|x3|' to the second and third columns and set their types, bounds, +and objective coefficients. + +The statements \verb|s21|---\verb|s29| prepare non-zero elements of the +constraint matrix (i.e. constraint coefficients). Row indices of each +element are stored in the array \verb|ia|, column indices are stored in +the array \verb|ja|, and numerical values of corresponding elements are +stored in the array \verb|ar|. Then the statement \verb|s30| calls +the routine \verb|glp_load_matrix|, which loads information from these +three arrays into the problem object. + +Now all data have been entered into the problem object, and therefore +the statement \verb|s31| calls the routine \verb|glp_simplex|, which is +a driver to the simplex method, in order to solve the LP problem. This +routine finds an optimal solution and stores all relevant information +back into the problem object. + +The statement \verb|s32| obtains a computed value of the objective +function, and the statements \verb|s33|---\verb|s35| obtain computed +values of structural variables (columns), which correspond to the +optimal basic solution found by the solver. + +The statement \verb|s36| writes the optimal solution to the standard +output. The printout may look like follows: + +{\footnotesize +\begin{verbatim} +* 0: objval = 0.000000000e+00 infeas = 0.000000000e+00 (0) +* 2: objval = 7.333333333e+02 infeas = 0.000000000e+00 (0) +OPTIMAL SOLUTION FOUND + +z = 733.333; x1 = 33.3333; x2 = 66.6667; x3 = 0 +\end{verbatim} + +} + +Finally, the statement \verb|s37| calls the routine +\verb|glp_delete_prob|, which frees all the memory allocated to the +problem object. + +\subsection{Compiling} + +The GLPK package has the only header file \verb|glpk.h|, which should +be available on compiling a C (or C++) program using GLPK API routines. + +If the header file is installed in the default location +\verb|/usr/local/include|, the following typical command may be used to +compile, say, the example C program described above with the GNU C +compiler: + +\begin{verbatim} + $ gcc -c sample.c +\end{verbatim} + +If \verb|glpk.h| is not in the default location, the corresponding +directory containing it should be made known to the C compiler through +\verb|-I| option, for example: + +\begin{verbatim} + $ gcc -I/foo/bar/glpk-4.15/include -c sample.c +\end{verbatim} + +In any case the compilation results in an object file \verb|sample.o|. + +\subsection{Linking} + +The GLPK library is a single file \verb|libglpk.a|. (On systems which +support shared libraries there may be also a shared version of the +library \verb|libglpk.so|.) + +If the library is installed in the default +location \verb|/usr/local/lib|, the following typical command may be +used to link, say, the example C program described above against with +the library: + +\begin{verbatim} + $ gcc sample.o -lglpk -lm +\end{verbatim} + +If the GLPK library is not in the default location, the corresponding +directory containing it should be made known to the linker through +\verb|-L| option, for example: + +\begin{verbatim} + $ gcc -L/foo/bar/glpk-4.15 sample.o -lglpk -lm +\end{verbatim} + +Depending on configuration of the package linking against with the GLPK +library may require the following optional libraries: + +\bigskip + +\begin{tabular}{@{}ll} +\verb|-lgmp| & the GNU MP bignum library; \\ +\verb|-lz| & the zlib data compression library; \\ +\verb|-lltdl| & the GNU ltdl shared support library. \\ +\end{tabular} + +\bigskip + +\noindent +in which case corresponding libraries should be also made known to the +linker, for example: + +\begin{verbatim} + $ gcc sample.o -lglpk -lz -lltdl -lm +\end{verbatim} + +For more details about configuration options of the GLPK package see +Appendix \ref{install}, page \pageref{install}. + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk02.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk02.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,3406 @@ +%* glpk02.tex *% + +\chapter{Basic API Routines} + +This chapter describes GLPK API routines intended for using in +application programs. + +\subsubsection*{Library header} + +All GLPK API data types and routines are defined in the header file +\verb|glpk.h|. It should be included in all source files which use +GLPK API, either directly or indirectly through some other header file +as follows: + +\begin{verbatim} + #include +\end{verbatim} + +\subsubsection*{Error handling} + +If some GLPK API routine detects erroneous or incorrect data passed by +the application program, it writes appropriate diagnostic messages to +the terminal and then abnormally terminates the application program. +In most practical cases this allows to simplify programming by avoiding +numerous checks of return codes. Thus, in order to prevent crashing the +application program should check all data, which are suspected to be +incorrect, before calling GLPK API routines. + +Should note that this kind of error handling is used only in cases of +incorrect data passed by the application program. If, for example, the +application program calls some GLPK API routine to read data from an +input file and these data are incorrect, the GLPK API routine reports +about error in the usual way by means of the return code. + +\subsubsection*{Thread safety} + +Currently GLPK API routines are non-reentrant and therefore cannot be +used in multi-threaded programs. + +\subsubsection*{Array indexing} + +Normally all GLPK API routines start array indexing from 1, not from 0 +(except the specially stipulated cases). This means, for example, that +if some vector $x$ of the length $n$ is passed as an array to some GLPK +API routine, the latter expects vector components to be placed in +locations \verb|x[1]|, \verb|x[2]|, \dots, \verb|x[n]|, and the location +\verb|x[0]| normally is not used. + +In order to avoid indexing errors it is most convenient and most +reliable to declare the array \verb|x| as follows: + +\begin{verbatim} + double x[1+n]; +\end{verbatim} + +\noindent +or to allocate it as follows: + +\begin{verbatim} + double *x; + . . . + x = calloc(1+n, sizeof(double)); +\end{verbatim} + +\noindent +In both cases one extra location \verb|x[0]| is reserved that allows +passing the array to GLPK routines in a usual way. + +\section{Problem object} + +All GLPK API routines deal with so called {\it problem object}, which +is a program object of type \verb|glp_prob| and intended to represent +a particular LP or MIP instance. + +The type \verb|glp_prob| is a data structure declared in the header +file \verb|glpk.h| as follows: + +\begin{verbatim} + typedef struct { ... } glp_prob; +\end{verbatim} + +Problem objects (i.e. program objects of the \verb|glp_prob| type) are +allocated and managed internally by the GLPK API routines. The +application program should never use any members of the \verb|glp_prob| +structure directly and should deal only with pointers to these objects +(that is, \verb|glp_prob *| values). + +\pagebreak + +The problem object consists of five segments, which are: + +$\bullet$ problem segment, + +$\bullet$ basis segment, + +$\bullet$ interior point segment, + +$\bullet$ MIP segment, and + +$\bullet$ control parameters and statistics segment. + +\subsubsection*{Problem segment} + +The {\it problem segment} contains original LP/MIP data, which +corresponds to the problem formulation (1.1)---(1.3) (see Section +\ref{seclp}, page \pageref{seclp}). It includes the following +components: + +$\bullet$ rows (auxiliary variables), + +$\bullet$ columns (structural variables), + +$\bullet$ objective function, and + +$\bullet$ constraint matrix. + +Rows and columns have the same set of the following attributes: + +$\bullet$ ordinal number, + +$\bullet$ symbolic name (1 up to 255 arbitrary graphic characters), + +$\bullet$ type (free, lower bound, upper bound, double bound, fixed), + +$\bullet$ numerical values of lower and upper bounds, + +$\bullet$ scale factor. + +{\it Ordinal numbers} are intended for referencing rows and columns. +Row ordinal numbers are integers $1, 2, \dots, m$, and column ordinal +numbers are integers $1, 2, \dots, n$, where $m$ and $n$ are, +respectively, the current number of rows and columns in the problem +object. + +{\it Symbolic names} are intended for informational purposes. They also +can be used for referencing rows and columns. + +{\it Types and bounds} of rows (auxiliary variables) and columns +(structural variables) are explained above (see Section \ref{seclp}, +page \pageref{seclp}). + +{\it Scale factors} are used internally for scaling rows and columns of +the constraint matrix. + +Information about the {\it objective function} includes numerical +values of objective coefficients and a flag, which defines the +optimization direction (i.e. minimization or maximization). + +The {\it constraint matrix} is a $m \times n$ rectangular matrix built +of constraint coefficients $a_{ij}$, which defines the system of linear +constraints (1.2) (see Section \ref{seclp}, page \pageref{seclp}). This +matrix is stored in the problem object in both row-wise and column-wise +sparse formats. + +Once the problem object has been created, the application program can +access and modify any components of the problem segment in arbitrary +order. + +\subsubsection*{Basis segment} + +The {\it basis segment} of the problem object keeps information related +to the current basic solution. It includes: + +$\bullet$ row and column statuses, + +$\bullet$ basic solution statuses, + +$\bullet$ factorization of the current basis matrix, and + +$\bullet$ basic solution components. + +The {\it row and column statuses} define which rows and columns are +basic and which are non-basic. These statuses may be assigned either by +the application program of by some API routines. Note that these +statuses are always defined independently on whether the corresponding +basis is valid or not. + +The {\it basic solution statuses} include the {\it primal status} and +the {\it dual status}, which are set by the simplex-based solver once +the problem has been solved. The primal status shows whether a primal +basic solution is feasible, infeasible, or undefined. The dual status +shows the same for a dual basic solution. + +The {\it factorization of the basis matrix} is some factorized form +(like LU-factorization) of the current basis matrix (defined by the +current row and column statuses). The factorization is used by the +simplex-based solver and kept when the solver terminates the search. +This feature allows efficiently reoptimizing the problem after some +modifications (for example, after changing some bounds or objective +coefficients). It also allows performing the post-optimal analysis (for +example, computing components of the simplex table, etc.). + +The {\it basic solution components} include primal and dual values of +all auxiliary and structural variables for the most recently obtained +basic solution. + +\subsubsection*{Interior point segment} + +The {\it interior point segment} is automatically allocated after the +problem has been solved using the interior point solver. It contains +interior point solution components, which include the solution status, +and primal and dual values of all auxiliary and structural variables. + +\subsubsection*{MIP segment} + +The {\it MIP segment} is used only for MIP problems. This segment +includes: + +$\bullet$ column kinds, + +$\bullet$ MIP solution status, and + +$\bullet$ MIP solution components. + +The {\it column kinds} define which columns (i.e. structural variables) +are integer and which are continuous. + +The {\it MIP solution status} is set by the MIP solver and shows whether +a MIP solution is integer optimal, integer feasible (non-optimal), or +undefined. + +The {\it MIP solution components} are computed by the MIP solver and +include primal values of all auxiliary and structural variables for the +most recently obtained MIP solution. + +Note that in case of MIP problem the basis segment corresponds to +the optimal solution of LP relaxation, which is also available to the +application program. + +Currently the search tree is not kept in the MIP segment. Therefore if +the search has been terminated, it cannot be continued. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Problem creating and modifying routines} + +\subsection{glp\_create\_prob---create problem object} + +\subsubsection*{Synopsis} + +\begin{verbatim} +glp_prob *glp_create_prob(void); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_create_prob| creates a new problem object, which +initially is ``empty'', i.e. has no rows and columns. + +\subsubsection*{Returns} + +The routine returns a pointer to the created object, which should be +used in any subsequent operations on this object. + +\subsection{glp\_set\_prob\_name---assign (change) problem name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_prob_name(glp_prob *lp, const char *name); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_prob_name| assigns a given symbolic +\verb|name| (1 up to 255 characters) to the specified problem object. + +If the parameter \verb|name| is \verb|NULL| or empty string, the routine +erases an existing symbolic name of the problem object. + +\subsection{glp\_set\_obj\_name---assign (change) objective function +name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_obj_name(glp_prob *lp, const char *name); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_obj_name| assigns a given symbolic +\verb|name| (1 up to 255 characters) to the objective function of the +specified problem object. + +If the parameter \verb|name| is \verb|NULL| or empty string, the routine +erases an existing symbolic name of the objective function. + +\subsection{glp\_set\_obj\_dir---set (change) optimization direction\\ +flag} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_obj_dir(glp_prob *lp, int dir); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_obj_dir| sets (changes) the optimization +direction flag (i.e. ``sense'' of the objective function) as specified +by the parameter \verb|dir|: + +\begin{tabular}{@{}ll} +\verb|GLP_MIN| & minimization; \\ +\verb|GLP_MAX| & maximization. \\ +\end{tabular} + +\noindent +(Note that by default the problem is minimization.) + +\subsection{glp\_add\_rows---add new rows to problem object} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_add_rows(glp_prob *lp, int nrs); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_add_rows| adds \verb|nrs| rows (constraints) to +the specified problem object. New rows are always added to the end of +the row list, so the ordinal numbers of existing rows are not changed. + +Being added each new row is initially free (unbounded) and has empty +list of the constraint coefficients. + +\subsubsection*{Returns} + +The routine \verb|glp_add_rows| returns the ordinal number of the first +new row added to the problem object. + +\newpage + +\subsection{glp\_add\_cols---add new columns to problem object} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_add_cols(glp_prob *lp, int ncs); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_add_cols| adds \verb|ncs| columns (structural +variables) to the specified problem object. New columns are always added +to the end of the column list, so the ordinal numbers of existing +columns are not changed. + +Being added each new column is initially fixed at zero and has empty +list of the constraint coefficients. + +\subsubsection*{Returns} + +The routine \verb|glp_add_cols| returns the ordinal number of the first +new column added to the problem object. + +\subsection{glp\_set\_row\_name---assign (change) row name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_row_name(glp_prob *lp, int i, const char *name); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_row_name| assigns a given symbolic +\verb|name| (1 up to 255 characters) to \verb|i|-th row (auxiliary +variable) of the specified problem object. + +If the parameter \verb|name| is \verb|NULL| or empty string, the routine +erases an existing name of $i$-th row. + +\subsection{glp\_set\_col\_name---assign (change) column name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_col_name(glp_prob *lp, int j, const char *name); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_col_name| assigns a given symbolic +\verb|name| (1 up to 255 characters) to \verb|j|-th column (structural +variable) of the specified problem object. + +If the parameter \verb|name| is \verb|NULL| or empty string, the routine +erases an existing name of $j$-th column. + +\subsection{glp\_set\_row\_bnds---set (change) row bounds} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_row_bnds(glp_prob *lp, int i, int type, + double lb, double ub); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_row_bnds| sets (changes) the type and bounds +of \verb|i|-th row (auxiliary variable) of the specified problem object. + +The parameters \verb|type|, \verb|lb|, and \verb|ub| specify the type, +lower bound, and upper bound, respectively, as follows: + +\begin{center} +\begin{tabular}{cr@{}c@{}ll} +Type & \multicolumn{3}{c}{Bounds} & Comment \\ +\hline +\verb|GLP_FR| & $-\infty <$ &$\ x\ $& $< +\infty$ + & Free (unbounded) variable \\ +\verb|GLP_LO| & $lb \leq$ &$\ x\ $& $< +\infty$ + & Variable with lower bound \\ +\verb|GLP_UP| & $-\infty <$ &$\ x\ $& $\leq ub$ + & Variable with upper bound \\ +\verb|GLP_DB| & $lb \leq$ &$\ x\ $& $\leq ub$ + & Double-bounded variable \\ +\verb|GLP_FX| & $lb =$ &$\ x\ $& $= ub$ + & Fixed variable \\ +\end{tabular} +\end{center} + +\noindent +where $x$ is the auxiliary variable associated with $i$-th row. + +If the row has no lower bound, the parameter \verb|lb| is ignored. If +the row has no upper bound, the parameter \verb|ub| is ignored. If the +row is an equality constraint (i.e. the corresponding auxiliary variable +is of fixed type), only the parameter \verb|lb| is used while the +parameter \verb|ub| is ignored. + +Being added to the problem object each row is initially free, i.e. its +type is \verb|GLP_FR|. + +\newpage + +\subsection{glp\_set\_col\_bnds---set (change) column bounds} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_col_bnds(glp_prob *lp, int j, int type, + double lb, double ub); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_col_bnds| sets (changes) the type and bounds +of \verb|j|-th column (structural variable) of the specified problem +object. + +The parameters \verb|type|, \verb|lb|, and \verb|ub| specify the type, +lower bound, and upper bound, respectively, as follows: + +\begin{center} +\begin{tabular}{cr@{}c@{}ll} +Type & \multicolumn{3}{c}{Bounds} & Comment \\ +\hline +\verb|GLP_FR| & $-\infty <$ &$\ x\ $& $< +\infty$ + & Free (unbounded) variable \\ +\verb|GLP_LO| & $lb \leq$ &$\ x\ $& $< +\infty$ + & Variable with lower bound \\ +\verb|GLP_UP| & $-\infty <$ &$\ x\ $& $\leq ub$ + & Variable with upper bound \\ +\verb|GLP_DB| & $lb \leq$ &$\ x\ $& $\leq ub$ + & Double-bounded variable \\ +\verb|GLP_FX| & $lb =$ &$\ x\ $& $= ub$ + & Fixed variable \\ +\end{tabular} +\end{center} + +\noindent +where $x$ is the structural variable associated with $j$-th column. + +If the column has no lower bound, the parameter \verb|lb| is ignored. +If the column has no upper bound, the parameter \verb|ub| is ignored. +If the column is of fixed type, only the parameter \verb|lb| is used +while the parameter \verb|ub| is ignored. + +Being added to the problem object each column is initially fixed at +zero, i.e. its type is \verb|GLP_FX| and both bounds are 0. + +\subsection{glp\_set\_obj\_coef---set (change) objective coefficient +or constant term} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_obj_coef(glp_prob *lp, int j, double coef); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_obj_coef| sets (changes) the objective +coefficient at \verb|j|-th column (structural variable). A new value of +the objective coefficient is specified by the parameter \verb|coef|. + +If the parameter \verb|j| is 0, the routine sets (changes) the constant +term (``shift'') of the objective function. + +\subsection{glp\_set\_mat\_row---set (replace) row of the constraint +matrix} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_mat_row(glp_prob *lp, int i, int len, + const int ind[], const double val[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_mat_row| stores (replaces) the contents of +\verb|i|-th row of the constraint matrix of the specified problem +object. + +Column indices and numerical values of new row elements must be placed +in locations \verb|ind[1]|, \dots, \verb|ind[len]| and \verb|val[1]|, +\dots, \verb|val[len]|, respectively, where $0 \leq$ \verb|len| $\leq n$ +is the new length of $i$-th row, $n$ is the current number of columns in +the problem object. Elements with identical column indices are not +allowed. Zero elements are allowed, but they are not stored in the +constraint matrix. + +If the parameter \verb|len| is 0, the parameters \verb|ind| and/or +\verb|val| can be specified as \verb|NULL|. + +\subsection{glp\_set\_mat\_col---set (replace) column of the +constr\-aint matrix} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_mat_col(glp_prob *lp, int j, int len, + const int ind[], const double val[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_mat_col| stores (replaces) the contents of +\verb|j|-th column of the constraint matrix of the specified problem +object. + +Row indices and numerical values of new column elements must be placed +in locations \verb|ind[1]|, \dots, \verb|ind[len]| and \verb|val[1]|, +\dots, \verb|val[len]|, respectively, where $0 \leq$ \verb|len| $\leq m$ +is the new length of $j$-th column, $m$ is the current number of rows in +the problem object. Elements with identical row indices are not allowed. +Zero elements are allowed, but they are not stored in the constraint +matrix. + +If the parameter \verb|len| is 0, the parameters \verb|ind| and/or +\verb|val| can be specified as \verb|NULL|. + +\subsection{glp\_load\_matrix---load (replace) the whole constraint +matrix} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_load_matrix(glp_prob *lp, int ne, const int ia[], + const int ja[], const double ar[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_load_matrix| loads the constraint matrix passed +in the arrays \verb|ia|, \verb|ja|, and \verb|ar| into the specified +problem object. Before loading the current contents of the constraint +matrix is destroyed. + +Constraint coefficients (elements of the constraint matrix) must be +specified as triplets (\verb|ia[k]|, \verb|ja[k]|, \verb|ar[k]|) for +$k=1,\dots,ne$, where \verb|ia[k]| is the row index, \verb|ja[k]| is +the column index, and \verb|ar[k]| is a numeric value of corresponding +constraint coefficient. The parameter \verb|ne| specifies the total +number of (non-zero) elements in the matrix to be loaded. Coefficients +with identical indices are not allowed. Zero coefficients are allowed, +however, they are not stored in the constraint matrix. + +If the parameter \verb|ne| is 0, the parameters \verb|ia|, \verb|ja|, +and/or \verb|ar| can be specified as \verb|NULL|. + +\subsection{glp\_check\_dup---check for duplicate elements in sparse +matrix} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_check_dup(int m, int n, int ne, const int ia[], + const int ja[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_check_dup checks| for duplicate elements (that +is, elements with identical indices) in a sparse matrix specified in +the coordinate format. + +The parameters $m$ and $n$ specifies, respectively, the number of rows +and columns in the matrix, $m\geq 0$, $n\geq 0$. + +The parameter {\it ne} specifies the number of (structurally) non-zero +elements in the matrix, {\it ne} $\geq 0$. + +Elements of the matrix are specified as doublets $(ia[k],ja[k])$ for +$k=1,\dots,ne$, where $ia[k]$ is a row index, $ja[k]$ is a column index. + +The routine \verb|glp_check_dup| can be used prior to a call to the +routine \verb|glp_load_matrix| to check that the constraint matrix to +be loaded has no duplicate elements. + +\subsubsection*{Returns} + +The routine \verb|glp_check_dup| returns one of the following values: + +\noindent +\begin{tabular}{@{}r@{\ }c@{\ }l@{}} +0&---&the matrix has no duplicate elements;\\ +$-k$&---&indices $ia[k]$ or/and $ja[k]$ are out of range;\\ +$+k$&---&element $(ia[k],ja[k])$ is duplicate.\\ +\end{tabular} + +\subsection{glp\_sort\_matrix---sort elements of the constraint matrix} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_sort_matrix(glp_prob *P); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sort_matrix| sorts elements of the constraint +matrix rebuilding its row and column linked lists. On exit from the +routine the constraint matrix is not changed, however, elements in the +row linked lists become ordered by ascending column indices, and the +elements in the column linked lists become ordered by ascending row +indices. + +\subsection{glp\_del\_rows---delete rows from problem object} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_del_rows(glp_prob *lp, int nrs, const int num[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_del_rows| deletes rows from the specified problem +ob-\linebreak ject. Ordinal numbers of rows to be deleted should be +placed in locations \verb|num[1]|, \dots, \verb|num[nrs]|, where +${\tt nrs}>0$. + +Note that deleting rows involves changing ordinal numbers of other +rows remaining in the problem object. New ordinal numbers of the +remaining rows are assigned under the assumption that the original +order of rows is not changed. Let, for example, before deletion there +be five rows $a$, $b$, $c$, $d$, $e$ with ordinal numbers 1, 2, 3, 4, 5, +and let rows $b$ and $d$ have been deleted. Then after deletion the +remaining rows $a$, $c$, $e$ are assigned new oridinal numbers 1, 2, 3. + +\subsection{glp\_del\_cols---delete columns from problem object} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_del_cols(glp_prob *lp, int ncs, const int num[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_del_cols| deletes columns from the specified +problem object. Ordinal numbers of columns to be deleted should be +placed in locations \verb|num[1]|, \dots, \verb|num[ncs]|, where +${\tt ncs}>0$. + +Note that deleting columns involves changing ordinal numbers of other +columns remaining in the problem object. New ordinal numbers of the +remaining columns are assigned under the assumption that the original +order of columns is not changed. Let, for example, before deletion there +be six columns $p$, $q$, $r$, $s$, $t$, $u$ with ordinal numbers 1, 2, +3, 4, 5, 6, and let columns $p$, $q$, $s$ have been deleted. Then after +deletion the remaining columns $r$, $t$, $u$ are assigned new ordinal +numbers 1, 2, 3. + +\subsection{glp\_copy\_prob---copy problem object content} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_copy_prob(glp_prob *dest, glp_prob *prob, int names); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_copy_prob| copies the content of the problem +object \verb|prob| to the problem object \verb|dest|. + +The parameter \verb|names| is a flag. If it is \verb|GLP_ON|, +the routine also copies all symbolic names; otherwise, if it is +\verb|GLP_OFF|, no symbolic names are copied. + +\newpage + +\subsection{glp\_erase\_prob---erase problem object content} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_erase_prob(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_erase_prob| erases the content of the specified +problem object. The effect of this operation is the same as if the +problem object would be deleted with the routine \verb|glp_delete_prob| +and then created anew with the routine \verb|glp_create_prob|, with the +only exception that the handle (pointer) to the problem object remains +valid. + +\subsection{glp\_delete\_prob---delete problem object} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_delete_prob(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_delete_prob| deletes a problem object, which the +parameter \verb|lp| points to, freeing all the memory allocated to this +object. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Problem retrieving routines} + +\subsection{glp\_get\_prob\_name---retrieve problem name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +const char *glp_get_prob_name(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_prob_name| returns a pointer to an internal +buffer, which contains symbolic name of the problem. However, if the +problem has no assigned name, the routine returns \verb|NULL|. + +\subsection{glp\_get\_obj\_name---retrieve objective function name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +const char *glp_get_obj_name(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_obj_name| returns a pointer to an internal +buffer, which contains symbolic name assigned to the objective +function. However, if the objective function has no assigned name, the +routine returns \verb|NULL|. + +\subsection{glp\_get\_obj\_dir---retrieve optimization direction flag} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_obj_dir(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_obj_dir| returns the optimization direction +flag (i.e. ``sense'' of the objective function): + +\begin{tabular}{@{}ll} +\verb|GLP_MIN| & minimization; \\ +\verb|GLP_MAX| & maximization. \\ +\end{tabular} + +\pagebreak + +\subsection{glp\_get\_num\_rows---retrieve number of rows} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_num_rows(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_num_rows| returns the current number of rows +in the specified problem object. + +\subsection{glp\_get\_num\_cols---retrieve number of columns} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_num_cols(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_num_cols| returns the current number of +columns the specified problem object. + +\subsection{glp\_get\_row\_name---retrieve row name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +const char *glp_get_row_name(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_row_name| returns a pointer to an internal +buffer, which contains a symbolic name assigned to \verb|i|-th row. +However, if the row has no assigned name, the routine returns +\verb|NULL|. + +\subsection{glp\_get\_col\_name---retrieve column name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +const char *glp_get_col_name(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_name| returns a pointer to an internal +buffer, which contains a symbolic name assigned to \verb|j|-th column. +However, if the column has no assigned name, the routine returns +\verb|NULL|. + +\subsection{glp\_get\_row\_type---retrieve row type} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_row_type(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_row_type| returns the type of \verb|i|-th +row, i.e. the type of corresponding auxiliary variable, as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_FR| & free (unbounded) variable; \\ +\verb|GLP_LO| & variable with lower bound; \\ +\verb|GLP_UP| & variable with upper bound; \\ +\verb|GLP_DB| & double-bounded variable; \\ +\verb|GLP_FX| & fixed variable. \\ +\end{tabular} + +\subsection{glp\_get\_row\_lb---retrieve row lower bound} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_row_lb(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_row_lb| returns the lower bound of +\verb|i|-th row, i.e. the lower bound of corresponding auxiliary +variable. However, if the row has no lower bound, the routine returns +\verb|-DBL_MAX|. + +\subsection{glp\_get\_row\_ub---retrieve row upper bound} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_row_ub(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_row_ub| returns the upper bound of +\verb|i|-th row, i.e. the upper bound of corresponding auxiliary +variable. However, if the row has no upper bound, the routine returns +\verb|+DBL_MAX|. + +\subsection{glp\_get\_col\_type---retrieve column type} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_col_type(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_type| returns the type of \verb|j|-th +column, i.e. the type of corresponding structural variable, as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_FR| & free (unbounded) variable; \\ +\verb|GLP_LO| & variable with lower bound; \\ +\verb|GLP_UP| & variable with upper bound; \\ +\verb|GLP_DB| & double-bounded variable; \\ +\verb|GLP_FX| & fixed variable. \\ +\end{tabular} + +\subsection{glp\_get\_col\_lb---retrieve column lower bound} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_col_lb(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_lb| returns the lower bound of +\verb|j|-th column, i.e. the lower bound of corresponding structural +variable. However, if the column has no lower bound, the routine returns +\verb|-DBL_MAX|. + +\subsection{glp\_get\_col\_ub---retrieve column upper bound} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_col_ub(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_ub| returns the upper bound of +\verb|j|-th column, i.e. the upper bound of corresponding structural +variable. However, if the column has no upper bound, the routine returns +\verb|+DBL_MAX|. + +\subsection{glp\_get\_obj\_coef---retrieve objective coefficient or\\ +constant term} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_obj_coef(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_obj_coef| returns the objective coefficient +at \verb|j|-th structural variable (column). + +If the parameter \verb|j| is 0, the routine returns the constant term +(``shift'') of the objective function. + +\subsection{glp\_get\_num\_nz---retrieve number of constraint +coefficients} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_num_nz(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_num_nz| returns the number of non-zero +elements in the constraint matrix of the specified problem object. + +\subsection{glp\_get\_mat\_row---retrieve row of the constraint +matrix} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_mat_row(glp_prob *lp, int i, int ind[], + double val[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_get_mat_row| scans (non-zero) elements of +\verb|i|-th row of the constraint matrix of the specified problem object +and stores their column indices and numeric values to locations +\verb|ind[1]|, \dots, \verb|ind[len]| and \verb|val[1]|, \dots, +\verb|val[len]|, respectively, where $0\leq{\tt len}\leq n$ is the +number of elements in $i$-th row, $n$ is the number of columns. + +The parameter \verb|ind| and/or \verb|val| can be specified as +\verb|NULL|, in which case corresponding information is not stored. + +\subsubsection*{Returns} + +The routine \verb|glp_get_mat_row| returns the length \verb|len|, i.e. +the number of (non-zero) elements in \verb|i|-th row. + +\subsection{glp\_get\_mat\_col---retrieve column of the constraint\\ +matrix} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_mat_col(glp_prob *lp, int j, int ind[], + double val[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_get_mat_col| scans (non-zero) elements of +\verb|j|-th column of the constraint matrix of the specified problem +object and stores their row indices and numeric values to locations +\verb|ind[1]|, \dots, \verb|ind[len]| and \verb|val[1]|, \dots, +\verb|val[len]|, respectively, where $0\leq{\tt len}\leq m$ is the +number of elements in $j$-th column, $m$ is the number of rows. + +The parameter \verb|ind| and/or \verb|val| can be specified as +\verb|NULL|, in which case corresponding information is not stored. + +\subsubsection*{Returns} + +The routine \verb|glp_get_mat_col| returns the length \verb|len|, i.e. +the number of (non-zero) elements in \verb|j|-th column. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Row and column searching routines} + +\subsection{glp\_create\_index---create the name index} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_create_index(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_create_index| creates the name index for the +specified problem object. The name index is an auxiliary data structure, +which is intended to quickly (i.e. for logarithmic time) find rows and +columns by their names. + +This routine can be called at any time. If the name index already +exists, the routine does nothing. + +\subsection{glp\_find\_row---find row by its name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_find_row(glp_prob *lp, const char *name); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_find_row| returns the ordinal number of a row, +which is assigned (by the routine \verb|glp_set_row_name|) the specified +symbolic \verb|name|. If no such row exists, the routine returns 0. + +\subsection{glp\_find\_col---find column by its name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_find_col(glp_prob *lp, const char *name); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_find_col| returns the ordinal number of a column, +which is assigned (by the routine \verb|glp_set_col_name|) the specified +symbolic \verb|name|. If no such column exists, the routine returns 0. + +\subsection{glp\_delete\_index---delete the name index} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_delete_index(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_delete_index| deletes the name index previously +created by the routine \verb|glp_create_index| and frees the memory +allocated to this auxiliary data structure. + +This routine can be called at any time. If the name index does not +exist, the routine does nothing. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Problem scaling routines} + +\subsection{Background} + +In GLPK the {\it scaling} means a linear transformation applied to the +constraint matrix to improve its numerical properties.\footnote{In many +cases a proper scaling allows making the constraint matrix to be better +conditioned, i.e. decreasing its condition number, that makes +computations numerically more stable.} + +The main equality is the following: +$$\widetilde{A}=RAS,\eqno(2.1)$$ +where $A=(a_{ij})$ is the original constraint matrix, $R=(r_{ii})>0$ is +a diagonal matrix used to scale rows (constraints), $S=(s_{jj})>0$ is a +diagonal matrix used to scale columns (variables), $\widetilde{A}$ is +the scaled constraint matrix. + +From (2.1) it follows that in the {\it scaled} problem instance each +original constraint coefficient $a_{ij}$ is replaced by corresponding +scaled constraint coefficient: +$$\widetilde{a}_{ij}=r_{ii}a_{ij}s_{jj}.\eqno(2.2)$$ + +Note that the scaling is performed internally and therefore +transparently to the user. This means that on API level the user always +deal with unscaled data. + +Scale factors $r_{ii}$ and $s_{jj}$ can be set or changed at any time +either directly by the application program in a problem specific way +(with the routines \verb|glp_set_rii| and \verb|glp_set_sjj|), or by +some API routines intended for automatic scaling. + +\subsection{glp\_set\_rii---set (change) row scale factor} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_rii(glp_prob *lp, int i, double rii); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_rii| sets (changes) the scale factor $r_{ii}$ +for $i$-th row of the specified problem object. + +\subsection{glp\_set\_sjj---set (change) column scale factor} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_sjj(glp_prob *lp, int j, double sjj); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_sjj| sets (changes) the scale factor $s_{jj}$ +for $j$-th column of the specified problem object. + +\subsection{glp\_get\_rii---retrieve row scale factor} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_rii(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_rii| returns current scale factor $r_{ii}$ for +$i$-th row of the specified problem object. + +\subsection{glp\_get\_sjj---retrieve column scale factor} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_sjj(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_sjj| returns current scale factor $s_{jj}$ for +$j$-th column of the specified problem object. + +\subsection{glp\_scale\_prob---scale problem data} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_scale_prob(glp_prob *lp, int flags); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_scale_prob| performs automatic scaling of problem +data for the specified problem object. + +The parameter \verb|flags| specifies scaling options used by the +routine. The options can be combined with the bitwise OR operator and +may be the following: + +\begin{tabular}{@{}ll} +\verb|GLP_SF_GM| & perform geometric mean scaling;\\ +\verb|GLP_SF_EQ| & perform equilibration scaling;\\ +\verb|GLP_SF_2N| & round scale factors to nearest power of two;\\ +\verb|GLP_SF_SKIP| & skip scaling, if the problem is well scaled.\\ +\end{tabular} + +The parameter \verb|flags| may be specified as \verb|GLP_SF_AUTO|, in +which case the routine chooses the scaling options automatically. + +\subsection{glp\_unscale\_prob---unscale problem data} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_unscale_prob(glp_prob *lp); +\end{verbatim} + +The routine \verb|glp_unscale_prob| performs unscaling of problem data +for the specified problem object. + +``Unscaling'' means replacing the current scaling matrices $R$ and $S$ +by unity matrices that cancels the scaling effect. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{LP basis constructing routines} + +\subsection{Background} + +To start the search the simplex method needs a valid initial basis. In +GLPK the basis is completely defined by a set of {\it statuses} assigned +to {\it all} (auxiliary and structural) variables, where the status may +be one of the following: + +\begin{tabular}{@{}ll} +\verb|GLP_BS| & basic variable;\\ +\verb|GLP_NL| & non-basic variable having active lower bound;\\ +\verb|GLP_NU| & non-basic variable having active upper bound;\\ +\verb|GLP_NF| & non-basic free variable;\\ +\verb|GLP_NS| & non-basic fixed variable.\\ +\end{tabular} + +The basis is {\it valid}, if the basis matrix, which is a matrix built +of columns of the augmented constraint matrix $(I\:|-A)$ corresponding +to basic variables, is non-singular. This, in particular, means that +the number of basic variables must be the same as the number of rows in +the problem object. (For more details see Section \ref{lpbasis}, page +\pageref{lpbasis}.) + +Any initial basis may be constructed (or restored) with the API +routines \verb|glp_set_row_stat| and \verb|glp_set_col_stat| by +assigning appropriate statuses to auxiliary and structural variables. +Another way to construct an initial basis is to use API routines like +\verb|glp_adv_basis|, which implement so called +{\it crashing}.\footnote{This term is from early linear programming +systems and means a heuristic to construct a valid initial basis.} Note +that on normal exit the simplex solver remains the basis valid, so in +case of reoptimization there is no need to construct an initial basis +from scratch. + +\subsection{glp\_set\_row\_stat---set (change) row status} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_row_stat(glp_prob *lp, int i, int stat); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_row_stat| sets (changes) the current status +of \verb|i|-th row (auxiliary variable) as specified by the parameter +\verb|stat|: + +\begin{tabular}{@{}lp{104.2mm}@{}} +\verb|GLP_BS| & make the row basic (make the constraint inactive); \\ +\verb|GLP_NL| & make the row non-basic (make the constraint active); \\ +\end{tabular} + +\newpage + +\begin{tabular}{@{}lp{104.2mm}@{}} +\verb|GLP_NU| & make the row non-basic and set it to the upper bound; + if the row is not double-bounded, this status is equivalent to + \verb|GLP_NL| (only in the case of this routine); \\ +\verb|GLP_NF| & the same as \verb|GLP_NL| (only in the case of this + routine); \\ +\verb|GLP_NS| & the same as \verb|GLP_NL| (only in the case of this + routine). \\ +\end{tabular} + +\subsection{glp\_set\_col\_stat---set (change) column status} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_col_stat(glp_prob *lp, int j, int stat); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_col_stat sets| (changes) the current status +of \verb|j|-th column (structural variable) as specified by the +parameter \verb|stat|: + +\begin{tabular}{@{}lp{104.2mm}@{}} +\verb|GLP_BS| & make the column basic; \\ +\verb|GLP_NL| & make the column non-basic; \\ +\verb|GLP_NU| & make the column non-basic and set it to the upper + bound; if the column is not double-bounded, this status is equivalent + to \verb|GLP_NL| (only in the case of this routine); \\ +\verb|GLP_NF| & the same as \verb|GLP_NL| (only in the case of this + routine); \\ +\verb|GLP_NS| & the same as \verb|GLP_NL| (only in the case of this + routine). +\end{tabular} + +\subsection{glp\_std\_basis---construct standard initial LP basis} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_std_basis(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_std_basis| constructs the ``standard'' (trivial) +initial LP basis for the specified problem object. + +In the ``standard'' LP basis all auxiliary variables (rows) are basic, +and all structural variables (columns) are non-basic (so the +corresponding basis matrix is unity). + +\newpage + +\subsection{glp\_adv\_basis---construct advanced initial LP basis} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_adv_basis(glp_prob *lp, int flags); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_adv_basis| constructs an advanced initial LP +basis for the specified problem object. + +The parameter \verb|flags| is reserved for use in the future and must +be specified as zero. + +In order to construct the advanced initial LP basis the routine does +the following: + +1) includes in the basis all non-fixed auxiliary variables; + +2) includes in the basis as many non-fixed structural variables as +possible keeping the triangular form of the basis matrix; + +3) includes in the basis appropriate (fixed) auxiliary variables to +complete the basis. + +As a result the initial LP basis has as few fixed variables as possible +and the corresponding basis matrix is triangular. + +\subsection{glp\_cpx\_basis---construct Bixby's initial LP basis} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_cpx_basis(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_cpx_basis| constructs an initial basis for the +specified problem object with the algorithm proposed by +R.~Bixby.\footnote{Robert E. Bixby, ``Implementing the Simplex Method: +The Initial Basis.'' ORSA Journal on Computing, Vol. 4, No. 3, 1992, +pp. 267-84.} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Simplex method routines} + +The {\it simplex method} is a well known efficient numerical procedure +to solve LP problems. + +On each iteration the simplex method transforms the original system of +equaility constraints (1.2) resolving them through different sets of +variables to an equivalent system called {\it the simplex table} (or +sometimes {\it the simplex tableau}), which has the following form: +$$ +\begin{array}{r@{\:}c@{\:}r@{\:}c@{\:}r@{\:}c@{\:}r} +z&=&d_1(x_N)_1&+&d_2(x_N)_2&+ \dots +&d_n(x_N)_n \\ +(x_B)_1&=&\xi_{11}(x_N)_1& +& \xi_{12}(x_N)_2& + \dots +& + \xi_{1n}(x_N)_n \\ +(x_B)_2&=& \xi_{21}(x_N)_1& +& \xi_{22}(x_N)_2& + \dots +& + \xi_{2n}(x_N)_n \\ +\multicolumn{7}{c} +{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .} \\ +(x_B)_m&=&\xi_{m1}(x_N)_1& +& \xi_{m2}(x_N)_2& + \dots +& + \xi_{mn}(x_N)_n \\ +\end{array} \eqno (2.3) +$$ +where: $(x_B)_1, (x_B)_2, \dots, (x_B)_m$ are basic variables; +$(x_N)_1, (x_N)_2, \dots, (x_N)_n$ are non-basic variables; +$d_1, d_2, \dots, d_n$ are reduced costs; +$\xi_{11}, \xi_{12}, \dots, \xi_{mn}$ are coefficients of the +simplex table. (May note that the original LP problem (1.1)---(1.3) also +has the form of a simplex table, where all equalities are resolved +through auxiliary variables.) + +From the linear programming theory it is known that if an optimal +solution of the LP problem (1.1)---(1.3) exists, it can always be +written in the form (2.3), where non-basic variables are set on their +bounds while values of the objective function and basic variables are +determined by the corresponding equalities of the simplex table. + +A set of values of all basic and non-basic variables determined by the +simplex table is called {\it basic solution}. If all basic variables are +within their bounds, the basic solution is called {\it (primal) +feasible}, otherwise it is called {\it (primal) infeasible}. A feasible +basic solution, which provides a smallest (in case of minimization) or +a largest (in case of maximization) value of the objective function is +called {\it optimal}. Therefore, for solving LP problem the simplex +method tries to find its optimal basic solution. + +Primal feasibility of some basic solution may be stated by simple +checking if all basic variables are within their bounds. Basic solution +is optimal if additionally the following optimality conditions are +satisfied for all non-basic variables: +\begin{center} +\begin{tabular}{lcc} +Status of $(x_N)_j$ & Minimization & Maximization \\ +\hline +$(x_N)_j$ is free & $d_j = 0$ & $d_j = 0$ \\ +$(x_N)_j$ is on its lower bound & $d_j \geq 0$ & $d_j \leq 0$ \\ +$(x_N)_j$ is on its upper bound & $d_j \leq 0$ & $d_j \geq 0$ \\ +\end{tabular} +\end{center} +In other words, basic solution is optimal if there is no non-basic +variable, which changing in the feasible direction (i.e. increasing if +it is free or on its lower bound, or decreasing if it is free or on its +upper bound) can improve (i.e. decrease in case of minimization or +increase in case of maximization) the objective function. + +If all non-basic variables satisfy to the optimality conditions shown +above (independently on whether basic variables are within their bounds +or not), the basic solution is called {\it dual feasible}, otherwise it +is called {\it dual infeasible}. + +It may happen that some LP problem has no primal feasible solution due +to incorrect formulation---this means that its constraints conflict +with each other. It also may happen that some LP problem has unbounded +solution again due to incorrect formulation---this means that some +non-basic variable can improve the objective function, i.e. the +optimality conditions are violated, and at the same time this variable +can infinitely change in the feasible direction meeting no resistance +from basic variables. (May note that in the latter case the LP problem +has no dual feasible solution.) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\subsection{glp\_simplex---solve LP problem with the primal or dual +simplex method} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_simplex(glp_prob *lp, const glp_smcp *parm); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_simplex| is a driver to the LP solver based on +the simplex method. This routine retrieves problem data from the +specified problem object, calls the solver to solve the problem +instance, and stores results of computations back into the problem +object. + +The simplex solver has a set of control parameters. Values of the +control parameters can be passed in the structure \verb|glp_smcp|, +which the parameter \verb|parm| points to. For detailed description of +this structure see paragraph ``Control parameters'' below. +Before specifying some control parameters the application program +should initialize the structure \verb|glp_smcp| by default values of +all control parameters using the routine \verb|glp_init_smcp| (see the +next subsection). This is needed for backward compatibility, because in +the future there may appear new members in the structure +\verb|glp_smcp|. + +The parameter \verb|parm| can be specified as \verb|NULL|, in which +case the solver uses default settings. + +\subsubsection*{Returns} + +\def\arraystretch{1} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & The LP problem instance has been successfully solved. (This code +does {\it not} necessarily mean that the solver has found optimal +solution. It only means that the solution process was successful.) \\ +\verb|GLP_EBADB| & Unable to start the search, because the initial basis +specified in the problem object is invalid---the number of basic +(auxiliary and structural) variables is not the same as the number of +rows in the problem object.\\ +\verb|GLP_ESING| & Unable to start the search, because the basis matrix +corresponding to the initial basis is singular within the working +precision.\\ +\verb|GLP_ECOND| & Unable to start the search, because the basis matrix +corresponding to the initial basis is ill-conditioned, i.e. its +condition number is too large.\\ +\verb|GLP_EBOUND| & Unable to start the search, because some +double-bounded (auxiliary or structural) variables have incorrect +bounds.\\ +\verb|GLP_EFAIL| & The search was prematurely terminated due to the +solver failure.\\ +\verb|GLP_EOBJLL| & The search was prematurely terminated, because the +objective function being maximized has reached its lower limit and +continues decreasing (the dual simplex only).\\ +\verb|GLP_EOBJUL| & The search was prematurely terminated, because the +objective function being minimized has reached its upper limit and +continues increasing (the dual simplex only).\\ +\verb|GLP_EITLIM| & The search was prematurely terminated, because the +simplex iteration limit has been exceeded.\\ +\verb|GLP_ETMLIM| & The search was prematurely terminated, because the +time limit has been exceeded.\\ +\verb|GLP_ENOPFS| & The LP problem instance has no primal feasible +solution (only if the LP presolver is used).\\ +\verb|GLP_ENODFS| & The LP problem instance has no dual feasible +solution (only if the LP presolver is used).\\ +\end{tabular} + +\subsubsection*{Built-in LP presolver} + +The simplex solver has {\it built-in LP presolver}. It is a subprogram +that transforms the original LP problem specified in the problem object +to an equivalent LP problem, which may be easier for solving with the +simplex method than the original one. This is attained mainly due to +reducing the problem size and improving its numeric properties (for +example, by removing some inactive constraints or by fixing some +non-basic variables). Once the transformed LP problem has been solved, +the presolver transforms its basic solution back to the corresponding +basic solution of the original problem. + +Presolving is an optional feature of the routine \verb|glp_simplex|, +and by default it is disabled. In order to enable the LP presolver the +control parameter \verb|presolve| should be set to \verb|GLP_ON| (see +paragraph ``Control parameters'' below). Presolving may be used when +the problem instance is solved for the first time. However, on +performing re-optimization the presolver should be disabled. + +The presolving procedure is transparent to the API user in the sense +that all necessary processing is performed internally, and a basic +solution of the original problem recovered by the presolver is the same +as if it were computed directly, i.e. without presolving. + +Note that the presolver is able to recover only optimal solutions. If +a computed solution is infeasible or non-optimal, the corresponding +solution of the original problem cannot be recovered and therefore +remains undefined. If you need to know a basic solution even if it is +infeasible or non-optimal, the presolver should be disabled. + +\subsubsection*{Terminal output} + +Solving large problem instances may take a long time, so the solver +reports some information about the current basic solution, which is sent +to the terminal. This information has the following format: + +\begin{verbatim} +nnn: obj = xxx infeas = yyy (ddd) +\end{verbatim} + +\noindent +where: `\verb|nnn|' is the iteration number, `\verb|xxx|' is the +current value of the objective function (it is is unscaled and has +correct sign); `\verb|yyy|' is the current sum of primal or dual +infeasibilities (it is scaled and therefore may be used only for visual +estimating), `\verb|ddd|' is the current number of fixed basic +variables. + +The symbol preceding the iteration number indicates which phase of the +simplex method is in effect: + +{\it Blank} means that the solver is searching for primal feasible +solution using the primal simplex or for dual feasible solution using +the dual simplex; + +{\it Asterisk} (\verb|*|) means that the solver is searching for +optimal solution using the primal simplex; + +{\it Vertical dash} (\verb/|/) means that the solver is searching for +optimal solution using the dual simplex. + +\subsubsection*{Control parameters} + +This paragraph describes all control parameters currently used in the +simplex solver. Symbolic names of control parameters are names of +corresponding members in the structure \verb|glp_smcp|. + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int msg\_lev} (default: {\tt GLP\_MSG\_ALL})} +\\ +&Message level for terminal output:\\ +&\verb|GLP_MSG_OFF|---no output;\\ +&\verb|GLP_MSG_ERR|---error and warning messages only;\\ +&\verb|GLP_MSG_ON |---normal output;\\ +&\verb|GLP_MSG_ALL|---full output (including informational messages). +\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int meth} (default: {\tt GLP\_PRIMAL})} +\\ +&Simplex method option:\\ +&\verb|GLP_PRIMAL|---use two-phase primal simplex;\\ +&\verb|GLP_DUAL |---use two-phase dual simplex;\\ +&\verb|GLP_DUALP |---use two-phase dual simplex, and if it fails, +switch to the\\ +&\verb| |$\:$ primal simplex.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int pricing} (default: {\tt GLP\_PT\_PSE})} +\\ +&Pricing technique:\\ +&\verb|GLP_PT_STD|---standard (textbook);\\ +&\verb|GLP_PT_PSE|---projected steepest edge.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int r\_test} (default: {\tt GLP\_RT\_HAR})} +\\ +&Ratio test technique:\\ +&\verb|GLP_RT_STD|---standard (textbook);\\ +&\verb|GLP_RT_HAR|---Harris' two-pass ratio test.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double tol\_bnd} (default: {\tt 1e-7})} +\\ +&Tolerance used to check if the basic solution is primal feasible. +(Do not change this parameter without detailed understanding its +purpose.)\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double tol\_dj} (default: {\tt 1e-7})} +\\ +&Tolerance used to check if the basic solution is dual feasible. +(Do not change this parameter without detailed understanding its +purpose.)\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double tol\_piv} (default: {\tt 1e-10})} +\\ +&Tolerance used to choose eligble pivotal elements of the simplex table. +(Do not change this parameter without detailed understanding its +purpose.)\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double obj\_ll} (default: {\tt -DBL\_MAX})} +\\ +&Lower limit of the objective function. If the objective function +reaches this limit and continues decreasing, the solver terminates the +search. (Used in the dual simplex only.)\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double obj\_ul} (default: {\tt +DBL\_MAX})} +\\ +&Upper limit of the objective function. If the objective function +reaches this limit and continues increasing, the solver terminates the +search. (Used in the dual simplex only.)\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int it\_lim} (default: {\tt INT\_MAX})} +\\ +&Simplex iteration limit.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int tm\_lim} (default: {\tt INT\_MAX})} +\\ +&Searching time limit, in milliseconds.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int out\_frq} (default: {\tt 500})} +\\ +&Output frequency, in iterations. This parameter specifies how +frequently the solver sends information about the solution process to +the terminal.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int out\_dly} (default: {\tt 0})} +\\ +&Output delay, in milliseconds. This parameter specifies how long the +solver should delay sending information about the solution process to +the terminal.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int presolve} (default: {\tt GLP\_OFF})} +\\ +&LP presolver option:\\ +&\verb|GLP_ON |---enable using the LP presolver;\\ +&\verb|GLP_OFF|---disable using the LP presolver.\\ +\end{tabular} + +\subsubsection*{Example 1} + +The following main program reads LP problem instance in fixed MPS +format from file \verb|25fv47.mps|,\footnote{This instance in fixed MPS +format can be found in the Netlib LP collection; see +{\tt ftp://ftp.netlib.org/lp/data/}.} constructs an advanced initial +basis, solves the instance with the primal simplex method (by default), +and writes the solution to file \verb|25fv47.txt|. + +\newpage + +\begin{footnotesize} +\begin{verbatim} +/* spxsamp1.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *P; + P = glp_create_prob(); + glp_read_mps(P, GLP_MPS_DECK, NULL, "25fv47.mps"); + glp_adv_basis(P, 0); + glp_simplex(P, NULL); + glp_print_sol(P, "25fv47.txt"); + glp_delete_prob(P); + return 0; +} + +/* eof */ +\end{verbatim} +\end{footnotesize} + +\noindent +Below here is shown the terminal output from this example program. + +\begin{footnotesize} +\begin{verbatim} +Reading problem data from `25fv47.mps'... +Problem: 25FV47 +Objective: R0000 +822 rows, 1571 columns, 11127 non-zeros +6919 records were read +Crashing... +Size of triangular part = 799 + 0: obj = 1.627307307e+04 infeas = 5.194e+04 (23) + 200: obj = 1.474901610e+04 infeas = 1.233e+04 (19) + 400: obj = 1.343909995e+04 infeas = 3.648e+03 (13) + 600: obj = 1.756052217e+04 infeas = 4.179e+02 (7) +* 775: obj = 1.789251591e+04 infeas = 4.982e-14 (1) +* 800: obj = 1.663354510e+04 infeas = 2.857e-14 (1) +* 1000: obj = 1.024935068e+04 infeas = 1.958e-12 (1) +* 1200: obj = 7.860174791e+03 infeas = 2.810e-29 (1) +* 1400: obj = 6.642378184e+03 infeas = 2.036e-16 (1) +* 1600: obj = 6.037014568e+03 infeas = 0.000e+00 (1) +* 1800: obj = 5.662171307e+03 infeas = 6.447e-15 (1) +* 2000: obj = 5.528146165e+03 infeas = 9.764e-13 (1) +* 2125: obj = 5.501845888e+03 infeas = 0.000e+00 (1) +OPTIMAL SOLUTION FOUND +Writing basic solution to `25fv47.txt'... +\end{verbatim} +\end{footnotesize} + +\newpage + +\subsubsection*{Example 2} + +The following main program solves the same LP problem instance as in +Example 1 above, however, it uses the dual simplex method, which starts +from the standard initial basis. + +\begin{footnotesize} +\begin{verbatim} +/* spxsamp2.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *P; + glp_smcp parm; + P = glp_create_prob(); + glp_read_mps(P, GLP_MPS_DECK, NULL, "25fv47.mps"); + glp_init_smcp(&parm); + parm.meth = GLP_DUAL; + glp_simplex(P, &parm); + glp_print_sol(P, "25fv47.txt"); + glp_delete_prob(P); + return 0; +} + +/* eof */ +\end{verbatim} +\end{footnotesize} + +\noindent +Below here is shown the terminal output from this example program. + +\begin{footnotesize} +\begin{verbatim} +Reading problem data from `25fv47.mps'... +Problem: 25FV47 +Objective: R0000 +822 rows, 1571 columns, 11127 non-zeros +6919 records were read + 0: infeas = 1.223e+03 (516) + 200: infeas = 7.000e+00 (471) + 240: infeas = 1.106e-14 (461) +| 400: obj = -5.394267152e+03 infeas = 5.571e-16 (391) +| 600: obj = -4.586395752e+03 infeas = 1.389e-15 (340) +| 800: obj = -4.158268146e+03 infeas = 1.640e-15 (264) +| 1000: obj = -3.725320045e+03 infeas = 5.181e-15 (245) +| 1200: obj = -3.104802163e+03 infeas = 1.019e-14 (210) +| 1400: obj = -2.584190499e+03 infeas = 8.865e-15 (178) +| 1600: obj = -2.073852927e+03 infeas = 7.867e-15 (142) +| 1800: obj = -1.164037407e+03 infeas = 8.792e-15 (109) +| 2000: obj = -4.370590250e+02 infeas = 2.591e-14 (85) +| 2200: obj = 1.068240144e+03 infeas = 1.025e-13 (70) +| 2400: obj = 1.607481126e+03 infeas = 3.272e-14 (67) +| 2600: obj = 3.038230551e+03 infeas = 4.850e-14 (52) +| 2800: obj = 4.316238187e+03 infeas = 2.622e-14 (36) +| 3000: obj = 5.443842629e+03 infeas = 3.976e-15 (11) +| 3060: obj = 5.501845888e+03 infeas = 8.806e-15 (2) +OPTIMAL SOLUTION FOUND +Writing basic solution to `25fv47.txt'... +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_exact---solve LP problem in exact arithmetic} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_exact(glp_prob *lp, const glp_smcp *parm); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_exact| is a tentative implementation of the +primal two-phase simplex method based on exact (rational) arithmetic. +It is similar to the routine \verb|glp_simplex|, however, for all +internal computations it uses arithmetic of rational numbers, which is +exact in mathematical sense, i.e. free of round-off errors unlike +floating-point arithmetic. + +Note that the routine \verb|glp_exact| uses only two control parameters +passed in the structure \verb|glp_smcp|, namely, \verb|it_lim| and +\verb|tm_lim|. + +\subsubsection*{Returns} + +\def\arraystretch{1} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & The LP problem instance has been successfully solved. (This code +does {\it not} necessarily mean that the solver has found optimal +solution. It only means that the solution process was successful.) \\ +\verb|GLP_EBADB| & Unable to start the search, because the initial basis +specified in the problem object is invalid---the number of basic +(auxiliary and structural) variables is not the same as the number of +rows in the problem object.\\ +\verb|GLP_ESING| & Unable to start the search, because the basis matrix +corresponding to the initial basis is exactly singular.\\ +\verb|GLP_EBOUND| & Unable to start the search, because some +double-bounded (auxiliary or structural) variables have incorrect +bounds.\\ +\verb|GLP_EFAIL| & The problem instance has no rows/columns.\\ +\verb|GLP_EITLIM| & The search was prematurely terminated, because the +simplex iteration limit has been exceeded.\\ +\verb|GLP_ETMLIM| & The search was prematurely terminated, because the +time limit has been exceeded.\\ +\end{tabular} + +\subsubsection*{Comments} + +Computations in exact arithmetic are very time consuming, so solving +LP problem with the routine \verb|glp_exact| from the very beginning is +not a good idea. It is much better at first to find an optimal basis +with the routine \verb|glp_simplex| and only then to call +\verb|glp_exact|, in which case only a few simplex iterations need to +be performed in exact arithmetic. + +\subsection{glp\_init\_smcp---initialize simplex solver control +parameters} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_init_smcp(glp_smcp *parm); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_init_smcp| initializes control parameters, which +are used by the simplex solver, with default values. + +Default values of the control parameters are stored in a \verb|glp_smcp| +structure, which the parameter \verb|parm| points to. + +\subsection{glp\_get\_status---determine generic status of basic +solution} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_status(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_status| reports the generic status of the +current basic solution for the specified problem object as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_OPT| & solution is optimal; \\ +\verb|GLP_FEAS| & solution is feasible; \\ +\verb|GLP_INFEAS| & solution is infeasible; \\ +\verb|GLP_NOFEAS| & problem has no feasible solution; \\ +\verb|GLP_UNBND| & problem has unbounded solution; \\ +\verb|GLP_UNDEF| & solution is undefined. \\ +\end{tabular} + +More detailed information about the status of basic solution can be +retrieved with the routines \verb|glp_get_prim_stat| and +\verb|glp_get_dual_stat|. + +\newpage + +\subsection{glp\_get\_prim\_stat---retrieve status of primal basic +solution} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_prim_stat(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_prim_stat| reports the status of the primal +basic solution for the specified problem object as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_UNDEF| & primal solution is undefined; \\ +\verb|GLP_FEAS| & primal solution is feasible; \\ +\verb|GLP_INFEAS| & primal solution is infeasible; \\ +\verb|GLP_NOFEAS| & no primal feasible solution exists. \\ +\end{tabular} + +\subsection{glp\_get\_dual\_stat---retrieve status of dual basic +solution} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_dual_stat(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_dual_stat| reports the status of the dual +basic solution for the specified problem object as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_UNDEF| & dual solution is undefined; \\ +\verb|GLP_FEAS| & dual solution is feasible; \\ +\verb|GLP_INFEAS| & dual solution is infeasible; \\ +\verb|GLP_NOFEAS| & no dual feasible solution exists. \\ +\end{tabular} + +\subsection{glp\_get\_obj\_val---retrieve objective value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_obj_val(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_obj_val| returns current value of the +objective function. + +\subsection{glp\_get\_row\_stat---retrieve row status} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_row_stat(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_row_stat| returns current status assigned to +the auxiliary variable associated with \verb|i|-th row as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_BS| & basic variable; \\ +\verb|GLP_NL| & non-basic variable on its lower bound; \\ +\verb|GLP_NU| & non-basic variable on its upper bound; \\ +\verb|GLP_NF| & non-basic free (unbounded) variable; \\ +\verb|GLP_NS| & non-basic fixed variable. \\ +\end{tabular} + +\subsection{glp\_get\_row\_prim---retrieve row primal value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_row_prim(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_row_prim| returns primal value of the +auxiliary variable associated with \verb|i|-th row. + +\subsection{glp\_get\_row\_dual---retrieve row dual value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_row_dual(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_row_dual| returns dual value (i.e. reduced +cost) of the auxiliary variable associated with \verb|i|-th row. + +\newpage + +\subsection{glp\_get\_col\_stat---retrieve column status} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_col_stat(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_stat| returns current status assigned to +the structural variable associated with \verb|j|-th column as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_BS| & basic variable; \\ +\verb|GLP_NL| & non-basic variable on its lower bound; \\ +\verb|GLP_NU| & non-basic variable on its upper bound; \\ +\verb|GLP_NF| & non-basic free (unbounded) variable; \\ +\verb|GLP_NS| & non-basic fixed variable. \\ +\end{tabular} + +\subsection{glp\_get\_col\_prim---retrieve column primal value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_col_prim(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_prim| returns primal value of the +structural variable associated with \verb|j|-th column. + +\subsection{glp\_get\_col\_dual---retrieve column dual value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_get_col_dual(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_dual| returns dual value (i.e. reduced +cost) of the structural variable associated with \verb|j|-th column. + +\newpage + +\subsection{glp\_get\_unbnd\_ray---determine variable causing\\ +unboundedness} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_unbnd_ray(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_unbnd_ray| returns the number $k$ of +a variable, which causes primal or dual unboundedness. +If $1\leq k\leq m$, it is $k$-th auxiliary variable, and if +$m+1\leq k\leq m+n$, it is $(k-m)$-th structural variable, where $m$ is +the number of rows, $n$ is the number of columns in the problem object. +If such variable is not defined, the routine returns 0. + +\subsubsection*{Comments} + +If it is not exactly known which version of the simplex solver +detected unboundedness, i.e. whether the unboundedness is primal or +dual, it is sufficient to check the status of the variable +with the routine \verb|glp_get_row_stat| or \verb|glp_get_col_stat|. +If the variable is non-basic, the unboundedness is primal, otherwise, +if the variable is basic, the unboundedness is dual (the latter case +means that the problem has no primal feasible dolution). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Interior-point method routines} + +{\it Interior-point methods} (also known as {\it barrier methods}) are +more modern and powerful numerical methods for large-scale linear +programming. Such methods are especially efficient for very sparse LP +problems and allow solving such problems much faster than the simplex +method. + +In brief, the GLPK interior-point solver works as follows. + +At first, the solver transforms the original LP to a {\it working} LP +in the standard format: + +\medskip + +\noindent +\hspace{.5in} minimize +$$z = c_1x_{m+1} + c_2x_{m+2} + \dots + c_nx_{m+n} + c_0 \eqno (2.4)$$ +\hspace{.5in} subject to linear constraints +$$ +\begin{array}{r@{\:}c@{\:}r@{\:}c@{\:}r@{\:}c@{\:}l} +a_{11}x_{m+1}&+&a_{12}x_{m+2}&+ \dots +&a_{1n}x_{m+n}&=&b_1 \\ +a_{21}x_{m+1}&+&a_{22}x_{m+2}&+ \dots +&a_{2n}x_{m+n}&=&b_2 \\ +\multicolumn{7}{c} +{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .} \\ +a_{m1}x_{m+1}&+&a_{m2}x_{m+2}&+ \dots +&a_{mn}x_{m+n}&=&b_m \\ +\end{array} \eqno (2.5) +$$ +\hspace{.5in} and non-negative variables +$$x_1\geq 0,\ \ x_2\geq 0,\ \ \dots,\ \ x_n\geq 0 \eqno(2.6)$$ +where: $z$ is the objective function; $x_1$, \dots, $x_n$ are variables; +$c_1$, \dots, $c_n$ are objective coefficients; $c_0$ is a constant term +of the objective function;\linebreak $a_{11}$, \dots, $a_{mn}$ are +constraint coefficients; $b_1$, \dots, $b_m$ are right-hand sides. + +Using vector and matrix notations the working LP (2.4)---(2.6) can be +written as follows: +$$z=c^Tx+c_0\ \rightarrow\ \min,\eqno(2.7)$$ +$$Ax=b,\eqno(2.8)$$ +$$x\geq 0,\eqno(2.9)$$ +where: $x=(x_j)$ is $n$-vector of variables, $c=(c_j)$ is $n$-vector of +objective coefficients, $A=(a_{ij})$ is $m\times n$-matrix of +constraint coefficients, and $b=(b_i)$ is $m$-vector of right-hand +sides. + +Karush--Kuhn--Tucker optimality conditions for LP (2.7)---(2.9) are the +following: + +\newpage + +$$Ax=b,\eqno(2.10)$$ +$$A^T\pi+\lambda=c,\eqno(2.11)$$ +$$\lambda^Tx=0,\eqno(2.12)$$ +$$x\geq 0,\ \ \lambda\geq 0,\eqno(2.13)$$ +where: $\pi$ is $m$-vector of Lagrange multipliers (dual variables) for +equality constraints (2.8), $\lambda$ is $n$-vector of Lagrange +multipliers (dual variables) for non-negativity constraints (2.9), +(2.10) is the primal feasibility condition, (2.11) is the dual +feasibility condition, (2.12) is the primal-dual complementarity +condition, and (2.13) is the non-negativity conditions. + +The main idea of the primal-dual interior-point method is based on +finding a point in the primal-dual space (i.e. in the space of all +primal and dual variables $x$, $\pi$, and $\lambda$), which satisfies +to all optimality conditions (2.10)---(2.13). Obviously, $x$-component +of such point then provides an optimal solution to the working LP +(2.7)---(2.9). + +To find the optimal point $(x^*,\pi^*,\lambda^*)$ the interior-point +method attempts to solve the system of equations (2.10)---(2.12), which +is closed in the sense that the number of variables $x_j$, $\pi_i$, and +$\lambda_j$ and the number equations are the same and equal to $m+2n$. +Due to condition (2.12) this system of equations is non-linear, so it +can be solved with a version of {\it Newton's method} provided with +additional rules to keep the current point within the positive orthant +as required by the non-negativity conditions (2.13). + +Finally, once the optimal point $(x^*,\pi^*,\lambda^*)$ has been found, +the solver performs inverse transformations to recover corresponding +solution to the original LP passed to the solver from the application +program. + +\subsection{glp\_interior---solve LP problem with the interior-point +method} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_interior(glp_prob *P, const glp_iptcp *parm); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_interior| is a driver to the LP solver based on +the primal-dual interior-point method. This routine retrieves problem +data from the specified problem object, calls the solver to solve the +problem instance, and stores results of computations back into the +problem object. + +The interior-point solver has a set of control parameters. Values of +the control parameters can be passed in the structure \verb|glp_iptcp|, +which the parameter \verb|parm| points to. For detailed description of +this structure see paragraph ``Control parameters'' below. Before +specifying some control parameters the application program should +initialize the structure \verb|glp_iptcp| by default values of all +control parameters using the routine \verb|glp_init_iptcp| (see the +next subsection). This is needed for backward compatibility, because in +the future there may appear new members in the structure +\verb|glp_iptcp|. + +The parameter \verb|parm| can be specified as \verb|NULL|, in which +case the solver uses default settings. + +\subsubsection*{Returns} + +\def\arraystretch{1} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & The LP problem instance has been successfully solved. (This code +does {\it not} necessarily mean that the solver has found optimal +solution. It only means that the solution process was successful.) \\ +\verb|GLP_EFAIL| & The problem has no rows/columns.\\ +\verb|GLP_ENOCVG| & Very slow convergence or divergence.\\ +\verb|GLP_EITLIM| & Iteration limit exceeded.\\ +\verb|GLP_EINSTAB| & Numerical instability on solving Newtonian +system.\\ +\end{tabular} + +\subsubsection*{Comments} + +The routine \verb|glp_interior| implements an easy version of +the primal-dual interior-point method based on Mehrotra's +technique.\footnote{S. Mehrotra. On the implementation of a primal-dual +interior point method. SIAM J. on Optim., 2(4), pp. 575-601, 1992.} + +Note that currently the GLPK interior-point solver does not include +many important features, in particular: + +$\bullet$ it is not able to process dense columns. Thus, if the +constraint matrix of the LP problem has dense columns, the solving +process may be inefficient; + +$\bullet$ it has no features against numerical instability. For some +LP problems premature termination may happen if the matrix $ADA^T$ +becomes singular or ill-conditioned; + +$\bullet$ it is not able to identify the optimal basis, which +corresponds to the interior-point solution found. + +\newpage + +\subsubsection*{Terminal output} + +Solving large LP problems may take a long time, so the solver reports +some information about every interior-point iteration,\footnote{Unlike +the simplex method the interior point method usually needs 30---50 +iterations (independently on the problem size) in order to find an +optimal solution.} which is sent to the terminal. This information has +the following format: + +\begin{verbatim} +nnn: obj = fff; rpi = ppp; rdi = ddd; gap = ggg +\end{verbatim} + +\noindent where: \verb|nnn| is iteration number, \verb|fff| is the +current value of the objective function (in the case of maximization it +has wrong sign), \verb|ppp| is the current relative primal +infeasibility (cf. (2.10)): +$$\frac{\|Ax^{(k)}-b\|}{1+\|b\|},\eqno(2.14)$$ +\verb|ddd| is the current relative dual infeasibility (cf. (2.11)): +$$\frac{\|A^T\pi^{(k)}+\lambda^{(k)}-c\|}{1+\|c\|},\eqno(2.15)$$ +\verb|ggg| is the current primal-dual gap (cf. (2.12)): +$$\frac{|c^Tx^{(k)}-b^T\pi^{(k)}|}{1+|c^Tx^{(k)}|},\eqno(2.16)$$ +and $[x^{(k)},\pi^{(k)},\lambda^{(k)}]$ is the current point on $k$-th +iteration, $k=0,1,2,\dots$\ . Note that all solution components are +internally scaled, so information sent to the terminal is suitable only +for visual inspection. + +\subsubsection*{Control parameters} + +This paragraph describes all control parameters currently used in the +interior-point solver. Symbolic names of control parameters are names of +corresponding members in the structure \verb|glp_iptcp|. + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int msg\_lev} (default: {\tt GLP\_MSG\_ALL})} +\\ +&Message level for terminal output:\\ +&\verb|GLP_MSG_OFF|---no output;\\ +&\verb|GLP_MSG_ERR|---error and warning messages only;\\ +&\verb|GLP_MSG_ON |---normal output;\\ +&\verb|GLP_MSG_ALL|---full output (including informational messages). +\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int ord\_alg} (default: {\tt GLP\_ORD\_AMD})} +\\ +&Ordering algorithm used prior to Cholesky factorization:\\ +&\verb|GLP_ORD_NONE |---use natural (original) ordering;\\ +&\verb|GLP_ORD_QMD |---quotient minimum degree (QMD);\\ +&\verb|GLP_ORD_AMD |---approximate minimum degree (AMD);\\ +&\verb|GLP_ORD_SYMAMD|---approximate minimum degree (SYMAMD).\\ +\end{tabular} + +\subsubsection*{Example} + +The following main program reads LP problem instance in fixed MPS +format from file \verb|25fv47.mps|,\footnote{This instance in fixed MPS +format can be found in the Netlib LP collection; see +{\tt ftp://ftp.netlib.org/lp/data/}.} solves it with the interior-point +solver, and writes the solution to file \verb|25fv47.txt|. + +\begin{footnotesize} +\begin{verbatim} +/* iptsamp.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *P; + P = glp_create_prob(); + glp_read_mps(P, GLP_MPS_DECK, NULL, "25fv47.mps"); + glp_interior(P, NULL); + glp_print_ipt(P, "25fv47.txt"); + glp_delete_prob(P); + return 0; +} + +/* eof */ +\end{verbatim} +\end{footnotesize} + +\noindent +Below here is shown the terminal output from this example program. + +\begin{footnotesize} +\begin{verbatim} +Reading problem data from `25fv47.mps'... +Problem: 25FV47 +Objective: R0000 +822 rows, 1571 columns, 11127 non-zeros +6919 records were read +Original LP has 822 row(s), 1571 column(s), and 11127 non-zero(s) +Working LP has 821 row(s), 1876 column(s), and 10705 non-zero(s) +Matrix A has 10705 non-zeros +Matrix S = A*A' has 11895 non-zeros (upper triangle) +Minimal degree ordering... +Computing Cholesky factorization S = L'*L... +Matrix L has 35411 non-zeros +Guessing initial point... +Optimization begins... + 0: obj = 1.823377629e+05; rpi = 1.3e+01; rdi = 1.4e+01; gap = 9.3e-01 + 1: obj = 9.260045192e+04; rpi = 5.3e+00; rdi = 5.6e+00; gap = 6.8e+00 + 2: obj = 3.596999742e+04; rpi = 1.5e+00; rdi = 1.2e+00; gap = 1.8e+01 + 3: obj = 1.989627568e+04; rpi = 4.7e-01; rdi = 3.0e-01; gap = 1.9e+01 + 4: obj = 1.430215557e+04; rpi = 1.1e-01; rdi = 8.6e-02; gap = 1.4e+01 + 5: obj = 1.155716505e+04; rpi = 2.3e-02; rdi = 2.4e-02; gap = 6.8e+00 + 6: obj = 9.660273208e+03; rpi = 6.7e-03; rdi = 4.6e-03; gap = 3.9e+00 + 7: obj = 8.694348283e+03; rpi = 3.7e-03; rdi = 1.7e-03; gap = 2.0e+00 + 8: obj = 8.019543639e+03; rpi = 2.4e-03; rdi = 3.9e-04; gap = 1.0e+00 + 9: obj = 7.122676293e+03; rpi = 1.2e-03; rdi = 1.5e-04; gap = 6.6e-01 + 10: obj = 6.514534518e+03; rpi = 6.1e-04; rdi = 4.3e-05; gap = 4.1e-01 + 11: obj = 6.361572203e+03; rpi = 4.8e-04; rdi = 2.2e-05; gap = 3.0e-01 + 12: obj = 6.203355508e+03; rpi = 3.2e-04; rdi = 1.7e-05; gap = 2.6e-01 + 13: obj = 6.032943411e+03; rpi = 2.0e-04; rdi = 9.3e-06; gap = 2.1e-01 + 14: obj = 5.796553021e+03; rpi = 9.8e-05; rdi = 3.2e-06; gap = 1.0e-01 + 15: obj = 5.667032431e+03; rpi = 4.4e-05; rdi = 1.1e-06; gap = 5.6e-02 + 16: obj = 5.613911867e+03; rpi = 2.5e-05; rdi = 4.1e-07; gap = 3.5e-02 + 17: obj = 5.560572626e+03; rpi = 9.9e-06; rdi = 2.3e-07; gap = 2.1e-02 + 18: obj = 5.537276001e+03; rpi = 5.5e-06; rdi = 8.4e-08; gap = 1.1e-02 + 19: obj = 5.522746942e+03; rpi = 2.2e-06; rdi = 4.0e-08; gap = 6.7e-03 + 20: obj = 5.509956679e+03; rpi = 7.5e-07; rdi = 1.8e-08; gap = 2.9e-03 + 21: obj = 5.504571733e+03; rpi = 1.6e-07; rdi = 5.8e-09; gap = 1.1e-03 + 22: obj = 5.502576367e+03; rpi = 3.4e-08; rdi = 1.0e-09; gap = 2.5e-04 + 23: obj = 5.502057119e+03; rpi = 8.1e-09; rdi = 3.0e-10; gap = 7.7e-05 + 24: obj = 5.501885996e+03; rpi = 9.4e-10; rdi = 1.2e-10; gap = 2.4e-05 + 25: obj = 5.501852464e+03; rpi = 1.4e-10; rdi = 1.2e-11; gap = 3.0e-06 + 26: obj = 5.501846549e+03; rpi = 1.4e-11; rdi = 1.2e-12; gap = 3.0e-07 + 27: obj = 5.501845954e+03; rpi = 1.4e-12; rdi = 1.2e-13; gap = 3.0e-08 + 28: obj = 5.501845895e+03; rpi = 1.5e-13; rdi = 1.2e-14; gap = 3.0e-09 +OPTIMAL SOLUTION FOUND +Writing interior-point solution to `25fv47.txt'... +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_init\_iptcp---initialize interior-point solver control +parameters} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_init_iptcp(glp_iptcp *parm); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_init_iptcp| initializes control parameters, which +are used by the interior-point solver, with default values. + +Default values of the control parameters are stored in the structure +\verb|glp_iptcp|, which the parameter \verb|parm| points to. + +\subsection{glp\_ipt\_status---determine solution status} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ipt_status(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ipt_status| reports the status of a solution +found by the interior-point solver as follows: + +\begin{tabular}{@{}p{25mm}p{91.3mm}@{}} +\verb|GLP_UNDEF| & interior-point solution is undefined. \\ +\verb|GLP_OPT| & interior-point solution is optimal. \\ +\verb|GLP_INFEAS|& interior-point solution is infeasible. \\ +\verb|GLP_NOFEAS|& no feasible primal-dual solution exists.\\ +\end{tabular} + +\subsection{glp\_ipt\_obj\_val---retrieve objective value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_ipt_obj_val(glp_prob *lp); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ipt_obj_val| returns value of the objective +function for interior-point solution. + +\subsection{glp\_ipt\_row\_prim---retrieve row primal value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_ipt_row_prim(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ipt_row_prim| returns primal value of the +auxiliary variable associated with \verb|i|-th row. + +\newpage + +\subsection{glp\_ipt\_row\_dual---retrieve row dual value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_ipt_row_dual(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ipt_row_dual| returns dual value (i.e. reduced +cost) of the auxiliary variable associated with \verb|i|-th row. + +\subsection{glp\_ipt\_col\_prim---retrieve column primal value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_ipt_col_prim(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ipt_col_prim| returns primal value of the +structural variable associated with \verb|j|-th column. + +\subsection{glp\_ipt\_col\_dual---retrieve column dual value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_ipt_col_dual(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ipt_col_dual| returns dual value (i.e. reduced +cost) of the structural variable associated with \verb|j|-th column. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Mixed integer programming routines} + +\subsection{glp\_set\_col\_kind---set (change) column kind} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_col_kind(glp_prob *mip, int j, int kind); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_col_kind| sets (changes) the kind of +\verb|j|-th column (structural variable) as specified by the parameter +\verb|kind|: + +\begin{tabular}{@{}ll} +\verb|GLP_CV| & continuous variable; \\ +\verb|GLP_IV| & integer variable; \\ +\verb|GLP_BV| & binary variable. \\ +\end{tabular} + +%If a column is set to \verb|GLP_IV|, its bounds must be exact integer +%numbers with no tolerance, such that the condition +%\verb|bnd == floor(bnd)| would hold. + +Setting a column to \verb|GLP_BV| has the same effect as if it were +set to \verb|GLP_IV|, its lower bound were set 0, and its upper bound +were set to 1. + +\subsection{glp\_get\_col\_kind---retrieve column kind} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_col_kind(glp_prob *mip, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_kind| returns the kind of \verb|j|-th +column (structural variable) as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_CV| & continuous variable; \\ +\verb|GLP_IV| & integer variable; \\ +\verb|GLP_BV| & binary variable. \\ +\end{tabular} + +\subsection{glp\_get\_num\_int---retrieve number of integer columns} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_num_int(glp_prob *mip); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_num_int| returns the number of columns +(structural variables), which are marked as integer. Note that this +number {\it does} include binary columns. + +\subsection{glp\_get\_num\_bin---retrieve number of binary columns} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_num_bin(glp_prob *mip); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_num_bin| returns the number of columns +(structural variables), which are marked as integer and whose lower +bound is zero and upper bound is one. + +\subsection{glp\_intopt---solve MIP problem with the branch-and-cut +method} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_intopt(glp_prob *mip, const glp_iocp *parm); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_intopt| is a driver to the MIP solver based on +the branch-and-cut method, which is a hybrid of branch-and-bound and +cutting plane methods. + +If the presolver is disabled (see paragraph ``Control parameters'' +below), on entry to the routine \verb|glp_intopt| the problem object, +which the parameter \verb|mip| points to, should contain optimal +solution to LP relaxation (it can be obtained, for example, with the +routine \verb|glp_simplex|). Otherwise, if the presolver is enabled, it +is not necessary. + +The MIP solver has a set of control parameters. Values of the control +parameters can be passed in the structure \verb|glp_iocp|, which the +parameter \verb|parm| points to. For detailed description of this +structure see paragraph ``Control parameters'' below. Before specifying +some control parameters the application program should initialize the +structure \verb|glp_iocp| by default values of all control parameters +using the routine \verb|glp_init_iocp| (see the next subsection). This +is needed for backward compatibility, because in the future there may +appear new members in the structure \verb|glp_iocp|. + +The parameter \verb|parm| can be specified as \verb|NULL|, in which case +the solver uses default settings. + +Note that the GLPK branch-and-cut solver is not perfect, so it is unable +to solve hard or very large scale MIP instances for a reasonable time. + +\subsubsection*{Returns} + +\def\arraystretch{1} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & The MIP problem instance has been successfully solved. (This code +does {\it not} necessarily mean that the solver has found optimal +solution. It only means that the solution process was successful.) \\ +\verb|GLP_EBOUND| & Unable to start the search, because some +double-bounded variables have incorrect bounds or some integer variables +have non-integer (fractional) bounds.\\ +\verb|GLP_EROOT| & Unable to start the search, because optimal basis for +initial LP relaxation is not provided. (This code may appear only if the +presolver is disabled.)\\ +\verb|GLP_ENOPFS| & Unable to start the search, because LP relaxation +of the MIP problem instance has no primal feasible solution. (This code +may appear only if the presolver is enabled.)\\ +\verb|GLP_ENODFS| & Unable to start the search, because LP relaxation +of the MIP problem instance has no dual feasible solution. In other +word, this code means that if the LP relaxation has at least one primal +feasible solution, its optimal solution is unbounded, so if the MIP +problem has at least one integer feasible solution, its (integer) +optimal solution is also unbounded. (This code may appear only if the +presolver is enabled.)\\ +\verb|GLP_EFAIL| & The search was prematurely terminated due to the +solver failure.\\ +\verb|GLP_EMIPGAP| & The search was prematurely terminated, because the +relative mip gap tolerance has been reached.\\ +\verb|GLP_ETMLIM| & The search was prematurely terminated, because the +time limit has been exceeded.\\ +\verb|GLP_ESTOP| & The search was prematurely terminated by application. +(This code may appear only if the advanced solver interface is used.)\\ +\end{tabular} + +\subsubsection*{Built-in MIP presolver} + +The branch-and-cut solver has {\it built-in MIP presolver}. It is +a subprogram that transforms the original MIP problem specified in the +problem object to an equivalent MIP problem, which may be easier for +solving with the branch-and-cut method than the original one. For +example, the presolver can remove redundant constraints and variables, +whose optimal values are known, perform bound and coefficient reduction, +etc. Once the transformed MIP problem has been solved, the presolver +transforms its solution back to corresponding solution of the original +problem. + +Presolving is an optional feature of the routine \verb|glp_intopt|, and +by default it is disabled. In order to enable the MIP presolver, the +control parameter \verb|presolve| should be set to \verb|GLP_ON| (see +paragraph ``Control parameters'' below). + +\subsubsection*{Advanced solver interface} + +The routine \verb|glp_intopt| allows the user to control the +branch-and-cut search by passing to the solver a user-defined callback +routine. For more details see Chapter ``Branch-and-Cut API Routines''. + +\subsubsection*{Terminal output} + +Solving a MIP problem may take a long time, so the solver reports some +information about best known solutions, which is sent to the terminal. +This information has the following format: + +\begin{verbatim} ++nnn: mip = xxx yyy gap (ppp; qqq) +\end{verbatim} + +\noindent +where: `\verb|nnn|' is the simplex iteration number; `\verb|xxx|' is a +value of the objective function for the best known integer feasible +solution (if no integer feasible solution has been found yet, +`\verb|xxx|' is the text `\verb|not found yet|'); `\verb|rho|' is the +string `\verb|>=|' (in case of minimization) or `\verb|<=|' (in case of +maximization); `\verb|yyy|' is a global bound for exact integer optimum +(i.e. the exact integer optimum is always in the range from `\verb|xxx|' +to `\verb|yyy|'); `\verb|gap|' is the relative mip gap, in percents, +computed as $gap=|xxx-yyy|/(|xxx|+{\tt DBL\_EPSILON})\cdot 100\%$ (if +$gap$ is greater than $999.9\%$, it is not printed); `\verb|ppp|' is the +number of subproblems in the active list, `\verb|qqq|' is the number of +subproblems which have been already fathomed and therefore removed from +the branch-and-bound search tree. + +\subsubsection{Control parameters} + +This paragraph describes all control parameters currently used in the +MIP solver. Symbolic names of control parameters are names of +corresponding members in the structure \verb|glp_iocp|. + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int msg\_lev} (default: {\tt GLP\_MSG\_ALL})} +\\ +&Message level for terminal output:\\ +&\verb|GLP_MSG_OFF|---no output;\\ +&\verb|GLP_MSG_ERR|---error and warning messages only;\\ +&\verb|GLP_MSG_ON |---normal output;\\ +&\verb|GLP_MSG_ALL|---full output (including informational messages). +\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int br\_tech} (default: {\tt GLP\_BR\_DTH})} +\\ +&Branching technique option:\\ +&\verb|GLP_BR_FFV|---first fractional variable;\\ +&\verb|GLP_BR_LFV|---last fractional variable;\\ +&\verb|GLP_BR_MFV|---most fractional variable;\\ +&\verb|GLP_BR_DTH|---heuristic by Driebeck and Tomlin;\\ +&\verb|GLP_BR_PCH|---hybrid pseudocost heuristic.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int bt\_tech} (default: {\tt GLP\_BT\_BLB})} +\\ +&Backtracking technique option:\\ +&\verb|GLP_BT_DFS|---depth first search;\\ +&\verb|GLP_BT_BFS|---breadth first search;\\ +&\verb|GLP_BT_BLB|---best local bound;\\ +&\verb|GLP_BT_BPH|---best projection heuristic.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int pp\_tech} (default: {\tt GLP\_PP\_ALL})} +\\ +&Preprocessing technique option:\\ +&\verb|GLP_PP_NONE|---disable preprocessing;\\ +&\verb|GLP_PP_ROOT|---perform preprocessing only on the root level;\\ +&\verb|GLP_PP_ALL |---perform preprocessing on all levels.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int fp\_heur} (default: {\tt GLP\_OFF})} +\\ +&Feasibility pump heuristic option:\\ +&\verb|GLP_ON |---enable applying the feasibility pump heuristic;\\ +&\verb|GLP_OFF|---disable applying the feasibility pump heuristic.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int gmi\_cuts} (default: {\tt GLP\_OFF})}\\ +&Gomory's mixed integer cut option:\\ +&\verb|GLP_ON |---enable generating Gomory's cuts;\\ +&\verb|GLP_OFF|---disable generating Gomory's cuts.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int mir\_cuts} (default: {\tt GLP\_OFF})}\\ +&Mixed integer rounding (MIR) cut option:\\ +&\verb|GLP_ON |---enable generating MIR cuts;\\ +&\verb|GLP_OFF|---disable generating MIR cuts.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int cov\_cuts} (default: {\tt GLP\_OFF})}\\ +&Mixed cover cut option:\\ +&\verb|GLP_ON |---enable generating mixed cover cuts;\\ +&\verb|GLP_OFF|---disable generating mixed cover cuts.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int clq\_cuts} (default: {\tt GLP\_OFF})}\\ +&Clique cut option:\\ +&\verb|GLP_ON |---enable generating clique cuts;\\ +&\verb|GLP_OFF|---disable generating clique cuts.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double tol\_int} (default: {\tt 1e-5})}\\ +&Absolute tolerance used to check if optimal solution to the current LP +relaxation is integer feasible. (Do not change this parameter without +detailed understanding its purpose.)\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double tol\_obj} (default: {\tt 1e-7})}\\ +&Relative tolerance used to check if the objective value in optimal +solution to the current LP relaxation is not better than in the best +known integer feasible solution. (Do not change this parameter without +detailed understanding its purpose.)\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double mip\_gap} (default: {\tt 0.0})}\\ +&The relative mip gap tolerance. If the relative mip gap for currently +known best integer feasible solution falls below this tolerance, the +solver terminates the search. This allows obtainig suboptimal integer +feasible solutions if solving the problem to optimality takes too long +time.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int tm\_lim} (default: {\tt INT\_MAX})}\\ +&Searching time limit, in milliseconds.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int out\_frq} (default: {\tt 5000})}\\ +&Output frequency, in milliseconds. This parameter specifies how +frequently the solver sends information about the solution process to +the terminal.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int out\_dly} (default: {\tt 10000})}\\ +&Output delay, in milliseconds. This parameter specifies how long the +solver should delay sending information about solution of the current +LP relaxation with the simplex method to the terminal.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l} +{{\tt void (*cb\_func)(glp\_tree *tree, void *info)} +(default: {\tt NULL})}\\ +&Entry point to the user-defined callback routine. \verb|NULL| means +the advanced solver interface is not used. For more details see Chapter +``Branch-and-Cut API Routines''.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt void *cb\_info} (default: {\tt NULL})}\\ +&Transit pointer passed to the routine \verb|cb_func| (see above).\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int cb\_size} (default: {\tt 0})}\\ +&The number of extra (up to 256) bytes allocated for each node of the +branch-and-bound tree to store application-specific data. On creating +a node these bytes are initialized by binary zeros.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int presolve} (default: {\tt GLP\_OFF})}\\ +&MIP presolver option:\\ +&\verb|GLP_ON |---enable using the MIP presolver;\\ +&\verb|GLP_OFF|---disable using the MIP presolver.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int binarize} (default: {\tt GLP\_OFF})}\\ +&Binarization option (used only if the presolver is enabled):\\ +&\verb|GLP_ON |---replace general integer variables by binary ones;\\ +&\verb|GLP_OFF|---do not use binarization.\\ +\end{tabular} + +\subsection{glp\_init\_iocp---initialize integer optimizer control +parameters} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_init_iocp(glp_iocp *parm); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_init_iocp| initializes control parameters, which +are used by the branch-and-cut solver, with default values. + +Default values of the control parameters are stored in a \verb|glp_iocp| +structure, which the parameter \verb|parm| points to. + +\subsection{glp\_mip\_status---determine status of MIP solution} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_mip_status(glp_prob *mip); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_mip_status| reports the status of a MIP solution +found by the MIP solver as follows: + +\smallskip + +\begin{tabular}{@{}p{25mm}p{91.3mm}@{}} +\verb|GLP_UNDEF| & MIP solution is undefined. \\ +\verb|GLP_OPT| & MIP solution is integer optimal. \\ +\verb|GLP_FEAS| & MIP solution is integer feasible, however, its + optimality (or non-optimality) has not been proven, perhaps due to + premature termination of the search. \\ +\end{tabular} + +\begin{tabular}{@{}p{25mm}p{91.3mm}@{}} +\verb|GLP_NOFEAS| & problem has no integer feasible solution (proven by + the solver). \\ +\end{tabular} + +\subsection{glp\_mip\_obj\_val---retrieve objective value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_mip_obj_val(glp_prob *mip); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_mip_obj_val| returns value of the objective +function for MIP solution. + +\subsection{glp\_mip\_row\_val---retrieve row value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_mip_row_val(glp_prob *mip, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_mip_row_val| returns value of the auxiliary +variable associated with \verb|i|-th row for MIP solution. + +\subsection{glp\_mip\_col\_val---retrieve column value} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_mip_col_val(glp_prob *mip, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_mip_col_val| returns value of the structural +variable associated with \verb|j|-th column for MIP solution. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Additional routines} + +\subsection{lpx\_check\_kkt---check Karush-Kuhn-Tucker optimality +conditions} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void lpx_check_kkt(glp_prob *lp, int scaled, LPXKKT *kkt); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|lpx_check_kkt| checks Karush-Kuhn-Tucker optimality +conditions for basic solution. It is assumed that both primal and dual +components of basic solution are valid. + +If the parameter \verb|scaled| is zero, the optimality conditions are +checked for the original, unscaled LP problem. Otherwise, if the +parameter \verb|scaled| is non-zero, the routine checks the conditions +for an internally scaled LP problem. + +The parameter \verb|kkt| is a pointer to the structure \verb|LPXKKT|, +to which the routine stores results of the check. Members of this +structure are shown in the table below. + +\begin{table}[h] +\begin{center} +\begin{tabular}{@{}c|l|l@{}} +Condition & Member & Comment \\ +\hline +(KKT.PE) & \verb|pe_ae_max| & + Largest absolute error \\ + & \verb|pe_ae_row| & + Number of row with largest absolute error \\ + & \verb|pe_re_max| & + Largest relative error \\ + & \verb|pe_re_row| & + Number of row with largest relative error \\ + & \verb|pe_quality| & + Quality of primal solution \\ +\hline +(KKT.PB) & \verb|pb_ae_max| & + Largest absolute error \\ + & \verb|pb_ae_ind| & + Number of variable with largest absolute error \\ + & \verb|pb_re_max| & + Largest relative error \\ + & \verb|pb_re_ind| & + Number of variable with largest relative error \\ + & \verb|pb_quality| & + Quality of primal feasibility \\ +\hline +(KKT.DE) & \verb|de_ae_max| & + Largest absolute error \\ + & \verb|de_ae_col| & + Number of column with largest absolute error \\ + & \verb|de_re_max| & + Largest relative error \\ + & \verb|de_re_col| & + Number of column with largest relative error \\ + & \verb|de_quality| & + Quality of dual solution \\ +\hline +(KKT.DB) & \verb|db_ae_max| & + Largest absolute error \\ + & \verb|db_ae_ind| & + Number of variable with largest absolute error \\ + & \verb|db_re_max| & + Largest relative error \\ + & \verb|db_re_ind| & + Number of variable with largest relative error \\ + & \verb|db_quality| & + Quality of dual feasibility \\ +\end{tabular} +\end{center} +\end{table} + +The routine performs all computations using only components of the +given LP problem and the current basic solution. + +\subsubsection*{Background} + +The first condition checked by the routine is: +$$x_R - A x_S = 0, \eqno{\rm (KKT.PE)}$$ +where $x_R$ is the subvector of auxiliary variables (rows), $x_S$ is the +subvector of structural variables (columns), $A$ is the constraint +matrix. This condition expresses the requirement that all primal +variables must satisfy to the system of equality constraints of the +original LP problem. In case of exact arithmetic this condition would be +satisfied for any basic solution; however, in case of inexact +(floating-point) arithmetic, this condition shows how accurate the +primal basic solution is, that depends on accuracy of a representation +of the basis matrix used by the simplex method routines. + +The second condition checked by the routine is: +$$l_k \leq x_k \leq u_k {\rm \ \ \ for\ all}\ k=1,\dots,m+n, +\eqno{\rm (KKT.PB)}$$ +where $x_k$ is auxiliary ($1\leq k\leq m$) or structural +($m+1\leq k\leq m+n$) variable, $l_k$ and $u_k$ are, respectively, +lower and upper bounds of the variable $x_k$ (including cases of +infinite bounds). This condition expresses the requirement that all +primal variables must satisfy to bound constraints of the original LP +problem. Since in case of basic solution all non-basic variables are +placed on their bounds, actually the condition (KKT.PB) needs to be +checked for basic variables only. If the primal basic solution has +sufficient accuracy, this condition shows primal feasibility of the +solution. + +The third condition checked by the routine is: +$${\rm grad}\;Z = c = (\tilde{A})^T \pi + d,$$ +where $Z$ is the objective function, $c$ is the vector of objective +coefficients, $(\tilde{A})^T$ is a matrix transposed to the expanded +constraint matrix $\tilde{A} = (I|-A)$, $\pi$ is a vector of Lagrange +multipliers that correspond to equality constraints of the original LP +problem, $d$ is a vector of Lagrange multipliers that correspond to +bound constraints for all (auxiliary and structural) variables of the +original LP problem. Geometrically the third condition expresses the +requirement that the gradient of the objective function must belong to +the orthogonal complement of a linear subspace defined by the equality +and active bound constraints, i.e. that the gradient must be a linear +combination of normals to the constraint planes, where Lagrange +multipliers $\pi$ and $d$ are coefficients of that linear combination. + +To eliminate the vector $\pi$ the third condition can be rewritten as: +$$ +\left(\begin{array}{@{}c@{}}I \\ -A^T\end{array}\right) \pi = +\left(\begin{array}{@{}c@{}}d_R \\ d_S\end{array}\right) + +\left(\begin{array}{@{}c@{}}c_R \\ c_S\end{array}\right), +$$ +or, equivalently: +$$ +\begin{array}{r@{}c@{}c} +\pi + d_R&\ =\ &c_R, \\ +-A^T\pi + d_S&\ =\ &c_S. \\ +\end{array} +$$ +Then substituting the vector $\pi$ from the first equation into the +second one we have: +$$A^T (d_R - c_R) + (d_S - c_S) = 0, \eqno{\rm (KKT.DE)}$$ +where $d_R$ is the subvector of reduced costs of auxiliary variables +(rows), $d_S$ is the subvector of reduced costs of structural variables +(columns), $c_R$ and $c_S$ are subvectors of objective coefficients at, +respectively, auxiliary and structural variables, $A^T$ is a matrix +transposed to the constraint matrix of the original LP problem. In case +of exact arithmetic this condition would be satisfied for any basic +solution; however, in case of inexact (floating-point) arithmetic, this +condition shows how accurate the dual basic solution is, that depends on +accuracy of a representation of the basis matrix used by the simplex +method routines. + +The last, fourth condition checked by the routine is (KKT.DB): + +\medskip + +\begin{tabular}{r@{}c@{}ll} +&$\ d_k\ $& $=0,$&if $x_k$ is basic or free non-basic variable \\ +$0\leq$&$\ d_k\ $&$<+\infty$&if $x_k$ is non-basic on its lower +(minimization) \\ +&&&or upper (maximization) bound \\ +$-\infty<$&$\ d_k\ $&$\leq 0$&if $x_k$ is non-basic on its upper +(minimization) \\ +&&&or lower (maximization) bound \\ +$-\infty<$&$\ d_k\ $&$<+\infty$&if $x_k$ is non-basic fixed variable \\ +\end{tabular} + +\medskip + +\noindent +for all $k=1,\dots,m+n$, where $d_k$ is a reduced cost (Lagrange +multiplier) of auxiliary ($1\leq k\leq m$) or structural +($m+1\leq k\leq m+n$) variable $x_k$. Geometrically this condition +expresses the requirement that constraints of the original problem must +"hold" the point preventing its movement along the anti-gradient (in +case of minimization) or the gradient (in case of maximization) of the +objective function. Since in case of basic solution reduced costs of +all basic variables are placed on their (zero) bounds, actually the +condition (KKT.DB) needs to be checked for non-basic variables only. +If the dual basic solution has sufficient accuracy, this condition shows +dual feasibility of the solution. + +Should note that the complete set of Karush-Kuhn-Tucker optimality +conditions also includes the fifth, so called complementary slackness +condition, which expresses the requirement that at least either a primal +variable $x_k$ or its dual counterpart $d_k$ must be on its bound for +all $k=1,\dots,m+n$. However, being always satisfied by definition for +any basic solution that condition is not checked by the routine. + +To check the first condition (KKT.PE) the routine computes a vector of +residuals: +$$g = x_R - A x_S,$$ +determines component of this vector that correspond to largest absolute +and relative errors: + +\medskip + +\hspace{30mm} +\verb|pe_ae_max| $\displaystyle{= \max_{1\leq i\leq m}|g_i|}$, + +\medskip + +\hspace{30mm} +\verb|pe_re_max| $\displaystyle{= \max_{1\leq i\leq m} +\frac{|g_i|}{1+|(x_R)_i|}}$, + +\medskip + +\noindent +and stores these quantities and corresponding row indices to the +structure \verb|LPXKKT|. + +To check the second condition (KKT.PB) the routine computes a vector +of residuals: +$$ +h_k = \left\{ +\begin{array}{ll} +0, & {\rm if}\ l_k \leq x_k \leq u_k \\ +x_k - l_k, & {\rm if}\ x_k < l_k \\ +x_k - u_k, & {\rm if}\ x_k > u_k \\ +\end{array} +\right. +$$ +for all $k=1,\dots,m+n$, determines components of this vector that +correspond to largest absolute and relative errors: + +\medskip + +\hspace{30mm} +\verb|pb_ae_max| $\displaystyle{= \max_{1\leq k \leq m+n}|h_k|}$, + +\medskip + +\hspace{30mm} +\verb|pb_re_max| $\displaystyle{= \max_{1\leq k \leq m+n} +\frac{|h_k|}{1+|x_k|}}$, + +\medskip + +\noindent +and stores these quantities and corresponding variable indices to the +structure \verb|LPXKKT|. + +To check the third condition (KKT.DE) the routine computes a vector of +residuals: +$$u = A^T (d_R - c_R) + (d_S - c_S),$$ +determines components of this vector that correspond to largest +absolute and relative errors: + +\medskip + +\hspace{30mm} +\verb|de_ae_max| $\displaystyle{= \max_{1\leq j\leq n}|u_j|}$, + +\medskip + +\hspace{30mm} +\verb|de_re_max| $\displaystyle{= \max_{1\leq j\leq n} +\frac{|u_j|}{1+|(d_S)_j - (c_S)_j|}}$, + +\medskip + +\noindent +and stores these quantities and corresponding column indices to the +structure \verb|LPXKKT|. + +To check the fourth condition (KKT.DB) the routine computes a vector +of residuals: + +$$ +v_k = \left\{ +\begin{array}{ll} +0, & {\rm if}\ d_k\ {\rm has\ correct\ sign} \\ +d_k, & {\rm if}\ d_k\ {\rm has\ wrong\ sign} \\ +\end{array} +\right. +$$ +for all $k=1,\dots,m+n$, determines components of this vector that +correspond to largest absolute and relative errors: + +\medskip + +\hspace{30mm} +\verb|db_ae_max| $\displaystyle{= \max_{1\leq k\leq m+n}|v_k|}$, + +\medskip + +\hspace{30mm} +\verb|db_re_max| $\displaystyle{= \max_{1\leq k\leq m+n} +\frac{|v_k|}{1+|d_k - c_k|}}$, + +\medskip + +\noindent +and stores these quantities and corresponding variable indices to the +structure \verb|LPXKKT|. + +Using the relative errors for all the four conditions listed above the +routine +\verb|lpx_check_kkt| also estimates a "quality" of the basic solution +from the standpoint of these conditions and stores corresponding +quality indicators to the structure \verb|LPXKKT|: + +\verb|pe_quality|---quality of primal solution; + +\verb|pb_quality|---quality of primal feasibility; + +\verb|de_quality|---quality of dual solution; + +\verb|db_quality|---quality of dual feasibility. + +Each of these indicators is assigned to one of the following four +values: + +\verb|'H'| means high quality, + +\verb|'M'| means medium quality, + +\verb|'L'| means low quality, or + +\verb|'?'| means wrong or infeasible solution. + +If all the indicators show high or medium quality (for an internally +scaled LP problem, i.e. when the parameter \verb|scaled| in a call to +the routine \verb|lpx_check_kkt| is non-zero), the user can be sure that +the obtained basic solution is quite accurate. + +If some of the indicators show low quality, the solution can still be +considered as relevant, though an additional analysis is needed +depending on which indicator shows low quality. + +If the indicator \verb|pe_quality| is assigned to \verb|'?'|, the +primal solution is wrong. If the indicator \verb|de_quality| is assigned +to \verb|'?'|, the dual solution is wrong. + +If the indicator \verb|db_quality| is assigned to \verb|'?'| while +other indicators show a good quality, this means that the current +basic solution being primal feasible is not dual feasible. Similarly, +if the indicator \verb|pb_quality| is assigned to \verb|'?'| while +other indicators are not, this means that the current basic solution +being dual feasible is not primal feasible. + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk03.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk03.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1577 @@ +%* glpk03.tex *% + +\chapter{Utility API routines} + +\section{Problem data reading/writing routines} + +\subsection{glp\_read\_mps---read problem data in MPS format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_mps(glp_prob *lp, int fmt, const void *parm, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_mps| reads problem data in MPS format from a +text file. (The MPS format is described in Appendix \ref{champs}, page +\pageref{champs}.) + +The parameter \verb|fmt| specifies the MPS format version as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_MPS_DECK| & fixed (ancient) MPS format; \\ +\verb|GLP_MPS_FILE| & free (modern) MPS format. \\ +\end{tabular} + +The parameter \verb|parm| is reserved for use in the future and must be +specified as \verb|NULL|. + +The character string \verb|fname| specifies a name of the text file to +be read in. (If the file name ends with suffix `\verb|.gz|', the file is +assumed to be compressed, in which case the routine \verb|glp_read_mps| +decompresses it ``on the fly''.) + +Note that before reading data the current content of the problem object +is completely erased with the routine \verb|glp_erase_prob|. + +\subsubsection*{Returns} + +If the operation was successful, the routine \verb|glp_read_mps| +returns zero. Otherwise, it prints an error message and returns +non-zero. + +\subsection{glp\_write\_mps---write problem data in MPS format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_mps(glp_prob *lp, int fmt, const void *parm, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_mps| writes problem data in MPS format to a +text file. (The MPS format is described in Appendix \ref{champs}, page +\pageref{champs}.) + +The parameter \verb|fmt| specifies the MPS format version as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_MPS_DECK| & fixed (ancient) MPS format; \\ +\verb|GLP_MPS_FILE| & free (modern) MPS format. \\ +\end{tabular} + +The parameter \verb|parm| is reserved for use in the future and must be +specified as \verb|NULL|. + +The character string \verb|fname| specifies a name of the text file to +be written out. (If the file name ends with suffix `\verb|.gz|', the +file is assumed to be compressed, in which case the routine +\verb|glp_write_mps| performs automatic compression on writing it.) + +\subsubsection*{Returns} + +If the operation was successful, the routine \verb|glp_write_mps| +returns zero. Otherwise, it prints an error message and returns +non-zero. + +\subsection{glp\_read\_lp---read problem data in CPLEX LP format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_lp(glp_prob *lp, const void *parm, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_lp| reads problem data in CPLEX LP format +from a text file. (The CPLEX LP format is described in Appendix +\ref{chacplex}, page \pageref{chacplex}.) + +The parameter \verb|parm| is reserved for use in the future and must be +specified as \verb|NULL|. + +The character string \verb|fname| specifies a name of the text file to +be read in. (If the file name ends with suffix `\verb|.gz|', the file is +assumed to be compressed, in which case the routine \verb|glp_read_lp| +decompresses it ``on the fly''.) + +Note that before reading data the current content of the problem object +is completely erased with the routine \verb|glp_erase_prob|. + +\subsubsection*{Returns} + +If the operation was successful, the routine \verb|glp_read_lp| returns +zero. Otherwise, it prints an error message and returns non-zero. + +\subsection{glp\_write\_lp---write problem data in CPLEX LP format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_lp(glp_prob *lp, const void *parm, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_lp| writes problem data in CPLEX LP format +to a text file. (The CPLEX LP format is described in Appendix +\ref{chacplex}, page \pageref{chacplex}.) + +The parameter \verb|parm| is reserved for use in the future and must be +specified as \verb|NULL|. + +The character string \verb|fname| specifies a name of the text file to +be written out. (If the file name ends with suffix `\verb|.gz|', the +file is assumed to be compressed, in which case the routine +\verb|glp_write_lp| performs automatic compression on writing it.) + +\subsubsection*{Returns} + +If the operation was successful, the routine \verb|glp_write_lp| +returns zero. Otherwise, it prints an error message and returns +non-zero. + +\subsection{glp\_read\_prob---read problem data in GLPK format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_prob(glp_prob *P, int flags, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_prob| reads problem data in the GLPK LP/MIP +format from a text file. (For description of the GLPK LP/MIP format see +below.) + +The parameter \verb|flags| is reserved for use in the future and should +be specified as zero. + +The character string \verb|fname| specifies a name of the text file to +be read in. (If the file name ends with suffix `\verb|.gz|', the file +is assumed to be compressed, in which case the routine +\verb|glp_read_prob| decompresses it ``on the fly''.) + +Note that before reading data the current content of the problem object +is completely erased with the routine \verb|glp_erase_prob|. + +\subsubsection*{Returns} + +If the operation was successful, the routine \verb|glp_read_prob| +returns zero. Otherwise, it prints an error message and returns +non-zero. + +\subsubsection*{GLPK LP/MIP format} + +The GLPK LP/MIP format is a DIMACS-like format.\footnote{The DIMACS +formats were developed by the Center for Discrete Mathematics and +Theoretical Computer Science (DIMACS) to facilitate exchange of problem +data. For details see: {\tt }. } +The file in this format is a plain ASCII text file containing lines of +several types described below. A line is terminated with the end-of-line +character. Fields in each line are separated by at least one blank +space. Each line begins with a one-character designator to identify the +line type. + +The first line of the data file must be the problem line (except +optional comment lines, which may precede the problem line). The last +line of the data file must be the end line. Other lines may follow in +arbitrary order, however, duplicate lines are not allowed. + +\paragraph{Comment lines.} Comment lines give human-readable +information about the data file and are ignored by GLPK routines. +Comment lines can appear anywhere in the data file. Each comment line +begins with the lower-case character \verb|c|. + +\begin{verbatim} + c This is an example of comment line +\end{verbatim} + +\paragraph{Problem line.} There must be exactly one problem line in the +data file. This line must appear before any other lines except comment +lines and has the following format: + +\begin{verbatim} + p CLASS DIR ROWS COLS NONZ +\end{verbatim} + +The lower-case letter \verb|p| specifies that this is the problem line. + +The \verb|CLASS| field defines the problem class and can contain either +the keyword \verb|lp| (that means linear programming problem) or +\verb|mip| (that means mixed integer programming problem). + +The \verb|DIR| field defines the optimization direction (that is, the +objective function sense) and can contain either the keyword \verb|min| +(that means minimization) or \verb|max| (that means maximization). + +The \verb|ROWS|, \verb|COLS|, and \verb|NONZ| fields contain +non-negative integer values specifying, respectively, the number of +rows (constraints), columns (variables), and non-zero constraint +coefficients in the problem instance. Note that \verb|NONZ| value does +not account objective coefficients. + +\paragraph{Row descriptors.} There must be at most one row descriptor +line in the data file for each row (constraint). This line has one of +the following formats: + +\begin{verbatim} + i ROW f + i ROW l RHS + i ROW u RHS + i ROW d RHS1 RHS2 + i ROW s RHS +\end{verbatim} + +The lower-case letter \verb|i| specifies that this is the row +descriptor line. + +The \verb|ROW| field specifies the row ordinal number, an integer +between 1 and $m$, where $m$ is the number of rows in the problem +instance. + +The next lower-case letter specifies the row type as follows: + +\verb|f| --- free (unbounded) row: $-\infty<\sum a_jx_j<+\infty$; + +\verb|l| --- inequality constraint of `$\geq$' type: +$\sum a_jx_j\geq b$; + +\verb|u| --- inequality constraint of `$\leq$' type: +$\sum a_jx_j\leq b$; + +\verb|d| --- double-sided inequality constraint: +$b_1\leq\sum a_jx_j\leq b_2$; + +\verb|s| --- equality constraint: $\sum a_jx_j=b$. + +The \verb|RHS| field contains a floaing-point value specifying the +row right-hand side. The \verb|RHS1| and \verb|RHS2| fields contain +floating-point values specifying, respectively, the lower and upper +right-hand sides for the double-sided row. + +If for some row its descriptor line does not appear in the data file, +by default that row is assumed to be an equality constraint with zero +right-hand side. + +\paragraph{Column descriptors.} There must be at most one column +descriptor line in the data file for each column (variable). This line +has one of the following formats depending on the problem class +specified in the problem line: + +\bigskip + +\begin{tabular}{@{}l@{\hspace*{40pt}}l} +LP class & MIP class \\ +\hline +\verb|j COL f| & \verb|j COL KIND f| \\ +\verb|j COL l BND| & \verb|j COL KIND l BND| \\ +\verb|j COL u BND| & \verb|j COL KIND u BND| \\ +\verb|j COL d BND1 BND2| & \verb|j COL KIND d BND1 BND2| \\ +\verb|j COL s BND| & \verb|j COL KIND s BND| \\ +\end{tabular} + +\bigskip + +The lower-case letter \verb|j| specifies that this is the column +descriptor line. + +The \verb|COL| field specifies the column ordinal number, an integer +between 1 and $n$, where $n$ is the number of columns in the problem +instance. + +The \verb|KIND| field is used only for MIP problems and specifies the +column kind as follows: + +\verb|c| --- continuous column; + +\verb|i| --- integer column; + +\verb|b| --- binary column (in this case all remaining fields must be +omitted). + +The next lower-case letter specifies the column type as follows: + +\verb|f| --- free (unbounded) column: $-\infty +#include +#include + +int main(void) +{ glp_prob *lp; + glp_tran *tran; + int ret; + lp = glp_create_prob(); + tran = glp_mpl_alloc_wksp(); + ret = glp_mpl_read_model(tran, "egypt.mod", 0); + if (ret != 0) + { fprintf(stderr, "Error on translating model\n"); + goto skip; + } + ret = glp_mpl_generate(tran, NULL); + if (ret != 0) + { fprintf(stderr, "Error on generating model\n"); + goto skip; + } + glp_mpl_build_prob(tran, lp); + ret = glp_write_mps(lp, GLP_MPS_FILE, NULL, "egypt.mps"); + if (ret != 0) + fprintf(stderr, "Error on writing MPS file\n"); +skip: glp_mpl_free_wksp(tran); + glp_delete_prob(lp); + return 0; +} + +/* eof */ +\end{verbatim} +\end{small} + +\subsubsection*{Example 2} + +In this example the program reads model section from file +\verb|sudoku.mod|\footnote{This is an example model which is included +in the GLPK distribution along with alternative data file +{\tt sudoku.dat}.} ignoring data section in this file, reads alternative +data section from file \verb|sudoku.dat|, solves the problem instance +and passes the solution found back to the model. + +\begin{small} +\begin{verbatim} +/* mplsamp2.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *mip; + glp_tran *tran; + int ret; + mip = glp_create_prob(); + tran = glp_mpl_alloc_wksp(); + ret = glp_mpl_read_model(tran, "sudoku.mod", 1); + if (ret != 0) + { fprintf(stderr, "Error on translating model\n"); + goto skip; + } + ret = glp_mpl_read_data(tran, "sudoku.dat"); + if (ret != 0) + { fprintf(stderr, "Error on translating data\n"); + goto skip; + } + ret = glp_mpl_generate(tran, NULL); + if (ret != 0) + { fprintf(stderr, "Error on generating model\n"); + goto skip; + } + glp_mpl_build_prob(tran, mip); + glp_simplex(mip, NULL); + glp_intopt(mip, NULL); + ret = glp_mpl_postsolve(tran, mip, GLP_MIP); + if (ret != 0) + fprintf(stderr, "Error on postsolving model\n"); +skip: glp_mpl_free_wksp(tran); + glp_delete_prob(mip); + return 0; +} + +/* eof */ +\end{verbatim} +\end{small} + +\subsection{glp\_mpl\_alloc\_wksp---allocate the translator workspace} + +\subsubsection*{Synopsis} + +\begin{verbatim} +glp_tran *glp_mpl_alloc_wksp(void); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mpl_alloc_wksp| allocates the MathProg translator +work\-space. (Note that multiple instances of the workspace may be +allocated, if necessary.) + +\subsubsection*{Returns} + +The routine returns a pointer to the workspace, which should be used in +all subsequent operations. + +\subsection{glp\_mpl\_read\_model---read and translate model section} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_mpl_read_model(glp_tran *tran, const char *fname, + int skip); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mpl_read_model| reads model section and, +optionally, data section, which may follow the model section, from a +text file, whose name is the character string \verb|fname|, performs +translation of model statements and data blocks, and stores all the +information in the workspace. + +The parameter \verb|skip| is a flag. If the input file contains the +data section and this flag is non-zero, the data section is not read as +if there were no data section and a warning message is printed. This +allows reading data section(s) from other file(s). + +\subsubsection*{Returns} + +If the operation is successful, the routine returns zero. Otherwise +the routine prints an error message and returns non-zero. + +\subsection{glp\_mpl\_read\_data---read and translate data section} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_mpl_read_data(glp_tran *tran, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mpl_read_data| reads data section from a text +file, whose name is the character string \verb|fname|, performs +translation of data blocks, and stores the data read in the translator +workspace. If necessary, this routine may be called more than once. + +\subsubsection*{Returns} + +If the operation is successful, the routine returns zero. Otherwise +the routine prints an error message and returns non-zero. + +\subsection{glp\_mpl\_generate---generate the model} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_mpl_generate(glp_tran *tran, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mpl_generate| generates the model using its +description stored in the translator workspace. This operation means +generating all variables, constraints, and objectives, executing check +and display statements, which precede the solve statement (if it is +presented). + +The character string \verb|fname| specifies the name of an output text +file, to which output produced by display statements should be written. +If \verb|fname| is \verb|NULL|, the output is sent to the terminal. + +\subsubsection*{Returns} + +If the operation is successful, the routine returns zero. Otherwise +the routine prints an error message and returns non-zero. + +\subsection{glp\_mpl\_build\_prob---build problem instance from the +model} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_mpl_build_prob(glp_tran *tran, glp_prob *prob); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mpl_build_prob| obtains all necessary information +from the translator workspace and stores it in the specified problem +object \verb|prob|. Note that before building the current content of +the problem object is erased with the routine \verb|glp_erase_prob|. + +\subsection{glp\_mpl\_postsolve---postsolve the model} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_mpl_postsolve(glp_tran *tran, glp_prob *prob, + int sol); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mpl_postsolve| copies the solution from the +specified problem object \verb|prob| to the translator workspace and +then executes all the remaining model statements, which follow the +solve statement. + +The parameter \verb|sol| specifies which solution should be copied +from the problem object to the workspace as follows: + +\begin{tabular}{@{}ll} +\verb|GLP_SOL| & basic solution; \\ +\verb|GLP_IPT| & interior-point solution; \\ +\verb|GLP_MIP| & mixed integer solution. \\ +\end{tabular} + +\subsubsection*{Returns} + +If the operation is successful, the routine returns zero. Otherwise +the routine prints an error message and returns non-zero. + +\subsection{glp\_mpl\_free\_wksp---free the translator workspace} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_mpl_free_wksp(glp_tran *tran); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mpl_free_wksp| frees all the memory allocated to +the translator workspace. It also frees all other resources, which are +still used by the translator. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Problem solution reading/writing routines} + +\subsection{glp\_print\_sol---write basic solution in printable format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_print_sol(glp_prob *lp, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_print_sol writes| the current basic solution of +an LP problem, which is specified by the pointer \verb|lp|, to a text +file, whose name is the character string \verb|fname|, in printable +format. + +Information reported by the routine \verb|glp_print_sol| is intended +mainly for visual analysis. + +\subsubsection*{Returns} + +If no errors occurred, the routine returns zero. Otherwise the routine +prints an error message and returns non-zero. + +\subsection{glp\_read\_sol---read basic solution from text file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_sol(glp_prob *lp, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_sol| reads basic solution from a text file +whose name is specified by the parameter \verb|fname| into the problem +object. + +For the file format see description of the routine \verb|glp_write_sol|. + +\subsubsection*{Returns} + +On success the routine returns zero, otherwise non-zero. + +\newpage + +\subsection{glp\_write\_sol---write basic solution to text file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_sol(glp_prob *lp, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_sol| writes the current basic solution to a +text file whose name is specified by the parameter \verb|fname|. This +file can be read back with the routine \verb|glp_read_sol|. + +\subsubsection*{Returns} + +On success the routine returns zero, otherwise non-zero. + +\subsubsection*{File format} + +The file created by the routine \verb|glp_write_sol| is a plain text +file, which contains the following information: + +\begin{verbatim} + m n + p_stat d_stat obj_val + r_stat[1] r_prim[1] r_dual[1] + . . . + r_stat[m] r_prim[m] r_dual[m] + c_stat[1] c_prim[1] c_dual[1] + . . . + c_stat[n] c_prim[n] c_dual[n] +\end{verbatim} + +\noindent +where: + +\noindent +$m$ is the number of rows (auxiliary variables); + +\noindent +$n$ is the number of columns (structural variables); + +\noindent +\verb|p_stat| is the primal status of the basic solution +(\verb|GLP_UNDEF| = 1, \verb|GLP_FEAS| = 2, \verb|GLP_INFEAS| = 3, or +\verb|GLP_NOFEAS| = 4); + +\noindent +\verb|d_stat| is the dual status of the basic solution +(\verb|GLP_UNDEF| = 1, \verb|GLP_FEAS| = 2, \verb|GLP_INFEAS| = 3, or +\verb|GLP_NOFEAS| = 4); + +\noindent +\verb|obj_val| is the objective value; + +\noindent +\verb|r_stat[i]|, $i=1,\dots,m$, is the status of $i$-th row +(\verb|GLP_BS| = 1, \verb|GLP_NL| = 2, \verb|GLP_NU| = 3, +\verb|GLP_NF| = 4, or \verb|GLP_NS| = 5); + +\noindent +\verb|r_prim[i]|, $i=1,\dots,m$, is the primal value of $i$-th row; + +\noindent +\verb|r_dual[i]|, $i=1,\dots,m$, is the dual value of $i$-th row; + +\noindent +\verb|c_stat[j]|, $j=1,\dots,n$, is the status of $j$-th column +(\verb|GLP_BS| = 1, \verb|GLP_NL| = 2, \verb|GLP_NU| = 3, +\verb|GLP_NF| = 4, or \verb|GLP_NS| = 5); + +\noindent +\verb|c_prim[j]|, $j=1,\dots,n$, is the primal value of $j$-th column; + +\noindent +\verb|c_dual[j]|, $j=1,\dots,n$, is the dual value of $j$-th column. + +\subsection{glp\_print\_ipt---write interior-point solution in +printable format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_print_ipt(glp_prob *lp, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_print_ipt| writes the current interior point +solution of an LP problem, which the parameter \verb|lp| points to, to +a text file, whose name is the character string \verb|fname|, in +printable format. + +Information reported by the routine \verb|glp_print_ipt| is intended +mainly for visual analysis. + +\subsubsection*{Returns} + +If no errors occurred, the routine returns zero. Otherwise the routine +prints an error message and returns non-zero. + +\subsection{glp\_read\_ipt---read interior-point solution from text +file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_ipt(glp_prob *lp, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_ipt| reads interior-point solution from a +text file whose name is specified by the parameter \verb|fname| into the +problem object. + +For the file format see description of the routine \verb|glp_write_ipt|. + +\subsubsection*{Returns} + +On success the routine returns zero, otherwise non-zero. + +\subsection{glp\_write\_ipt---write interior-point solution to text +file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_ipt(glp_prob *lp, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_ipt| writes the current interior-point +solution to a text file whose name is specified by the parameter +\verb|fname|. This file can be read back with the routine +\verb|glp_read_ipt|. + +\subsubsection*{Returns} + +On success the routine returns zero, otherwise non-zero. + +\subsubsection*{File format} + +The file created by the routine \verb|glp_write_ipt| is a plain text +file, which contains the following information: + +\begin{verbatim} + m n + stat obj_val + r_prim[1] r_dual[1] + . . . + r_prim[m] r_dual[m] + c_prim[1] c_dual[1] + . . . + c_prim[n] c_dual[n] +\end{verbatim} + +\noindent +where: + +\noindent +$m$ is the number of rows (auxiliary variables); + +\noindent +$n$ is the number of columns (structural variables); + +\noindent +\verb|stat| is the solution status (\verb|GLP_UNDEF| = 1 or +\verb|GLP_OPT| = 5); + +\noindent +\verb|obj_val| is the objective value; + +\noindent +\verb|r_prim[i]|, $i=1,\dots,m$, is the primal value of $i$-th row; + +\noindent +\verb|r_dual[i]|, $i=1,\dots,m$, is the dual value of $i$-th row; + +\noindent +\verb|c_prim[j]|, $j=1,\dots,n$, is the primal value of $j$-th column; + +\noindent +\verb|c_dual[j]|, $j=1,\dots,n$, is the dual value of $j$-th column. + +\subsection{glp\_print\_mip---write MIP solution in printable format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_print_mip(glp_prob *lp, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_print_mip| writes a best known integer solution +of a MIP problem, which is specified by the pointer \verb|lp|, to a text +file, whose name is the character string \verb|fname|, in printable +format. + +Information reported by the routine \verb|glp_print_mip| is intended +mainly for visual analysis. + +\subsubsection*{Returns} + +If no errors occurred, the routine returns zero. Otherwise the routine +prints an error message and returns non-zero. + +\newpage + +\subsection{glp\_read\_mip---read MIP solution from text file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_mip(glp_prob *mip, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_mip| reads MIP solution from a text file +whose name is specified by the parameter \verb|fname| into the problem +object. + +For the file format see description of the routine \verb|glp_write_mip|. + +\subsubsection*{Returns} + +On success the routine returns zero, otherwise non-zero. + +\subsection{glp\_write\_mip---write MIP solution to text file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_mip(glp_prob *mip, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_mip| writes the current MIP solution to a +text file whose name is specified by the parameter \verb|fname|. This +file can be read back with the routine \verb|glp_read_mip|. + +\subsubsection*{Returns} + +On success the routine returns zero, otherwise non-zero. + +\subsubsection*{File format} + +The file created by the routine \verb|glp_write_sol| is a plain text +file, which contains the following information: + +\begin{verbatim} + m n + stat obj_val + r_val[1] + . . . + r_val[m] + c_val[1] + . . . + c_val[n] +\end{verbatim} + +\noindent +where: + +\noindent +$m$ is the number of rows (auxiliary variables); + +\noindent +$n$ is the number of columns (structural variables); + +\noindent +\verb|stat| is the solution status (\verb|GLP_UNDEF| = 1, +\verb|GLP_FEAS| = 2, \verb|GLP_NOFEAS| = 4, or \verb|GLP_OPT| = 5); + +\noindent +\verb|obj_val| is the objective value; + +\noindent +\verb|r_val[i]|, $i=1,\dots,m$, is the value of $i$-th row; + +\noindent +\verb|c_val[j]|, $j=1,\dots,n$, is the value of $j$-th column. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Post-optimal analysis routines} + +\subsection{glp\_print\_ranges---print sensitivity analysis report} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_print_ranges(glp_prob *P, int len, const int list[], + int flags, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_print_ranges| performs sensitivity analysis of +current optimal basic solution and writes the analysis report in +human-readable format to a text file, whose name is the character +string {\it fname}. (Detailed description of the report structure is +given below.) + +The parameter {\it len} specifies the length of the row/column list. + +The array {\it list} specifies ordinal number of rows and columns to be +analyzed. The ordinal numbers should be passed in locations +{\it list}[1], {\it list}[2], \dots, {\it list}[{\it len}]. Ordinal +numbers from 1 to $m$ refer to rows, and ordinal numbers from $m+1$ to +$m+n$ refer to columns, where $m$ and $n$ are, resp., the total number +of rows and columns in the problem object. Rows and columns appear in +the analysis report in the same order as they follow in the array list. + +It is allowed to specify $len=0$, in which case the array {\it list} is +not used (so it can be specified as \verb|NULL|), and the routine +performs analysis for all rows and columns of the problem object. + +The parameter {\it flags} is reserved for use in the future and must be +specified as zero. + +On entry to the routine \verb|glp_print_ranges| the current basic +solution must be optimal and the basis factorization must exist. +The application program can check that with the routine +\verb|glp_bf_exists|, and if the factorization does +not exist, compute it with the routine \verb|glp_factorize|. Note that +if the LP preprocessor is not used, on normal exit from the simplex +solver routine \verb|glp_simplex| the basis factorization always exists. + +\subsubsection*{Returns} + +If the operation was successful, the routine \verb|glp_print_ranges| +returns zero. Otherwise, it prints an error message and returns +non-zero. + +\subsubsection*{Analysis report example} + +An example of the sensitivity analysis report is shown on the next two +pages. This example corresponds to the example of LP problem described +in Subsection ``Example of MPS file''. + +\subsubsection*{Structure of the analysis report} + +For each row and column specified in the array {\it list} the routine +prints two lines containing generic information and analysis +information, which depends on the status of corresponding row or column. + +Note that analysis of a row is analysis of its auxiliary variable, +which is equal to the row linear form $\sum a_jx_j$, and analysis of +a column is analysis of corresponding structural variable. Therefore, +formally, on performing the sensitivity analysis there is no difference +between rows and columns. + +\bigskip + +\noindent +{\it Generic information} + +\medskip + +\noindent +{\tt No.} is the row or column ordinal number in the problem object. +Rows are numbered from 1 to $m$, and columns are numbered from 1 to $n$, +where $m$ and $n$ are, resp., the total number of rows and columns in +the problem object. + +\medskip + +\noindent +{\tt Row name} is the symbolic name assigned to the row. If the row has +no name assigned, this field contains blanks. + +\medskip + +\noindent +{\tt Column name} is the symbolic name assigned to the column. If the +column has no name assigned, this field contains blanks. + +\medskip + +\noindent +{\tt St} is the status of the row or column in the optimal solution: + +{\tt BS} --- non-active constraint (row), basic column; + +{\tt NL} --- inequality constraint having its lower right-hand side +active (row), non-basic column having its lower bound active; + +{\tt NU} --- inequality constraint having its upper right-hand side +active (row), non-basic column having its upper bound active; + +{\tt NS} --- active equality constraint (row), non-basic fixed column. + +{\tt NF} --- active free row, non-basic free (unbounded) column. (This +case means that the optimal solution is dual degenerate.) + +\medskip + +\noindent +{\tt Activity} is the (primal) value of the auxiliary variable (row) or +structural variable (column) in the optimal solution. + +\medskip + +\noindent +{\tt Slack} is the (primal) value of the row slack variable. + +\medskip + +\noindent +{\tt Obj coef} is the objective coefficient of the column (structural +variable). + +\begin{landscape} +\begin{scriptsize} +\begin{verbatim} +GLPK 4.42 - SENSITIVITY ANALYSIS REPORT Page 1 + +Problem: PLAN +Objective: VALUE = 296.2166065 (MINimum) + + No. Row name St Activity Slack Lower bound Activity Obj coef Obj value at Limiting + Marginal Upper bound range range break point variable +------ ------------ -- ------------- ------------- ------------- ------------- ------------- ------------- ------------ + 1 VALUE BS 296.21661 -296.21661 -Inf 299.25255 -1.00000 . MN + . +Inf 296.21661 +Inf +Inf + + 2 YIELD NS 2000.00000 . 2000.00000 1995.06864 -Inf 296.28365 BIN3 + -.01360 2000.00000 2014.03479 +Inf 296.02579 CU + + 3 FE NU 60.00000 . -Inf 55.89016 -Inf 306.77162 BIN4 + -2.56823 60.00000 62.69978 2.56823 289.28294 BIN3 + + 4 CU BS 83.96751 16.03249 -Inf 93.88467 -.30613 270.51157 MN + . 100.00000 79.98213 .21474 314.24798 BIN5 + + 5 MN NU 40.00000 . -Inf 34.42336 -Inf 299.25255 BIN4 + -.54440 40.00000 41.68691 .54440 295.29825 BIN3 + + 6 MG BS 19.96029 10.03971 -Inf 24.74427 -1.79618 260.36433 BIN1 + . 30.00000 9.40292 .28757 301.95652 MN + + 7 AL NL 1500.00000 . 1500.00000 1485.78425 -.25199 292.63444 CU + .25199 +Inf 1504.92126 +Inf 297.45669 BIN3 + + 8 SI NL 250.00000 50.00000 250.00000 235.32871 -.48520 289.09812 CU + .48520 300.00000 255.06073 +Inf 298.67206 BIN3 +\end{verbatim} +\end{scriptsize} +\end{landscape} + +\begin{landscape} +\begin{scriptsize} +\begin{verbatim} +GLPK 4.42 - SENSITIVITY ANALYSIS REPORT Page 2 + +Problem: PLAN +Objective: VALUE = 296.2166065 (MINimum) + + No. Column name St Activity Obj coef Lower bound Activity Obj coef Obj value at Limiting + Marginal Upper bound range range break point variable +------ ------------ -- ------------- ------------- ------------- ------------- ------------- ------------- ------------ + 1 BIN1 NL . .03000 . -28.82475 -.22362 288.90594 BIN4 + .25362 200.00000 33.88040 +Inf 304.80951 BIN4 + + 2 BIN2 BS 665.34296 .08000 . 802.22222 .01722 254.44822 BIN1 + . 2500.00000 313.43066 .08863 301.95652 MN + + 3 BIN3 BS 490.25271 .17000 400.00000 788.61314 .15982 291.22807 MN + . 800.00000 -347.42857 .17948 300.86548 BIN5 + + 4 BIN4 BS 424.18773 .12000 100.00000 710.52632 .10899 291.54745 MN + . 700.00000 -256.15524 .14651 307.46010 BIN1 + + 5 BIN5 NL . .15000 . -201.78739 .13544 293.27940 BIN3 + .01456 1500.00000 58.79586 +Inf 297.07244 BIN3 + + 6 ALUM BS 299.63899 .21000 . 358.26772 .18885 289.87879 AL + . +Inf 112.40876 .22622 301.07527 MN + + 7 SILICON BS 120.57762 .38000 . 124.27093 .14828 268.27586 BIN5 + . +Inf 85.54745 .46667 306.66667 MN + +End of report +\end{verbatim} +\end{scriptsize} +\end{landscape} + +\noindent +{\tt Marginal} is the reduced cost (dual activity) of the auxiliary +variable (row) or structural variable (column). + +\medskip + +\noindent +{\tt Lower bound} is the lower right-hand side (row) or lower bound +(column). If the row or column has no lower bound, this field contains +{\tt -Inf}. + +\medskip + +\noindent +{\tt Upper bound} is the upper right-hand side (row) or upper bound +(column). If the row or column has no upper bound, this field contains +{\tt +Inf}. + +\bigskip + +\noindent +{\it Sensitivity analysis of active bounds} + +\medskip + +\noindent +The sensitivity analysis of active bounds is performed only for rows, +which are active constraints, and only for non-basic columns, because +inactive constraints and basic columns have no active bounds. + +For every auxiliary (row) or structural (column) non-basic variable the +routine starts changing its active bound in both direction. The first +of the two lines in the report corresponds to decreasing, and the +second line corresponds to increasing of the active bound. Since the +variable being analyzed is non-basic, its activity, which is equal to +its active bound, also starts changing. This changing leads to changing +of basic (auxiliary and structural) variables, which depend on the +non-basic variable. The current basis remains primal feasible and +therefore optimal while values of all basic variables are primal +feasible, i.e. are within their bounds. Therefore, if some basic +variable called the {\it limiting variable} reaches its (lower or +upper) bound first, before any other basic variables, it thereby limits +further changing of the non-basic variable, because otherwise the +current basis would become primal infeasible. The point, at which this +happens, is called the {\it break point}. Note that there are two break +points: the lower break point, which corresponds to decreasing of the +non-basic variable, and the upper break point, which corresponds to +increasing of the non-basic variable. + +In the analysis report values of the non-basic variable (i.e. of its +active bound) being analyzed at both lower and upper break points are +printed in the field `{\tt Activity range}'. Corresponding values of +the objective function are printed in the field `{\tt Obj value at +break point}', and symbolic names of corresponding limiting basic +variables are printed in the field `{\tt Limiting variable}'. +If the active bound can decrease or/and increase unlimitedly, the field +`{\tt Activity range}' contains {\tt -Inf} or/and {\tt +Inf}, resp. + +For example (see the example report above), row SI is a double-sided +constraint, which is active on its lower bound (right-hand side), and +its activity in the optimal solution being equal to the lower bound is +250. The activity range for this row is $[235.32871,255.06073]$. This +means that the basis remains optimal while the lower bound is +increasing up to 255.06073, and further increasing is limited by +(structural) variable BIN3. If the lower bound reaches this upper break +point, the objective value becomes equal to 298.67206. + +Note that if the basis does not change, the objective function depends +on the non-basic variable linearly, and the per-unit change of the +objective function is the reduced cost (marginal value) of the +non-basic variable. + +\bigskip + +\noindent +{\it Sensitivity analysis of objective coefficients at non-basic +variables} + +\medskip + +\noindent +The sensitivity analysis of the objective coefficient at a non-basic +variable is quite simple, because in this case change in the objective +coefficient leads to equivalent change in the reduced cost (marginal +value). + +For every auxiliary (row) or structural (column) non-basic variable the +routine starts changing its objective coefficient in both direction. +(Note that auxiliary variables are not included in the objective +function and therefore always have zero objective coefficients.) The +first of the two lines in the report corresponds to decreasing, and the +second line corresponds to increasing of the objective coefficient. +This changing leads to changing of the reduced cost of the non-basic +variable to be analyzed and does affect reduced costs of all other +non-basic variables. The current basis remains dual feasible and +therefore optimal while the reduced cost keeps its sign. Therefore, if +the reduced cost reaches zero, it limits further changing of the +objective coefficient (if only the non-basic variable is non-fixed). + +In the analysis report minimal and maximal values of the objective +coefficient, on which the basis remains optimal, are printed in the +field `\verb|Obj coef range|'. If the objective coefficient can +decrease or/and increase unlimitedly, this field contains {\tt -Inf} +or/and {\tt +Inf}, resp. + +For example (see the example report above), column BIN5 is non-basic +having its lower bound active. Its objective coefficient is 0.15, and +reduced cost in the optimal solution 0.01456. The column lower bound +remains active while the column reduced cost remains non-negative, +thus, minimal value of the objective coefficient, on which the current +basis still remains optimal, is $0.15-0.01456=0.13644$, that is +indicated in the field `\verb|Obj coef range|'. + +\bigskip + +\noindent +{\it Sensitivity analysis of objective coefficients at basic variables} + +\medskip + +\noindent +To perform sensitivity analysis for every auxiliary (row) or structural +(column) variable the routine starts changing its objective coefficient +in both direction. (Note that auxiliary variables are not included in +the objective function and therefore always have zero objective +coefficients.) The first of the two lines in the report corresponds to +decreasing, and the second line corresponds to increasing of the +objective coefficient. This changing leads to changing of reduced costs +of non-basic variables. The current basis remains dual feasible and +therefore optimal while reduced costs of all non-basic variables +(except fixed variables) keep their signs. Therefore, if the reduced +cost of some non-basic non-fixed variable called the {\it limiting +variable} reaches zero first, before reduced cost of any other +non-basic non-fixed variable, it thereby limits further changing of the +objective coefficient, because otherwise the current basis would become +dual infeasible (non-optimal). The point, at which this happens, is +called the {\it break point}. Note that there are two break points: the +lower break point, which corresponds to decreasing of the objective +coefficient, and the upper break point, which corresponds to increasing +of the objective coefficient. Let the objective coefficient reach its +limit value and continue changing a bit further in the same direction +that makes the current basis dual infeasible (non-optimal). Then the +reduced cost of the non-basic limiting variable becomes ``a bit'' dual +infeasible that forces the limiting variable to enter the basis +replacing there some basic variable, which leaves the basis to keep its +primal feasibility. It should be understood that if we change the +current basis in this way exactly at the break point, both the current +and adjacent bases will be optimal with the same objective value, +because at the break point the limiting variable has zero reduced cost. +On the other hand, in the adjacent basis the value of the limiting +variable changes, because there it becomes basic, that leads to +changing of the value of the basic variable being analyzed. Note that +on determining the adjacent basis the bounds of the analyzed basic +variable are ignored as if it were a free (unbounded) variable, so it +cannot leave the current basis. + +In the analysis report lower and upper limits of the objective +coefficient at the basic variable being analyzed, when the basis +remains optimal, are printed in the field `{\tt Obj coef range}'. +Corresponding values of the objective function at both lower and upper +break points are printed in the field `{\tt Obj value at break point}', +symbolic names of corresponding non-basic limiting variables are +printed in the field `{\tt Limiting variable}', and values of the basic +variable, which it would take on in the adjacent bases (as was +explained above) are printed in the field `{\tt Activity range}'. +If the objective coefficient can increase or/and decrease unlimitedly, +the field `{\tt Obj coef range}' contains {\tt -Inf} and/or {\tt +Inf}, +resp. It also may happen that no dual feasible adjacent basis exists +(i.e. on entering the basis the limiting variable can increase or +decrease unlimitedly), in which case the field `{\tt Activity range}' +contains {\tt -Inf} and/or {\tt +Inf}. + +\newpage + +For example (see the example report above), structural variable +(column) BIN3 is basic, its optimal value is 490.25271, and its +objective coefficient is 0.17. The objective coefficient range for this +column is $[0.15982,0.17948]$. This means that the basis remains +optimal while the objective coefficient is decreasing down to 0.15982, +and further decreasing is limited by (auxiliary) variable MN. If we +make the objective coefficient a bit less than 0.15982, the limiting +variable MN will enter the basis, and in that adjacent basis the +structural variable BIN3 will take on new optimal value 788.61314. At +the lower break point, where the objective coefficient is exactly +0.15982, the objective function takes on the value 291.22807 in both +the current and adjacent bases. + +Note that if the basis does not change, the objective function depends +on the objective coefficient at the basic variable linearly, and the +per-unit change of the objective function is the value of the basic +variable. + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk04.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk04.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1411 @@ +%* glpk04.tex *% + +\chapter{Advanced API Routines} + +\section{Background} +\label{basbgd} + +Using vector and matrix notations LP problem (1.1)---(1.3) (see Section +\ref{seclp}, page \pageref{seclp}) can be stated as follows: + +\medskip + +\noindent +\hspace{.5in} minimize (or maximize) +$$z=c^Tx_S+c_0\eqno(3.1)$$ +\hspace{.5in} subject to linear constraints +$$x_R=Ax_S\eqno(3.2)$$ +\hspace{.5in} and bounds of variables +$$ +\begin{array}{l@{\ }c@{\ }l@{\ }c@{\ }l} +l_R&\leq&x_R&\leq&u_R\\ +l_S&\leq&x_S&\leq&u_S\\ +\end{array}\eqno(3.3) +$$ +where: + +\noindent +$x_R=(x_1,\dots,x_m)$ is the vector of auxiliary variables; + +\noindent +$x_S=(x_{m+1},\dots,x_{m+n})$ is the vector of structural +variables; + +\noindent +$z$ is the objective function; + +\noindent +$c=(c_1,\dots,c_n)$ is the vector of objective coefficients; + +\noindent +$c_0$ is the constant term (``shift'') of the objective function; + +\noindent +$A=(a_{11},\dots,a_{mn})$ is the constraint matrix; + +\noindent +$l_R=(l_1,\dots,l_m)$ is the vector of lower bounds of auxiliary +variables; + +\noindent +$u_R=(u_1,\dots,u_m)$ is the vector of upper bounds of auxiliary +variables; + +\noindent +$l_S=(l_{m+1},\dots,l_{m+n})$ is the vector of lower bounds of +structural variables; + +\noindent +$u_S=(u_{m+1},\dots,u_{m+n})$ is the vector of upper bounds of +structural variables. + +\medskip + +From the simplex method's standpoint there is no difference between +auxiliary and structural variables. This allows combining all these +variables into one vector that leads to the following problem statement: + +\medskip + +\noindent +\hspace{.5in} minimize (or maximize) +$$z=(0\ |\ c)^Tx+c_0\eqno(3.4)$$ +\hspace{.5in} subject to linear constraints +$$(I\ |-\!A)x=0\eqno(3.5)$$ +\hspace{.5in} and bounds of variables +$$l\leq x\leq u\eqno(3.6)$$ +where: + +\noindent +$x=(x_R\ |\ x_S)$ is the $(m+n)$-vector of (all) variables; + +\noindent +$(0\ |\ c)$ is the $(m+n)$-vector of objective +coefficients;\footnote{Subvector 0 corresponds to objective coefficients +at auxiliary variables.} + +\noindent +$(I\ |-\!A)$ is the {\it augmented} constraint +$m\times(m+n)$-matrix;\footnote{Note that due to auxiliary variables +matrix $(I\ |-\!A)$ contains the unity submatrix and therefore has full +rank. This means, in particular, that the system (3.5) has no linearly +dependent constraints.} + +\noindent +$l=(l_R\ |\ l_S)$ is the $(m+n)$-vector of lower bounds of (all) +variables; + +\noindent +$u=(u_R\ |\ u_S)$ is the $(m+n)$-vector of upper bounds of (all) +variables. + +\medskip + +By definition an {\it LP basic solution} geometrically is a point in +the space of all variables, which is the intersection of planes +corresponding to active constraints\footnote{A constraint is called +{\it active} if in a given point it is satisfied as equality, otherwise +it is called {\it inactive}.}. The space of all variables has the +dimension $m+n$, therefore, to define some basic solution we have to +define $m+n$ active constraints. Note that $m$ constraints (3.5) being +linearly independent equalities are always active, so remaining $n$ +active constraints can be chosen only from bound constraints (3.6). + +A variable is called {\it non-basic}, if its (lower or upper) bound is +active, otherwise it is called {\it basic}. Since, as was said above, +exactly $n$ bound constraints must be active, in any basic solution +there are always $n$ non-basic variables and $m$ basic variables. +(Note that a free variable also can be non-basic. Although such +variable has no bounds, we can think it as the difference between two +non-negative variables, which both are non-basic in this case.) + +Now consider how to determine numeric values of all variables for a +given basic solution. + +Let $\Pi$ be an appropriate permutation matrix of the order $(m+n)$. +Then we can write: +$$\left(\begin{array}{@{}c@{}}x_B\\x_N\\\end{array}\right)= +\Pi\left(\begin{array}{@{}c@{}}x_R\\x_S\\\end{array}\right)=\Pi x, +\eqno(3.7)$$ +where $x_B$ is the vector of basic variables, $x_N$ is the vector of +non-basic variables, $x=(x_R\ |\ x_S)$ is the vector of all variables +in the original order. In this case the system of linear constraints +(3.5) can be rewritten as follows: +$$(I\ |-\!A)\Pi^T\Pi x=0\ \ \ \Rightarrow\ \ \ (B\ |\ N) +\left(\begin{array}{@{}c@{}}x_B\\x_N\\\end{array}\right)=0,\eqno(3.8)$$ +where +$$(B\ |\ N)=(I\ |-\!A)\Pi^T.\eqno(3.9)$$ +Matrix $B$ is a square non-singular $m\times m$-matrix, which is +composed from columns of the augmented constraint matrix corresponding +to basic variables. It is called the {\it basis matrix} or simply the +{\it basis}. Matrix $N$ is a rectangular $m\times n$-matrix, which is +composed from columns of the augmented constraint matrix corresponding +to non-basic variables. + +From (3.8) it follows that: +$$Bx_B+Nx_N=0,\eqno(3.10)$$ +therefore, +$$x_B=-B^{-1}Nx_N.\eqno(3.11)$$ +Thus, the formula (3.11) shows how to determine numeric values of basic +variables $x_B$ assuming that non-basic variables $x_N$ are fixed on +their active bounds. + +The $m\times n$-matrix +$$\Xi=-B^{-1}N,\eqno(3.12)$$ +which appears in (3.11), is called the {\it simplex +tableau}.\footnote{This definition corresponds to the GLPK +implementation.} It shows how basic variables depend on non-basic +variables: +$$x_B=\Xi x_N.\eqno(3.13)$$ + +The system (3.13) is equivalent to the system (3.5) in the sense that +they both define the same set of points in the space of (primal) +variables, which satisfy to these systems. If, moreover, values of all +basic variables satisfy to their bound constraints (3.3), the +corresponding basic solution is called {\it (primal) feasible}, +otherwise {\it (primal) infeasible}. It is understood that any (primal) +feasible basic solution satisfy to all constraints (3.2) and (3.3). + +The LP theory says that if LP has optimal solution, it has (at least +one) basic feasible solution, which corresponds to the optimum. And the +most natural way to determine whether a given basic solution is optimal +or not is to use the Karush---Kuhn---Tucker optimality conditions. + +\def\arraystretch{1.5} + +For the problem statement (3.4)---(3.6) the optimality conditions are +the following:\footnote{These conditions can be appiled to any solution, +not only to a basic solution.} +$$(I\ |-\!A)x=0\eqno(3.14)$$ +$$(I\ |-\!A)^T\pi+\lambda_l+\lambda_u=\nabla z=(0\ |\ c)^T\eqno(3.15)$$ +$$l\leq x\leq u\eqno(3.16)$$ +$$\lambda_l\geq 0,\ \ \lambda_u\leq 0\ \ \mbox{(minimization)} +\eqno(3.17)$$ +$$\lambda_l\leq 0,\ \ \lambda_u\geq 0\ \ \mbox{(maximization)} +\eqno(3.18)$$ +$$(\lambda_l)_k(x_k-l_k)=0,\ \ (\lambda_u)_k(x_k-u_k)=0,\ \ k=1,2,\dots, +m+n\eqno(3.19)$$ +where: +$\pi=(\pi_1,\pi_2,\dots,\pi_m)$ is a $m$-vector of Lagrange +multipliers for equality constraints (3.5); +$\lambda_l=[(\lambda_l)_1,(\lambda_l)_2,\dots,(\lambda_l)_n]$ is a +$n$-vector of Lagrange multipliers for lower bound constraints (3.6); +$\lambda_u=[(\lambda_u)_1,(\lambda_u)_2,\dots,(\lambda_u)_n]$ is a +$n$-vector of Lagrange multipliers for upper bound constraints (3.6). + +Condition (3.14) is the {\it primal} (original) system of equality +constraints (3.5). + +Condition (3.15) is the {\it dual} system of equality constraints. +It requires the gradient of the objective function to be a linear +combination of normals to the planes defined by constraints of the +original problem. + +Condition (3.16) is the primal (original) system of bound constraints +(3.6). + +Condition (3.17) (or (3.18) in case of maximization) is the dual system +of bound constraints. + +Condition (3.19) is the {\it complementary slackness condition}. It +requires, for each original (auxiliary or structural) variable $x_k$, +that either its (lower or upper) bound must be active, or zero bound of +the corresponding Lagrange multiplier ($(\lambda_l)_k$ or +$(\lambda_u)_k$) must be active. + +In GLPK two multipliers $(\lambda_l)_k$ and $(\lambda_u)_k$ for each +primal (original) variable $x_k$, $k=1,2,\dots,m+n$, are combined into +one multiplier: +$$\lambda_k=(\lambda_l)_k+(\lambda_u)_k,\eqno(3.20)$$ +which is called a {\it dual variable} for $x_k$. This {\it cannot} lead +to the ambiguity, because both lower and upper bounds of $x_k$ cannot be +active at the same time,\footnote{If $x_k$ is a fixed variable, we can +think it as double-bounded variable $l_k\leq x_k\leq u_k$, where +$l_k=u_k.$} so at least one of $(\lambda_l)_k$ and $(\lambda_u)_k$ must +be equal to zero, and because these multipliers have different signs, +the combined multiplier, which is their sum, uniquely defines each of +them. + +\def\arraystretch{1} + +Using dual variables $\lambda_k$ the dual system of bound constraints +(3.17) and (3.18) can be written in the form of so called {\it ``rule of +signs''} as follows: + +\begin{center} +\begin{tabular}{|@{\,}c@{$\,$}|@{$\,$}c@{$\,$}|@{$\,$}c@{$\,$}| +@{$\,$}c|c@{$\,$}|@{$\,$}c@{$\,$}|@{$\,$}c@{$\,$}|} +\hline +Original bound&\multicolumn{3}{c|}{Minimization}&\multicolumn{3}{c|} +{Maximization}\\ +\cline{2-7} +constraint&$(\lambda_l)_k$&$(\lambda_u)_k$&$(\lambda_l)_k+ +(\lambda_u)_k$&$(\lambda_l)_k$&$(\lambda_u)_k$&$(\lambda_l)_k+ +(\lambda_u)_k$\\ +\hline +$-\infty= {\tt piv\_tol}\cdot\max|u_{i*}|$, i.e. if it is not very +small in the magnitude among other elements in the same row. Decreasing +this parameter may lead to better sparsity at the expense of numerical +accuracy, and vice versa.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int piv\_lim} (default: {\tt 4})} \\ +&This parameter is used on computing $LU$-factorization of the basis +matrix and specifies how many pivot candidates needs to be considered +on choosing a pivot element, \verb|piv_lim| $\geq$ 1. If \verb|piv_lim| +candidates have been considered, the pivoting routine prematurely +terminates the search with the best candidate found.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int suhl} (default: {\tt GLP\_ON})} \\ +&This parameter is used on computing $LU$-factorization of the basis +matrix. Being set to {\tt GLP\_ON} it enables applying the following +heuristic proposed by Uwe Suhl: if a column of the active submatrix has +no eligible pivot candidates, it is no more considered until it becomes +a column singleton. In many cases this allows reducing the time needed +for pivot searching. To disable this heuristic the parameter \verb|suhl| +should be set to {\tt GLP\_OFF}.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double eps\_tol} (default: {\tt 1e-15})} \\ +&Epsilon tolerance, \verb|eps_tol| $\geq$ 0, used on computing +$LU$-factorization of the basis matrix. If an element of the active +submatrix of factor $U$ is less than \verb|eps_tol| in the magnitude, +it is replaced by exact zero.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double max\_gro} (default: {\tt 1e+10})} \\ +&Maximal growth of elements of factor $U$, \verb|max_gro| $\geq$ 1, +allowable on computing $LU$-factorization of the basis matrix. If on +some elimination step the ratio $u_{big}/b_{max}$ (where $u_{big}$ is +the largest magnitude of elements of factor $U$ appeared in its active +submatrix during all the factorization process, $b_{max}$ is the largest +magnitude of elements of the basis matrix to be factorized), the basis +matrix is considered as ill-conditioned.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int nfs\_max} (default: {\tt 100})} \\ +&Maximal number of additional row-like factors (entries of the eta +file), \verb|nfs_max| $\geq$ 1, which can be added to $LU$-factorization +of the basis matrix on updating it with the Forrest--Tomlin technique. +This parameter is used only once, before $LU$-factorization is computed +for the first time, to allocate working arrays. As a rule, each update +adds one new factor (however, some updates may need no addition), so +this parameter limits the number of updates between refactorizations.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt double upd\_tol} (default: {\tt 1e-6})} \\ +&Update tolerance, 0 $<$ \verb|upd_tol| $<$ 1, used on updating +$LU$-factorization of the basis matrix with the Forrest--Tomlin +technique. If after updating the magnitude of some diagonal element +$u_{kk}$ of factor $U$ becomes less than +${\tt upd\_tol}\cdot\max(|u_{k*}|, |u_{*k}|)$, the factorization is +considered as inaccurate.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int nrs\_max} (default: {\tt 100})} \\ +&Maximal number of additional rows and columns, \verb|nrs_max| $\geq$ 1, +which can be added to $LU$-factorization of the basis matrix on updating +it with the Schur complement technique. This parameter is used only +once, before $LU$-factorization is computed for the first time, to +allocate working arrays. As a rule, each update adds one new row and +column (however, some updates may need no addition), so this parameter +limits the number of updates between refactorizations.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int rs\_size} (default: {\tt 0})} \\ +&The initial size of the Sparse Vector Area, in non-zeros, used to +store non-zero elements of additional rows and columns introduced on +updating $LU$-factorization of the basis matrix with the Schur +complement technique. If this parameter is set to 0, the initial SVA +size is determined automatically.\\ +\end{tabular} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\subsection{glp\_get\_bhead---retrieve the basis header information} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_bhead(glp_prob *lp, int k); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_get_bhead| returns the basis header information +for the current basis associated with the specified problem object. + +\subsubsection*{Returns} + +If basic variable $(x_B)_k$, $1\leq k\leq m$, is $i$-th auxiliary +variable ($1\leq i\leq m$), the routine returns $i$. Otherwise, if +$(x_B)_k$ is $j$-th structural variable ($1\leq j\leq n$), the routine +returns $m+j$. Here $m$ is the number of rows and $n$ is the number of +columns in the problem object. + +\subsubsection*{Comments} + +Sometimes the application program may need to know which original +(auxiliary and structural) variable correspond to a given basic +variable, or, that is the same, which column of the augmented constraint +matrix $(I\ |-\!A)$ correspond to a given column of the basis matrix +$B$. + +\def\arraystretch{1} + +The correspondence is defined as follows:\footnote{For more details see +Subsection \ref{basbgd}, page \pageref{basbgd}.} +$$\left(\begin{array}{@{}c@{}}x_B\\x_N\\\end{array}\right)= +\Pi\left(\begin{array}{@{}c@{}}x_R\\x_S\\\end{array}\right) +\ \ \Leftrightarrow +\ \ \left(\begin{array}{@{}c@{}}x_R\\x_S\\\end{array}\right)= +\Pi^T\left(\begin{array}{@{}c@{}}x_B\\x_N\\\end{array}\right),$$ +where $x_B$ is the vector of basic variables, $x_N$ is the vector of +non-basic variables, $x_R$ is the vector of auxiliary variables +following in their original order,\footnote{The original order of +auxiliary and structural variables is defined by the ordinal numbers +of corresponding rows and columns in the problem object.} $x_S$ is the +vector of structural variables following in their original order, $\Pi$ +is a permutation matrix (which is a component of the basis +factorization). + +Thus, if $(x_B)_k=(x_R)_i$ is $i$-th auxiliary variable, the routine +returns $i$, and if $(x_B)_k=(x_S)_j$ is $j$-th structural variable, +the routine returns $m+j$, where $m$ is the number of rows in the +problem object. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\subsection{glp\_get\_row\_bind---retrieve row index in the basis\\ +header} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_row_bind(glp_prob *lp, int i); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_row_bind| returns the index $k$ of basic +variable $(x_B)_k$, $1\leq k\leq m$, which is $i$-th auxiliary variable +(that is, the auxiliary variable corresponding to $i$-th row), +$1\leq i\leq m$, in the current basis associated with the specified +problem object, where $m$ is the number of rows. However, if $i$-th +auxiliary variable is non-basic, the routine returns zero. + +\subsubsection*{Comments} + +The routine \verb|glp_get_row_bind| is an inverse to the routine +\verb|glp_get_bhead|: if \verb|glp_get_bhead|$(lp,k)$ returns $i$, +\verb|glp_get_row_bind|$(lp,i)$ returns $k$, and vice versa. + +\subsection{glp\_get\_col\_bind---retrieve column index in the basis +header} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_get_col_bind(glp_prob *lp, int j); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_get_col_bind| returns the index $k$ of basic +variable $(x_B)_k$, $1\leq k\leq m$, which is $j$-th structural +variable (that is, the structural variable corresponding to $j$-th +column), $1\leq j\leq n$, in the current basis associated with the +specified problem object, where $m$ is the number of rows, $n$ is the +number of columns. However, if $j$-th structural variable is non-basic, +the routine returns zero. + +\subsubsection*{Comments} + +The routine \verb|glp_get_col_bind| is an inverse to the routine +\verb|glp_get_bhead|: if \verb|glp_get_bhead|$(lp,k)$ returns $m+j$, +\verb|glp_get_col_bind|$(lp,j)$ returns $k$, and vice versa. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\subsection{glp\_ftran---perform forward transformation} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_ftran(glp_prob *lp, double x[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ftran| performs forward transformation (FTRAN), +i.e. it solves the system $Bx=b$, where $B$ is the basis matrix +associated with the specified problem object, $x$ is the vector of +unknowns to be computed, $b$ is the vector of right-hand sides. + +On entry to the routine elements of the vector $b$ should be stored in +locations \verb|x[1]|, \dots, \verb|x[m]|, where $m$ is the number of +rows. On exit the routine stores elements of the vector $x$ in the same +locations. + +\subsection{glp\_btran---perform backward transformation} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_btran(glp_prob *lp, double x[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_btran| performs backward transformation (BTRAN), +i.e. it solves the system $B^Tx=b$, where $B^T$ is a matrix transposed +to the basis matrix $B$ associated with the specified problem object, +$x$ is the vector of unknowns to be computed, $b$ is the vector of +right-hand sides. + +On entry to the routine elements of the vector $b$ should be stored in +locations \verb|x[1]|, \dots, \verb|x[m]|, where $m$ is the number of +rows. On exit the routine stores elements of the vector $x$ in the same +locations. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\subsection{glp\_warm\_up---``warm up'' LP basis} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_warm_up(glp_prob *P); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_warm_up| ``warms up'' the LP basis for the +specified problem object using current statuses assigned to rows and +columns (that is, to auxiliary and structural variables). + +This operation includes computing factorization of the basis matrix +(if it does not exist), computing primal and dual components of basic +solution, and determining the solution status. + +\subsubsection*{Returns} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & The operation has been successfully performed.\\ +\verb|GLP_EBADB| & The basis matrix is invalid, because the number of +basic (auxiliary and structural) variables is not the same as the number +of rows in the problem object.\\ +\verb|GLP_ESING| & The basis matrix is singular within the working +precision.\\ +\verb|GLP_ECOND| & The basis matrix is ill-conditioned, i.e. its +condition number is too large.\\ +\end{tabular} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Simplex tableau routines} + +\subsection{glp\_eval\_tab\_row---compute row of the tableau} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_eval_tab_row(glp_prob *lp, int k, int ind[], + double val[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_eval_tab_row| computes a row of the current +simplex tableau (see Subsection 3.1.1, formula (3.12)), which (row) +corresponds to some basic variable specified by the parameter $k$ as +follows: if $1\leq k\leq m$, the basic variable is $k$-th auxiliary +variable, and if $m+1\leq k\leq m+n$, the basic variable is $(k-m)$-th +structural variable, where $m$ is the number of rows and $n$ is the +number of columns in the specified problem object. The basis +factorization must exist. + +The computed row shows how the specified basic variable depends on +non-basic variables: +$$x_k=(x_B)_i=\xi_{i1}(x_N)_1+\xi_{i2}(x_N)_2+\dots+\xi_{in}(x_N)_n,$$ +where $\xi_{i1}$, $\xi_{i2}$, \dots, $\xi_{in}$ are elements of the +simplex table row, $(x_N)_1$, $(x_N)_2$, \dots, $(x_N)_n$ are non-basic +(auxiliary and structural) variables. + +The routine stores column indices and corresponding numeric values of +non-zero elements of the computed row in unordered sparse format in +locations \verb|ind[1]|, \dots, \verb|ind[len]| and \verb|val[1]|, +\dots, \verb|val[len]|, respectively, where $0\leq{\tt len}\leq n$ is +the number of non-zero elements in the row returned on exit. + +Element indices stored in the array \verb|ind| have the same sense as +index $k$, i.e. indices 1 to $m$ denote auxiliary variables while +indices $m+1$ to $m+n$ denote structural variables (all these variables +are obviously non-basic by definition). + +\subsubsection*{Returns} + +The routine \verb|glp_eval_tab_row| returns \verb|len|, which is the +number of non-zero elements in the simplex table row stored in the +arrays \verb|ind| and \verb|val|. + +\subsubsection*{Comments} + +A row of the simplex table is computed as follows. At first, the +routine checks that the specified variable $x_k$ is basic and uses the +permutation matrix $\Pi$ (3.7) to determine index $i$ of basic variable +$(x_B)_i$, which corresponds to $x_k$. + +The row to be computed is $i$-th row of the matrix $\Xi$ (3.12), +therefore: +$$\xi_i=e_i^T\Xi=-e_i^TB^{-1}N=-(B^{-T}e_i)^TN,$$ +where $e_i$ is $i$-th unity vector. So the routine performs BTRAN to +obtain $i$-th row of the inverse $B^{-1}$: +$$\varrho_i=B^{-T}e_i,$$ +and then computes elements of the simplex table row as inner products: +$$\xi_{ij}=-\varrho_i^TN_j,\ \ j=1,2,\dots,n,$$ +where $N_j$ is $j$-th column of matrix $N$ (3.9), which (column) +corresponds to non-basic variable $(x_N)_j$. The permutation matrix +$\Pi$ is used again to convert indices $j$ of non-basic columns to +original ordinal numbers of auxiliary and structural variables. + +\subsection{glp\_eval\_tab\_col---compute column of the tableau} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_eval_tab_col(glp_prob *lp, int k, int ind[], + double val[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_eval_tab_col| computes a column of the current +simplex tableau (see Subsection 3.1.1, formula (3.12)), which (column) +corresponds to some non-basic variable specified by the parameter $k$: +if $1\leq k\leq m$, the non-basic variable is $k$-th auxiliary variable, +and if $m+1\leq k\leq m+n$, the non-basic variable is $(k-m)$-th +structural variable, where $m$ is the number of rows and $n$ is the +number of columns in the specified problem object. The basis +factorization must exist. + +The computed column shows how basic variables depends on the specified +non-basic variable $x_k=(x_N)_j$: +$$ +\begin{array}{r@{\ }c@{\ }l@{\ }l} +(x_B)_1&=&\dots+\xi_{1j}(x_N)_j&+\dots\\ +(x_B)_2&=&\dots+\xi_{2j}(x_N)_j&+\dots\\ +.\ \ .&.&.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\\ +(x_B)_m&=&\dots+\xi_{mj}(x_N)_j&+\dots\\ +\end{array} +$$ +where $\xi_{1j}$, $\xi_{2j}$, \dots, $\xi_{mj}$ are elements of the +simplex table column, $(x_B)_1$, $(x_B)_2$, \dots, $(x_B)_m$ are basic +(auxiliary and structural) variables. + +The routine stores row indices and corresponding numeric values of +non-zero elements of the computed column in unordered sparse format in +locations \verb|ind[1]|, \dots, \verb|ind[len]| and \verb|val[1]|, +\dots, \verb|val[len]|, respectively, where $0\leq{\tt len}\leq m$ is +the number of non-zero elements in the column returned on exit. + +Element indices stored in the array \verb|ind| have the same sense as +index $k$, i.e. indices 1 to $m$ denote auxiliary variables while +indices $m+1$ to $m+n$ denote structural variables (all these variables +are obviously basic by definition). + +\subsubsection*{Returns} + +The routine \verb|glp_eval_tab_col| returns \verb|len|, which is the +number of non-zero elements in the simplex table column stored in the +arrays \verb|ind| and \verb|val|. + +\subsubsection*{Comments} + +A column of the simplex table is computed as follows. At first, the +routine checks that the specified variable $x_k$ is non-basic and uses +the permutation matrix $\Pi$ (3.7) to determine index $j$ of non-basic +variable $(x_N)_j$, which corresponds to $x_k$. + +The column to be computed is $j$-th column of the matrix $\Xi$ (3.12), +therefore: +$$\Xi_j=\Xi e_j=-B^{-1}Ne_j=-B^{-1}N_j,$$ +where $e_j$ is $j$-th unity vector, $N_j$ is $j$-th column of matrix +$N$ (3.9). So the routine performs FTRAN to transform $N_j$ to the +simplex table column $\Xi_j=(\xi_{ij})$ and uses the permutation matrix +$\Pi$ to convert row indices $i$ to original ordinal numbers of +auxiliary and structural variables. + +\newpage + +\subsection{glp\_transform\_row---transform explicitly specified\\ +row} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_transform_row(glp_prob *P, int len, int ind[], + double val[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_transform_row| performs the same operation as the +routine \verb|glp_eval_tab_row| with exception that the row to be +transformed is specified explicitly as a sparse vector. + +The explicitly specified row may be thought as a linear form: +$$x=a_1x_{m+1}+a_2x_{m+2}+\dots+a_nx_{m+n},$$ +where $x$ is an auxiliary variable for this row, $a_j$ are coefficients +of the linear form, $x_{m+j}$ are structural variables. + +On entry column indices and numerical values of non-zero coefficients +$a_j$ of the specified row should be placed in locations \verb|ind[1]|, +\dots, \verb|ind[len]| and \verb|val[1]|, \dots, \verb|val[len]|, where +\verb|len| is number of non-zero coefficients. + +This routine uses the system of equality constraints and the current +basis in order to express the auxiliary variable $x$ through the current +non-basic variables (as if the transformed row were added to the problem +object and the auxiliary variable $x$ were basic), i.e. the resultant +row has the form: +$$x=\xi_1(x_N)_1+\xi_2(x_N)_2+\dots+\xi_n(x_N)_n,$$ +where $\xi_j$ are influence coefficients, $(x_N)_j$ are non-basic +(auxiliary and structural) variables, $n$ is the number of columns in +the problem object. + +On exit the routine stores indices and numerical values of non-zero +coefficients $\xi_j$ of the resultant row in locations \verb|ind[1]|, +\dots, \verb|ind[len']| and \verb|val[1]|, \dots, \verb|val[len']|, +where $0\leq{\tt len'}\leq n$ is the number of non-zero coefficients in +the resultant row returned by the routine. Note that indices of +non-basic variables stored in the array \verb|ind| correspond to +original ordinal numbers of variables: indices 1 to $m$ mean auxiliary +variables and indices $m+1$ to $m+n$ mean structural ones. + +\subsubsection*{Returns} + +The routine \verb|glp_transform_row| returns \verb|len'|, the number of +non-zero coefficients in the resultant row stored in the arrays +\verb|ind| and \verb|val|. + +\subsection{glp\_transform\_col---transform explicitly specified\\ +column} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_transform_col(glp_prob *P, int len, int ind[], + double val[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_transform_col| performs the same operation as the +routine \verb|glp_eval_tab_col| with exception that the column to be +transformed is specified explicitly as a sparse vector. + +The explicitly specified column may be thought as it were added to +the original system of equality constraints: +$$ +\begin{array}{l@{\ }c@{\ }r@{\ }c@{\ }r@{\ }c@{\ }r} +x_1&=&a_{11}x_{m+1}&+\dots+&a_{1n}x_{m+n}&+&a_1x \\ +x_2&=&a_{21}x_{m+1}&+\dots+&a_{2n}x_{m+n}&+&a_2x \\ +\multicolumn{7}{c} +{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .}\\ +x_m&=&a_{m1}x_{m+1}&+\dots+&a_{mn}x_{m+n}&+&a_mx \\ +\end{array} +$$ +where $x_i$ are auxiliary variables, $x_{m+j}$ are structural variables +(presented in the problem object), $x$ is a structural variable for the +explicitly specified column, $a_i$ are constraint coefficients at $x$. + +On entry row indices and numerical values of non-zero coefficients +$a_i$ of the specified column should be placed in locations +\verb|ind[1]|, \dots, \verb|ind[len]| and \verb|val[1]|, \dots, +\verb|val[len]|, where \verb|len| is number of non-zero coefficients. + +This routine uses the system of equality constraints and the current +basis in order to express the current basic variables through the +structural variable $x$ (as if the transformed column were added to the +problem object and the variable $x$ were non-basic): +$$ +\begin{array}{l@{\ }c@{\ }r} +(x_B)_1&=\dots+&\xi_{1}x\\ +(x_B)_2&=\dots+&\xi_{2}x\\ +\multicolumn{3}{c}{.\ \ .\ \ .\ \ .\ \ .\ \ .}\\ +(x_B)_m&=\dots+&\xi_{m}x\\ +\end{array} +$$ +where $\xi_i$ are influence coefficients, $x_B$ are basic (auxiliary +and structural) variables, $m$ is the number of rows in the problem +object. + +On exit the routine stores indices and numerical values of non-zero +coefficients $\xi_i$ of the resultant column in locations \verb|ind[1]|, +\dots, \verb|ind[len']| and \verb|val[1]|, \dots, \verb|val[len']|, +where $0\leq{\tt len'}\leq m$ is the number of non-zero coefficients in +the resultant column returned by the routine. Note that indices of basic +variables stored in the array \verb|ind| correspond to original ordinal +numbers of variables, i.e. indices 1 to $m$ mean auxiliary variables, +indices $m+1$ to $m+n$ mean structural ones. + +\subsubsection*{Returns} + +The routine \verb|glp_transform_col| returns \verb|len'|, the number of +non-zero coefficients in the resultant column stored in the arrays +\verb|ind| and \verb|val|. + +\subsection{glp\_prim\_rtest---perform primal ratio test} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_prim_rtest(glp_prob *P, int len, const int ind[], + const double val[], int dir, double eps); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_prim_rtest| performs the primal ratio test using +an explicitly specified column of the simplex table. + +The current basic solution associated with the LP problem object must be +primal feasible. + +The explicitly specified column of the simplex table shows how the basic +variables $x_B$ depend on some non-basic variable $x$ (which is not +necessarily presented in the problem object): +$$ +\begin{array}{l@{\ }c@{\ }r} +(x_B)_1&=\dots+&\xi_{1}x\\ +(x_B)_2&=\dots+&\xi_{2}x\\ +\multicolumn{3}{c}{.\ \ .\ \ .\ \ .\ \ .\ \ .}\\ +(x_B)_m&=\dots+&\xi_{m}x\\ +\end{array} +$$ + +The column is specifed on entry to the routine in sparse format. Ordinal +numbers of basic variables $(x_B)_i$ should be placed in locations +\verb|ind[1]|, \dots, \verb|ind[len]|, where ordinal number 1 to $m$ +denote auxiliary variables, and ordinal numbers $m+1$ to $m+n$ denote +structural variables. The corresponding non-zero coefficients $\xi_i$ +should be placed in locations \verb|val[1]|, \dots, \verb|val[len]|. The +arrays \verb|ind| and \verb|val| are not changed by the routine. + +The parameter \verb|dir| specifies direction in which the variable $x$ +changes on entering the basis: $+1$ means increasing, $-1$ means +decreasing. + +The parameter \verb|eps| is an absolute tolerance (small positive +number, say, $10^{-9}$) used by the routine to skip $\xi_i$'s whose +magnitude is less than \verb|eps|. + +The routine determines which basic variable (among those specified in +\verb|ind[1]|, \dots, \verb|ind[len]|) reaches its (lower or upper) +bound first before any other basic variables do, and which, therefore, +should leave the basis in order to keep primal feasibility. + +\subsubsection*{Returns} + +The routine \verb|glp_prim_rtest| returns the index, \verb|piv|, in the +arrays \verb|ind| and \verb|val| corresponding to the pivot element +chosen, $1\leq$ \verb|piv| $\leq$ \verb|len|. If the adjacent basic +solution is primal unbounded, and therefore the choice cannot be made, +the routine returns zero. + +\subsubsection*{Comments} + +If the non-basic variable $x$ is presented in the LP problem object, the +input column can be computed with the routine \verb|glp_eval_tab_col|; +otherwise, it can be computed with the routine \verb|glp_transform_col|. + +\subsection{glp\_dual\_rtest---perform dual ratio test} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_dual_rtest(glp_prob *P, int len, const int ind[], + const double val[], int dir, double eps); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_dual_rtest| performs the dual ratio test using +an explicitly specified row of the simplex table. + +The current basic solution associated with the LP problem object must be +dual feasible. + +The explicitly specified row of the simplex table is a linear form +that shows how some basic variable $x$ (which is not necessarily +presented in the problem object) depends on non-basic variables $x_N$: +$$x=\xi_1(x_N)_1+\xi_2(x_N)_2+\dots+\xi_n(x_N)_n.$$ + +The row is specified on entry to the routine in sparse format. Ordinal +numbers of non-basic variables $(x_N)_j$ should be placed in locations +\verb|ind[1]|, \dots, \verb|ind[len]|, where ordinal numbers 1 to $m$ +denote auxiliary variables, and ordinal numbers $m+1$ to $m+n$ denote +structural variables. The corresponding non-zero coefficients $\xi_j$ +should be placed in locations \verb|val[1]|, \dots, \verb|val[len]|. +The arrays \verb|ind| and \verb|val| are not changed by the routine. + +The parameter \verb|dir| specifies direction in which the variable $x$ +changes on leaving the basis: $+1$ means that $x$ goes on its lower +bound, so its reduced cost (dual variable) is increasing (minimization) +or decreasing (maximization); $-1$ means that $x$ goes on its upper +bound, so its reduced cost is decreasing (minimization) or increasing +(maximization). + +The parameter \verb|eps| is an absolute tolerance (small positive +number, say, $10^{-9}$) used by the routine to skip $\xi_j$'s whose +magnitude is less than \verb|eps|. + +The routine determines which non-basic variable (among those specified +in \verb|ind[1]|, \dots, \verb|ind[len]|) should enter the basis in +order to keep dual feasibility, because its reduced cost reaches the +(zero) bound first before this occurs for any other non-basic variables. + +\subsubsection*{Returns} + +The routine \verb|glp_dual_rtest| returns the index, \verb|piv|, in the +arrays \verb|ind| and \verb|val| corresponding to the pivot element +chosen, $1\leq$ \verb|piv| $\leq$ \verb|len|. If the adjacent basic +solution is dual unbounded, and therefore the choice cannot be made, +the routine returns zero. + +\subsubsection*{Comments} + +If the basic variable $x$ is presented in the LP problem object, the +input row can be computed with the routine \verb|glp_eval_tab_row|; +otherwise, it can be computed with the routine \verb|glp_transform_row|. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Post-optimal analysis routines} + +\subsection{glp\_analyze\_bound---analyze active bound of non-basic +variable} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_analyze_bound(glp_prob *P, int k, double *limit1, + int *var1, double *limit2, int *var2); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_analyze_bound| analyzes the effect of varying the +active bound of specified non-basic variable. + +The non-basic variable is specified by the parameter $k$, where +$1\leq k\leq m$ means auxiliary variable of corresponding row, and +$m+1\leq k\leq m+n$ means structural variable (column). + +Note that the current basic solution must be optimal, and the basis +factorization must exist. + +Results of the analysis have the following meaning. + +\verb|value1| is the minimal value of the active bound, at which the +basis still remains primal feasible and thus optimal. \verb|-DBL_MAX| +means that the active bound has no lower limit. + +\verb|var1| is the ordinal number of an auxiliary (1 to $m$) or +structural ($m+1$ to $m+n$) basic variable, which reaches its bound +first and thereby limits further decreasing the active bound being +analyzed. if \verb|value1| = \verb|-DBL_MAX|, \verb|var1| is set to 0. + +\verb|value2| is the maximal value of the active bound, at which the +basis still remains primal feasible and thus optimal. \verb|+DBL_MAX| +means that the active bound has no upper limit. + +\verb|var2| is the ordinal number of an auxiliary (1 to $m$) or +structural ($m+1$ to $m+n$) basic variable, which reaches its bound +first and thereby limits further increasing the active bound being +analyzed. if \verb|value2| = \verb|+DBL_MAX|, \verb|var2| is set to 0. + +The parameters \verb|value1|, \verb|var1|, \verb|value2|, \verb|var2| +can be specified as \verb|NULL|, in which case corresponding information +is not stored. + +\newpage + +\subsection{glp\_analyze\_coef---analyze objective coefficient at basic +variable} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_analyze_coef(glp_prob *P, int k, double *coef1, + int *var1, double *value1, double *coef2, int *var2, + double *value2); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_analyze_coef| analyzes the effect of varying the +objective coefficient at specified basic variable. + +The basic variable is specified by the parameter $k$, where +$1\leq k\leq m$ means auxiliary variable of corresponding row, and +$m+1\leq k\leq m+n$ means structural variable (column). + +Note that the current basic solution must be optimal, and the basis +factorization must exist. + +Results of the analysis have the following meaning. + +\verb|coef1| is the minimal value of the objective coefficient, at +which the basis still remains dual feasible and thus optimal. +\verb|-DBL_MAX| means that the objective coefficient has no lower limit. + +\verb|var1| is the ordinal number of an auxiliary (1 to $m$) or +structural ($m+1$ to $m+n$) non-basic variable, whose reduced cost +reaches its zero bound first and thereby limits further decreasing the +objective coefficient being analyzed. If \verb|coef1| = \verb|-DBL_MAX|, +\verb|var1| is set to 0. + +\verb|value1| is value of the basic variable being analyzed in an +adjacent basis, which is defined as follows. Let the objective +coefficient reaches its minimal value (\verb|coef1|) and continues +decreasing. Then the reduced cost of the limiting non-basic variable +(\verb|var1|) becomes dual infeasible and the current basis becomes +non-optimal that forces the limiting non-basic variable to enter the +basis replacing there some basic variable that leaves the basis to keep +primal feasibility. Should note that on determining the adjacent basis +current bounds of the basic variable being analyzed are ignored as if +it were free (unbounded) variable, so it cannot leave the basis. It may +happen that no dual feasible adjacent basis exists, in which case +\verb|value1| is set to \verb|-DBL_MAX| or \verb|+DBL_MAX|. + +\verb|coef2| is the maximal value of the objective coefficient, at +which the basis still remains dual feasible and thus optimal. +\verb|+DBL_MAX| means that the objective coefficient has no upper limit. + +\verb|var2| is the ordinal number of an auxiliary (1 to $m$) or +structural ($m+1$ to $m+n$) non-basic variable, whose reduced cost +reaches its zero bound first and thereby limits further increasing the +objective coefficient being analyzed. If \verb|coef2| = \verb|+DBL_MAX|, +\verb|var2| is set to 0. + +\verb|value2| is value of the basic variable being analyzed in an +adjacent basis, which is defined exactly in the same way as +\verb|value1| above with exception that now the objective coefficient +is increasing. + +The parameters \verb|coef1|, \verb|var1|, \verb|value1|, \verb|coef2|, +\verb|var2|, \verb|value2| can be specified as \verb|NULL|, in which +case corresponding information is not stored. + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk05.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk05.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1100 @@ +%* glpk05.tex *% + +\chapter{Branch-and-Cut API Routines} + +\section{Introduction} + +\subsection{Using the callback routine} + +The GLPK MIP solver based on the branch-and-cut method allows the +application program to control the solution process. This is attained +by means of the user-defined callback routine, which is called by the +solver at various points of the branch-and-cut algorithm. + +The callback routine passed to the MIP solver should be written by the +user and has the following specification:\footnote{The name +{\tt foo\_bar} used here is a placeholder for the callback routine +name.} + +\begin{verbatim} + void foo_bar(glp_tree *tree, void *info); +\end{verbatim} + +\noindent +where \verb|tree| is a pointer to the data structure \verb|glp_tree|, +which should be used on subsequent calls to branch-and-cut interface +routines, and \verb|info| is a transit pointer passed to the routine +\verb|glp_intopt|, which may be used by the application program to pass +some external data to the callback routine. + +The callback routine is passed to the MIP solver through the control +parameter structure \verb|glp_iocp| (see Chapter ``Basic API Routines'', +Section ``Mixed integer programming routines'', Subsection ``Solve MIP +problem with the branch-and-cut method'') as follows: + +\newpage + +\begin{verbatim} + glp_prob *mip; + glp_iocp parm; + . . . + glp_init_iocp(&parm); + . . . + parm.cb_func = foo_bar; + parm.cb_info = ... ; + ret = glp_intopt(mip, &parm); + . . . +\end{verbatim} + +To determine why it is being called by the MIP solver the callback +routine should use the routine \verb|glp_ios_reason| (described in this +section below), which returns a code indicating the reason for calling. +Depending on the reason the callback routine may perform necessary +actions to control the solution process. + +The reason codes, which correspond to various point of the +branch-and-cut algorithm implemented in the MIP solver, are described +in Subsection ``Reasons for calling the callback routine'' below. + +To ignore calls for reasons, which are not processed by the callback +routine, it should just return to the MIP solver doing nothing. For +example: + +\begin{verbatim} +void foo_bar(glp_tree *tree, void *info) +{ . . . + switch (glp_ios_reason(tree)) + { case GLP_IBRANCH: + . . . + break; + case GLP_ISELECT: + . . . + break; + default: + /* ignore call for other reasons */ + break; + } + return; +} +\end{verbatim} + +To control the solution process as well as to obtain necessary +information the callback routine may use the branch-and-cut API +routines described in this chapter. Names of all these routines begin +with `\verb|glp_ios_|'. + +\subsection{Branch-and-cut algorithm} + +This section gives a schematic description of the branch-and-cut +algorithm as it is implemented in the GLPK MIP solver. + +\medskip + +{\it 1. Initialization} + +Set $L:=\{P_0\}$, where $L$ is the {\it active list} (i.e. the list of +active subproblems), $P_0$ is the original MIP problem to be solved. + +Set $z^{\it best}:=+\infty$ (in case of minimization) or +$z^{\it best}:=-\infty$ (in case of maximization), where $z^{\it best}$ +is {\it incumbent value}, i.e. an upper (minimization) or lower +(maximization) global bound for $z^{\it opt}$, the optimal objective +value for $P^0$. + +\medskip + +{\it 2. Subproblem selection} + +If $L=\varnothing$ then GO TO 9. + +Select $P\in L$, i.e. make active subproblem $P$ current. + +\medskip + +{\it 3. Solving LP relaxation} + +Solve $P^{\it LP}$, which is LP relaxation of $P$. + +If $P^{\it LP}$ has no primal feasible solution then GO TO 8. + +Let $z^{\it LP}$ be the optimal objective value for $P^{\it LP}$. + +If $z^{\it LP}\geq z^{\it best}$ (in case of minimization) or +$z^{\it LP}\leq z^{\rm best}$ (in case of maximization) then GO TO 8. + +\medskip + +{\it 4. Adding ``lazy'' constraints} + +Let $x^{\it LP}$ be the optimal solution to $P^{\it LP}$. + +If there are ``lazy'' constraints (i.e. essential constraints not +included in the original MIP problem $P_0$), which are violated at the +optimal point $x^{\it LP}$, add them to $P$, and GO TO 3. + +\medskip + +{\it 5. Check for integrality} + +Let $x_j$ be a variable, which is required to be integer, and let +$x^{\it LP}_j\in x^{\it LP}$ be its value in the optimal solution to +$P^{\it LP}$. + +If $x^{\it LP}_j$ are integral for all integer variables, then a better +integer feasible solution is found. Store its components, set +$z^{\it best}:=z^{\it LP}$, and GO TO 8. + +\medskip + +{\it 6. Adding cutting planes} + +If there are cutting planes (i.e. valid constraints for $P$), +which are violated at the optimal point $x^{\it LP}$, add them to $P$, +and GO TO 3. + +\medskip + +{\it 7. Branching} + +Select {\it branching variable} $x_j$, i.e. a variable, which is +required to be integer, and whose value $x^{\it LP}_j\in x^{\it LP}$ is +fractional in the optimal solution to $P^{\it LP}$. + +Create new subproblem $P^D$ (so called {\it down branch}), which is +identical to the current subproblem $P$ with exception that the upper +bound of $x_j$ is replaced by $\lfloor x^{\it LP}_j\rfloor$. (For +example, if $x^{\it LP}_j=3.14$, the new upper bound of $x_j$ in the +down branch will be $\lfloor 3.14\rfloor=3$.) + +Create new subproblem $P^U$ (so called {\it up branch}), which is +identical to the current subproblem $P$ with exception that the lower +bound of $x_j$ is replaced by $\lceil x^{\it LP}_j\rceil$. (For example, +if $x^{\it LP}_j=3.14$, the new lower bound of $x_j$ in the up branch +will be $\lceil 3.14\rceil=4$.) + +Set $L:=(L\backslash\{P\})\cup\{P^D,P^U\}$, i.e. remove the current +subproblem $P$ from the active list $L$ and add two new subproblems +$P^D$ and $P^U$ to it. Then GO TO 2. + +\medskip + +{\it 8. Pruning} + +Remove from the active list $L$ all subproblems (including the current +one), whose local bound $\widetilde{z}$ is not better than the global +bound $z^{\it best}$, i.e. set $L:=L\backslash\{P\}$ for all $P$, where +$\widetilde{z}\geq z^{\it best}$ (in case of minimization) or +$\widetilde{z}\leq z^{\it best}$ (in case of maximization), and then +GO TO 2. + +The local bound $\widetilde{z}$ for subproblem $P$ is an lower +(minimization) or upper (maximization) bound for integer optimal +solution to {\it this} subproblem (not to the original problem). This +bound is local in the sense that only subproblems in the subtree rooted +at node $P$ cannot have better integer feasible solutions. Note that +the local bound is not necessarily the optimal objective value to LP +relaxation $P^{\it LP}$. + +\medskip + +{\it 9. Termination} + +If $z^{\it best}=+\infty$ (in case of minimization) or +$z^{\it best}=-\infty$ (in case of maximization), the original problem +$P_0$ has no integer feasible solution. Otherwise, the last integer +feasible solution stored on step 5 is the integer optimal solution to +the original problem $P_0$ with $z^{\it opt}=z^{\it best}$. STOP. + +\subsection{The search tree} + +On the branching step of the branch-and-cut algorithm the current +subproblem is divided into two\footnote{In more general cases the +current subproblem may be divided into more than two subproblems. +However, currently such feature is not used in GLPK.} new subproblems, +so the set of all subproblems can be represented in the form of a rooted +tree, which is called the {\it search} or {\it branch-and-bound} tree. +An example of the search tree is shown on Fig.~1. Each node of the +search tree corresponds to a subproblem, so the terms `node' and +`subproblem' may be used synonymously. + +\newpage + +\begin{figure}[t] +\noindent\hfil +\xymatrix @R=20pt @C=10pt +{&&&&&&*+<14pt>[o][F=]{A}\ar@{-}[dllll]\ar@{-}[dr]\ar@{-}[drrrr]&&&&\\ +&&*+<14pt>[o][F=]{B}\ar@{-}[dl]\ar@{-}[dr]&&&&&*+<14pt>[o][F=]{C} +\ar@{-}[dll]\ar@{-}[dr]\ar@{-}[drrr]&&&*+<14pt>[o][F-]{\times}\\ +&*+<14pt>[o][F-]{\times}\ar@{-}[dl]\ar@{-}[d]\ar@{-}[dr]&& +*+<14pt>[o][F-]{D}&&*+<14pt>[o][F=]{E}\ar@{-}[dl]\ar@{-}[dr]&&& +*+<14pt>[o][F=]{F}\ar@{-}[dl]\ar@{-}[dr]&&*+<14pt>[o][F-]{G}\\ +*+<14pt>[o][F-]{\times}&*+<14pt>[o][F-]{\times}&*+<14pt>[o][F-]{\times} +&&*+<14pt>[][F-]{H}&&*+<14pt>[o][F-]{I}&*+<14pt>[o][F-]{\times}&& +*+<14pt>[o][F-]{J}&\\} + +\bigskip + +\noindent\hspace{.8in} +\xymatrix @R=11pt +{*+<20pt>[][F-]{}&*\txt{\makebox[1in][l]{Current}}&& +*+<20pt>[o][F-]{}&*\txt{\makebox[1in][l]{Active}}\\ +*+<20pt>[o][F=]{}&*\txt{\makebox[1in][l]{Non-active}}&& +*+<14pt>[o][F-]{\times}&*\txt{\makebox[1in][l]{Fathomed}}\\ +} + +\begin{center} +Fig. 1. An example of the search tree. +\end{center} +\end{figure} + +In GLPK each node may have one of the following four statuses: + +$\bullet$ {\it current node} is the active node currently being +processed; + +$\bullet$ {\it active node} is a leaf node, which still has to be +processed; + +$\bullet$ {\it non-active node} is a node, which has been processed, +but not fathomed; + +$\bullet$ {\it fathomed node} is a node, which has been processed and +fathomed. + +In the data structure representing the search tree GLPK keeps only +current, active, and non-active nodes. Once a node has been fathomed, +it is removed from the tree data structure. + +Being created each node of the search tree is assigned a distinct +positive integer called the {\it subproblem reference number}, which +may be used by the application program to specify a particular node of +the tree. The root node corresponding to the original problem to be +solved is always assigned the reference number 1. + +\subsection{Current subproblem} + +The current subproblem is a MIP problem corresponding to the current +node of the search tree. It is represented as the GLPK problem object +(\verb|glp_prob|) that allows the application program using API routines +to access its content in the standard way. If the MIP presolver is not +used, it is the original problem object passed to the routine +\verb|glp_intopt|; otherwise, it is an internal problem object built by +the MIP presolver. + +Note that the problem object is used by the MIP solver itself during +the solution process for various purposes (to solve LP relaxations, to +perfom branching, etc.), and even if the MIP presolver is not used, the +current content of the problem object may differ from its original +content. For example, it may have additional rows, bounds of some rows +and columns may be changed, etc. In particular, LP segment of the +problem object corresponds to LP relaxation of the current subproblem. +However, on exit from the MIP solver the content of the problem object +is restored to its original state. + +To obtain information from the problem object the application program +may use any API routines, which do not change the object. Using API +routines, which change the problem object, is restricted to stipulated +cases. + +\subsection{The cut pool} + +The {\it cut pool} is a set of cutting plane constraints maintained by +the MIP solver. It is used by the GLPK cut generation routines and may +be used by the application program in the same way, i.e. rather than +to add cutting plane constraints directly to the problem object the +application program may store them to the cut pool. In the latter case +the solver looks through the cut pool, selects efficient constraints, +and adds them to the problem object. + +\subsection{Reasons for calling the callback routine} + +The callback routine may be called by the MIP solver for the following +reasons. + +\subsubsection*{Request for subproblem selection} + +The callback routine is called with the reason code \verb|GLP_ISELECT| +if the current subproblem has been fathomed and therefore there is no +current subproblem. + +In response the callback routine may select some subproblem from the +active list and pass its reference number to the solver using the +routine \verb|glp_ios_select_node|, in which case the solver continues +the search from the specified active subproblem. If no selection is made +by the callback routine, the solver uses a backtracking technique +specified by the control parameter \verb|bt_tech|. + +To explore the active list (i.e. active nodes of the branch-and-bound +tree) the callback routine may use the routines \verb|glp_ios_next_node| +and \verb|glp_ios_prev_node|. + +\subsubsection*{Request for preprocessing} + +The callback routine is called with the reason code \verb|GLP_IPREPRO| +if the current subproblem has just been selected from the active list +and its LP relaxation is not solved yet. + +In response the callback routine may perform some preprocessing of the +current subproblem like tightening bounds of some variables or removing +bounds of some redundant constraints. + +\subsubsection*{Request for row generation} + +The callback routine is called with the reason code \verb|GLP_IROWGEN| +if LP relaxation of the current subproblem has just been solved to +optimality and its objective value is better than the best known integer +feasible solution. + +In response the callback routine may add one or more ``lazy'' +constraints (rows), which are violated by the current optimal solution +of LP relaxation, using API routines \verb|glp_add_rows|, +\verb|glp_set_row_name|, \verb|glp_set_row_bnds|, and +\verb|glp_set_mat_row|, in which case the solver will perform +re-optimization of LP relaxation. If there are no violated constraints, +the callback routine should just return. + +Optimal solution components for LP relaxation can be obtained with API +routines \verb|glp_get_obj_val|, \verb|glp_get_row_prim|, +\verb|glp_get_row_dual|, \verb|glp_get_col_prim|, and +\verb|glp_get_col_dual|. + +\subsubsection*{Request for heuristic solution} + +The callback routine is called with the reason code \verb|GLP_IHEUR| +if LP relaxation of the current subproblem being solved to optimality +is integer infeasible (i.e. values of some structural variables of +integer kind are fractional), though its objective value is better than +the best known integer feasible solution. + +In response the callback routine may try applying a primal heuristic +to find an integer feasible solution,\footnote{Integer feasible to the +original MIP problem, not to the current subproblem.} which is better +than the best known one. In case of success the callback routine may +store such better solution in the problem object using the routine +\verb|glp_ios_heur_sol|. + +\subsubsection*{Request for cut generation} + +The callback routine is called with the reason code \verb|GLP_ICUTGEN| +if LP relaxation of the current subproblem being solved to optimality +is integer infeasible (i.e. values of some structural variables of +integer kind are fractional), though its objective value is better than +the best known integer feasible solution. + +In response the callback routine may reformulate the {\it current} +subproblem (before it will be splitted up due to branching) by adding to +the problem object one or more {\it cutting plane constraints}, which +cut off the fractional optimal point from the MIP +polytope.\footnote{Since these constraints are added to the current +subproblem, they may be globally as well as locally valid.} + +Adding cutting plane constraints may be performed in two ways. +One way is the same as for the reason code \verb|GLP_IROWGEN| (see +above), in which case the callback routine adds new rows corresponding +to cutting plane constraints directly to the current subproblem. + +The other way is to add cutting plane constraints to the {\it cut pool}, +a set of cutting plane constraints maintained by the solver, rather than +directly to the current subproblem. In this case after return from the +callback routine the solver looks through the cut pool, selects +efficient cutting plane constraints, adds them to the current +subproblem, drops other constraints, and then performs re-optimization. + +\subsubsection*{Request for branching} + +The callback routine is called with the reason code \verb|GLP_IBRANCH| +if LP relaxation of the current subproblem being solved to optimality +is integer infeasible (i.e. values of some structural variables of +integer kind are fractional), though its objective value is better than +the best known integer feasible solution. + +In response the callback routine may choose some variable suitable for +branching (i.e. integer variable, whose value in optimal solution to +LP relaxation of the current subproblem is fractional) and pass its +ordinal number to the solver using the routine +\verb|glp_ios_branch_upon|, in which case the solver splits the current +subproblem in two new subproblems and continues the search. If no choice +is made by the callback routine, the solver uses a branching technique +specified by the control parameter \verb|br_tech|. + +\subsubsection*{Better integer solution found} + +The callback routine is called with the reason code \verb|GLP_IBINGO| +if LP relaxation of the current subproblem being solved to optimality +is integer feasible (i.e. values of all structural variables of integer +kind are integral within the working precision) and its objective value +is better than the best known integer feasible solution. + +Optimal solution components for LP relaxation can be obtained in the +same way as for the reason code \verb|GLP_IROWGEN| (see above). + +Components of the new MIP solution can be obtained with API routines +\verb|glp_mip_obj_val|, \verb|glp_mip_row_val|, and +\verb|glp_mip_col_val|. Note, however, that due to row/cut generation +there may be additional rows in the problem object. + +The difference between optimal solution to LP relaxation and +corresponding MIP solution is that in the former case some structural +variables of integer kind (namely, basic variables) may have values, +which are close to nearest integers within the working precision, while +in the latter case all such variables have exact integral values. + +The reason \verb|GLP_IBINGO| is intended only for informational +purposes, so the callback routine should not modify the problem object +in this case. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Basic routines} + +\subsection{glp\_ios\_reason---determine reason for calling the +callback routine} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_reason(glp_tree *tree); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ios_reason| returns a code, which indicates why +the user-defined callback routine is being called: + +\verb|GLP_ISELECT|---request for subproblem selection; + +\verb|GLP_IPREPRO|---request for preprocessing; + +\verb|GLP_IROWGEN|---request for row generation; + +\verb|GLP_IHEUR |---request for heuristic solution; + +\verb|GLP_ICUTGEN|---request for cut generation; + +\verb|GLP_IBRANCH|---request for branching; + +\verb|GLP_IBINGO |---better integer solution found. + +\subsection{glp\_ios\_get\_prob---access the problem object} + +\subsubsection*{Synopsis} + +\begin{verbatim} +glp_prob *glp_ios_get_prob(glp_tree *tree); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_get_prob| can be called from the user-defined +callback routine to access the problem object, which is used by the MIP +solver. It is the original problem object passed to the routine +\verb|glp_intopt| if the MIP presolver is not used; otherwise it is an +internal problem object built by the presolver. + +\subsubsection*{Returns} + +The routine \verb|glp_ios_get_prob| returns a pointer to the problem +object used by the MIP solver. + +\subsubsection*{Comments} + +To obtain various information about the problem instance the callback +routine can access the problem object (i.e. the object of type +\verb|glp_prob|) using the routine \verb|glp_ios_get_prob|. It is the +original problem object passed to the routine \verb|glp_intopt| if the +MIP presolver is not used; otherwise it is an internal problem object +built by the presolver. + +\subsection{glp\_ios\_row\_attr---determine additional row attributes} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_row_attr| retrieves additional attributes of +$i$-th row of the current subproblem and stores them in the structure +\verb|glp_attr|, which the parameter \verb|attr| points to. + +The structure \verb|glp_attr| has the following fields: + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int level}}\\ +&Subproblem level at which the row was created. (If \verb|level| = 0, +the row was added either to the original problem object passed to the +routine \verb|glp_intopt| or to the root subproblem on generating +``lazy'' or/and cutting plane constraints.)\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int origin}}\\ +&The row origin flag:\\ +&\verb|GLP_RF_REG |---regular constraint;\\ +&\verb|GLP_RF_LAZY|---``lazy'' constraint;\\ +&\verb|GLP_RF_CUT |---cutting plane constraint.\\ +\end{tabular} + +\medskip + +\noindent\begin{tabular}{@{}p{17pt}@{}p{120.5mm}@{}} +\multicolumn{2}{@{}l}{{\tt int klass}}\\ +&The row class descriptor, which is a number passed to the routine +\verb|glp_ios_add_row| as its third parameter. If the row is a cutting +plane constraint generated by the solver, its class may be the +following:\\ +&\verb|GLP_RF_GMI |---Gomory's mixed integer cut;\\ +&\verb|GLP_RF_MIR |---mixed integer rounding cut;\\ +&\verb|GLP_RF_COV |---mixed cover cut;\\ +&\verb|GLP_RF_CLQ |---clique cut.\\ +\end{tabular} + +\newpage + +\subsection{glp\_ios\_mip\_gap---compute relative MIP gap} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_ios_mip_gap(glp_tree *tree); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_mip_gap| computes the relative MIP gap (also +called {\it duality gap}) with the following formula: +$${\tt gap} = \frac{|{\tt best\_mip} - {\tt best\_bnd}|} +{|{\tt best\_mip}| + {\tt DBL\_EPSILON}}$$ +where \verb|best_mip| is the best integer feasible solution found so +far, \verb|best_bnd| is the best (global) bound. If no integer feasible +solution has been found yet, \verb|gap| is set to \verb|DBL_MAX|. + +\subsubsection*{Returns} + +The routine \verb|glp_ios_mip_gap| returns the relative MIP gap. + +\subsubsection*{Comments} + +The relative MIP gap is used to measure the quality of the best integer +feasible solution found so far, because the optimal solution value +$z^*$ for the original MIP problem always lies in the range +$${\tt best\_bnd}\leq z^*\leq{\tt best\_mip}$$ +in case of minimization, or in the range +$${\tt best\_mip}\leq z^*\leq{\tt best\_bnd}$$ +in case of maximization. + +To express the relative MIP gap in percents the value returned by the +routine \verb|glp_ios_mip_gap| should be multiplied by 100\%. + +\newpage + +\subsection{glp\_ios\_node\_data---access application-specific data} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void *glp_ios_node_data(glp_tree *tree, int p); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_node_data| allows the application accessing a +memory block allocated for the subproblem (which may be active or +inactive), whose reference number is $p$. + +The size of the block is defined by the control parameter \verb|cb_size| +passed to the routine \verb|glp_intopt|. The block is initialized by +binary zeros on creating corresponding subproblem, and its contents is +kept until the subproblem will be removed from the tree. + +The application may use these memory blocks to store specific data for +each subproblem. + +\subsubsection*{Returns} + +The routine \verb|glp_ios_node_data| returns a pointer to the memory +block for the specified subproblem. Note that if \verb|cb_size| = 0, the +routine returns a null pointer. + +\subsection{glp\_ios\_select\_node---select subproblem to continue the +search} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_ios_select_node(glp_tree *tree, int p); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_select_node| can be called from the +user-defined callback routine in response to the reason +\verb|GLP_ISELECT| to select an active subproblem, whose reference +number is $p$. The search will be continued from the subproblem +selected. + +\newpage + +\subsection{glp\_ios\_heur\_sol---provide solution found by heuristic} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_heur_sol(glp_tree *tree, const double x[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_heur_sol| can be called from the user-defined +callback routine in response to the reason \verb|GLP_IHEUR| to provide +an integer feasible solution found by a primal heuristic. + +Primal values of {\it all} variables (columns) found by the heuristic +should be placed in locations $x[1]$, \dots, $x[n]$, where $n$ is the +number of columns in the original problem object. Note that the routine +\verb|glp_ios_heur_sol| does {\it not} check primal feasibility of the +solution provided. + +Using the solution passed in the array $x$ the routine computes value +of the objective function. If the objective value is better than the +best known integer feasible solution, the routine computes values of +auxiliary variables (rows) and stores all solution components in the +problem object. + +\subsubsection*{Returns} + +If the provided solution is accepted, the routine +\verb|glp_ios_heur_sol| returns zero. Otherwise, if the provided +solution is rejected, the routine returns non-zero. + +\subsection{glp\_ios\_can\_branch---check if can branch upon specified +variable} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_can_branch(glp_tree *tree, int j); +\end{verbatim} + +\subsubsection*{Returns} + +If $j$-th variable (column) can be used to branch upon, the routine +returns non-zero, otherwise zero. + +\newpage + +\subsection{glp\_ios\_branch\_upon---choose variable to branch upon} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_ios_branch_upon(glp_tree *tree, int j, int sel); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_branch_upon| can be called from the +user-defined callback routine in response to the reason +\verb|GLP_IBRANCH| to choose a branching variable, whose ordinal number +is $j$. Should note that only variables, for which the routine +\verb|glp_ios_can_branch| returns non-zero, can be used to branch upon. + +The parameter \verb|sel| is a flag that indicates which branch +(subproblem) should be selected next to continue the search: + +\verb|GLP_DN_BRNCH|---select down-branch; + +\verb|GLP_UP_BRNCH|---select up-branch; + +\verb|GLP_NO_BRNCH|---use general selection technique. + +\subsubsection*{Comments} + +On branching the solver removes the current active subproblem from the +active list and creates two new subproblems ({\it down-} and {\it +up-branches}), which are added to the end of the active list. Note that +the down-branch is created before the up-branch, so the last active +subproblem will be the up-branch. + +The down- and up-branches are identical to the current subproblem with +exception that in the down-branch the upper bound of $x_j$, the variable +chosen to branch upon, is replaced by $\lfloor x_j^*\rfloor$, while in +the up-branch the lower bound of $x_j$ is replaced by +$\lceil x_j^*\rceil$, where $x_j^*$ is the value of $x_j$ in optimal +solution to LP relaxation of the current subproblem. For example, if +$x_j^*=3.14$, the new upper bound of $x_j$ in the down-branch is +$\lfloor 3.14\rfloor=3$, and the new lower bound in the up-branch is +$\lceil 3.14\rceil=4$.) + +Additionally the callback routine may select either down- or up-branch, +from which the solver will continue the search. If none of the branches +is selected, a general selection technique will be used. + +\newpage + +\subsection{glp\_ios\_terminate---terminate the solution process} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_ios_terminate(glp_tree *tree); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_terminate| sets a flag indicating that the +MIP solver should prematurely terminate the search. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{The search tree exploring routines} + +\subsection{glp\_ios\_tree\_size---determine size of the search tree} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_ios_tree_size(glp_tree *tree, int *a_cnt, int *n_cnt, + int *t_cnt); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_tree_size| stores the following three counts +which characterize the current size of the search tree: + +\verb|a_cnt| is the current number of active nodes, i.e. the current +size of the active list; + +\verb|n_cnt| is the current number of all (active and inactive) nodes; + +\verb|t_cnt| is the total number of nodes including those which have +been already removed from the tree. This count is increased whenever +a new node appears in the tree and never decreased. + +If some of the parameters \verb|a_cnt|, \verb|n_cnt|, \verb|t_cnt| is +a null pointer, the corresponding count is not stored. + +\subsection{glp\_ios\_curr\_node---determine current active subprob-\\ +lem} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_curr_node(glp_tree *tree); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ios_curr_node| returns the reference number of the +current active subproblem. However, if the current subproblem does not +exist, the routine returns zero. + +\newpage + +\subsection{glp\_ios\_next\_node---determine next active subproblem} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_next_node(glp_tree *tree, int p); +\end{verbatim} + +\subsubsection*{Returns} + +If the parameter $p$ is zero, the routine \verb|glp_ios_next_node| +returns the reference number of the first active subproblem. However, +if the tree is empty, zero is returned. + +If the parameter $p$ is not zero, it must specify the reference number +of some active subproblem, in which case the routine returns the +reference number of the next active subproblem. However, if there is +no next active subproblem in the list, zero is returned. + +All subproblems in the active list are ordered chronologically, i.e. +subproblem $A$ precedes subproblem $B$ if $A$ was created before $B$. + +\subsection{glp\_ios\_prev\_node---determine previous active subproblem} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_prev_node(glp_tree *tree, int p); +\end{verbatim} + +\subsubsection*{Returns} + +If the parameter $p$ is zero, the routine \verb|glp_ios_prev_node| +returns the reference number of the last active subproblem. However, if +the tree is empty, zero is returned. + +If the parameter $p$ is not zero, it must specify the reference number +of some active subproblem, in which case the routine returns the +reference number of the previous active subproblem. However, if there +is no previous active subproblem in the list, zero is returned. + +All subproblems in the active list are ordered chronologically, i.e. +subproblem $A$ precedes subproblem $B$ if $A$ was created before $B$. + +\newpage + +\subsection{glp\_ios\_up\_node---determine parent subproblem} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_up_node(glp_tree *tree, int p); +\end{verbatim} + +\subsubsection*{Returns} + +The parameter $p$ must specify the reference number of some (active or +inactive) subproblem, in which case the routine \verb|iet_get_up_node| +returns the reference number of its parent subproblem. However, if the +specified subproblem is the root of the tree and, therefore, has +no parent, the routine returns zero. + +\subsection{glp\_ios\_node\_level---determine subproblem level} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_node_level(glp_tree *tree, int p); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ios_node_level| returns the level of the +subproblem,\linebreak whose reference number is $p$, in the +branch-and-bound tree. (The root subproblem has level 0, and the level +of any other subproblem is the level of its parent plus one.) + +\subsection{glp\_ios\_node\_bound---determine subproblem local\\bound} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_ios_node_bound(glp_tree *tree, int p); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ios_node_bound| returns the local bound for +(active or inactive) subproblem, whose reference number is $p$. + +\subsubsection*{Comments} + +The local bound for subproblem $p$ is an lower (minimization) or upper +(maximization) bound for integer optimal solution to {\it this} +subproblem (not to the original problem). This bound is local in the +sense that only subproblems in the subtree rooted at node $p$ cannot +have better integer feasible solutions. + +On creating a subproblem (due to the branching step) its local bound is +inherited from its parent and then may get only stronger (never weaker). +For the root subproblem its local bound is initially set to +\verb|-DBL_MAX| (minimization) or \verb|+DBL_MAX| (maximization) and +then improved as the root LP relaxation has been solved. + +Note that the local bound is not necessarily the optimal objective value +to corresponding LP relaxation. + +\subsection{glp\_ios\_best\_node---find active subproblem with best +local bound} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_best_node(glp_tree *tree); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ios_best_node| returns the reference number of +the active subproblem, whose local bound is best (i.e. smallest in case +of minimization or largest in case of maximization). However, if the +tree is empty, the routine returns zero. + +\subsubsection*{Comments} + +The best local bound is an lower (minimization) or upper (maximization) +bound for integer optimal solution to the original MIP problem. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{The cut pool routines} + +\subsection{glp\_ios\_pool\_size---determine current size of the cut\\ +pool} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_pool_size(glp_tree *tree); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_ios_pool_size| returns the current size of the +cut pool, that is, the number of cutting plane constraints currently +added to it. + +\subsection{glp\_ios\_add\_row---add constraint to the cut pool} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_ios_add_row(glp_tree *tree, const char *name, + int klass, int flags, int len, const int ind[], + const double val[], int type, double rhs); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_add_row| adds specified row (cutting plane +constraint) to the cut pool. + +The cutting plane constraint should have the following format: +$$\sum_{j\in J}a_jx_j\left\{\begin{array}{@{}c@{}}\geq\\\leq\\ +\end{array}\right\}b,$$ +where $J$ is a set of indices (ordinal numbers) of structural variables, +$a_j$ are constraint coefficients, $x_j$ are structural variables, $b$ +is the right-hand side. + +The parameter \verb|name| specifies a symbolic name assigned to the +constraint (1 up to 255 characters). If it is \verb|NULL| or an empty +string, no name is assigned. + +The parameter \verb|klass| specifies the constraint class, which must +be either zero or a number in the range from 101 to 200. The application +may use this attribute to distinguish between cutting plane constraints +of different classes.\footnote{Constraint classes numbered from 1 to 100 +are reserved for GLPK cutting plane generators.} + +The parameter \verb|flags| currently is not used and must be zero. + +Ordinal numbers of structural variables (i.e. column indices) $j\in J$ +and numerical values of corresponding constraint coefficients $a_j$ must +be placed in locations \verb|ind[1]|, \dots, \verb|ind[len]| and +\verb|val[1]|, \dots, \verb|val[len]|, respectively, where +${\tt len}=|J|$ is the number of constraint coefficients, +$0\leq{\tt len}\leq n$, and $n$ is the number of columns in the problem +object. Coefficients with identical column indices are not allowed. +Zero coefficients are allowed, however, they are ignored. + +The parameter \verb|type| specifies the constraint type as follows: + +\verb|GLP_LO| means inequality constraint $\Sigma a_jx_j\geq b$; + +\verb|GLP_UP| means inequality constraint $\Sigma a_jx_j\leq b$; + +The parameter \verb|rhs| specifies the right-hand side $b$. + +All cutting plane constraints in the cut pool are identified by their +ordinal numbers 1, 2, \dots, $size$, where $size$ is the current size +of the cut pool. New constraints are always added to the end of the cut +pool, thus, ordinal numbers of previously added constraints are not +changed. + +\subsubsection*{Returns} + +The routine \verb|glp_ios_add_row| returns the ordinal number of the +cutting plane constraint added, which is the new size of the cut pool. + +\subsubsection*{Example} + +\begin{verbatim} +/* generate triangle cutting plane: + x[i] + x[j] + x[k] <= 1 */ +. . . +/* add the constraint to the cut pool */ +ind[1] = i, val[1] = 1.0; +ind[2] = j, val[2] = 1.0; +ind[3] = k, val[3] = 1.0; +glp_ios_add_row(tree, NULL, TRIANGLE_CUT, 0, 3, ind, val, + GLP_UP, 1.0); +\end{verbatim} + +\subsubsection*{Comments} + +Cutting plane constraints added to the cut pool are intended to be then +added only to the {\it current} subproblem, so these constraints can be +globally as well as locally valid. However, adding a constraint to the +cut pool does not mean that it will be added to the current +subproblem---it depends on the solver's decision: if the constraint +seems to be efficient, it is moved from the pool to the current +subproblem, otherwise it is simply dropped.\footnote{Globally valid +constraints could be saved and then re-used for other subproblems, but +currently such feature is not implemented.} + +Normally, every time the callback routine is called for cut generation, +the cut pool is empty. On the other hand, the solver itself can generate +cutting plane constraints (like Gomory's or mixed integer rounding +cuts), in which case the cut pool may be non-empty. + +\subsection{glp\_ios\_del\_row---remove constraint from the cut pool} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_ios_del_row(glp_tree *tree, int i); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_del_row| deletes $i$-th row (cutting plane +constraint) from the cut pool, where $1\leq i\leq size$ is the ordinal +number of the constraint in the pool, $size$ is the current size of the +cut pool. + +Note that deleting a constraint from the cut pool leads to changing +ordinal numbers of other constraints remaining in the pool. New ordinal +numbers of the remaining constraints are assigned under assumption that +the original order of constraints is not changed. Let, for example, +there be four constraints $a$, $b$, $c$ and $d$ in the cut pool, which +have ordinal numbers 1, 2, 3 and 4, respectively, and let constraint +$b$ have been deleted. Then after deletion the remaining constraint $a$, +$c$ and $d$ are assigned new ordinal numbers 1, 2 and 3, respectively. + +To find the constraint to be deleted the routine \verb|glp_ios_del_row| +uses ``smart'' linear search, so it is recommended to remove constraints +in a natural or reverse order and avoid removing them in a random order. + +\subsubsection*{Example} + +\begin{verbatim} +/* keep first 10 constraints in the cut pool and remove other + constraints */ +while (glp_ios_pool_size(tree) > 10) + glp_ios_del_row(tree, glp_ios_pool_size(tree)); +\end{verbatim} + +\newpage + +\subsection{glp\_ios\_clear\_pool---remove all constraints from the cut +pool} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_ios_clear_pool(glp_tree *tree); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_ios_clear_pool| makes the cut pool empty deleting +all existing rows (cutting plane constraints) from it. + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk06.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk06.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,896 @@ +%* glpk06.tex *% + +\chapter{Miscellaneous API Routines} + +\section{GLPK environment routines} + +\subsection{glp\_long---64-bit integer data type} + +Some GLPK API routines use 64-bit integer data type, which is declared +in the header \verb|glpk.h| as follows: + +\begin{verbatim} +typedef struct { int lo, hi; } glp_long; +\end{verbatim} + +\noindent +where \verb|lo| contains low 32 bits, and \verb|hi| contains high 32 +bits of 64-bit integer value.\footnote{GLPK conforms to ILP32, LLP64, +and LP64 programming models, where the built-in type {\tt int} +corresponds to 32-bit integers.} + +\subsection{glp\_init\_env---initialize GLPK environment} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_init_env(void); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_init_env| initializes the GLPK environment. +Normally the application program does not need to call this routine, +because it is called automatically on the first call to any API routine. + +\newpage + +\subsubsection*{Returns} + +The routine \verb|glp_init_env| returns one of the following codes: + +\noindent +0 --- initialization successful; + +\noindent +1 --- environment is already initialized; + +\noindent +2 --- initialization failed (insufficient memory); + +\noindent +3 --- initialization failed (unsupported programming model). + +\subsection{glp\_version---determine library version} + +\subsubsection*{Synopsis} + +\begin{verbatim} +const char *glp_version(void); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_version| returns a pointer to a null-terminated +character string, which specifies the version of the GLPK library in +the form \verb|"X.Y"|, where `\verb|X|' is the major version number, and +`\verb|Y|' is the minor version number, for example, \verb|"4.16"|. + +\subsubsection*{Example} + +\begin{footnotesize} +\begin{verbatim} +printf("GLPK version is %s\n", glp_version()); +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_free\_env---free GLPK environment} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_free_env(void); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_free_env| frees all resources used by GLPK +routines (memory blocks, etc.) which are currently still in use. + +Normally the application program does not need to call this routine, +because GLPK routines always free all unused resources. However, if +the application program even has deleted all problem objects, there +will be several memory blocks still allocated for the internal library +needs. For some reasons the application program may want GLPK to free +this memory, in which case it should call \verb|glp_free_env|. + +Note that a call to \verb|glp_free_env| invalidates all problem objects +which still exist. + +\subsubsection*{Returns} + +The routine \verb|glp_free_env| returns one of the following codes: + +\noindent +0 --- termination successful; + +\noindent +1 --- environment is inactive (was not initialized). + +\subsection{glp\_printf---write formatted output to terminal} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_printf(const char *fmt, ...); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_printf| uses the format control string +\verb|fmt| to format its parameters and writes the formatted output to +the terminal. + +This routine is a replacement of the standard C function +\verb|printf| and used by all GLPK routines to perform terminal +output. The application program may use \verb|glp_printf| for the same +purpose that allows controlling its terminal output with the routines +\verb|glp_term_out| and \verb|glp_term_hook|. + +\subsection{glp\_vprintf---write formatted output to terminal} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_vprintf(const char *fmt, va_list arg); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_vprintf| uses the format control string +\verb|fmt| to format its parameters specified by the list \verb|arg| +and writes the formatted output to the terminal. + +This routine is a replacement of the standard C function +\verb|vprintf| and used by all GLPK routines to perform terminal +output. The application program may use \verb|glp_vprintf| for the same +purpose that allows controlling its terminal output with the routines +\verb|glp_term_out| and \verb|glp_term_hook|. + +\newpage + +\subsection{glp\_term\_out---enable/disable terminal output} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_term_out(int flag); +\end{verbatim} + +\subsubsection*{Description} + +Depending on the parameter flag the routine \verb|glp_term_out| enables +or disables terminal output performed by glpk routines: + +\verb|GLP_ON | --- enable terminal output; + +\verb|GLP_OFF| --- disable terminal output. + +\subsubsection*{Returns} + +The routine \verb|glp_term_out| returns the previous value of the +terminal output flag (\verb|GLP_ON| or \verb|GLP_OFF|). + +\subsection{glp\_term\_hook---intercept terminal output} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_term_hook(int (*func)(void *info, const char *s), + void *info); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_term_hook| installs the user-defined hook routine +to intercept all terminal output performed by GLPK routines. + +%This feature can be used to redirect the terminal output to other +%destination, for example, to a file or a text window. + +The parameter {\it func} specifies the user-defined hook routine. It is +called from an internal printing routine, which passes to it two +parameters: {\it info} and {\it s}. The parameter {\it info} is a +transit pointer specified in corresponding call to the routine +\verb|glp_term_hook|; it may be used to pass some additional information +to the hook routine. The parameter {\it s} is a pointer to the null +terminated character string, which is intended to be written to the +terminal. If the hook routine returns zero, the printing routine writes +the string {\it s} to the terminal in a usual way; otherwise, if the +hook routine returns non-zero, no terminal output is performed. + +To uninstall the hook routine both parameters {\it func} and {\it info} +should be specified as \verb|NULL|. + +\newpage + +\subsubsection*{Example} + +\begin{footnotesize} +\begin{verbatim} +static int hook(void *info, const char *s) +{ FILE *foo = info; + fputs(s, foo); + return 1; +} + +int main(void) +{ FILE *foo; + . . . + /* redirect terminal output */ + glp_term_hook(hook, foo); + . . . + /* resume terminal output */ + glp_term_hook(NULL, NULL); + . . . +} +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_open\_tee---start copying terminal output} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_open_tee(const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_open_tee| starts copying all the terminal output +to an output text file, whose name is specified by the character string +\verb|fname|. + +\subsubsection*{Returns} + +The routine \verb|glp_open_tee| returns one of the following codes: + +\noindent +0 --- operation successful; + +\noindent +1 --- copying terminal output is already active; + +\noindent +2 --- unable to create output file. + +\newpage + +\subsection{glp\_close\_tee---stop copying terminal output} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_close_tee(void); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_close_tee| stops copying the terminal output to +the output text file previously open by the routine \verb|glp_open_tee| +closing that file. + +\subsubsection*{Returns} + +The routine \verb|glp_close_tee| returns one of the following codes: + +\noindent +0 --- operation successful; + +\noindent +1 --- copying terminal output was not started. + +\subsection{glp\_error---display error message and terminate execution} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_error(const char *fmt, ...); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_error| (implemented as a macro) formats its +parameters using the format control string \verb|fmt|, writes the +formatted message to the terminal, and then abnormally terminates the +program. + +\subsection{glp\_assert---check logical condition} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_assert(int expr); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_assert| (implemented as a macro) checks +a logical condition specified by the expression \verb|expr|. If the +condition is true (non-zero), the routine does nothing; otherwise, if +the condition is false (zero), the routine prints an error message and +abnormally terminates the program. + +This routine is a replacement of the standard C function \verb|assert| +and used by all GLPK routines to check program logic. The application +program may use \verb|glp_assert| for the same purpose. + +\subsection{glp\_error\_hook---install hook to intercept abnormal +termination} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_error_hook(void (*func)(void *info), void *info); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_error_hook| installs a user-defined hook routine +to intercept abnormal termination. + +The parameter \verb|func| specifies the user-defined hook routine. It +is called from the routine \verb|glp_error| before the latter calls the +abort function to abnormally terminate the application program because +of fatal error. The parameter \verb|info| is a transit pointer, +specified in the corresponding call to the routine +\verb|glp_error_hook|; it may be used to pass some information to the +hook routine. + +To uninstall the hook routine the parameters \verb|func| and \verb|info| +should be specified as \verb|NULL|. + +\subsubsection*{Usage note} + +If the hook routine returns, the application program is abnormally +terminated. To prevent abnormal termnation the hook routine may perform +a global jump using the standard function \verb|longjmp|, in which case +the application program {\it must} call the routine \verb|glp_free_env|. + +\subsection{glp\_malloc---allocate memory block} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void *glp_malloc(int size); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_malloc| dynamically allocates a memory block of +\verb|size| bytes long. Should note that: + +1) the parameter \verb|size| must be positive; + +2) being allocated the memory block contains arbitrary data, that is, +it is {\it not} initialized by binary zeros; + +3) if the block cannot be allocated due to insufficient memory, the +routine prints an error message and abnormally terminates the program. + +This routine is a replacement of the standard C function \verb|malloc| +and used by all GLPK routines for dynamic memory allocation. The +application program may use \verb|glp_malloc| for the same purpose. + +\subsubsection*{Returns} + +The routine \verb|glp_malloc| returns a pointer to the memory block +allocated. To free this block the routine \verb|glp_free| (not the +standard C function \verb|free|!) must be used. + +\subsection{glp\_calloc---allocate memory block} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void *glp_calloc(int n, int size); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_calloc| dynamically allocates a memory block of +\verb|n|$\times$\verb|size| bytes long. Should note that: + +1) both parameters \verb|n| and \verb|size| must be positive; + +2) being allocated the memory block contains arbitrary data, that is, +it is {\it not} initialized by binary zeros; + +3) if the block cannot be allocated due to insufficient memory, the +routine prints an error message and abnormally terminates the program. + +This routine is a replacement of the standard C function \verb|calloc| +(with exception that the block is not cleaned) and used by all GLPK +routines for dynamic memory allocation. The application program may use +\verb|glp_calloc| for the same purpose. + +\subsubsection*{Returns} + +The routine \verb|glp_calloc| returns a pointer to the memory block +allocated. To free this block the routine \verb|glp_free| (not the +standard C function \verb|free|!) must be used. + +\subsection{glp\_free---free memory block} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_free(void *ptr); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_free| frees (deallocates) a memory block pointed +to by \verb|ptr|, which was previously allocated by the routine +\verb|glp_malloc| or \verb|glp_calloc|. Note that the pointer \verb|ptr| +must valid and must not be \verb|NULL|. + +This routine is a replacement of the standard C function \verb|free| +and used by all GLPK routines for dynamic memory allocation. The +application program may use \verb|glp_free| for the same purpose. + +\subsection{glp\_mem\_usage---get memory usage information} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_mem_usage(int *count, int *cpeak, glp_long *total, + glp_long *tpeak); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mem_usage| reports some information about +utilization of the memory by the routines \verb|glp_malloc|, +\verb|glp_calloc|, and \verb|glp_free|. Information is stored to +locations specified by corresponding parameters (see below). Any +parameter can be specified as \verb|NULL|, in which case corresponding +information is not stored. + +\verb|*count| is the number of currently allocated memory blocks. + +\verb|*cpeak| is the peak value of \verb|*count| reached since the +initialization of the GLPK library environment. + +\verb|*total| is the total amount, in bytes, of currently allocated +memory blocks. + +\verb|*tpeak| is the peak value of \verb|*total| reached since the +initialization of the GLPK library envirionment. + +\subsubsection*{Example} + +\begin{footnotesize} +\begin{verbatim} +glp_mem_usage(&count, NULL, NULL, NULL); +printf("%d memory block(s) are still allocated\n", count); +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_mem\_limit---set memory usage limit} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_mem_limit(int limit); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mem_limit| limits the amount of memory available +for dynamic allocation (with the routines \verb|glp_malloc| and +\verb|glp_calloc|) to \verb|limit| megabytes. + +\subsection{glp\_time---determine current universal time} + +\subsubsection*{Synopsis} + +\begin{verbatim} +glp_long glp_time(void); +\end{verbatim} + +\subsection*{Returns} + +The routine \verb|glp_time| returns the current universal time (UTC), +in milliseconds, elapsed since 00:00:00 GMT January 1, 1970. + +\subsection{glp\_difftime---compute difference between two time values} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_difftime(glp_long t1, glp_long t0); +\end{verbatim} + +\subsection*{Returns} + +The routine \verb|glp_difftime| returns the difference between two time +values \verb|t1| and \verb|t0|, expressed in seconds. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Plain data file reading routines} + +\subsection{Introduction} + +On developing simple applications to solve optimization problems it is +often needed to read data from plain text files. To do this the standard +C function \verb|fscanf| may be used, however, it is not convenient; for +example, if it scans an integer number according to the format +specification `\verb|%d|', and that number is coded incorrectly, +no diagnostics is provided. + +This section describes a set of GLPK API routines, which may be used in +application programs to simplify reading data from plain text files. + +\subsubsection*{Example 1} + +The following main program reads ten integer numbers from plain text +file \verb|data.txt| and prints their sum. + +\begin{footnotesize} +\begin{verbatim} +/* sdfsamp1.c */ + +#include +#include +#include + +int main(void) +{ glp_data *data; + int j, num, sum; + /* open plain data file */ + data = glp_sdf_open_file("data.txt"); + if (data == NULL) exit(EXIT_FAILURE); + sum = 0; + for (j = 1; j <= 10; j++) + { /* read next integer number */ + num = glp_sdf_read_int(data); + sum += num; + } + printf("sum = %d\n", sum); + /* close plain data file */ + glp_sdf_close_file(data); + return 0; +} + +/* eof */ +\end{verbatim} +\end{footnotesize} + +The input data are coded in free format. For example, the file +\verb|data.txt| may look like this: + +\begin{footnotesize} +\begin{verbatim} +123 65 432 890 -12 743 895 -7 111 326 +\end{verbatim} +\end{footnotesize} + +\noindent +or like this: + +\begin{footnotesize} +\begin{verbatim} +123 65 432 890 -12 +743 895 -7 111 326 +\end{verbatim} +\end{footnotesize} + +\noindent +If the input data file contains incorrect data, the routine +\verb|glp_sdf_read_int| prints an error message and, if no error +handling is provided by the application program, abnormally terminates +program execution. For example, the file \verb|data.txt| could contain +the following data: + +\begin{footnotesize} +\begin{verbatim} +123 65 432 890 -12 +743 895 =7 111 326 +\end{verbatim} +\end{footnotesize} + +\noindent +in which case the error message would be the following: + +\begin{footnotesize} +\begin{verbatim} +data.txt:2: cannot convert `=7' to integer +\end{verbatim} +\end{footnotesize} + +\subsubsection*{Example 2} + +As it was said above, by default any attempt to read incorrect data +leads to abnormal termination. However, sometimes it is desirable to +catch such errors. This feature is illustrated by the following main +program, which does the same job as in the previous example. + +\begin{footnotesize} +\begin{verbatim} +/* sdfsamp2.c */ + +#include +#include +#include +#include + +int main(void) +{ glp_data *data; + jmp_buf jump; + int j, num, sum, ret; + /* open plain data file */ + data = glp_sdf_open_file("data.txt"); + if (data == NULL) + { ret = EXIT_FAILURE; + goto done; + } + /* set up error handling */ + if (setjmp(jump)) + { ret = EXIT_FAILURE; + goto done; + } + glp_sdf_set_jump(data, jump); + /* read and process data */ + sum = 0; + for (j = 1; j <= 10; j++) + { /* read next integer number */ + num = glp_sdf_read_int(data); + if (abs(num) > 1000) + glp_sdf_error(data, "integer %d too big\n", num); + if (num < 0) + glp_sdf_warning(data, "integer %d is negative\n", num); + sum += num; + } + printf("sum = %d\n", sum); + ret = EXIT_SUCCESS; +done: /* close plain data file */ + if (data != NULL) glp_sdf_close_file(data); + return ret; +} + +/* eof */ +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_sdf\_open\_file---open plain data file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +glp_data *glp_sdf_open_file(const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_open_file| opens a plain data file, whose +name is specified by the character string \verb|fname|. + +\subsubsection*{Returns} + +If the operation was successful, the routine \verb|glp_sdf_open_file| +returns a pointer to the opaque program object of the type +\verb|glp_data|\footnote{This data structure is declared in the header +file {\tt glpk.h}.} associated with the plain data file. Otherwise, if +the operation failed, the routine prints an error message and returns +\verb|NULL|. + +\subsubsection*{Note} + +The application program should use the pointer returned by the routine +\verb|glp_sdf_open_file| to perform all subsequent operations on the +data file. + +\newpage + +\subsection{glp\_sdf\_set\_jump---set up error handling} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_sdf_set_jump(glp_data *data, jmp_buf jump); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_set_jump| sets up error handling for the +plain data file specified by the parameter \verb|data|. + +The parameter \verb|jump| specifies the environment buffer, which must +be initialized with the standard C function \verb|setjmp| prior to call +to the routine \verb|glp_sdf_set_jump|. Detecting any incorrect data in +the corresponding plain data file will cause non-local ``go to'' by +a call to the standard C function \verb|longjmp|. + +The parameter \verb|jump| can be specified as \verb|NULL|, in which +case the routine \verb|glp_sdf_set_jump| restores the default behavior, +in which case detecting incorrect data leads to abnormal termination. + +\subsection{glp\_sdf\_error---print error message} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_sdf_error(glp_data *data, const char *fmt, ...); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_error| prints an error message related to the +plain data file specified by the parameter \verb|data|. If error handing +was not previously provided, the routine then abnormally terminates +execution of the application program. Otherwise, it signals about the +error by a call to the standard C function \verb|longjmp|. + +The character string \verb|fmt| and optional parameters following it +have the same meaning as for the standard C function \verb|printf|. + +The message produced by the routine \verb|glp_sdf_error| looks like +follows: + +\medskip + +{\it file}{\tt :}{\it line}{\tt :} {\it message text} + +\medskip + +\noindent +where {\it file} is the filename passed to the routine +\verb|glp_sdf_open| and {\it line} is the current line number. + +\newpage + +\subsection{glp\_sdf\_warning---print warning message} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_sdf_warning(glp_data *data, const char *fmt, ...); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_warning| prints a warning message related to +the plain data file specified by the parameter \verb|data|. + +The character string \verb|fmt| and optional parameters following it +have the same meaning as for the standard C function \verb|printf|. + +The message produced by the routine \verb|glp_sdf_warning| looks like +follows: + +\medskip + +{\it file}{\tt :}{\it line}\verb|: warning:| {\it message text} + +\medskip + +\noindent +where {\it file} is the filename passed to the routine +\verb|glp_sdf_open| and {\it line} is the current line number. + +\subsection{glp\_sdf\_read\_int---read integer number} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_sdf_read_int(glp_data *data); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_read_int| skips optional white-space +characters and then reads an integer number from the plain data file +specified by the parameter \verb|data|. If the operation failed, the +routine \verb|glp_sdf_read_int| calls the routine \verb|glp_sdf_error| +(see above). + +\subsubsection*{Returns} + +The routine \verb|glp_sdf_read_int| returns the integer number read. + +\newpage + +\subsection{glp\_sdf\_read\_num---read floating-point number} + +\subsubsection*{Synopsis} + +\begin{verbatim} +double glp_sdf_read_num(glp_data *data); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_read_num| skips optional white-space +characters and then reads a floating-point number from the plain data +file specified by the parameter \verb|data|. If the operation failed, +the routine \verb|glp_sdf_num| calls the routine \verb|glp_sdf_error| +(see above). + +\subsubsection*{Returns} + +The routine \verb|glp_sdf_read_num| returns the floating-point number +read. + +\subsection{glp\_sdf\_read\_item---read data item} + +\subsubsection*{Synopsis} + +\begin{verbatim} +const char *glp_sdf_read_item(glp_data *data); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_read_item| skips optional white-space +characters and then reads a data item from the plain data file specified +by the parameter \verb|data|. If the operation failed, the routine +\verb|glp_sdf_read_item| calls the routine \verb|glp_sdf_error| (see +above). + +{\it Data item} is a sequence of 1 to 255 arbitrary graphic characters +delimited by white-space characters. Data items may be used to represent +symbolic names, identifiers, etc. + +\subsubsection*{Returns} + +The routine \verb|glp_sdf_read_item| returns a pointer to the internal +buffer, which contains the data item read in the form of a +null-terminated character string. + +\newpage + +\subsection{glp\_sdf\_read\_text---read text until end of line} + +\subsubsection*{Synopsis} + +\begin{verbatim} +const char *glp_sdf_read_text(glp_data *data); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_read_text| reads a text from the plain data +file specified by the parameter \verb|data|. + +Reading starts from the current position and extends until end of the +current line. Initial and trailing white-space characters as well as +the newline character are not included in the text. + +\subsubsection*{Returns} + +The routine \verb|glp_sdf_read_text| returns a pointer to the internal +buffer, which contains the text read in the form of a null-terminated +character string. + +\subsection{glp\_sdf\_line---determine current line number} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_sdf_line(glp_data *data); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_sdf_line| returns the current line number for the +plain data file specified by the parameter \verb|data|. + +\subsection{glp\_sdf\_close\_file---close plain data file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_sdf_close_file(glp_data *data); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_sdf_close_file| closes the plain data file +specified by the parameter \verb|data| and frees all the resources +allocated to this program object. + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk07.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk07.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,336 @@ +%* glpk07.tex *% + +\chapter{Installing GLPK on Your Computer} +\label{install} + +\section{Downloading the distribution tarball} + +The distribution tarball of the most recent version of the GLPK +package can be found on \url{http://ftp.gnu.org/gnu/glpk/} [via http] +and \url{ftp://ftp.gnu.org/gnu/glpk/} [via FTP]. It can also be found +on one of the FTP mirrors; see \url{http://www.gnu.org/prep/ftp.html}. +Please use a mirror if possible. + +To make sure that the GLPK distribution tarball you have downloaded is +intact you need to download the corresponding `\verb|.sig|' file and +run a command like this: + +\begin{verbatim} + gpg --verify glpk-4.38.tar.gz.sig +\end{verbatim} + +\noindent +If that command fails because you do not have the required public key, +run the following command to import it: + +\begin{verbatim} + gpg --keyserver keys.gnupg.net --recv-keys 5981E818 +\end{verbatim} + +\noindent +and then re-run the previous command. + +\section{Unpacking the distribution tarball} + +The GLPK package (like all other GNU software) is distributed in the +form of packed archive. This is one file named \verb|glpk-X.Y.tar.gz|, +where {\it X} is the major version number and {\it Y} is the minor +version number. + +In order to prepare the distribution for installation you should: + +\medskip + +1. Copy the GLPK distribution file to a working subdirectory. + +\medskip + +2. Unpack the distribution file with the following command: + +\begin{verbatim} + gzip -d glpk-X.Y.tar.gz +\end{verbatim} + +\noindent +that renames the distribution file to \verb|glpk-X.Y.tar|. + +\medskip + +3. Unarchive the distribution file with the following command: + +\begin{verbatim} + tar -x < glpk-X.Y.tar +\end{verbatim} + +\noindent +that automatically creates the subdirectory \verb|glpk-X.Y| containing +the GLPK distribution. + + +\section{Configuring the package} + +After unpacking and unarchiving the GLPK distribution you should +configure the package, i.e. automatically tune it for your platform. + +Normally, you should just \verb|cd| to the subdirectory +\verb|glpk-X.Y| and run the configure script, e.g. + +\begin{verbatim} + ./configure +\end{verbatim} + +The `\verb|configure|' shell script attempts to guess correct values +for various system-dependent variables used during compilation. It uses +those values to create a `\verb|Makefile|' in each directory of the +package. It also creates file `\verb|config.h|' containing +platform-dependent definitions. Finally, it creates a shell script +`\verb|config.status|' that you can run in the future to recreate the +current configuration, a file `\verb|config.cache|' that saves the +results of its tests to speed up reconfiguring, and a file +`\verb|config.log|' containing compiler output (useful mainly for +debugging `\verb|configure|'). + +Running `\verb|configure|' takes about a few minutes. While it is +running, it displays some informational messages that tell you what it +is doing. If you don't want to see these messages, run +`\verb|configure|' with its standard output redirected to +`\verb|dev/null|'; for example, `\verb|./configure > /dev/null|'. + +By default both static and shared versions of the GLPK library will be +compiled. Compilation of the shared librariy can be turned off by +specifying the `\verb|--disable-shared|' option to `\verb|configure|', +e.g. + +\begin{verbatim} + ./configure --disable-shared +\end{verbatim} + +\noindent +If you encounter problems building the library try using the above +option, because some platforms do not support shared libraries. + +\newpage + +The GLPK package has some optional features listed below. By default +all these features are disabled. To enable a feature the corresponding +option should be passed to the configure script. + +\bigskip + +\noindent +\verb|--with-gmp | Enable using the GNU MP bignum library + +\medskip + +This feature allows the exact simplex solver to use the GNU MP bignum +library. If it is disabled, the exact simplex solver uses the GLPK +bignum module, which provides the same functionality as GNU MP, however, +it is much less efficient. + +For details about the GNU MP bignum library see its web page at +\url{http://gmplib.org/}. + +\bigskip + +\noindent +\verb|--with-zlib | +Enable using the zlib data compression library + +\medskip + +This feature allows GLPK API routines and the stand-alone solver to +read and write compressed data files performing compression and +decompression ``on the fly'' (compressed data files are recognized by +suffix `\verb|.gz|' in the file name). It may be useful in case of +large MPS files to save the disk space. + +For details about the zlib compression library see its web page at +\url{http://www.zlib.net/}. + +\bigskip + +\noindent +\verb|--enable-dl | The same as `\verb|--enable-dl=ltdl|' + +\noindent +\verb|--enable-dl=ltdl | Enable shared library support (GNU) + +\noindent +\verb|--enable-dl=dlfcn | Enable shared library support (POSIX) + +\medskip + +Currently this feature is only needed to provide dynamic linking to +ODBC and MySQL shared libraries (see below). + +For details about the GNU shared library support see the manual at +\url{http://www.gnu.org/software/libtool/manual/}. + +\bigskip + +\noindent +\verb|--enable-odbc | +Enable using ODBC table driver (\verb|libiodbc|) + +\noindent +\verb|--enable-odbc=unix | +Enable using ODBC table driver (\verb|libodbc|) + +\medskip + +This feature allows transmitting data between MathProg model objects +and relational databases accessed through ODBC. + +For more details about this feature see the supplement ``Using Data +Tables in the GNU MathProg Modeling Language'' (\verb|doc/tables.pdf|). + +\bigskip + +\noindent +\verb|--enable-mysql | +Enable using MySQL table driver (\verb|libmysql|) + +\medskip + +This feature allows transmitting data between MathProg model objects +and MySQL relational databases. + +For more details about this feature see the supplement ``Using Data +Tables in the GNU MathProg Modeling Language'' (\verb|doc/tables.pdf|). + +\section{Compiling the package} + +Normally, you can compile (build) the package by typing the command: + +\begin{verbatim} + make +\end{verbatim} + +\noindent +It reads `\verb|Makefile|' generated by `\verb|configure|' and performs +all necessary jobs. + +If you want, you can override the `\verb|make|' variables \verb|CFLAGS| +and \verb|LDFLAGS| like this: + +\begin{verbatim} + make CFLAGS=-O2 LDFLAGS=-s +\end{verbatim} + +To compile the package in a different directory from the one containing +the source code, you must use a version of `\verb|make|' that supports +`\verb|VPATH|' variable, such as GNU `\verb|make|'. `\verb|cd|' to the +directory where you want the object files and executables to go and run +the `\verb|configure|' script. `\verb|configure|' automatically checks +for the source code in the directory that `\verb|configure|' is in and +in `\verb|..|'. If for some reason `\verb|configure|' is not in the +source code directory that you are configuring, then it will report that +it can't find the source code. In that case, run `\verb|configure|' with +the option `\verb|--srcdir=DIR|', where \verb|DIR| is the directory that +contains the source code. + +Some systems require unusual options for compilation or linking that +the `\verb|configure|' script does not know about. You can give +`\verb|configure|' initial values for variables by setting them in the +environment. Using a Bourne-compatible shell, you can do that on the +command line like this: + +\begin{verbatim} + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure +\end{verbatim} + +\noindent +Or on systems that have the `\verb|env|' program, you can do it like +this: + +\begin{verbatim} + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure +\end{verbatim} + +Here are the `\verb|make|' variables that you might want to override +with environment variables when running `\verb|configure|'. + +For these variables, any value given in the environment overrides the +value that `\verb|configure|' would choose: + +\medskip + +\noindent +\verb|CC | C compiler program. The default is `\verb|cc|'. + +\medskip + +\noindent +\verb|INSTALL | Program used to install files. The default value is +`\verb|install|' if + +\noindent +\verb| | you have it, otherwise `\verb|cp|'. + +\medskip + +For these variables, any value given in the environment is added to the +value that `\verb|configure|' chooses: + +\medskip + +\noindent +\verb|DEFS | Configuration options, in the form +`\verb|-Dfoo -Dbar| \dots'. + +\medskip + +\noindent +\verb|LIBS | Libraries to link with, in the form +`\verb|-lfoo -lbar| \dots'. + +\section{Checking the package} + +To check the package, i.e. to run some tests included in the package, +you can use the following command: + +\begin{verbatim} + make check +\end{verbatim} + +\section{Installing the package} + +Normally, to install the GLPK package you should type the following +command: + +\begin{verbatim} + make install +\end{verbatim} + +\noindent +By default, `\verb|make install|' will install the package's files in +`\verb|usr/local/bin|', `\verb|usr/local/lib|', etc. You can specify an +installation prefix other than `\verb|/usr/local|' by giving +`\verb|configure|' the option `\verb|--prefix=PATH|'. Alternately, you +can do so by consistently giving a value for the `\verb|prefix|' +variable when you run `\verb|make|', e.g. + +\begin{verbatim} + make prefix=/usr/gnu + make prefix=/usr/gnu install +\end{verbatim} + +After installing you can remove the program binaries and object files +from the source directory by typing `\verb|make clean|'. To remove all +files that `\verb|configure|' created (`\verb|Makefile|', +`\verb|config.status|', etc.), just type `\verb|make distclean|'. + +The file `\verb|configure.ac|' is used to create `\verb|configure|' by +a program called `\verb|autoconf|'. You only need it if you want to +remake `\verb|configure|' using a newer version of `\verb|autoconf|'. + +\section{Uninstalling the package} + +To uninstall the GLPK package, i.e. to remove all the package's files +from the system places, you can use the following command: + +\begin{verbatim} + make uninstall +\end{verbatim} + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk08.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk08.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,696 @@ +%* glpk08.tex *% + +\chapter{MPS Format} +\label{champs} + +\section{Fixed MPS Format} +\label{secmps} + +The MPS format\footnote{The MPS format was developed in 1960's by IBM +as input format for their mathematical programming system MPS/360. +Today the MPS format is a most widely used format understood by most +mathematical programming packages. This appendix describes only the +features of the MPS format, which are implemented in the GLPK package.} +is intended for coding LP/MIP problem data. This format assumes the +formulation of LP/MIP problem (1.1)---(1.3) (see Section \ref{seclp}, +page \pageref{seclp}). + +{\it MPS file} is a text file, which contains two types of +cards\footnote{In 1960's MPS file was a deck of 80-column punched cards, +so the author decided to keep the word ``card'', which may be understood +as ``line of text file''.}: indicator cards and data cards. + +Indicator cards determine a kind of succeeding data. Each indicator card +has one word in uppercase letters beginning in column 1. + +Data cards contain problem data. Each data card is divided into six +fixed fields: + +\begin{center} +\begin{tabular}{lcccccc} +& Field 1 & Field 2 & Field 3 & Field 4 & Field 5 & Feld 6 \\ +\hline +Columns & 2---3 & 5---12 & 15---22 & 25---36 & 40---47 & 50---61 \\ +Contents & Code & Name & Name & Number & Name & Number \\ +\end{tabular} +\end{center} + +On a particular data card some fields may be optional. + +Names are used to identify rows, columns, and some vectors (see below). + +Aligning the indicator code in the field 1 to the left margin is +optional. + +All names specified in the fields 2, 3, and 5 should contain from 1 up +to 8 arbitrary characters (except control characters). If a name is +placed in the field 3 or 5, its first character should not be the dollar +sign `\verb|$|'. If a name contains spaces, the spaces are ignored. + +All numerical values in the fields 4 and 6 should be coded in the form +$sxx$\verb|E|$syy$, where $s$ is the plus `\verb|+|' or the minus +`\verb|-|' sign, $xx$ is a real number with optional decimal point, +$yy$ is an integer decimal exponent. Any number should contain up to 12 +characters. If the sign $s$ is omitted, the plus sign is assumed. The +exponent part is optional. If a number contains spaces, the spaces are +ignored. + +If a card has the asterisk `\verb|*|' in the column 1, this card is +considered as a comment and ignored. Besides, if the first character in +the field 3 or 5 is the dollar sign `\verb|$|', all characters from the +dollar sign to the end of card are considered as a comment and ignored. + +MPS file should contain cards in the following order: + +$\bullet$ NAME indicator card; + +$\bullet$ ROWS indicator card; + +$\bullet$ data cards specifying rows (constraints); + +$\bullet$ COLUMNS indicator card; + +$\bullet$ data cards specifying columns (structural variables) and +constraint coefficients; + +$\bullet$ RHS indicator card; + +$\bullet$ data cards specifying right-hand sides of constraints; + +$\bullet$ RANGES indicator card; + +$\bullet$ data cards specifying ranges for double-bounded constraints; + +$\bullet$ BOUNDS indicator card; + +$\bullet$ data cards specifying types and bounds of structural +variables; + +$\bullet$ ENDATA indicator card. + +{\it Section} is a group of cards consisting of an indicator card and +data cards succeeding this indicator card. For example, the ROWS section +consists of the ROWS indicator card and data cards specifying rows. + +The sections RHS, RANGES, and BOUNDS are optional and may be omitted. + +\section{Free MPS Format} + +{\it Free MPS format} is an improved version of the standard (fixed) +MPS format described above.\footnote{This format was developed in the +beginning of 1990's by IBM as an alternative to the standard fixed MPS +format for Optimization Subroutine Library (OSL).} Note that all +changes in free MPS format concern only the coding of data while the +structure of data is the same for both fixed and free versions of the +MPS format. + +In free MPS format indicator and data records\footnote{{\it Record} in +free MPS format has the same meaning as {\it card} in fixed MPS format.} +may have arbitrary length not limited to 80 characters. Fields of data +records have no predefined positions, i.e. the fields may begin in any +position, except position 1, which must be blank, and must be separated +from each other by one or more blanks. However, the fields must appear +in the same order as in fixed MPS format. + +Symbolic names in fields 2, 3, and 5 may be longer than 8 +characters\footnote{GLPK allows symbolic names having up to 255 +characters.} +and must not contain embedded blanks. + +Numeric values in fields 4 and 6 are limited to 12 characters and must +not contain embedded blanks. + +Only six fields on each data record are used. Any other fields are +ignored. + +If the first character of any field (not necessarily fields 3 and 5) +is the dollar sign (\$), all characters from the dollar sign to the end +of record are considered as a comment and ignored. + +\section{NAME indicator card} + +The NAME indicator card should be the first card in the MPS file (except +optional comment cards, which may precede the NAME card). This card +should contain the word \verb|NAME| in the columns 1---4 and the problem +name in the field 3. The problem name is optional and may be omitted. + +\section{ROWS section} +\label{secrows} + +The ROWS section should start with the indicator card, which contains +the word \verb|ROWS| in the columns 1---4. + +Each data card in the ROWS section specifies one row (constraint) of the +problem. All these data cards have the following format. + +`\verb|N|' in the field 1 means that the row is free (unbounded): +$$-\infty < x_i = a_{i1}x_{m+1} + a_{i2}x_{m+2} + \dots + a_{in}x_{m+n} +< +\infty;$$ + +`\verb|L|' in the field 1 means that the row is of ``less than or equal +to'' type: +$$-\infty < x_i = a_{i1}x_{m+1} + a_{i2}x_{m+2} + \dots + a_{in}x_{m+n} +\leq b_i;$$ + +`\verb|G|' in the field 1 means that the row is of ``greater than or +equal to'' type: +$$b_i \leq x_i = a_{i1}x_{m+1} + a_{i2}x_{m+2} + \dots + a_{in}x_{m+n} +< +\infty;$$ + +`\verb|E|' in the field 1 means that the row is of ``equal to'' type: +$$x_i = a_{i1}x_{m+1} + a_{i2}x_{m+2} + \dots + a_{in}x_{m+n} \leq +b_i,$$ +where $b_i$ is a right-hand side. Note that each constraint has a +corresponding implictly defined auxiliary variable ($x_i$ above), whose +value is a value of the corresponding linear form, therefore row bounds +can be considered as bounds of such auxiliary variable. + +The filed 2 specifies a row name (which is considered as the name of +the corresponding auxiliary variable). + +The fields 3, 4, 5, and 6 are not used and should be empty. + +Numerical values of all non-zero right-hand sides $b_i$ should be +specified in the RHS section (see below). All double-bounded (ranged) +constraints should be specified in the RANGES section (see below). + +\section{COLUMNS section} + +The COLUMNS section should start with the indicator card, which contains +the word \verb|COLUMNS| in the columns 1---7. + +Each data card in the COLUMNS section specifies one or two constraint +coefficients $a_{ij}$ and also introduces names of columns, i.e. names +of structural variables. All these data cards have the following format. + +The field 1 is not used and should be empty. + +The field 2 specifies a column name. If this field is empty, the column +name from the immediately preceeding data card is assumed. + +The field 3 specifies a row name defined in the ROWS section. + +The field 4 specifies a numerical value of the constraint coefficient +$a_{ij}$, which is placed in the corresponding row and column. + +The fields 5 and 6 are optional. If they are used, they should contain +a second pair ``row name---constraint coefficient'' for the same column. + +Elements of the constraint matrix (i.e. constraint coefficients) should +be enumerated in the column wise manner: all elements for the current +column should be specified before elements for the next column. However, +the order of rows in the COLUMNS section may differ from the order of +rows in the ROWS section. + +Constraint coefficients not specified in the COLUMNS section are +considered as zeros. Therefore zero coefficients may be omitted, +although it is allowed to explicitly specify them. + +\section{RHS section} + +The RHS section should start with the indicator card, which contains the +word \verb|RHS| in the columns 1---3. + +Each data card in the RHS section specifies one or two right-hand sides +$b_i$ (see Section \ref{secrows}, page \pageref{secrows}). All these +data cards have the following format. + +The field 1 is not used and should be empty. + +The field 2 specifies a name of the right-hand side (RHS) +vector\footnote{This feature allows the user to specify several RHS +vectors in the same MPS file. However, before solving the problem a +particular RHS vector should be chosen.}. If this field is empty, the +RHS vector name from the immediately preceeding data card is assumed. + +The field 3 specifies a row name defined in the ROWS section. + +The field 4 specifies a right-hand side $b_i$ for the row, whose name is +specified in the field 3. Depending on the row type $b_i$ is a lower +bound (for the row of \verb|G| type), an upper bound (for the row of +\verb|L| type), or a fixed value (for the row of \verb|E| +type).\footnote{If the row is of {\tt N} type, $b_i$ is considered as +a constant term of the corresponding linear form. Should note, however, +this convention is non-standard.} + +The fields 5 and 6 are optional. If they are used, they should contain +a second pair ``row name---right-hand side'' for the same RHS vector. + +All right-hand sides for the current RHS vector should be specified +before right-hand sides for the next RHS vector. However, the order of +rows in the RHS section may differ from the order of rows in the ROWS +section. + +Right-hand sides not specified in the RHS section are considered as +zeros. Therefore zero right-hand sides may be omitted, although it is +allowed to explicitly specify them. + +\section{RANGES section} + +The RANGES section should start with the indicator card, which contains +the word \verb|RANGES| in the columns 1---6. + +Each data card in the RANGES section specifies one or two ranges for +double-side constraints, i.e. for constraints that are of the types +\verb|L| and \verb|G| at the same time: +$$l_i \leq x_i = a_{i1}x_{m+1} + a_{i2}x_{m+2} + \dots + a_{in}x_{m+n} +\leq u_i,$$ +where $l_i$ is a lower bound, $u_i$ is an upper bound. All these data +cards have the following format. + +The field 1 is not used and should be empty. + +The field 2 specifies a name of the range vector\footnote{This feature +allows the user to specify several range vectors in the same MPS file. +However, before solving the problem a particular range vector should be +chosen.}. If this field is empty, the range vector name from the +immediately preceeding data card is assumed. + +The field 3 specifies a row name defined in the ROWS section. + +The field 4 specifies a range value $r_i$ (see the table below) for the +row, whose name is specified in the field 3. + +The fields 5 and 6 are optional. If they are used, they should contain +a second pair ``row name---range value'' for the same range vector. + +All range values for the current range vector should be specified before +range values for the next range vector. However, the order of rows in +the RANGES section may differ from the order of rows in the ROWS +section. + +For each double-side constraint specified in the RANGES section its +lower and upper bounds are determined as follows: + +\begin{center} +\begin{tabular}{cccc} +Row type & Sign of $r_i$ & Lower bound & Upper bound \\ +\hline +{\tt G} & $+$ or $-$ & $b_i$ & $b_i + |r_i|$ \\ +{\tt L} & $+$ or $-$ & $b_i - |r_i|$ & $b_i$ \\ +{\tt E} & $+$ & $b_i$ & $b_i + |r_i|$ \\ +{\tt E} & $-$ & $b_i - |r_i|$ & $b_i$ \\ +\end{tabular} +\end{center} + +\noindent +where $b_i$ is a right-hand side specified in the RHS section (if $b_i$ +is not specified, it is considered as zero), $r_i$ is a range value +specified in the RANGES section. + +\section{BOUNDS section} +\label{secbounds} + +The BOUNDS section should start with the indicator card, which contains +the word \verb|BOUNDS| in the columns 1---6. + +Each data card in the BOUNDS section specifies one (lower or upper) +bound for one structural variable (column). All these data cards have +the following format. + +The indicator in the field 1 specifies the bound type: + +\begin{tabular}{@{}ll} +\verb|LO| & lower bound; \\ +\verb|UP| & upper bound; \\ +\verb|FX| & fixed variable (lower and upper bounds are equal); \\ +\verb|FR| & free variable (no bounds); \\ +\verb|MI| & no lower bound (lower bound is ``minus infinity''); \\ +\verb|PL| & no upper bound (upper bound is ``plus infinity''); \\ +\end{tabular} + +The field 2 specifies a name of the bound vector\footnote{This feature +allows the user to specify several bound vectors in the same MPS file. +However, before solving the problem a particular bound vector should be +chosen.}. If this field is empty, the bound vector name from the +immediately preceeding data card is assumed. + +The field 3 specifies a column name defined in the COLUMNS section. + +The field 4 specifies a bound value. If the bound type in the field 1 +differs from \verb|LO|, \verb|UP|, and \verb|FX|, the value in the field +4 is ignored and may be omitted. + +The fields 5 and 6 are not used and should be empty. + +All bound values for the current bound vector should be specified before +bound values for the next bound vector. However, the order of columns in +the BOUNDS section may differ from the order of columns in the COLUMNS +section. Specification of a lower bound should precede specification of +an upper bound for the same column (if both the lower and upper bounds +are explicitly specified). + +By default, all columns (structural variables) are non-negative, i.e. +have zero lower bound and no upper bound. Lower ($l_j$) and upper +($u_j$) bounds of some column (structural variable $x_j$) are set in the +following way, where $s_j$ is a corresponding bound value explicitly +specified in the BOUNDS section: + +\begin{tabular}{@{}ll} +\verb|LO| & sets $l_j$ to $s_j$; \\ +\verb|UP| & sets $u_j$ to $s_j$; \\ +\verb|FX| & sets both $l_j$ and $u_j$ to $s_j$; \\ +\verb|FR| & sets $l_j$ to $-\infty$ and $u_j$ to $+\infty$; \\ +\verb|MI| & sets $l_j$ to $-\infty$; \\ +\verb|PL| & sets $u_j$ to $+\infty$. \\ +\end{tabular} + +\section{ENDATA indicator card} + +The ENDATA indicator card should be the last card of MPS file (except +optional comment cards, which may follow the ENDATA card). This card +should contain the word \verb|ENDATA| in the columns 1---6. + +\section{Specifying objective function} + +It is impossible to explicitly specify the objective function and +optimization direction in the MPS file. However, the following implicit +rule is used by default: the first row of \verb|N| type is considered +as a row of the objective function (i.e. the objective function is the +corresponding auxiliary variable), which should be {\it minimized}. + +GLPK also allows specifying a constant term of the objective function +as a right-hand side of the corresponding row in the RHS section. + +\section{Example of MPS file} +\label{secmpsex} + +In order to illustrate what the MPS format is, consider the following +example of LP problem: + +\medskip +\noindent minimize +$$ +value = .03\ bin_1 + .08\ bin_2 + .17\ bin_3 + .12\ bin_4 + .15\ bin_5 ++ .21\ al + .38\ si +$$ + +\noindent subject to linear constraints +$$ +\begin{array}{@{}l@{\:}l@{}} +yield &= \ \ \ \ \;bin_1 + \ \ \ \ \;bin_2 + \ \ \ \ \;bin_3 + + \ \ \ \ \;bin_4 + \ \ \ \ \;bin_5 + \ \ \ \ \;al + + \ \ \ \ \;si \\ +FE &= .15\ bin_1 + .04\ bin_2 + .02\ bin_3 + .04\ bin_4 + .02\ bin_5 + + .01\ al + .03\ si \\ +CU &= .03\ bin_1 + .05\ bin_2 + .08\ bin_3 + .02\ bin_4 + .06\ bin_5 + + .01\ al \\ +MN &= .02\ bin_1 + .04\ bin_2 + .01\ bin_3 + .02\ bin_4 + .02\ bin_5 + \\ +MG &= .02\ bin_1 + .03\ bin_2 +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ + .01\ bin_5 \\ +AL &= .70\ bin_1 + .75\ bin_2 + .80\ bin_3 + .75\ bin_4 + .80\ bin_5 + + .97\ al \\ +SI &= .02\ bin_1 + .06\ bin_2 + .08\ bin_3 + .12\ bin_4 + .02\ bin_5 + + .01\ al + .97\ si \\ +\end{array} +$$ +and bounds of (auxiliary and structural) variables +$$ +\begin{array}{r@{\ }l@{\ }l@{\ }l@{\ }rcr@{\ }l@{\ }l@{\ }l@{\ }r} +&&yield&=&2000&&0&\leq&bin_1&\leq&200\\ +-\infty&<&FE&\leq&60&&0&\leq&bin_2&\leq&2500\\ +-\infty&<&CU&\leq&100&&400&\leq&bin_3&\leq&800\\ +-\infty&<&MN&\leq&40&&100&\leq&bin_4&\leq&700\\ +-\infty&<&MG&\leq&30&&0&\leq&bin_5&\leq&1500\\ +1500&\leq&AL&<&+\infty&&0&\leq&al&<&+\infty\\ +250&\leq&SI&\leq&300&&0&\leq&si&<&+\infty\\ +\end{array} +$$ + +A complete MPS file which specifies data for this example is shown +below (the first two comment lines show card positions). + +\begin{verbatim} +*000000001111111111222222222233333333334444444444555555555566 +*234567890123456789012345678901234567890123456789012345678901 +NAME PLAN +ROWS + N VALUE + E YIELD + L FE + L CU + L MN + L MG + G AL + L SI +COLUMNS + BIN1 VALUE .03000 YIELD 1.00000 + FE .15000 CU .03000 + MN .02000 MG .02000 + AL .70000 SI .02000 + BIN2 VALUE .08000 YIELD 1.00000 + FE .04000 CU .05000 + MN .04000 MG .03000 + AL .75000 SI .06000 + BIN3 VALUE .17000 YIELD 1.00000 + FE .02000 CU .08000 + MN .01000 AL .80000 + SI .08000 + BIN4 VALUE .12000 YIELD 1.00000 + FE .04000 CU .02000 + MN .02000 AL .75000 + SI .12000 + BIN5 VALUE .15000 YIELD 1.00000 + FE .02000 CU .06000 + MN .02000 MG .01000 + AL .80000 SI .02000 + ALUM VALUE .21000 YIELD 1.00000 + FE .01000 CU .01000 + AL .97000 SI .01000 + SILICON VALUE .38000 YIELD 1.00000 + FE .03000 SI .97000 +RHS + RHS1 YIELD 2000.00000 FE 60.00000 + CU 100.00000 MN 40.00000 + SI 300.00000 + MG 30.00000 AL 1500.00000 +RANGES + RNG1 SI 50.00000 +BOUNDS + UP BND1 BIN1 200.00000 + UP BIN2 2500.00000 + LO BIN3 400.00000 + UP BIN3 800.00000 + LO BIN4 100.00000 + UP BIN4 700.00000 + UP BIN5 1500.00000 +ENDATA +\end{verbatim} + +\section{MIP features} + +The MPS format provides two ways for introducing integer variables into +the problem. + +The first way is most general and based on using special marker cards +INTORG and INTEND. These marker cards are placed in the COLUMNS section. +The INTORG card indicates the start of a group of integer variables +(columns), and the card INTEND indicates the end of the group. The MPS +file may contain arbitrary number of the marker cards. + +The marker cards have the same format as the data cards (see Section +\ref{secmps}, page \pageref{secmps}). + +The fields 1, 2, and 6 are not used and should be empty. + +The field 2 should contain a marker name. This name may be arbitrary. + +The field 3 should contain the word \verb|'MARKER'| (including +apostrophes). + +The field 5 should contain either the word \verb|'INTORG'| (including +apostrophes) for the marker card, which begins a group of integer +columns, or the word \verb|'INTEND'| (including apostrophes) for the +marker card, which ends the group. + +The second way is less general but more convenient in some cases. It +allows the user declaring integer columns using three additional types +of bounds, which are specified in the field 1 of data cards in the +BOUNDS section (see Section \ref{secbounds}, page \pageref{secbounds}): + +\begin{tabular}{@{}lp{112.3mm}@{}} +\verb|LI| & lower integer. This bound type specifies that the +corresponding column (structural variable), whose name is specified in +field 3, is of integer kind. In this case an lower bound of the +column should be specified in field 4 (like in the case of \verb|LO| +bound type). \\ +\verb|UI| & upper integer. This bound type specifies that the +corresponding column (structural variable), whose name is specified in +field 3, is of integer kind. In this case an upper bound of the +column should be specified in field 4 (like in the case of \verb|UP| +bound type). \\ +\end{tabular} + +\pagebreak + +\begin{tabular}{@{}lp{112.3mm}@{}} +\verb|BV| & binary variable. This bound type specifies that the +corresponding column (structural variable), whose name is specified in +the field 3, is of integer kind, its lower bound is zero, and its upper +bound is one (thus, such variable being of integer kind can have only +two values zero and one). In this case a numeric value specified in the +field 4 is ignored and may be omitted.\\ +\end{tabular} + +Consider the following example of MIP problem: + +\medskip + +\noindent +\hspace{1in} minimize +$$Z = 3 x_1 + 7 x_2 - x_3 + x4$$ +\hspace{1in} subject to linear constraints +$$ +\begin{array}{c} +\nonumber r_1 = 2 x_1 - \ \ x_2 + \ \ x_3 - \ \;x_4 \\ +\nonumber r_2 = \ \;x_1 - \ \;x_2 - 6 x_3 + 4 x_4 \\ +\nonumber r_3 = 5 x_1 + 3 x_2 \ \ \ \ \ \ \ \ \ + \ \ x_4 \\ +\end{array} +$$ +\hspace{1in} and bound of variables +$$ +\begin{array}{cccl} +\nonumber 1 \leq r_1 < +\infty && 0 \leq x_1 \leq 4 &{\rm(continuous)}\\ +\nonumber 8 \leq r_2 < +\infty && 2 \leq x_2 \leq 5 &{\rm(integer)} \\ +\nonumber 5 \leq r_3 < +\infty && 0 \leq x_3 \leq 1 &{\rm(integer)} \\ +\nonumber && 3 \leq x_4 \leq 8 &{\rm(continuous)}\\ +\end{array} +$$ + +The corresponding MPS file may look like the following: + +\begin{verbatim} +NAME SAMP1 +ROWS + N Z + G R1 + G R2 + G R3 +COLUMNS + X1 R1 2.0 R2 1.0 + X1 R3 5.0 Z 3.0 + MARK0001 'MARKER' 'INTORG' + X2 R1 -1.0 R2 -1.0 + X2 R3 3.0 Z 7.0 + X3 R1 1.0 R2 -6.0 + X3 Z -1.0 + MARK0002 'MARKER' 'INTEND' + X4 R1 -1.0 R2 4.0 + X4 R3 1.0 Z 1.0 +RHS + RHS1 R1 1.0 + RHS1 R2 8.0 + RHS1 R3 5.0 +BOUNDS + UP BND1 X1 4.0 + LO BND1 X2 2.0 + UP BND1 X2 5.0 + UP BND1 X3 1.0 + LO BND1 X4 3.0 + UP BND1 X4 8.0 +ENDATA +\end{verbatim} + +The same example may be coded without INTORG/INTEND markers using the +bound type UI for the variable $x_2$ and the bound type BV for the +variable $x_3$: + +\begin{verbatim} +NAME SAMP2 +ROWS + N Z + G R1 + G R2 + G R3 +COLUMNS + X1 R1 2.0 R2 1.0 + X1 R3 5.0 Z 3.0 + X2 R1 -1.0 R2 -1.0 + X2 R3 3.0 Z 7.0 + X3 R1 1.0 R2 -6.0 + X3 Z -1.0 + X4 R1 -1.0 R2 4.0 + X4 R3 1.0 Z 1.0 +RHS + RHS1 R1 1.0 + RHS1 R2 8.0 + RHS1 R3 5.0 +BOUNDS + UP BND1 X1 4.0 + LO BND1 X2 2.0 + UI BND1 X2 5.0 + BV BND1 X3 + LO BND1 X4 3.0 + UP BND1 X4 8.0 +ENDATA +\end{verbatim} + +%\section{Specifying predefined basis} +%\label{secbas} +% +%The MPS format can also be used to specify some predefined basis for an +%LP problem, i.e. to specify which rows and columns are basic and which +%are non-basic. +% +%The order of a basis file in the MPS format is: +% +%$\bullet$ NAME indicator card; +% +%$\bullet$ data cards (can appear in arbitrary order); +% +%$\bullet$ ENDATA indicator card. +% +%Each data card specifies either a pair "basic column---non-basic row" +%or a non-basic column. All the data cards have the following format. +% +%`\verb|XL|' in the field 1 means that a column, whose name is given in +%the field 2, is basic, and a row, whose name is given in the field 3, +%is non-basic and placed on its lower bound. +% +%`\verb|XU|' in the field 1 means that a column, whose name is given in +%the field 2, is basic, and a row, whose name is given in the field 3, +%is non-basic and placed on its upper bound. +% +%`\verb|LL|' in the field 1 means that a column, whose name is given in +%the field 3, is non-basic and placed on its lower bound. +% +%`\verb|UL|' in the field 1 means that a column, whose name is given in +%the field 3, is non-basic and placed on its upper bound. +% +%The field 2 contains a column name. +% +%If the indicator given in the field 1 is `\verb|XL|' or `\verb|XU|', +%the field 3 contains a row name. Otherwise, if the indicator is +%`\verb|LL|' or `\verb|UL|', the field 3 is not used and should be +%empty. +% +%The field 4, 5, and 6 are not used and should be empty. +% +%A basis file in the MPS format acts like a patch: it doesn't specify +%a basis completely, instead that it is just shows in what a given basis +%differs from the "standard" basis, where all rows (auxiliary variables) +%are assumed to be basic and all columns (structural variables) are +%assumed to be non-basic. +% +%As an example here is a basis file that specifies an optimal basis +%for the example LP problem given in Section \ref{secmpsex}, +%Page \pageref{secmpsex}: +% +%\pagebreak +% +%\begin{verbatim} +%*000000001111111111222222222233333333334444444444555555555566 +%*234567890123456789012345678901234567890123456789012345678901 +%NAME PLAN +% XL BIN2 YIELD +% XL BIN3 FE +% XL BIN4 MN +% XL ALUM AL +% XL SILICON SI +% LL BIN1 +% LL BIN5 +%ENDATA +%\end{verbatim} + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk09.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk09.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,413 @@ +%* glpk09.tex *% + +\chapter{CPLEX LP Format} +\label{chacplex} + +\section{Prelude} + +The CPLEX LP format\footnote{The CPLEX LP format was developed in +the end of 1980's by CPLEX Optimization, Inc. as an input format for +the CPLEX linear programming system. Although the CPLEX LP format is +not as widely used as the MPS format, being row-oriented it is more +convenient for coding mathematical programming models by human. This +appendix describes only the features of the CPLEX LP format which are +implemented in the GLPK package.} is intended for coding LP/MIP problem +data. It is a row-oriented format that assumes the formulation of LP/MIP +problem (1.1)---(1.3) (see Section \ref{seclp}, page \pageref{seclp}). + +CPLEX LP file is a plain text file written in CPLEX LP format. Each text +line of this file may contain up to 255 characters\footnote{GLPK allows +text lines of arbitrary length.}. Blank lines are ignored. If a line +contains the backslash character ($\backslash$), this character and +everything that follows it until the end of line are considered as a +comment and also ignored. + +An LP file is coded by the user using the following elements: + +$\bullet$ keywords; + +$\bullet$ symbolic names; + +$\bullet$ numeric constants; + +$\bullet$ delimiters; + +$\bullet$ blanks. + +\newpage + +{\it Keywords} which may be used in the LP file are the following: + +\begin{verbatim} + minimize minimum min + maximize maximum max + subject to such that s.t. st. st + bounds bound + general generals gen + integer integers int + binary binaries bin + infinity inf + free + end +\end{verbatim} + +\noindent +All the keywords are case insensitive. Keywords given above on the same +line are equivalent. Any keyword (except \verb|infinity|, \verb|inf|, +and \verb|free|) being used in the LP file must start at the beginning +of a text line. + +{\it Symbolic names} are used to identify the objective function, +constraints (rows), and variables (columns). All symbolic names are case +sensitive and may contain up to 16 alphanumeric characters\footnote{GLPK +allows symbolic names having up to 255 characters.} (\verb|a|, \dots, +\verb|z|, \verb|A|, \dots, \verb|Z|, \verb|0|, \dots, \verb|9|) as well +as the following characters: + +\begin{verbatim} +! " # $ % & ( ) / , . ; ? @ _ ` ' { } | ~ +\end{verbatim} + +\noindent +with exception that no symbolic name can begin with a digit or a period. + +{\it Numeric constants} are used to denote constraint and objective +coefficients, right-hand sides of constraints, and bounds of variables. +They are coded in the standard form $xx$\verb|E|$syy$, where $xx$ is +a real number with optional decimal point, $s$ is a sign (\verb|+| or +\verb|-|), $yy$ is an integer decimal exponent. Numeric constants may +contain arbitrary number of characters. The exponent part is optional. +The letter `\verb|E|' can be coded as `\verb|e|'. If the sign $s$ is +omitted, plus is assumed. + +{\it Delimiters} that may be used in the LP file are the following: + +\begin{verbatim} + : + + + - + < <= =< + > >= => + = +\end{verbatim} + +\noindent +Delimiters given above on the same line are equivalent. The meaning of +the delimiters will be explained below. + +{\it Blanks} are non-significant characters. They may be used freely to +improve readability of the LP file. Besides, blanks should be used to +separate elements from each other if there is no other way to do that +(for example, to separate a keyword from a following symbolic name). + +The order of an LP file is: + +$\bullet$ objective function definition; + +$\bullet$ constraints section; + +$\bullet$ bounds section; + +$\bullet$ general, integer, and binary sections (can appear in arbitrary +order); + +$\bullet$ end keyword. + +These components are discussed in following sections. + +\section{Objective function definition} + +The objective function definition must appear first in the LP file. It +defines the objective function and specifies the optimization direction. + +The objective function definition has the following form: +$$ +\left\{ +\begin{array}{@{}c@{}} +{\tt minimize} \\ {\tt maximize} +\end{array} +\right\}\ f\ {\tt :}\ s\ c\ x\ s\ c\ x\ \dots\ s\ c\ x +$$ +where $f$ is a symbolic name of the objective function, $s$ is a sign +\verb|+| or \verb|-|, $c$ is a numeric constant that denotes an +objective coefficient, $x$ is a symbolic name of a variable. + +If necessary, the objective function definition can be continued on as +many text lines as desired. + +The name of the objective function is optional and may be omitted +(together with the semicolon that follows it). In this case the default +name `\verb|obj|' is assigned to the objective function. + +If the very first sign $s$ is omitted, the sign plus is assumed. Other +signs cannot be omitted. + +If some objective coefficient $c$ is omitted, 1 is assumed. + +Symbolic names $x$ used to denote variables are recognized by context +and therefore needn't to be declared somewhere else. + +Here is an example of the objective function definition: + +\begin{verbatim} + Minimize Z : - x1 + 2 x2 - 3.5 x3 + 4.997e3x(4) + x5 + x6 + + x7 - .01x8 +\end{verbatim} + +\section{Constraints section} + +The constraints section must follow the objective function definition. +It defines a system of equality and/or inequality constraints. + +The constraint section has the following form: + +\begin{center} +\begin{tabular}{l} +\verb|subject to| \\ +{\it constraint}$_1$ \\ +{\it constraint}$_2$ \\ +\hspace{20pt}\dots \\ +{\it constraint}$_m$ \\ +\end{tabular} +\end{center} + +\noindent where {\it constraint}$_i, i=1,\dots,m,$ is a particular +constraint definition. + +Each constraint definition can be continued on as many text lines as +desired. However, each constraint definition must begin on a new line +except the very first constraint definition which can begin on the same +line as the keyword `\verb|subject to|'. + +Constraint definitions have the following form: +$$ +r\ {\tt:}\ s\ c\ x\ s\ c\ x\ \dots\ s\ c\ x +\ \left\{ +\begin{array}{@{}c@{}} +\mbox{\tt<=} \\ \mbox{\tt>=} \\ \mbox{\tt=} +\end{array} +\right\}\ b +$$ +where $r$ is a symbolic name of a constraint, $s$ is a sign \verb|+| or +\verb|-|, $c$ is a numeric constant that denotes a constraint +coefficient, $x$ is a symbolic name of a variable, $b$ is a right-hand +side. + +The name $r$ of a constraint (which is the name of the corresponding +auxiliary variable) is optional and may be omitted (together with the +semicolon that follows it). In this case the default names like +`\verb|r.nnn|' are assigned to unnamed constraints. + +The linear form $s\ c\ x\ s\ c\ x\ \dots\ s\ c\ x$ in the left-hand +side of a constraint definition has exactly the same meaning as in the +case of the objective function definition (see above). + +After the linear form one of the following delimiters that indicate +the constraint sense must be specified: + +\verb|<=| \ means `less than or equal to' + +\verb|>=| \ means `greater than or equal to' + +\verb|= | \ means `equal to' + +The right hand side $b$ is a numeric constant with an optional sign. + +Here is an example of the constraints section: + +\begin{verbatim} + Subject To + one: y1 + 3 a1 - a2 - b >= 1.5 + y2 + 2 a3 + 2 + a4 - b >= -1.5 + two : y4 + 3 a1 + 4 a5 - b <= +1 + .20y5 + 5 a2 - b = 0 + 1.7 y6 - a6 + 5 a777 - b >= 1 +\end{verbatim} + +(Should note that it is impossible to express ranged constraints in the +CPLEX LP format. Each a ranged constraint can be coded as two +constraints with identical linear forms in the left-hand side, one of +which specifies a lower bound and other does an upper one of the +original ranged constraint.) + +\section{Bounds section} + +The bounds section is intended to define bounds of variables. This +section is optional; if it is specified, it must follow the constraints +section. If the bound section is omitted, all variables are assumed to +be non-negative (i.e. that they have zero lower bound and no upper +bound). + +The bounds section has the following form: + +\begin{center} +\begin{tabular}{l} +\verb|bounds| \\ +{\it definition}$_1$ \\ +{\it definition}$_2$ \\ +\hspace{20pt}\dots \\ +{\it definition}$_p$ \\ +\end{tabular} +\end{center} + +\noindent +where {\it definition}$_k, k=1,\dots,p,$ is a particular bound +definition. + +Each bound definition must begin on a new line\footnote{The GLPK +implementation allows several bound definitions to be placed on the same +line.} except the very first bound definition which can begin on the +same line as the keyword `\verb|bounds|'. + +Syntactically constraint definitions can have one of the following six +forms: + +\begin{center} +\begin{tabular}{ll} +$x$ \verb|>=| $l$ & specifies a lower bound \\ +$l$ \verb|<=| $x$ & specifies a lower bound \\ +$x$ \verb|<=| $u$ & specifies an upper bound \\ +$l$ \verb|<=| $x$ \verb|<=| $u$ &specifies both lower and upper bounds\\ +$x$ \verb|=| $t$ &specifies a fixed value \\ +$x$ \verb|free| &specifies free variable +\end{tabular} +\end{center} + +\noindent +where $x$ is a symbolic name of a variable, $l$ is a numeric constant +with an optional sign that defines a lower bound of the variable or +\verb|-inf| that means that the variable has no lower bound, $u$ is a +numeric constant with an optional sign that defines an upper bound of +the variable or \verb|+inf| that means that the variable has no upper +bound, $t$ is a numeric constant with an optional sign that defines a +fixed value of the variable. + +By default all variables are non-negative, i.e. have zero lower bound +and no upper bound. Therefore definitions of these default bounds can be +omitted in the bounds section. + +Here is an example of the bounds section: + +\begin{verbatim} + Bounds + -inf <= a1 <= 100 + -100 <= a2 + b <= 100 + x2 = +123.456 + x3 free +\end{verbatim} + +\section{General, integer, and binary sections} + +The general, integer, and binary sections are intended to define +some variables as integer or binary. All these sections are optional +and needed only in case of MIP problems. If they are specified, they +must follow the bounds section or, if the latter is omitted, the +constraints section. + +All the general, integer, and binary sections have the same form as +follows: + +\begin{center} +\begin{tabular}{l} +$ +\left\{ +\begin{array}{@{}l@{}} +\verb|general| \\ +\verb|integer| \\ +\verb|binary | \\ +\end{array} +\right\} +$ \\ +\hspace{10pt}$x_1$ \\ +\hspace{10pt}$x_2$ \\ +\hspace{10pt}\dots \\ +\hspace{10pt}$x_q$ \\ +\end{tabular} +\end{center} + +\noindent +where $x_k$ is a symbolic name of variable, $k=1,\dots,q$. + +Each symbolic name must begin on a new line\footnote{The GLPK +implementation allows several symbolic names to be placed on the same +line.} except the very first symbolic name which can begin on the same +line as the keyword `\verb|general|', `\verb|integer|', or +`\verb|binary|'. + +If a variable appears in the general or the integer section, it is +assumed to be general integer variable. If a variable appears in the +binary section, it is assumed to be binary variable, i.e. an integer +variable whose lower bound is zero and upper bound is one. (Note that +if bounds of a variable are specified in the bounds section and then +the variable appears in the binary section, its previously specified +bounds are ignored.) + +Here is an example of the integer section: + +\begin{verbatim} + Integer + z12 + z22 + z35 +\end{verbatim} + +\section{End keyword} + +The keyword `\verb|end|' is intended to end the LP file. It must begin +on a separate line and no other elements (except comments and blank +lines) must follow it. Although this keyword is optional, it is strongly +recommended to include it in the LP file. + +\section{Example of CPLEX LP file} + +Here is a complete example of CPLEX LP file that corresponds to the +example given in Section \ref{secmpsex}, page \pageref{secmpsex}. + +{\footnotesize +\begin{verbatim} +\* plan.lp *\ + +Minimize + value: .03 bin1 + .08 bin2 + .17 bin3 + .12 bin4 + .15 bin5 + + .21 alum + .38 silicon + +Subject To + yield: bin1 + bin2 + bin3 + bin4 + bin5 + + alum + silicon = 2000 + + fe: .15 bin1 + .04 bin2 + .02 bin3 + .04 bin4 + .02 bin5 + + .01 alum + .03 silicon <= 60 + + cu: .03 bin1 + .05 bin2 + .08 bin3 + .02 bin4 + .06 bin5 + + .01 alum <= 100 + + mn: .02 bin1 + .04 bin2 + .01 bin3 + .02 bin4 + .02 bin5 <= 40 + + mg: .02 bin1 + .03 bin2 + .01 bin5 <= 30 + + al: .70 bin1 + .75 bin2 + .80 bin3 + .75 bin4 + .80 bin5 + + .97 alum >= 1500 + + si1: .02 bin1 + .06 bin2 + .08 bin3 + .12 bin4 + .02 bin5 + + .01 alum + .97 silicon >= 250 + + si2: .02 bin1 + .06 bin2 + .08 bin3 + .12 bin4 + .02 bin5 + + .01 alum + .97 silicon <= 300 + +Bounds + bin1 <= 200 + bin2 <= 2500 + 400 <= bin3 <= 800 + 100 <= bin4 <= 700 + bin5 <= 1500 + +End + +\* eof *\ +\end{verbatim} + +} + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk10.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk10.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,210 @@ +%* glpk10.tex *% + +\chapter{Stand-alone LP/MIP Solver} +\label{chaglpsol} + +The GLPK package includes the program \verb|glpsol|, which is a +stand-alone LP/MIP solver. This program can be invoked from the command +line of from the shell to read LP/MIP problem data in any format +supported by GLPK, solve the problem, and write the problem solution +obtained to an output text file. + +\subsubsection*{Usage} + +\noindent +\verb|glpsol| [{\it options\dots}] [{\it filename}] + +\subsubsection*{General options} + +\noindent +\begin{tabular}{@{}p{30mm}p{92.3mm}@{}} +\verb|--mps| & read LP/MIP problem in fixed MPS format \\ +\verb|--freemps| & read LP/MIP problem in free MPS format (default)\\ +\verb|--lp| & read LP/MIP problem in CPLEX LP format \\ +\verb|--glp| & read LP/MIP problem in GLPK format \\ +\verb|--math| & read LP/MIP model written in GNU MathProg modeling + language \\ +\multicolumn{2}{@{}l}{{\tt -m} {\it filename}, {\tt --model} +{\it filename}} \\ + & read model section and optional data section from + {\it filename} (the same as \verb|--math|) \\ +\multicolumn{2}{@{}l}{{\tt -d} {\it filename}, {\tt --data} +{\it filename}} \\ + & read data section from {\it filename} + (for \verb|--math| only); if model file also has + data section, that section is ignored \\ +\multicolumn{2}{@{}l}{{\tt -y} {\it filename}, {\tt --display} +{\it filename}} \\ + & send display output to {\it filename} + (for \verb|--math| only); by default the output is + sent to \verb|stdout| \\ +\end{tabular} + +\noindent +\begin{tabular}{@{}p{30mm}p{92.3mm}@{}} +\verb|--seed| {\it value} + & initialize pseudo-random number generator used in + MathProg model with specified seed (any integer); + if the seed value is specified as \verb|?| + (question mark), some random seed will be used\\ +\verb|--mincost| & read min-cost flow problem in DIMACS format\\ +\verb|--maxflow| & read maximum flow problem in DIMACS format\\ +\verb|--simplex| & use simplex method (default) \\ +\verb|--interior| & use interior point method (for pure LP only) \\ +\multicolumn{2}{@{}l}{{\tt -r} {\it filename}, {\tt --read} +{\it filename}} \\ + & read solution from {\it filename} rather to find + it with the solver \\ +\verb|--min| & minimization \\ +\verb|--max| & maximization \\ +\verb|--scale| & scale problem (default) \\ +\verb|--noscale| & do not scale problem \\ +\multicolumn{2}{@{}l}{{\tt -o} {\it filename}, {\tt --output} +{\it filename}} \\ + & write solution to {\it filename} in printable + format \\ +\multicolumn{2}{@{}l}{{\tt -w} {\it filename}, {\tt --write} +{\it filename}} \\ + & write solution to {\it filename} in plain text + format \\ +\multicolumn{2}{@{}l}{{\tt --ranges} {\it filename}} \\ + & write sensitivity analysis report to {\it filename} + in printable format (simplex only) \\ +\verb|--tmlim| {\it nnn} + & limit solution time to {\it nnn} seconds + (\verb|--tmlim 0| allows obtaining solution at + initial point) \\ +\verb|--memlim| {\it nnn} + & limit available memory to {\it nnn} megabytes \\ +\verb|--check| & do not solve problem, check input data only \\ +\verb|--name| {\it probname} + & change problem name to {\it probname} \\ +\verb|--wmps| {\it filename} + & write problem to {\it filename} in fixed MPS + format \\ +\multicolumn{2}{@{}l}{{\tt --wfreemps} {\it filename}} \\ + & write problem to {\it filename} in free MPS + format \\ +\verb|--wlp| {\it filename} + & write problem to {\it filename} in CPLEX LP + format \\ +\verb|--wglp| {\it filename} + & write problem to {\it filename} in GLPK format \\ +\verb|--log| {\it filename} + & write copy of terminal output to {\it filename} \\ +\verb|-h|, \verb|--help| + & display this help information and exit \\ +\verb|-v|, \verb|--version| + & display program version and exit \\ +\end{tabular} + +\subsection*{LP basis factorization options} + +\noindent +\begin{tabular}{@{}p{30mm}p{92.3mm}@{}} +\verb|--luf| & LU + Forrest--Tomlin update \\ + & (faster, less stable; default) \\ +\verb|--cbg| & LU + Schur complement + Bartels--Golub update \\ + & (slower, more stable) \\ +\verb|--cgr| & LU + Schur complement + Givens rotation update \\ + & (slower, more stable) \\ +\end{tabular} + +\subsubsection*{Options specific to the simplex solver} + +\noindent +\begin{tabular}{@{}p{30mm}p{92.3mm}@{}} +\verb|--primal| & use primal simplex (default) \\ +\verb|--dual| & use dual simplex \\ +\verb|--std| & use standard initial basis of all slacks \\ +\verb|--adv| & use advanced initial basis (default) \\ +\verb|--bib| & use Bixby's initial basis\\ +\verb|--ini| {\it filename} + & use as initial basis previously saved with + \verb|-w| \\ + & (disables LP presolver) \\ +\verb|--steep| & use steepest edge technique (default) \\ +\verb|--nosteep| & use standard ``textbook'' pricing \\ +\verb|--relax| & use Harris' two-pass ratio test (default) \\ +\verb|--norelax| & use standard ``textbook'' ratio test \\ +\verb|--presol| & use LP presolver (default; assumes \verb|--scale| + and \verb|--adv|) \\ +\verb|--nopresol| & do not use LP presolver \\ +\verb|--exact| & use simplex method based on exact arithmetic \\ +\verb|--xcheck| & check final basis using exact arithmetic \\ +\end{tabular} + +\subsubsection*{Options specific to the interior-point solver} + +\noindent +\begin{tabular}{@{}p{30mm}p{92.3mm}@{}} +\verb|--nord| & use natural (original) ordering \\ +\verb|--qmd| & use quotient minimum degree ordering \\ +\verb|--amd| & use approximate minimum degree ordering (default)\\ +\verb|--symamd| & use approximate minimum degree ordering \\ +\end{tabular} + +\subsubsection*{Options specific to the MIP solver} + +\noindent +\begin{tabular}{@{}p{30mm}p{92.3mm}@{}} +\verb|--nomip| & consider all integer variables as continuous + (allows solving MIP as pure LP) \\ +\verb|--first| & branch on first integer variable \\ +\verb|--last| & branch on last integer variable \\ +\verb|--mostf| & branch on most fractional variable \\ +\end{tabular} + +\noindent +\begin{tabular}{@{}p{30mm}p{92.3mm}@{}} +\verb|--drtom| & branch using heuristic by Driebeck and Tomlin + (default) \\ +\verb|--pcost| & branch using hybrid pseudocost heuristic (may be + useful for hard instances) \\ +\verb|--dfs| & backtrack using depth first search \\ +\verb|--bfs| & backtrack using breadth first search \\ +\verb|--bestp| & backtrack using the best projection heuristic + (default) \\ +\verb|--bestb| & backtrack using node with best local bound \\ +\verb|--intopt| & use MIP presolver (default)\\ +\verb|--nointopt| & do not use MIP presolver\\ +\verb|--binarize| & replace general integer variables by binary ones + (assumes \verb|--intopt|)\\ +\verb|--fpump| & apply feasibility pump heuristic\\ +\verb|--gomory| & generate Gomory's mixed integer cuts\\ +\verb|--mir| & generate MIR (mixed integer rounding) cuts\\ +\verb|--cover| & generate mixed cover cuts\\ +\verb|--clique| & generate clique cuts\\ +\verb|--cuts| & generate cuts of all classes above (assumes + \verb|--intopt|)\\ +\verb|--mipgap| {\it tol} + & set relative mip gap tolerance to {\it tol}\\ +\end{tabular} + +\bigskip + +\noindent +For description of the MPS format see Appendix \ref{champs}, +page \pageref{champs}. + +\bigskip + +\noindent +For description of the CPLEX LP format see Appendix \ref{chacplex}, +page \pageref{chacplex}. + +\bigskip + +\noindent +For description of the modeling language see the document ``Modeling +Language GNU MathProg: Language Reference'' included in the GLPK +distribution. + +\bigskip + +\noindent +For description of the DIMACS min-cost flow problem format and DIMACS +maximum flow problem format see the document ``GLPK: Graph and Network +Routines'' included in the GLPK distribution. + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk11.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk11.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,124 @@ +%* glpk11.tex *% + +\begin{footnotesize} + +\chapter{External Software Modules Used In GLPK} + +In the GLPK package there are used some external software modules +listed in this Appendix. Note that these modules are {\it not} part of +GLPK, but are used with GLPK and included in the distribution. + +\section{AMD} + +AMD Version 2.2, Copyright {\copyright} 2007 by Timothy A. Davis, +Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. + +\subsection*{Description} + +AMD is a set of routines for pre-ordering sparse matrices prior to +Cholesky or LU factorization, using the approximate minimum degree +ordering algorithm. + +\subsection*{License} + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 of +the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA. + +Permission is hereby granted to use or copy this program under the +terms of the GNU LGPL, provided that the Copyright, this License, +and the Availability of the original version is retained on all +copies. User documentation of any code that uses this code or any +modified version of this code must cite the Copyright, this License, +the Availability note, and ``Used by permission.'' Permission to +modify the code and to distribute modified code is granted, provided +the Copyright, this License, and the Availability note are retained, +and a notice that the code was modified is included. + +AMD is available under alternate licences; contact T. Davis for +details. + +\subsection*{Availability} + +\verb|http://www.cise.ufl.edu/research/sparse/amd| + +\bigskip + +\noindent +Used by permission. + +\section{COLAMD/SYMAMD} + +COLAMD/SYMAMD Version 2.7, Copyright {\copyright} 1998-2007, Timothy A. +Davis, All Rights Reserved. + +\subsection*{Description} + +\paragraph{colamd:} an approximate minimum degree column ordering +algorithm, for LU factorization of symmetric or unsymmetric matrices, +QR factorization, least squares, interior point methods for linear +programming problems, and other related problems. + +\paragraph{symamd:} an approximate minimum degree ordering algorithm +for Cholesky factorization of symmetric matrices. + +\subsection*{Authors} + +The authors of the code itself are Stefan I. Larimore and Timothy A. +Davis (davis at cise.ufl.edu), University of Florida. The algorithm +was developed in collaboration with John Gilbert, Xerox PARC, and +Esmond Ng, Oak Ridge National Laboratory. + +\subsection*{License} + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 of +the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA. + +Permission is hereby granted to use or copy this program under the +terms of the GNU LGPL, provided that the Copyright, this License, +and the Availability of the original version is retained on all +copies. User documentation of any code that uses this code or any +modified version of this code must cite the Copyright, this License, +the Availability note, and ``Used by permission.'' Permission to +modify the code and to distribute modified code is granted, provided +the Copyright, this License, and the Availability note are retained, +and a notice that the code was modified is included. + +COLAMD is also available under alternate licenses, contact T. Davis for +details. + +\subsection*{Availability} + +\verb|http://www.cise.ufl.edu/research/sparse/colamd| + +\bigskip + +\noindent +Used by permission. + +\end{footnotesize} + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk12.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk12.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,726 @@ +%* glpk12.tex *% + +\begin{footnotesize} + +\chapter*{\sf\bfseries GNU General Public License} +\addcontentsline{toc}{chapter}{GNU General Public License} + +\begin{center} +{\bf Version 3, 29 June 2007} +\end{center} + +\begin{quotation} +\noindent +Copyright {\copyright} 2007 Free Software Foundation, Inc. +\verb|| +\end{quotation} + +\begin{quotation} +\noindent +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +\end{quotation} + +\section*{Preamble} + +\noindent\indent + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + +\section*{TERMS AND CONDITIONS} + +\subsubsection*{0. Definitions.} + +\noindent\indent + ``This License'' refers to version 3 of the GNU General Public +License. + + ``Copyright'' also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + + ``The Program'' refers to any copyrightable work licensed under this +License. Each licensee is addressed as ``you''. ``Licensees'' and +``recipients'' may be individuals or organizations. + + To ``modify'' a work means to copy from or adapt all or part of the +work in a fashion requiring copyright permission, other than the making +of an exact copy. The resulting work is called a ``modified version'' +of the earlier work or a work ``based on'' the earlier work. + + A ``covered work'' means either the unmodified Program or a work based +on the Program. + + To ``propagate'' a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To ``convey'' a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays ``Appropriate Legal Notices'' +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +\subsubsection*{1. Source Code.} + +\noindent\indent + The ``source code'' for a work means the preferred form of the work +for making modifications to it. ``Object code'' means any non-source +form of a work. + + A ``Standard Interface'' means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The ``System Libraries'' of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +``Major Component'', in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The ``Corresponding Source'' for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + +\subsubsection*{2. Basic Permissions.} + +\noindent\indent + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +\subsubsection*{3. Protecting Users' Legal Rights From +Anti-Circumvention Law.} + +\noindent\indent + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +\subsubsection*{4. Conveying Verbatim Copies.} + +\noindent\indent + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +\subsubsection*{5. Conveying Modified Source Versions.} + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + ``keep intact all notices''. + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +``aggregate'' if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +\subsubsection*{6. Conveying Non-Source Forms.} + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A ``User Product'' is either (1) a ``consumer product'', which means +any tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of coverage. +For a particular product received by a particular user, ``normally +used'' refers to a typical or common use of that class of product, +regardless of the status of the particular user or of the way in which +the particular user actually uses, or expects or is expected to use, the +product. A product is a consumer product regardless of whether the +product has substantial commercial, industrial or non-consumer uses, +unless such uses represent the only significant mode of use of the +product. + + ``Installation Information'' for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product +from a modified version of its Corresponding Source. The information +must suffice to ensure that the continued functioning of the modified +object code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to +a network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +\subsubsection*{7. Additional Terms.} + +\noindent\indent + ``Additional permissions'' are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered ``further +restrictions'' within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +\subsubsection*{8. Termination.} + +\noindent\indent + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +\subsubsection*{9. Acceptance Not Required for Having Copies.} + +\noindent\indent + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +\subsubsection*{10. Automatic Licensing of Downstream Recipients.} + +\noindent\indent + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An ``entity transaction'' is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +\subsubsection*{11. Patents.} + +\noindent\indent + A ``contributor'' is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's ``contributor version''. + + A contributor's ``essential patent claims'' are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, ``control'' includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a ``patent license'' is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To ``grant'' such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. ``Knowingly relying'' means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is ``discriminatory'' if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +\subsubsection*{12. No Surrender of Others' Freedom.} + +\noindent\indent + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not convey it at all. For example, if you agree to terms that +obligate you to collect a royalty for further conveying from those to +whom you convey the Program, the only way you could satisfy both those +terms and this License would be to refrain entirely from conveying the +Program. + +\subsubsection*{13. Use with the GNU Affero General Public License.} + +\noindent\indent + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +\subsubsection*{14. Revised Versions of this License.} + +\noindent\indent + The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License ``or any later version'' applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +\subsubsection*{15. Disclaimer of Warranty.} + +\noindent\indent + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM ``AS IS'' WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU +ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +\subsubsection*{16. Limitation of Liability.} + +\noindent\indent + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES +SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE +WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +\subsubsection*{17. Interpretation of Sections 15 and 16.} + +\noindent\indent + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +\section*{END OF TERMS AND CONDITIONS} + +\newpage + +\section*{How to Apply These Terms to Your New Programs} + +\noindent\indent + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +\begin{verbatim} + +Copyright (C) + +This program 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. + +This program 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 this program. If not, see . +\end{verbatim} + +\noindent +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + +\begin{verbatim} + Copyright (C) +This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +\end{verbatim} + +\noindent +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an ``about box''. + + You should also get your employer (if you work as a programmer) or +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. For more information on this, and how to apply and follow the +GNU GPL, see \verb||. + + The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read \verb||. + +\end{footnotesize} + +%* eof *% diff -r d59bea55db9b -r c445c931472f doc/glpk_faq.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/glpk_faq.txt Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,435 @@ + +GNU Linear Programming Kit FAQ + + Summary: Frequently Asked Questions about the GNU Linear Programming Kit + + Author: Dr. Harley Mackenzie + + Posting-Frequency: Monthly + + Language: English + + $Date: 2004/01/09 05:57:57 $ + + $Revision: 1.6 $ + + + +Introduction + + Q. What is GPLK? + + A. GLPK stands for the GNU Linear Programming Kit. The GLPK package is + a set of routines written in ANSI C and organized in the form of a + callable library. This package is intended for solving large-scale + linear programming (LP), mixed integer linear programming (MIP), and + other related problems. + + The GLPK package includes the following main components: + + * implementation of the simplex method, + + * implementation of the primal-dual interior point method, + + * implementation of the branch-and-bound method, + + * application program interface (API), + + * GNU MathProg modeling language (a subset of AMPL), + + * GLPSOL, a stand-alone LP/MIP solver. + + + Q. Who develops and maintains GLPK? + + A. GLPK is currently developed and maintained by Andrew Makhorin, + Department for Applied Informatics, Moscow Aviation Institute, Moscow, + Russia. Andrew's email address is and his postal + address is 125871, Russia, Moscow, Volokolamskoye sh., 4, Moscow + Aviation Institute, Andrew O. Makhorin + + + Q. How is GLPK licensed? + + A. GLPK is currently licensed under the GNU General Public License + (GPL). 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 2, or (at your option) any + later version. + + GLPK is not licensed under the Lesser General Public License (LGPL) as + distinct from other free LP codes such as lp_solve. The most + significant implication is that code that is linked to the GLPK library + must be released under the GPL, whereas with the LGPL, code linked to + the library does not have to be released under the same license. + + + Q. Where is the GLPK home page? + + The GLPK home page is part of the GNU web site and can found at + . + + + Q. How do I download and install GLPK? + + A. The GLPK source distribution can be found in the subdirectory + /gnu/glpk/ on your favorite GNU mirror + and can be compiled directly from + the source. + + The GLPK package (like all other GNU software) is distributed in the + form of packed archive. This is one file named 'glpk-x.y.tar.gz', where + x is the major version number and y is the minor version number. + + In order to prepare the distribution for installation you should: + + 1. Copy the GLPK distribution file to some subdirectory. + + 2. Enter the command 'gzip -d glpk-x.y.tar.gz' in order to unpack the + distribution file. After unpacking the name of the distribution file + will be automatically changed to 'glpk-x.y.tar'. + + 3. Enter the command 'tar -x < glpk-x.y.tar' in order to unarchive the + distribution. After this operation the subdirectory 'glpk-x.y' which is + the GLPK distribution will have been automatically created. + + After you have unpacked and unarchived GLPK distribution you should + configure the package, and compiled the application. The result of + compilation is: + + * the file 'libglpk.a', which is a library archive containing object code + for all GLPK routines; and + + * the program 'glpsol', which is a stand-alone LP/MIP solver. + + Complete compilation and installation instructions are included in the + INSTALL file included with the distribution. + + The distribution includes make files for the Microsoft Visual C/C++ + version 6 and Borland C/C++ version 5 and by default compiles and links + a glpk*.lib library file, a glpk*.dll DLL file and an glpsol.exe + application file. A GNU Windows 4.1 binary, source and documentation + compiled using the mingw32 C/C++ compiler is also available from + . + + + Q. Is there a GLPK mailing list or newsgroup? + + A. GLPK has two mailing lists: and + . + + The main discussion list is , and is used to discuss + all aspects of GLPK, including its development and porting. There is + also a special list used for reporting bugs at . + + To subscribe to any GLPK mailing list, send an empty mail with a + Subject: header line of just "subscribe" to the relevant -request list. + For example, to subscribe yourself to the main list, you would send + mail to with no body and a Subject: header + line of just "subscribe". + + Another way to subscribe to the GLP mailing lists is to visit the web + pages and + . + + Currently there are no newsgroups dedicated to GLPK. + + + Q. Who maintains this FAQ and how do I contribute to this FAQ? + + A. The present maintainer of this FAQ is Dr. Harley Mackenzie, HARD + software, although the content of the FAQ is derived from many sources + such as GLPK documentation, GLPK email archives and original content. + + Harley's email address is and his postal address + is c/o HARD software, PO Box 8004, Newtown, Victoria 3220, Australia. + + All contributions to this FAQ, such as questions and (preferably) + answers should be sent to the email address. + This FAQ is copyright Harley Mackenzie 2003 and is released under the + same license, terms and conditions as GLPK, that is, GPL version 2 or + later. + + Contributions are not directly referenced in the body of the FAQ as + this would become unmanageable and messy, but rather as a list of + contributors to this FAQ. If you are the author of any information + included in this FAQ and you do not want your content to be included, + please contact the FAQ maintainer and your material will be removed. + Also if you have not been correctly included as a contributor to this + FAQ, your details have changed, or you do not want your name listed in + the list of contributors, please contact the FAQ maintainer for + correction. + + + Q. Where can I download this FAQ? + + The latest version of the GLPK FAQ is available to download from + in the following + formats: + + * DocBook + + * Formatted text + + * Adobe PDF + + + Q. Who are the FAQ contributors? + + A. The FAQ contents were created from the following sources: + + * Michael Hennebry + + * http://www-unix.mcs.anl.gov/otc/Guide/faq/linear-programming-faq.html + + * Harley Mackenzie, HARD software Pty. Ltd. + + * Andrew Makhorin, Department for Applied Informatics, Moscow Aviation + Institute + + +GLPK functions & features + + Q. What is the current state of GLPK development? + + A. GLPK is a work in progress and is presently under continual + development. As of the current version 4.3, GLPK is a simplex-based + solver is able to handle problems with up to 100,000 constraints. In + particular, it successfully solves all instances from netlib (see the + file bench.txt included in the GLPK distribution). The interior-point + solver is not very robust as it is unable to handle dense columns, + sometimes terminates due to numeric instability or slow convergence. + + The Mixed Integer Programming (MIP) solver currently is based on + branch-and-bound, so it is unable to solve hard or very large problems + with a probable practical limit of 100-200 integer variables. However, + sometimes it is able to solve larger problems of up to 1000 integer + variables, although the size that depends on properties of particular + problem. + + + Q. How does GLPK compare with other LP codes? + + A. I think that on very large-scale instances CPLEX 8.0 dual simplex is + 10-100 times faster than the GLPK simplex solver and, of course, much + more robust. In many cases GLPK is faster and more robust than lp_solve + 4.0 for pure LPs as well as MIP's. See the bench.txt file in the GLPK + distribution doc directory for GLPK netlib benchmark results. + + You can find benchmarks for some LP and MIP solvers such as CPLEX, + GLPK, lp_solve, and OSL on Hans Mittelmann's webpage at + . + + + Q. What are the differences between AMPL and GNU MathProg? + + A. The subset of AMPL implemented in MathProg approximately corresponds + to AMPL status in 1990, because it is mainly based on the paper Robert + Fourer, David M Gay and Brian W Kernighan (1990), "A Modeling Language + for Mathematical Programming", Management Science, Vol 36, pp. 519-554 + and is available at + . + + The GNU MathProg translator was developed as part of GLPK. However, GNU + MathProg can be easily used in other applications as there is a set of + MathProg interface routines designed for use in other applications. + + + Q. What input file formats does GLPK support? + + A. GLPK presently can read input and output LP model files in three + supported formats: + + * MPS format - which is a column oriented and widely supported file + format but has poor human readability. + + * CPLEX format - which is an easily readable row oriented format. + + * GNU MathProg - which is an AMPL like mathematical modeling language. + + + Q. What interfaces are available for GLPK? + + A. The GLPK package is in fact a C API that can be either by statically + or dynamically linked directly with many programming systems. + + Presently there are three contributed external interfaces included with + the GLPK package: + + * GLPK Java Native Interface (JNI) + + * GLPK Delphi Interface (DELI) + + * GLPKMEX Matlab MEX interface + + There is an unofficial Microsoft Visual Basic, Tcl/Tk and Java GLPK + interface available at + . + + There are other language interfaces under development, including a Perl + interface currently being developed by the FAQ maintainer, Dr. Harley + Mackenzie . + + + Q. Where can I find some examples? + + A. The GLPK package distribution contains many examples written in GNU + MathProg (*.mod), C API calls (*.c), CPLEX input file format (*.lpt), + MPS format (*.mps) as well as some specific Traveling Salesman examples + (*.tsp). + + All of the examples can be found in the GLPK distribution examples + sub-directory. + + + Q. What are the future plans for GLPK? + + A. Developments planned for GLPK include improving the existing key + GLPK components, such as developing a more robust and more efficient + implementation of the simplex-based and interior-point solvers. Future + GLPK enhancements planned are implementing a branch-and-cut solver, a + MIP pre-processor, post-optimal and sensitivity analysis and possibly + network simplex and quadratic programming solvers. + + + Q. How do I report a GLPK bug? + + A. If you think you have found a bug in GLPK, then you should send as + complete a report as possible to . + + + Q. How do I contribute to the GLPK development? + + A. At present new GLPK development patches should be emailed to Andrew + Makhorin , with sufficient documentation and test + code to explain the nature of the patch, how to install it and the + implications of its use. Before commencing any major GLPK development + for inclusion in the GLPK distribution, it would be a good idea to + discuss the idea on the GLPK mailing list. + + + Q. How do I compile and link a GLPK application on a UNIX platform? + + A. To compile a GLPK application on a UNIX platform, then compiler must + be able to include the GLPK include files and link to the GLPK library. + For example, on a system where the GLPK system is installed: + + gcc mylp.c -o mylp -lglpk + + or specify the include files and libglpk.a explicitly, if GLPK is not + installed. + + + Q. How do I compile and link a GLPK application on a Win32 platform? + + A. On a Win32 platform, GLPK is implemented either as a Win32 Dynamic + Link Library (DLL) or can be statically linked to the glpk*.lib file. + As with the UNIX instructions, a GLPK application must set a path to + the GLPK include files and also reference the GLPK library if + statically linked. + + + Q. How do I limit the GLPK execution time? + + A. You can limit the computing time by setting the control parameter + LPX_K_TMLIM via the API routine lpx_set_real_parm . At present there is + no way of limiting the execution time of glpsol without changing the + source and recompiling a specific version. + + +GLPK Linear Programming + + Q. What is Linear Programming and how does it work? + + A. Linear Programming is a mathematical technique that is a generic + method for solving certain systems of equations with linear terms. The + real power of LP's are that they have many practical applications and + have proven to be a powerful and robust tool. + + The best single source of information on LP's is the Linear Programming + FAQ + + that has information on LP's and MIP's, includes a comprehensive list + of available LP software and has many LP references for further study. + + + Q. How do I determine the stability of an LP solution? + + A. You can perform sensitivity analysis by specifying the --bounds + option for glpsol as: + + glpsol ... --bounds filename + + in which case the solver writes results of the analysis to the + specified filename in plain text format. The corresponding API routine + is lpx_print_sens_bnds() . + + + Q. How do I determine which constraints are causing infeasibility? + + A straightforward way to find such a set of constraints is to drop + constraints one at a time. If dropping a constraint results in a + solvable problem, pick it up and go on to the next constraint. After + applying phase 1 to an infeasible problem, all basic satisfied + constraints may be dropped. + + If the problem has a feasible dual, then running the dual simplex + method is a more direct approach. After the last pivot, the nonbasic + constraints and one of the violated constraints will constitute a + minimal set. The GLPK simplex table routines will allow you to pick a + correct constraint from the violated ones. + + Note that the GLPK pre-solver needs to be turned off for the preceding + technique to work, otherwise GLPK does not keep the basis of an + infeasible solution. + + Also a more detailed methodology has been posted on the mail list + archive at + . + + + Q. What is the difference between checks and constraints? + + A. Check statements are intended to check that all data specified by + the user of the model are correct, mainly in the data section of a + MathProg model. For example, if some parameter means the number of + nodes in a network, it must be positive integer, that is just the + condition to be checked in the check statement (although in this case + such condition may be also checked directly in the parameter + statement). Note that check statements are performed when the + translator is generating the model, so they cannot include variables. + + Constraints are conditions that are expressed in terms of variables and + resolved by the solver after the model has been completely generated. + If all data specified in the model are correct a priori, check + statements are not needed and can be omitted, while constraints are + essential components of the model and therefore cannot be omitted. + + +GLPK Integer Programming + + Q. What is Integer Programming and how does it work? + + A. Integer LP models are ones whose variables are constrained to take + integer or whole number (as opposed to fractional) values. It may not + be obvious that integer programming is a very much harder problem than + ordinary linear programming, but that is nonetheless the case, in both + theory and practice. + + + Q. What is the Integer Optimization Suite (IOS)? + + A. IOS is a framework to implement implicit enumeration methods based + on LP relaxation (like branch-and-bound and branch-and-cut). Currently + IOS includes only basic features (the enumeration tree, API routines, + and the driver) and is not completely documented. + + + Q. I have just changed an LP to a MIP and now it doesn't work? + + A. If you have an existing LP that is working and you change to an MIP + and receive a "lpx_integer: optimal solution of LP relaxation required" + 204 (==LPX_E_FAULT) error, you probably have not called the LP solution + method lpx_simplex() before lpx_integer() . The MIP routines use the LP + solution as part of the MIP solution methodology. + diff -r d59bea55db9b -r c445c931472f doc/gmpl.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/gmpl.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,4653 @@ +%* gmpl.tex *% + +%*********************************************************************** +% This code is part of GLPK (GNU Linear Programming Kit). +% +% Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +% 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +% Moscow Aviation Institute, Moscow, Russia. All rights reserved. +% E-mail: . +% +% GLPK is free software: you can redistribute it and/or modify it +% under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% GLPK is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +% or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License +% along with GLPK. If not, see . +%*********************************************************************** + +\documentclass[10pt]{article} +\usepackage[dvipdfm,linktocpage,colorlinks,linkcolor=blue]{hyperref} + +\begin{document} + +\thispagestyle{empty} + +\begin{center} + +\vspace*{1in} + +\begin{huge} +\sf\bfseries Modeling Language GNU MathProg +\end{huge} + +\vspace{0.5in} + +\begin{LARGE} +\sf Language Reference +\end{LARGE} + +\vspace{0.5in} + +\begin{LARGE} +\sf for GLPK Version 4.45 +\end{LARGE} + +\vspace{0.5in} +\begin{Large} +\sf (DRAFT, December 2010) +\end{Large} + +\end{center} + +\newpage + +\vspace*{1in} + +\vfill + +\noindent +The GLPK package is part of the GNU Project released under the aegis of +GNU. + +\medskip\noindent +Copyright \copyright{} 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. + +\medskip\noindent +Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +MA 02110-1301, USA. + +\medskip\noindent +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +\medskip\noindent +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that +the entire resulting derived work is distributed under the terms of +a permission notice identical to this one. + +\medskip\noindent +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions. + +\newpage + +\tableofcontents + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Introduction} + +{\it GNU MathProg} is a modeling language intended for describing +linear mathematical programming models.\footnote{The GNU MathProg +language is a subset of the AMPL language. Its GLPK implementation is +mainly based on the paper: {\it Robert Fourer}, {\it David M. Gay}, and +{\it Brian W. Kernighan}, ``A Modeling Language for Mathematical +Programming.'' {\it Management Science} 36 (1990)\linebreak pp. 519-54.} + +Model descriptions written in the GNU MathProg language consist of +a set of statements and data blocks constructed by the user from the +language elements described in this document. + +In a process called {\it translation}, a program called the {\it model +translator} analyzes the model description and translates it into +internal data structures, which may be then used either for generating +mathematical programming problem instance or directly by a program +called the {\it solver} to obtain numeric solution of the problem. + +\subsection{Linear programming problem} +\label{problem} + +In MathProg the linear programming (LP) problem is stated as follows: + +\medskip + +\noindent\hspace{.7in}minimize (or maximize) +$$z=c_1x_1+c_2x_2+\dots+c_nx_n+c_0\eqno(1.1)$$ +\noindent\hspace{.7in}subject to linear constraints +$$ +\begin{array}{l@{\ }c@{\ }r@{\ }c@{\ }r@{\ }c@{\ }r@{\ }c@{\ }l} +L_1&\leq&a_{11}x_1&+&a_{12}x_2&+\dots+&a_{1n}x_n&\leq&U_1\\ +L_2&\leq&a_{21}x_1&+&a_{22}x_2&+\dots+&a_{2n}x_n&\leq&U_2\\ +\multicolumn{9}{c}{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .}\\ +L_m&\leq&a_{m1}x_1&+&a_{m2}x_2&+\dots+&a_{mn}x_n&\leq&U_m\\ +\end{array}\eqno(1.2) +$$ +\noindent\hspace{.7in}and bounds of variables +$$ +\begin{array}{l@{\ }c@{\ }c@{\ }c@{\ }l} +l_1&\leq&x_1&\leq&u_1\\ +l_2&\leq&x_2&\leq&u_2\\ +\multicolumn{5}{c}{.\ \ .\ \ .\ \ .\ \ .}\\ +l_n&\leq&x_n&\leq&u_n\\ +\end{array}\eqno(1.3) +$$ +where $x_1$, $x_2$, \dots, $x_n$ are variables; $z$ is the objective +function; $c_1$, $c_2$, \dots, $c_n$ are objective coefficients; $c_0$ +is the constant term (``shift'') of the objective function; $a_{11}$, +$a_{12}$, \dots, $a_{mn}$ are constraint coefficients; $L_1$, $L_2$, +\dots, $L_m$ are lower constraint bounds; $U_1$, $U_2$, \dots, $U_m$ +are upper constraint bounds; $l_1$, $l_2$, \dots, $l_n$ are lower +bounds of variables; $u_1$, $u_2$, \dots, $u_n$ are upper bounds of +variables. + +Bounds of variables and constraint bounds can be finite as well as +infinite. Besides, lower bounds can be equal to corresponding upper +bounds. Thus, the following types of variables and constraints are +allowed: + +\newpage + +\begin{tabular}{@{}r@{\ }c@{\ }c@{\ }c@{\ }l@{\hspace*{38pt}}l} +$-\infty$&$<$&$x$&$<$&$+\infty$&Free (unbounded) variable\\ +$l$&$\leq$&$x$&$<$&$+\infty$&Variable with lower bound\\ +$-\infty$&$<$&$x$&$\leq$&$u$&Variable with upper bound\\ +$l$&$\leq$&$x$&$\leq$&$u$&Double-bounded variable\\ +$l$&$=$&$x$&=&$u$&Fixed variable\\ +\end{tabular} + +\bigskip + +\begin{tabular}{@{}r@{\ }c@{\ }c@{\ }c@{\ }ll} +$-\infty$&$<$&$\sum a_jx_j$&$<$&$+\infty$&Free (unbounded) linear +form\\ +$L$&$\leq$&$\sum a_jx_j$&$<$&$+\infty$&Inequality constraint ``greater +than or equal to''\\ +$-\infty$&$<$&$\sum a_jx_j$&$\leq$&$U$&Inequality constraint ``less +than or equal to''\\ +$L$&$\leq$&$\sum a_jx_j$&$\leq$&$U$&Double-bounded inequality +constraint\\ +$L$&$=$&$\sum a_jx_j$&=&$U$&Equality constraint\\ +\end{tabular} + +\bigskip + +In addition to pure LP problems MathProg also allows mixed integer +linear programming (MIP) problems, where some or all variables are +restricted to be integer or binary. + +\subsection{Model objects} + +In MathProg the model is described in terms of sets, parameters, +variables, constraints, and objectives, which are called {\it model +objects}. + +The user introduces particular model objects using the language +statements. Each model object is provided with a symbolic name that +uniquely identifies the object and is intended for referencing purposes. + +Model objects, including sets, can be multidimensional arrays built +over indexing sets. Formally, $n$-dimensional array $A$ is the mapping: +$$A:\Delta\rightarrow\Xi,\eqno(1.4)$$ +where $\Delta\subseteq S_1\times\dots\times S_n$ is a subset of the +Cartesian product of indexing sets,\linebreak $\Xi$ is a set of array members. +In MathProg the set $\Delta$ is called the {\it subscript domain}. Its +members are $n$-tuples $(i_1,\dots,i_n)$, where $i_1\in S_1$, \dots, +$i_n\in S_n$. + +If $n=0$, the Cartesian product above has exactly one member (namely, +\linebreak 0-tuple), so it is convenient to think scalar objects as +0-dimensional arrays having one member. + +The type of array members is determined by the type of corresponding +model object as follows: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}ll@{}} +Model object&Array member\\ +\hline +Set&Elemental plain set\\ +Parameter&Number or symbol\\ +Variable&Elemental variable\\ +Constraint&Elemental constraint\\ +Objective&Elemental objective\\ +\end{tabular} + +\medskip + +In order to refer to a particular object member the object should be +provided with {\it subscripts}. For example, if $a$ is a 2-dimensional +parameter defined over $I\times J$, a reference to its particular +member can be written as $a[i,j]$, where $i\in I$ and $j\in J$. It is +understood that scalar objects being 0-dimensional need no subscripts. + +\subsection{Structure of model description} + +It is sometimes desirable to write a model which, at various points, +may require different data for each problem instance to be solved using +that model. For this reason in MathProg the model description consists +of two parts: the {\it model section} and the {\it data section}. + +The model section is a main part of the model description that contains +declarations of model objects and is common for all problems based on +the corresponding model. + +The data section is an optional part of the model description that +contains data specific for a particular problem instance. + +Depending on what is more convenient the model and data sections can be +placed either in one file or in two separate files. The latter feature +allows having arbitrary number of different data sections to be used +with the same model section. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Coding model description} +\label{coding} + +The model description is coded in plain text format using ASCII +character set. Characters valid in the model description are the +following: + +\begin{itemize} +\item alphabetic characters:\\ +\verb|A B C D E F G H I J K L M N O P Q R S T U V W X Y Z|\\ +\verb|a b c d e f g h i j k l m n o p q r s t u v w x y z _| +\item numeric characters:\\ +\verb|0 1 2 3 4 5 6 7 8 9| +\item special characters:\\ +\verb?! " # & ' ( ) * + , - . / : ; < = > [ ] ^ { | }? +\item white-space characters:\\ +\verb|SP HT CR NL VT FF| +\end{itemize} + +Within string literals and comments any ASCII characters (except +control characters) are valid. + +White-space characters are non-significant. They can be used freely +between lexical units to improve readability of the model description. +They are also used to separate lexical units from each other if there +is no other way to do that. + +Syntactically model description is a sequence of lexical units in the +following categories: + +\begin{itemize} +\item symbolic names; +\item numeric literals; +\item string literals; +\item keywords; +\item delimiters; +\item comments. +\end{itemize} + +The lexical units of the language are discussed below. + +\subsection{Symbolic names} + +A {\it symbolic name} consists of alphabetic and numeric characters, +the first of which must be alphabetic. All symbolic names are distinct +(case sensitive). + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent\verb|alpha123| + +\noindent\verb|This_is_a_name| + +\noindent\verb|_P123_abc_321| + +\newpage + +Symbolic names are used to identify model objects (sets, parameters, +variables, constraints, objectives) and dummy indices. + +All symbolic names (except names of dummy indices) must be unique, i.e. +the model description must have no objects with identical names. +Symbolic names of dummy indices must be unique within the scope, where +they are valid. + +\subsection{Numeric literals} + +A {\it numeric literal} has the form {\it xx}{\tt E}{\it syy}, where +{\it xx} is a number with optional decimal point, {\it s} is the sign +{\tt+} or {\tt-}, {\it yy} is a decimal exponent. The letter {\tt E} is +case insensitive and can be coded as {\tt e}. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent\verb|123| + +\noindent\verb|3.14159| + +\noindent\verb|56.E+5| + +\noindent\verb|.78| + +\noindent\verb|123.456e-7| + +\medskip + +Numeric literals are used to represent numeric quantities. They have +obvious fixed meaning. + +\subsection{String literals} + +A {\it string literal} is a sequence of arbitrary characters enclosed +either in single quotes or in double quotes. Both these forms are +equivalent. + +If the single quote is part of a string literal enclosed in single +quotes, it must be coded twice. Analogously, if the double quote is +part of a string literal enclosed in double quotes, it must be coded +twice. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent\verb|'This is a string'| + +\noindent\verb|"This is another string"| + +\noindent\verb|'1 + 2 = 3'| + +\noindent\verb|'That''s all'| + +\noindent\verb|"She said: ""No"""| + +\medskip + +String literals are used to represent symbolic quantities. + +\subsection{Keywords} + +A {\it keyword} is a sequence of alphabetic characters and possibly +some special characters. + +All keywords fall into two categories: {\it reserved keywords}, which +cannot be used as symbolic names, and {\it non-reserved keywords}, +which being recognized by context can be used as symbolic names. + +\newpage + +The reserved keywords are the following: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}p{.7in}p{.7in}p{.7in}p{.7in}@{}} +{\tt and}&{\tt else}&{\tt mod}&{\tt union}\\ +{\tt by}&{\tt if}&{\tt not}&{\tt within}\\ +{\tt cross}&{\tt in}&{\tt or}\\ +{\tt diff}&{\tt inter}&{\tt symdiff}\\ +{\tt div}&{\tt less}&{\tt then}\\ +\end{tabular} + +\medskip + +Non-reserved keywords are described in following sections. + +All the keywords have fixed meaning, which will be explained on +discussion of corresponding syntactic constructions, where the keywords +are used. + +\subsection{Delimiters} + +A {\it delimiter} is either a single special character or a sequence of +two special characters as follows: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}p{.3in}p{.3in}p{.3in}p{.3in}p{.3in}p{.3in}@{}} +{\tt+}&{\tt\textasciicircum}&{\tt==}&{\tt!}&{\tt:}&{\tt)}\\ +{\tt-}&{\tt\&}&{\tt>=}&{\tt\&\&}&{\tt;}&{\tt[}\\ +{\tt*}&{\tt<}&{\tt>}&{\tt||}&{\tt:=}&{\tt|}\\ +{\tt/}&{\tt<=}&{\tt<>}&{\tt.}&{\tt..}&{\tt\{}\\ +{\tt**}&{\tt=}&{\tt!=}&{\tt,}&{\tt(}&{\tt\}}\\ +\end{tabular} + +\medskip + +If the delimiter consists of two characters, there must be no spaces +between the characters. + +All the delimiters have fixed meaning, which will be explained on +discussion corresponding syntactic constructions, where the delimiters +are used. + +\subsection{Comments} + +For documenting purposes the model description can be provided with +{\it comments}, which may have two different forms. The first form is +a {\it single-line comment}, which begins with the character {\tt\#} +and extends until end of line. The second form is a {\it comment +sequence}, which is a sequence of any characters enclosed within +{\tt/*} and {\tt*/}. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent\verb|param n := 10; # This is a comment| + +\noindent\verb|/* This is another comment */| + +\medskip + +Comments are ignored by the model translator and can appear anywhere in +the model description, where white-space characters are allowed. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Expressions} + +An {\it expression} is a rule for computing a value. In model +description expressions are used as constituents of certain statements. + +In general case expressions consist of operands and operators. + +Depending on the type of the resultant value all expressions fall into +the following categories: + +\begin{itemize} +\item numeric expressions; +\item symbolic expressions; +\item indexing expressions; +\item set expressions; +\item logical expressions; +\item linear expressions. +\end{itemize} + +\subsection{Numeric expressions} + +A {\it numeric expression} is a rule for computing a single numeric +value represented as a floating-point number. + +The primary numeric expression may be a numeric literal, dummy index, +unsubscripted parameter, subscripted parameter, built-in function +reference, iterated numeric expression, conditional numeric expression, +or another numeric expression enclosed in parentheses. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent +\begin{tabular}{@{}ll@{}} +\verb|1.23|&(numeric literal)\\ +\verb|j|&(dummy index)\\ +\verb|time|&(unsubscripted parameter)\\ +\verb|a['May 2003',j+1]|&(subscripted parameter)\\ +\verb|abs(b[i,j])|&(function reference)\\ +\verb|sum{i in S diff T} alpha[i] * b[i,j]|&(iterated expression)\\ +\verb|if i in I then 2 * p else q[i+1]|&(conditional expression)\\ +\verb|(b[i,j] + .5 * c)|&(parenthesized expression)\\ +\end{tabular} + +\medskip + +More general numeric expressions containing two or more primary numeric +expressions may be constructed by using certain arithmetic operators. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent\verb|j+1| + +\noindent\verb|2 * a[i-1,j+1] - b[i,j]| + +\noindent\verb|sum{j in J} a[i,j] * x[j] + sum{k in K} b[i,k] * x[k]| + +\noindent\verb|(if i in I then 2 * p else q[i+1]) / (a[i,j] + 1.5)| + +\subsubsection{Numeric literals} + +If the primary numeric expression is a numeric literal, the resultant +value is obvious. + +\subsubsection{Dummy indices} + +If the primary numeric expression is a dummy index, the resultant value +is current value assigned to that dummy index. + +\subsubsection{Unsubscripted parameters} + +If the primary numeric expression is an unsubscripted parameter (which +must be 0-dimensional), the resultant value is the value of that +parameter. + +\subsubsection{Subscripted parameters} + +The primary numeric expression, which refers to a subscripted parameter, +has the following syntactic form: + +\medskip + +\noindent\hfil +{\it name}{\tt[}$i_1${\tt,} $i_2${\tt,} \dots{\tt,} $i_n${\tt]} + +\medskip + +\noindent where {\it name} is the symbolic name of the parameter, +$i_1$, $i_2$, \dots, $i_n$ are subscripts. + +Each subscript must be a numeric or symbolic expression. The number of +subscripts in the subscript list must be the same as the dimension of +the parameter with which the subscript list is associated. + +Actual values of subscript expressions are used to identify +a particular member of the parameter that determines the resultant +value of the primary expression. + +\subsubsection{Function references} + +In MathProg there exist the following built-in functions which may be +used in numeric expressions: + +\medskip + +\begin{tabular}{@{}p{96pt}p{222pt}@{}} +{\tt abs(}$x${\tt)}&$|x|$, absolute value of $x$\\ +{\tt atan(}$x${\tt)}&$\arctan x$, principal value of the arc tangent of +$x$ (in radians)\\ +{\tt atan(}$y${\tt,} $x${\tt)}&$\arctan y/x$, principal value of the +arc tangent of $y/x$ (in radians). In this case the signs of both +arguments $y$ and $x$ are used to determine the quadrant of the +resultant value\\ +{\tt card(}$X${\tt)}&$|X|$, cardinality (the number of elements) of +set $X$\\ +{\tt ceil(}$x${\tt)}&$\lceil x\rceil$, smallest integer not less than +$x$ (``ceiling of $x$'')\\ +{\tt cos(}$x${\tt)}&$\cos x$, cosine of $x$ (in radians)\\ +{\tt exp(}$x${\tt)}&$e^x$, base-$e$ exponential of $x$\\ +{\tt floor(}$x${\tt)}&$\lfloor x\rfloor$, largest integer not greater +than $x$ (``floor of $x$'')\\ +\end{tabular} + +\begin{tabular}{@{}p{96pt}p{222pt}@{}} +{\tt gmtime()}&the number of seconds elapsed since 00:00:00~Jan~1, 1970, +Coordinated Universal Time (for details see Subsection \ref{gmtime}, +page \pageref{gmtime})\\ +{\tt length(}$s${\tt)}&$|s|$, length of character string $s$\\ +{\tt log(}$x${\tt)}&$\log x$, natural logarithm of $x$\\ +{\tt log10(}$x${\tt)}&$\log_{10}x$, common (decimal) logarithm of $x$\\ +{\tt max(}$x_1${\tt,} $x_2${\tt,} \dots{\tt,} $x_n${\tt)}&the largest +of values $x_1$, $x_2$, \dots, $x_n$\\ +{\tt min(}$x_1${\tt,} $x_2${\tt,} \dots{\tt,} $x_n${\tt)}&the smallest +of values $x_1$, $x_2$, \dots, $x_n$\\ +{\tt round(}$x${\tt)}&rounding $x$ to nearest integer\\ +{\tt round(}$x${\tt,} $n${\tt)}&rounding $x$ to $n$ fractional decimal +digits\\ +{\tt sin(}$x${\tt)}&$\sin x$, sine of $x$ (in radians)\\ +{\tt sqrt(}$x${\tt)}&$\sqrt{x}$, non-negative square root of $x$\\ +{\tt str2time(}$s${\tt,} $f${\tt)}&converting character string $s$ to +calendar time (for details see Subsection \ref{str2time}, page +\pageref{str2time})\\ +{\tt trunc(}$x${\tt)}&truncating $x$ to nearest integer\\ +{\tt trunc(}$x${\tt,} $n${\tt)}&truncating $x$ to $n$ fractional +decimal digits\\ +{\tt Irand224()}&generating pseudo-random integer uniformly distributed +in $[0,2^{24})$\\ +{\tt Uniform01()}&generating pseudo-random number uniformly distributed +in $[0,1)$\\ +{\tt Uniform(}$a${\tt,} $b${\tt)}&generating pseudo-random number +uniformly distributed in $[a,b)$\\ +{\tt Normal01()}&generating Gaussian pseudo-random variate with +$\mu=0$ and $\sigma=1$\\ +{\tt Normal(}$\mu${\tt,} $\sigma${\tt)}&generating Gaussian +pseudo-random variate with given $\mu$ and $\sigma$\\ +\end{tabular} + +\medskip + +Arguments of all built-in functions, except {\tt card}, {\tt length}, +and {\tt str2time}, must be numeric expressions. The argument of +{\tt card} must be a set expression. The argument of {\tt length} and +both arguments of {\tt str2time} must be symbolic expressions. + +The resultant value of the numeric expression, which is a function +reference, is the result of applying the function to its argument(s). + +Note that each pseudo-random generator function has a latent argument +(i.e. some internal state), which is changed whenever the function has +been applied. Thus, if the function is applied repeatedly even to +identical arguments, due to the side effect different resultant values +are always produced. + +\subsubsection{Iterated expressions} +\label{itexpr} + +An {\it iterated numeric expression} is a primary numeric expression, +which has the following syntactic form: + +\medskip + +\noindent\hfil +{\it iterated-operator indexing-expression integrand} + +\medskip + +\noindent where {\it iterated-operator} is the symbolic name of the +iterated operator to be performed (see below), {\it indexing-expression} +is an indexing expression which introduces dummy indices and controls +iterating, {\it integrand} is a numeric expression that participates in +the operation. + +In MathProg there exist four iterated operators, which may be used in +numeric expressions: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}lll@{}} +{\tt sum}&summation&$\displaystyle\sum_{(i_1,\dots,i_n)\in\Delta} +f(i_1,\dots,i_n)$\\ +{\tt prod}&production&$\displaystyle\prod_{(i_1,\dots,i_n)\in\Delta} +f(i_1,\dots,i_n)$\\ +{\tt min}&minimum&$\displaystyle\min_{(i_1,\dots,i_n)\in\Delta} +f(i_1,\dots,i_n)$\\ +{\tt max}&maximum&$\displaystyle\max_{(i_1,\dots,i_n)\in\Delta} +f(i_1,\dots,i_n)$\\ +\end{tabular} + +\medskip + +\noindent where $i_1$, \dots, $i_n$ are dummy indices introduced in +the indexing expression, $\Delta$ is the domain, a set of $n$-tuples +specified by the indexing expression which defines particular values +assigned to the dummy indices on performing the iterated operation, +$f(i_1,\dots,i_n)$ is the integrand, a numeric expression whose +resultant value depends on the dummy indices. + +The resultant value of an iterated numeric expression is the result of +applying of the iterated operator to its integrand over all $n$-tuples +contained in the domain. + +\subsubsection{Conditional expressions} +\label{ifthen} + +A {\it conditional numeric expression} is a primary numeric expression, +which has one of the following two syntactic forms: + +\medskip + +\noindent\hfil +{\tt if} $b$ {\tt then} $x$ {\tt else} $y$ + +\medskip + +\noindent\hspace{126.5pt} +{\tt if} $b$ {\tt then} $x$ + +\medskip + +\noindent where $b$ is an logical expression, $x$ and $y$ are numeric +expressions. + +The resultant value of the conditional expression depends on the value +of the logical expression that follows the keyword {\tt if}. If it +takes on the value {\it true}, the value of the conditional expression +is the value of the expression that follows the keyword {\tt then}. +Otherwise, if the logical expression takes on the value {\it false}, +the value of the conditional expression is the value of the expression +that follows the keyword {\it else}. If the second, reduced form of the +conditional expression is used and the logical expression takes on the +value {\it false}, the resultant value of the conditional expression is +zero. + +\subsubsection{Parenthesized expressions} + +Any numeric expression may be enclosed in parentheses that +syntactically makes it a primary numeric expression. + +Parentheses may be used in numeric expressions, as in algebra, to +specify the desired order in which operations are to be performed. +Where parentheses are used, the expression within the parentheses is +evaluated before the resultant value is used. + +The resultant value of the parenthesized expression is the same as the +value of the expression enclosed within parentheses. + +\subsubsection{Arithmetic operators} + +In MathProg there exist the following arithmetic operators, which may +be used in numeric expressions: + +\medskip + +\begin{tabular}{@{}p{96pt}p{222pt}@{}} +{\tt +} $x$&unary plus\\ +{\tt -} $x$&unary minus\\ +$x$ {\tt +} $y$&addition\\ +$x$ {\tt -} $y$&subtraction\\ +$x$ {\tt less} $y$&positive difference (if $x1$, the second form must be used. + +If the first form of the indexing entry is used, the index $i$ can be +a dummy index only (see below). If the second form is used, the indices +$i_1$, $i_2$, \dots, $i_n$ can be either dummy indices or some numeric +or symbolic expressions, where at least one index must be a dummy index. +The third, reduced form of the indexing entry has the same effect as if +there were $i$ (if $S$ is 1-dimensional) or $i_1$, $i_2$, \dots, $i_n$ +(if $S$ is $n$-dimensional) all specified as dummy indices. + +A {\it dummy index} is an auxiliary model object, which acts like an +individual variable. Values assigned to dummy indices are components of +$n$-tuples from basic sets, i.e. some numeric and symbolic quantities. + +For referencing purposes dummy indices can be provided with symbolic +names. However, unlike other model objects (sets, parameters, etc.) +dummy indices need not be explicitly declared. Each {\it undeclared} +symbolic name being used in the indexing position of an indexing entry +is recognized as the symbolic name of corresponding dummy index. + +Symbolic names of dummy indices are valid only within the scope of the +indexing expression, where the dummy indices were introduced. Beyond +the scope the dummy indices are completely inaccessible, so the same +symbolic names may be used for other purposes, in particular, to +represent dummy indices in other indexing expressions. + +The scope of indexing expression, where implicit declarations of dummy +indices are valid, depends on the context, in which the indexing +expression is used: + +\begin{enumerate} +\item If the indexing expression is used in iterated operator, its +scope extends until the end of the integrand. +\item If the indexing expression is used as a primary set expression, +its scope extends until the end of that indexing expression. +\item If the indexing expression is used to define the subscript domain +in declarations of some model objects, its scope extends until the end +of the corresponding statement. +\end{enumerate} + +The indexing mechanism implemented by means of indexing expressions is +best explained by some examples discussed below. + +Let there be given three sets: + +\medskip + +\noindent\hspace{33.5pt} +$A=\{4,7,9\}$, + +\medskip + +\noindent\hfil +$B=\{(1,Jan),(1,Feb),(2,Mar),(2,Apr),(3,May),(3,Jun)\}$, + +\medskip + +\noindent\hspace{33.5pt} +$C=\{a,b,c\}$, + +\medskip + +\noindent where $A$ and $C$ consist of 1-tuples (singlets), $B$ +consists of 2-tuples (doublets). Consider the following indexing +expression: + +\medskip + +\noindent\hfil +{\tt\{i in A, (j,k) in B, l in C\}} + +\medskip + +\noindent where {\tt i}, {\tt j}, {\tt k}, and {\tt l} are dummy +indices. + +Although MathProg is not a procedural language, for any indexing +expression an equivalent algorithmic description can be given. In +particular, the algorithmic description of the indexing expression +above could look like follows: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}l@{}} +{\bf for all} $i\in A$ {\bf do}\\ +\hspace{12pt}{\bf for all} $(j,k)\in B$ {\bf do}\\ +\hspace{24pt}{\bf for all} $l\in C$ {\bf do}\\ +\hspace{36pt}{\it action};\\ +\end{tabular} + +\newpage + +\noindent where the dummy indices $i$, $j$, $k$, $l$ are consecutively +assigned corresponding components of $n$-tuples from the basic sets $A$, +$B$, $C$, and {\it action} is some action that depends on the context, +where the indexing expression is used. For example, if the action were +printing current values of dummy indices, the printout would look like +follows: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}llll@{}} +$i=4$&$j=1$&$k=Jan$&$l=a$\\ +$i=4$&$j=1$&$k=Jan$&$l=b$\\ +$i=4$&$j=1$&$k=Jan$&$l=c$\\ +$i=4$&$j=1$&$k=Feb$&$l=a$\\ +$i=4$&$j=1$&$k=Feb$&$l=b$\\ +\multicolumn{4}{c}{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .}\\ +$i=9$&$j=3$&$k=Jun$&$l=b$\\ +$i=9$&$j=3$&$k=Jun$&$l=c$\\ +\end{tabular} + +\medskip + +Let the example indexing expression be used in the following iterated +operation: + +\medskip + +\noindent\hfil +{\tt sum\{i in A, (j,k) in B, l in C\} p[i,j,k,l]} + +\medskip + +\noindent where {\tt p} is a 4-dimensional numeric parameter or some +numeric expression whose resultant value depends on {\tt i}, {\tt j}, +{\tt k}, and {\tt l}. In this case the action is summation, so the +resultant value of the primary numeric expression is: +$$\sum_{i\in A,(j,k)\in B,l\in C}(p_{ijkl}).$$ + +Now let the example indexing expression be used as a primary set +expression. In this case the action is gathering all 4-tuples +(quadruplets) of the form $(i,j,k,l)$ in one set, so the resultant +value of such operation is simply the Cartesian product of the basic +sets: +$$A\times B\times C=\{(i,j,k,l):i\in A,(j,k)\in B,l\in C\}.$$ +Note that in this case the same indexing expression might be written in +the reduced form: + +\medskip + +\noindent\hfil +{\tt\{A, B, C\}} + +\medskip + +\noindent because the dummy indices $i$, $j$, $k$, and $l$ are not +referenced and therefore their symbolic names need not be specified. + +Finally, let the example indexing expression be used as the subscript +domain in the declaration of a 4-dimensional model object, say, +a numeric parameter: + +\medskip + +\noindent\hfil +{\tt param p\{i in A, (j,k) in B, l in C\}} \dots {\tt;} + +\medskip + +\noindent In this case the action is generating the parameter members, +where each member has the form $p[i,j,k,l]$. + +As was said above, some indices in the second form of indexing entries +may be numeric or symbolic expressions, not only dummy indices. In this +case resultant values of such expressions play role of some logical +conditions to select only that $n$-tuples from the Cartesian product of +basic sets that satisfy these conditions. + +Consider, for example, the following indexing expression: + +\medskip + +\noindent\hfil +{\tt\{i in A, (i-1,k) in B, l in C\}} + +\medskip + +\noindent where {\tt i}, {\tt k}, {\tt l} are dummy indices, and +{\tt i-1} is a numeric expression. The algorithmic decsription of this +indexing expression is the following: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}l@{}} +{\bf for all} $i\in A$ {\bf do}\\ +\hspace{12pt}{\bf for all} $(j,k)\in B$ {\bf and} $j=i-1$ {\bf do}\\ +\hspace{24pt}{\bf for all} $l\in C$ {\bf do}\\ +\hspace{36pt}{\it action};\\ +\end{tabular} + +\medskip + +\noindent Thus, if this indexing expression were used as a primary set +expression, the resultant set would be the following: +$$\{(4,May,a),(4,May,b),(4,May,c),(4,Jun,a),(4,Jun,b),(4,Jun,c)\}.$$ +Should note that in this case the resultant set consists of 3-tuples, +not of 4-tuples, because in the indexing expression there is no dummy +index that corresponds to the first component of 2-tuples from the set +$B$. + +The general rule is: the number of components of $n$-tuples defined by +an indexing expression is the same as the number of dummy indices in +that expression, where the correspondence between dummy indices and +components on $n$-tuples in the resultant set is positional, i.e. the +first dummy index corresponds to the first component, the second dummy +index corresponds to the second component, etc. + +In some cases it is needed to select a subset from the Cartesian +product of some sets. This may be attained by using an optional logical +predicate, which is specified in the indexing expression. + +Consider, for example, the following indexing expression: + +\medskip + +\noindent\hfil +{\tt\{i in A, (j,k) in B, l in C: i <= 5 and k <> 'Mar'\}} + +\medskip + +\noindent where the logical expression following the colon is a +predicate. The algorithmic description of this indexing expression is +the following: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}l@{}} +{\bf for all} $i\in A$ {\bf do}\\ +\hspace{12pt}{\bf for all} $(j,k)\in B$ {\bf do}\\ +\hspace{24pt}{\bf for all} $l\in C$ {\bf do}\\ +\hspace{36pt}{\bf if} $i\leq 5$ {\bf and} $l\neq`Mar'$ {\bf then}\\ +\hspace{48pt}{\it action};\\ +\end{tabular} + +\medskip + +\noindent Thus, if this indexing expression were used as a primary set +expression, the resultant set would be the following: +$$\{(4,1,Jan,a),(4,1,Feb,a),(4,2,Apr,a),\dots,(4,3,Jun,c)\}.$$ + +If no predicate is specified in the indexing expression, one, which +takes on the value {\it true}, is assumed. + +\subsection{Set expressions} + +A {\it set expression} is a rule for computing an elemental set, i.e. +a collection of $n$-tuples, where components of $n$-tuples are numeric +and symbolic quantities. + +The primary set expression may be a literal set, unsubscripted set, +subscripted set, ``arithmetic'' set, indexing expression, iterated set +expression, conditional set expression, or another set expression +enclosed in parentheses. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent +\begin{tabular}{@{}ll@{}} +\verb|{(123,'aa'), (i,'bb'), (j-1,'cc')}|&(literal set)\\ +\verb|I|&(unsubscripted set)\\ +\verb|S[i-1,j+1]|&(subscripted set)\\ +\verb|1..t-1 by 2|&(``arithmetic'' set)\\ +\verb|{t in 1..T, (t+1,j) in S: (t,j) in F}|&(indexing expression)\\ +\verb|setof{i in I, j in J}(i+1,j-1)|&(iterated expression)\\ +\verb|if i < j then S[i] else F diff S[j]|&(conditional expression)\\ +\verb|(1..10 union 21..30)|&(parenthesized expression)\\ +\end{tabular} + +\medskip + +More general set expressions containing two or more primary set +expressions may be constructed by using certain set operators. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent\verb|(A union B) inter (I cross J)| + +\noindent +\verb|1..10 cross (if i < j then {'a', 'b', 'c'} else {'d', 'e', 'f'})| + +\subsubsection{Literal sets} + +A {\it literal set} is a primary set expression, which has the +following two syntactic forms: + +\medskip + +\noindent\hspace{39pt} +{\tt\{}$e_1${\tt,} $e_2${\tt,} \dots{\tt,} $e_m${\tt\}} + +\medskip + +\noindent\hfil +{\tt\{(}$e_{11}${\tt,} \dots{\tt,} $e_{1n}${\tt),} +{\tt(}$e_{21}${\tt,} \dots{\tt,} $e_{2n}${\tt),} \dots{\tt,} +{\tt(}$e_{m1}${\tt,} \dots{\tt,} $e_{mn}${\tt)\}} + +\medskip + +\noindent where $e_1$, \dots, $e_m$, $e_{11}$, \dots, $e_{mn}$ are +numeric or symbolic expressions. + +If the first form is used, the resultant set consists of 1-tuples +(singlets) enumerated within the curly braces. It is allowed to specify +an empty set as {\tt\{\ \}}, which has no 1-tuples. If the second form +is used, the resultant set consists of $n$-tuples enumerated within the +curly braces, where a particular $n$-tuple consists of corresponding +components enumerated within the parentheses. All $n$-tuples must have +the same number of components. + +\subsubsection{Unsubscripted sets} + +If the primary set expression is an unsubscripted set (which must be +0-dimen\-sional), the resultant set is an elemental set associated with +the corresponding set object. + +\newpage + +\subsubsection{Subscripted sets} + +The primary set expression, which refers to a subscripted set, has the +following syntactic form: + +\medskip + +\noindent\hfil +{\it name}{\tt[}$i_1${\tt,} $i_2${\tt,} \dots{\tt,} $i_n${\tt]} + +\medskip + +\noindent where {\it name} is the symbolic name of the set object, +$i_1$, $i_2$, \dots, $i_n$ are subscripts. + +Each subscript must be a numeric or symbolic expression. The number of +subscripts in the subscript list must be the same as the dimension of +the set object with which the subscript list is associated. + +Actual values of subscript expressions are used to identify a +particular member of the set object that determines the resultant set. + +\subsubsection{``Arithmetic'' sets} + +The primary set expression, which is an ``arithmetic'' set, has the +following two syntactic forms: + +\medskip + +\noindent\hfil +$t_0$ {\tt..} $t_1$ {\tt by} $\delta t$ + +\medskip + +\noindent\hspace{138.5pt} +$t_0$ {\tt..} $t_1$ + +\medskip + +\noindent where $t_0$, $t_1$, and $\delta t$ are numeric expressions +(the value of $\delta t$ must not be zero). The second form is +equivalent to the first form, where $\delta t=1$. + +If $\delta t>0$, the resultant set is determined as follows: +$$\{t:\exists k\in{\cal Z}(t=t_0+k\delta t,\ t_0\leq t\leq t_1)\}.$$ +Otherwise, if $\delta t<0$, the resultant set is determined as follows: +$$\{t:\exists k\in{\cal Z}(t=t_0+k\delta t,\ t_1\leq t\leq t_0)\}.$$ + +\subsubsection{Indexing expressions} + +If the primary set expression is an indexing expression, the resultant +set is determined as described above in Subsection \ref{indexing}, page +\pageref{indexing}. + +\subsubsection{Iterated expressions} + +An {\it iterated set expression} is a primary set expression, which has +the following syntactic form: + +\medskip + +\noindent\hfil +{\tt setof} {\it indexing-expression} {\it integrand} + +\medskip + +\noindent where {\it indexing-expression} is an indexing expression, +which introduces dummy indices and controls iterating, {\it integrand} +is either a single numeric or symbolic expression or a list of numeric +and symbolic expressions separated by commae and enclosed in +parentheses. + +If the integrand is a single numeric or symbolic expression, the +resultant set consists of 1-tuples and is determined as follows: +$$\{x:(i_1,\dots,i_n)\in\Delta\},$$ +\noindent where $x$ is a value of the integrand, $i_1$, \dots, $i_n$ +are dummy indices introduced in the indexing expression, $\Delta$ is +the domain, a set of $n$-tuples specified by the indexing expression, +which defines particular values assigned to the dummy indices on +performing the iterated operation. + +If the integrand is a list containing $m$ numeric and symbolic +expressions, the resultant set consists of $m$-tuples and is determined +as follows: +$$\{(x_1,\dots,x_m):(i_1,\dots,i_n)\in\Delta\},$$ +where $x_1$, \dots, $x_m$ are values of the expressions in the +integrand list, $i_1$, \dots, $i_n$ and $\Delta$ have the same meaning +as above. + +\subsubsection{Conditional expressions} + +A {\it conditional set expression} is a primary set expression that has +the following syntactic form: + +\medskip + +\noindent\hfil +{\tt if} $b$ {\tt then} $X$ {\tt else} $Y$ + +\medskip + +\noindent where $b$ is an logical expression, $X$ and $Y$ are set +expressions, which must define sets of the same dimension. + +The resultant value of the conditional expression depends on the value +of the logical expression that follows the keyword {\tt if}. If it +takes on the value {\it true}, the resultant set is the value of the +expression that follows the keyword {\tt then}. Otherwise, if the +logical expression takes on the value {\it false}, the resultant set is +the value of the expression that follows the keyword {\tt else}. + +\subsubsection{Parenthesized expressions} + +Any set expression may be enclosed in parentheses that syntactically +makes it a primary set expression. + +Parentheses may be used in set expressions, as in algebra, to specify +the desired order in which operations are to be performed. Where +parentheses are used, the expression within the parentheses is +evaluated before the resultant value is used. + +The resultant value of the parenthesized expression is the same as the +value of the expression enclosed within parentheses. + +\subsubsection{Set operators} + +In MathProg there exist the following set operators, which may be used +in set expressions: + +\medskip + +\begin{tabular}{@{}p{96pt}p{222pt}@{}} +$X$ {\tt union} $Y$&union $X\cup Y$\\ +$X$ {\tt diff} $Y$&difference $X\backslash Y$\\ +$X$ {\tt symdiff} $Y$&symmetric difference $X\oplus Y$\\ +$X$ {\tt inter} $Y$&intersection $X\cap Y$\\ +$X$ {\tt cross} $Y$&cross (Cartesian) product $X\times Y$\\ +\end{tabular} + +\medskip + +\noindent where $X$ and Y are set expressions, which must define sets +of the identical dimension (except the Cartesian product). + +If the expression includes more than one set operator, all operators +are performed from left to right according to the hierarchy of +operations (see below). + +The resultant value of the expression, which contains set operators, is +the result of applying the operators to their operands. + +The dimension of the resultant set, i.e. the dimension of $n$-tuples, +of which the resultant set consists of, is the same as the dimension of +the operands, except the Cartesian product, where the dimension of the +resultant set is the sum of the dimensions of its operands. + +\subsubsection{Hierarchy of operations} + +The following list shows the hierarchy of operations in set +expressions: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}ll@{}} +Operation&Hierarchy\\ +\hline +Evaluation of numeric operations&1st-7th\\ +Evaluation of symbolic operations&8th-9th\\ +Evaluation of iterated or ``arithmetic'' set ({\tt setof}, {\tt..})& +10th\\ +Cartesian product ({\tt cross})&11th\\ +Intersection ({\tt inter})&12th\\ +Union and difference ({\tt union}, {\tt diff}, {\tt symdiff})&13th\\ +Conditional evaluation ({\tt if} \dots {\tt then} \dots {\tt else})& +14th\\ +\end{tabular} + +\medskip + +This hierarchy has the same meaning as was explained above for numeric +expressions (see Subsection \ref{hierarchy}, page \pageref{hierarchy}). + +\subsection{Logical expressions} + +A {\it logical expression} is a rule for computing a single logical +value, which can be either {\it true} or {\it false}. + +The primary logical expression may be a numeric expression, relational +expression, iterated logical expression, or another logical expression +enclosed in parentheses. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent +\begin{tabular}{@{}ll@{}} +\verb|i+1|&(numeric expression)\\ +\verb|a[i,j] < 1.5|&(relational expression)\\ +\verb|s[i+1,j-1] <> 'Mar'|&(relational expression)\\ +\verb|(i+1,'Jan') not in I cross J|&(relational expression)\\ +\verb|S union T within A[i] inter B[j]|&(relational expression)\\ +\verb|forall{i in I, j in J} a[i,j] < .5 * b|&(iterated expression)\\ +\verb|(a[i,j] < 1.5 or b[i] >= a[i,j])|&(parenthesized expression)\\ +\end{tabular} + +\medskip + +More general logical expressions containing two or more primary logical +expressions may be constructed by using certain logical operators. + +\newpage + +\noindent{\bf Examples} + +\medskip + +\noindent\verb|not (a[i,j] < 1.5 or b[i] >= a[i,j]) and (i,j) in S| + +\noindent\verb|(i,j) in S or (i,j) not in T diff U| + +\subsubsection{Numeric expressions} + +The resultant value of the primary logical expression, which is a +numeric expression, is {\it true}, if the resultant value of the +numeric expression is non-zero. Otherwise the resultant value of the +logical expression is {\it false}. + +\subsubsection{Relational operators} + +In MathProg there exist the following relational operators, which may +be used in logical expressions: + +\medskip + +\begin{tabular}{@{}ll@{}} +$x$ {\tt<} $y$&test on $x=} $y$&test on $x\geq y$\\ +$x$ {\tt>} $y$&test on $x>y$\\ +$x$ {\tt<>} $y$, $x$ {\tt!=} $y$&test on $x\neq y$\\ +$x$ {\tt in} $Y$&test on $x\in Y$\\ +{\tt(}$x_1${\tt,}\dots{\tt,}$x_n${\tt)} {\tt in} $Y$&test on +$(x_1,\dots,x_n)\in Y$\\ +$x$ {\tt not} {\tt in} $Y$, $x$ {\tt!in} $Y$&test on $x\not\in Y$\\ +{\tt(}$x_1${\tt,}\dots{\tt,}$x_n${\tt)} {\tt not} {\tt in} $Y$, +{\tt(}$x_1${\tt,}\dots{\tt,}$x_n${\tt)} {\tt !in} $Y$&test on +$(x_1,\dots,x_n)\not\in Y$\\ +$X$ {\tt within} $Y$&test on $X\subseteq Y$\\ +$X$ {\tt not} {\tt within} $Y$, $X$ {\tt !within} $Y$&test on +$X\not\subseteq Y$\\ +\end{tabular} + +\medskip + +\noindent where $x$, $x_1$, \dots, $x_n$, $y$ are numeric or symbolic +expressions, $X$ and $Y$ are set expression. + +{\it Notes:} + +1. In the operations {\tt in}, {\tt not in}, and {\tt !in} the +number of components in the first operands must be the same as the +dimension of the second operand. + +2. In the operations {\tt within}, {\tt not within}, and {\tt !within} +both operands must have identical dimension. + +All the relational operators listed above have their conventional +mathematical meaning. The resultant value is {\it true}, if +corresponding relation is satisfied for its operands, otherwise +{\it false}. (Note that symbolic values are ordered lexicographically, +and any numeric value precedes any symbolic value.) + +\subsubsection{Iterated expressions} + +An {\it iterated logical expression} is a primary logical expression, +which has the following syntactic form: + +\medskip + +\noindent\hfil +{\it iterated-operator} {\it indexing-expression} {\it integrand} + +\medskip + +\noindent where {\it iterated-operator} is the symbolic name of the +iterated operator to be performed (see below), {\it indexing-expression} +is an indexing expression which introduces dummy indices and controls +iterating, {\it integrand} is a numeric expression that participates in +the operation. + +In MathProg there exist two iterated operators, which may be used in +logical expressions: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}lll@{}} +{\tt forall}&$\forall$-quantification&$\displaystyle +\forall(i_1,\dots,i_n)\in\Delta[f(i_1,\dots,i_n)],$\\ +{\tt exists}&$\exists$-quantification&$\displaystyle +\exists(i_1,\dots,i_n)\in\Delta[f(i_1,\dots,i_n)],$\\ +\end{tabular} + +\medskip + +\noindent where $i_1$, \dots, $i_n$ are dummy indices introduced in +the indexing expression, $\Delta$ is the domain, a set of $n$-tuples +specified by the indexing expression which defines particular values +assigned to the dummy indices on performing the iterated operation, +$f(i_1,\dots,i_n)$ is the integrand, a logical expression whose +resultant value depends on the dummy indices. + +For $\forall$-quantification the resultant value of the iterated +logical expression is {\it true}, if the value of the integrand is +{\it true} for all $n$-tuples contained in the domain, otherwise +{\it false}. + +For $\exists$-quantification the resultant value of the iterated +logical expression is {\it false}, if the value of the integrand is +{\it false} for all $n$-tuples contained in the domain, otherwise +{\it true}. + +\subsubsection{Parenthesized expressions} + +Any logical expression may be enclosed in parentheses that +syntactically makes it a primary logical expression. + +Parentheses may be used in logical expressions, as in algebra, to +specify the desired order in which operations are to be performed. +Where parentheses are used, the expression within the parentheses is +evaluated before the resultant value is used. + +The resultant value of the parenthesized expression is the same as the +value of the expression enclosed within parentheses. + +\subsubsection{Logical operators} + +In MathProg there exist the following logical operators, which may be +used in logical expressions: + +\medskip + +\begin{tabular}{@{}p{96pt}p{222pt}@{}} +{\tt not} $x$, {\tt!}$x$&negation $\neg\ x$\\ +$x$ {\tt and} $y$, $x$ {\tt\&\&} $y$&conjunction (logical ``and'') +$x\;\&\;y$\\ +$x$ {\tt or} $y$, $x$ {\tt||} $y$&disjunction (logical ``or'') +$x\vee y$\\ +\end{tabular} + +\medskip + +\noindent where $x$ and $y$ are logical expressions. + +If the expression includes more than one logical operator, all +operators are performed from left to right according to the hierarchy +of the operations (see below). The resultant value of the expression, +which contains logical operators, is the result of applying the +operators to their operands. + +\subsubsection{Hierarchy of operations} + +The following list shows the hierarchy of operations in logical +expressions: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}ll@{}} +Operation&Hierarchy\\ +\hline +Evaluation of numeric operations&1st-7th\\ +Evaluation of symbolic operations&8th-9th\\ +Evaluation of set operations&10th-14th\\ +Relational operations ({\tt<}, {\tt<=}, etc.)&15th\\ +Negation ({\tt not}, {\tt!})&16th\\ +Conjunction ({\tt and}, {\tt\&\&})&17th\\ +$\forall$- and $\exists$-quantification ({\tt forall}, {\tt exists})& +18th\\ +Disjunction ({\tt or}, {\tt||})&19th\\ +\end{tabular} + +\medskip + +This hierarchy has the same meaning as was explained above for numeric +expressions (see Subsection \ref{hierarchy}, page \pageref{hierarchy}). + +\subsection{Linear expressions} + +An {\it linear expression} is a rule for computing so called +a {\it linear form} or simply a {\it formula}, which is a linear (or +affine) function of elemental variables. + +The primary linear expression may be an unsubscripted variable, +subscripted variable, iterated linear expression, conditional linear +expression, or another linear expression enclosed in parentheses. + +It is also allowed to use a numeric expression as the primary linear +expression, in which case the resultant value of the numeric expression +is automatically converted to a formula that includes the constant term +only. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent +\begin{tabular}{@{}ll@{}} +\verb|z|&(unsubscripted variable)\\ +\verb|x[i,j]|&(subscripted variable)\\ +\verb|sum{j in J} (a[i] * x[i,j] + 3 * y)|&(iterated expression)\\ +\verb|if i in I then x[i,j] else 1.5 * z + 3|&(conditional expression)\\ +\verb|(a[i,j] * x[i,j] + y[i-1] + .1)|&(parenthesized expression)\\ +\end{tabular} + +\medskip + +More general linear expressions containing two or more primary linear +expressions may be constructed by using certain arithmetic operators. + +\medskip + +\noindent{\bf Examples} + +\medskip + +\noindent\verb|2 * x[i-1,j+1] + 3.5 * y[k] + .5 * z| + +\noindent\verb|(- x[i,j] + 3.5 * y[k]) / sum{t in T} abs(d[i,j,t])| + +\subsubsection{Unsubscripted variables} + +If the primary linear expression is an unsubscripted variable (which +must be 0-dimensional), the resultant formula is that unsubscripted +variable. + +\subsubsection{Subscripted variables} + +The primary linear expression, which refers to a subscripted variable, +has the following syntactic form: + +\medskip + +\noindent\hfil +{\it name}{\tt[}$i_1${\tt,} $i_2${\tt,} \dots{\tt,} $i_n${\tt]} + +\medskip + +\noindent where {\it name} is the symbolic name of the model variable, +$i_1$, $i_2$, \dots, $i_n$ are subscripts. + +Each subscript must be a numeric or symbolic expression. The number of +subscripts in the subscript list must be the same as the dimension of +the model variable with which the subscript list is associated. + +Actual values of the subscript expressions are used to identify a +particular member of the model variable that determines the resultant +formula, which is an elemental variable associated with corresponding +member. + +\subsubsection{Iterated expressions} + +An {\it iterated linear expression} is a primary linear expression, +which has the following syntactic form: + +\medskip + +\noindent\hfil +{\tt sum} {\it indexing-expression} {\it integrand} + +\medskip + +\noindent where {\it indexing-expression} is an indexing expression, +which introduces dummy indices and controls iterating, {\it integrand} +is a linear expression that participates in the operation. + +The iterated linear expression is evaluated exactly in the same way as +the iterated numeric expression (see Subection \ref{itexpr}, page +\pageref{itexpr}) with exception that the integrand participated in the +summation is a formula, not a numeric value. + +\subsubsection{Conditional expressions} + +A {\it conditional linear expression} is a primary linear expression, +which has one of the following two syntactic forms: + +\medskip + +\noindent\hfil +{\tt if} $b$ {\tt then} $f$ {\tt else} $g$ + +\medskip + +\noindent\hspace{127pt} +{\tt if} $b$ {\tt then} $f$ + +\medskip + +\noindent where $b$ is an logical expression, $f$ and $g$ are linear +expressions. + +The conditional linear expression is evaluated exactly in the same way +as the conditional numeric expression (see Subsection \ref{ifthen}, +page \pageref{ifthen}) with exception that operands participated in the +operation are formulae, not numeric values. + +\subsubsection{Parenthesized expressions} + +Any linear expression may be enclosed in parentheses that syntactically +makes it a primary linear expression. + +Parentheses may be used in linear expressions, as in algebra, to +specify the desired order in which operations are to be performed. +Where parentheses are used, the expression within the parentheses is +evaluated before the resultant formula is used. + +The resultant value of the parenthesized expression is the same as the +value of the expression enclosed within parentheses. + +\subsubsection{Arithmetic operators} + +In MathProg there exists the following arithmetic operators, which may +be used in linear expressions: + +\medskip + +\begin{tabular}{@{}p{96pt}p{222pt}@{}} +{\tt+} $f$&unary plus\\ +{\tt-} $f$&unary minus\\ +$f$ {\tt+} $g$&addition\\ +$f$ {\tt-} $g$&subtraction\\ +$x$ {\tt*} $f$, $f$ {\tt*} $x$&multiplication\\ +$f$ {\tt/} $x$&division +\end{tabular} + +\medskip + +\noindent where $f$ and $g$ are linear expressions, $x$ is a numeric +expression (more precisely, a linear expression containing only the +constant term). + +If the expression includes more than one arithmetic operator, all +operators are performed from left to right according to the hierarchy +of operations (see below). The resultant value of the expression, which +contains arithmetic operators, is the result of applying the operators +to their operands. + +\subsubsection{Hierarchy of operations} + +The hierarchy of arithmetic operations used in linear expressions is +the same as for numeric expressions (see Subsection \ref{hierarchy}, +page \pageref{hierarchy}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Statements} + +{\it Statements} are basic units of the model description. In MathProg +all statements are divided into two categories: declaration statements +and functional statements. + +{\it Declaration statements} (set statement, parameter statement, +variable statement, constraint statement, and objective statement) are +used to declare model objects of certain kinds and define certain +properties of such objects. + +{\it Functional statements} (solve statement, check statement, display +statement, printf statement, loop statement) are intended for +performing some specific actions. + +Note that declaration statements may follow in arbitrary order, which +does not affect the result of translation. However, any model object +must be declared before it is referenced in other statements. + +\subsection{Set statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][24pt]{345pt}{ +\hspace{6pt} {\tt set} {\it name} {\it alias} {\it domain} {\tt,} +{\it attrib} {\tt,} \dots {\tt,} {\it attrib} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it name} is a symbolic name of the +set; +\item[\hspace*{54pt}] {\it alias} is an optional string literal, which +specifies an alias of the set; +\item[\hspace*{54pt}] {\it domain} is an optional indexing expression, +which specifies a subscript domain of the set; +\item[\hspace*{54pt}] {\it attrib}, \dots, {\it attrib} are optional +attributes of the set. (Commae preceding attributes may be omitted.) +\end{description} + +\noindent Optional attributes: + +\begin{description} +\item[{\tt dimen} $n$\hspace*{19pt}] specifies the dimension of +$n$-tuples, which the set consists of; +\item[{\tt within} {\it expression}]\hspace*{0pt}\\ +specifies a superset which restricts the set or all its members +(elemental sets) to be within that superset; +\item[{\tt:=} {\it expression}]\hspace*{0pt}\\ +specifies an elemental set assigned to the set or its members; +\item[{\tt default} {\it expression}]\hspace*{0pt}\\ +specifies an elemental set assigned to the set or its members whenever +no appropriate data are available in the data section. +\end{description} + +\newpage + +\noindent{\bf Examples} + +\begin{verbatim} +set V; +set E within V cross V; +set step{s in 1..maxiter} dimen 2 := if s = 1 then E else + step[s-1] union setof{k in V, (i,k) in step[s-1], (k,j) + in step[s-1]}(i,j); +set A{i in I, j in J}, within B[i+1] cross C[j-1], within + D diff E, default {('abc',123), (321,'cba')}; +\end{verbatim} + +The set statement declares a set. If the subscript domain is not +specified, the set is a simple set, otherwise it is an array of +elemental sets. + +The {\tt dimen} attribute specifies the dimension of $n$-tuples, which +the set (if it is a simple set) or its members (if the set is an array +of elemental sets) consist of, where $n$ must be unsigned integer from +1 to 20. At most one {\tt dimen} attribute can be specified. If the +{\tt dimen} attribute is not specified, the dimension of\linebreak +$n$-tuples is implicitly determined by other attributes (for example, +if there is a set expression that follows {\tt:=} or the keyword +{\tt default}, the dimension of $n$-tuples of corresponding elemental +set is used). If no dimension information is available, {\tt dimen 1} +is assumed. + +The {\tt within} attribute specifies a set expression whose resultant +value is a superset used to restrict the set (if it is a simple set) or +its members (if the set is an array of elemental sets) to be within +that superset. Arbitrary number of {\tt within} attributes may be +specified in the same set statement. + +The assign ({\tt:=}) attribute specifies a set expression used to +evaluate elemental set(s) assigned to the set (if it is a simple set) +or its members (if the set is an array of elemental sets). If the +assign attribute is specified, the set is {\it computable} and +therefore needs no data to be provided in the data section. If the +assign attribute is not specified, the set must be provided with data +in the data section. At most one assign or default attribute can be +specified for the same set. + +The {\tt default} attribute specifies a set expression used to evaluate +elemental set(s) assigned to the set (if it is a simple set) or its +members (if the set is an array of elemental sets) whenever +no appropriate data are available in the data section. If neither +assign nor default attribute is specified, missing data will cause an +error. + +\subsection{Parameter statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][24pt]{345pt}{ +\hspace{6pt} {\tt param} {\it name} {\it alias} {\it domain} {\tt,} +{\it attrib} {\tt,} \dots {\tt,} {\it attrib} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it name} is a symbolic name of the +parameter; +\item[\hspace*{54pt}] {\it alias} is an optional string literal, which +specifies an alias of the parameter; +\item[\hspace*{54pt}] {\it domain} is an optional indexing expression, +which specifies a subscript domain of the parameter; +\item[\hspace*{54pt}] {\it attrib}, \dots, {\it attrib} are optional +attributes of the parameter. (Commae preceding attributes may be +omitted.) +\end{description} + +\noindent Optional attributes: + +\begin{description} +\item[{\tt integer}\hspace*{18.5pt}] specifies that the parameter is +integer; +\item[{\tt binary}\hspace*{24pt}] specifies that the parameter is +binary; +\item[{\tt symbolic}\hspace*{13.5pt}] specifies that the parameter is +symbolic; +\item[{\it relation expression}]\hspace*{0pt}\\ +(where {\it relation} is one of: {\tt<}, {\tt<=}, {\tt=}, {\tt==}, +{\tt>=}, {\tt>}, {\tt<>}, {\tt!=})\\ +specifies a condition that restricts the parameter or its members to +satisfy that condition; +\item[{\tt in} {\it expression}]\hspace*{0pt}\\ +specifies a superset that restricts the parameter or its members to be +in that superset; +\item[{\tt:=} {\it expression}]\hspace*{0pt}\\ +specifies a value assigned to the parameter or its members; +\item[{\tt default} {\it expression}]\hspace*{0pt}\\ +specifies a value assigned to the parameter or its members whenever +no appropriate data are available in the data section. +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +param units{raw, prd} >= 0; +param profit{prd, 1..T+1}; +param N := 20, integer, >= 0, <= 100; +param comb 'n choose k' {n in 0..N, k in 0..n} := + if k = 0 or k = n then 1 else comb[n-1,k-1] + comb[n-1,k]; +param p{i in I, j in J}, integer, >= 0, <= i+j, + in A[i] symdiff B[j], in C[i,j], default 0.5 * (i + j); +param month symbolic default 'May' in {'Mar', 'Apr', 'May'}; +\end{verbatim} + +The parameter statement declares a parameter. If a subscript domain is +not specified, the parameter is a simple (scalar) parameter, otherwise +it is a $n$-dimensional array. + +The type attributes {\tt integer}, {\tt binary}, and {\tt symbolic} +qualify the type of values that can be assigned to the parameter as +shown below: + +\medskip + +\noindent\hfil +\begin{tabular}{@{}ll@{}} +Type attribute&Assigned values\\ +\hline +(not specified)&Any numeric values\\ +{\tt integer}&Only integer numeric values\\ +{\tt binary}&Either 0 or 1\\ +{\tt symbolic}&Any numeric and symbolic values\\ +\end{tabular} + +\newpage + +The {\tt symbolic} attribute cannot be specified along with other type +attributes. Being specified it must precede all other attributes. + +The condition attribute specifies an optional condition that restricts +values assigned to the parameter to satisfy that condition. This +attribute has the following syntactic forms: + +\medskip + +\begin{tabular}{@{}ll@{}} +{\tt<} $v$&check for $x=} $v$&check for $x\geq v$\\ +{\tt>} $v$&check for $x\geq v$\\ +{\tt<>} $v$, {\tt!=} $v$&check for $x\neq v$\\ +\end{tabular} + +\medskip + +\noindent where $x$ is a value assigned to the parameter, $v$ is the +resultant value of a numeric or symbolic expression specified in the +condition attribute. Arbitrary number of condition attributes can be +specified for the same parameter. If a value being assigned to the +parameter during model evaluation violates at least one of specified +conditions, an error is raised. (Note that symbolic values are ordered +lexicographically, and any numeric value precedes any symbolic value.) + +The {\tt in} attribute is similar to the condition attribute and +specifies a set expression whose resultant value is a superset used to +restrict numeric or symbolic values assigned to the parameter to be in +that superset. Arbitrary number of the {\tt in} attributes can be +specified for the same parameter. If a value being assigned to the +parameter during model evaluation is not in at least one of specified +supersets, an error is raised. + +The assign ({\tt:=}) attribute specifies a numeric or symbolic +expression used to compute a value assigned to the parameter (if it is +a simple parameter) or its member (if the parameter is an array). If +the assign attribute is specified, the parameter is {\it computable} +and therefore needs no data to be provided in the data section. If the +assign attribute is not specified, the parameter must be provided with +data in the data section. At most one assign or {\tt default} attribute +can be specified for the same parameter. + +The {\tt default} attribute specifies a numeric or symbolic expression +used to compute a value assigned to the parameter or its member +whenever no appropriate data are available in the data section. If +neither assign nor {\tt default} attribute is specified, missing data +will cause an error. + +\subsection{Variable statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][24pt]{345pt}{ +\hspace{6pt} {\tt var} {\it name} {\it alias} {\it domain} {\tt,} +{\it attrib} {\tt,} \dots {\tt,} {\it attrib} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it name} is a symbolic name of the +variable; +\item[\hspace*{54pt}] {\it alias} is an optional string literal, which +specifies an alias of the variable; +\item[\hspace*{54pt}] {\it domain} is an optional indexing expression, +which specifies a subscript domain of the variable; +\item[\hspace*{54pt}] {\it attrib}, \dots, {\it attrib} are optional +attributes of the variable. (Commae preceding attributes may be +omitted.) +\end{description} + +\noindent Optional attributes: + +\begin{description} +\item[{\tt integer}\hspace*{18.5pt}] restricts the variable to be +integer; +\item[{\tt binary}\hspace*{24pt}] restricts the variable to be binary; +\item[{\tt>=} {\it expression}]\hspace*{0pt}\\ +specifies an lower bound of the variable; +\item[{\tt<=} {\it expression}]\hspace*{0pt}\\ +specifies an upper bound of the variable; +\item[{\tt=} {\it expression}]\hspace*{0pt}\\ +specifies a fixed value of the variable; +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +var x >= 0; +var y{I,J}; +var make{p in prd}, integer, >= commit[p], <= market[p]; +var store{raw, 1..T+1} >= 0; +var z{i in I, j in J} >= i+j; +\end{verbatim} + +The variable statement declares a variable. If a subscript domain is +not specified, the variable is a simple (scalar) variable, otherwise it +is a $n$-dimensional array of elemental variables. + +Elemental variable(s) associated with the model variable (if it is a +simple variable) or its members (if it is an array) correspond to the +variables in the LP/MIP problem formulation (see Subsection +\ref{problem}, page \pageref{problem}). Note that only elemental +variables actually referenced in some constraints and/or objectives are +included in the LP/MIP problem instance to be generated. + +The type attributes {\tt integer} and {\tt binary} restrict the +variable to be integer or binary, respectively. If no type attribute is +specified, the variable is continuous. If all variables in the model +are continuous, the corresponding problem is of LP class. If there is +at least one integer or binary variable, the problem is of MIP class. + +The lower bound ({\tt>=}) attribute specifies a numeric expression for +computing an lower bound of the variable. At most one lower bound can +be specified. By default all variables (except binary ones) have no +lower bound, so if a variable is required to be non-negative, its zero +lower bound should be explicitly specified. + +The upper bound ({\tt<=}) attribute specifies a numeric expression for +computing an upper bound of the variable. At most one upper bound +attribute can be specified. + +The fixed value ({\tt=}) attribute specifies a numeric expression for +computing a value, at which the variable is fixed. This attribute +cannot be specified along with the bound attributes. + +\subsection{Constraint statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][96pt]{345pt}{ +\hspace{6pt} {\tt s.t.} {\it name} {\it alias} {\it domain} {\tt:} +{\it expression} {\tt,} {\tt=} {\it expression} {\tt;} + +\medskip + +\hspace{6pt} {\tt s.t.} {\it name} {\it alias} {\it domain} {\tt:} +{\it expression} {\tt,} {\tt<=} {\it expression} {\tt;} + +\medskip + +\hspace{6pt} {\tt s.t.} {\it name} {\it alias} {\it domain} {\tt:} +{\it expression} {\tt,} {\tt>=} {\it expression} {\tt;} + +\medskip + +\hspace{6pt} {\tt s.t.} {\it name} {\it alias} {\it domain} {\tt:} +{\it expression} {\tt,} {\tt<=} {\it expression} {\tt,} {\tt<=} +{\it expression} {\tt;} + +\medskip + +\hspace{6pt} {\tt s.t.} {\it name} {\it alias} {\it domain} {\tt:} +{\it expression} {\tt,} {\tt>=} {\it expression} {\tt,} {\tt>=} +{\it expression} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it name} is a symbolic name of the +constraint; +\item[\hspace*{54pt}] {\it alias} is an optional string literal, which +specifies an alias of the constraint; +\item[\hspace*{54pt}] {\it domain} is an optional indexing expression, +which specifies a subscript domain of the constraint; +\item[\hspace*{54pt}] {\it expression} is a linear expression used to +compute a component of the constraint. (Commae following expressions +may be omitted.) +\end{description} + +\begin{description} +\item[{\rm Note:}\hspace*{31pt}] The keyword {\tt s.t.} may be written +as {\tt subject to} or as {\tt subj to}, or may be omitted at all. +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +s.t. r: x + y + z, >= 0, <= 1; +limit{t in 1..T}: sum{j in prd} make[j,t] <= max_prd; +subject to balance{i in raw, t in 1..T}: store[i,t+1] - + store[i,t] - sum{j in prd} units[i,j] * make[j,t]; +subject to rlim 'regular-time limit' {t in time}: +sum{p in prd} pt[p] * rprd[p,t] <= 1.3 * dpp[t] * crews[t]; +\end{verbatim} + +The constraint statement declares a constraint. If a subscript domain +is not specified, the constraint is a simple (scalar) constraint, +otherwise it is a $n$-dimensional array of elemental constraints. + +Elemental constraint(s) associated with the model constraint (if it is +a simple constraint) or its members (if it is an array) correspond to +the linear constraints in the LP/MIP problem formulation (see +Subsection \ref{problem}, page \pageref{problem}). + +If the constraint has the form of equality or single inequality, i.e. +includes two expressions, one of which follows the colon and other +follows the relation sign {\tt=}, {\tt<=}, or {\tt>=}, both expressions +in the statement can be linear expressions. If the constraint has the +form of double inequality, i.e. includes three expressions, the middle +expression can be a linear expression while the leftmost and rightmost +ones can be only numeric expressions. + +Generating the model is, roughly speaking, generating its constraints, +which are always evaluated for the entire subscript domain. Evaluation +of the constraints leads, in turn, to evaluation of other model objects +such as sets, parameters, and variables. + +Constructing an actual linear constraint included in the problem +instance, which (constraint) corresponds to a particular elemental +constraint, is performed as follows. + +If the constraint has the form of equality or single inequality, +evaluation of both linear expressions gives two resultant linear forms: +$$\begin{array}{r@{\ }c@{\ }r@{\ }c@{\ }r@{\ }c@{\ }r@{\ }c@{\ }r} +f&=&a_1x_1&+&a_2x_2&+\dots+&a_nx_n&+&a_0,\\ +g&=&b_1x_1&+&a_2x_2&+\dots+&a_nx_n&+&b_0,\\ +\end{array}$$ +where $x_1$, $x_2$, \dots, $x_n$ are elemental variables; $a_1$, $a_2$, +\dots, $a_n$, $b_1$, $b_2$, \dots, $b_n$ are numeric coefficients; +$a_0$ and $b_0$ are constant terms. Then all linear terms of $f$ and +$g$ are carried to the left-hand side, and the constant terms are +carried to the right-hand side, that gives the final elemental +constraint in the standard form: +$$(a_1-b_1)x_1+(a_2-b_2)x_2+\dots+(a_n-b_n)x_n\left\{ +\begin{array}{@{}c@{}}=\\\leq\\\geq\\\end{array}\right\}b_0-a_0.$$ + +If the constraint has the form of double inequality, evaluation of the +middle linear expression gives the resultant linear form: +$$f=a_1x_1+a_2x_2+\dots+a_nx_n+a_0,$$ +and evaluation of the leftmost and rightmost numeric expressions gives +two numeric values $l$ and $u$, respectively. Then the constant term of +the linear form is carried to both left-hand and right-handsides that +gives the final elemental constraint in the standard form: +$$l-a_0\leq a_1x_1+a_2x_2+\dots+a_nx_n\leq u-a_0.$$ + +\subsection{Objective statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][44pt]{345pt}{ +\hspace{6pt} {\tt minimize} {\it name} {\it alias} {\it domain} {\tt:} +{\it expression} {\tt;} + +\medskip + +\hspace{6pt} {\tt maximize} {\it name} {\it alias} {\it domain} {\tt:} +{\it expression} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it name} is a symbolic name of the +objective; +\item[\hspace*{54pt}] {\it alias} is an optional string literal, which +specifies an alias of the objective; +\item[\hspace*{54pt}] {\it domain} is an optional indexing expression, +which specifies a subscript domain of the objective; +\item[\hspace*{54pt}] {\it expression} is a linear expression used to +compute the linear form of the objective. +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +minimize obj: x + 1.5 * (y + z); +maximize total_profit: sum{p in prd} profit[p] * make[p]; +\end{verbatim} + +The objective statement declares an objective. If a subscript domain is +not specified, the objective is a simple (scalar) objective. Otherwise +it is a $n$-dimensional array of elemental objectives. + +Elemental objective(s) associated with the model objective (if it is a +simple objective) or its members (if it is an array) correspond to +general linear constraints in the LP/MIP problem formulation (see +Subsection \ref{problem}, page \pageref{problem}). However, unlike +constraints the corresponding linear forms are free (unbounded). + +Constructing an actual linear constraint included in the problem +instance, which (constraint) corresponds to a particular elemental +constraint, is performed as follows. The linear expression specified in +the objective statement is evaluated that, gives the resultant linear +form: +$$f=a_1x_1+a_2x_2+\dots+a_nx_n+a_0,$$ +where $x_1$, $x_2$, \dots, $x_n$ are elemental variables; $a_1$, $a_2$, +\dots, $a_n$ are numeric coefficients; $a_0$ is the constant term. Then +the linear form is used to construct the final elemental constraint in +the standard form: +$$-\infty= 0 and y >= 0; +check sum{i in ORIG} supply[i] = sum{j in DEST} demand[j]; +check{i in I, j in 1..10}: S[i,j] in U[i] union V[j]; +\end{verbatim} + +The check statement allows checking the resultant value of an logical +expression specified in the statement. If the value is {\it false}, an +error is reported. + +If the subscript domain is not specified, the check is performed only +once. Specifying the subscript domain allows performing multiple checks +for every\linebreak $n$-tuple in the domain set. In the latter case the +logical expression may include dummy indices introduced in +corresponding indexing expression. + +\subsection{Display statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][24pt]{345pt}{ +\hspace{6pt} {\tt display} {\it domain} {\tt:} {\it item} {\tt,} +\dots {\tt,} {\it item} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it domain} is an optional indexing +expression, which specifies a subscript domain of the check statement; +\item[\hspace*{54pt}] {\it item}, \dots, {\it item} are items to be +displayed. (The colon preceding the first item may be omitted.) +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +display: 'x =', x, 'y =', y, 'z =', z; +display sqrt(x ** 2 + y ** 2 + z ** 2); +display{i in I, j in J}: i, j, a[i,j], b[i,j]; +\end{verbatim} + +\newpage + +The display statement evaluates all items specified in the statement +and writes their values to the terminal in plain text format. + +If a subscript domain is not specified, items are evaluated and then +displayed only once. Specifying the subscript domain causes items to be +evaluated and displayed for every $n$-tuple in the domain set. In the +latter case items may include dummy indices introduced in corresponding +indexing expression. + +An item to be displayed can be a model object (set, parameter, variable, +constraint, objective) or an expression. + +If the item is a computable object (i.e. a set or parameter provided +with the assign attribute), the object is evaluated over the entire +domain and then its content (i.e. the content of the object array) is +displayed. Otherwise, if the item is not a computable object, only its +current content (i.e. members actually generated during the model +evaluation) is displayed. + +If the item is an expression, the expression is evaluated and its +resultant value is displayed. + +\subsection{Printf statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][60pt]{345pt}{ +\hspace{6pt} {\tt printf} {\it domain} {\tt:} {\it format} {\tt,} +{\it expression} {\tt,} \dots {\tt,} {\it expression} {\tt;} + +\medskip + +\hspace{6pt} {\tt printf} {\it domain} {\tt:} {\it format} {\tt,} +{\it expression} {\tt,} \dots {\tt,} {\it expression} {\tt>} +{\it filename} {\tt;} + +\medskip + +\hspace{6pt} {\tt printf} {\it domain} {\tt:} {\it format} {\tt,} +{\it expression} {\tt,} \dots {\tt,} {\it expression} {\tt>>} +{\it filename} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it domain} is an optional indexing +expression, which specifies a subscript domain of the printf statement; +\item[\hspace*{54pt}] {\it format} is a symbolic expression whose value +specifies a format control string. (The colon preceding the format +expression may be omitted.) +\item[\hspace*{54pt}] {\it expression}, \dots, {\it expression} are +zero or more expressions whose values have to be formatted and printed. +Each expression must be of numeric, symbolic, or logical type. +\item[\hspace*{54pt}] {\it filename} is a symbolic expression whose +value specifies a name of a text file, to which the output is +redirected. The flag {\tt>} means creating a new empty file while the +flag {\tt>>} means appending the output to an existing file. If no file +name is specified, the output is written to the terminal. +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +printf 'Hello, world!\n'; +printf: "x = %.3f; y = %.3f; z = %.3f\n", + x, y, z > "result.txt"; +printf{i in I, j in J}: "flow from %s to %s is %d\n", + i, j, x[i,j] >> result_file & ".txt"; +\end{verbatim} + +\newpage + +\begin{verbatim} +printf{i in I} 'total flow from %s is %g\n', + i, sum{j in J} x[i,j]; +printf{k in K} "x[%s] = " & (if x[k] < 0 then "?" else "%g"), + k, x[k]; +\end{verbatim} + +The printf statement is similar to the display statement, however, it +allows formatting data to be written. + +If a subscript domain is not specified, the printf statement is +executed only once. Specifying a subscript domain causes executing the +printf statement for every $n$-tuple in the domain set. In the latter +case the format and expression may include dummy indices introduced in +corresponding indexing expression. + +The format control string is a value of the symbolic expression +{\it format} specified in the printf statement. It is composed of zero +or more directives as follows: ordinary characters (not {\tt\%}), which +are copied unchanged to the output stream, and conversion +specifications, each of which causes evaluating corresponding +expression specified in the printf statement, formatting it, and +writing its resultant value to the output stream. + +Conversion specifications that may be used in the format control string +are the following: {\tt d}, {\tt i}, {\tt f}, {\tt F}, {\tt e}, {\tt E}, +{\tt g}, {\tt G}, and {\tt s}. These specifications have the same +syntax and semantics as in the C programming language. + +\subsection{For statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][44pt]{345pt}{ +\hspace{6pt} {\tt for} {\it domain} {\tt:} {\it statement} {\tt;} + +\medskip + +\hspace{6pt} {\tt for} {\it domain} {\tt:} {\tt\{} {\it statement} +\dots {\it statement} {\tt\}} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it domain} is an indexing +expression which specifies a subscript domain of the for statement. +(The colon following the indexing expression may be omitted.) +\item[\hspace*{54pt}] {\it statement} is a statement, which should be +executed under control of the for statement; +\item[\hspace*{54pt}] {\it statement}, \dots, {\it statement} is a +sequence of statements (enclosed in curly braces), which should be +executed under control of the for statement. +\end{description} + +\begin{description} +\item[{\rm Note:}\hspace*{31pt}] Only the following statements can be +used within the for statement: check, display, printf, and another for. +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +for {(i,j) in E: i != j} +{ printf "flow from %s to %s is %g\n", i, j, x[i,j]; + check x[i,j] >= 0; +} +\end{verbatim} + +\newpage + +\begin{verbatim} +for {i in 1..n} +{ for {j in 1..n} printf " %s", if x[i,j] then "Q" else "."; + printf("\n"); +} +for {1..72} printf("*"); +\end{verbatim} + +The for statement causes a statement or a sequence of statements +specified as part of the for statement to be executed for every +$n$-tuple in the domain set. Thus, statements within the for statement +may include dummy indices introduced in corresponding indexing +expression. + +\subsection{Table statement} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][68pt]{345pt}{ +\hspace{6pt} {\tt table} {\it name} {\it alias} {\tt IN} {\it driver} +{\it arg} \dots {\it arg} {\tt:} + +\hspace{6pt} {\tt\ \ \ \ \ } {\it set} {\tt<-} {\tt[} {\it fld} {\tt,} +\dots {\tt,} {\it fld} {\tt]} {\tt,} {\it par} {\tt\textasciitilde} +{\it fld} {\tt,} \dots {\tt,} {\it par} {\tt\textasciitilde} {\it fld} +{\tt;} + +\medskip + +\hspace{6pt} {\tt table} {\it name} {\it alias} {\it domain} {\tt OUT} +{\it driver} {\it arg} \dots {\it arg} {\tt:} + +\hspace{6pt} {\tt\ \ \ \ \ } {\it expr} {\tt\textasciitilde} {\it fld} +{\tt,} \dots {\tt,} {\it expr} {\tt\textasciitilde} {\it fld} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it name} is a symbolic name of the +table; +\item[\hspace*{54pt}] {\it alias} is an optional string literal, which +specifies an alias of the table; +\item[\hspace*{54pt}] {\it domain} is an indexing expression, which +specifies a subscript domain of the (output) table; +\item[\hspace*{54pt}] {\tt IN} means reading data from the input table; +\item[\hspace*{54pt}] {\tt OUT} means writing data to the output table; +\item[\hspace*{54pt}] {\it driver} is a symbolic expression, which +specifies the driver used to access the table (for details see Section +\ref{drivers}, page \pageref{drivers}); +\item[\hspace*{54pt}] {\it arg} is an optional symbolic expression, +which is an argument pass\-ed to the table driver. This symbolic +expression must not include dummy indices specified in the domain; +\item[\hspace*{54pt}] {\it set} is the name of an optional simple set +called {\it control set}. It can be omitted along with the delimiter +{\tt<-}; +\item[\hspace*{54pt}] {\it fld} is a field name. Within square brackets +at least one field should be specified. The field name following +a parameter name or expression is optional and can be omitted along +with the delimiter {\tt\textasciitilde}, in which case the name of +corresponding model object is used as the field name; +\item[\hspace*{54pt}] {\it par} is a symbolic name of a model parameter; +\item[\hspace*{54pt}] {\it expr} is a numeric or symbolic expression. +\end{description} + +\newpage + +\noindent{\bf Examples} + +\begin{verbatim} +table data IN "CSV" "data.csv": + S <- [FROM,TO], d~DISTANCE, c~COST; +table result{(f,t) in S} OUT "CSV" "result.csv": + f~FROM, t~TO, x[f,t]~FLOW; +\end{verbatim} + +The table statement allows reading data from a table into model +objects such as sets and (non-scalar) parameters as well as writing +data from the model to a table. + +\subsubsection{Table structure} + +A {\it data table} is an (unordered) set of {\it records}, where each +record consists of the same number of {\it fields}, and each field is +provided with a unique symbolic name called the {\it field name}. For +example: + +\bigskip + +\begin{tabular}{@{\hspace*{38mm}}c@{\hspace*{11mm}}c@{\hspace*{10mm}}c +@{\hspace*{9mm}}c} +First&Second&&Last\\ +field&field&.\ \ .\ \ .&field\\ +$\downarrow$&$\downarrow$&&$\downarrow$\\ +\end{tabular} + +\begin{tabular}{ll@{}} +Table header&$\rightarrow$\\ +First record&$\rightarrow$\\ +Second record&$\rightarrow$\\ +\\ +\hfil .\ \ .\ \ .\\ +\\ +Last record&$\rightarrow$\\ +\end{tabular} +\begin{tabular}{|l|l|c|c|} +\hline +{\tt FROM}&{\tt TO}&{\tt DISTANCE}&{\tt COST}\\ +\hline +{\tt Seattle} &{\tt New-York}&{\tt 2.5}&{\tt 0.12}\\ +{\tt Seattle} &{\tt Chicago} &{\tt 1.7}&{\tt 0.08}\\ +{\tt Seattle} &{\tt Topeka} &{\tt 1.8}&{\tt 0.09}\\ +{\tt San-Diego}&{\tt New-York}&{\tt 2.5}&{\tt 0.15}\\ +{\tt San-Diego}&{\tt Chicago} &{\tt 1.8}&{\tt 0.10}\\ +{\tt San-Diego}&{\tt Topeka} &{\tt 1.4}&{\tt 0.07}\\ +\hline +\end{tabular} + +\subsubsection{Reading data from input table} + +The input table statement causes reading data from the specified table +record by record. + +Once a next record has been read, numeric or symbolic values of fields, +whose names are enclosed in square brackets in the table statement, are +gathered into $n$-tuple, and if the control set is specified in the +table statement, this $n$-tuple is added to it. Besides, a numeric or +symbolic value of each field associated with a model parameter is +assigned to the parameter member identified by subscripts, which are +components of the $n$-tuple just read. + +For example, the following input table statement: + +\medskip + +\noindent\hfil +\verb|table data IN "...": S <- [FROM,TO], d~DISTANCE, c~COST;| + +\medskip + +\noindent +causes reading values of four fields named {\tt FROM}, {\tt TO}, +{\tt DISTANCE}, and {\tt COST} from each record of the specified table. +Values of fields {\tt FROM} and {\tt TO} give a pair $(f,t)$, which is +added to the control set {\tt S}. The value of field {\tt DISTANCE} is +assigned to parameter member ${\tt d}[f,t]$, and the value of field +{\tt COST} is assigned to parameter member ${\tt c}[f,t]$. + +Note that the input table may contain extra fields whose names are not +specified in the table statement, in which case values of these fields +on reading the table are ignored. + +\subsubsection{Writing data to output table} + +The output table statement causes writing data to the specified table. +Note that some drivers (namely, CSV and xBASE) destroy the output table +before writing data, i.e. delete all its existing records. + +Each $n$-tuple in the specified domain set generates one record written +to the output table. Values of fields are numeric or symbolic values of +corresponding expressions specified in the table statement. These +expressions are evaluated for each $n$-tuple in the domain set and, +thus, may include dummy indices introduced in the corresponding indexing +expression. + +For example, the following output table statement: + +\medskip + +\noindent +\verb| table result{(f,t) in S} OUT "...": f~FROM, t~TO, x[f,t]~FLOW;| + +\medskip + +\noindent +causes writing records, by one record for each pair $(f,t)$ in set +{\tt S}, to the output table, where each record consists of three +fields named {\tt FROM}, {\tt TO}, and {\tt FLOW}. The values written +to fields {\tt FROM} and {\tt TO} are current values of dummy indices +{\tt f} and {\tt t}, and the value written to field {\tt FLOW} is +a value of member ${\tt x}[f,t]$ of corresponding subscripted parameter +or variable. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Model data} + +{\it Model data} include elemental sets, which are ``values'' of model +sets, and numeric and symbolic values of model parameters. + +In MathProg there are two different ways to saturate model sets and +parameters with data. One way is simply providing necessary data using +the assign attribute. However, in many cases it is more practical to +separate the model itself and particular data needed for the model. For +the latter reason in MathProg there is another way, when the model +description is divided into two parts: model section and data section. + +A {\it model section} is a main part of the model description that +contains declarations of all model objects and is common for all +problems based on that model. + +A {\it data section} is an optional part of the model description that +contains model data specific for a particular problem. + +In MathProg model and data sections can be placed either in one text +file or in two separate text files. + +1. If both model and data sections are placed in one file, the file is +composed as follows: + +\bigskip + +\noindent\hfil +\framebox{\begin{tabular}{l} +{\it statement}{\tt;}\\ +{\it statement}{\tt;}\\ +\hfil.\ \ .\ \ .\\ +{\it statement}{\tt;}\\ +{\tt data;}\\ +{\it data block}{\tt;}\\ +{\it data block}{\tt;}\\ +\hfil.\ \ .\ \ .\\ +{\it data block}{\tt;}\\ +{\tt end;} +\end{tabular}} + +\bigskip + +2. If the model and data sections are placed in two separate files, the +files are composed as follows: + +\bigskip + +\noindent\hfil +\begin{tabular}{@{}c@{}} +\framebox{\begin{tabular}{l} +{\it statement}{\tt;}\\ +{\it statement}{\tt;}\\ +\hfil.\ \ .\ \ .\\ +{\it statement}{\tt;}\\ +{\tt end;}\\ +\end{tabular}}\\ +\\\\Model file\\ +\end{tabular} +\hspace{32pt} +\begin{tabular}{@{}c@{}} +\framebox{\begin{tabular}{l} +{\tt data;}\\ +{\it data block}{\tt;}\\ +{\it data block}{\tt;}\\ +\hfil.\ \ .\ \ .\\ +{\it data block}{\tt;}\\ +{\tt end;}\\ +\end{tabular}}\\ +\\Data file\\ +\end{tabular} + +\bigskip + +\begin{description} +\item[{\rm Note:}\hspace*{31pt}] If the data section is placed in a +separate file, the keyword {\tt data} is optional and may be omitted +along with the semicolon that follows it. +\end{description} + +\subsection{Coding data section} + +The {\it data section} is a sequence of data blocks in various formats, +which are discussed in following subsections. The order, in which data +blocks follow in the data section, may be arbitrary, not necessarily +the same, in which corresponding model objects follow in the model +section. + +The rules of coding the data section are commonly the same as the rules +of coding the model description (see Subsection \ref{coding}, page +\pageref{coding}), i.e. data blocks are composed from basic lexical +units such as symbolic names, numeric and string literals, keywords, +delimiters, and comments. However, for the sake of convenience and +improving readability there is one deviation from the common rule: if +a string literal consists of only alphanumeric characters (including +the underscore character), the signs {\tt+} and {\tt-}, and/or the +decimal point, it may be coded without bordering by (single or double) +quotes. + +All numeric and symbolic material provided in the data section is coded +in the form of numbers and symbols, i.e. unlike the model section +no expressions are allowed in the data section. Nevertheless, the signs +{\tt+} and {\tt-} can precede numeric literals to allow coding signed +numeric quantities, in which case there must be no white-space +characters between the sign and following numeric literal (if there is +at least one white-space, the sign and following numeric literal are +recognized as two different lexical units). + +\subsection{Set data block} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][44pt]{345pt}{ +\hspace{6pt} {\tt set} {\it name} {\tt,} {\it record} {\tt,} \dots +{\tt,} {\it record} {\tt;} + +\medskip + +\hspace{6pt} {\tt set} {\it name} {\tt[} {\it symbol} {\tt,} \dots +{\tt,} {\it symbol} {\tt]} {\tt,} {\it record} {\tt,} \dots {\tt,} +{\it record} {\tt;} +}} + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it name} is a symbolic name of the +set; +\item[\hspace*{54pt}] {\it symbol}, \dots, {\it symbol} are subscripts, +which specify a particular member of the set (if the set is an array, +i.e. a set of sets); +\item[\hspace*{54pt}] {\it record}, \dots, {\it record} are data +records. +\end{description} + +\begin{description} +\item[{\rm Note:}\hspace*{31pt}] Commae preceding data records may be +omitted. +\end{description} + +\noindent Data records: + +\begin{description} +\item[{\tt :=}\hspace*{45pt}] is a non-significant data record, which +may be used freely to improve readability; +\item[{\tt(} {\it slice} {\tt)}\hspace*{18.5pt}] specifies a slice; +\item[{\it simple-data}\hspace*{5.5pt}] specifies set data in the +simple format; +\item[{\tt:} {\it matrix-data}]\hspace*{0pt}\\ +specifies set data in the matrix format; +\item[{\tt(tr)} {\tt:} {\it matrix-data}]\hspace*{0pt}\\ +specifies set data in the transposed matrix format. (In this case the +colon following the keyword {\tt(tr)} may be omitted.) +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +set month := Jan Feb Mar Apr May Jun; +set month "Jan", "Feb", "Mar", "Apr", "May", "Jun"; +set A[3,Mar] := (1,2) (2,3) (4,2) (3,1) (2,2) (4,4) (3,4); +set A[3,'Mar'] := 1 2 2 3 4 2 3 1 2 2 4 4 2 4; +set A[3,'Mar'] : 1 2 3 4 := + 1 - + - - + 2 - + + - + 3 + - - + + 4 - + - + ; +set B := (1,2,3) (1,3,2) (2,3,1) (2,1,3) (1,2,2) (1,1,1) (2,1,1); +set B := (*,*,*) 1 2 3, 1 3 2, 2 3 1, 2 1 3, 1 2 2, 1 1 1, 2 1 1; +set B := (1,*,2) 3 2 (2,*,1) 3 1 (1,2,3) (2,1,3) (1,1,1); +set B := (1,*,*) : 1 2 3 := + 1 + - - + 2 - + + + 3 - + - + (2,*,*) : 1 2 3 := + 1 + - + + 2 - - - + 3 + - - ; +\end{verbatim} + +\noindent(In these examples {\tt month} is a simple set of singlets, +{\tt A} is a 2-dimensional array of doublets, and {\tt B} is a simple +set of triplets. Data blocks for the same set are equivalent in the +sense that they specify the same data in different formats.) + +\medskip + +The {\it set data block} is used to specify a complete elemental set, +which is assigned to a set (if it is a simple set) or one of its +members (if the set is an array of sets).\footnote{There is another way +to specify data for a simple set along with data for parameters. This +feature is discussed in the next subsection.} + +Data blocks can be specified only for non-computable sets, i.e. for +sets, which have no assign ({\tt:=}) attribute in the corresponding set +statements. + +If the set is a simple set, only its symbolic name should be specified +in the header of the data block. Otherwise, if the set is a +$n$-dimensional array, its symbolic name should be provided with a +complete list of subscripts separated by commae and enclosed in square +brackets to specify a particular member of the set array. The number of +subscripts must be the same as the dimension of the set array, where +each subscript must be a number or symbol. + +An elemental set defined in the set data block is coded as a sequence +of data records described below.\footnote{{\it Data record} is simply a +technical term. It does not mean that data records have any special +formatting.} + +\newpage + +\subsubsection{Assign data record} + +The {\it assign} ({\tt:=}) {\it data record} is a non-signficant +element. It may be used for improving readability of data blocks. + +\subsubsection{Slice data record} + +The {\it slice data record} is a control record, which specifies a +{\it slice} of the elemental set defined in the data block. It has the +following syntactic form: + +\medskip + +\noindent\hfil +{\tt(} $s_1$ {\tt,} $s_2$ {\tt,} \dots {\tt,} $s_n$ {\tt)} + +\medskip + +\noindent where $s_1$, $s_2$, \dots, $s_n$ are components of the slice. + +Each component of the slice can be a number or symbol or the asterisk +({\tt*}). The number of components in the slice must be the same as the +dimension of $n$-tuples in the elemental set to be defined. For +instance, if the elemental set contains 4-tuples (quadruplets), the +slice must have four components. The number of asterisks in the slice +is called the {\it slice dimension}. + +The effect of using slices is the following. If a $m$-dimensional slice +(i.e. a slice having $m$ asterisks) is specified in the data block, all +subsequent data records must specify tuples of the dimension $m$. +Whenever a $m$-tuple is encountered, each asterisk in the slice is +replaced by corresponding components of the $m$-tuple that gives the +resultant $n$-tuple, which is included in the elemental set to be +defined. For example, if the slice $(a,*,1,2,*)$ is in effect, and +2-tuple $(3,b)$ is encountered in a subsequent data record, the +resultant 5-tuple included in the elemental set is $(a,3,1,2,b)$. + +The slice having no asterisks itself defines a complete $n$-tuple, +which is included in the elemental set. + +Being once specified the slice effects until either a new slice or the +end of data block is encountered. Note that if no slice is specified in +the data block, one, components of which are all asterisks, is assumed. + +\subsubsection{Simple data record} + +The {\it simple data record} defines one $n$-tuple in a simple format +and has the following syntactic form: + +\medskip + +\noindent\hfil +$t_1$ {\tt,} $t_2$ {\tt,} \dots {\tt,} $t_n$ + +\medskip + +\noindent where $t_1$, $t_2$, \dots, $t_n$ are components of the +$n$-tuple. Each component can be a number or symbol. Commae between +components are optional and may be omitted. + +\subsubsection{Matrix data record} + +The {\it matrix data record} defines several 2-tuples (doublets) in +a matrix format and has the following syntactic form: + +\newpage + +$$\begin{array}{cccccc} +\mbox{{\tt:}}&c_1&c_2&\dots&c_n&\mbox{{\tt:=}}\\ +r_1&a_{11}&a_{12}&\dots&a_{1n}&\\ +r_2&a_{21}&a_{22}&\dots&a_{2n}&\\ +\multicolumn{5}{c}{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .}&\\ +r_m&a_{m1}&a_{m2}&\dots&a_{mn}&\\ +\end{array}$$ +where $r_1$, $r_2$, \dots, $r_m$ are numbers and/or symbols +corresponding to rows of the matrix; $c_1$, $c_2$, \dots, $c_n$ are +numbers and/or symbols corresponding to columns of the matrix, $a_{11}$, +$a_{12}$, \dots, $a_{mn}$ are matrix elements, which can be either +{\tt+} or {\tt-}. (In this data record the delimiter {\tt:} preceding +the column list and the delimiter {\tt:=} following the column list +cannot be omitted.) + +Each element $a_{ij}$ of the matrix data block (where $1\leq i\leq m$, +$1\leq j\leq n$) corresponds to 2-tuple $(r_i,c_j)$. If $a_{ij}$ is the +plus sign ({\tt+}), that 2-tuple (or a longer $n$-tuple, if a slice is +used) is included in the elemental set. Otherwise, if $a_{ij}$ is the +minus sign ({\tt-}), that 2-tuple is not included in the elemental set. + +Since the matrix data record defines 2-tuples, either the elemental set +must consist of 2-tuples or the slice currently used must be +2-dimensional. + +\subsubsection{Transposed matrix data record} + +The {\it transposed matrix data record} has the following syntactic +form: +$$\begin{array}{cccccc} +\mbox{{\tt(tr) :}}&c_1&c_2&\dots&c_n&\mbox{{\tt:=}}\\ +r_1&a_{11}&a_{12}&\dots&a_{1n}&\\ +r_2&a_{21}&a_{22}&\dots&a_{2n}&\\ +\multicolumn{5}{c}{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .}&\\ +r_m&a_{m1}&a_{m2}&\dots&a_{mn}&\\ +\end{array}$$ +(In this case the delimiter {\tt:} following the keyword {\tt(tr)} is +optional and may be omitted.) + +This data record is completely analogous to the matrix data record (see +above) with only exception that in this case each element $a_{ij}$ of +the matrix corresponds to 2-tuple $(c_j,r_i)$ rather than $(r_i,c_j)$. + +Being once specified the {\tt(tr)} indicator affects all subsequent +data records until either a slice or the end of data block is +encountered. + +\subsection{Parameter data block} + +\medskip + +\framebox[345pt][l]{ +\parbox[c][80pt]{345pt}{ +\hspace{6pt} {\tt param} {\it name} {\tt,} {\it record} {\tt,} \dots +{\tt,} {\it record} {\tt;} + +\medskip + +\hspace{6pt} {\tt param} {\it name} {\tt default} {\it value} {\tt,} +{\it record} {\tt,} \dots {\tt,} {\it record} {\tt;} + +\medskip + +\hspace{6pt} {\tt param} {\tt:} {\it tabbing-data} {\tt;} + +\medskip + +\hspace{6pt} {\tt param} {\tt default} {\it value} {\tt:} +{\it tabbing-data} {\tt;} +}} + +\newpage + +\setlength{\leftmargini}{60pt} + +\begin{description} +\item[{\rm Where:}\hspace*{23pt}] {\it name} is a symbolic name of the +parameter; +\item[\hspace*{54pt}] {\it value} is an optional default value of the +parameter; +\item[\hspace*{54pt}] {\it record}, \dots, {\it record} are data +records; +\item[\hspace*{54pt}] {\it tabbing-data} specifies parameter data in +the tabbing format. +\end{description} + +\begin{description} +\item[{\rm Note:}\hspace*{31pt}] Commae preceding data records may be +omitted. +\end{description} + +\noindent Data records: + +\begin{description} +\item[{\tt :=}\hspace*{45pt}] is a non-significant data record, which +may be used freely to improve readability; +\item[{\tt[} {\it slice} {\tt]}\hspace*{18.5pt}] specifies a slice; +\item[{\it plain-data}\hspace*{11pt}] specifies parameter data in the +plain format; +\item[{\tt:} {\it tabular-data}]\hspace*{0pt}\\ +specifies parameter data in the tabular format; +\item[{\tt(tr)} {\tt:} {\it tabular-data}]\hspace*{0pt}\\ +specifies set data in the transposed tabular format. (In this case the +colon following the keyword {\tt(tr)} may be omitted.) +\end{description} + +\noindent{\bf Examples} + +\begin{verbatim} +param T := 4; +param month := 1 'Jan' 2 'Feb' 3 'Mar' 4 'Apr' 5 'May'; +param month := [1] Jan, [2] Feb, [3] Mar, [4] Apr, [5] May; +param day := [Sun] 0, [Mon] 1, [Tue] 2, [Wed] 3, [Thu] 4, + [Fri] 5, [Sat] 6; +param init_stock := iron 7.32 nickel 35.8; +param init_stock [*] iron 7.32, nickel 35.8; +param cost [iron] .025 [nickel] .03; +param value := iron -.1, nickel .02; +param : init_stock cost value := + iron 7.32 .025 -.1 + nickel 35.8 .03 .02 ; +param : raw : init_stock cost value := + iron 7.32 .025 -.1 + nickel 35.8 .03 .02 ; +param demand default 0 (tr) + : FRA DET LAN WIN STL FRE LAF := + bands 300 . 100 75 . 225 250 + coils 500 750 400 250 . 850 500 + plate 100 . . 50 200 . 250 ; +\end{verbatim} + +\newpage + +\begin{verbatim} +param trans_cost := + [*,*,bands]: FRA DET LAN WIN STL FRE LAF := + GARY 30 10 8 10 11 71 6 + CLEV 22 7 10 7 21 82 13 + PITT 19 11 12 10 25 83 15 + [*,*,coils]: FRA DET LAN WIN STL FRE LAF := + GARY 39 14 11 14 16 82 8 + CLEV 27 9 12 9 26 95 17 + PITT 24 14 17 13 28 99 20 + [*,*,plate]: FRA DET LAN WIN STL FRE LAF := + GARY 41 15 12 16 17 86 8 + CLEV 29 9 13 9 28 99 18 + PITT 26 14 17 13 31 104 20 ; +\end{verbatim} + +The {\it parameter data block} is used to specify complete data for a +parameter (or parameters, if data are specified in the tabbing format). + +Data blocks can be specified only for non-computable parameters, i.e. +for parameters, which have no assign ({\tt:=}) attribute in the +corresponding parameter statements. + +Data defined in the parameter data block are coded as a sequence of +data records described below. Additionally the data block can be +provided with the optional {\tt default} attribute, which specifies a +default numeric or symbolic value of the parameter (parameters). This +default value is assigned to the parameter or its members, if +no appropriate value is defined in the parameter data block. The +{\tt default} attribute cannot be used, if it is already specified in +the corresponding parameter statement. + +\subsubsection{Assign data record} + +The {\it assign} ({\tt:=}) {\it data record} is a non-signficant +element. It may be used for improving readability of data blocks. + +\subsubsection{Slice data record} + +The {\it slice data record} is a control record, which specifies a +{\it slice} of the parameter array. It has the following syntactic form: + +\medskip + +\noindent\hfil +{\tt[} $s_1$ {\tt,} $s_2$ {\tt,} \dots {\tt,} $s_n$ {\tt]} + +\medskip + +\noindent where $s_1$, $s_2$, \dots, $s_n$ are components of the slice. + +Each component of the slice can be a number or symbol or the asterisk +({\tt*}). The number of components in the slice must be the same as the +dimension of the parameter. For instance, if the parameter is a +4-dimensional array, the slice must have four components. The number of +asterisks in the slice is called the {\it slice dimension}. + +The effect of using slices is the following. If a $m$-dimensional slice +(i.e. a slice having $m$ asterisks) is specified in the data block, all +subsequent data records must specify subscripts of the parameter +members as if the parameter were $m$-dimensional, not $n$-dimensional. + +Whenever $m$ subscripts are encountered, each asterisk in the slice is +replaced by corresponding subscript that gives $n$ subscripts, which +define the actual parameter member. For example, if the slice +$[a,*,1,2,*]$ is in effect, and subscripts 3 and $b$ are encountered in +a subsequent data record, the complete subscript list used to choose a +parameter member is $[a,3,1,2,b]$. + +It is allowed to specify a slice having no asterisks. Such slice itself +defines a complete subscript list, in which case the next data record +should define only a single value of corresponding parameter member. + +Being once specified the slice effects until either a new slice or the +end of data block is encountered. Note that if no slice is specified in +the data block, one, components of which are all asterisks, is assumed. + +\subsubsection{Plain data record} + +The {\it plain data record} defines a subscript list and a single value +in the plain format. This record has the following syntactic form: + +\medskip + +\noindent\hfil +$t_1$ {\tt,} $t_2$ {\tt,} \dots {\tt,} $t_n$ {\tt,} $v$ + +\medskip + +\noindent where $t_1$, $t_2$, \dots, $t_n$ are subscripts, and $v$ is a +value. Each subscript as well as the value can be a number or symbol. +Commae following subscripts are optional and may be omitted. + +In case of 0-dimensional parameter or slice the plain data record has +no subscripts and consists of a single value only. + +\subsubsection{Tabular data record} + +The {\it tabular data record} defines several values, where each value +is provided with two subscripts. This record has the following +syntactic form: +$$\begin{array}{cccccc} +\mbox{{\tt:}}&c_1&c_2&\dots&c_n&\mbox{{\tt:=}}\\ +r_1&a_{11}&a_{12}&\dots&a_{1n}&\\ +r_2&a_{21}&a_{22}&\dots&a_{2n}&\\ +\multicolumn{5}{c}{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .}&\\ +r_m&a_{m1}&a_{m2}&\dots&a_{mn}&\\ +\end{array}$$ +where $r_1$, $r_2$, \dots, $r_m$ are numbers and/or symbols +corresponding to rows of the table; $c_1$, $c_2$, \dots, $c_n$ are +numbers and/or symbols corresponding to columns of the table, $a_{11}$, +$a_{12}$, \dots, $a_{mn}$ are table elements. Each element can be a +number or symbol or the single decimal point ({\tt.}). (In this data +record the delimiter {\tt:} preceding the column list and the delimiter +{\tt:=} following the column list cannot be omitted.) + +Each element $a_{ij}$ of the tabular data block ($1\leq i\leq m$, +$1\leq j\leq n$) defines two subscripts, where the first subscript is +$r_i$, and the second one is $c_j$. These subscripts are used in +conjunction with the current slice to form the complete subscript list +that identifies a particular member of the parameter array. If $a_{ij}$ +is a number or symbol, this value is assigned to the parameter member. +However, if $a_{ij}$ is the single decimal point, the member is +assigned a default value specified either in the parameter data block +or in the parameter statement, or, if no default value is specified, +the member remains undefined. + +Since the tabular data record provides two subscripts for each value, +either the parameter or the slice currently used must be 2-dimensional. + +\subsubsection{Transposed tabular data record} + +The {\it transposed tabular data record} has the following syntactic +form: +$$\begin{array}{cccccc} +\mbox{{\tt(tr) :}}&c_1&c_2&\dots&c_n&\mbox{{\tt:=}}\\ +r_1&a_{11}&a_{12}&\dots&a_{1n}&\\ +r_2&a_{21}&a_{22}&\dots&a_{2n}&\\ +\multicolumn{5}{c}{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .}&\\ +r_m&a_{m1}&a_{m2}&\dots&a_{mn}&\\ +\end{array}$$ +(In this case the delimiter {\tt:} following the keyword {\tt(tr)} is +optional and may be omitted.) + +This data record is completely analogous to the tabular data record +(see above) with only exception that the first subscript defined by +element $a_{ij}$ is $c_j$ while the second one is $r_i$. + +Being once specified the {\tt(tr)} indicator affects all subsequent +data records until either a slice or the end of data block is +encountered. + +\subsubsection{Tabbing data format} + +The parameter data block in the {\it tabbing format} has the following +syntactic form: +$$\begin{array}{p{12pt}@{\ }l@{\ }c@{\ }l@{\ }c@{\ }l@{\ }r@{\ }l@{\ }c +@{\ }l@{\ }c@{\ }l@{\ }l} +\multicolumn{7}{@{}c@{}}{\mbox{\tt param}\ \mbox{\tt default}\ \mbox +{\it value}\ \mbox{\tt:}\ \mbox{\it s}\ \mbox{\tt:}}& +p_1&\mbox{\tt,}&p_2&\mbox{\tt,} \dots \mbox{\tt,}&p_k&\mbox{\tt:=}\\ +&t_{11}&\mbox{\tt,}&t_{12}&\mbox{\tt,} \dots \mbox{\tt,}&t_{1n}& +\mbox{\tt,}&a_{11}&\mbox{\tt,}&a_{12}&\mbox{\tt,} \dots \mbox{\tt,}& +a_{1k}\\ +&t_{21}&\mbox{\tt,}&t_{22}&\mbox{\tt,} \dots \mbox{\tt,}&t_{2n}& +\mbox{\tt,}&a_{21}&\mbox{\tt,}&a_{22}&\mbox{\tt,} \dots \mbox{\tt,}& +a_{2k}\\ +\multicolumn{13}{c} +{.\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .\ \ .}\\ +&t_{m1}&\mbox{\tt,}&t_{m2}&\mbox{\tt,} \dots \mbox{\tt,}&t_{mn}& +\mbox{\tt,}&a_{m1}&\mbox{\tt,}&a_{m2}&\mbox{\tt,} \dots \mbox{\tt,}& +a_{mk}&\mbox{\tt;}\\ +\end{array}$$ + +{\it Notes:} + +1. The keyword {\tt default} may be omitted along with a value +following it. + +2. Symbolic name {\tt s} may be omitted along with the colon following +it. + +3. All comae are optional and may be omitted. + +\medskip + +The data block in the tabbing format shown above is exactly equivalent +to the following data blocks for $j=1,2,\dots,k$: + +\medskip + +{\tt set} {\it s} {\tt:=} +{\tt(}$t_{11}${\tt,}$t_{12}${\tt,}\dots{\tt,}$t_{1n}${\tt)} +{\tt(}$t_{21}${\tt,}$t_{22}${\tt,}\dots{\tt,}$t_{2n}${\tt)} \dots +{\tt(}$t_{m1}${\tt,}$t_{m2}${\tt,}\dots{\tt,}$t_{mn}${\tt)} {\tt;} + +{\tt param} $p_j$ {\tt default} {\it value} {\tt:=} + +$\!${\tt[}$t_{11}${\tt,}$t_{12}${\tt,}\dots{\tt,}$t_{1n}${\tt]} +$a_{1j}$ +{\tt[}$t_{21}${\tt,}$t_{22}${\tt,}\dots{\tt,}$t_{2n}${\tt]} $a_{2j}$ +\dots +{\tt[}$t_{m1}${\tt,}$t_{m2}${\tt,}\dots{\tt,}$t_{mn}${\tt]} $a_{mj}$ +{\tt;} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\appendix + +\newpage + +\section{Using suffixes} + +Suffixes can be used to retrieve additional values associated with +model variables, constraints, and objectives. + +A {\it suffix} consists of a period ({\tt.}) followed by a non-reserved +keyword. For example, if {\tt x} is a two-dimensional variable, +{\tt x[i,j].lb} is a numeric value equal to the lower bound of +elemental variable {\tt x[i,j]}, which (value) can be used everywhere +in expressions like a numeric parameter. + +For model variables suffixes have the following meaning: + +\medskip + +\begin{tabular}{@{}p{96pt}p{222pt}@{}} +{\tt.lb}&lower bound\\ +{\tt.ub}&upper bound\\ +{\tt.status}&status in the solution:\\ +&0 --- undefined\\ +&1 --- basic\\ +&2 --- non-basic on lower bound\\ +&3 --- non-basic on upper bound\\ +&4 --- non-basic free (unbounded) variable\\ +&5 --- non-basic fixed variable\\ +{\tt.val}&primal value in the solution\\ +{\tt.dual}&dual value (reduced cost) in the solution\\ +\end{tabular} + +\medskip + +For model constraints and objectives suffixes have the following +meaning: + +\medskip + +\begin{tabular}{@{}p{96pt}p{222pt}@{}} +{\tt.lb}&lower bound of the linear form\\ +{\tt.ub}&upper bound of the linear form\\ +{\tt.status}&status in the solution:\\ +&0 --- undefined\\ +&1 --- non-active\\ +&2 --- active on lower bound\\ +&3 --- active on upper bound\\ +&4 --- active free (unbounded) row\\ +&5 --- active equality constraint\\ +{\tt.val}&primal value of the linear form in the solution\\ +{\tt.dual}&dual value (reduced cost) of the linear form in the +solution\\ +\end{tabular} + +\medskip + +Note that suffixes {\tt.status}, {\tt.val}, and {\tt.dual} can be used +only below the solve statement. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Date and time functions} + +\noindent\hfil +by Andrew Makhorin \verb|| + +\noindent\hfil +and Heinrich Schuchardt \verb|| + +\subsection{Obtaining current calendar time} +\label{gmtime} + +To obtain the current calendar time in MathProg there exists the +function {\tt gmtime}. It has no arguments and returns the number of +seconds elapsed since 00:00:00 on January 1, 1970, Coordinated +Universal Time (UTC). For example: + +\medskip + +\verb| param utc := gmtime();| + +\medskip + +MathProg has no function to convert UTC time returned by the function +{\tt gmtime} to {\it local} calendar times. Thus, if you need to +determine the current local calendar time, you have to add to the UTC +time returned the time offset from UTC expressed in seconds. For +example, the time in Berlin during the winter is one hour ahead of UTC +that corresponds to the time offset +1 hour = +3600 secs, so the +current winter calendar time in Berlin may be determined as follows: + +\medskip + +\verb| param now := gmtime() + 3600;| + +\medskip + +\noindent Similarly, the summer time in Chicago (Central Daylight Time) +is five hours behind UTC, so the corresponding current local calendar +time may be determined as follows: + +\medskip + +\verb| param now := gmtime() - 5 * 3600;| + +\medskip + +Note that the value returned by {\tt gmtime} is volatile, i.e. being +called several times this function may return different values. + +\subsection{Converting character string to calendar time} +\label{str2time} + +The function {\tt str2time(}{\it s}{\tt,} {\it f}{\tt)} converts a +character string (timestamp) specified by its first argument {\it s}, +which must be a symbolic expression, to the calendar time suitable for +arithmetic calculations. The conversion is controlled by the specified +format string {\it f} (the second argument), which also must be a +symbolic expression. + +The result of conversion returned by {\tt str2time} has the same +meaning as values returned by the function {\tt gmtime} (see Subsection +\ref{gmtime}, page \pageref{gmtime}). Note that {\tt str2time} does +{\tt not} correct the calendar time returned for the local timezone, +i.e. being applied to 00:00:00 on January 1, 1970 it always returns 0. + +For example, the model statements: + +\medskip + +\verb| param s, symbolic, := "07/14/98 13:47";| + +\verb| param t := str2time(s, "%m/%d/%y %H:%M");| + +\verb| display t;| + +\medskip + +\noindent produce the following printout: + +\medskip + +\verb| t = 900424020| + +\medskip + +\noindent where the calendar time printed corresponds to 13:47:00 on +July 14, 1998. + +\newpage + +The format string passed to the function {\tt str2time} consists of +conversion specifiers and ordinary characters. Each conversion +specifier begins with a percent ({\tt\%}) character followed by a +letter. + +The following conversion specifiers may be used in the format string: + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%b}&The abbreviated month name (case insensitive). At least three +first letters of the month name must appear in the input string.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%d}&The day of the month as a decimal number (range 1 to 31). +Leading zero is permitted, but not required.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%h}&The same as {\tt\%b}.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%H}&The hour as a decimal number, using a 24-hour clock (range 0 +to 23). Leading zero is permitted, but not required.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%m}&The month as a decimal number (range 1 to 12). Leading zero is +permitted, but not required.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%M}&The minute as a decimal number (range 0 to 59). Leading zero +is permitted, but not required.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%S}&The second as a decimal number (range 0 to 60). Leading zero +is permitted, but not required.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%y}&The year without a century as a decimal number (range 0 to 99). +Leading zero is permitted, but not required. Input values in the range +0 to 68 are considered as the years 2000 to 2068 while the values 69 to +99 as the years 1969 to 1999.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%z}&The offset from GMT in ISO 8601 format.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%\%}&A literal {\tt\%} character.\\ +\end{tabular} + +\medskip + +All other (ordinary) characters in the format string must have a +matching character in the input string to be converted. Exceptions are +spaces in the input string which can match zero or more space +characters in the format string. + +If some date and/or time component(s) are missing in the format and, +therefore, in the input string, the function {\tt str2time} uses their +default values corresponding to 00:00:00 on January 1, 1970, that is, +the default value of the year is 1970, the default value of the month +is January, etc. + +The function {\tt str2time} is applicable to all calendar times in the +range 00:00:00 on January 1, 0001 to 23:59:59 on December 31, 4000 of +the Gregorian calendar. + +\subsection{Converting calendar time to character string} +\label{time2str} + +The function {\tt time2str(}{\it t}{\tt,} {\it f}{\tt)} converts the +calendar time specified by its first argument {\it t}, which must be a +numeric expression, to a character string (symbolic value). The +conversion is controlled by the specified format string {\it f} (the +second argument), which must be a symbolic expression. + +The calendar time passed to {\tt time2str} has the same meaning as +values returned by the function {\tt gmtime} (see Subsection +\ref{gmtime}, page \pageref{gmtime}). Note that {\tt time2str} does +{\it not} correct the specified calendar time for the local timezone, +i.e. the calendar time 0 always corresponds to 00:00:00 on January 1, +1970. + +For example, the model statements: + +\medskip + +\verb| param s, symbolic, := time2str(gmtime(), "%FT%TZ");| + +\verb| display s;| + +\medskip + +\noindent may produce the following printout: + +\medskip + +\verb| s = '2008-12-04T00:23:45Z'| + +\medskip + +\noindent which is a timestamp in the ISO format. + +The format string passed to the function {\tt time2str} consists of +conversion specifiers and ordinary characters. Each conversion +specifier begins with a percent ({\tt\%}) character followed by a +letter. + +The following conversion specifiers may be used in the format string: + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%a}&The abbreviated (2-character) weekday name.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%A}&The full weekday name.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%b}&The abbreviated (3-character) month name.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%B}&The full month name.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%C}&The century of the year, that is the greatest integer not +greater than the year divided by 100.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%d}&The day of the month as a decimal number (range 01 to 31).\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%D}&The date using the format \verb|%m/%d/%y|.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%e}&The day of the month like with \verb|%d|, but padded with +blank rather than zero.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%F}&The date using the format \verb|%Y-%m-%d|.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%g}&The year corresponding to the ISO week number, but without the +century (range 00 to 99). This has the same format and value as +\verb|%y|, except that if the ISO week number (see \verb|%V|) belongs +to the previous or next year, that year is used instead.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%G}&The year corresponding to the ISO week number. This has the +same format and value as \verb|%Y|, except that if the ISO week number +(see \verb|%V|) belongs to the previous or next year, that year is used +instead. +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%h}&The same as \verb|%b|.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%H}&The hour as a decimal number, using a 24-hour clock (range 00 +to 23).\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%I}&The hour as a decimal number, using a 12-hour clock (range 01 +to 12).\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%j}&The day of the year as a decimal number (range 001 to 366).\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%k}&The hour as a decimal number, using a 24-hour clock like +\verb|%H|, but padded with blank rather than zero.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%l}&The hour as a decimal number, using a 12-hour clock like +\verb|%I|, but padded with blank rather than zero. +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%m}&The month as a decimal number (range 01 to 12).\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%M}&The minute as a decimal number (range 00 to 59).\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%p}&Either {\tt AM} or {\tt PM}, according to the given time value. +Midnight is treated as {\tt AM} and noon as {\tt PM}.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%P}&Either {\tt am} or {\tt pm}, according to the given time value. +Midnight is treated as {\tt am} and noon as {\tt pm}.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%R}&The hour and minute in decimal numbers using the format +\verb|%H:%M|.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%S}&The second as a decimal number (range 00 to 59).\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%T}&The time of day in decimal numbers using the format +\verb|%H:%M:%S|.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%u}&The day of the week as a decimal number (range 1 to 7), Monday +being 1.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%U}&The week number of the current year as a decimal number (range +00 to 53), starting with the first Sunday as the first day of the first +week. Days preceding the first Sunday in the year are considered to be +in week 00. +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%V}&The ISO week number as a decimal number (range 01 to 53). ISO +weeks start with Monday and end with Sunday. Week 01 of a year is the +first week which has the majority of its days in that year; this is +equivalent to the week containing January 4. Week 01 of a year can +contain days from the previous year. The week before week 01 of a year +is the last week (52 or 53) of the previous year even if it contains +days from the new year. In other word, if 1 January is Monday, Tuesday, +Wednesday or Thursday, it is in week 01; if 1 January is Friday, +Saturday or Sunday, it is in week 52 or 53 of the previous year.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%w}&The day of the week as a decimal number (range 0 to 6), Sunday +being 0.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%W}&The week number of the current year as a decimal number (range +00 to 53), starting with the first Monday as the first day of the first +week. Days preceding the first Monday in the year are considered to be +in week 00.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%y}&The year without a century as a decimal number (range 00 to +99), that is the year modulo 100.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%Y}&The year as a decimal number, using the Gregorian calendar.\\ +\end{tabular} + +\medskip + +\begin{tabular}{@{}p{20pt}p{298pt}@{}} +{\tt\%\%}&A literal \verb|%| character.\\ +\end{tabular} + +\medskip + +All other (ordinary) characters in the format string are simply copied +to the resultant string. + +The first argument (calendar time) passed to the function {\tt time2str} +must be in the range from $-62135596800$ to $+64092211199$ that +corresponds to the period from 00:00:00 on January 1, 0001 to 23:59:59 +on December 31, 4000 of the Gregorian calendar. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Table drivers} +\label{drivers} + +\noindent\hfil +by Andrew Makhorin \verb|| + +\noindent\hfil +and Heinrich Schuchardt \verb|| + +\bigskip\bigskip + +The {\it table driver} is a program module which provides transmitting +data between MathProg model objects and data tables. + +Currently the GLPK package has four table drivers: + +\setlength{\leftmargini}{2.5em} + +\begin{itemize} +\item built-in CSV table driver; +\item built-in xBASE table driver; +\item ODBC table driver; +\item MySQL table driver. +\end{itemize} + +\subsection{CSV table driver} + +The CSV table driver assumes that the data table is represented in the +form of a plain text file in the CSV (comma-separated values) file +format as described below. + +To choose the CSV table driver its name in the table statement should +be specified as \verb|"CSV"|, and the only argument should specify the +name of a plain text file containing the table. For example: + +\medskip + +\verb| table data IN "CSV" "data.csv": ... ;| + +\medskip + +The filename suffix may be arbitrary, however, it is recommended to use +the suffix `\verb|.csv|'. + +On reading input tables the CSV table driver provides an implicit field +named \verb|RECNO|, which contains the current record number. This +field can be specified in the input table statement as if there were +the actual field having the name \verb|RECNO| in the CSV file. For +example: + +\medskip + +\verb| table list IN "CSV" "list.csv": num <- [RECNO], ... ;| + +\subsubsection*{CSV format\footnote{This material is based on the RFC +document 4180.}} + +The CSV (comma-separated values) format is a plain text file format +defined as follows. + +1. Each record is located on a separate line, delimited by a line +break. For example: + +\medskip + +\verb| aaa,bbb,ccc\n| + +\verb| xxx,yyy,zzz\n| + +\medskip + +\noindent +where \verb|\n| means the control character \verb|LF| ({\tt 0x0A}). + +\newpage + +2. The last record in the file may or may not have an ending line +break. For example: + +\medskip + +\verb| aaa,bbb,ccc\n| + +\verb| xxx,yyy,zzz| + +\medskip + +3. There should be a header line appearing as the first line of the +file in the same format as normal record lines. This header should +contain names corresponding to the fields in the file. The number of +field names in the header line should be the same as the number of +fields in the records of the file. For example: + +\medskip + +\verb| name1,name2,name3\n| + +\verb| aaa,bbb,ccc\n| + +\verb| xxx,yyy,zzz\n| + +\medskip + +4. Within the header and each record there may be one or more fields +separated by commas. Each line should contain the same number of fields +throughout the file. Spaces are considered as part of a field and +therefore not ignored. The last field in the record should not be +followed by a comma. For example: + +\medskip + +\verb| aaa,bbb,ccc\n| + +\medskip + +5. Fields may or may not be enclosed in double quotes. For example: + +\medskip + +\verb| "aaa","bbb","ccc"\n| + +\verb| zzz,yyy,xxx\n| + +\medskip + +6. If a field is enclosed in double quotes, each double quote which is +part of the field should be coded twice. For example: + +\medskip + +\verb| "aaa","b""bb","ccc"\n| + +\medskip + +\noindent{\bf Example} + +\begin{verbatim} +FROM,TO,DISTANCE,COST +Seattle,New-York,2.5,0.12 +Seattle,Chicago,1.7,0.08 +Seattle,Topeka,1.8,0.09 +San-Diego,New-York,2.5,0.15 +San-Diego,Chicago,1.8,0.10 +San-Diego,Topeka,1.4,0.07 +\end{verbatim} + +\subsection{xBASE table driver} + +The xBASE table driver assumes that the data table is stored in the +.dbf file format. + +To choose the xBASE table driver its name in the table statement should +be specified as \verb|"xBASE"|, and the first argument should specify +the name of a .dbf file containing the table. For the output table there +should be the second argument defining the table format in the form +\verb|"FF...F"|, where \verb|F| is either {\tt C({\it n})}, +which specifies a character field of length $n$, or +{\tt N({\it n}{\rm [},{\it p}{\rm ]})}, which specifies a numeric field +of length $n$ and precision $p$ (by default $p$ is 0). + +The following is a simple example which illustrates creating and +reading a .dbf file: + +\begin{verbatim} +table tab1{i in 1..10} OUT "xBASE" "foo.dbf" + "N(5)N(10,4)C(1)C(10)": 2*i+1 ~ B, Uniform(-20,+20) ~ A, + "?" ~ FOO, "[" & i & "]" ~ C; +set S, dimen 4; +table tab2 IN "xBASE" "foo.dbf": S <- [B, C, RECNO, A]; +display S; +end; +\end{verbatim} + +\subsection{ODBC table driver} + +The ODBC table driver allows connecting to SQL databases using an +implementation of the ODBC interface based on the Call Level Interface +(CLI).\footnote{The corresponding software standard is defined in +ISO/IEC 9075-3:2003.} + +\paragraph{Debian GNU/Linux.} +Under Debian GNU/Linux the ODBC table driver uses the iODBC +package,\footnote{See {\tt}.} which should be +installed before building the GLPK package. The installation can be +effected with the following command: + +\begin{verbatim} +sudo apt-get install libiodbc2-dev +\end{verbatim} + +Note that on configuring the GLPK package to enable using the iODBC +library the option `\verb|--enable-odbc|' should be passed to the +configure script. + +The individual databases must be entered for systemwide usage in +\linebreak \verb|/etc/odbc.ini| and \verb|/etc/odbcinst.ini|. Database +connections to be used by a single user are specified by files in the +home directory (\verb|.odbc.ini| and \verb|.odbcinst.ini|). + +\paragraph{Microsoft Windows.} +Under Microsoft Windows the ODBC table driver uses the Microsoft ODBC +library. To enable this feature the symbol: + +\begin{verbatim} +#define ODBC_DLNAME "odbc32.dll" +\end{verbatim} + +\noindent +should be defined in the GLPK configuration file `\verb|config.h|'. + +Data sources can be created via the Administrative Tools from the +Control Panel. + +\bigskip + +To choose the ODBC table driver its name in the table statement should +be specified as \verb|'ODBC'| or \verb|'iODBC'|. + +The argument list is specified as follows. + +The first argument is the connection string passed to the ODBC library, +for example: + +\verb|'DSN=glpk;UID=user;PWD=password'|, or + +\verb|'DRIVER=MySQL;DATABASE=glpkdb;UID=user;PWD=password'|. + +Different parts of the string are separated by semicolons. Each part +consists of a pair {\it fieldname} and {\it value} separated by the +equal sign. Allowable fieldnames depend on the ODBC library. Typically +the following fieldnames are allowed: + +\verb|DATABASE | database; + +\verb|DRIVER | ODBC driver; + +\verb|DSN | name of a data source; + +\verb|FILEDSN | name of a file data source; + +\verb|PWD | user password; + +\verb|SERVER | database; + +\verb|UID | user name. + +The second argument and all following are considered to be SQL +statements + +SQL statements may be spread over multiple arguments. If the last +character of an argument is a semicolon this indicates the end of +a SQL statement. + +The arguments of a SQL statement are concatenated separated by space. +The eventual trailing semicolon will be removed. + +All but the last SQL statement will be executed directly. + +For IN-table the last SQL statement can be a SELECT command starting +with the capitalized letters \verb|'SELECT '|. If the string does not +start with \verb|'SELECT '| it is considered to be a table name and a +SELECT statement is automatically generated. + +For OUT-table the last SQL statement can contain one or multiple +question marks. If it contains a question mark it is considered a +template for the write routine. Otherwise the string is considered a +table name and an INSERT template is automatically generated. + +The writing routine uses the template with the question marks and +replaces the first question mark by the first output parameter, the +second question mark by the second output parameter and so forth. Then +the SQL command is issued. + +The following is an example of the output table statement: + +\begin{small} +\begin{verbatim} +table ta { l in LOCATIONS } OUT + 'ODBC' + 'DSN=glpkdb;UID=glpkuser;PWD=glpkpassword' + 'DROP TABLE IF EXISTS result;' + 'CREATE TABLE result ( ID INT, LOC VARCHAR(255), QUAN DOUBLE );' + 'INSERT INTO result 'VALUES ( 4, ?, ? )' : + l ~ LOC, quantity[l] ~ QUAN; +\end{verbatim} +\end{small} + +\noindent +Alternatively it could be written as follows: + +\begin{small} +\begin{verbatim} +table ta { l in LOCATIONS } OUT + 'ODBC' + 'DSN=glpkdb;UID=glpkuser;PWD=glpkpassword' + 'DROP TABLE IF EXISTS result;' + 'CREATE TABLE result ( ID INT, LOC VARCHAR(255), QUAN DOUBLE );' + 'result' : + l ~ LOC, quantity[l] ~ QUAN, 4 ~ ID; +\end{verbatim} +\end{small} + +Using templates with `\verb|?|' supports not only INSERT, but also +UPDATE, DELETE, etc. For example: + +\begin{small} +\begin{verbatim} +table ta { l in LOCATIONS } OUT + 'ODBC' + 'DSN=glpkdb;UID=glpkuser;PWD=glpkpassword' + 'UPDATE result SET DATE = ' & date & ' WHERE ID = 4;' + 'UPDATE result SET QUAN = ? WHERE LOC = ? AND ID = 4' : + quantity[l], l; +\end{verbatim} +\end{small} + +\subsection{MySQL table driver} + +The MySQL table driver allows connecting to MySQL databases. + +\paragraph{Debian GNU/Linux.} +Under Debian GNU/Linux the MySQL table\linebreak driver uses the MySQL +package,\footnote{For download development files see +{\tt}.} which should be installed +before building the GLPK package. The installation can be effected with +the following command: + +\begin{verbatim} +sudo apt-get install libmysqlclient15-dev +\end{verbatim} + +Note that on configuring the GLPK package to enable using the MySQL +library the option `\verb|--enable-mysql|' should be passed to the +configure script. + +\paragraph{Microsoft Windows.} +Under Microsoft Windows the MySQL table driver also uses the MySQL +library. To enable this feature the symbol: + +\begin{verbatim} +#define MYSQL_DLNAME "libmysql.dll" +\end{verbatim} + +\noindent +should be defined in the GLPK configuration file `\verb|config.h|'. + +\bigskip + +To choose the MySQL table driver its name in the table statement should +be specified as \verb|'MySQL'|. + +The argument list is specified as follows. + +The first argument specifies how to connect the data base in the DSN +style, for example: + +\verb|'Database=glpk;UID=glpk;PWD=gnu'|. + +Different parts of the string are separated by semicolons. Each part +consists of a pair {\it fieldname} and {\it value} separated by the +equal sign. The following fieldnames are allowed: + +\verb|Server | server running the database (defaulting to localhost); + +\verb|Database | name of the database; + +\verb|UID | user name; + +\verb|PWD | user password; + +\verb|Port | port used by the server (defaulting to 3306). + +The second argument and all following are considered to be SQL +statements + +SQL statements may be spread over multiple arguments. If the last +character of an argument is a semicolon this indicates the end of +a SQL statement. + +The arguments of a SQL statement are concatenated separated by space. +The eventual trailing semicolon will be removed. + +All but the last SQL statement will be executed directly. + +For IN-table the last SQL statement can be a SELECT command starting +with the capitalized letters \verb|'SELECT '|. If the string does not +start with \verb|'SELECT '| it is considered to be a table name and a +SELECT statement is automatically generated. + +For OUT-table the last SQL statement can contain one or multiple +question marks. If it contains a question mark it is considered a +template for the write routine. Otherwise the string is considered a +table name and an INSERT template is automatically generated. + +The writing routine uses the template with the question marks and +replaces the first question mark by the first output parameter, the +second question mark by the second output parameter and so forth. Then +the SQL command is issued. + +The following is an example of the output table statement: + +\begin{small} +\begin{verbatim} +table ta { l in LOCATIONS } OUT + 'MySQL' + 'Database=glpkdb;UID=glpkuser;PWD=glpkpassword' + 'DROP TABLE IF EXISTS result;' + 'CREATE TABLE result ( ID INT, LOC VARCHAR(255), QUAN DOUBLE );' + 'INSERT INTO result VALUES ( 4, ?, ? )' : + l ~ LOC, quantity[l] ~ QUAN; +\end{verbatim} +\end{small} + +\noindent +Alternatively it could be written as follows: + +\begin{small} +\begin{verbatim} +table ta { l in LOCATIONS } OUT + 'MySQL' + 'Database=glpkdb;UID=glpkuser;PWD=glpkpassword' + 'DROP TABLE IF EXISTS result;' + 'CREATE TABLE result ( ID INT, LOC VARCHAR(255), QUAN DOUBLE );' + 'result' : + l ~ LOC, quantity[l] ~ QUAN, 4 ~ ID; +\end{verbatim} +\end{small} + +Using templates with `\verb|?|' supports not only INSERT, but also +UPDATE, DELETE, etc. For example: + +\begin{small} +\begin{verbatim} +table ta { l in LOCATIONS } OUT + 'MySQL' + 'Database=glpkdb;UID=glpkuser;PWD=glpkpassword' + 'UPDATE result SET DATE = ' & date & ' WHERE ID = 4;' + 'UPDATE result SET QUAN = ? WHERE LOC = ? AND ID = 4' : + quantity[l], l; +\end{verbatim} +\end{small} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Solving models with glpsol} + +The GLPK package\footnote{{\tt http://www.gnu.org/software/glpk/}} +includes the program {\tt glpsol}, which is a stand-alone LP/MIP solver. +This program can be launched from the command line or from the shell to +solve models written in the GNU MathProg modeling language. + +In order to tell the solver that the input file contains a model +description, you need to specify the option \verb|--model| in the +command line. For example: + +\medskip + +\verb| glpsol --model foo.mod| + +\medskip + +Sometimes it is necessary to use the data section placed in a separate +file, in which case you may use the following command: + +\medskip + +\verb| glpsol --model foo.mod --data foo.dat| + +\medskip + +\noindent Note that if the model file also contains the data section, +that section is ignored. + +If the model description contains some display and/or printf statements, +by default the output is sent to the terminal. In order to redirect the +output to a file you may use the following command: + +\medskip + +\verb| glpsol --model foo.mod --display foo.out| + +\medskip + +If you need to look at the problem, which has been generated by the +model translator, you may use the option \verb|--wlp| as follows: + +\medskip + +\verb| glpsol --model foo.mod --wlp foo.lp| + +\medskip + +\noindent in which case the problem data is written to file +\verb|foo.lp| in CPLEX LP format suitable for visual analysis. + +Sometimes it is needed merely to check the model description not +solving the generated problem instance. In this case you may specify +the option \verb|--check|, for example: + +\medskip + +\verb| glpsol --check --model foo.mod --wlp foo.lp| + +\medskip + +In order to write a numeric solution obtained by the solver you may use +the following command: + +\medskip + +\verb| glpsol --model foo.mod --output foo.sol| + +\medskip + +\noindent in which case the solution is written to file \verb|foo.sol| +in a plain text format. + +The complete list of the \verb|glpsol| options can be found in the +reference manual included in the GLPK distribution. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Example model description} + +\subsection{Model description written in MathProg} + +Below here is a complete example of the model description written in +the GNU MathProg modeling language. + +\begin{small} +\begin{verbatim} +# A TRANSPORTATION PROBLEM +# +# This problem finds a least cost shipping schedule that meets +# requirements at markets and supplies at factories. +# +# References: +# Dantzig G B, "Linear Programming and Extensions." +# Princeton University Press, Princeton, New Jersey, 1963, +# Chapter 3-3. + +set I; +/* canning plants */ + +set J; +/* markets */ + +param a{i in I}; +/* capacity of plant i in cases */ + +param b{j in J}; +/* demand at market j in cases */ + +param d{i in I, j in J}; +/* distance in thousands of miles */ + +param f; +/* freight in dollars per case per thousand miles */ + +param c{i in I, j in J} := f * d[i,j] / 1000; +/* transport cost in thousands of dollars per case */ + +var x{i in I, j in J} >= 0; +/* shipment quantities in cases */ + +minimize cost: sum{i in I, j in J} c[i,j] * x[i,j]; +/* total transportation costs in thousands of dollars */ + +s.t. supply{i in I}: sum{j in J} x[i,j] <= a[i]; +/* observe supply limit at plant i */ + +s.t. demand{j in J}: sum{i in I} x[i,j] >= b[j]; +/* satisfy demand at market j */ + +data; + +set I := Seattle San-Diego; + +set J := New-York Chicago Topeka; + +param a := Seattle 350 + San-Diego 600; + +param b := New-York 325 + Chicago 300 + Topeka 275; + +param d : New-York Chicago Topeka := + Seattle 2.5 1.7 1.8 + San-Diego 2.5 1.8 1.4 ; + +param f := 90; + +end; +\end{verbatim} +\end{small} + +\subsection{Generated LP problem instance} + +Below here is the result of the translation of the example model +produced by the solver \verb|glpsol| and written in CPLEX LP format +with the option \verb|--wlp|. + +\begin{small} +\begin{verbatim} +\* Problem: transp *\ + +Minimize + cost: + 0.225 x(Seattle,New~York) + 0.153 x(Seattle,Chicago) + + 0.162 x(Seattle,Topeka) + 0.225 x(San~Diego,New~York) + + 0.162 x(San~Diego,Chicago) + 0.126 x(San~Diego,Topeka) + +Subject To + supply(Seattle): + x(Seattle,New~York) + x(Seattle,Chicago) + + x(Seattle,Topeka) <= 350 + supply(San~Diego): + x(San~Diego,New~York) + x(San~Diego,Chicago) + + x(San~Diego,Topeka) <= 600 + demand(New~York): + x(Seattle,New~York) + x(San~Diego,New~York) >= 325 + demand(Chicago): + x(Seattle,Chicago) + x(San~Diego,Chicago) >= 300 + demand(Topeka): + x(Seattle,Topeka) + x(San~Diego,Topeka) >= 275 + +End +\end{verbatim} +\end{small} + +\subsection{Optimal LP solution} + +Below here is the optimal solution of the generated LP problem instance +found by the solver \verb|glpsol| and written in plain text format +with the option \verb|--output|. + +\newpage + +\begin{small} +\begin{verbatim} +Problem: transp +Rows: 6 +Columns: 6 +Non-zeros: 18 +Status: OPTIMAL +Objective: cost = 153.675 (MINimum) + +No. Row name St Activity Lower bound Upper bound Marginal +--- ------------ -- ------------ ------------ ------------ ------------ + 1 cost B 153.675 + 2 supply[Seattle] + B 300 350 + 3 supply[San-Diego] + NU 600 600 < eps + 4 demand[New-York] + NL 325 325 0.225 + 5 demand[Chicago] + NL 300 300 0.153 + 6 demand[Topeka] + NL 275 275 0.126 + +No. Column name St Activity Lower bound Upper bound Marginal +--- ------------ -- ------------ ------------ ------------ ------------ + 1 x[Seattle,New-York] + B 0 0 + 2 x[Seattle,Chicago] + B 300 0 + 3 x[Seattle,Topeka] + NL 0 0 0.036 + 4 x[San-Diego,New-York] + B 325 0 + 5 x[San-Diego,Chicago] + NL 0 0 0.009 + 6 x[San-Diego,Topeka] + B 275 0 + +End of output +\end{verbatim} +\end{small} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\setcounter{secnumdepth}{-1} + +\section{Acknowledgment} + +The authors would like to thank the following people, who kindly read, +commented, and corrected the draft of this document: + +\medskip + +\noindent Juan Carlos Borras \verb|| + +\medskip + +\noindent Harley Mackenzie \verb|| + +\medskip + +\noindent Robbie Morrison \verb|| + +\end{document} diff -r d59bea55db9b -r c445c931472f doc/graphs.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/graphs.tex Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,3935 @@ +%* graphs.tex *% + +%*********************************************************************** +% This code is part of GLPK (GNU Linear Programming Kit). +% +% Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +% 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +% Moscow Aviation Institute, Moscow, Russia. All rights reserved. +% E-mail: . +% +% GLPK is free software: you can redistribute it and/or modify it +% under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% GLPK is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +% or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License +% along with GLPK. If not, see . +%*********************************************************************** + +\documentclass[11pt]{report} +\usepackage{amssymb} +\usepackage[dvipdfm,linktocpage,colorlinks,linkcolor=blue, +urlcolor=blue]{hyperref} +\usepackage[all]{xy} + +\renewcommand\contentsname{\sf\bfseries Contents} +\renewcommand\chaptername{\sf\bfseries Chapter} +\renewcommand\appendixname{\sf\bfseries Appendix} + +\begin{document} + +\thispagestyle{empty} + +\begin{center} + +\vspace*{1in} + +\begin{huge} +\sf\bfseries GNU Linear Programming Kit +\end{huge} + +\vspace{0.5in} + +\begin{LARGE} +\sf Graph and Network Routines +\end{LARGE} + +\vspace{0.5in} + +\begin{LARGE} +\sf for GLPK Version 4.45 +\end{LARGE} + +\vspace{0.5in} +\begin{Large} +\sf (DRAFT, December 2010) +\end{Large} +\end{center} + +\newpage + +\vspace*{1in} + +\vfill + +\noindent +The GLPK package is part of the GNU Project released under the aegis of +GNU. + +\medskip \noindent +Copyright \copyright{} 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. + +\medskip \noindent +Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +02110-1301, USA. + +\medskip \noindent +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +\medskip \noindent +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +entire resulting derived work is distributed under the terms of +a permission notice identical to this one. + +\medskip \noindent +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. + +\tableofcontents + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\chapter{Basic Graph API Routines} + +\section{Graph program object} + +In GLPK the base program object used to represent graphs and networks +is a directed graph (digraph). + +Formally, {\it digraph} (or simply, {\it graph}) is a pair $G=(V,A)$, +where $V$ is a set of {\it vertices}, and $A$ is a set +{\it arcs}.\footnote{$A$ may be a multiset.} Each arc $a\in A$ is an +ordered pair of vertices $a=(x,y)$, where $x\in V$ is called {\it tail +vertex} of arc $a$, and $y\in V$ is called its {\it head vertex}. + +Representation of a graph in the program includes three structs defined +by typedef in the header \verb|glpk.h|: + +\medskip + +$\bullet$ \verb|glp_graph|, which represents the graph in a whole, + +$\bullet$ \verb|glp_vertex|, which represents a vertex of the graph, and + +$\bullet$ \verb|glp_arc|, which represents an arc of the graph. + +\medskip + +All these three structs are ``semi-opaque'', i.e. the application +program can directly access their fields through pointers, however, +changing the fields directly is not allowed---all changes should be +performed only with appropriate GLPK API routines. + +\newpage + +\newenvironment{comment} +{\addtolength{\leftskip}{17pt}\noindent} +{\par\addtolength{\leftskip}{-17pt}} + +\noindent +{\bf glp\_graph.} The struct \verb|glp_graph| has the following +fields available to the application program: + +\medskip + +\noindent +\verb|char *name;| + +\begin{comment}Symbolic name assigned to the graph. It is a pointer to +a null terminated character string of length from 1 to 255 characters. +If no name is assigned to the graph, this field contains \verb|NULL|. +\end{comment} + +\medskip + +\noindent +\verb|int nv;| + +\begin{comment}The number of vertices in the graph, $nv\geq 0$. +\end{comment} + +\medskip + +\noindent +\verb|int na;| + +\begin{comment}The number of arcs in the graph, $na\geq 0$. +\end{comment} + +\medskip + +\noindent +\verb|glp_vertex **v;| + +\begin{comment}Pointer to an array containing the list of vertices. +Element $v[0]$ is not used. Element $v[i]$, $1\leq i\leq nv$, is a +pointer to $i$-th vertex of the graph. Note that on adding new vertices +to the graph the field $v$ may be altered due to reallocation. However, +pointers $v[i]$ are not changed while corresponding vertices exist in +the graph. +\end{comment} + +\medskip + +\noindent +\verb|int v_size;| + +\begin{comment}Size of vertex data blocks, in bytes, +$0\leq v\_size\leq 256$. (See also the field \verb|data| in the struct +\verb|glp_vertex|.) +\end{comment} + +\medskip + +\noindent +\verb|int a_size;| + +\begin{comment}Size of arc data blocks, in bytes, +$0\leq v\_size\leq 256$. (See also the field \verb|data| in the struct +\verb|glp_arc|.) +\end{comment} + +\bigskip + +\noindent +{\bf glp\_vertex.} The struct \verb|glp_vertex| has the following +fields available to the application program: + +\medskip + +\noindent +\verb|int i;| + +\begin{comment}Ordinal number of the vertex, $1\leq i\leq nv$. Note +that element $v[i]$ in the struct \verb|glp_graph| points to the vertex, +whose ordinal number is $i$. +\end{comment} + +\medskip + +\noindent +\verb|char *name;| + +\begin{comment}Symbolic name assigned to the vertex. It is a pointer to +a null terminated character string of length from 1 to 255 characters. +If no name is assigned to the vertex, this field contains \verb|NULL|. +\end{comment} + +\medskip + +\noindent +\verb|void *data;| + +\begin{comment}Pointer to a data block associated with the vertex. This +data block is automatically allocated on creating a new vertex and freed +on deleting the vertex. If $v\_size=0$, the block is not allocated, and +this field contains \verb|NULL|. +\end{comment} + +\medskip + +\noindent +\verb|void *temp;| + +\begin{comment}Working pointer, which may be used freely for any +purposes. The application program can change this field directly. +\end{comment} + +\medskip + +\noindent +\verb|glp_arc *in;| + +\begin{comment}Pointer to the (unordered) list of incoming arcs. If the +vertex has no incoming arcs, this field contains \verb|NULL|. +\end{comment} + +\medskip + +\noindent +\verb|glp_arc *out;| + +\begin{comment}Pointer to the (unordered) list of outgoing arcs. If the +vertex has no outgoing arcs, this field contains \verb|NULL|. +\end{comment} + +\bigskip + +\noindent +{\bf glp\_arc.} The struct \verb|glp_arc| has the following fields +available to the application program: + +\medskip + +\noindent +\verb|glp_vertex *tail;| + +\begin{comment}Pointer to a vertex, which is tail endpoint of the arc. +\end{comment} + +\medskip + +\noindent +\verb|glp_vertex *head;| + +\begin{comment}Pointer to a vertex, which is head endpoint of the arc. +\end{comment} + +\medskip + +\noindent +\verb|void *data;| + +\begin{comment}Pointer to a data block associated with the arc. This +data block is automatically allocated on creating a new arc and freed on +deleting the arc. If $v\_size=0$, the block is not allocated, and this +field contains \verb|NULL|. +\end{comment} + +\medskip + +\noindent +\verb|void *temp;| + +\begin{comment}Working pointer, which may be used freely for any +purposes. The application program can change this field directly. +\end{comment} + +\medskip + +\noindent +\verb|glp_arc *t_next;| + +\begin{comment}Pointer to another arc, which has the same tail endpoint +as this one. \verb|NULL| in this field indicates the end of the list of +outgoing arcs. +\end{comment} + +\medskip + +\noindent +\verb|glp_arc *h_next;| + +\begin{comment}Pointer to another arc, which has the same head endpoint +as this one. \verb|NULL| in this field indicates the end of the list of +incoming arcs. +\end{comment} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Graph creating and modifying routines} + +\subsection{glp\_create\_graph---create graph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +glp_graph *glp_create_graph(int v_size, int a_size); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_create_graph| creates a new graph, which +initially is\linebreak empty, i.e. has no vertices and arcs. + +The parameter \verb|v_size| specifies the size of vertex data blocks, +in bytes, $0\leq v\_size\leq 256$. + +The parameter \verb|a_size| specifies the size of arc data blocks, in +bytes, $0\leq a\_size\leq 256$. + +\subsubsection*{Returns} + +The routine returns a pointer to the graph created. + +\subsection{glp\_set\_graph\_name---assign (change) graph name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_graph_name(glp_graph *G, const char *name); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_graph_name| assigns a symbolic name specified +by the character string \verb|name| (1 to 255 chars) to the graph. + +If the parameter \verb|name| is \verb|NULL| or an empty string, the +routine erases the existing symbolic name of the graph. + +\newpage + +\subsection{glp\_add\_vertices---add new vertices to graph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_add_vertices(glp_graph *G, int nadd); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_add_vertices| adds \verb|nadd| vertices to the +specified graph. New vertices are always added to the end of the vertex +list, so ordinal numbers of existing vertices remain unchanged. Note +that this operation may change the field \verb|v| in the struct +\verb|glp_graph| (pointer to the vertex array) due to reallocation. + +Being added each new vertex is isolated, i.e. has no incident arcs. + +If the size of vertex data blocks specified on creating the graph is +non-zero, the routine also allocates a memory block of that size for +each new vertex added, fills it by binary zeros, and stores a pointer to +it in the field \verb|data| of the struct \verb|glp_vertex|. Otherwise, +if the block size is zero, the field \verb|data| is set to \verb|NULL|. + +\subsubsection*{Returns} + +The routine \verb|glp_add_vertices| returns the ordinal number of the +first new vertex added to the graph. + +\subsection{glp\_set\_vertex\_name---assign (change) vertex name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_set_vertex_name(glp_graph *G, int i, const char *name); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_set_vertex_name| assigns a given symbolic name +(1 up to 255 characters) to \verb|i|-th vertex of the specified graph. + +If the parameter \verb|name| is \verb|NULL| or empty string, the +routine erases an existing name of \verb|i|-th vertex. + +\newpage + +\subsection{glp\_add\_arc---add new arc to graph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +glp_arc *glp_add_arc(glp_graph *G, int i, int j); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_add_arc| adds one new arc to the specified graph. + +The parameters \verb|i| and \verb|j| specify the ordinal numbers of, +resp., tail and head endpoints (vertices) of the arc. Note that +self-loops and multiple arcs are allowed. + +If the size of arc data blocks specified on creating the graph is +non-zero, the routine also allocates a memory block of that size, fills +it by binary zeros, and stores a pointer to it in the field \verb|data| +of the struct \verb|glp_arc|. Otherwise, if the block size is zero, the +field \verb|data| is set to \verb|NULL|. + +\subsection{glp\_del\_vertices---delete vertices from graph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_del_vertices(glp_graph *G, int ndel, const int num[]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_del_vertices| deletes vertices along with all +incident arcs from the specified graph. Ordinal numbers of vertices to +be deleted should be placed in locations \verb|num[1]|, \dots, +\verb|num[ndel]|, \verb|ndel| $>0$. + +Note that deleting vertices involves changing ordinal numbers of other +vertices remaining in the graph. New ordinal numbers of the remaining +vertices are assigned under the assumption that the original order of +vertices is not changed. + +\subsection{glp\_del\_arc---delete arc from graph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_del_arc(glp_graph *G, glp_arc *a); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_del_arc| deletes an arc from the specified graph. +The arc to be deleted must exist. + +\subsection{glp\_erase\_graph---erase graph content} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_erase_graph(glp_graph *G, int v_size, int a_size); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_erase_graph| erases the content of the specified +graph. The effect of this operation is the same as if the graph would be +deleted with the routine \verb|glp_delete_graph| and then created anew +with the routine \verb|glp_create_graph|, with exception that the handle +(pointer) to the graph remains valid. + +The parameters \verb|v_size| and \verb|a_size| have the same meaning as +for the routine \verb|glp_create_graph|. + +\subsection{glp\_delete\_graph---delete graph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_delete_graph(glp_graph *G); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_delete_graph| deletes the specified graph and +frees all the memory allocated to this program object. + +\newpage + +\section{Graph searching routines} + +\subsection{glp\_create\_v\_index---create vertex name index} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_create_v_index(glp_graph *G); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_create_v_index| creates the name index for the +specified graph. The name index is an auxiliary data structure, which +is intended to quickly (i.e. for logarithmic time) find vertices by +their names. + +This routine can be called at any time. If the name index already +exists, the routine does nothing. + +\subsection{glp\_find\_vertex---find vertex by its name} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_find_vertex(glp_graph *G, const char *name); +\end{verbatim} + +\subsubsection*{Returns} + +The routine \verb|glp_find_vertex| returns the ordinal number of +a vertex, which is assigned (by the routine \verb|glp_set_vertex_name|) +the specified symbolic \verb|name|. If no such vertex exists, the +routine returns 0. + +\subsection{glp\_delete\_v\_index---delete vertex name index} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_delete_v_index(glp_graph *G); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_delete_v_index| deletes the name index previously +created by the routine \verb|glp_create_v_index| and frees the memory +allocated to this auxiliary data structure. + +This routine can be called at any time. If the name index does not +exist, the routine does nothing. + +\newpage + +\section{Graph reading/writing routines} + +\subsection{glp\_read\_graph---read graph from plain text file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_graph(glp_graph *G, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_graph| reads a graph from a plain text file, +whose name is specified by the parameter \verb|fname|. Note that before +reading data the current content of the graph object is completely +erased with the routine \verb|glp_erase_graph|. + +For the file format see description of the routine +\verb|glp_write_graph|. + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise +it prints an error message and returns non-zero. + +\subsection{glp\_write\_graph---write graph to plain text file} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_graph(glp_graph *G, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_graph| writes the graph to a plain text +file, whose name is specified by the parameter \verb|fname|. + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise +it prints an error message and returns non-zero. + +\subsubsection*{File format} + +The file created by the routine \verb|glp_write_graph| is a plain text +file, which contains the following information: + +\begin{verbatim} + nv na + i[1] j[1] + i[2] j[2] + . . . + i[na] j[na] +\end{verbatim} + +\noindent +where: + +\noindent +\verb|nv| is the number of vertices (nodes); + +\noindent +\verb|na| is the number of arcs; + +\noindent +\verb|i[k]|, $k=1,\dots,na$, is the index of tail vertex of arc $k$; + +\noindent +\verb|j[k]|, $k=1,\dots,na$, is the index of head vertex of arc $k$. + +\subsection{glp\_read\_ccdata---read graph from text file in DIMACS +clique/coloring format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_ccdata(glp_graph *G, int v_wgt, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine {\it glp\_read\_ccdata} reads a graph from a text file in +DIMACS clique/coloring format. (Though this format is originally +designed to represent data for the minimal vertex coloring and maximal +clique problems, it may be used to represent general undirected and +directed graphs, because the routine allows reading self-loops and +multiple edges/arcs keeping the order of vertices specified for each +edge/arc of the graph.) + +The parameter $G$ specifies the graph object to be read in. Note that +before reading data the current content of the graph object is +completely erased with the routine {\it glp\_erase\_graph}. + +The parameter {\it v\_wgt} specifies an offset of the field of type +{\bf double} in the vertex data block, to which the routine stores the +vertex weight. If {\it v\_wgt} $<0$, the vertex weights are not stored. + +The character string {\it fname} specifies the name of a text file to +be read in. (If the file name ends with the suffix `\verb|.gz|', the +file is assumed to be compressed, in which case the routine decompresses +it ``on the fly''.) + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise, +it prints an error message and returns non-zero. + +\subsubsection*{DIMACS clique/coloring format\footnote{This material is +based on the paper ``Clique and Coloring Problems Graph Format'', which +is publically available at +{\tt http://dimacs.rutgers.edu/Challenges/}.}} + +The DIMACS input file is a plain ASCII text file. It contains +{\it lines} of several types described below. A line is terminated with +an end-of-line character. Fields in each line are separated by at least +one blank space. Each line begins with a one-character designator to +identify the line type. + +Note that DIMACS requires all numerical quantities to be integers in +the range $[-2^{31},2^{31}-1]$ while GLPK allows the quantities to be +floating-point numbers. + +\paragraph{Comment lines.} Comment lines give human-readable information +about the file and are ignored by programs. Comment lines can appear +anywhere in the file. Each comment line begins with a lower-case +character \verb|c|. + +\begin{verbatim} + c This is a comment line +\end{verbatim} + +\paragraph{Problem line.} There is one problem line per data file. The +problem line must appear before any node or edge descriptor lines. It +has the following format: + +\begin{verbatim} + p edge NODES EDGES +\end{verbatim} + +\noindent +The lower-case letter \verb|p| signifies that this is a problem line. +The four-character problem designator \verb|edge| identifies the file +as containing data for the minimal vertex coloring or maximal clique +problem. The \verb|NODES| field contains an integer value specifying +the number of vertices in the graph. The \verb|EDGES| field contains an +integer value specifying the number of edges (arcs) in the graph. + +\paragraph{Vertex descriptors.} These lines give the weight assigned to +a vertex of the graph. There is one vertex descriptor line for each +vertex, with the following format. Vertices without a descriptor take on +a default value of 1. + +\begin{verbatim} + n ID VALUE +\end{verbatim} + +\noindent +The lower-case character \verb|n| signifies that this is a vertex +descriptor line. The \verb|ID| field gives a vertex identification +number, an integer between 1 and $n$, where $n$ is the number of +vertices in the graph. The \verb|VALUE| field gives a vertex weight, +which can either positive or negative (or zero). + +\paragraph{Edge descriptors.} There is one edge descriptor line for +each edge (arc) of the graph, each with the following format: + +\begin{verbatim} + e I J +\end{verbatim} + +\noindent +The lower-case character \verb|e| signifies that this is an edge +descriptor line. For an edge (arc) $(i,j)$ the fields \verb|I| and +\verb|J| specify its endpoints. + +\paragraph{Example.} The following example of an undirected graph: + +\bigskip + +\noindent\hfil +\xymatrix %@C=32pt +{&{v_1}\ar@{-}[ldd]\ar@{-}[dd]\ar@{-}[rd]\ar@{-}[rr]&&{v_2}\ar@{-}[ld] +\ar@{-}[dd]\ar@{-}[rdd]&\\ +&&{v_7}\ar@{-}[ld]\ar@{-}[rd]&&\\ +{v_6}\ar@{-}[r]\ar@{-}[rdd]&{v_{10}}\ar@{-}[rr]\ar@{-}[rd]\ar@{-}[dd]&& +{v_8}\ar@{-}[ld]\ar@{-}[dd]\ar@{-}[r]&{v_3}\ar@{-}[ldd]\\ +&&{v_9}\ar@{-}[ld]\ar@{-}[rd]&&\\ +&{v_5}\ar@{-}[rr]&&{v_4}&\\ +} + +\bigskip + +\noindent +might be coded in DIMACS clique/coloring format as follows: + +\begin{footnotesize} +\begin{verbatim} +c sample.col +c +c This is an example of the vertex coloring problem data +c in DIMACS format. +c +p edge 10 21 +c +e 1 2 +e 1 6 +e 1 7 +e 1 10 +e 2 3 +e 2 7 +e 2 8 +e 3 4 +e 3 8 +e 4 5 +e 4 8 +e 4 9 +e 5 6 +e 5 9 +e 5 10 +e 6 10 +e 7 8 +e 7 10 +e 8 9 +e 8 10 +e 9 10 +c +c eof +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_write\_ccdata---write graph to text file in DIMACS +clique/coloring format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_ccdata(glp_graph *G, int v_wgt, + const char *fname); +\end{verbatim} + +\subsection*{Description} + +The routine {\it glp\_write\_ccdata} writes the graph object specified +by the parameter $G$ to a text file in DIMACS clique/coloring format. +(Though this format is originally designed to represent data for the +minimal vertex coloring and maximal clique problems, it may be used to +represent general undirected and directed graphs, because the routine +allows writing self-loops and multiple edges/arcs keeping the order of +vertices specified for each edge/arc of the graph.) + +The parameter {\it v\_wgt} specifies an offset of the field of type +{\bf double} in the vertex data block, which contains the vertex weight. +If {\it v\_wgt} $<0$, it is assumed that the weight of each vertex is 1. + +The character string {\it fname} specifies a name of the text file to be +written out. (If the file name ends with suffix `\verb|.gz|', the file +is assumed to be compressed, in which case the routine performs +automatic compression on writing it.) + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise, +it prints an error message and returns non-zero. + +\newpage + +\section{Graph analysis routines} + +\subsection{glp\_weak\_comp---find all weakly connected components of +graph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_weak_comp(glp_graph *G, int v_num); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_weak_comp| finds all weakly connected components +of the specified graph. + +The parameter \verb|v_num| specifies an offset of the field of type +\verb|int| in the vertex data block, to which the routine stores the +number of a weakly connected component containing that vertex. If +\verb|v_num| $<0$, no component numbers are stored. + +The components are numbered in arbitrary order from 1 to \verb|nc|, +where \verb|nc| is the total number of components found, +$0\leq$ \verb|nc| $\leq|V|$. + +\subsubsection*{Returns} + +The routine returns \verb|nc|, the total number of components found. + +\subsection{glp\_strong\_comp---find all strongly connected components +of graph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_strong_comp(glp_graph *G, int v_num); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_strong_comp| finds all strongly connected +components of the specified graph. + +The parameter \verb|v_num| specifies an offset of the field of type +\verb|int| in the vertex data block, to which the routine stores the +number of a strongly connected component containing that vertex. If +\verb|v_num| $<0$, no component numbers are stored. + +The components are numbered in arbitrary order from 1 to \verb|nc|, +where \verb|nc| is the total number of components found, +$0\leq$ \verb|nc| $\leq|V|$. However, the component numbering has the +property that for every arc $(i\rightarrow j)$ in the graph the +condition $num(i)\geq num(j)$ holds. + +\subsubsection*{Returns} + +The routine returns \verb|nc|, the total number of components found. + +\subsubsection*{References} + +I.~S.~Duff, J.~K.~Reid, Algorithm 529: Permutations to block triangular +form, ACM Trans. on Math. Softw. 4 (1978), 189-92. + +\subsubsection*{Example} + +The following program reads a graph from a plain text file +`\verb|graph.txt|' and finds all its strongly connected components. + +\begin{footnotesize} +\begin{verbatim} +#include +#include +#include +#include + +typedef struct { int num; } v_data; + +#define vertex(v) ((v_data *)((v)->data)) + +int main(void) +{ glp_graph *G; + int i, nc; + G = glp_create_graph(sizeof(v_data), 0); + glp_read_graph(G, "graph.txt"); + nc = glp_strong_comp(G, offsetof(v_data, num)); + printf("nc = %d\n", nc); + for (i = 1; i <= G->nv; i++) + printf("num[%d] = %d\n", i, vertex(G->v[i])->num); + glp_delete_graph(G); + return 0; +} +\end{verbatim} +\end{footnotesize} + +\noindent +If the file `\verb|graph.txt|' contains the graph shown below: + +\bigskip + +\noindent\hfil +\xymatrix +{1\ar[r]&2\ar[r]&3\ar[r]\ar[dd]&4\ar[dd]\\ +5\ar[u]&6\ar[l]\\ +7\ar[u]&&8\ar[lu]\ar[ll]\ar[r]&9\ar[r]&10\ar[r]\ar[d]&11\ar[d]\\ +12\ar[u]\ar[rru]\ar@/_/[rr]&&13\ar[ll]\ar[u]\ar[rr]&&14\ar[lu]&15\ar[l] +\\ +} + +\bigskip\bigskip + +\noindent +the program output may look like follows: + +\begin{footnotesize} +\begin{verbatim} +Reading graph from `graph.txt'... +Graph has 15 vertices and 30 arcs +31 lines were read +nc = 4 +num[1] = 3 +num[2] = 3 +num[3] = 3 +num[4] = 2 +num[5] = 3 +num[6] = 3 +num[7] = 3 +num[8] = 3 +num[9] = 1 +num[10] = 1 +num[11] = 1 +num[12] = 4 +num[13] = 4 +num[14] = 1 +num[15] = 1 +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_top\_sort---topological sorting of acyclic digraph} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_top_sort(glp_graph *G, int v_num); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_top_sort| performs topological sorting of +vertices of the specified acyclic digraph. + +The parameter \verb|v_num| specifies an offset of the field of type +\verb|int| in the vertex data block, to which the routine stores the +vertex number assigned. If \verb|v_num| $<0$, vertex numbers are not +stored. + +The vertices are numbered from 1 to $n$, where $n$ is the total number +of vertices in the graph. The vertex numbering has the property that +for every arc $(i\rightarrow j)$ in the graph the condition +$num(i) +#include +#include +#include + +typedef struct { int num; } v_data; + +#define vertex(v) ((v_data *)((v)->data)) + +int main(void) +{ glp_graph *G; + int i, cnt; + G = glp_create_graph(sizeof(v_data), 0); + glp_read_graph(G, "graph.txt"); + cnt = glp_top_sort(G, offsetof(v_data, num)); + printf("cnt = %d\n", cnt); + for (i = 1; i <= G->nv; i++) + printf("num[%d] = %d\n", i, vertex(G->v[i])->num); + glp_delete_graph(G); + return 0; +} +\end{verbatim} +\end{footnotesize} + +\noindent +If the file `\verb|graph.txt|' contains the graph shown below: + +\bigskip + +\noindent\hfil +\xymatrix @=20pt +{ +1\ar[rrr]&&&2\ar[r]\ar[rddd]&3\ar[rd]&&&&\\ +&&&4\ar[ru]&&5\ar[r]&6\ar[rd]\ar[dd]&&\\ +7\ar[r]&8\ar[r]&9\ar[ruu]\ar[ru]\ar[r]\ar[rd]&10\ar[rr]\ar[rru]&& +11\ar[ru]&&12\ar[r]&13\\ +&&&14\ar[r]&15\ar[rrru]\ar[rr]&&16\ar[rru]\ar[rr]&&17\\ +} + +\bigskip\bigskip + +\noindent +the program output may look like follows: + +\begin{footnotesize} +\begin{verbatim} +Reading graph from `graph.txt'... +Graph has 17 vertices and 23 arcs +24 lines were read +cnt = 0 +num[1] = 8 +num[2] = 9 +num[3] = 10 +num[4] = 4 +num[5] = 11 +num[6] = 12 +num[7] = 1 +num[8] = 2 +num[9] = 3 +num[10] = 5 +num[11] = 6 +num[12] = 14 +num[13] = 16 +num[14] = 7 +num[15] = 13 +num[16] = 15 +num[17] = 17 +\end{verbatim} +\end{footnotesize} + +\noindent +The output corresponds to the following vertex numbering: + +\bigskip + +\noindent\hfil +\xymatrix @=20pt +{ +8\ar[rrr]&&&9\ar[r]\ar[rddd]&10\ar[rd]&&&&\\ +&&&4\ar[ru]&&11\ar[r]&12\ar[rd]\ar[dd]&&\\ +1\ar[r]&2\ar[r]&3\ar[ruu]\ar[ru]\ar[r]\ar[rd]&5\ar[rr]\ar[rru]&& +6\ar[ru]&&14\ar[r]&16\\ +&&&7\ar[r]&13\ar[rrru]\ar[rr]&&15\ar[rru]\ar[rr]&&17\\ +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\chapter{Network optimization API routines} + +\section{Minimum cost flow problem} + +\subsection{Background} + +The {\it minimum cost flow problem} (MCFP) is stated as follows. Let +there be given a directed graph (flow network) $G=(V,A)$, where $V$ is +a set of vertices (nodes), and $A\subseteq V\times V$ is a set of arcs. +Let for each node $i\in V$ there be given a quantity $b_i$ having the +following meaning: + +if $b_i>0$, then $|b_i|$ is a {\it supply} at node $i$, which shows +how many flow units are {\it generated} at node $i$ (or, equivalently, +entering the network through node $i$ from the outside); + +if $b_i<0$, then $|b_i|$ is a {\it demand} at node $i$, which shows how +many flow units are {\it lost} at node $i$ (or, equivalently, leaving +the network through node $i$ to the outside); + +if $b_i=0$, then $i$ is a {\it transshipment} node, at which the flow +is conserved, i.e. neither generated nor lost. + +Let also for each arc $a=(i,j)\in A$ there be given the following three +quantities: + +$l_{ij}$, a (non-negative) lower bound to the flow through arc $(i,j)$; + +$u_{ij}$, an upper bound to the flow through arc $(i,j)$, which is the +{\it arc capacity}; + +$c_{ij}$, a per-unit cost of the flow through arc $(i,j)$. + +The problem is to find flows $x_{ij}$ through every arc of the network, +which satisfy the specified bounds and the conservation constraints at +all nodes, and minimize the total flow cost. Here the conservation +constraint at a node means that the total flow entering this node +through its incoming arcs plus the supply at this node must be equal to +the total flow leaving this node through its outgoing arcs plus the +demand at this node. + +An example of the minimum cost flow problem is shown on Fig.~1. + +\bigskip + +\noindent\hfil +\xymatrix @C=48pt +{_{20}\ar@{~>}[d]& +v_2\ar[r]|{_{0,10,\$2}}\ar[dd]|{_{0,9,\$3}}& +v_3\ar[dd]|{_{2,12,\$1}}\ar[r]|{_{0,18,\$0}}& +v_8\ar[rd]|{_{0,20,\$9}}&\\ +v_1\ar[ru]|{_{0,14,\$0}}\ar[rd]|{_{0,23,\$0}}&&& +v_6\ar[d]|{_{0,7,\$0}}\ar[u]|{_{4,8,\$0}}& +v_9\ar@{~>}[d]\\ +&v_4\ar[r]|{_{0,26,\$0}}& +v_5\ar[luu]|{_{0,11,\$1}}\ar[ru]|{_{0,25,\$5}}\ar[r]|{_{0,4,\$7}}& +v_7\ar[ru]|{_{0,15,\$3}}&_{20}\\ +} + +\medskip + +\noindent\hfil +\begin{tabular}{ccc} +\xymatrix @C=48pt{v_i\ar[r]|{\ l,u,\$c\ }&v_j\\}& +\xymatrix{\hbox{\footnotesize supply}\ar@{~>}[r]&v_i\\}& +\xymatrix{v_i\ar@{~>}[r]&\hbox{\footnotesize demand}\\}\\ +\end{tabular} + +\bigskip + +\noindent\hfil +Fig.~1. An example of the minimum cost flow problem. + +\bigskip + +The minimum cost flow problem can be naturally formulated as the +following LP problem: + +\medskip + +\noindent +\hspace{.5in}minimize +$$z=\sum_{(i,j)\in A}c_{ij}x_{ij}\eqno(1)$$ +\hspace{.5in}subject to +$$\sum_{(i,j)\in A}x_{ij}-\sum_{(j,i)\in A}x_{ji}=b_i\ \ \ \hbox +{for all}\ i\in V\eqno(2)$$ +$$l_{ij}\leq x_{ij}\leq u_{ij}\ \ \ \hbox{for all}\ (i,j)\in A +\eqno(3)$$ + +\newpage + +\subsection{glp\_read\_mincost---read minimum cost flow problem\\data +in DIMACS format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_mincost(glp_graph *G, int v_rhs, int a_low, + int a_cap, int a_cost, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_mincost| reads the minimum cost flow problem +data from a text file in DIMACS format. + +The parameter \verb|G| specifies the graph object, to which the problem +data have to be stored. Note that before reading data the current +content of the graph object is completely erased with the routine +\verb|glp_erase_graph|. + +The parameter \verb|v_rhs| specifies an offset of the field of type +\verb|double| in the vertex data block, to which the routine stores +$b_i$, the supply/demand value. If \verb|v_rhs| $<0$, the value is not +stored. + +The parameter \verb|a_low| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores +$l_{ij}$, the lower bound to the arc flow. If \verb|a_low| $<0$, the +lower bound is not stored. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores +$u_{ij}$, the upper bound to the arc flow (the arc capacity). If +\verb|a_cap| $<0$, the upper bound is not stored. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores +$c_{ij}$, the per-unit cost of the arc flow. If \verb|a_cost| $<0$, the +cost is not stored. + +The character string \verb|fname| specifies the name of a text file to +be read in. (If the file name name ends with the suffix `\verb|.gz|', +the file is assumed to be compressed, in which case the routine +decompresses it ``on the fly''.) + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise, +it prints an error message and returns non-zero. + +\newpage + +\subsubsection*{Example} + +\begin{footnotesize} +\begin{verbatim} +typedef struct +{ /* vertex data block */ + ... + double rhs; + ... +} v_data; + +typedef struct +{ /* arc data block */ + ... + double low, cap, cost; + ... +} a_data; + +int main(void) +{ glp_graph *G; + int ret; + G = glp_create_graph(sizeof(v_data), sizeof(a_data)); + ret = glp_read_mincost(G, offsetof(v_data, rhs), + offsetof(a_data, low), offsetof(a_data, cap), + offsetof(a_data, cost), "sample.min"); + if (ret != 0) goto ... + ... +} +\end{verbatim} +\end{footnotesize} + +\subsubsection*{DIMACS minimum cost flow problem format\footnote{This +material is based on the paper ``The First DIMACS International +Algorithm Implementation Challenge: Problem Definitions and +Specifications'', which is publically available at +{\tt http://dimacs.rutgers.edu/Challenges/}.}} +\label{subsecmincost} + +The DIMACS input file is a plain ASCII text file. It contains +{\it lines} of several types described below. A line is terminated with +an end-of-line character. Fields in each line are separated by at least +one blank space. Each line begins with a one-character designator to +identify the line type. + +Note that DIMACS requires all numerical quantities to be integers in +the range $[-2^{31},\ 2^{31}-1]$ while GLPK allows the quantities to be +floating-point numbers. + +\newpage + +\paragraph{Comment lines.} Comment lines give human-readable information +about the file and are ignored by programs. Comment lines can appear +anywhere in the file. Each comment line begins with a lower-case +character \verb|c|. + +\begin{verbatim} + c This is a comment line +\end{verbatim} + +\paragraph{Problem line.} There is one problem line per data file. The +problem line must appear before any node or arc descriptor lines. It has +the following format: + +\begin{verbatim} + p min NODES ARCS +\end{verbatim} + +\noindent +The lower-case character \verb|p| signifies that this is a problem line. +The three-character problem designator \verb|min| identifies the file as +containing specification information for the minimum cost flow problem. +The \verb|NODES| field contains an integer value specifying the number +of nodes in the network. The \verb|ARCS| field contains an integer value +specifying the number of arcs in the network. + +\paragraph{Node descriptors.} All node descriptor lines must appear +before all arc descriptor lines. The node descriptor lines describe +supply and demand nodes, but not transshipment nodes. That is, only +nodes with non-zero node supply/demand values appear. There is one node +descriptor line for each such node, with the following format: + +\begin{verbatim} + n ID FLOW +\end{verbatim} + +\noindent +The lower-case character \verb|n| signifies that this is a node +descriptor line. The \verb|ID| field gives a node identification number, +an integer between 1 and \verb|NODES|. The \verb|FLOW| field gives the +amount of supply (if positive) or demand (if negative) at node +\verb|ID|. + +\paragraph{Arc descriptors.} There is one arc descriptor line for each +arc in the network. Arc descriptor lines are of the following format: + +\begin{verbatim} + a SRC DST LOW CAP COST +\end{verbatim} + +\noindent +The lower-case character \verb|a| signifies that this is an arc +descriptor line. For a directed arc $(i,j)$ the \verb|SRC| field gives +the identification number $i$ for the tail endpoint, and the \verb|DST| +field gives the identification number $j$ for the head endpoint. +Identification numbers are integers between 1 and \verb|NODES|. The +\verb|LOW| field specifies the minimum amount of flow that can be sent +along arc $(i,j)$, and the \verb|CAP| field gives the maximum amount of +flow that can be sent along arc $(i,j)$ in a feasible flow. The +\verb|COST| field contains the per-unit cost of flow sent along arc +$(i,j)$. + +\paragraph{Example.} Below here is an example of the data file in +DIMACS format corresponding to the minimum cost flow problem shown on +Fig~1. + +\begin{footnotesize} +\begin{verbatim} +c sample.min +c +c This is an example of the minimum cost flow problem data +c in DIMACS format. +c +p min 9 14 +c +n 1 20 +n 9 -20 +c +a 1 2 0 14 0 +a 1 4 0 23 0 +a 2 3 0 10 2 +a 2 4 0 9 3 +a 3 5 2 12 1 +a 3 8 0 18 0 +a 4 5 0 26 0 +a 5 2 0 11 1 +a 5 6 0 25 5 +a 5 7 0 4 7 +a 6 7 0 7 0 +a 6 8 4 8 0 +a 7 9 0 15 3 +a 8 9 0 20 9 +c +c eof +\end{verbatim} +\end{footnotesize} + +\newpage + +\subsection{glp\_write\_mincost---write minimum cost flow problem\\data +in DIMACS format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_mincost(glp_graph *G, int v_rhs, int a_low, + int a_cap, int a_cost, const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_mincost| writes the minimum cost flow +problem data to a text file in DIMACS format. + +The parameter \verb|G| is the graph (network) program object, which +specifies the minimum cost flow problem instance. + +The parameter \verb|v_rhs| specifies an offset of the field of type +\verb|double| in the vertex data block, which contains $b_i$, the +supply/demand value. If \verb|v_rhs| $<0$, it is assumed that $b_i=0$ +for all nodes. + +The parameter \verb|a_low| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $l_{ij}$, the lower +bound to the arc flow. If \verb|a_low| $<0$, it is assumed that +$l_{ij}=0$ for all arcs. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $u_{ij}$, the upper +bound to the arc flow (the arc capacity). If the upper bound is +specified as \verb|DBL_MAX|, it is assumed that $u_{ij}=\infty$, i.e. +the arc is uncapacitated. If \verb|a_cap| $<0$, it is assumed that +$u_{ij}=1$ for all arcs. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $c_{ij}$, the +per-unit cost of the arc flow. If \verb|a_cost| $<0$, it is assumed that +$c_{ij}=0$ for all arcs. + +The character string \verb|fname| specifies a name of the text file to +be written out. (If the file name ends with suffix `\verb|.gz|', the +file is assumed to be compressed, in which case the routine performs +automatic compression on writing it.) + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise, +it prints an error message and returns non-zero. + +\newpage + +\subsection{glp\_mincost\_lp---convert minimum cost flow problem\\to LP} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, + int v_rhs, int a_low, int a_cap, int a_cost); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mincost_lp| builds LP problem (1)---(3), which +corresponds to the specified minimum cost flow problem. + +The parameter \verb|lp| is the resultant LP problem object to be built. +Note that on entry its current content is erased with the routine +\verb|glp_erase_prob|. + +The parameter \verb|G| is the graph (network) program object, which +specifies the minimum cost flow problem instance. + +The parameter \verb|names| is a flag. If it is \verb|GLP_ON|, the +routine uses symbolic names of the graph object components to assign +symbolic names to the LP problem object components. If the flag is +\verb|GLP_OFF|, no symbolic names are assigned. + +The parameter \verb|v_rhs| specifies an offset of the field of type +\verb|double| in the vertex data block, which contains $b_i$, the +supply/demand value. If \verb|v_rhs| $<0$, it is assumed that $b_i=0$ +for all nodes. + +The parameter \verb|a_low| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $l_{ij}$, the lower +bound to the arc flow. If \verb|a_low| $<0$, it is assumed that +$l_{ij}=0$ for all arcs. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $u_{ij}$, the upper +bound to the arc flow (the arc capacity). If the upper bound is +specified as \verb|DBL_MAX|, it is assumed that $u_{ij}=\infty$, i.e. +the arc is uncapacitated. If \verb|a_cap| $<0$, it is assumed that +$u_{ij}=1$ for all arcs. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $c_{ij}$, the +per-unit cost of the arc flow. If \verb|a_cost| $<0$, it is assumed that +$c_{ij}=0$ for all arcs. + +\subsubsection*{Example} + +The example program below reads the minimum cost problem instance in +DIMACS format from file `\verb|sample.min|', converts the instance to +LP, and then writes the resultant LP in CPLEX format to file +`\verb|mincost.lp|'. + +\newpage + +\begin{footnotesize} +\begin{verbatim} +#include +#include + +typedef struct { double rhs; } v_data; +typedef struct { double low, cap, cost; } a_data; + +int main(void) +{ glp_graph *G; + glp_prob *lp; + G = glp_create_graph(sizeof(v_data), sizeof(a_data)); + glp_read_mincost(G, offsetof(v_data, rhs), + offsetof(a_data, low), offsetof(a_data, cap), + offsetof(a_data, cost), "sample.min"); + lp = glp_create_prob(); + glp_mincost_lp(lp, G, GLP_ON, offsetof(v_data, rhs), + offsetof(a_data, low), offsetof(a_data, cap), + offsetof(a_data, cost)); + glp_delete_graph(G); + glp_write_lp(lp, NULL, "mincost.lp"); + glp_delete_prob(lp); + return 0; +} +\end{verbatim} +\end{footnotesize} + +If `\verb|sample.min|' is the example data file from the subsection +describing the routine \verb|glp_read_mincost|, file `\verb|mincost.lp|' +may look like follows: + +\begin{footnotesize} +\begin{verbatim} +Minimize + obj: + 3 x(2,4) + 2 x(2,3) + x(3,5) + 7 x(5,7) + 5 x(5,6) + + x(5,2) + 3 x(7,9) + 9 x(8,9) + +Subject To + r_1: + x(1,2) + x(1,4) = 20 + r_2: - x(5,2) + x(2,3) + x(2,4) - x(1,2) = 0 + r_3: + x(3,5) + x(3,8) - x(2,3) = 0 + r_4: + x(4,5) - x(2,4) - x(1,4) = 0 + r_5: + x(5,2) + x(5,6) + x(5,7) - x(4,5) - x(3,5) = 0 + r_6: + x(6,7) + x(6,8) - x(5,6) = 0 + r_7: + x(7,9) - x(6,7) - x(5,7) = 0 + r_8: + x(8,9) - x(6,8) - x(3,8) = 0 + r_9: - x(8,9) - x(7,9) = -20 + +Bounds + 0 <= x(1,4) <= 23 + 0 <= x(1,2) <= 14 + 0 <= x(2,4) <= 9 + 0 <= x(2,3) <= 10 + 0 <= x(3,8) <= 18 + 2 <= x(3,5) <= 12 + 0 <= x(4,5) <= 26 + 0 <= x(5,7) <= 4 + 0 <= x(5,6) <= 25 + 0 <= x(5,2) <= 11 + 4 <= x(6,8) <= 8 + 0 <= x(6,7) <= 7 + 0 <= x(7,9) <= 15 + 0 <= x(8,9) <= 20 + +End +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_mincost\_okalg---solve minimum cost flow problem with +out-of-kilter algorithm} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_mincost_okalg(glp_graph *G, int v_rhs, int a_low, + int a_cap, int a_cost, double *sol, int a_x, int v_pi); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mincost_okalg| finds optimal solution to the +minimum cost flow problem with the out-of-kilter +algorithm.\footnote{GLPK implementation of the out-of-kilter algorithm +is based on the following book: L.~R.~Ford,~Jr., and D.~R.~Fulkerson, +``Flows in Networks,'' The RAND Corp., Report R-375-PR (August 1962), +Chap. III ``Minimal Cost Flow Problems,'' pp.~113-26.} Note that this +routine requires all the problem data to be integer-valued. + +The parameter \verb|G| is a graph (network) program object which +specifies the minimum cost flow problem instance to be solved. + +The parameter \verb|v_rhs| specifies an offset of the field of type +\verb|double| in the vertex data block, which contains $b_i$, the +supply/demand value. This value must be integer in the range +[$-$\verb|INT_MAX|, $+$\verb|INT_MAX|]. If \verb|v_rhs| $<0$, it is +assumed that $b_i=0$ for all nodes. + +The parameter \verb|a_low| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $l_{ij}$, the lower +bound to the arc flow. This bound must be integer in the range +[$0$, \verb|INT_MAX|]. If \verb|a_low| $<0$, it is assumed that +$l_{ij}=0$ for all arcs. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $u_{ij}$, the upper +bound to the arc flow (the arc capacity). This bound must be integer in +the range [$l_{ij}$, \verb|INT_MAX|]. If \verb|a_cap| $<0$, it is +assumed that $u_{ij}=1$ for all arcs. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $c_{ij}$, the +per-unit cost of the arc flow. This value must be integer in the range +[$-$\verb|INT_MAX|, $+$\verb|INT_MAX|]. If \verb|a_cost| $<0$, it is +assumed that $c_{ij}=0$ for all arcs. + +The parameter \verb|sol| specifies a location, to which the routine +stores the objective value (that is, the total cost) found. If +\verb|sol| is NULL, the objective value is not stored. + +The parameter \verb|a_x| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores +$x_{ij}$, the arc flow found. If \verb|a_x| $<0$, the arc flow value is +not stored. + +The parameter \verb|v_pi| specifies an offset of the field of type +\verb|double| in the vertex data block, to which the routine stores +$\pi_i$, the node potential, which is the Lagrange multiplier for the +corresponding flow conservation equality constraint (see (2) in +Subsection ``Background''). If necessary, the application program may +use the node potentials to compute $\lambda_{ij}$, reduced costs of the +arc flows $x_{ij}$, which are the Lagrange multipliers for the arc flow +bound constraints (see (3) ibid.), using the following formula: +$$\lambda_{ij}=c_{ij}-(\pi_i-\pi_j),$$ +where $c_{ij}$ is the per-unit cost for arc $(i,j)$. + +Note that all solution components (the objective value, arc flows, and +node potentials) computed by the routine are always integer-valued. + +\subsubsection*{Returns} + +\def\arraystretch{1} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & Optimal solution found.\\ +\verb|GLP_ENOPFS| & No (primal) feasible solution exists.\\ +\verb|GLP_EDATA| & Unable to start the search, because some problem +data are either not integer-valued or out of range. This code is also +returned if the total supply, which is the sum of $b_i$ over all source +nodes (nodes with $b_i>0$), exceeds \verb|INT_MAX|.\\ +\end{tabular} + +\noindent +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +\verb|GLP_ERANGE| & The search was prematurely terminated because of +integer overflow.\\ +\verb|GLP_EFAIL| & An error has been detected in the program logic. +(If this code is returned for your problem instance, please report to +\verb||.)\\ +\end{tabular} + +\subsubsection*{Comments} + +By design the out-of-kilter algorithm is applicable only to networks, +where $b_i=0$ for {\it all} nodes, i.e. actually this algorithm finds a +minimal cost {\it circulation}. Due to this requirement the routine +\verb|glp_mincost_okalg| converts the original network to a network +suitable for the out-of-kilter algorithm in the following +way:\footnote{The conversion is performed internally and does not change +the original network program object passed to the routine.} + +1) it adds two auxiliary nodes $s$ and $t$; + +2) for each original node $i$ with $b_i>0$ the routine adds auxiliary +supply arc $(s\rightarrow i)$, flow $x_{si}$ through which is costless +($c_{si}=0$) and fixed to $+b_i$ ($l_{si}=u_{si}=+b_i$); + +3) for each original node $i$ with $b_i<0$ the routine adds auxiliary +demand arc $(i\rightarrow t)$, flow $x_{it}$ through which is costless +($c_{it}=0$) and fixed to $-b_i$ ($l_{it}=u_{it}=-b_i$); + +4) finally, the routine adds auxiliary feedback arc $(t\rightarrow s)$, +flow $x_{ts}$ through which is also costless ($c_{ts}=0$) and fixed to +$F$ ($l_{ts}=u_{ts}=F$), where $\displaystyle F=\sum_{b_i>0}b_i$ is the +total supply. + +\subsubsection*{Example} + +The example program below reads the minimum cost problem instance in +DIMACS format from file `\verb|sample.min|', solves it by using the +routine \verb|glp_mincost_okalg|, and writes the solution found to the +standard output. + +\begin{footnotesize} +\begin{verbatim} +#include +#include +#include +#include + +typedef struct { double rhs, pi; } v_data; +typedef struct { double low, cap, cost, x; } a_data; + +#define node(v) ((v_data *)((v)->data)) +#define arc(a) ((a_data *)((a)->data)) + +int main(void) +{ glp_graph *G; + glp_vertex *v, *w; + glp_arc *a; + int i, ret; + double sol; + G = glp_create_graph(sizeof(v_data), sizeof(a_data)); + glp_read_mincost(G, offsetof(v_data, rhs), + offsetof(a_data, low), offsetof(a_data, cap), + offsetof(a_data, cost), "sample.min"); + ret = glp_mincost_okalg(G, offsetof(v_data, rhs), + offsetof(a_data, low), offsetof(a_data, cap), + offsetof(a_data, cost), &sol, offsetof(a_data, x), + offsetof(v_data, pi)); + printf("ret = %d; sol = %5g\n", ret, sol); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + printf("node %d: pi = %5g\n", i, node(v)->pi); + for (a = v->out; a != NULL; a = a->t_next) + { w = a->head; + printf("arc %d->%d: x = %5g; lambda = %5g\n", + v->i, w->i, arc(a)->x, + arc(a)->cost - (node(v)->pi - node(w)->pi)); + } + } + glp_delete_graph(G); + return 0; +} +\end{verbatim} +\end{footnotesize} + +If `\verb|sample.min|' is the example data file from the subsection +describing the routine \verb|glp_read_mincost|, the output may look like +follows: + +\begin{footnotesize} +\begin{verbatim} +Reading min-cost flow problem data from `sample.min'... +Flow network has 9 nodes and 14 arcs +24 lines were read +ret = 0; sol = 213 +node 1: pi = -12 +arc 1->4: x = 13; lambda = 0 +arc 1->2: x = 7; lambda = 0 +node 2: pi = -12 +arc 2->4: x = 0; lambda = 3 +arc 2->3: x = 7; lambda = 0 +node 3: pi = -14 +arc 3->8: x = 5; lambda = 0 +arc 3->5: x = 2; lambda = 3 +node 4: pi = -12 +arc 4->5: x = 13; lambda = 0 +node 5: pi = -12 +arc 5->7: x = 4; lambda = -1 +arc 5->6: x = 11; lambda = 0 +arc 5->2: x = 0; lambda = 1 +node 6: pi = -17 +arc 6->8: x = 4; lambda = 3 +arc 6->7: x = 7; lambda = -3 +node 7: pi = -20 +arc 7->9: x = 11; lambda = 0 +node 8: pi = -14 +arc 8->9: x = 9; lambda = 0 +node 9: pi = -23 +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_netgen---Klingman's network problem generator} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_netgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, + const int parm[1+15]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_netgen| is a GLPK version of the network problem +generator developed by Dr.~Darwin~Klingman.\footnote{D.~Klingman, +A.~Napier, and J.~Stutz. NETGEN: A program for generating large scale +capacitated assignment, transportation, and minimum cost flow networks. +Management Science 20 (1974), 814-20.} It can create capacitated and +uncapacitated minimum cost flow (or transshipment), transportation, and +assignment problems. + +The parameter \verb|G| specifies the graph object, to which the +generated problem data have to be stored. Note that on entry the graph +object is erased with the routine \verb|glp_erase_graph|. + +The parameter \verb|v_rhs| specifies an offset of the field of type +\verb|double| in the vertex data block, to which the routine stores the +supply or demand value. If \verb|v_rhs| $<0$, the value is not stored. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores the +arc capacity. If \verb|a_cap| $<0$, the capacity is not stored. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores the +per-unit cost if the arc flow. If \verb|a_cost| $<0$, the cost is not +stored. + +The array \verb|parm| contains description of the network to be +generated: + +\begin{tabular}{@{}lll@{}} +\verb|parm[0] |& ¬ used\\ +\verb|parm[1] |&\verb|iseed |&8-digit positive random number seed\\ +\verb|parm[2] |&\verb|nprob |&8-digit problem id number\\ +\verb|parm[3] |&\verb|nodes |&total number of nodes\\ +\verb|parm[4] |&\verb|nsorc |&total number of source nodes\\ +&&(including transshipment nodes)\\ +\verb|parm[5] |&\verb|nsink |&total number of sink nodes\\ +&&(including transshipment nodes)\\ +\verb|parm[6] |&\verb|iarcs |&number of arc\\ +\verb|parm[7] |&\verb|mincst|&minimum cost for arcs\\ +\verb|parm[8] |&\verb|maxcst|&maximum cost for arcs\\ +\verb|parm[9] |&\verb|itsup |&total supply\\ +\end{tabular} + +\begin{tabular}{@{}lll@{}} +\verb|parm[10]|&\verb|ntsorc|&number of transshipment source nodes\\ +\verb|parm[11]|&\verb|ntsink|&number of transshipment sink nodes\\ +\verb|parm[12]|&\verb|iphic |&percentage of skeleton arcs to be given +the maxi-\\&&mum cost\\ +\verb|parm[13]|&\verb|ipcap |&percentage of arcs to be capacitated\\ +\verb|parm[14]|&\verb|mincap|&minimum upper bound for capacitated arcs\\ +\verb|parm[15]|&\verb|maxcap|&maximum upper bound for capacitated arcs\\ +\end{tabular} + +\subsubsection*{Notes} + +\noindent\indent +1. The routine generates a transportation problem if: +$${\tt nsorc}+{\tt nsink}={\tt nodes}, +\ {\tt ntsorc}=0,\ \mbox{and}\ {\tt ntsink}=0.$$ + +2. The routine generates an assignment problem if the requirements for +a transportation problem are met and: +$${\tt nsorc}={\tt nsink}\ \mbox{and}\ {\tt itsup}={\tt nsorc}.$$ + +3. The routine always generates connected graphs. So, if the number of +requested arcs has been reached and the generated instance is not fully +connected, the routine generates a few remaining arcs to ensure +connectedness. Thus, the actual number of arcs generated by the routine +may be greater than the requested number of arcs. + +\subsubsection*{Returns} + +If the instance was successfully generated, the routine +\verb|glp_netgen| returns zero; otherwise, if specified parameters are +inconsistent, the routine returns a non-zero error code. + +\subsection{glp\_gridgen---grid-like network problem generator} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_gridgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, + const int parm[1+14]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_gridgen| is a GLPK version of the grid-like +network problem generator developed by Yusin Lee and Jim +Orlin.\footnote{Y.~Lee and J.~Orlin. GRIDGEN generator., 1991. The +original code is publically available from +{\tt}.} + +The parameter \verb|G| specifies the graph object, to which the +generated problem data have to be stored. Note that on entry the graph +object is erased with the routine \verb|glp_erase_graph|. + +The parameter \verb|v_rhs| specifies an offset of the field of type +\verb|double| in the vertex data block, to which the routine stores the +supply or demand value. If \verb|v_rhs| $<0$, the value is not stored. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores the +arc capacity. If \verb|a_cap| $<0$, the capacity is not stored. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores the +per-unit cost if the arc flow. If \verb|a_cost| $<0$, the cost is not +stored. + +The array \verb|parm| contains parameters of the network to be +generated: + +\begin{tabular}{@{}ll@{}} +\verb|parm[0] |¬ used\\ +\verb|parm[1] |&two-ways arcs indicator:\\ + &1 --- if links in both direction should be generated\\ + &0 --- otherwise\\ +\verb|parm[2] |&random number seed (a positive integer)\\ +\verb|parm[3] |&number of nodes (the number of nodes generated might +be\\&slightly different to make the network a grid)\\ +\verb|parm[4] |&grid width\\ +\verb|parm[5] |&number of sources\\ +\verb|parm[6] |&number of sinks\\ +\verb|parm[7] |&average degree\\ +\verb|parm[8] |&total flow\\ +\verb|parm[9] |&distribution of arc costs:\\ + &1 --- uniform\\ + &2 --- exponential\\ +\verb|parm[10]|&lower bound for arc cost (uniform)\\ + &$100\lambda$ (exponential)\\ +\verb|parm[11]|&upper bound for arc cost (uniform)\\ + ¬ used (exponential)\\ +\verb|parm[12]|&distribution of arc capacities:\\ + &1 --- uniform\\ + &2 --- exponential\\ +\verb|parm[13]|&lower bound for arc capacity (uniform)\\ + &$100\lambda$ (exponential)\\ +\verb|parm[14]|&upper bound for arc capacity (uniform)\\ + ¬ used (exponential)\\ +\end{tabular} + +\subsubsection*{Returns} + +If the instance was successfully generated, the routine +\verb|glp_gridgen| returns zero; otherwise, if specified parameters are +inconsistent, the routine returns a non-zero error code. + +\subsubsection*{Comments\footnote{This material is based on comments +to the original version of GRIDGEN.}} + +This network generator generates a grid-like network plus a super node. +In additional to the arcs connecting the nodes in the grid, there is an +arc from each supply node to the super node and from the super node to +each demand node to guarantee feasiblity. These arcs have very high +costs and very big capacities. + +The idea of this network generator is as follows: First, a grid of +$n_1\times n_2$ is generated. For example, $5\times 3$. The nodes are +numbered as 1 to 15, and the supernode is numbered as $n_1\times n_2+1$. +Then arcs between adjacent nodes are generated. For these arcs, the user +is allowed to specify either to generate two-way arcs or one-way arcs. +If two-way arcs are to be generated, two arcs, one in each direction, +will be generated between each adjacent node pairs. Otherwise, only one +arc will be generated. If this is the case, the arcs will be generated +in alterntive directions as shown below. + +\bigskip + +\noindent\hfil +\xymatrix +{1\ar[r]\ar[d]&2\ar[r]&3\ar[r]\ar[d]&4\ar[r]&5\ar[d]\\ +6\ar[d]&7\ar[l]\ar[u]&8\ar[l]\ar[d]&9\ar[l]\ar[u]&10\ar[l]\ar[d]\\ +11\ar[r]&12\ar[r]\ar[u]&13\ar[r]&14\ar[r]\ar[u]&15\\ +} + +\bigskip + +Then the arcs between the super node and the source/sink nodes are added +as mentioned before. If the number of arcs still doesn't reach the +requirement, additional arcs will be added by uniformly picking random +node pairs. There is no checking to prevent multiple arcs between any +pair of nodes. However, there will be no self-arcs (arcs that poins back +to its tail node) in the network. + +The source and sink nodes are selected uniformly in the network, and +the imbalances of each source/sink node are also assigned by uniform +distribution. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Maximum flow problem} + +\subsection{Background} + +The {\it maximum flow problem} (MAXFLOW) is stated as follows. Let +there be given a directed graph (flow network) $G=(V,A)$, where $V$ is +a set of vertices (nodes), and $A\subseteq V\times V$ is a set of arcs. +Let also for each arc $a=(i,j)\in A$ there be given its capacity +$u_{ij}$. The problem is, for given {\it source} node $s\in V$ and +{\it sink} node $t\in V$, to find flows $x_{ij}$ through every arc of +the network, which satisfy the specified arc capacities and the +conservation constraints at all nodes, and maximize the total flow $F$ +through the network from $s$ to $t$. Here the conservation constraint +at a node means that the total flow entering this node through its +incoming arcs (plus $F$, if it is the source node) must be equal to the +total flow leaving this node through its outgoing arcs (plus $F$, if it +is the sink node). + +An example of the maximum flow problem, where $s=v_1$ and $t=v_9$, is +shown on Fig.~2. + +\bigskip + +\noindent\hfil +\xymatrix @C=48pt +{_{F}\ar@{~>}[d]& +v_2\ar[r]|{_{10}}\ar[dd]|{_{9}}& +v_3\ar[dd]|{_{12}}\ar[r]|{_{18}}& +v_8\ar[rd]|{_{20}}&\\ +v_1\ar[ru]|{_{14}}\ar[rd]|{_{23}}&&& +v_6\ar[d]|{_{7}}\ar[u]|{_{8}}& +v_9\ar@{~>}[d]\\ +&v_4\ar[r]|{_{26}}& +v_5\ar[luu]|{_{11}}\ar[ru]|{_{25}}\ar[r]|{_{4}}& +v_7\ar[ru]|{_{15}}&_{F}\\ +} + +\bigskip + +\noindent\hfil +Fig.~2. An example of the maximum flow problem. + +\bigskip + +The maximum flow problem can be naturally formulated as the following +LP problem: + +\medskip + +\noindent +\hspace{.5in}maximize +$$F\eqno(4)$$ +\hspace{.5in}subject to +$$\sum_{(i,j)\in A}x_{ij}-\sum_{(j,i)\in A}x_{ji}=\left\{ +\begin{array}{@{\ }rl} ++F,&\hbox{for}\ i=s\\ + 0,&\hbox{for all}\ i\in V\backslash\{s,t\}\\ +-F,&\hbox{for}\ i=t\\ +\end{array} +\right.\eqno(5) +$$ +$$0\leq x_{ij}\leq u_{ij}\ \ \ \hbox{for all}\ (i,j)\in A +\eqno(6)$$ + +\medskip + +\noindent +where $F\geq 0$ is an additional variable playing the role of the +objective. + +\newpage + +Another LP formulation of the maximum flow problem, which does not +include the variable $F$, is the following: + +\medskip + +\noindent +\hspace{.5in}maximize +$$z=\sum_{(s,j)\in A}x_{sj}-\sum_{(j,s)\in A}x_{js}\ (=F)\eqno(7)$$ +\hspace{.5in}subject to +$$\sum_{(i,j)\in A}x_{ij}-\sum_{(j,i)\in A}x_{ji}\left\{ +\begin{array}{@{\ }rl} +\geq 0,&\hbox{for}\ i=s\\ +=0,&\hbox{for all}\ i\in V\backslash\{s,t\}\\ +\leq 0,&\hbox{for}\ i=t\\ +\end{array} +\right.\eqno(8) +$$ +$$0\leq x_{ij}\leq u_{ij}\ \ \ \hbox{for all}\ (i,j)\in A +\eqno(9)$$ + +\subsection{glp\_read\_maxflow---read maximum flow problem data\\in +DIMACS format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_maxflow(glp_graph *G, int *s, int *t, int a_cap, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_maxflow| reads the maximum flow problem +data from a text file in DIMACS format. + +The parameter \verb|G| specifies the graph object, to which the problem +data have to be stored. Note that before reading data the current +content of the graph object is completely erased with the routine +\verb|glp_erase_graph|. + +The pointer \verb|s| specifies a location, to which the routine stores +the ordinal number of the source node. If \verb|s| is \verb|NULL|, the +source node number is not stored. + +The pointer \verb|t| specifies a location, to which the routine stores +the ordinal number of the sink node. If \verb|t| is \verb|NULL|, the +sink node number is not stored. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores +$u_{ij}$, the arc capacity. If \verb|a_cap| $<0$, the arc capacity is +not stored. + +The character string \verb|fname| specifies the name of a text file to +be read in. (If the file name name ends with the suffix `\verb|.gz|', +the file is assumed to be compressed, in which case the routine +decompresses it ``on the fly''.) + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise, +it prints an error message and returns non-zero. + +\subsubsection*{Example} + +\begin{footnotesize} +\begin{verbatim} +typedef struct +{ /* arc data block */ + ... + double cap; + ... +} a_data; + +int main(void) +{ glp_graph *G; + int s, t, ret; + G = glp_create_graph(..., sizeof(a_data)); + ret = glp_read_maxflow(G, &s, &t, offsetof(a_data, cap), + "sample.max"); + if (ret != 0) goto ... + ... +} +\end{verbatim} +\end{footnotesize} + +\subsubsection*{DIMACS maximum flow problem format\footnote{This +material is based on the paper ``The First DIMACS International +Algorithm Implementation Challenge: Problem Definitions and +Specifications'', which is publically available at +{\tt http://dimacs.rutgers.edu/Challenges/}.}} +\label{subsecmaxflow} + +The DIMACS input file is a plain ASCII text file. It contains +{\it lines} of several types described below. A line is terminated with +an end-of-line character. Fields in each line are separated by at least +one blank space. Each line begins with a one-character designator to +identify the line type. + +Note that DIMACS requires all numerical quantities to be integers in +the range $[-2^{31},\ 2^{31}-1]$ while GLPK allows the quantities to be +floating-point numbers. + +\paragraph{Comment lines.} Comment lines give human-readable information +about the file and are ignored by programs. Comment lines can appear +anywhere in the file. Each comment line begins with a lower-case +character \verb|c|. + +\begin{verbatim} + c This is a comment line +\end{verbatim} + +\newpage + +\paragraph{Problem line.} There is one problem line per data file. The +problem line must appear before any node or arc descriptor lines. It has +the following format: + +\begin{verbatim} + p max NODES ARCS +\end{verbatim} + +\noindent +The lower-case character \verb|p| signifies that this is a problem line. +The three-character problem designator \verb|max| identifies the file as +containing specification information for the maximum flow problem. The +\verb|NODES| field contains an integer value specifying the number of +nodes in the network. The \verb|ARCS| field contains an integer value +specifying the number of arcs in the network. + +\paragraph{Node descriptors.} Two node descriptor lines for the source +and sink nodes must appear before all arc descriptor lines. They may +appear in either order, each with the following format: + +\begin{verbatim} + n ID WHICH +\end{verbatim} + +\noindent +The lower-case character \verb|n| signifies that this a node descriptor +line. The \verb|ID| field gives a node identification number, an integer +between 1 and \verb|NODES|. The \verb|WHICH| field gives either a +lower-case \verb|s| or \verb|t|, designating the source and sink, +respectively. + +\paragraph{Arc descriptors.} There is one arc descriptor line for each +arc in the network. Arc descriptor lines are of the following format: + +\begin{verbatim} + a SRC DST CAP +\end{verbatim} + +\noindent +The lower-case character \verb|a| signifies that this is an arc +descriptor line. For a directed arc $(i,j)$ the \verb|SRC| field gives +the identification number $i$ for the tail endpoint, and the \verb|DST| +field gives the identification number $j$ for the head endpoint. +Identification numbers are integers between 1 and \verb|NODES|. The +\verb|CAP| field gives the arc capacity, i.e. maximum amount of flow +that can be sent along arc $(i,j)$ in a feasible flow. + +\paragraph{Example.} Below here is an example of the data file in +DIMACS format corresponding to the maximum flow problem shown on Fig~2. + +\newpage + +\begin{footnotesize} +\begin{verbatim} +c sample.max +c +c This is an example of the maximum flow problem data +c in DIMACS format. +c +p max 9 14 +c +n 1 s +n 9 t +c +a 1 2 14 +a 1 4 23 +a 2 3 10 +a 2 4 9 +a 3 5 12 +a 3 8 18 +a 4 5 26 +a 5 2 11 +a 5 6 25 +a 5 7 4 +a 6 7 7 +a 6 8 8 +a 7 9 15 +a 8 9 20 +c +c eof +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_write\_maxflow---write maximum flow problem data\\ +in DIMACS format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_maxflow(glp_graph *G, int s, int t, int a_cap, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_maxflow| writes the maximum flow problem +data to a text file in DIMACS format. + +The parameter \verb|G| is the graph (network) program object, which +specifies the maximum flow problem instance. + +The parameter \verb|s| specifies the ordinal number of the source node. + +The parameter \verb|t| specifies the ordinal number of the sink node. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $u_{ij}$, the upper +bound to the arc flow (the arc capacity). If the upper bound is +specified as \verb|DBL_MAX|, it is assumed that $u_{ij}=\infty$, i.e. +the arc is uncapacitated. If \verb|a_cap| $<0$, it is assumed that +$u_{ij}=1$ for all arcs. + +The character string \verb|fname| specifies a name of the text file to +be written out. (If the file name ends with suffix `\verb|.gz|', the +file is assumed to be compressed, in which case the routine performs +automatic compression on writing it.) + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise, +it prints an error message and returns non-zero. + +\subsection{glp\_maxflow\_lp---convert maximum flow problem to LP} + +\subsubsection*{Synopsis} + +\begin{verbatim} +void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, + int s, int t, int a_cap); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_maxflow_lp| builds LP problem (7)---(9), which +corresponds to the specified maximum flow problem. + +The parameter \verb|lp| is the resultant LP problem object to be built. +Note that on entry its current content is erased with the routine +\verb|glp_erase_prob|. + +The parameter \verb|G| is the graph (network) program object, which +specifies the maximum flow problem instance. + +The parameter \verb|names| is a flag. If it is \verb|GLP_ON|, the +routine uses symbolic names of the graph object components to assign +symbolic names to the LP problem object components. If the flag is +\verb|GLP_OFF|, no symbolic names are assigned. + +The parameter \verb|s| specifies the ordinal number of the source node. + +The parameter \verb|t| specifies the ordinal number of the sink node. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $u_{ij}$, the upper +bound to the arc flow (the arc capacity). If the upper bound is +specified as \verb|DBL_MAX|, it is assumed that $u_{ij}=\infty$, i.e. +the arc is uncapacitated. If \verb|a_cap| $<0$, it is assumed that +$u_{ij}=1$ for all arcs. + +\subsubsection*{Example} + +The example program below reads the maximum flow problem in DIMACS +format from file `\verb|sample.max|', converts the instance to LP, and +then writes the resultant LP in CPLEX format to file +`\verb|maxflow.lp|'. + +\begin{footnotesize} +\begin{verbatim} +#include +#include + +int main(void) +{ glp_graph *G; + glp_prob *lp; + int s, t; + G = glp_create_graph(0, sizeof(double)); + glp_read_maxflow(G, &s, &t, 0, "sample.max"); + lp = glp_create_prob(); + glp_maxflow_lp(lp, G, GLP_ON, s, t, 0); + glp_delete_graph(G); + glp_write_lp(lp, NULL, "maxflow.lp"); + glp_delete_prob(lp); + return 0; +} +\end{verbatim} +\end{footnotesize} + +If `\verb|sample.max|' is the example data file from the previous +subsection, the output `\verb|maxflow.lp|' may look like follows: + +\begin{footnotesize} +\begin{verbatim} +Maximize + obj: + x(1,4) + x(1,2) + +Subject To + r_1: + x(1,2) + x(1,4) >= 0 + r_2: - x(5,2) + x(2,3) + x(2,4) - x(1,2) = 0 + r_3: + x(3,5) + x(3,8) - x(2,3) = 0 + r_4: + x(4,5) - x(2,4) - x(1,4) = 0 + r_5: + x(5,2) + x(5,6) + x(5,7) - x(4,5) - x(3,5) = 0 + r_6: + x(6,7) + x(6,8) - x(5,6) = 0 + r_7: + x(7,9) - x(6,7) - x(5,7) = 0 + r_8: + x(8,9) - x(6,8) - x(3,8) = 0 + r_9: - x(8,9) - x(7,9) <= 0 + +Bounds + 0 <= x(1,4) <= 23 + 0 <= x(1,2) <= 14 + 0 <= x(2,4) <= 9 + 0 <= x(2,3) <= 10 + 0 <= x(3,8) <= 18 + 0 <= x(3,5) <= 12 + 0 <= x(4,5) <= 26 + 0 <= x(5,7) <= 4 + 0 <= x(5,6) <= 25 + 0 <= x(5,2) <= 11 + 0 <= x(6,8) <= 8 + 0 <= x(6,7) <= 7 + 0 <= x(7,9) <= 15 + 0 <= x(8,9) <= 20 + +End +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_maxflow\_ffalg---solve maximum flow problem with +Ford-Fulkerson algorithm} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_maxflow_ffalg(glp_graph *G, int s, int t, int a_cap, + double *sol, int a_x, int v_cut); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mincost_ffalg| finds optimal solution to the +maximum flow problem with the Ford-Fulkerson algorithm.\footnote{GLPK +implementation of the Ford-Fulkerson algorithm is based on the following +book: L.~R.~Ford,~Jr., and D.~R.~Fulkerson, ``Flows in Networks,'' The +RAND Corp., Report\linebreak R-375-PR (August 1962), Chap. I +``Static Maximal Flow,'' pp.~30-33.} Note that this routine requires all +the problem data to be integer-valued. + +The parameter \verb|G| is a graph (network) program object which +specifies the maximum flow problem instance to be solved. + +The parameter $s$ specifies the ordinal number of the source node. + +The parameter $t$ specifies the ordinal number of the sink node. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $u_{ij}$, the upper +bound to the arc flow (the arc capacity). This bound must be integer in +the range [0, \verb|INT_MAX|]. If \verb|a_cap| $<0$, it is assumed that +$u_{ij}=1$ for all arcs. + +The parameter \verb|sol| specifies a location, to which the routine +stores the objective value (that is, the total flow from $s$ to $t$) +found. If \verb|sol| is NULL, the objective value is not stored. + +The parameter \verb|a_x| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores +$x_{ij}$, the arc flow found. If \verb|a_x| $<0$, the arc flow values +are not stored. + +The parameter \verb|v_cut| specifies an offset of the field of type +\verb|int| in the vertex data block, to which the routine stores node +flags corresponding to the optimal solution found: if the node flag is +1, the node is labelled, and if the node flag is 0, the node is +unlabelled. The calling program may use these node flags to determine +the {\it minimal cut}, which is a subset of arcs whose one endpoint is +labelled and other is not. If \verb|v_cut| $<0$, the node flags are not +stored. + +Note that all solution components (the objective value and arc flows) +computed by the routine are always integer-valued. + +\subsubsection*{Returns} + +\def\arraystretch{1} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & Optimal solution found.\\ +\verb|GLP_EDATA| & Unable to start the search, because some problem +data are either not integer-valued or out of range.\\ +\end{tabular} + +\subsubsection*{Example} + +The example program shown below reads the maximum flow problem instance +in DIMACS format from file `\verb|sample.max|', solves it using the +routine \verb|glp_maxflow_ffalg|, and write the solution found to the +standard output. + +\begin{footnotesize} +\begin{verbatim} +#include +#include +#include +#include + +typedef struct { int cut; } v_data; +typedef struct { double cap, x; } a_data; + +#define node(v) ((v_data *)((v)->data)) +#define arc(a) ((a_data *)((a)->data)) + +int main(void) +{ glp_graph *G; + glp_vertex *v, *w; + glp_arc *a; + int i, s, t, ret; + double sol; + G = glp_create_graph(sizeof(v_data), sizeof(a_data)); + glp_read_maxflow(G, &s, &t, offsetof(a_data, cap), + "sample.max"); + ret = glp_maxflow_ffalg(G, s, t, offsetof(a_data, cap), + &sol, offsetof(a_data, x), offsetof(v_data, cut)); + printf("ret = %d; sol = %5g\n", ret, sol); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { w = a->head; + printf("x[%d->%d] = %5g (%d)\n", v->i, w->i, + arc(a)->x, node(v)->cut ^ node(w)->cut); + } + } + glp_delete_graph(G); + return 0; +} +\end{verbatim} +\end{footnotesize} + +If `\verb|sample.max|' is the example data file from the subsection +describing the routine \verb|glp_read_maxflow|, the output may look like +follows: + +\begin{footnotesize} +\begin{verbatim} +Reading maximum flow problem data from `sample.max'... +Flow network has 9 nodes and 14 arcs +24 lines were read +ret = 0; sol = 29 +x[1->4] = 19 (0) +x[1->2] = 10 (0) +x[2->4] = 0 (0) +x[2->3] = 10 (1) +x[3->8] = 10 (0) +x[3->5] = 0 (1) +x[4->5] = 19 (0) +x[5->7] = 4 (1) +x[5->6] = 15 (0) +x[5->2] = 0 (0) +x[6->8] = 8 (1) +x[6->7] = 7 (1) +x[7->9] = 11 (0) +x[8->9] = 18 (0) +\end{verbatim} +\end{footnotesize} + +\newpage + +\subsection{glp\_rmfgen---Goldfarb's maximum flow problem generator} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_rmfgen(glp_graph *G, int *s, int *t, int a_cap, + const int parm[1+5]); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_rmfgen| is a GLPK version of the maximum flow +problem generator developed by D.~Goldfarb and +M.~Grigoriadis.\footnote{D.~Goldfarb and M.~D.~Grigoriadis, +``A computational comparison of the Dinic and network simplex methods +for maximum flow.'' Annals of Op. Res. 13 (1988), +pp.~83-123.}$^{,}$\footnote{U.~Derigs and W.~Meier, ``Implementing +Goldberg's max-flow algorithm: A computational investigation.'' +Zeitschrift f\"ur Operations Research 33 (1989), +pp.~383-403.}$^{,}$\footnote{The original code of RMFGEN implemented by +Tamas Badics is publically available from +{\tt }.} + +The parameter \verb|G| specifies the graph object, to which the +generated problem data have to be stored. Note that on entry the graph +object is erased with the routine \verb|glp_erase_graph|. + +The pointers \verb|s| and \verb|t| specify locations, to which the +routine stores the source and sink node numbers, respectively. If +\verb|s| or \verb|t| is \verb|NULL|, corresponding node number is not +stored. + +The parameter \verb|a_cap| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores the arc +capacity. If \verb|a_cap| $<0$, the capacity is not stored. + +The array \verb|parm| contains description of the network to be +generated: + +\begin{tabular}{@{}lll@{}} +\verb|parm[0]|& ¬ used\\ +\verb|parm[1]|&\verb|seed|&random number seed (a positive integer)\\ +\verb|parm[2]|&\verb|a |&frame size\\ +\verb|parm[3]|&\verb|b |&depth\\ +\verb|parm[4]|&\verb|c1 |&minimal arc capacity\\ +\verb|parm[5]|&\verb|c2 |&maximal arc capacity\\ +\end{tabular} + +\subsubsection*{Returns} + +If the instance was successfully generated, the routine +\verb|glp_netgen| returns zero; otherwise, if specified parameters are +inconsistent, the routine returns a non-zero error code. + +\newpage + +\subsubsection*{Comments\footnote{This material is based on comments +to the original version of RMFGEN.}} + +The generated network is as follows. It has $b$ pieces of frames of +size $a\times a$. (So alltogether the number of vertices is +$a\times a\times b$.) + +In each frame all the vertices are connected with their neighbours +(forth and back). In addition the vertices of a frame are connected +one to one with the vertices of next frame using a random permutation +of those vertices. + +The source is the lower left vertex of the first frame, the sink is +the upper right vertex of the $b$-th frame. + +\begin{verbatim} + t + +-------+ + | .| + | . | + / | / | + +-------+/ -+ b + | | |/. + a | -v- |/ + | | |/ + +-------+ 1 + s a +\end{verbatim} + +The capacities are randomly chosen integers from the range of +$[c_1,c_2]$ in the case of interconnecting edges, and $c_2\cdot a^2$ +for the in-frame edges. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Assignment problem} + +\subsection{Background} + +Let there be given an undirected bipartite graph $G=(R\cup S,E)$, where +$R$ and $S$ are disjoint sets of vertices (nodes), and +$E\subseteq R\times S$ is a set of edges. Let also for each edge +$e=(i,j)\in E$ there be given its cost $c_{ij}$. A {\it matching} +(which in case of bipartite graph is also called {\it assignment}) +$M\subseteq E$ in $G$ is a set of pairwise non-adjacent edges, that is, +no two edges in $M$ share a common vertex. A matching, which matches +all vertices of the graph, is called a {\it perfect matching}. +Obviously, a perfect matching in bipartite graph $G=(R\cup S,E)$ +defines some bijection $R\leftrightarrow S$. + +The {\it assignment problem} has two different variants. In the first +variant the problem is to find matching (assignment) $M$, which +maximizes the sum: +$$\sum_{(i,j)\in M}c_{ij}\eqno(10)$$ +(so this variant is also called the {\it maximum weighted bipartite +matching problem} or, if all $c_{ij}=1$, the {\it maximum cardinality +bipartite matching problem}). In the second, classic variant the +problem is to find {\it perfect} matching (assignment) $M$, which +minimizes or maximizes the sum (10). + +An example of the assignment problem, which is the maximum weighted +bipartite matching problem, is shown on Fig. 3. + +The maximum weighted bipartite matching problem can be naturally +formulated as the following LP problem: + +\medskip + +\noindent +\hspace{.5in}maximize +$$z=\sum_{(i,j)\in E}c_{ij}x_{ij}\eqno(11)$$ +\hspace{.5in}subject to +$$\sum_{(i,j)\in E}x_{ij}\leq 1\ \ \ \hbox{for all}\ i\in R\eqno(12)$$ +$$\sum_{(i,j)\in E}x_{ij}\leq 1\ \ \ \hbox{for all}\ j\in S\eqno(13)$$ +$$\ \ \ \ \ \ \ \ 0\leq x_{ij}\leq 1\ \ \ \hbox{for all}\ (i,j)\in E +\eqno(14)$$ + +\medskip + +\noindent +where $x_{ij}=1$ means that $(i,j)\in M$, and $x_{ij}=0$ means that +$(i,j)\notin M$.\footnote{The constraint matrix of LP formulation +(11)---(14) is totally unimodular, due to which $x_{ij}\in\{0,1\}$ for +any basic solution.} + +\newpage + +\bigskip + +\noindent\hfil +\xymatrix @C=48pt +{v_1\ar@{-}[rr]|{_{13}}\ar@{-}[rrd]|{_{21}}\ar@{-}[rrddd]|(.2){_{20}}&& +v_9\\ +v_2\ar@{-}[rr]|{_{12}}\ar@{-}[rrdd]|(.3){_{8}} +\ar@{-}[rrddd]|(.4){_{26}}&&v_{10}\\ +v_3\ar@{-}[rr]|(.2){_{22}}\ar@{-}[rrdd]|(.3){_{11}}&&v_{11}\\ +v_4\ar@{-}[rruuu]|(.6){_{12}}\ar@{-}[rr]|(.2){_{36}} +\ar@{-}[rrdd]|(.7){_{25}}&&v_{12}\\ +v_5\ar@{-}[rruu]|(.42){_{41}}\ar@{-}[rru]|(.4){_{40}} +\ar@{-}[rr]|(.75){_{11}}\ar@{-}[rrd]|(.6){_{4}}\ar@{-}[rrdd]|{_{8}} +\ar@{-}[rrddd]|{_{35}}\ar@{-}[rrdddd]|{_{32}}&&v_{13}\\ +v_6\ar@{-}[rruuuuu]|(.7){_{13}}&&v_{14}\\ +v_7\ar@{-}[rruuuuu]|(.15){_{19}}&&v_{15}\\ +v_8\ar@{-}[rruuuuuu]|(.25){_{39}}\ar@{-}[rruuuuu]|(.65){_{15}}&& +v_{16}\\ +&&v_{17}\\ +} + +\bigskip + +\noindent\hfil +Fig.~3. An example of the assignment problem. + +\bigskip + +Similarly, the perfect assignment problem can be naturally formulated +as the following LP problem: + +\medskip + +\noindent +\hspace{.5in}minimize (or maximize) +$$z=\sum_{(i,j)\in E}c_{ij}x_{ij}\eqno(15)$$ +\hspace{.5in}subject to +$$\sum_{(i,j)\in E}x_{ij}=1\ \ \ \hbox{for all}\ i\in R\eqno(16)$$ +$$\sum_{(i,j)\in E}x_{ij}=1\ \ \ \hbox{for all}\ j\in S\eqno(17)$$ +$$\ \ \ \ \ \ \ \ 0\leq x_{ij}\leq 1\ \ \ \hbox{for all}\ (i,j)\in E +\eqno(18)$$ + +\medskip + +\noindent +where variables $x_{ij}$ have the same meaning as for (11)---(14) +above. + +\newpage + +In GLPK an undirected bipartite graph $G=(R\cup S,E)$ is represented as +directed graph $\overline{G}=(V,A)$, where $V=R\cup S$ and +$A=\{(i,j):(i,j)\in E\}$, i.e. every edge $(i,j)\in E$ in $G$ +corresponds to arc $(i\rightarrow j)\in A$ in $\overline{G}$. + +\subsection{glp\_read\_asnprob---read assignment problem data in\\DIMACS +format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_read_asnprob(glp_graph *G, int v_set, int a_cost, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_read_asnprob| reads the assignment problem data +from a text file in DIMACS format. + +The parameter \verb|G| specifies the graph object, to which the problem +data have to be stored. Note that before reading data the current +content of the graph object is completely erased with the routine +\verb|glp_erase_graph|. + +The parameter \verb|v_set| specifies an offset of the field of type +\verb|int| in the vertex data block, to which the routine stores the +node set indicator: + +0 --- the node is in set $R$; + +1 --- the node is in set $S$. + +\noindent +If \verb|v_set| $<0$, the node set indicator is not stored. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, to which the routine stores the +edge cost $c_{ij}$. If \verb|a_cost| $<0$, the edge cost is not stored. + +The character string \verb|fname| specifies the name of a text file to +be read in. (If the file name name ends with the suffix `\verb|.gz|', +the file is assumed to be compressed, in which case the routine +decompresses it ``on the fly''.) + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise, +it prints an error message and returns non-zero. + +\newpage + +\subsubsection*{Example} + +\begin{footnotesize} +\begin{verbatim} +typedef struct +{ /* vertex data block */ + ... + int set; + ... +} v_data; + +typedef struct +{ /* arc data block */ + ... + double cost; + ... +} a_data; + +int main(void) +{ glp_graph *G; + int ret; + G = glp_create_graph(sizeof(v_data), sizeof(a_data)); + ret = glp_read_asnprob(G, offsetof(v_data, set), + offsetof(a_data, cost), "sample.asn"); + if (ret != 0) goto ... + ... +} +\end{verbatim} +\end{footnotesize} + +\subsubsection*{DIMACS assignment problem format\footnote{This +material is based on the paper ``The First DIMACS International +Algorithm Implementation Challenge: Problem Definitions and +Specifications'', which is publically available at +{\tt http://dimacs.rutgers.edu/Challenges/}.}} +\label{subsecasnprob} + +The DIMACS input file is a plain ASCII text file. It contains +{\it lines} of several types described below. A line is terminated with +an end-of-line character. Fields in each line are separated by at least +one blank space. Each line begins with a one-character designator to +identify the line type. + +Note that DIMACS requires all numerical quantities to be integers in +the range $[-2^{31},\ 2^{31}-1]$ while GLPK allows the quantities to be +floating-point numbers. + +\paragraph{Comment lines.} Comment lines give human-readable information +about the file and are ignored by programs. Comment lines can appear +anywhere in the file. Each comment line begins with a lower-case +character \verb|c|. + +\begin{verbatim} + c This is a comment line +\end{verbatim} + +\newpage + +\paragraph{Problem line.} There is one problem line per data file. The +problem line must appear before any node or arc descriptor lines. It has +the following format: + +\begin{verbatim} + p asn NODES EDGES +\end{verbatim} + +\noindent +The lower-case character \verb|p| signifies that this is a problem line. +The three-character problem designator \verb|asn| identifies the file as +containing specification information for the assignment problem. +The \verb|NODES| field contains an integer value specifying the total +number of nodes in the graph (i.e. in both sets $R$ and $S$). The +\verb|EDGES| field contains an integer value specifying the number of +edges in the graph. + +\paragraph{Node descriptors.} All node descriptor lines must appear +before all edge descriptor lines. The node descriptor lines lists the +nodes in set $R$ only, and all other nodes are assumed to be in set +$S$. There is one node descriptor line for each such node, with the +following format: + +\begin{verbatim} + n ID +\end{verbatim} + +\noindent +The lower-case character \verb|n| signifies that this is a node +descriptor line. The \verb|ID| field gives a node identification number, +an integer between 1 and \verb|NODES|. + +\paragraph{Edge descriptors.} There is one edge descriptor line for +each edge in the graph. Edge descriptor lines are of the following +format: + +\begin{verbatim} + a SRC DST COST +\end{verbatim} + +\noindent +The lower-case character \verb|a| signifies that this is an edge +descriptor line. For each edge $(i,j)$, where $i\in R$ and $j\in S$, +the \verb|SRC| field gives the identification number of vertex $i$, and +the \verb|DST| field gives the identification number of vertex $j$. +Identification numbers are integers between 1 and \verb|NODES|. The +\verb|COST| field contains the cost of edge $(i,j)$. + +\paragraph{Example.} Below here is an example of the data file in +DIMACS format corresponding to the assignment problem shown on Fig~3. + +\newpage + +\begin{footnotesize} +\begin{verbatim} +c sample.asn +c +c This is an example of the assignment problem data +c in DIMACS format. +c +p asn 17 22 +c +n 1 +n 2 +n 3 +n 4 +n 5 +n 6 +n 7 +n 8 +c +a 1 9 13 +a 1 10 21 +a 1 12 20 +a 2 10 12 +a 2 12 8 +a 2 13 26 +a 3 11 22 +a 3 13 11 +a 4 9 12 +a 4 12 36 +a 4 14 25 +a 5 11 41 +a 5 12 40 +a 5 13 11 +a 5 14 4 +a 5 15 8 +a 5 16 35 +a 5 17 32 +a 6 9 13 +a 7 10 19 +a 8 10 39 +a 8 11 15 +c +c eof +\end{verbatim} +\end{footnotesize} + +\newpage + +\subsection{glp\_write\_asnprob---write assignment problem data in\\ +DIMACS format} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_write_asnprob(glp_graph *G, int v_set, int a_cost, + const char *fname); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_write_asnprob| writes the assignment problem data +to a text file in DIMACS format. + +The parameter \verb|G| is the graph program object, which specifies the +assignment problem instance. + +The parameter \verb|v_set| specifies an offset of the field of type +\verb|int| in the vertex data block, which contains the node set +indicator: + +0 --- the node is in set $R$; + +1 --- the node is in set $S$. + +\noindent +If \verb|v_set| $<0$, it is assumed that a node having no incoming arcs +is in set $R$, and a node having no outgoing arcs is in set $S$. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $c_{ij}$, the edge +cost. If \verb|a_cost| $<0$, it is assumed that $c_{ij}=1$ for all +edges. + +The character string \verb|fname| specifies a name of the text file to +be written out. (If the file name ends with suffix `\verb|.gz|', the +file is assumed to be compressed, in which case the routine performs +automatic compression on writing it.) + +\subsubsection*{Note} + +The routine \verb|glp_write_asnprob| does not check that the specified +graph object correctly represents a bipartite graph. To make sure that +the problem data are correct, use the routine \verb|glp_check_asnprob|. + +\subsubsection*{Returns} + +If the operation was successful, the routine returns zero. Otherwise, +it prints an error message and returns non-zero. + +\newpage + +\subsection{glp\_check\_asnprob---check correctness of assignment +problem data} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_check_asnprob(glp_graph *G, int v_set); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_check_asnprob| checks that the specified graph +object \verb|G| correctly represents a bipartite graph. + +The parameter \verb|v_set| specifies an offset of the field of type +\verb|int| in the vertex data block, which contains the node set +indicator: + +0 --- the node is in set $R$; + +1 --- the node is in set $S$. + +\noindent +If \verb|v_set| $<0$, it is assumed that a node having no incoming arcs +is in set $R$, and a node having no outgoing arcs is in set $S$. + +\subsubsection*{Returns} + +The routine \verb|glp_check_asnprob| may return the following codes: + +0 --- the data are correct; + +1 --- the set indicator of some node is 0, however, that node has one +or more incoming arcs; + +2 --- the set indicator of some node is 1, however, that node has one +or more outgoing arcs; + +3 --- the set indicator of some node is invalid (neither 0 nor 1); + +4 --- some node has both incoming and outgoing arcs. + +\subsection{glp\_asnprob\_lp---convert assignment problem to LP} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, + int names, int v_set, int a_cost); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_asnprob_lp| builds LP problem, which corresponds +to the specified assignment problem. + +The parameter \verb|lp| is the resultant LP problem object to be built. +Note that on entry its current content is erased with the routine +\verb|glp_erase_prob|. + +The parameter \verb|form| defines which LP formulation should be used: + +\verb|GLP_ASN_MIN| --- perfect matching (15)---(18), minimization; + +\verb|GLP_ASN_MAX| --- perfect matching (15)---(18), maximization; + +\verb|GLP_ASN_MMP| --- maximum weighted matching (11)---(14). + +The parameter \verb|G| is the graph program object, which specifies the +assignment problem instance. + +The parameter \verb|names| is a flag. If it is \verb|GLP_ON|, the +routine uses symbolic names of the graph object components to assign +symbolic names to the LP problem object components. If the \verb|flag| +is \verb|GLP_OFF|, no symbolic names are assigned. + +The parameter \verb|v_set| specifies an offset of the field of type +\verb|int| in the vertex data block, which contains the node set +indicator: + +0 --- the node is in set $R$; + +1 --- the node is in set $S$. + +\noindent +If \verb|v_set| $<0$, it is assumed that a node having no incoming arcs +is in set $R$, and a node having no outgoing arcs is in set $S$. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $c_{ij}$, the edge +cost. If \verb|a_cost| $<0$, it is assumed that $c_{ij}=1$ for all +edges. + +\subsubsection*{Returns} + +If the LP problem has been successfully built, the routine +\verb|glp_asnprob_lp| returns zero, otherwise, non-zero (see the +routine \verb|glp_check_asnprob|). + +\subsubsection*{Example} + +The example program below reads the assignment problem instance in +DIMACS format from file `\verb|sample.asn|', converts the instance to +LP (11)---(14), and writes the resultant LP in CPLEX format to file +`\verb|matching.lp|'. + +\begin{footnotesize} +\begin{verbatim} +#include +#include + +typedef struct { int set; } v_data; +typedef struct { double cost; } a_data; + +int main(void) +{ glp_graph *G; + glp_prob *P; + G = glp_create_graph(sizeof(v_data), sizeof(a_data)); + glp_read_asnprob(G, offsetof(v_data, set), + offsetof(a_data, cost), "sample.asn"); + P = glp_create_prob(); + glp_asnprob_lp(P, GLP_ASN_MMP, G, GLP_ON, + offsetof(v_data, set), offsetof(a_data, cost)); + glp_delete_graph(G); + glp_write_lp(P, NULL, "matching.lp"); + glp_delete_prob(P); + return 0; +} +\end{verbatim} +\end{footnotesize} + +If `\verb|sample.asn|' is the example data file from the subsection +describing the routine \verb|glp_read_asnprob|, file +`\verb|matching.lp|' may look like follows: + +\begin{footnotesize} +\begin{verbatim} +Maximize + obj: + 20 x(1,12) + 21 x(1,10) + 13 x(1,9) + 26 x(2,13) + 8 x(2,12) + + 12 x(2,10) + 11 x(3,13) + 22 x(3,11) + 25 x(4,14) + 36 x(4,12) + + 12 x(4,9) + 32 x(5,17) + 35 x(5,16) + 8 x(5,15) + 4 x(5,14) + + 11 x(5,13) + 40 x(5,12) + 41 x(5,11) + 13 x(6,9) + 19 x(7,10) + + 15 x(8,11) + 39 x(8,10) + +Subject To + r_1: + x(1,9) + x(1,10) + x(1,12) <= 1 + r_2: + x(2,10) + x(2,12) + x(2,13) <= 1 + r_3: + x(3,11) + x(3,13) <= 1 + r_4: + x(4,9) + x(4,12) + x(4,14) <= 1 + r_5: + x(5,11) + x(5,12) + x(5,13) + x(5,14) + x(5,15) + x(5,16) + + x(5,17) <= 1 + r_6: + x(6,9) <= 1 + r_7: + x(7,10) <= 1 + r_8: + x(8,10) + x(8,11) <= 1 + r_9: + x(6,9) + x(4,9) + x(1,9) <= 1 + r_10: + x(8,10) + x(7,10) + x(2,10) + x(1,10) <= 1 + r_11: + x(8,11) + x(5,11) + x(3,11) <= 1 + r_12: + x(5,12) + x(4,12) + x(2,12) + x(1,12) <= 1 + r_13: + x(5,13) + x(3,13) + x(2,13) <= 1 + r_14: + x(5,14) + x(4,14) <= 1 + r_15: + x(5,15) <= 1 + r_16: + x(5,16) <= 1 + r_17: + x(5,17) <= 1 + +Bounds + 0 <= x(1,12) <= 1 + 0 <= x(1,10) <= 1 + 0 <= x(1,9) <= 1 + 0 <= x(2,13) <= 1 + 0 <= x(2,12) <= 1 + 0 <= x(2,10) <= 1 + 0 <= x(3,13) <= 1 + 0 <= x(3,11) <= 1 + 0 <= x(4,14) <= 1 + 0 <= x(4,12) <= 1 + 0 <= x(4,9) <= 1 + 0 <= x(5,17) <= 1 + 0 <= x(5,16) <= 1 + 0 <= x(5,15) <= 1 + 0 <= x(5,14) <= 1 + 0 <= x(5,13) <= 1 + 0 <= x(5,12) <= 1 + 0 <= x(5,11) <= 1 + 0 <= x(6,9) <= 1 + 0 <= x(7,10) <= 1 + 0 <= x(8,11) <= 1 + 0 <= x(8,10) <= 1 + +End +\end{verbatim} +\end{footnotesize} + +\subsection{glp\_asnprob\_okalg---solve assignment problem with +out-of-kilter algorithm} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_asnprob_okalg(int form, glp_graph *G, int v_set, + int a_cost, double *sol, int a_x); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_mincost_okalg| finds optimal solution to the +assignment problem with the out-of-kilter +algorithm.\footnote{GLPK implementation of the out-of-kilter algorithm +is based on the following book: L.~R.~Ford,~Jr., and D.~R.~Fulkerson, +``Flows in Networks,'' The RAND Corp., Report R-375-PR (August 1962), +Chap. III ``Minimal Cost Flow Problems,'' pp.~113-26.} Note that this +routine requires all the problem data to be integer-valued. + +The parameter \verb|form| defines which LP formulation should be used: + +\verb|GLP_ASN_MIN| --- perfect matching (15)---(18), minimization; + +\verb|GLP_ASN_MAX| --- perfect matching (15)---(18), maximization; + +\verb|GLP_ASN_MMP| --- maximum weighted matching (11)---(14). + +The parameter \verb|G| is the graph program object, which specifies the +assignment problem instance. + +The parameter \verb|v_set| specifies an offset of the field of type +\verb|int| in the vertex data block, which contains the node set +indicator: + +0 --- the node is in set $R$; + +1 --- the node is in set $S$. + +\newpage + +\noindent +If \verb|v_set| $<0$, it is assumed that a node having no incoming arcs +is in set $R$, and a node having no outgoing arcs is in set $S$. + +The parameter \verb|a_cost| specifies an offset of the field of type +\verb|double| in the arc data block, which contains $c_{ij}$, the edge +cost. This value must be integer in the range [\verb|-INT_MAX|, +\verb|+INT_MAX|]. If \verb|a_cost| $<0$, it is assumed that $c_{ij}=1$ +for all edges. + +The parameter \verb|sol| specifies a location, to which the routine +stores the objective value (that is, the total cost) found. +If \verb|sol| is \verb|NULL|, the objective value is not stored. + +The parameter \verb|a_x| specifies an offset of the field of type +\verb|int| in the arc data block, to which the routine stores $x_{ij}$. +If \verb|a_x| $<0$, this value is not stored. + +\subsubsection*{Returns} + +\def\arraystretch{1} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & Optimal solution found.\\ +\verb|GLP_ENOPFS| & No (primal) feasible solution exists.\\ +\verb|GLP_EDATA| & Unable to start the search, because the assignment +problem data are either incorrect (this error is detected by the +routine \verb|glp_check_asnprob|), not integer-valued or out of range.\\ +\verb|GLP_ERANGE| & The search was prematurely terminated because of +integer overflow.\\ +\verb|GLP_EFAIL| & An error has been detected in the program logic. +(If this code is returned for your problem instance, please report to +\verb||.)\\ +\end{tabular} + +\subsubsection*{Comments} + +Since the out-of-kilter algorithm is designed to find a minimal cost +circulation, the routine \verb|glp_asnprob_okalg| converts the original +graph to a network suitable for this algorithm in the following +way:\footnote{The conversion is performed internally and does not change +the original graph program object passed to the routine.} + +1) it replaces each edge $(i,j)$ by arc $(i\rightarrow j)$, +flow $x_{ij}$ through which has zero lower bound ($l_{ij}=0$), unity +upper bound ($u_{ij}=1$), and per-unit cost $+c_{ij}$ (in case of +\verb|GLP_ASN_MIN|), or $-c_{ij}$ (in case of \verb|GLP_ASN_MAX| and +\verb|GLP_ASN_MMP|); + +2) then it adds one auxiliary feedback node $k$; + +\newpage + +3) for each original node $i\in R$ the routine adds auxiliary supply +arc $(k\rightarrow i)$, flow $x_{ki}$ through which is costless +($c_{ki}=0$) and either fixed at 1 ($l_{ki}=u_{ki}=1$, in case of +\verb|GLP_ASN_MIN| and \verb|GLP_ASN_MAX|) or has zero lower bound and +unity upper bound ($l_{ij}=0$, $u_{ij}=1$, in case of +\verb|GLP_ASN_MMP|); + +4) similarly, for each original node $j\in S$ the routine adds +auxiliary demand arc $(j\rightarrow k)$, flow $x_{jk}$ through which is +costless ($c_{jk}=0$) and either fixed at 1 ($l_{jk}=u_{jk}=1$, in case +of \verb|GLP_ASN_MIN| and \verb|GLP_ASN_MAX|) or has zero lower bound +and unity upper bound ($l_{jk}=0$, $u_{jk}=1$, in case of +\verb|GLP_ASN_MMP|). + +\subsubsection*{Example} + +The example program shown below reads the assignment problem instance +in DIMACS format from file `\verb|sample.asn|', solves it by using the +routine \verb|glp_asnprob_okalg|, and writes the solution found to the +standard output. + +\begin{footnotesize} +\begin{verbatim} +#include +#include +#include +#include + +typedef struct { int set; } v_data; +typedef struct { double cost; int x; } e_data; + +#define node(v) ((v_data *)((v)->data)) +#define edge(e) ((e_data *)((e)->data)) + +int main(void) +{ glp_graph *G; + glp_vertex *v; + glp_arc *e; + int i, ret; + double sol; + G = glp_create_graph(sizeof(v_data), sizeof(e_data)); + glp_read_asnprob(G, offsetof(v_data, set), + offsetof(e_data, cost), "sample.asn"); + ret = glp_asnprob_okalg(GLP_ASN_MMP, G, + offsetof(v_data, set), offsetof(e_data, cost), &sol, + offsetof(e_data, x)); + printf("ret = %d; sol = %5g\n", ret, sol); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (e = v->out; e != NULL; e = e->t_next) + printf("edge %2d %2d: x = %d; c = %g\n", + e->tail->i, e->head->i, edge(e)->x, edge(e)->cost); + } + glp_delete_graph(G); + return 0; +} +\end{verbatim} +\end{footnotesize} + +If `\verb|sample.asn|' is the example data file from the subsection +describing the routine \verb|glp_read_asnprob|, the output may look +like follows: + +\begin{footnotesize} +\begin{verbatim} +Reading assignment problem data from `sample.asn'... +Assignment problem has 8 + 9 = 17 nodes and 22 arcs +38 lines were read +ret = 0; sol = 180 +edge 1 12: x = 1; c = 20 +edge 1 10: x = 0; c = 21 +edge 1 9: x = 0; c = 13 +edge 2 13: x = 1; c = 26 +edge 2 12: x = 0; c = 8 +edge 2 10: x = 0; c = 12 +edge 3 13: x = 0; c = 11 +edge 3 11: x = 1; c = 22 +edge 4 14: x = 1; c = 25 +edge 4 12: x = 0; c = 36 +edge 4 9: x = 0; c = 12 +edge 5 17: x = 0; c = 32 +edge 5 16: x = 1; c = 35 +edge 5 15: x = 0; c = 8 +edge 5 14: x = 0; c = 4 +edge 5 13: x = 0; c = 11 +edge 5 12: x = 0; c = 40 +edge 5 11: x = 0; c = 41 +edge 6 9: x = 1; c = 13 +edge 7 10: x = 0; c = 19 +edge 8 11: x = 0; c = 15 +edge 8 10: x = 1; c = 39 +\end{verbatim} +\end{footnotesize} + +\newpage + +\subsection{glp\_asnprob\_hall---find bipartite matching of maximum +cardinality} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_asnprob_hall(glp_graph *G, int v_set, int a_x); +\end{verbatim} + +\subsubsection*{Description} + +The routine \verb|glp_asnprob_hall| finds a matching of maximal +cardinality in the specified bipartite graph. It uses a version of the +Fortran routine \verb|MC21A| developed by +I.~S.~Duff\footnote{I.~S.~Duff, Algorithm 575: Permutations for +zero-free diagonal, ACM Trans. on Math. Softw. 7 (1981), pp.~387-390.}, +which implements Hall's algorithm.\footnote{M.~Hall, ``An Algorithm for +Distinct Representatives,'' Am. Math. Monthly 63 (1956), pp.~716-717.} + +The parameter \verb|G| is a pointer to the graph program object. + +The parameter \verb|v_set| specifies an offset of the field of type +\verb|int| in the vertex data block, which contains the node set +indicator: + +0 --- the node is in set $R$; + +1 --- the node is in set $S$. + +\noindent +If \verb|v_set| $<0$, it is assumed that a node having no incoming arcs +is in set $R$, and a node having no outgoing arcs is in set $S$. + +The parameter \verb|a_x| specifies an offset of the field of type +\verb|int| in the arc data block, to which the routine stores $x_{ij}$. +If \verb|a_x| $<0$, this value is not stored. + +\subsubsection*{Returns} + +The routine \verb|glp_asnprob_hall| returns the cardinality of the +matching found. However, if the specified graph is incorrect (as +detected by the routine \verb|glp_check_asnprob|), this routine returns +a negative value. + +\subsubsection*{Comments} + +The same solution may be obtained with the routine +\verb|glp_asnprob_okalg| (for LP formulation \verb|GLP_ASN_MMP| and +all edge costs equal to 1). However, the routine \verb|glp_asnprob_hall| +is much faster. + +\newpage + +\subsubsection*{Example} + +The example program shown below reads the assignment problem instance +in DIMACS format from file `\verb|sample.asn|', finds a bipartite +matching of maximal cardinality by using the routine +\verb|glp_asnprob_hall|, and writes the solution found to the standard +output. + +\begin{footnotesize} +\begin{verbatim} +#include +#include +#include +#include + +typedef struct { int set; } v_data; +typedef struct { int x; } e_data; + +#define node(v) ((v_data *)((v)->data)) +#define edge(e) ((e_data *)((e)->data)) + +int main(void) +{ glp_graph *G; + glp_vertex *v; + glp_arc *e; + int i, card; + G = glp_create_graph(sizeof(v_data), sizeof(e_data)); + glp_read_asnprob(G, offsetof(v_data, set), -1, + "sample.asn"); + card = glp_asnprob_hall(G, offsetof(v_data, set), + offsetof(e_data, x)); + printf("card = %d\n", card); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (e = v->out; e != NULL; e = e->t_next) + printf("edge %2d %2d: x = %d\n", + e->tail->i, e->head->i, edge(e)->x); + } + glp_delete_graph(G); + return 0; +} +\end{verbatim} +\end{footnotesize} + +If `\verb|sample.asn|' is the example data file from the subsection +describing the routine \verb|glp_read_asnprob|, the output may look +like follows: + +\begin{footnotesize} +\begin{verbatim} +Reading assignment problem data from `sample.asn'... +Assignment problem has 8 + 9 = 17 nodes and 22 arcs +38 lines were read +card = 7 +edge 1 12: x = 1 +edge 1 10: x = 0 +edge 1 9: x = 0 +edge 2 13: x = 1 +edge 2 12: x = 0 +edge 2 10: x = 0 +edge 3 13: x = 0 +edge 3 11: x = 1 +edge 4 14: x = 1 +edge 4 12: x = 0 +edge 4 9: x = 0 +edge 5 17: x = 1 +edge 5 16: x = 0 +edge 5 15: x = 0 +edge 5 14: x = 0 +edge 5 13: x = 0 +edge 5 12: x = 0 +edge 5 11: x = 0 +edge 6 9: x = 1 +edge 7 10: x = 1 +edge 8 11: x = 0 +edge 8 10: x = 0 +\end{verbatim} +\end{footnotesize} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newpage + +\section{Critical path problem} + +\subsection{Background} + +The {\it critical path problem} (CPP) is stated as follows. Let there +be given a project $J$, which a set of jobs (tasks, activities, etc.). +Performing each job $i\in J$ requires time $t_i\geq 0$. Besides, over +the set $J$ there is given a precedence relation $R\subseteq J\times J$, +where $(i,j)\in R$ means that job $i$ immediately precedes job $j$, i.e. +performing job $j$ cannot start until job $i$ has been completely +performed. The problem is to find starting times $x_i$ for each job +$i\in J$, which satisfy to the precedence relation and minimize the +total duration (makespan) of the project. + +The following is an example of the critical path problem: + +\begin{center} +\begin{tabular}{|c|l|c|c|} +\hline +Job&Desription&Time&Predecessors\\ +\hline +A&Excavate&3&---\\ +B&Lay foundation&4&A\\ +C&Rough plumbing&3&B\\ +D&Frame&10&B\\ +E&Finish exterior&8&D\\ +F&Install HVAC&4&D\\ +G&Rough electric&6&D\\ +H&Sheet rock&8&C, E, F, G\\ +I&Install cabinets&5&H\\ +J&Paint&5&H\\ +K&Final plumbing&4&I\\ +L&Final electric&2&J\\ +M&Install flooring&4&K, L\\ +\hline +\end{tabular} +\end{center} + +Obviously, the project along with the precedence relation can be +represented as a directed graph $G=(J,R)$ called {\it project network}, +where each node $i\in J$ corresponds to a job, and arc +$(i\rightarrow j)\in R$ means that job $i$ immediately precedes job +$j$.\footnote{There exists another network representation of the +critical path problem, where jobs correspond to arcs while nodes +correspond to events introduced to express the precedence relation. +That representation, however, is much less convenient than the one, +where jobs are represented as nodes of the network.} The project network +for the example above is shown on Fig.~4. + +May note that the project network must be acyclic; otherwise, it would +be impossible to satisfy to the precedence relation for any job that +belongs to a cycle. + +\newpage + +\xymatrix +{&&&C|3\ar[rd]&&I|5\ar[r]&K|4\ar[rd]&\\ +A|3\ar[r]&B|4\ar[rru]\ar[rd]&&E|8\ar[r]&H|8\ar[ru]\ar[rd]&&&M|4\\ +&&D|10\ar[ru]\ar[r]\ar[rd]&F|4\ar[ru]&&J|5\ar[r]&L|2\ar[ru]&\\ +&&&G|6\ar[ruu]&&&&\\ +} + +\bigskip + +\noindent\hfil +Fig.~4. An example of the project network. + +\bigskip + +The critical path problem can be naturally formulated as the following +LP problem: + +\medskip + +\noindent +\hspace{.5in}minimize +$$z\eqno(19)$$ +\hspace{.5in}subject to +$$x_i+t_i\leq z\ \ \ \hbox{for all}\ i\in J\ \ \ \ \eqno(20)$$ +$$x_i+t_i\leq x_j\ \ \ \hbox{for all}\ (i,j)\in R\eqno(21)$$ +$$x_i\geq 0\ \ \ \ \ \ \ \hbox{for all}\ i\in J\ \ \eqno(22)$$ + +The inequality constraints (21), which are active in the optimal +solution, define so called {\it critical path} having the following +property: the minimal project duration $z$ can be decreased only by +decreasing the times $t_j$ for jobs on the critical path, and delaying +any critical job delays the entire project. + +\subsection{glp\_cpp---solve critical path problem} + +\subsubsection{Synopsis} + +\begin{verbatim} +double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls); +\end{verbatim} + +\subsubsection{Description} + +The routine \verb|glp_cpp| solves the critical path problem represented +in the form of the project network. + +The parameter \verb|G| is a pointer to the graph object, which +specifies the project network. This graph must be acyclic. Multiple +arcs are allowed being considered as single arcs. + +The parameter \verb|v_t| specifies an offset of the field of type +\verb|double| in the vertex data block, which contains time $t_i\geq 0$ +needed to perform corresponding job $j\in J$. If \verb|v_t| $<0$, it is +assumed that $t_i=1$ for all jobs. + +The parameter \verb|v_es| specifies an offset of the field of type +\verb|double| in the vertex data block, to which the routine stores +the {\it earliest start time} for corresponding job. If \verb|v_es| +$<0$, this time is not stored. + +The parameter \verb|v_ls| specifies an offset of the field of type +\verb|double| in the vertex data block, to which the routine stores +the {\it latest start time} for corresponding job. If \verb|v_ls| +$<0$, this time is not stored. + +The difference between the latest and earliest start times of some job +is called its {\it time reserve}. Delaying a job within its time reserve +does not affect the project duration, so if the time reserve is zero, +the corresponding job is critical. + +\subsubsection{Returns} + +The routine \verb|glp_cpp| returns the minimal project duration, i.e. +minimal time needed to perform all jobs in the project. + +\subsubsection{Example} + +The example program below solves the critical path problem shown on +Fig.~4 by using the routine \verb|glp_cpp| and writes the solution found +to the standard output. + +\begin{footnotesize} +\begin{verbatim} +#include +#include +#include +#include + +typedef struct { double t, es, ls; } v_data; + +#define node(v) ((v_data *)((v)->data)) + +int main(void) +{ glp_graph *G; + int i; + double t, es, ef, ls, lf, total; + G = glp_create_graph(sizeof(v_data), 0); + glp_add_vertices(G, 13); + node(G->v[1])->t = 3; /* A: Excavate */ + node(G->v[2])->t = 4; /* B: Lay foundation */ + node(G->v[3])->t = 3; /* C: Rough plumbing */ + node(G->v[4])->t = 10; /* D: Frame */ + node(G->v[5])->t = 8; /* E: Finish exterior */ + node(G->v[6])->t = 4; /* F: Install HVAC */ + node(G->v[7])->t = 6; /* G: Rough elecrtic */ + node(G->v[8])->t = 8; /* H: Sheet rock */ + node(G->v[9])->t = 5; /* I: Install cabinets */ + node(G->v[10])->t = 5; /* J: Paint */ + node(G->v[11])->t = 4; /* K: Final plumbing */ + node(G->v[12])->t = 2; /* L: Final electric */ + node(G->v[13])->t = 4; /* M: Install flooring */ + glp_add_arc(G, 1, 2); /* A precedes B */ + glp_add_arc(G, 2, 3); /* B precedes C */ + glp_add_arc(G, 2, 4); /* B precedes D */ + glp_add_arc(G, 4, 5); /* D precedes E */ + glp_add_arc(G, 4, 6); /* D precedes F */ + glp_add_arc(G, 4, 7); /* D precedes G */ + glp_add_arc(G, 3, 8); /* C precedes H */ + glp_add_arc(G, 5, 8); /* E precedes H */ + glp_add_arc(G, 6, 8); /* F precedes H */ + glp_add_arc(G, 7, 8); /* G precedes H */ + glp_add_arc(G, 8, 9); /* H precedes I */ + glp_add_arc(G, 8, 10); /* H precedes J */ + glp_add_arc(G, 9, 11); /* I precedes K */ + glp_add_arc(G, 10, 12); /* J precedes L */ + glp_add_arc(G, 11, 13); /* K precedes M */ + glp_add_arc(G, 12, 13); /* L precedes M */ + total = glp_cpp(G, offsetof(v_data, t), offsetof(v_data, es), + offsetof(v_data, ls)); + printf("Minimal project duration is %.2f\n\n", total); + printf("Job Time ES EF LS LF\n"); + printf("--- ------ ------ ------ ------ ------\n"); + for (i = 1; i <= G->nv; i++) + { t = node(G->v[i])->t; + es = node(G->v[i])->es; + ef = es + node(G->v[i])->t; + ls = node(G->v[i])->ls; + lf = ls + node(G->v[i])->t; + printf("%3d %6.2f %s %6.2f %6.2f %6.2f %6.2f\n", + i, t, ls - es < 0.001 ? "*" : " ", es, ef, ls, lf); + } + glp_delete_graph(G); + return 0; +} +\end{verbatim} +\end{footnotesize} + +The output from the example program shown below includes job number, +the time needed to perform a job, earliest start time (\verb|ES|), +earliest finish time (\verb|EF|), latest start time (\verb|LS|), and +latest finish time (\verb|LF|) for each job in the project. Critical +jobs are marked by asterisks. + +\newpage + +\begin{footnotesize} +\begin{verbatim} +Minimal project duration is 46.00 + +Job Time ES EF LS LF +--- ------ ------ ------ ------ ------ + 1 3.00 * 0.00 3.00 0.00 3.00 + 2 4.00 * 3.00 7.00 3.00 7.00 + 3 3.00 7.00 10.00 22.00 25.00 + 4 10.00 * 7.00 17.00 7.00 17.00 + 5 8.00 * 17.00 25.00 17.00 25.00 + 6 4.00 17.00 21.00 21.00 25.00 + 7 6.00 17.00 23.00 19.00 25.00 + 8 8.00 * 25.00 33.00 25.00 33.00 + 9 5.00 * 33.00 38.00 33.00 38.00 + 10 5.00 33.00 38.00 35.00 40.00 + 11 4.00 * 38.00 42.00 38.00 42.00 + 12 2.00 38.00 40.00 40.00 42.00 + 13 4.00 * 42.00 46.00 42.00 46.00 +\end{verbatim} +\end{footnotesize} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\chapter{Graph Optimization API Routines} + +\section{Maximum clique problem} + +\subsection{Background} + +The {\it maximum clique problem (MCP)} is a classic combinatorial +optimization problem. Given an undirected graph $G=(V,E)$, where $V$ is +a set of vertices, and $E$ is a set of edges, this problem is to find +the largest {\it clique} $C\subseteq G$, i.e. the largest induced +complete subgraph. A generalization of this problem is the {\it maximum +weight clique problem (MWCP)}, which is to find a clique $C\subseteq G$ +of the largest weight $\displaystyle\sum_{v\in C}w(v)\rightarrow\max$, +where $w(v)$ is a weight of vertex $v\in V$. + +An example of the maximum weight clique problem is shown on Fig.~5. + +\begin{figure} +\noindent\hfil +\begin{tabular}{c} +{\xymatrix %@C=16pt +{&&&{v_1}\ar@{-}[lllddd]\ar@{-}[llddddd]\ar@{-}[dddddd] +\ar@{-}[rrrddd]&&&\\ +&{v_2}\ar@{-}[rrrr]\ar@{-}[rrrrdddd]\ar@{-}[rrddddd]\ar@{-}[dddd]&&&& +{v_3}\ar@{-}[llllldd]\ar@{-}[lllldddd]\ar@{-}[dddd]&\\ +&&&&&&\\ +{v_4}\ar@{-}[rrrrrr]\ar@{-}[rrrddd]&&&&&&{v_5}\ar@{-}[lllddd] +\ar@{-}[ldd]\\ +&&&&&&\\ +&{v_6}\ar@{-}[rrrr]&&&&{v_7}&\\ +&&&{v_8}&&&\\ +}} +\end{tabular} +\begin{tabular}{r@{\ }c@{\ }l} +$w(v_1)$&=&3\\$w(v_2)$&=&4\\$w(v_3)$&=&8\\$w(v_4)$&=&1\\ +$w(v_5)$&=&5\\$w(v_6)$&=&2\\$w(v_7)$&=&1\\$w(v_8)$&=&3\\ +\end{tabular} + +\begin{center} +Fig.~5. An example of the maximum weight clique problem. +\end{center} +\end{figure} + +\subsection{glp\_wclique\_exact---find maximum weight clique with exact +algorithm} + +\subsubsection*{Synopsis} + +\begin{verbatim} +int glp_wclique_exact(glp_graph *G, int v_wgt, double *sol, + int v_set); +\end{verbatim} + +\subsection*{Description} + +The routine {\it glp\_wclique\_exact} finds a maximum weight clique in +the specified undirected graph with the exact algorithm developed by +Patric \"Osterg{\aa}rd.\footnote{P.~R.~J.~\"Osterg{\aa}rd, A new +algorithm for the maximum-weight clique problem, Nordic J. of Computing, +Vol.~8, No.~4, 2001, pp.~424--36.} + +The parameter $G$ is the program object, which specifies an undirected +graph. Each arc $(x\rightarrow y)$ in $G$ is considered as edge +$(x,y)$, self-loops are ignored, and multiple edges, if present, are +replaced (internally) by simple edges. + +The parameter {\it v\_wgt} specifies an offset of the field of type +{\bf double} in the vertex data block, which contains a weight of +corresponding vertex. Vertex weights must be integer-valued in the +range $[0,$ {\it INT\_MAX}$]$. If {\it v\_wgt} $<0$, it is assumed that +all vertices of the graph have the weight 1. + +The parameter {\it sol} specifies a location, to which the routine +stores the weight of the clique found (the clique weight is the sum +of weights of all vertices included in the clique.) If {\it sol} is +{\it NULL}, the solution is not stored. + +The parameter {\it v\_set} specifies an offset of the field of type +{\bf int} in the vertex data block, to which the routines stores a +vertex flag: 1 means that the corresponding vertex is included in the +clique found, and 0 otherwise. If {\it v\_set} $<0$, vertex flags are +not stored. + +\subsubsection*{Returns} + +\def\arraystretch{1} + +\begin{tabular}{@{}p{25mm}p{97.3mm}@{}} +0 & Optimal solution found.\\ +\verb|GLP_EDATA| & Unable to start the search, because some vertex +weights are either not integer-valued or out of range. This code is +also returned if the sum of weights of all vertices exceeds +{\it INT\_MAX}. \\ +\end{tabular} + +\subsubsection*{Notes} + +\noindent\indent +1. The routine {\it glp\_wclique\_exact} finds exact solution. Since +both MCP and MWCP problems are NP-complete, the algorithm may require +exponential time in worst cases. + +2. Internally the specified graph is converted to an adjacency matrix +in {\it dense} format. This requires about $|V|^2/16$ bytes of memory, +where $|V|$ is the number of vertices in the graph. + +\subsubsection*{Example} + +The example program shown below reads a MWCP instance in DIMACS +clique/coloring format from file `\verb|sample.clq|', finds the clique +of largest weight, and writes the solution found to the standard output. + +\begin{footnotesize} +\begin{verbatim} +#include +#include +#include +#include + +typedef struct { double wgt; int set; } v_data; + +#define vertex(v) ((v_data *)((v)->data)) + +int main(void) +{ glp_graph *G; + glp_vertex *v; + int i, ret; + double sol; + G = glp_create_graph(sizeof(v_data), 0); + glp_read_ccdata(G, offsetof(v_data, wgt), "sample.clq"); + ret = glp_wclique_exact(G, offsetof(v_data, wgt), &sol, + offsetof(v_data, set)); + printf("ret = %d; sol = %g\n", ret, sol); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + printf("vertex %d: weight = %g, flag = %d\n", + i, vertex(v)->wgt, vertex(v)->set); + } + glp_delete_graph(G); + return 0; +} +\end{verbatim} +\end{footnotesize} + +\noindent +For the example shown on Fig.~5 the data file may look like follows: + +\begin{footnotesize} +\begin{verbatim} +c sample.clq +c +c This is an example of the maximum weight clique +c problem in DIMACS clique/coloring format. +c +p edge 8 16 +n 1 3 +n 2 4 +n 3 8 +n 5 5 +n 6 2 +n 8 3 +e 1 4 +e 1 5 +e 1 6 +e 1 8 +e 2 3 +e 2 6 +e 2 7 +e 2 8 +e 3 4 +e 3 6 +e 3 7 +e 4 5 +e 4 8 +e 5 7 +e 5 8 +e 6 7 +c +c eof +\end{verbatim} +\end{footnotesize} + +\noindent +The corresponding output from the example program is the following: + +\begin{footnotesize} +\begin{verbatim} +Reading graph from `sample.clq'... +Graph has 8 vertices and 16 edges +28 lines were read +ret = 0; sol = 15 +vertex 1: weight = 3, flag = 0 +vertex 2: weight = 4, flag = 1 +vertex 3: weight = 8, flag = 1 +vertex 4: weight = 1, flag = 0 +vertex 5: weight = 5, flag = 0 +vertex 6: weight = 2, flag = 1 +vertex 7: weight = 1, flag = 1 +vertex 8: weight = 3, flag = 0 +\end{verbatim} +\end{footnotesize} + +\end{document} diff -r d59bea55db9b -r c445c931472f doc/miplib2.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/miplib2.txt Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,135 @@ +Solver: GLPSOL 4.40 (options used: --pcost) +Computer: Intel Pentium 4, 3.0 GHz +Platform: Cygwin 1.5.25 +Compiler: GCC 3.4.4 (options used: -O3) +Test set: MIPLIB 2.0 + +Problem Optimal Solution Cuts Used Nodes Iters Time,s Mem,MB +-------- ---------------- --------- -------- ------ ------ ------ +air01 +6.796000000e+03 3 41 < 1 1.2 +air02 +7.810000000e+03 43 201 6 13.8 +air03 +3.401600000e+05 33 414 12 21.0 +air04 +5.613700000e+04 1901 109800 396 32.4 +air05 +2.637400000e+04 6445 201649 452 45.0 +air06 +4.964900000e+04 11 6868 31 18.1 +bell3a +8.784303160e+05 --gomory 7965 42363 17 6.1 +bell3b +1.178616062e+07 --gomory 6031 30467 19 3.2 +bell4 +1.854148420e+07 --gomory 7203 25019 16 2.9 +bell5 +8.966406492e+06 --gomory 5605 18555 8 1.5 +bm23 +3.400000000e+01 373 878 < 1 0.2 +cracpb1 +2.219900000e+04 47 5258 2 1.3 +dcmulti +1.881820000e+05 743 3366 2 1.1 +diamond infeasible 3 4 < 1 0.1 +dsbmip -3.051981750e+02 --mir 217 46088 24 4.5 +egout +5.681007000e+02 91 137 < 1 0.3 +enigma +0.000000000e+00 16419 55071 6 3.2 +fixnet3 +5.197300000e+04 81 380 < 1 1.4 +fixnet4 +8.936000000e+03 211 1095 1 1.4 +fixnet6 +3.983000000e+03 1031 3136 2 1.7 +flugpl +1.201500000e+06 397 231 < 1 0.1 +gen +1.123133627e+05 195 3991 1 1.7 +khb05250 +1.069402260e+08 2163 14498 5 2.8 +l152lav +4.722000000e+03 7419 95299 68 12.0 +lp4l +2.967000000e+03 173 1331 2 1.7 +lseu +1.120000000e+03 10821 31954 5 2.5 +misc01 +5.635000000e+02 769 4593 1 0.5 +misc02 +1.690000000e+03 29 282 < 1 0.2 +misc03 +3.360000000e+03 957 6742 2 1.1 +misc04 +2.666699247e+03 17 2052 1 7.0 +misc05 +2.984500000e+03 293 2520 1 1.1 +misc06 +1.285086074e+04 57 941 < 1 2.7 +misc07 +2.810000000e+03 --mir 66075 579129 424 33.4 +mod008 +3.070000000e+02 8185 24245 8 2.3 +mod010 +6.548000000e+03 315 6283 7 5.3 +mod011 +mod013 +2.809500000e+02 545 1155 < 1 0.3 +modglob +2.074050809e+07 --mir 5197 31985 20 2.8 +noswot +p0033 +3.089000000e+03 305 955 < 1 0.2 +p0040 +6.202700000e+04 17 66 < 1 0.1 +p0201 +7.615000000e+03 521 3660 1 0.9 +p0282 +2.584110000e+05 623 1204 1 0.8 +p0291 +5.223749000e+03 71 154 < 1 0.7 +p0548 +8.691000000e+03 7617 23556 9 2.9 +p2756 +3.124000000e+03 --mir 3911 15157 57 10.9 +p6000 -2.451377000e+06 19209 40906 570 15.8 +pipex +7.882630000e+02 1569 2469 < 1 0.4 +qiu -1.328731369e+02 80473 1918742 1174 69.2 +rentacar +3.035676098e+07 43 1649 3 12.1 +rgn +8.219999924e+01 3325 18700 2 1.2 +sample2 +3.750000000e+02 163 347 < 1 0.2 +sentoy -7.772000000e+03 335 723 < 1 0.4 +set1al +1.586975000e+04 --mir 17 532 < 1 1.5 +set1ch +set1cl +6.484250000e+03 --mir 1 502 < 1 1.1 +stein15 +9.000000000e+00 87 375 < 1 0.2 +stein27 +1.800000000e+01 3255 15327 2 1.0 +stein45 +3.000000000e+01 52301 389140 139 19.2 +stein9 +5.000000000e+00 17 45 < 1 0.1 +vpm1 +2.000000000e+01 --mir 9 836 < 1 0.9 + +PROBLEM CHARACTERISTICS + +Problem Rows Cols ( Int 0/1) Nonz Best Solution +-------- ------ ---------------------- ------ -------------------------- +air01 24 771 ( all all) 4986 6796 (opt) +air02 51 6774 ( all all) 68329 7810 (opt) +air03 125 10757 ( all all) 101785 340160 (opt) +air04 824 8904 ( all all) 81869 56138 (opt) +air05 427 7195 ( all all) 59316 26402 (not opt) +air06 826 8627 ( all all) 79433 49649 (opt) +bell3a 124 133 ( 71 39) 441 878430.32 (opt) +bell3b 124 133 ( 71 39) 441 11786160.62 (opt) +bell4 106 117 ( 64 34) 385 18541484.20 (opt) +bell5 92 104 ( 58 30) 340 8966406.49 (opt) +bm23 21 27 ( all all) 505 34 (opt) +cracpb1 144 572 ( all all) 4730 22199 (opt) +dcmulti 291 548 ( 75 all) 1833 188182.0000 (opt) +diamond 5 2 ( all all) 9 integer infeasible +dsbmip 1855 1886 ( 192 160) 9768 -305.198 (opt) +egout 99 141 ( 55 all) 392 568.101 (opt) +enigma 22 100 ( all all) 298 0.0 (opt) +fixnet3 479 878 ( 378 all) 2631 51973 (opt) +fixnet4 479 878 ( 378 all) 2621 8936 (opt) +fixnet6 479 878 ( 378 all) 2550 3983 (opt) +flugpl 19 18 ( 11 none) 64 1201500 (opt) +gen 781 870 ( 150 144) 3174 112313 (opt) +khb05250 102 1350 ( 24 all) 3973 106940226 (opt) +l152lav 98 1989 ( all all) 11911 4750 (not opt) +lp4l 86 1086 ( all all) 5763 2967 (opt) +lseu 29 89 ( all all) 394 1120 (opt) +misc01 55 83 ( 82 all) 746 563.5 (opt) +misc02 40 59 ( 58 all) 414 1690 (opt) +misc03 97 160 ( 159 all) 2054 3360 (opt) +misc04 1726 4897 ( 30 all) 17253 2666.699 (opt) +misc05 301 136 ( 74 all) 2946 2984.5 (opt) +misc06 821 1808 ( 112 all) 5860 12850.8607 (opt) +misc07 213 260 ( 259 all) 8620 2810 (not opt) +mod008 7 319 ( all all) 1562 307 (opt) +mod010 147 2655 ( all all) 13858 6548 (opt) +mod011 4482 10958 ( 96 all) 37425 -54558535 (opt) +mod013 63 96 ( 48 all) 288 280.95 (opt) +modglob 292 422 ( 98 all) 1390 20740508 (opt) +noswot 183 128 ( 100 75) 760 -43 (opt) +p0033 17 33 ( all all) 131 3089 (opt) +p0040 24 40 ( all all) 150 62027 (opt) +p0201 134 201 ( all all) 2124 7615 (opt) +p0282 242 282 ( all all) 2248 258411 (opt) +p0291 253 291 ( all all) 349 5223.7490 (opt) +p0548 177 548 ( all all) 2127 8691 (opt) +p2756 756 2756 ( all all) 11103 3124 (opt) +p6000 2177 6000 ( all all) 54238 -2451377 (opt) +pipex 26 48 ( all all) 240 788.263 (opt) +qiu 1193 840 ( 48 all) 3432 -132.873137 (opt) +rentacar 6804 9557 ( 55 all) 42019 30356761 (opt) +rgn 25 180 ( 100 all) 540 82.1999 (opt) +sample2 46 67 ( 21 all) 179 375 (opt) +sentoy 31 60 ( all all) 1860 -7772 (opt) +set1al 493 712 ( 240 all) 1884 15869.7 (opt) +set1ch 493 712 ( 240 all) 1884 54537.7 (opt) +set1cl 493 712 ( 240 all) 1884 6484.25 (opt) +stein15 37 15 ( all all) 135 9 (opt) +stein27 119 27 ( all all) 405 18 (opt) +stein45 332 45 ( all all) 1079 30 (opt) +stein9 14 9 ( all all) 54 5 (opt) +vpm1 235 378 ( 168 all) 917 20 (opt) diff -r d59bea55db9b -r c445c931472f doc/miplib3.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/miplib3.txt Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,143 @@ +Solver: GLPSOL 4.40 +Computer: Intel Pentium 4, 3.0 GHz +Platform: Cygwin 1.5.25 +Compiler: GCC 3.4.4 (options used: -O3) +Test set: MIPLIB 3.0 + +Problem Optimal Solution Options Used Nodes Iters Time,s Mem,MB +-------- ---------------- ---------------- -------- ------ ------ ------ +10teams +9.240000000e+02 --pcost --gomory 6013 349276 207 12.7 +air03 +3.401600000e+05 --pcost 33 414 12 21.0 +air04 +5.613700000e+04 --pcost 1901 109800 396 32.4 +air05 +2.637400000e+04 --pcost 6445 201649 452 45.0 +arki001 +bell3a +8.784303160e+05 --pcost --gomory 7965 42363 17 6.1 +bell5 +8.966406492e+06 --pcost --gomory 5605 18555 8 1.5 +blend2 +7.598985000e+00 --pcost 7185 24256 7 2.1 +cap6000 -2.451377000e+06 --pcost 19209 40906 569 15.8 +dano3mip +danoint +dcmulti +1.881820000e+05 --pcost 743 3366 2 1.1 +dsbmip -3.051981750e+02 --pcost --mir 217 46088 24 4.5 +egout +5.681007000e+02 --pcost 91 137 < 1 0.3 +enigma +0.000000000e+00 --pcost 16419 55071 6 3.2 +fast0507 +fiber +4.059351800e+05 --pcost --mir 407 3108 4 2.4 +fixnet6 +3.983000000e+03 --pcost 1031 3136 2 1.7 +flugpl +1.201500000e+06 --pcost 397 231 < 1 0.1 +gen +1.123133627e+05 --pcost 195 3991 1 1.7 +gesa2 +2.577985637e+07 --pcost --mir 59 2723 4 4.1 +gesa2_o +2.577985637e+07 --pcost --mir 69 2588 5 3.7 +gesa3 +2.799104265e+07 --pcost --mir 93 2774 5 3.7 +gesa3_o +2.799104265e+07 --pcost --mir 117 3271 6 3.6 +gt2 +2.116600000e+04 --pcost 5613 26115 2 1.2 +harp2 +khb05250 +1.069402260e+08 --pcost 2163 14498 5 2.8 +l152lav +4.722000000e+03 --pcost 7419 95299 68 12.0 +lseu +1.120000000e+03 --pcost 10821 31954 5 2.5 +marksh1 +marksh2 +mas74 +mas76 +misc03 +3.360000000e+03 --pcost 957 6742 2 1.1 +misc06 +1.285086074e+04 --pcost 57 941 < 1 2.7 +misc07 +2.810000000e+03 --pcost --mir 66075 579129 424 33.4 +mitre +mkc +mod008 +3.070000000e+02 --pcost 8185 24245 8 2.3 +mod010 +6.548000000e+03 --pcost 315 6283 7 5.3 +mod011 +modglob +2.074050809e+07 --pcost --mir 5197 31985 20 2.8 +noswot +nw04 +1.686200000e+04 (none) 361 5544 345 138.3 +p0033 +3.089000000e+03 --pcost 305 955 < 1 0.2 +p0201 +7.615000000e+03 --pcost 521 3660 1 0.9 +p0282 +2.584110000e+05 --pcost 623 1204 1 0.8 +p0548 +8.691000000e+03 --pcost 7617 23556 9 2.9 +p2756 +3.124000000e+03 --pcost --mir 3911 15157 57 10.9 +pk1 +pp08a +7.350000000e+03 --pcost --mir 663 9196 4 1.3 +pp08acut +7.350000000e+03 --pcost --mir 17233 260160 154 21.1 +qiu -1.328731369e+02 --pcost 80473 1918742 1174 69.2 +qnet1 +1.602969268e+04 --pcost --mir 183 20352 16 3.6 +qnet1_o +1.602969268e+04 --pcost --mir 75 7645 9 3.3 +rentacar +3.035676098e+07 --pcost 43 1649 3 12.1 +rgn +8.219999924e+01 --pcost 3325 18700 2 1.2 +rout +set1ch +seymour +stein27 +1.800000000e+01 --pcost 3255 15327 2 1.0 +stein45 +3.000000000e+01 --pcost 52301 389140 139 19.2 +swath +vpm1 +2.000000000e+01 --pcost --mir 9 836 < 1 0.9 +vpm2 +1.375000000e+01 --pcost --mir 11729 164856 91 9.2 + +PROBLEM CHARACTERISTICS + +Problem Rows Cols ( Int 0/1) Nonz Best Solution +-------- ------ ---------------------- ------ -------------------------- +10teams 230 2025 ( 1800 all) 12150 924 (opt) +air03 125 10757 ( all all) 101785 340160 (opt) +air04 824 8904 ( all all) 81869 56138 (opt) +air05 427 7195 ( all all) 59316 26402 (not opt) +arki001 1048 1388 ( 538 415) 20439 7580813.0459 (not opt) +bell3a 124 133 ( 71 39) 441 878430.32 (opt) +bell5 92 104 ( 58 30) 340 8966406.49 (opt) +blend2 274 353 ( 264 231) 1409 7.598985 (opt) +cap6000 2176 6000 ( all all) 48249 -2451377 (opt) +dano3mip 3202 13873 ( 552 all) 79655 728.1111 (not opt) +danoint 664 521 ( 56 all) 3232 65.67 (opt) +dcmulti 291 548 ( 75 all) 1833 188182.0000 (opt) +dsbmip 1855 1886 ( 192 160) 9768 -305.198 (opt) +egout 99 141 ( 55 all) 392 568.101 (opt) +enigma 22 100 ( all all) 298 0.0 (opt) +fast0507 507 63009 ( all all) 409439 174 (opt) +fiber 363 1298 ( 1254 all) 2944 405935.18000 (opt) +fixnet6 479 878 ( 378 all) 2550 3983 (opt) +flugpl 19 18 ( 11 none) 64 1201500 (opt) +gen 781 870 ( 150 144) 3174 112313 (opt) +gesa2 1392 1224 ( 408 240) 5064 25779856.372 (opt) +gesa2_o 1248 1224 ( 720 384) 3672 25779856.372 (opt) +gesa3 1368 1152 ( 384 216) 4944 27991042.648 (opt) +gesa3_o 1224 1152 ( 672 336) 3624 27991042.648 (opt) +gt2 29 188 ( all 24) 376 21166.000 (opt) +harp2 112 2993 ( all all) 5840 -73899798.00 (opt) +khb05250 102 1350 ( 24 all) 3973 106940226 (opt) +l152lav 98 1989 ( all all) 11911 4750 (not opt) +lseu 29 89 ( all all) 394 1120 (opt) +marksh1 7 62 ( 50 all) 324 +marksh2 8 74 ( 60 all) 448 +mas74 13 151 ( 150 all) 1705 11801.1857 (opt) +mas76 12 151 ( 150 all) 1639 40005.0541 (opt) +misc03 97 160 ( 159 all) 2054 3360 (opt) +misc06 821 1808 ( 112 all) 5860 12850.8607 (opt) +misc07 213 260 ( 259 all) 8620 2810 (not opt) +mitre 2054 10724 ( all all) 39704 115155 (opt) +mkc 3412 5325 ( 5323 all) 20621 +mod008 7 319 ( all all) 1562 307 (opt) +mod010 147 2655 ( all all) 13858 6548 (opt) +mod011 4482 10958 ( 96 all) 37425 -54558535 (opt) +modglob 292 422 ( 98 all) 1390 20740508 (opt) +noswot 183 128 ( 100 75) 760 -43 (opt) +nw04 36 87482 ( all all) 636666 16862 (opt) +p0033 17 33 ( all all) 131 3089 (opt) +p0201 134 201 ( all all) 2124 7615 (opt) +p0282 242 282 ( all all) 2248 258411 (opt) +p0548 177 548 ( all all) 2127 8691 (opt) +p2756 756 2756 ( all all) 11103 3124 (opt) +pk1 45 86 ( 55 all) 915 11 (opt) +pp08a 136 240 ( 64 all) 480 7350 (opt) +pp08acut 246 240 ( 64 all) 839 7350 (opt) +qiu 1193 840 ( 48 all) 3432 -132.873137 (opt) +qnet1 503 1541 ( 1417 1288) 4622 16029.692681 (opt) +qnet1_o 456 1541 ( 1417 1288) 4214 16029.692681 (opt) +rentacar 6804 9557 ( 55 all) 42019 30356761 (opt) +rgn 25 180 ( 100 all) 540 82.1999 (opt) +rout 291 556 ( 315 300) 2431 1077.56 (opt) +set1ch 493 712 ( 240 all) 1884 54537.7 (opt) +seymour 4944 1372 ( all all) 33549 423 (not opt) +stein27 119 27 ( all all) 405 18 (opt) +stein45 332 45 ( all all) 1079 30 (opt) +swath 885 6805 ( 6724 all) 34966 +vpm1 235 378 ( 168 all) 917 20 (opt) +vpm2 234 378 ( 168 all) 917 13.75 (opt) diff -r d59bea55db9b -r c445c931472f doc/netlib.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/netlib.txt Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,103 @@ +Solver: GLPSOL 4.40 (default options used) +Computer: Intel Pentium 4, 3.0 GHz +Platform: Cygwin 1.5.25 +Compiler: GCC 3.4.4 (options used: -O3) +Test set: Netlib LP collection + +Problem Rows Cols Nonz Optimum Iters Time,s Mem,MB +-------- ----- ----- ------ ---------------- ------ ------ ------ +25fv47 822 1571 11127 +5.501845888e+03 1651 < 1 2.1 +80bau3b 2263 9799 29063 +9.872241924e+05 5358 3 6.4 +adlittle 57 97 465 +2.254949632e+05 71 < 1 0.1 +afiro 28 32 88 -4.647531429e+02 10 < 1 0.1 +agg 489 163 2541 -3.599176729e+07 100 < 1 0.5 +agg2 517 302 4515 -2.023925236e+07 178 < 1 0.8 +agg3 517 302 4531 +1.031211594e+07 182 < 1 0.8 +bandm 306 472 2659 -1.586280185e+02 252 < 1 0.6 +beaconfd 174 262 3476 +3.359248581e+04 61 < 1 0.4 +blend 75 83 521 -3.081214985e+01 41 < 1 0.1 +bnl1 644 1175 6129 +1.977629562e+03 581 < 1 1.4 +bnl2 2325 3489 16124 +1.811236540e+03 1730 1 3.7 +boeing1 351 384 3865 -3.352135675e+02 419 < 1 0.7 +boeing2 167 143 1339 -3.150187280e+02 161 < 1 0.3 +bore3d 234 315 1525 +1.373080394e+03 38 < 1 0.3 +brandy 221 249 2150 +1.518509896e+03 191 < 1 0.5 +capri 272 353 1786 +2.690012914e+03 203 < 1 0.4 +cycle 1904 2857 21322 -5.226393025e+00 953 < 1 3.5 +czprob 930 3523 14173 +2.185196699e+06 754 < 1 2.6 +d2q06c 2172 5167 35674 +1.227842108e+05 5368 7 6.2 +d6cube 416 6184 43888 +3.154916667e+02 6596 6 6.0 +degen2 445 534 4449 -1.435178000e+03 506 < 1 1.0 +degen3 1504 1818 26230 -9.872940000e+02 2205 2 4.1 +dfl001 6072 12230 41873 +1.126639605e+07 39863 117 11.0 +e226 224 282 2767 -2.586492907e+01 206 < 1 0.5 +etamacro 401 688 2489 -7.557152333e+02 444 < 1 0.7 +fffff800 525 854 6235 +5.556795648e+05 167 < 1 1.0 +finnis 498 614 2714 +1.727910656e+05 338 < 1 0.6 +fit1d 25 1026 14430 -9.146378092e+03 488 < 1 1.7 +fit1p 628 1677 10894 +9.146378092e+03 1379 < 1 1.9 +fit2d 26 10500 138018 -6.846429329e+04 5751 16 15.8 +fit2p 3001 13525 60784 +6.846429329e+04 11960 17 11.3 +forplan 162 421 4916 -6.642189613e+02 170 < 1 0.7 +ganges 1310 1681 7021 -1.095857361e+05 724 < 1 1.9 +gfrd-pnc 617 1092 3467 +6.902236000e+06 416 < 1 1.0 +greenbea 2393 5405 31499 -7.255524813e+07 3012 3 5.8 +greenbeb 2393 5405 31499 -4.302260261e+06 2153 2 5.8 +grow15 301 645 5665 -1.068709413e+08 358 < 1 1.1 +grow22 441 946 8318 -1.608343365e+08 606 < 1 1.6 +grow7 141 301 2633 -4.778781181e+07 159 < 1 0.5 +israel 175 142 2358 -8.966448219e+05 123 < 1 0.4 +kb2 44 41 291 -1.749900130e+03 38 < 1 0.1 +lotfi 154 308 1086 -2.526470606e+01 104 < 1 0.3 +maros 847 1443 10006 -5.806374370e+04 703 < 1 1.8 +maros-r7 3137 9408 151120 +1.497185166e+06 2340 5 16.7 +modszk1 688 1620 4158 +3.206197291e+02 705 < 1 1.4 +nesm 663 2923 13988 +1.407603649e+07 2240 1 2.4 +perold 626 1376 6026 -9.380755278e+03 1103 < 1 1.5 +pilot 1442 3652 43220 -5.574831533e+02 5726 11 7.8 +pilot-ja 941 1988 14706 -6.113136466e+03 1697 1 2.5 +pilot-we 723 2789 9218 -2.720107533e+06 1382 1 2.3 +pilot4 411 1000 5145 -2.581139259e+03 532 < 1 1.3 +pilot87 2031 4883 73804 +3.017103744e+02 7573 28 12.2 +pilotnov 976 2172 13129 -4.497276188e+03 988 1 2.5 +recipe 92 180 752 -2.666160000e+02 17 < 1 0.2 +sc105 106 103 281 -5.220206121e+01 51 < 1 0.2 +sc205 206 203 552 -5.220206121e+01 124 < 1 0.3 +sc50a 51 48 131 -6.457507706e+01 25 < 1 0.1 +sc50b 51 48 119 -7.000000000e+01 30 < 1 0.1 +scagr25 472 500 2029 -1.475343306e+07 352 < 1 0.7 +scagr7 130 140 553 -2.331389824e+06 94 < 1 0.2 +scfxm1 331 457 2612 +1.841675903e+04 281 < 1 0.6 +scfxm2 661 914 5229 +3.666026156e+04 587 < 1 1.1 +scfxm3 991 1371 7846 +5.490125455e+04 881 < 1 1.7 +scorpion 389 358 1708 +1.878124823e+03 146 < 1 0.4 +scrs8 491 1169 4029 +9.042969538e+02 545 < 1 1.1 +scsd1 78 760 3148 +8.666666674e+00 91 < 1 0.6 +scsd6 148 1350 5666 +5.050000008e+01 182 < 1 1.0 +scsd8 398 2750 11334 +9.049999999e+02 397 < 1 2.1 +sctap1 301 480 2052 +1.412250000e+03 196 < 1 0.5 +sctap2 1091 1880 8124 +1.724807143e+03 425 < 1 1.7 +sctap3 1481 2480 10734 +1.424000000e+03 729 < 1 2.4 +seba 516 1028 4874 +1.571160000e+04 883 < 1 1.0 +share1b 118 225 1182 -7.658931858e+04 167 < 1 0.3 +share2b 97 79 730 -4.157322407e+02 87 < 1 0.2 +shell 537 1775 4900 +1.208825346e+09 467 < 1 1.2 +ship04l 403 2118 8450 +1.793324538e+06 373 < 1 1.5 +ship04s 403 1458 5810 +1.798714700e+06 262 < 1 1.0 +ship08l 779 4283 17085 +1.909055211e+06 516 < 1 2.9 +ship08s 779 2387 9501 +1.920098211e+06 274 < 1 1.7 +ship12l 1152 5427 21597 +1.470187919e+06 686 < 1 3.7 +ship12s 1152 2763 10941 +1.489236134e+06 383 < 1 2.0 +sierra 1228 2036 9252 +1.539436218e+07 857 < 1 2.1 +stair 357 467 3857 -2.512669512e+02 399 < 1 1.0 +standata 360 1075 3038 +1.257699500e+03 140 < 1 0.7 +standgub 362 1184 3147 +1.257699500e+03 140 < 1 0.8 +standmps 468 1075 3686 +1.406017500e+03 299 < 1 0.9 +stocfor1 118 111 474 -4.113197622e+04 24 < 1 0.2 +stocfor2 2158 2031 9492 -3.902440854e+04 499 < 1 2.6 +stocfor3 16676 15695 74004 -3.997678394e+04 4456 11 19.7 +truss 1001 8806 36642 +4.588158472e+05 4744 4 6.5 +tuff 334 587 4523 +2.921477651e-01 61 < 1 0.8 +vtp-base 199 203 914 +1.298314625e+05 69 < 1 0.2 +wood1p 245 2594 70216 +1.442902412e+00 326 < 1 7.1 +woodw 1099 8405 37478 +1.304476333e+00 1093 < 1 6.0 diff -r d59bea55db9b -r c445c931472f examples/INDEX --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/INDEX Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,52 @@ +assign.mod Assignment problem +bpp.mod Bin packing problem +cal.mod Print an ASCII calendar of the given year +cf12a.mod Curve fitting problem +cf12b.mod Curve fitting problem +cflsq.mod Curve fitting problem by least squares +color.mod Graph coloring problem +cpp.mod Critical path problem +crypto.mod A crypto-arithmetic puzzle +dea.mod Data envelopment analysis (DEA) +diet.mod Stigler's nutrition model +dist.mod A product distribution model +egypt.mod A static model for fertilizer production +fctp.mod Fixed-charge transportation problem +food.mod Food manufacture model +food2.mod Food manufacture model +gap.mod Generalized assignment problem +graph.mod Graph visualization +hashi.mod A solver for the Japanese number-puzzle Hashiwokakero +huge.mod Arithmetic mean of a large number of integers +jssp.mod Job-shop scheduling problem +magic.mod Magic square +maxcut.mod Maximum cut problem +maxflow.mod Maximum flow problem +mfasp.mod Minimum feedback arc set problem +mfvsp.mod Minimum feedback vertex set problem +min01ks.mod Finding minimal equivalent 0-1 knapsack inequality +misp.mod Maximum independent set problem +money.mod A crypto-arithmetic puzzle +mvcp.mod Minimum vertex cover problem +numbrix.mod Number placement puzzle +pbn.mod Paint-by-numbers puzzle +plan.mod A simple LP problem +prod.mod A multiperiod production model +qfit.mod Quadratic curve fitting solution +queens.mod A classic combinatorial optimization problem +sat.mod Satisfiability problem +shiftcover.mod Workforce shift coverage assignment problem +shikaku.mod A solver for the logic puzzle Shikaku +sorting.mod How to sort arrays in MathProg +spp.mod Shortest path problem +stigler.mod Original Stigler's 1939 diet problem +sudoku.mod Number placement puzzle +tas.mod Tail assignment problem +todd.mod A class of hard instances of 0-1 knapsack problems +train.mod A model of railroad passenger car allocation +transp.mod A transportation problem +trick.mod A transportation design problem +tsp.mod Traveling salesman problem +xyacfs.mod Extended yet another curve fitting solution +yacfs.mod Yet another curve fitting solution +zebra.mod Who owns the zebra? diff -r d59bea55db9b -r c445c931472f examples/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/Makefile.am Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,15 @@ +## Process this file with automake to produce Makefile.in ## + +INCLUDES = -I$(srcdir)/../include + +LDADD = ../src/libglpk.la + +bin_PROGRAMS = glpsol + +glpsol_SOURCES = glpsol.c + +check: glpsol$(EXEEXT) + ./glpsol$(EXEEXT) --version + ./glpsol$(EXEEXT) --mps $(srcdir)/plan.mps + +## eof ## diff -r d59bea55db9b -r c445c931472f examples/assign.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/assign.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,77 @@ +/* ASSIGN, Assignment Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The assignment problem is one of the fundamental combinatorial + optimization problems. + + In its most general form, the problem is as follows: + + There are a number of agents and a number of tasks. Any agent can be + assigned to perform any task, incurring some cost that may vary + depending on the agent-task assignment. It is required to perform all + tasks by assigning exactly one agent to each task in such a way that + the total cost of the assignment is minimized. + + (From Wikipedia, the free encyclopedia.) */ + +param m, integer, > 0; +/* number of agents */ + +param n, integer, > 0; +/* number of tasks */ + +set I := 1..m; +/* set of agents */ + +set J := 1..n; +/* set of tasks */ + +param c{i in I, j in J}, >= 0; +/* cost of allocating task j to agent i */ + +var x{i in I, j in J}, >= 0; +/* x[i,j] = 1 means task j is assigned to agent i + note that variables x[i,j] are binary, however, there is no need to + declare them so due to the totally unimodular constraint matrix */ + +s.t. phi{i in I}: sum{j in J} x[i,j] <= 1; +/* each agent can perform at most one task */ + +s.t. psi{j in J}: sum{i in I} x[i,j] = 1; +/* each task must be assigned exactly to one agent */ + +minimize obj: sum{i in I, j in J} c[i,j] * x[i,j]; +/* the objective is to find a cheapest assignment */ + +solve; + +printf "\n"; +printf "Agent Task Cost\n"; +printf{i in I} "%5d %5d %10g\n", i, sum{j in J} j * x[i,j], + sum{j in J} c[i,j] * x[i,j]; +printf "----------------------\n"; +printf " Total: %10g\n", sum{i in I, j in J} c[i,j] * x[i,j]; +printf "\n"; + +data; + +/* These data correspond to an example from [Christofides]. */ + +/* Optimal solution is 76 */ + +param m := 8; + +param n := 8; + +param c : 1 2 3 4 5 6 7 8 := + 1 13 21 20 12 8 26 22 11 + 2 12 36 25 41 40 11 4 8 + 3 35 32 13 36 26 21 13 37 + 4 34 54 7 8 12 22 11 40 + 5 21 6 45 18 24 34 12 48 + 6 42 19 39 15 14 16 28 46 + 7 16 34 38 3 34 40 22 24 + 8 26 20 5 17 45 31 37 43 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/bpp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/bpp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,83 @@ +/* BPP, Bin Packing Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* Given a set of items I = {1,...,m} with weight w[i] > 0, the Bin + Packing Problem (BPP) is to pack the items into bins of capacity c + in such a way that the number of bins used is minimal. */ + +param m, integer, > 0; +/* number of items */ + +set I := 1..m; +/* set of items */ + +param w{i in 1..m}, > 0; +/* w[i] is weight of item i */ + +param c, > 0; +/* bin capacity */ + +/* We need to estimate an upper bound of the number of bins sufficient + to contain all items. The number of items m can be used, however, it + is not a good idea. To obtain a more suitable estimation an easy + heuristic is used: we put items into a bin while it is possible, and + if the bin is full, we use another bin. The number of bins used in + this way gives us a more appropriate estimation. */ + +param z{i in I, j in 1..m} := +/* z[i,j] = 1 if item i is in bin j, otherwise z[i,j] = 0 */ + + if i = 1 and j = 1 then 1 + /* put item 1 into bin 1 */ + + else if exists{jj in 1..j-1} z[i,jj] then 0 + /* if item i is already in some bin, do not put it into bin j */ + + else if sum{ii in 1..i-1} w[ii] * z[ii,j] + w[i] > c then 0 + /* if item i does not fit into bin j, do not put it into bin j */ + + else 1; + /* otherwise put item i into bin j */ + +check{i in I}: sum{j in 1..m} z[i,j] = 1; +/* each item must be exactly in one bin */ + +check{j in 1..m}: sum{i in I} w[i] * z[i,j] <= c; +/* no bin must be overflowed */ + +param n := sum{j in 1..m} if exists{i in I} z[i,j] then 1; +/* determine the number of bins used by the heuristic; obviously it is + an upper bound of the optimal solution */ + +display n; + +set J := 1..n; +/* set of bins */ + +var x{i in I, j in J}, binary; +/* x[i,j] = 1 means item i is in bin j */ + +var used{j in J}, binary; +/* used[j] = 1 means bin j contains at least one item */ + +s.t. one{i in I}: sum{j in J} x[i,j] = 1; +/* each item must be exactly in one bin */ + +s.t. lim{j in J}: sum{i in I} w[i] * x[i,j] <= c * used[j]; +/* if bin j is used, it must not be overflowed */ + +minimize obj: sum{j in J} used[j]; +/* objective is to minimize the number of bins used */ + +data; + +/* The optimal solution is 3 bins */ + +param m := 6; + +param w := 1 50, 2 60, 3 30, 4 70, 5 50, 6 40; + +param c := 100; + +end; diff -r d59bea55db9b -r c445c931472f examples/cal.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cal.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,49 @@ +/* cal.mod - print an ASCII calendar of the given year */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +param year, integer, >= 0001, <= 3999, default 2010; + +param first_day{m in 1..12}, integer, >= 0, <= 6, := + time2str(str2time(year & "-" & m & "-01", "%Y-%m-%d"), "%w"); + +param days_in_month{m in 1..12}, integer, >= 28, <= 31, := + (str2time(year + (if m < 12 then 0 else 1) & "-" & + (if m < 12 then m+1 else 1) & "-01", "%Y-%m-%d") - + str2time(year & "-" & m & "-01", "%Y-%m-%d")) / 86400; + +param foo{m in 1..12, k in 0..5, d in 0..6}, integer, := + 7 * k + d + 1 - first_day[m]; + +param cal{m in 1..12, k in 0..5, d in 0..6}, integer, := + if 1 <= foo[m,k,d] and foo[m,k,d] <= days_in_month[m] then + foo[m,k,d]; + +printf "\n"; +printf "%33s%04d\n", "", year; +printf "\n"; +for {t in 1..12 by 3} +{ for {m in t..t+2} + { printf "%7s%-14s", "", time2str(str2time(m, "%m"), "%B"); + printf{0..0: m < t+2} " "; + } + printf "\n"; + for {m in t..t+2} + { printf " S M Tu W Th F S"; + printf{0..0: m < t+2} " "; + } + printf "\n"; + for {k in 0..5} + { for {m in t..t+2} + { for {d in 0..6} + { printf{0..0: cal[m,k,d] = 0} " "; + printf{0..0: cal[m,k,d] != 0} " %2d", cal[m,k,d]; + } + printf{0..0: m < t+2} " "; + } + printf "\n"; + } +} +printf "\n"; + +end; diff -r d59bea55db9b -r c445c931472f examples/cf12a.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cf12a.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,81 @@ +/* + + Curve fitting problem 12.11(a) H P Williams "Model Building in Mathematical Programming" + + Dr. H J Mackenzie + HARD software + hjm@hardsoftware.com + + 2006-01-05 + + */ + +# set of points + +set I; + +# independent variable + +param x {i in I}; + +# dependent variable + +param y {i in I}; + +# output input values + +printf {i in I} "x = %.1f; y = %.1f\n", x[i], y[i]; + +# define equation variables + +var a; + +var b; + +var u {i in I}, >= 0; + +var v {i in I}, >= 0; + +# define objective function + +minimize error: sum {i in I} u[i] + sum {i in I} v[i]; + +# define equation constraint + +s.t. equation {i in I} : b * x[i] + a + u[i] - v[i] = y[i]; + +solve; + +printf "y = %.4fx + %.4f\n", b, a; + +/* + * + * DATA section + * + */ + +data; + +param : I : x y := + 1 0 1 + 2 0.5 0.9 + 3 1 0.7 + 4 1.5 1.5 + 5 1.9 2 + 6 2.5 2.4 + 7 3 3.2 + 8 3.5 2 + 9 4 2.7 + 10 4.5 3.5 + 11 5 1 + 12 5.5 4 + 13 6 3.6 + 14 6.6 2.7 + 15 7 5.7 + 16 7.6 4.6 + 17 8.5 6 + 18 9 6.8 + 19 10 7.3 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/cf12b.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cf12b.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,88 @@ +/* + + Curve fitting problem 12.11(b) H P Williams "Model Building in Mathematical Programming" + + Dr. H J Mackenzie + HARD software + hjm@hardsoftware.com + + 2006-01-23 + + */ + +# set of points + +set I; + +# independent variable + +param x {i in I}; + +# dependent variable + +param y {i in I}; + +# output input values + +printf {i in I} "x = %.1f; y = %.1f\n", x[i], y[i]; + +# define equation variables + +var a; + +var b; + +var u {i in I}, >= 0; + +var v {i in I}, >= 0; + +var z; + +# define objective function + +minimize deviation: z; + +# define equation constraint + +s.t. equation {i in I} : b * x[i] + a + u[i] - v[i] = y[i]; + +# define deviation constrains + +s.t. u_deviation {i in I} : z - u[i] >= 0; +s.t. v_deviation {i in I} : z - v[i] >= 0; + +solve; + +printf "y = %.4fx + %.4f Max deviation = %.4f\n", b, a, z; + +/* + * + * DATA section + * + */ + +data; + +param : I : x y := + 1 0 1 + 2 0.5 0.9 + 3 1 0.7 + 4 1.5 1.5 + 5 1.9 2 + 6 2.5 2.4 + 7 3 3.2 + 8 3.5 2 + 9 4 2.7 + 10 4.5 3.5 + 11 5 1 + 12 5.5 4 + 13 6 3.6 + 14 6.6 2.7 + 15 7 5.7 + 16 7.6 4.6 + 17 8.5 6 + 18 9 6.8 + 19 10 7.3 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/cflsq.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cflsq.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,51 @@ +/*Curve fitting problem by Least Squares + Nigel_Galloway@operamail.com + October 1st., 2007 +*/ +set Sample; +param Sx {z in Sample}; +param Sy {z in Sample}; + +var X; +var Y; +var Ex{z in Sample}; +var Ey{z in Sample}; + +/* sum of variances is zero for Sx*/ +variencesX{z in Sample}: X + Ex[z] = Sx[z]; +zumVariancesX: sum{z in Sample} Ex[z] = 0; +/* sum of variances is zero for Sy*/ +variencesY{z in Sample}: Y + Ey[z] = Sy[z]; +zumVariancesY: sum{z in Sample} Ey[z] = 0; + +solve; + +param b1 := (sum{z in Sample} Ex[z]*Ey[z])/(sum{z in Sample} Ex[z]*Ex[z]); +printf "\nbest linear fit is:\n\ty = %f %s %fx\n\n", Y-b1*X, if b1 < 0 then "-" else "+", abs(b1); + +data; + +param: +Sample: Sx Sy := + 1 0 1 + 2 0.5 0.9 + 3 1 0.7 + 4 1.5 1.5 + 5 1.9 2 + 6 2.5 2.4 + 7 3 3.2 + 8 3.5 2 + 9 4 2.7 + 10 4.5 3.5 + 11 5 1 + 12 5.5 4 + 13 6 3.6 + 14 6.6 2.7 + 15 7 5.7 + 16 7.6 4.6 + 17 8.5 6 + 18 9 6.8 + 19 10 7.3 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/color.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/color.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,113 @@ +/* COLOR, Graph Coloring Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* Given an undirected loopless graph G = (V, E), where V is a set of + nodes, E <= V x V is a set of arcs, the Graph Coloring Problem is to + find a mapping (coloring) F: V -> C, where C = {1, 2, ... } is a set + of colors whose cardinality is as small as possible, such that + F(i) != F(j) for every arc (i,j) in E, that is adjacent nodes must + be assigned different colors. */ + +param n, integer, >= 2; +/* number of nodes */ + +set V := {1..n}; +/* set of nodes */ + +set E, within V cross V; +/* set of arcs */ + +check{(i,j) in E}: i != j; +/* there must be no loops */ + +/* We need to estimate an upper bound of the number of colors |C|. + The number of nodes |V| can be used, however, for sparse graphs such + bound is not very good. To obtain a more suitable estimation we use + an easy "greedy" heuristic. Let nodes 1, ..., i-1 are already + assigned some colors. To assign a color to node i we see if there is + an existing color not used for coloring nodes adjacent to node i. If + so, we use this color, otherwise we introduce a new color. */ + +set EE := setof{(i,j) in E} (i,j) union setof{(i,j) in E} (j,i); +/* symmetrisized set of arcs */ + +param z{i in V, case in 0..1} := +/* z[i,0] = color index assigned to node i + z[i,1] = maximal color index used for nodes 1, 2, ..., i-1 which are + adjacent to node i */ +( if case = 0 then + ( /* compute z[i,0] */ + min{c in 1..z[i,1]} + ( if not exists{j in V: j < i and (i,j) in EE} z[j,0] = c then + c + else + z[i,1] + 1 + ) + ) + else + ( /* compute z[i,1] */ + if not exists{j in V: j < i} (i,j) in EE then + 1 + else + max{j in V: j < i and (i,j) in EE} z[j,0] + ) +); + +check{(i,j) in E}: z[i,0] != z[j,0]; +/* check that all adjacent nodes are assigned distinct colors */ + +param nc := max{i in V} z[i,0]; +/* number of colors used by the heuristic; obviously, it is an upper + bound of the optimal solution */ + +display nc; + +var x{i in V, c in 1..nc}, binary; +/* x[i,c] = 1 means that node i is assigned color c */ + +var u{c in 1..nc}, binary; +/* u[c] = 1 means that color c is used, i.e. assigned to some node */ + +s.t. map{i in V}: sum{c in 1..nc} x[i,c] = 1; +/* each node must be assigned exactly one color */ + +s.t. arc{(i,j) in E, c in 1..nc}: x[i,c] + x[j,c] <= u[c]; +/* adjacent nodes cannot be assigned the same color */ + +minimize obj: sum{c in 1..nc} u[c]; +/* objective is to minimize the number of colors used */ + +data; + +/* These data correspond to the instance myciel3.col from: + http://mat.gsia.cmu.edu/COLOR/instances.html */ + +/* The optimal solution is 4 */ + +param n := 11; + +set E := + 1 2 + 1 4 + 1 7 + 1 9 + 2 3 + 2 6 + 2 8 + 3 5 + 3 7 + 3 10 + 4 5 + 4 6 + 4 10 + 5 8 + 5 9 + 6 11 + 7 11 + 8 11 + 9 11 + 10 11 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/cplex/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cplex/README Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,44 @@ +The program module in this subdirectory is a crude implementation of +CPLEX-like interface to GLPK API. It consists of two files: cplex.c and +cplex.h. + +NOTE that this module is NOT a clean room implementation of the CPLEX +callable library. It only implements a CPLEX-like interface to the GLPK +API routines, and its main purpose is to provide possibility to build +and run applications which normally use the CPLEX callable library. + +This module approximately corresponds to CPLEX 9.0. + +Currently this module can be used as a linear programming solver for +Concorde, the state-of-the-art computer code for solving the symmetric +traveling salesman problem (TSP) developed by David Applegate, Robert +Bixby, Vasek Chvatal, and William Cook. For details about Concorde see +its web page at http://www.tsp.gatech.edu/concorde.html. + +To build Concorde along with GLPK you need to do the following: + +1. Configure, build, and install GLPK. + +2. Download the Concorde tarball co031219.tgz (version Dec 19, 2003), + unpack and unarchive it. + +3. Copy files cplex.h and cplex.c to subdirectory concorde/LP/. + +4. Create file named lpglpk.c in subdirectory concorde/LP/. This file + must contain the following two lines: + + #include "cplex.c" + #include "lpcplex8.c" + +5. Configure Concorde in usual way (./configure) and then build it with + the following command: + + make CPPFLAGS=-I. LPSOLVER_INTERFACE=lpglpk.c LPSOLVER_LIB=-lglpk + + The Concorde executable can be found in subdirectory concorde/TSP/. + +Please note that currently this GLPK interface module does not support +some important features (namely, CPXgetijdiv, CPXmdleave, CPXpivotin, +CPXpivotout, and CPXstrongbranch), so large (more than 1000 nodes) TSP +instances cannot be solved in a reasonable time, and some instances may +cause abnormal termination of Concorde (if CPXgetijdiv is called). diff -r d59bea55db9b -r c445c931472f examples/cplex/concorde.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cplex/concorde.txt Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,121 @@ +Solver: Concorde-03.12.19 (options used: -s 99) + http://www.tsp.gatech.edu/concorde.html +LP Solver: GLPK 4.34 (CPLEX-like interface module examples/cplex) +Computer: Intel Pentium 4 CPU 3GHz, 2GB of RAM +Platform: Cygwin 1.5.24 (Windows XP 5.1 Build 2600 Service Pack 4) +Compiler: GCC 3.4.4 (options used: -O2) +Test set: http://www.iwr.uni-heidelberg.de/groups/comopt/software/ + TSPLIB95/ + +Problem Solution B&B Time, s +--------- -------- --- ------- +a280 2579 1 3.09 +ali535 202339 1 21.88 +att48 10628 1 0.20 +att532 27686 7 74.31 +bayg29 1610 1 0.08 +bays29 2020 1 0.08 +berlin52 7542 1 0.11 +bier127 118282 1 0.62 +brazil58 25395 1 0.23 +brd14051 +brg180 1950 1 0.34 +burma14 3323 1 0.06 +ch130 6110 1 0.92 +ch150 6528 1 1.69 +d1291 +d15112 +d1655 +d18512 +d198 15780 3 4.92 +d2103 +d493 35002 5 123.89 +d657 48913 11 148.17 +dantzig42 699 1 0.08 +dsj1000 18660188 13 251.00 +eil101 (failed due to CPXgetijdiv) +eil51 426 1 0.17 +eil76 538 1 0.11 +fl1400 +fl1577 +fl3795 +fl417 11861 1 47.20 +fnl4461 +fri26 937 1 0.05 +gil262 2378 3 10.39 +gr120 6942 1 0.66 +gr137 69853 1 2.09 +gr17 2085 1 0.03 +gr202 40160 1 3.97 +gr21 2707 1 0.03 +gr229 134602 7 19.45 +gr24 1272 1 0.03 +gr431 171414 9 40.67 +gr48 5046 1 0.22 +gr666 294358 3 40.23 +gr96 55209 1 1.22 +hk48 11461 1 0.08 +kroA100 21282 1 0.41 +kroA150 26524 1 2.09 +kroA200 29368 1 2.44 +kroB100 22141 1 1.20 +kroB150 26130 1 1.66 +kroB200 29437 1 1.41 +kroC100 20749 1 0.42 +kroD100 21294 1 0.50 +kroE100 22068 1 0.94 +lin105 14379 1 0.23 +lin318 42029 1 4.28 +nrw1379 +p654 34643 1 17.08 +pa561 2763 15 370.70 +pcb1173 56892 11 370.30 +pcb3038 +pcb442 59778 13 35.86 +pla33810 +pla7397 +pla85900 +pr1002 259045 1 23.08 +pr107 44303 1 0.38 +pr124 59030 1 1.23 +pr136 96772 1 2.19 +pr144 58537 1 0.89 +pr152 73682 1 2.73 +pr226 80369 1 2.72 +pr2392 +pr264 49135 1 1.61 +pr299 48191 3 14.52 +pr439 107217 15 117.75 +pr76 108159 1 0.95 +rat195 2323 5 12.91 +rat575 6773 19 202.52 +rat783 8806 1 37.92 +rat99 1211 1 0.50 +rd100 7910 1 0.28 +rd400 15281 11 74.41 +rl11849 +rl1304 +rl1323 +rl1889 +rl5915 +rl5934 +si1032 92650 1 82.09 +si175 21407 3 8.97 +si535 48450 1 71.28 +st70 675 1 0.20 +swiss42 1273 1 0.06 +ts225 126643 1 21.25 +tsp225 3916 1 10.14 +u1060 224094 13 507.44 +u1432 +u159 42080 1 0.41 +u1817 +u2152 +u2319 +u574 36905 1 32.84 +u724 41910 19 238.42 +ulysses16 6859 1 0.19 +ulysses22 7013 1 0.47 +usa13509 +vm1084 239297 9 543.38 +vm1748 diff -r d59bea55db9b -r c445c931472f examples/cplex/cplex.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cplex/cplex.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,2130 @@ +/* cplex.c (CPLEX-like interface to GLPK API) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "cplex.h" + +struct CPXENV +{ /* environment block */ + CPXLP *list; + /* linked list of problem objects */ + int *intparam; /* int intparam[]; */ + /* integer control parameters */ + double *dblparam; /* double dblparam[]; */ + /* floating-point control parameters */ +}; + +struct CPXLP +{ /* problem object */ + CPXENV *env; + /* pointer to environment block */ + glp_prob *prob; + /* pointer to underlying GLPK problem object */ + int rflen; + /* length of the array rflag */ + char *rflag; /* char rflag[rflen]; */ + /* rflag[i], i = 0,...,nrows-1, is a flag of i-th row: */ +#define RF_NOT_RANGED 0 /* not ranged */ +#define RF_RANGED_POS 1 /* ranged, RHS = lower bound */ +#define RF_RANGED_NEG 2 /* ranged, RHS = upper bound */ + int stat; + /* solution status reported by CPXgetstat; zero means no solution + exists */ + int meth; + /* method indicator reported by CPXgetmethod */ + int iwlen; + /* length of the working array */ + int *iwork; /* int iwork[iwlen] */ + /* working array initialized by binary zeros */ + CPXLP *link; + /* pointer to another problem object */ +}; + +struct intparam +{ int which; + int defv; + int minv; + int maxv; +}; + +struct dblparam +{ int which; + double defv; + double minv; + double maxv; +}; + +struct errstring +{ int code; + const char *string; +}; + +#define BIGINT 2100000000 +#define BIGDBL 1e75 + +static const struct intparam intparam[] = +{ {CPX_PARAM_ADVIND, 0, 0, 2}, + {CPX_PARAM_AGGIND, -1, -1, BIGINT}, + {CPX_PARAM_DATACHECK, CPX_OFF, CPX_OFF, CPX_ON}, + {CPX_PARAM_DPRIIND, CPX_DPRIIND_AUTO, CPX_DPRIIND_AUTO, + CPX_DPRIIND_DEVEX}, + {CPX_PARAM_FASTMIP, CPX_OFF, CPX_OFF, CPX_ON}, /* ??? */ + {CPX_PARAM_ITLIM, BIGINT, 0, BIGINT}, + {CPX_PARAM_PERIND, CPX_OFF, CPX_OFF, CPX_ON}, + {CPX_PARAM_PPRIIND, CPX_PPRIIND_AUTO, CPX_PPRIIND_PARTIAL, + CPX_PPRIIND_FULL}, + {CPX_PARAM_PREIND, CPX_ON, CPX_OFF, CPX_ON}, + {CPX_PARAM_REINV, 0, 0, 10000}, + {CPX_PARAM_SCRIND, CPX_OFF, CPX_OFF, CPX_ON}, + {CPX_PARAM_SIMDISPLAY, 1, 0, 2}, +}; + +static const struct dblparam dblparam[] = +{ {CPX_PARAM_EPOPT, 1e-6, 1e-9, 1e-1}, + {CPX_PARAM_EPPER, 1e-6, 1e-8, BIGDBL}, + {CPX_PARAM_EPRHS, 1e-6, 1e-9, 1e-1}, + {CPX_PARAM_OBJLLIM, -BIGDBL, -BIGDBL, +BIGDBL}, + {CPX_PARAM_OBJULIM, +BIGDBL, -BIGDBL, +BIGDBL}, +}; + +static const struct errstring errstring[] = +{ {CPXERR_ARRAY_NOT_ASCENDING, "Array entry %d not ascending"}, + {CPXERR_BAD_ARGUMENT, "Invalid argument"}, + {CPXERR_BAD_CTYPE, "Invalid ctype entry %d"}, + {CPXERR_BAD_FILETYPE, "Invalid filetype"}, + {CPXERR_BAD_LUB, "Invalid bound change indicator entry %d"}, + {CPXERR_BAD_PARAM_NUM, "Invalid parameter number"}, + {CPXERR_BAD_SENSE, "Invalid sense entry %d"}, + {CPXERR_BAD_STATUS, "Invalid status entry %d for basis specificat" + "ion"}, + {CPXERR_COL_INDEX_RANGE, "Column index %d out of range"}, + {CPXERR_COUNT_RANGE, "Count entry %d negative or larger than allo" + "wed"}, + {CPXERR_DUP_ENTRY, "Duplicate entry"}, + {CPXERR_FAIL_OPEN_WRITE, "Could not open file '%s' for writing"}, + {CPXERR_INDEX_RANGE, "Index is outside range of valid values"}, + {CPXERR_NEGATIVE_SURPLUS, "Insufficient array length"}, + {CPXERR_NO_BASIC_SOLN, "No basic solution exists"}, + {CPXERR_NO_ENVIRONMENT, "No environment exists"}, + {CPXERR_NO_FILENAME, "File name not specified"}, + {CPXERR_NO_MEMORY, "Out of memory"}, + {CPXERR_NO_PROBLEM, "No problem exists"}, + {CPXERR_NO_SOLN, "No solution exists"}, + {CPXERR_NOT_FIXED, "Only fixed variables are pivoted out"}, + {CPXERR_NULL_NAME, "Null pointer %d in name array"}, + {CPXERR_NULL_POINTER, "Null pointer for required data"}, + {CPXERR_PARAM_TOO_BIG, "Parameter value too big"}, + {CPXERR_PARAM_TOO_SMALL, "Parameter value too small"}, + {CPXERR_ROW_INDEX_RANGE, "Row index %d out of range"}, +}; + +/**********************************************************************/ + +#define xassert glp_assert +#define xprintf glp_printf +#define xmalloc glp_malloc +#define xcalloc glp_calloc +#define xfree glp_free + +/**********************************************************************/ + +static int findintparam(int whichparam) +{ int k, card; + card = sizeof(intparam) / sizeof(struct intparam); + for (k = 0; k < card; k++) + if (intparam[k].which == whichparam) return k; + return -1; +} + +static int getintparam(CPXENV *env, int whichparam) +{ int k; + xassert(env != NULL); + k = findintparam(whichparam); + xassert(k >= 0); + return env->intparam[k]; +} + +static int finddblparam(int whichparam) +{ int k, card; + card = sizeof(dblparam) / sizeof(struct dblparam); + for (k = 0; k < card; k++) + if (dblparam[k].which == whichparam) return k; + return -1; +} + +static double getdblparam(CPXENV *env, int whichparam) +{ int k; + xassert(env != NULL); + k = finddblparam(whichparam); + xassert(k >= 0); + return env->dblparam[k]; +} + +static const char *finderrstring(int errcode) +{ int k, card; + card = sizeof(errstring) / sizeof(struct errstring); + for (k = 0; k < card; k++) + { if (errstring[k].code == errcode) + return errstring[k].string; + } + return NULL; +} + +static int error(CPXENV *env, int errcode, ...) +{ va_list arg; + char buffer[510]; + xassert(env != NULL); + if (getintparam(env, CPX_PARAM_SCRIND) == CPX_ON) + { xassert(CPXgeterrorstring(env, errcode, buffer) == buffer); + va_start(arg, errcode); + vprintf(buffer, arg); + va_end(arg); + } + return errcode; +} + +static int checkenv(CPXENV *env) +{ int errcode; + if (env == NULL) + errcode = CPXERR_NO_ENVIRONMENT; + else + errcode = 0; + return errcode; +} + +static checklp(CPXENV *env, CPXLP *lp) +{ int errcode; + errcode = checkenv(env); + if (errcode) goto done; + if (lp == NULL) + errcode = error(env, CPXERR_NO_PROBLEM); +done: return errcode; +} + +static void invalidate(CPXLP *lp) +{ lp->stat = 0; + lp->meth = CPX_ALG_NONE; + return; +} + +static void enlargerflag(CPXLP *lp) +{ int m; + xassert(lp != NULL); + m = glp_get_num_rows(lp->prob); + if (lp->rflen < m) + { int rflen = lp->rflen; + char *rflag = lp->rflag; + while (lp->rflen < m) + { lp->rflen += lp->rflen; + xassert(lp->rflen > 0); + } + lp->rflag = xcalloc(lp->rflen, sizeof(char)); + memcpy(lp->rflag, rflag, rflen); + xfree(rflag); + } + return; +} + +static void enlargeiwork(CPXLP *lp, int len) +{ xassert(len >= 0); + if (lp->iwlen < len) + { xfree(lp->iwork); + while (lp->iwlen < len) + { lp->iwlen += lp->iwlen; + xassert(lp->iwlen > 0); + } + lp->iwork = xcalloc(lp->iwlen, sizeof(int)); + memset(lp->iwork, 0, lp->iwlen * sizeof(int)); + } + return; +} + +/**********************************************************************/ + +int CPXaddcols(CPXENV *env, CPXLP *lp, int ccnt, int nzcnt, + const double obj[], const int cmatbeg[], const int cmatind[], + const double cmatval[], const double lb[], const double ub[], + char *colname[]) +{ int j, k, m, n, beg, end, type, errcode; + double lbnd, ubnd; + errcode = checklp(env, lp); + if (errcode) goto done; + if (ccnt < 0 || nzcnt < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (ccnt > 0) + { if (cmatbeg == NULL || cmatind == NULL || cmatval == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + } + m = glp_get_num_rows(lp->prob); + n = glp_get_num_cols(lp->prob); + enlargeiwork(lp, m); + for (j = 0; j < ccnt; j++) + { beg = cmatbeg[j]; + if (j > 0 && !(cmatbeg[j-1] <= beg)) + { errcode = error(env, CPXERR_ARRAY_NOT_ASCENDING, j); + goto done; + } + if (!(0 <= beg && beg <= nzcnt)) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + end = (j < ccnt-1 ? cmatbeg[j+1] : nzcnt); + for (k = beg; k < end; k++) + { if (!(0 <= cmatind[k] && cmatind[k] < m)) + { errcode = error(env, CPXERR_ROW_INDEX_RANGE, k); + goto done; + } + } + errcode = 0; + for (k = beg; k < end; k++) + { if (lp->iwork[cmatind[k]]) + { errcode = error(env, CPXERR_DUP_ENTRY); + break; + } + lp->iwork[cmatind[k]] = 1; + } + for (k = beg; k < end; k++) + lp->iwork[cmatind[k]] = 0; + if (errcode) goto done; + if (colname != NULL) + { if (colname[j] == NULL) + { errcode = error(env, CPXERR_NULL_NAME, j); + goto done; + } + } + } + errcode = 0; + invalidate(lp); + if (ccnt > 0) + glp_add_cols(lp->prob, ccnt); + for (j = 0; j < ccnt; j++) + { if (colname != NULL) + glp_set_col_name(lp->prob, n+j+1, colname[j]); + lbnd = (lb == NULL ? 0.0 : lb[j]); + ubnd = (ub == NULL ? +CPX_INFBOUND : ub[j]); + if (lbnd <= -CPX_INFBOUND && ubnd >= +CPX_INFBOUND) + type = GLP_FR; + else if (ubnd >= +CPX_INFBOUND) + type = GLP_LO; + else if (lbnd <= -CPX_INFBOUND) + type = GLP_UP; + else if (lbnd != ubnd) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(lp->prob, n+j+1, type, lbnd, ubnd); + if (obj != NULL) + glp_set_obj_coef(lp->prob, n+j+1, obj[j]); + beg = cmatbeg[j]; + end = (j < ccnt-1 ? cmatbeg[j+1] : nzcnt); + for (k = beg; k < end; k++) + lp->iwork[k-beg] = cmatind[k]+1; + glp_set_mat_col(lp->prob, n+j+1, end-beg, lp->iwork-1, + cmatval+beg-1); + for (k = beg; k < end; k++) + lp->iwork[k-beg] = 0; + } +done: return errcode; +} + +int CPXaddrows(CPXENV *env, CPXLP *lp, int ccnt, int rcnt, int nzcnt, + const double rhs[], const char sense[], const int rmatbeg[], + const int rmatind[], const double rmatval[], char *colname[], + char *rowname[]) +{ int i, j, k, m, n, beg, end, type, errcode; + double temp; + errcode = checklp(env, lp); + if (errcode) goto done; + if (ccnt < 0 || rcnt < 0 || nzcnt < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (rcnt > 0) + { if (rmatbeg == NULL || rmatind == NULL || rmatval == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + } + m = glp_get_num_rows(lp->prob); + n = glp_get_num_cols(lp->prob); + enlargeiwork(lp, n+ccnt); + for (i = 0; i < rcnt; i++) + { if (sense != NULL) + { if (!(sense[i] == 'L' || sense[i] == 'E' || + sense[i] == 'G' || sense[i] == 'R')) + { errcode = error(env, CPXERR_BAD_SENSE, i); + goto done; + } + } + beg = rmatbeg[i]; + if (i > 0 && !(rmatbeg[i-1] <= beg)) + { errcode = error(env, CPXERR_ARRAY_NOT_ASCENDING, i); + goto done; + } + if (!(0 <= beg && beg <= nzcnt)) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + end = (i < rcnt-1 ? rmatbeg[i+1] : nzcnt); + for (k = beg; k < end; k++) + { if (!(0 <= rmatind[k] && rmatind[k] < n+ccnt)) + { errcode = error(env, CPXERR_COL_INDEX_RANGE, k); + goto done; + } + } + errcode = 0; + for (k = beg; k < end; k++) + { if (lp->iwork[rmatind[k]]) + { errcode = error(env, CPXERR_DUP_ENTRY); + break; + } + lp->iwork[rmatind[k]] = 1; + } + for (k = beg; k < end; k++) + lp->iwork[rmatind[k]] = 0; + if (errcode) goto done; + if (rowname != NULL) + { if (rowname[i] == NULL) + { errcode = error(env, CPXERR_NULL_NAME, i); + goto done; + } + } + } + for (j = 0; j < ccnt; j++) + { if (colname != NULL) + { if (colname[j] == NULL) + { errcode = error(env, CPXERR_NULL_NAME, j); + goto done; + } + } + } + errcode = 0; + invalidate(lp); + if (rcnt > 0) + glp_add_rows(lp->prob, rcnt); + if (ccnt > 0) + glp_add_cols(lp->prob, ccnt); + enlargerflag(lp); + for (i = 0; i < rcnt; i++) + { if (rowname != NULL) + glp_set_row_name(lp->prob, m+i+1, rowname[i]); + temp = (rhs == NULL ? 0.0 : rhs[i]); + if (sense == NULL || sense[i] == 'E') + { lp->rflag[m+i] = RF_NOT_RANGED; + type = GLP_FX; + } + else if (sense[i] == 'L') + { lp->rflag[m+i] = RF_NOT_RANGED; + type = GLP_UP; + } + else if (sense[i] == 'G') + { lp->rflag[m+i] = RF_NOT_RANGED; + type = GLP_LO; + } + else if (sense[i] == 'R') + { lp->rflag[m+i] = RF_RANGED_POS; + type = GLP_FX; + } + else + xassert(sense != sense); + glp_set_row_bnds(lp->prob, m+i+1, type, temp, temp); + beg = rmatbeg[i]; + end = (i < rcnt-1 ? rmatbeg[i+1] : nzcnt); + for (k = beg; k < end; k++) + lp->iwork[k-beg] = rmatind[k]+1; + glp_set_mat_row(lp->prob, m+i+1, end-beg, lp->iwork-1, + rmatval+beg-1); + for (k = beg; k < end; k++) + lp->iwork[k-beg] = 0; + } + for (j = 0; j < ccnt; j++) + { if (colname != NULL) + glp_set_col_name(lp->prob, n+j+1, colname[j]); + glp_set_col_bnds(lp->prob, n+j+1, GLP_LO, 0.0, 0.0); + } +done: return errcode; +} + +int CPXbaropt(CPXENV *env, CPXLP *lp) +{ xassert(env == env); + xassert(lp == lp); + xprintf("CPXbaropt: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXbinvrow(CPXENV *env, CPXLP *lp, int i, double y[]) +{ xassert(env == env); + xassert(lp == lp); + xassert(i == i); + xassert(y == y); + xprintf("CPXbinvrow: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXchgbds(CPXENV *env, CPXLP *lp, int cnt, const int indices[], + const char lu[], const double bd[]) +{ int j, n, type, errcode; + double lbnd, ubnd; + errcode = checklp(env, lp); + if (errcode) goto done; + if (cnt < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (cnt > 0) + { if (indices == NULL || lu == NULL || bd == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + } + n = glp_get_num_cols(lp->prob); + for (j = 0; j < cnt; j++) + { if (!(0 <= indices[j] && indices[j] < n)) + { errcode = error(env, CPXERR_COL_INDEX_RANGE, j); + goto done; + } + if (!(lu[j] == 'L' || lu[j] == 'U' || lu[j] == 'B')) + { errcode = error(env, CPXERR_BAD_LUB, j); + goto done; + } + } + errcode = 0; + invalidate(lp); + for (j = 0; j < cnt; j++) + { type = glp_get_col_type(lp->prob, indices[j]+1); + lbnd = glp_get_col_lb(lp->prob, indices[j]+1); + ubnd = glp_get_col_ub(lp->prob, indices[j]+1); + if (type == GLP_FR || type == GLP_UP) + lbnd = -CPX_INFBOUND; + if (type == GLP_FR || type == GLP_LO) + ubnd = +CPX_INFBOUND; + if (lu[j] == 'L') + lbnd = bd[j]; + else if (lu[j] == 'U') + ubnd = bd[j]; + else if (lu[j] == 'B') + lbnd = ubnd = bd[j]; + else + xassert(lu != lu); + if (lbnd <= -CPX_INFBOUND && ubnd >= +CPX_INFBOUND) + type = GLP_FR; + else if (ubnd >= +CPX_INFBOUND) + type = GLP_LO; + else if (lbnd <= -CPX_INFBOUND) + type = GLP_UP; + else if (lbnd != ubnd) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(lp->prob, indices[j]+1, type, lbnd, ubnd); + } +done: return errcode; +} + +int CPXchgcoeflist(CPXENV *env, CPXLP *lp, int numcoefs, + const int rowlist[], const int collist[], const double vallist[]) +{ int i, j, k, m, n, rcnt, ccnt, len, ptr, errcode; + int *head, *next, *ind; + double *val; + errcode = checklp(env, lp); + if (errcode) goto done; + if (numcoefs < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (numcoefs == 0) + { errcode = 0; + goto done; + } + if (rowlist == NULL || collist == NULL || vallist == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + /* check triplets and determine the number of rows and columns + to be changed */ + m = glp_get_num_rows(lp->prob); + n = glp_get_num_cols(lp->prob); + enlargeiwork(lp, m); + enlargeiwork(lp, n); + rcnt = ccnt = 0; + for (k = 0; k < numcoefs; k++) + { i = rowlist[k]; + if (!(0 <= i && i < m)) + { errcode = error(env, CPXERR_ROW_INDEX_RANGE, i); + goto done; + } + if (!(lp->iwork[i] & 0x01)) + rcnt++, lp->iwork[i] |= 0x01; + j = collist[k]; + if (!(0 <= j && j < n)) + { errcode = error(env, CPXERR_COL_INDEX_RANGE, j); + goto done; + } + if (!(lp->iwork[j] & 0x02)) + ccnt++, lp->iwork[j] |= 0x02; + } + memset(lp->iwork, 0, m * sizeof(int)); + memset(lp->iwork, 0, n * sizeof(int)); + errcode = 0; + invalidate(lp); + if (rcnt <= ccnt) + { /* change the matrix by rows */ + /* build the linked list of triplets: + head[i] is a pointer to first triplet for row i + next[k] is a pointer to next triplet for the same row */ + head = xcalloc(m, sizeof(int)); + for (i = 0; i < m; i++) + head[i] = -1; + next = xcalloc(numcoefs, sizeof(int)); + for (k = 0; k < numcoefs; k++) + { i = rowlist[k]; + next[k] = head[i]; + head[i] = k; + } + /* check duplicate columns */ + for (i = 0; i < m; i++) + { for (k = head[i]; k >= 0; k = next[k]) + { j = collist[k]; + if (lp->iwork[j]) + { xfree(head); + xfree(next); + errcode = error(env, CPXERR_DUP_ENTRY); + goto done; + } + lp->iwork[j] = 1; + } + for (k = head[i]; k >= 0; k = next[k]) + lp->iwork[collist[k]] = 0; + } + /* perform operation */ + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + for (i = 0; i < m; i++) + { if (head[i] < 0) continue; + len = glp_get_mat_row(lp->prob, i+1, ind, val); + for (ptr = 1; ptr <= len; ptr++) + { j = ind[ptr]-1; + xassert(lp->iwork[j] == 0); + lp->iwork[j] = ptr; + } + for (k = head[i]; k >= 0; k = next[k]) + { j = collist[k]; + if (lp->iwork[j] == 0) + lp->iwork[j] = ++len; + ptr = lp->iwork[j]; + ind[ptr] = j+1, val[ptr] = vallist[k]; + } + glp_set_mat_row(lp->prob, i+1, len, ind, val); + for (ptr = 1; ptr <= len; ptr++) + lp->iwork[ind[ptr]-1] = 0; + } + } + else + { /* change the matrix by columns */ + /* build the linked lists of triplets: + head[j] is a pointer to first triplet for column j + next[k] is a pointer to next triplet for the same column */ + head = xcalloc(n, sizeof(int)); + for (j = 0; j < n; j++) + head[j] = -1; + next = xcalloc(numcoefs, sizeof(int)); + for (k = 0; k < numcoefs; k++) + { j = collist[k]; + next[k] = head[j]; + head[j] = k; + } + /* check duplicate rows */ + for (j = 0; j < n; j++) + { for (k = head[j]; k >= 0; k = next[k]) + { i = rowlist[k]; + if (lp->iwork[i]) + { xfree(head); + xfree(next); + errcode = error(env, CPXERR_DUP_ENTRY); + goto done; + } + lp->iwork[i] = 1; + } + for (k = head[j]; k >= 0; k = next[k]) + lp->iwork[rowlist[k]] = 0; + } + /* perform operation */ + ind = xcalloc(1+m, sizeof(int)); + val = xcalloc(1+m, sizeof(double)); + for (j = 0; j < n; j++) + { if (head[j] < 0) continue; + len = glp_get_mat_col(lp->prob, j+1, ind, val); + for (ptr = 1; ptr <= len; ptr++) + { i = ind[ptr]-1; + xassert(lp->iwork[i] == 0); + lp->iwork[i] = ptr; + } + for (k = head[j]; k >= 0; k = next[k]) + { i = rowlist[k]; + if (lp->iwork[i] == 0) + lp->iwork[i] = ++len; + ptr = lp->iwork[i]; + ind[ptr] = i+1, val[ptr] = vallist[k]; + } + glp_set_mat_col(lp->prob, j+1, len, ind, val); + for (ptr = 1; ptr <= len; ptr++) + lp->iwork[ind[ptr]-1] = 0; + } + } + xfree(head); + xfree(next); + xfree(ind); + xfree(val); +done: return errcode; +} + +void CPXchgobjsen(CPXENV *env, CPXLP *lp, int maxormin) +{ int errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + if (!(maxormin == CPX_MIN || maxormin == CPX_MAX)) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + errcode = 0; + invalidate(lp); + if (maxormin == CPX_MIN) + glp_set_obj_dir(lp->prob, GLP_MIN); + else + glp_set_obj_dir(lp->prob, GLP_MAX); +done: xassert(errcode == errcode); + return; +} + +int CPXchgsense(CPXENV *env, CPXLP *lp, int cnt, const int indices[], + const char sense[]) +{ int i, m, type, errcode; + double rhs; + errcode = checklp(env, lp); + if (errcode) goto done; + if (cnt < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (cnt > 0 && (indices == NULL || sense == NULL)) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + m = glp_get_num_rows(lp->prob); + for (i = 0; i < cnt; i++) + { if (!(0 <= indices[i] && indices[i] < m)) + { errcode = error(env, CPXERR_ROW_INDEX_RANGE, i); + goto done; + } + if (!(sense[i] == 'L' || sense[i] == 'E' || sense[i] == 'G' || + sense[i] == 'R')) + { errcode = error(env, CPXERR_BAD_SENSE, i); + goto done; + } + } + errcode = 0; + invalidate(lp); + for (i = 0; i < cnt; i++) + { type = glp_get_row_type(lp->prob, indices[i]+1); + if (lp->rflag[indices[i]] == RF_NOT_RANGED) + { if (type == GLP_LO || type == GLP_FX) + rhs = glp_get_row_lb(lp->prob, indices[i]+1); + else if (type == GLP_UP) + rhs = glp_get_row_ub(lp->prob, indices[i]+1); + else + xassert(type != type); + } + else if (lp->rflag[indices[i]] == RF_RANGED_POS) + { xassert(type == GLP_DB || type == GLP_FX); + rhs = glp_get_row_lb(lp->prob, indices[i]+1); + } + else if (lp->rflag[indices[i]] == RF_RANGED_NEG) + { xassert(type == GLP_DB); + rhs = glp_get_row_ub(lp->prob, indices[i]+1); + } + else + xassert(lp != lp); + if (sense[i] == 'L') + { lp->rflag[indices[i]] = RF_NOT_RANGED; + type = GLP_UP; + } + else if (sense[i] == 'E') + { lp->rflag[indices[i]] = RF_NOT_RANGED; + type = GLP_FX; + } + else if (sense[i] == 'G') + { lp->rflag[indices[i]] = RF_NOT_RANGED; + type = GLP_LO; + } + else if (sense[i] == 'R') + { lp->rflag[indices[i]] = RF_RANGED_POS; + type = GLP_FX; + } + else + xassert(sense != sense); + glp_set_row_bnds(lp->prob, indices[i]+1, type, rhs, rhs); + } +done: return errcode; +} + +int CPXcloseCPLEX(CPXENV **_env) +{ CPXENV *env; + CPXLP *lp; + int errcode; + if (_env == NULL) + { errcode = CPXERR_NULL_POINTER; + goto done; + } + env = *_env; + errcode = checkenv(env); + if (errcode) goto done; + while (env->list != NULL) + { lp = env->list; + errcode = CPXfreeprob(env, &lp); + xassert(!errcode); + } + xfree(env->intparam); + xfree(env->dblparam); + xfree(env); + *_env = NULL; + errcode = 0; +done: return errcode; +} + +int CPXcopybase(CPXENV *env, CPXLP *lp, const int cstat[], + const int rstat[]) +{ int i, j, m, n, stat, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + m = glp_get_num_rows(lp->prob); + n = glp_get_num_cols(lp->prob); + if (m > 0 && rstat == NULL || n > 0 && cstat == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + for (i = 0; i < m; i++) + { if (!(rstat[i] == CPX_AT_LOWER || rstat[i] == CPX_BASIC || + rstat[i] == CPX_AT_UPPER)) + { errcode = error(env, CPXERR_BAD_STATUS, i); + goto done; + } + } + for (j = 0; j < n; j++) + { if (!(cstat[j] == CPX_AT_LOWER || cstat[j] == CPX_BASIC || + cstat[j] == CPX_AT_UPPER || cstat[j] == CPX_FREE_SUPER)) + { errcode = error(env, CPXERR_BAD_STATUS, j); + goto done; + } + } + errcode = 0; + invalidate(lp); + for (i = 0; i < m; i++) + { if (rstat[i] == CPX_AT_LOWER) + stat = GLP_NL; + else if (rstat[i] == CPX_BASIC) + stat = GLP_BS; + else if (rstat[i] == CPX_AT_UPPER) + stat = GLP_NU; + else + xassert(rstat != rstat); + glp_set_row_stat(lp->prob, i+1, stat); + } + for (j = 0; j < n; j++) + { if (cstat[j] == CPX_AT_LOWER) + stat = GLP_NL; + else if (cstat[j] == CPX_BASIC) + stat = GLP_BS; + else if (cstat[j] == CPX_AT_UPPER) + stat = GLP_NU; + else if (cstat[j] == CPX_FREE_SUPER) + stat = GLP_NF; + else + xassert(cstat != cstat); + glp_set_col_stat(lp->prob, j+1, stat); + } +done: return errcode; +} + +int CPXcopybasednorms(CPXENV *env, CPXLP *lp, const int cstat[], + const int rstat[], const double dnorm[]) +{ int errcode; + errcode = CPXcopybase(env, lp, cstat, rstat); + xassert(dnorm == dnorm); + return errcode; +} + +int CPXcopylp(CPXENV *env, CPXLP *lp, int numcols, int numrows, + int objsen, const double obj[], const double rhs[], + const char sense[], const int matbeg[], const int matcnt[], + const int matind[], const double matval[], const double lb[], + const double ub[], const double rngval[]) +{ int errcode; + errcode = CPXcopylpwnames(env, lp, numcols, numrows, objsen, obj, + rhs, sense, matbeg, matcnt, matind, matval, lb, ub, rngval, + NULL, NULL); + return errcode; +} + +int CPXcopylpwnames(CPXENV *env, CPXLP *lp, int numcols, int numrows, + int objsen, const double obj[], const double rhs[], + const char sense[], const int matbeg[], const int matcnt[], + const int matind[], const double matval[], const double lb[], + const double ub[], const double rngval[], char *colname[], + char *rowname[]) +{ int i, j, k, beg, end, type, errcode; + double lbnd, ubnd; + char name[255+1]; + errcode = checklp(env, lp); + if (errcode) goto done; + if (numcols < 0 || numrows < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (!(objsen == CPX_MIN || objsen == CPX_MAX)) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (numcols > 0) + { if (matbeg == NULL || matcnt == NULL || matind == NULL || + matval == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + } + for (i = 0; i < numrows; i++) + { if (sense != NULL) + { if (!(sense[i] == 'L' || sense[i] == 'E' || + sense[i] == 'G' || sense[i] == 'R')) + { errcode = error(env, CPXERR_BAD_SENSE, i); + goto done; + } + } + if (rowname != NULL) + { if (rowname[i] == NULL) + { errcode = error(env, CPXERR_NULL_NAME, i); + goto done; + } + } + } + enlargeiwork(lp, numrows); + for (j = 0; j < numcols; j++) + { beg = matbeg[j]; + if (j > 0 && !(matbeg[j-1] <= beg)) + { errcode = error(env, CPXERR_ARRAY_NOT_ASCENDING, j); + goto done; + } + if (beg < 0) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + end = beg + matcnt[j]; + if (!(beg <= end) || j < numcols-1 && !(end <= matbeg[j+1])) + { errcode = error(env, CPXERR_COUNT_RANGE, j); + goto done; + } + for (k = beg; k < end; k++) + { if (!(0 <= matind[k] && matind[k] < numrows)) + { errcode = error(env, CPXERR_ROW_INDEX_RANGE, k); + goto done; + } + } + errcode = 0; + for (k = beg; k < end; k++) + { if (lp->iwork[matind[k]]) + { errcode = error(env, CPXERR_DUP_ENTRY); + break; + } + lp->iwork[matind[k]] = 1; + } + for (k = beg; k < end; k++) + lp->iwork[matind[k]] = 0; + if (errcode) goto done; + if (colname != NULL) + { if (colname[j] != NULL) + { errcode = error(env, CPXERR_NULL_NAME, j); + goto done; + } + } + } + errcode = 0; + invalidate(lp); + if (glp_get_prob_name(lp->prob) == NULL) + name[0] = '\0'; + else + strcpy(name, glp_get_prob_name(lp->prob)); + glp_erase_prob(lp->prob); + glp_set_prob_name(lp->prob, name); + if (objsen == CPX_MIN) + glp_set_obj_dir(lp->prob, GLP_MIN); + else if (objsen == CPX_MAX) + glp_set_obj_dir(lp->prob, GLP_MAX); + else + xassert(objsen != objsen); + if (numrows > 0) + glp_add_rows(lp->prob, numrows); + enlargerflag(lp); + for (i = 0; i < numrows; i++) + { if (rowname != NULL) + glp_set_row_name(lp->prob, i+1, rowname[i]); + lbnd = ubnd = (rhs == NULL ? 0.0 : rhs[i]); + if (sense == NULL || sense[i] == 'E') + { lp->rflag[i] = RF_NOT_RANGED; + type = GLP_FX; + } + else if (sense[i] == 'L') + { lp->rflag[i] = RF_NOT_RANGED; + type = GLP_UP; + } + else if (sense[i] == 'G') + { lp->rflag[i] = RF_NOT_RANGED; + type = GLP_LO; + } + else if (sense[i] == 'R') + { if (rngval == NULL || rngval[i] == 0.0) + { lp->rflag[i] = RF_RANGED_POS; + type = GLP_FX; + } + else if (rngval[i] > 0.0) + { lp->rflag[i] = RF_RANGED_POS; + type = GLP_DB; + ubnd += rngval[i]; + } + else /* rngval[i] < 0.0 */ + { lp->rflag[i] = RF_RANGED_NEG; + type = GLP_DB; + lbnd += rngval[i]; + } + } + else + xassert(sense != sense); + glp_set_row_bnds(lp->prob, i+1, type, lbnd, ubnd); + } + if (numcols > 0) + glp_add_cols(lp->prob, numcols); + for (j = 0; j < numcols; j++) + { if (colname != NULL) + glp_set_col_name(lp->prob, j+1, colname[j]); + lbnd = (lb == NULL ? 0.0 : lb[j]); + ubnd = (ub == NULL ? +CPX_INFBOUND : ub[j]); + if (lbnd <= -CPX_INFBOUND && ubnd >= +CPX_INFBOUND) + type = GLP_FR; + else if (ubnd >= +CPX_INFBOUND) + type = GLP_LO; + else if (lbnd <= -CPX_INFBOUND) + type = GLP_UP; + else if (lbnd != ubnd) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(lp->prob, j+1, type, lbnd, ubnd); + if (obj != NULL) + glp_set_obj_coef(lp->prob, j+1, obj[j]); + beg = matbeg[j]; + end = beg + matcnt[j]; + for (k = beg; k < end; k++) + lp->iwork[k-beg] = matind[k]+1; + glp_set_mat_col(lp->prob, j+1, end-beg, lp->iwork-1, + matval+beg-1); + for (k = beg; k < end; k++) + lp->iwork[k-beg] = 0; + } +done: return errcode; +} + +CPXLP *CPXcreateprob(CPXENV *env, int *status, const char *probname) +{ CPXLP *lp = NULL; + int errcode; + errcode = checkenv(env); + if (errcode) goto done; + lp = xmalloc(sizeof(struct CPXLP)); + lp->env = env; + lp->prob = glp_create_prob(); + glp_set_prob_name(lp->prob, probname); + lp->rflen = 100; + lp->rflag = xcalloc(lp->rflen, sizeof(char)); + lp->iwlen = 100; + lp->iwork = xcalloc(lp->iwlen, sizeof(int)); + memset(lp->iwork, 0, lp->iwlen * sizeof(int)); + lp->link = env->list; + env->list = lp; + invalidate(lp); +done: if (status != NULL) *status = errcode; + return lp; +} + +int CPXdelcols(CPXENV *env, CPXLP *lp, int begin, int end) +{ int j, n, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + n = glp_get_num_cols(lp->prob); + if (!(0 <= begin && begin <= end && end < n)) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + errcode = 0; + invalidate(lp); + enlargeiwork(lp, end-begin+1); + for (j = begin; j <= end; j++) + lp->iwork[j-begin] = j+1; + glp_del_cols(lp->prob, end-begin+1, lp->iwork-1); + for (j = begin; j <= end; j++) + lp->iwork[j-begin] = 0; +done: return errcode; +} + +int CPXdelrows(CPXENV *env, CPXLP *lp, int begin, int end) +{ int i, m, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + m = glp_get_num_rows(lp->prob); + if (!(0 <= begin && begin <= end && end < m)) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + errcode = 0; + invalidate(lp); + enlargeiwork(lp, end-begin+1); + for (i = begin; i <= end; i++) + lp->iwork[i-begin] = i+1; + glp_del_rows(lp->prob, end-begin+1, lp->iwork-1); + for (i = begin; i <= end; i++) + lp->iwork[i-begin] = 0; + for (i = end+1; i < m; i++) + lp->rflag[i-(end-begin+1)] = lp->rflag[i]; +done: return errcode; +} + +int CPXdelsetcols(CPXENV *env, CPXLP *lp, int delstat[]) +{ xassert(env == env); + xassert(lp == lp); + xassert(delstat == delstat); + xprintf("CPXdelsetcols: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXdelsetrows(CPXENV *env, CPXLP *lp, int delstat[]) +{ int i, m, cnt, ind, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + m = glp_get_num_rows(lp->prob); + if (m > 0 && delstat == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + errcode = 0; + invalidate(lp); + enlargeiwork(lp, m); + cnt = ind = 0; + for (i = 0; i < m; i++) + { if (delstat[i] == 1) + { delstat[i] = -1; + lp->iwork[cnt++] = i+1; + } + else + { delstat[i] = ind; + lp->rflag[ind++] = lp->rflag[i]; + } + } + if (cnt > 0) + glp_del_rows(lp->prob, cnt, lp->iwork-1); + for (i = 0; i < cnt; i++) + lp->iwork[i] = 0; +done: return errcode; +} + +int CPXdualopt(CPXENV *env, CPXLP *lp); + +int CPXfreeprob(CPXENV *env, CPXLP **_lp) +{ CPXLP *lp; + int errcode; + errcode = checkenv(env); + if (errcode) goto done; + if (_lp == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + lp = *_lp; + errcode = checklp(env, lp); + if (errcode) goto done; + errcode = 0; + env = lp->env; + if (env->list == lp) + env->list = lp->link; + else + { CPXLP *pp; + for (pp = env->list; pp != NULL; pp = pp->link) + if (pp->link == lp) break; + xassert(pp != NULL); + pp->link = lp->link; + } + glp_delete_prob(lp->prob); + xfree(lp->rflag); + xfree(lp->iwork); + xfree(lp); + *_lp = NULL; +done: return errcode; +} + +int CPXgetbase(CPXENV *env, CPXLP *lp, int cstat[], int rstat[]) +{ int i, j, m, n, stat, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + if (!lp->stat) + { errcode = error(env, CPXERR_NO_SOLN); + goto done; + } + if (lp->meth == CPX_ALG_PRIMAL || lp->meth == CPX_ALG_DUAL) + ; + else + { errcode = error(env, CPXERR_NO_BASIC_SOLN); + goto done; + } + errcode = 0; + if (rstat != NULL) + { m = glp_get_num_rows(lp->prob); + for (i = 0; i < m; i++) + { stat = glp_get_row_stat(lp->prob, i+1); + if (stat == GLP_BS) + rstat[i] = CPX_BASIC; + else if (lp->rflag[i] == RF_NOT_RANGED || stat != GLP_NU) + rstat[i] = CPX_AT_LOWER; + else + rstat[i] = CPX_AT_UPPER; + } + } + if (cstat != NULL) + { n = glp_get_num_cols(lp->prob); + for (j = 0; j < n; j++) + { stat = glp_get_col_stat(lp->prob, j+1); + if (stat == GLP_BS) + cstat[j] = CPX_BASIC; + else if (stat == GLP_NU) + cstat[j] = CPX_AT_UPPER; + else if (stat == GLP_NF) + cstat[j] = CPX_FREE_SUPER; + else + cstat[j] = CPX_AT_LOWER; + } + } +done: return errcode; +} + +int CPXgetbasednorms(CPXENV *env, CPXLP *lp, int cstat[], int rstat[], + double dnorm[]) +{ int i, m, errcode; + errcode = CPXgetbase(env, lp, cstat, rstat); + if (errcode) goto done; + if (dnorm != NULL) + { m = glp_get_num_rows(lp->prob); + for (i = 0; i < m; i++) dnorm[i] = 1.0; + } +done: return errcode; +} + +int CPXgetbhead(CPXENV *env, CPXLP *lp, int head[], double x[]) +{ xassert(env == env); + xassert(lp == lp); + xassert(head == head); + xassert(x == x); + xprintf("CPXgetbhead: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXgetdblparam(CPXENV *env, int whichparam, double *value) +{ int k, errcode; + errcode = checkenv(env); + if (errcode) goto done; + k = finddblparam(whichparam); + if (k < 0) + { errcode = error(env, CPXERR_BAD_PARAM_NUM); + goto done; + } + errcode = 0; + if (value != NULL) + *value = env->dblparam[k]; +done: return errcode; +} + +int CPXgetdj(CPXENV *env, CPXLP *lp, double dj[], int begin, int end) +{ int j, n, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + n = glp_get_num_cols(lp->prob); + if (!(0 <= begin && begin <= end && end < n)) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + if (!lp->stat) + { errcode = error(env, CPXERR_NO_SOLN); + goto done; + } + errcode = 0; + if (lp->meth == CPX_ALG_PRIMAL || lp->meth == CPX_ALG_DUAL) + { if (dj != NULL) + { for (j = begin; j <= end; j++) + dj[j-begin] = glp_get_col_dual(lp->prob, j+1); + } + } + else + xassert(lp != lp); +done: return errcode; +} + +char *CPXgeterrorstring(CPXENV *env, int errcode, char *buffer) +{ const char *string; + xassert(env == env); + string = finderrstring(errcode); + if (string == NULL) + buffer = NULL; + else + sprintf(buffer, "CPLEX Error %5d: %s.\n", errcode, string); + return buffer; +} + +int CPXgetijdiv(CPXENV *env, CPXLP *lp, int *idiv, int *jdiv) +{ xassert(env == env); + xassert(lp == lp); + xassert(idiv == idiv); + xassert(jdiv == jdiv); + xprintf("CPXgetijdiv: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXgetintparam(CPXENV *env, int whichparam, int *value) +{ int k, errcode; + errcode = checkenv(env); + if (errcode) goto done; + k = findintparam(whichparam); + if (k < 0) + { errcode = error(env, CPXERR_BAD_PARAM_NUM); + goto done; + } + errcode = 0; + if (value != NULL) + *value = env->intparam[k]; +done: return errcode; +} + +int CPXgetlb(CPXENV *env, CPXLP *lp, double lb[], int begin, int end) +{ xassert(env == env); + xassert(lp == lp); + xassert(lb == lb); + xassert(begin == begin); + xassert(end == end); + xprintf("CPXgetlb: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXgetmethod(CPXENV *env, CPXLP *lp) +{ int method; + if (checklp(env, lp)) + method = CPX_ALG_NONE; + else + method = lp->meth; + return method; +} + +int CPXgetnumcols(CPXENV *env, CPXLP *lp) +{ int numcols; + if (checklp(env, lp)) + numcols = 0; + else + numcols = glp_get_num_cols(lp->prob); + return numcols; +} + +int CPXgetnumnz(CPXENV *env, CPXLP *lp) +{ int numnz; + if (checklp(env, lp)) + numnz = 0; + else + numnz = glp_get_num_nz(lp->prob); + return numnz; +} + +int CPXgetnumrows(CPXENV *env, CPXLP *lp) +{ int numrows; + if (checklp(env, lp)) + numrows = 0; + else + numrows = glp_get_num_rows(lp->prob); + return numrows; +} + +int CPXgetobjval(CPXENV *env, CPXLP *lp, double *objval) +{ int errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + if (!lp->stat) + { errcode = error(env, CPXERR_NO_SOLN); + goto done; + } + errcode = 0; + if (lp->meth == CPX_ALG_PRIMAL || lp->meth == CPX_ALG_DUAL) + { if (objval != NULL) + *objval = glp_get_obj_val(lp->prob); + } + else + xassert(lp != lp); +done: return errcode; +} + +int CPXgetpi(CPXENV *env, CPXLP *lp, double pi[], int begin, int end) +{ int i, m, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + m = glp_get_num_rows(lp->prob); + if (!(0 <= begin && begin <= end && end < m)) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + if (!lp->stat) + { errcode = error(env, CPXERR_NO_SOLN); + goto done; + } + errcode = 0; + if (lp->meth == CPX_ALG_PRIMAL || lp->meth == CPX_ALG_DUAL) + { if (pi != NULL) + { for (i = begin; i <= end; i++) + pi[i-begin] = glp_get_row_dual(lp->prob, i+1); + } + } + else + xassert(lp != lp); +done: return errcode; +} + +int CPXgetsense(CPXENV *env, CPXLP *lp, char sense[], int begin, + int end) +{ xassert(env == env); + xassert(lp == lp); + xassert(sense == sense); + xassert(begin == begin); + xassert(end == end); + xprintf("CPXgetsense: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXgetslack(CPXENV *env, CPXLP *lp, double slack[], int begin, + int end) +{ int i, m, type, errcode; + double temp; + errcode = checklp(env, lp); + if (errcode) goto done; + m = glp_get_num_rows(lp->prob); + if (!(0 <= begin && begin <= end && end < m)) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + if (!lp->stat) + { errcode = error(env, CPXERR_NO_SOLN); + goto done; + } + errcode = 0; + if (lp->meth == CPX_ALG_PRIMAL || lp->meth == CPX_ALG_DUAL) + { if (slack != NULL) + { for (i = begin; i <= end; i++) + { type = glp_get_row_type(lp->prob, i+1); + temp = glp_get_row_prim(lp->prob, i+1); + if (lp->rflag[i] == RF_NOT_RANGED) + { if (type == GLP_LO || type == GLP_FX) + slack[i-begin] = + glp_get_row_lb(lp->prob, i+1) - temp; + else if (type == GLP_UP) + slack[i-begin] = + glp_get_row_ub(lp->prob, i+1) - temp; + else + xassert(type != type); + } + else if (lp->rflag[i] == RF_RANGED_POS) + { xassert(type == GLP_DB || type == GLP_FX); + slack[i-begin] = + temp - glp_get_row_lb(lp->prob, i+1); + } + else if (lp->rflag[i] == RF_RANGED_NEG) + { xassert(type == GLP_DB); + slack[i-begin] = + temp - glp_get_row_ub(lp->prob, i+1); + } + else + xassert(lp != lp); + } + } + } + else + xassert(lp != lp); +done: return errcode; +} + +int CPXgetstat(CPXENV *env, CPXLP *lp) +{ int stat; + if (checklp(env, lp)) + stat = 0; + else + stat = lp->stat; + return stat; +} + +int CPXgetub(CPXENV *env, CPXLP *lp, double ub[], int begin, int end) +{ xassert(env == env); + xassert(lp == lp); + xassert(ub == ub); + xassert(begin == begin); + xassert(end == end); + xprintf("CPXgetub: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXgetweight(CPXENV *env, CPXLP *lp, int rcnt, const int rmatbeg[], + const int rmatind[], const double rmatval[], double weight[], + int dpriind) +{ xassert(env == env); + xassert(lp == lp); + xassert(rcnt == rcnt); + xassert(rmatbeg == rmatbeg); + xassert(rmatind == rmatind); + xassert(rmatval == rmatval); + xassert(weight == weight); + xassert(dpriind == dpriind); + xprintf("CPXgetweight: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXgetx(CPXENV *env, CPXLP *lp, double x[], int begin, int end) +{ int j, n, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + n = glp_get_num_cols(lp->prob); + if (!(0 <= begin && begin <= end && end < n)) + { errcode = error(env, CPXERR_INDEX_RANGE); + goto done; + } + if (!lp->stat) + { errcode = error(env, CPXERR_NO_SOLN); + goto done; + } + errcode = 0; + if (lp->meth == CPX_ALG_PRIMAL || lp->meth == CPX_ALG_DUAL) + { if (x != NULL) + { for (j = begin; j <= end; j++) + x[j-begin] = glp_get_col_prim(lp->prob, j+1); + } + } + else + xassert(lp != lp); +done: return errcode; +} + +int CPXinfodblparam(CPXENV *env, int whichparam, double *defvalue, + double *minvalue, double *maxvalue) +{ int k, errcode; + errcode = checkenv(env); + if (errcode) goto done; + k = finddblparam(whichparam); + if (k < 0) + { errcode = error(env, CPXERR_BAD_PARAM_NUM); + goto done; + } + errcode = 0; + if (defvalue != NULL) + *defvalue = dblparam[k].defv; + if (minvalue != NULL) + *minvalue = dblparam[k].minv; + if (maxvalue != NULL) + *maxvalue = dblparam[k].maxv; +done: return errcode; +} + +int CPXinfointparam(CPXENV *env, int whichparam, int *defvalue, + int *minvalue, int *maxvalue) +{ int k, errcode; + errcode = checkenv(env); + if (errcode) goto done; + k = findintparam(whichparam); + if (k < 0) + { errcode = error(env, CPXERR_BAD_PARAM_NUM); + goto done; + } + errcode = 0; + if (defvalue != NULL) + *defvalue = intparam[k].defv; + if (minvalue != NULL) + *minvalue = intparam[k].minv; + if (maxvalue != NULL) + *maxvalue = intparam[k].maxv; +done: return errcode; +} + +int CPXmdleave(const CPXENV *env, CPXLP *lp, const int goodlist[], + int goodlen, double downratio[], double upratio[]) +{ int k; + xassert(env == env); + xassert(lp == lp); + xassert(goodlist == goodlist); + xassert(goodlen >= 0); + xassert(downratio != NULL); + xassert(upratio != NULL); + /* not implemented yet */ + for (k = 0; k < goodlen; k++) + downratio[k] = upratio[k] = 0.0; + return 0; +} + +int CPXnewcols(CPXENV *env, CPXLP *lp, int ccnt, const double obj[], + const double lb[], const double ub[], const char ctype[], + char *colname[]) +{ int j, n, kind, type, errcode; + double lbnd, ubnd; + errcode = checklp(env, lp); + if (errcode) goto done; + if (ccnt < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + for (j = 0; j < ccnt; j++) + { if (ctype != NULL) + { if (!(ctype[j] == 'C' || ctype[j] == 'B' || + ctype[j] == 'I')) + { errcode = error(env, CPXERR_BAD_CTYPE, j); + goto done; + } + } + if (colname != NULL) + { if (colname[j] == NULL) + { errcode = error(env, CPXERR_NULL_NAME, j); + goto done; + } + } + } + errcode = 0; + invalidate(lp); + n = glp_get_num_cols(lp->prob); + if (ccnt > 0) + glp_add_cols(lp->prob, ccnt); + for (j = 0; j < ccnt; j++) + { if (colname != NULL) + glp_set_col_name(lp->prob, n+j+1, colname[j]); + if (obj != NULL) + glp_set_obj_coef(lp->prob, n+j+1, obj[j]); + lbnd = (lb == NULL ? 0.0 : lb[j]); + ubnd = (ub == NULL ? 0.0 : ub[j]); + if (lbnd <= -CPX_INFBOUND && ubnd >= +CPX_INFBOUND) + type = GLP_FR; + else if (ubnd >= +CPX_INFBOUND) + type = GLP_LO; + else if (lbnd <= -CPX_INFBOUND) + type = GLP_UP; + else if (lbnd != ubnd) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(lp->prob, n+j+1, type, lbnd, ubnd); + if (ctype != NULL) + { if (ctype[j] == 'C') + kind = GLP_CV; + else if (ctype[j] == 'B') + kind = GLP_BV; + else if (ctype[j] == 'I') + kind = GLP_IV; + else + xassert(ctype != ctype); + glp_set_col_kind(lp->prob, n+j+1, kind); + } + } +done: return errcode; +} + +int CPXnewrows(CPXENV *env, CPXLP *lp, int rcnt, const double rhs[], + const char sense[], const double rngval[], char *rowname[]) +{ int i, m, type, errcode; + double lbnd, ubnd; + errcode = checklp(env, lp); + if (errcode) goto done; + if (rcnt < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + for (i = 0; i < rcnt; i++) + { if (sense != NULL) + { if (!(sense[i] == 'L' || sense[i] == 'E' || + sense[i] == 'G' || sense[i] == 'R')) + { errcode = error(env, CPXERR_BAD_SENSE, i); + goto done; + } + } + if (rowname != NULL) + { if (rowname[i] == NULL) + { errcode = error(env, CPXERR_NULL_NAME, i); + goto done; + } + } + } + errcode = 0; + invalidate(lp); + m = glp_get_num_rows(lp->prob); + if (rcnt > 0) + glp_add_rows(lp->prob, rcnt); + enlargerflag(lp); + for (i = 0; i < rcnt; i++) + { if (rowname != NULL) + glp_set_row_name(lp->prob, m+i+1, rowname[i]); + lbnd = ubnd = (rhs == NULL ? 0.0 : rhs[i]); + if (sense == NULL || sense[i] == 'E') + { lp->rflag[m+i] = RF_NOT_RANGED; + type = GLP_FX; + } + else if (sense[i] == 'L') + { lp->rflag[m+i] = RF_NOT_RANGED; + type = GLP_UP; + } + else if (sense[i] == 'G') + { lp->rflag[m+i] = RF_NOT_RANGED; + type = GLP_LO; + } + else if (sense[i] == 'R') + { if (rngval == NULL || rngval[i] == 0.0) + { lp->rflag[m+i] = RF_RANGED_POS; + type = GLP_FX; + } + else if (rngval[i] > 0.0) + { lp->rflag[m+i] = RF_RANGED_POS; + type = GLP_DB; + ubnd += rngval[i]; + } + else /* rngval[i] < 0.0 */ + { lp->rflag[m+i] = RF_RANGED_NEG; + type = GLP_DB; + lbnd += rngval[i]; + } + } + else + xassert(sense != sense); + glp_set_row_bnds(lp->prob, m+i+1, type, lbnd, ubnd); + } +done: return errcode; +} + +CPXENV *CPXopenCPLEX(int *status) +{ CPXENV *env; + int k, card; + env = xmalloc(sizeof(CPXENV)); + env->list = NULL; + card = sizeof(intparam) / sizeof(struct intparam); + env->intparam = xcalloc(card, sizeof(int)); + for (k = 0; k < card; k++) + env->intparam[k] = intparam[k].defv; + card = sizeof(dblparam) / sizeof(struct dblparam); + env->dblparam = xcalloc(card, sizeof(double)); + for (k = 0; k < card; k++) + env->dblparam[k] = dblparam[k].defv; + if (status != NULL) *status = 0; + return env; +} + +int CPXpivotin(CPXENV *env, CPXLP *lp, const int rlist[], int rlen) +{ int i, m, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + if (rlen < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (rlen > 0 && rlist == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + m = glp_get_num_rows(lp->prob); + for (i = 0; i < rlen; i++) + { if (!(0 <= rlist[i] && rlist[i] < m)) + { errcode = error(env, CPXERR_ROW_INDEX_RANGE, i); + goto done; + } + } + errcode = 0; + for (i = 0; i < rlen; i++) + { if (glp_get_row_type(lp->prob, rlist[i]+1) != GLP_FX) + { if (glp_get_row_stat(lp->prob, rlist[i]+1) != GLP_BS) + { /* not implemented yet */ + break; + } + } + } +done: return errcode; +} + +int CPXpivotout(CPXENV *env, CPXLP *lp, const int clist[], int clen) +{ int j, n, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + if (clen < 0) + { errcode = error(env, CPXERR_BAD_ARGUMENT); + goto done; + } + if (clen > 0 && clist == NULL) + { errcode = error(env, CPXERR_NULL_POINTER); + goto done; + } + n = glp_get_num_cols(lp->prob); + for (j = 0; j < clen; j++) + { if (!(0 <= clist[j] && clist[j] < n)) + { errcode = error(env, CPXERR_COL_INDEX_RANGE, j); + goto done; + } + if (glp_get_col_type(lp->prob, clist[j]+1) != GLP_FX) + { errcode = error(env, CPXERR_NOT_FIXED); + goto done; + } + } + errcode = 0; + for (j = 0; j < clen; j++) + { if (glp_get_col_stat(lp->prob, clist[j]+1) == GLP_BS) + { /* not implemented yet */ + break; + } + } +done: return errcode; +} + +int CPXprimopt(CPXENV *env, CPXLP *lp); + +int CPXsavwrite(CPXENV *env, CPXLP *lp, const char *filename) +{ xassert(env == env); + xassert(lp == lp); + xassert(filename == filename); + xprintf("CPXsavwrite: not implemented yet\n"); + exit(EXIT_FAILURE); + return -1; +} + +int CPXsetdblparam(CPXENV *env, int whichparam, double newvalue) +{ int k, errcode; + errcode = checkenv(env); + if (errcode) goto done; + k = finddblparam(whichparam); + if (k < 0) + { errcode = error(env, CPXERR_BAD_PARAM_NUM); + goto done; + } + if (newvalue < dblparam[k].minv) + { errcode = error(env, CPXERR_PARAM_TOO_SMALL); + goto done; + } + if (newvalue > dblparam[k].maxv) + { errcode = error(env, CPXERR_PARAM_TOO_BIG); + goto done; + } + errcode = 0; + env->dblparam[k] = newvalue; +done: return errcode; +} + +int CPXsetintparam(CPXENV *env, int whichparam, int newvalue) +{ int k, errcode; + errcode = checkenv(env); + if (errcode) goto done; + k = findintparam(whichparam); + if (k < 0) + { errcode = error(env, CPXERR_BAD_PARAM_NUM); + goto done; + } + if (newvalue < intparam[k].minv) + { errcode = error(env, CPXERR_PARAM_TOO_SMALL); + goto done; + } + if (newvalue > intparam[k].maxv) + { errcode = error(env, CPXERR_PARAM_TOO_BIG); + goto done; + } + errcode = 0; + env->intparam[k] = newvalue; +done: return errcode; +} + +int CPXsolninfo(CPXENV *env, CPXLP *lp, int *solnmethod, int *solntype, + int *pfeasind, int *dfeasind) +{ int type, pfeas, dfeas, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + errcode = 0; + if (!lp->stat) + type = CPX_NO_SOLN, pfeas = dfeas = 0; + else if (lp->meth == CPX_ALG_PRIMAL || lp->meth == CPX_ALG_DUAL) + { type = CPX_BASIC_SOLN; + pfeas = (glp_get_prim_stat(lp->prob) == GLP_FEAS); + dfeas = (glp_get_dual_stat(lp->prob) == GLP_FEAS); + } + else + xassert(lp != lp); + if (solnmethod != NULL) + *solnmethod = lp->meth; + if (solntype != NULL) + *solntype = type; + if (pfeasind != NULL) + *pfeasind = pfeas; + if (dfeasind != NULL) + *dfeasind = dfeas; +done: return errcode; +} + +int CPXsolution(CPXENV *env, CPXLP *lp, int *lpstat, double *objval, + double x[], double pi[], double slack[], double dj[]) +{ int m, n, errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + if (!lp->stat) + { errcode = error(env, CPXERR_NO_SOLN); + goto done; + } + errcode = 0; + m = glp_get_num_rows(lp->prob); + n = glp_get_num_cols(lp->prob); + if (lp->meth == CPX_ALG_PRIMAL || lp->meth == CPX_ALG_DUAL) + { if (lpstat != NULL) + *lpstat = CPXgetstat(env, lp); + if (objval != NULL) + xassert(CPXgetobjval(env, lp, objval) == 0); + if (x != NULL) + xassert(CPXgetx(env, lp, x, 0, n-1) == 0); + if (pi != NULL) + xassert(CPXgetpi(env, lp, pi, 0, m-1) == 0); + if (slack != NULL) + xassert(CPXgetslack(env, lp, slack, 0, m-1) == 0); + if (dj != NULL) + xassert(CPXgetdj(env, lp, dj, 0, n-1) == 0); + } + else + xassert(lp != lp); +done: return errcode; +} + +int CPXstrongbranch(CPXENV *env, CPXLP *lp, const int goodlist[], + int goodlen, double downpen[], double uppen[], int itlim) +{ int k; + xassert(env == env); + xassert(lp == lp); + xassert(goodlist == goodlist); + xassert(goodlen >= 0); + xassert(downpen != NULL); + xassert(uppen != NULL); + xassert(itlim == itlim); + /* not implemented yet */ + for (k = 0; k < goodlen; k++) + downpen[k] = uppen[k] = 0.0; + return 0; +} + +static int xstrcasecmp(const char *s1, const char *s2) +{ int c1, c2; + for (;;) + { c1 = toupper((unsigned char)*s1++); + c2 = toupper((unsigned char)*s2++); + if (c1 == '\0' || c1 != c2) break; + } + return c1 - c2; +} + +static void getfiletype(const char *filename, char type[3+1]) +{ /* determine filetype from filename */ + int beg, end; + beg = end = strlen(filename); + while (beg > 0 && filename[beg-1] != '.' && end - beg < 3) + beg--; + if (beg > 0 && filename[beg-1] == '.' && + xstrcasecmp(&filename[beg], "gz") == 0) + { end = --beg; + while (beg > 0 && filename[beg-1] != '.' && end - beg < 3) + beg--; + } + if (beg > 0 && filename[beg-1] == '.') + { memcpy(type, &filename[beg], end - beg); + type[end - beg] = '\0'; + } + else + type[0] = '\0'; + return; +} + +int CPXwriteprob(CPXENV *env, CPXLP *lp, const char *filename, + const char *filetype) +{ glp_prob *copy; + int errcode; + char type[3+1]; + errcode = checklp(env, lp); + if (errcode) goto done; + if (filename == NULL) + { errcode = error(env, CPXERR_NO_FILENAME); + goto done; + } + if (filetype == NULL) + getfiletype(filename, type), filetype = type; + if (xstrcasecmp(filetype, "MPS") == 0) + { glp_term_out(GLP_OFF); + errcode = glp_write_mps(lp->prob, GLP_MPS_FILE, NULL, filename) + ; + glp_term_out(GLP_ON); + } + else if (xstrcasecmp(filetype, "LP") == 0) + { glp_term_out(GLP_OFF); + errcode = glp_write_lp(lp->prob, NULL, filename); + glp_term_out(GLP_ON); + } + else if (xstrcasecmp(filetype, "RMP") == 0 || + xstrcasecmp(filetype, "REW") == 0) + { copy = glp_create_prob(); + glp_copy_prob(copy, lp->prob, GLP_OFF); + glp_term_out(GLP_OFF); + errcode = glp_write_mps(copy, GLP_MPS_DECK, NULL, filename); + glp_term_out(GLP_ON); + glp_delete_prob(copy); + } + else if (xstrcasecmp(filetype, "RLP") == 0) + { copy = glp_create_prob(); + glp_copy_prob(copy, lp->prob, GLP_OFF); + glp_term_out(GLP_OFF); + errcode = glp_write_lp(copy, NULL, filename); + glp_term_out(GLP_ON); + glp_delete_prob(copy); + } + else + { errcode = error(env, CPXERR_BAD_FILETYPE); + goto done; + } + if (errcode) + errcode = error(env, CPXERR_FAIL_OPEN_WRITE, filename); +done: return errcode; +} + +/**********************************************************************/ + +static int solvelp(CPXENV *env, CPXLP *lp, int meth) +{ glp_smcp parm; + int errcode; + errcode = checklp(env, lp); + if (errcode) goto done; + errcode = 0; + invalidate(lp); + glp_init_smcp(&parm); + switch (meth) + { case CPX_ALG_PRIMAL: + parm.meth = GLP_PRIMAL; + break; + case CPX_ALG_DUAL: + parm.meth = GLP_DUAL; + break; + default: + xassert(meth != meth); + } + switch (getintparam(env, CPX_PARAM_SIMDISPLAY)) + { case 0: + parm.msg_lev = GLP_MSG_OFF; + break; + case 1: + parm.msg_lev = GLP_MSG_ALL; + break; + case 2: + parm.msg_lev = GLP_MSG_ALL; + parm.out_frq = 1; + break; + default: + xassert(env != env); + } + xassert(getdblparam == getdblparam); + switch (getintparam(env, CPX_PARAM_ADVIND)) + { case 0: + glp_term_out(GLP_OFF); + glp_adv_basis(lp->prob, 0); + glp_term_out(GLP_ON); + break; + case 1: + case 2: + break; + default: + xassert(env != env); + } + if (!glp_bf_exists(lp->prob)) + { if (glp_factorize(lp->prob) != 0) + { glp_term_out(GLP_OFF); + glp_adv_basis(lp->prob, 0); + glp_term_out(GLP_ON); + if (glp_factorize(lp->prob) != 0) + glp_std_basis(lp->prob); + } + } + xassert(glp_simplex(lp->prob, &parm) == 0); + switch (glp_get_status(lp->prob)) + { case GLP_OPT: + lp->stat = CPX_STAT_OPTIMAL; + lp->meth = meth; + break; + case GLP_NOFEAS: + lp->stat = CPX_STAT_INFEASIBLE; + lp->meth = meth; + break; + case GLP_UNBND: + lp->stat = CPX_STAT_UNBOUNDED; + lp->meth = meth; + break; + default: + xassert(lp != lp); + } +done: return errcode; +} + +int CPXprimopt(CPXENV *env, CPXLP *lp) +{ int errcode; + errcode = solvelp(env, lp, CPX_ALG_PRIMAL); + return errcode; +} + +int CPXdualopt(CPXENV *env, CPXLP *lp) +{ int errcode; + errcode = solvelp(env, lp, CPX_ALG_DUAL); + return errcode; +} + +int CPXlpopt(CPXENV *env, CPXLP *lp) +{ int errcode; + errcode = solvelp(env, lp, CPX_ALG_PRIMAL); + return errcode; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/cplex/cplex.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cplex/cplex.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,301 @@ +/* cplex.h (CPLEX-like interface to GLPK API) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef _CPLEX_H +#define _CPLEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CPXENV CPXENV, *CPXENVptr; +typedef struct CPXLP CPXLP, *CPXLPptr; + +#define CPX_VERSION 900 + +#define CPX_OFF 0 +#define CPX_ON 1 + +#define CPX_INFBOUND 1e20 + +/* error codes: */ +#define CPXERR_NO_MEMORY 1001 +#define CPXERR_NO_ENVIRONMENT 1002 +#define CPXERR_BAD_ARGUMENT 1003 +#define CPXERR_NULL_POINTER 1004 +#define CPXERR_NO_PROBLEM 1009 +#define CPXERR_BAD_PARAM_NUM 1013 +#define CPXERR_PARAM_TOO_SMALL 1014 +#define CPXERR_PARAM_TOO_BIG 1015 +#define CPXERR_INDEX_RANGE 1200 +#define CPXERR_COL_INDEX_RANGE 1201 +#define CPXERR_ROW_INDEX_RANGE 1203 +#define CPXERR_NEGATIVE_SURPLUS 1207 +#define CPXERR_BAD_SENSE 1215 +#define CPXERR_NO_SOLN 1217 +#define CPXERR_NOT_FIXED 1221 +#define CPXERR_DUP_ENTRY 1222 +#define CPXERR_NULL_NAME 1224 +#define CPXERR_ARRAY_NOT_ASCENDING 1226 +#define CPXERR_COUNT_RANGE 1227 +#define CPXERR_BAD_LUB 1229 +#define CPXERR_BAD_STATUS 1253 +#define CPXERR_NO_BASIC_SOLN 1261 +#define CPXERR_NO_FILENAME 1421 +#define CPXERR_FAIL_OPEN_WRITE 1422 +#define CPXERR_BAD_FILETYPE 1424 +#define CPXERR_BAD_CTYPE 3021 + +/* control parameters: */ +#define CPX_PARAM_ADVIND 1001 +#define CPX_PARAM_AGGIND 1003 +#define CPX_PARAM_DPRIIND 1009 +#define CPX_PARAM_EPOPT 1014 +#define CPX_PARAM_EPPER 1015 +#define CPX_PARAM_EPRHS 1016 +#define CPX_PARAM_FASTMIP 1017 /* ??? */ +#define CPX_PARAM_SIMDISPLAY 1019 +#define CPX_PARAM_ITLIM 1020 +#define CPX_PARAM_OBJLLIM 1025 +#define CPX_PARAM_OBJULIM 1026 +#define CPX_PARAM_PERIND 1027 +#define CPX_PARAM_PPRIIND 1029 +#define CPX_PARAM_PREIND 1030 +#define CPX_PARAM_REINV 1031 +#define CPX_PARAM_SCRIND 1035 +#define CPX_PARAM_DATACHECK 1056 + +/* CPX_PARAM_DPRIIND: */ +#define CPX_DPRIIND_AUTO 0 +#define CPX_DPRIIND_FULL 1 +#define CPX_DPRIIND_STEEP 2 +#define CPX_DPRIIND_FULL_STEEP 3 +#define CPX_DPRIIND_STEEPQSTART 4 +#define CPX_DPRIIND_DEVEX 5 + +/* CPX_PARAM_PPRIIND: */ +#define CPX_PPRIIND_PARTIAL (-1) +#define CPX_PPRIIND_AUTO 0 +#define CPX_PPRIIND_DEVEX 1 +#define CPX_PPRIIND_STEEP 2 +#define CPX_PPRIIND_STEEPQSTART 3 +#define CPX_PPRIIND_FULL 4 + +/* CPXgetprobtype: */ +#define CPXPROB_LP 0 +#define CPXPROB_MIP 1 +#define CPXPROB_RELAXED 2 +#define CPXPROB_FIXED 3 +#define CPXPROB_QP 5 +#define CPXPROB_ZEROEDQP 6 + +/* CPXgetobjsen: */ +#define CPX_MIN 1 +#define CPX_MAX (-1) + +/* CPXgetbase: */ +#define CPX_AT_LOWER 0 +#define CPX_BASIC 1 +#define CPX_AT_UPPER 2 +#define CPX_FREE_SUPER 3 + +/* CPXgetstat: */ +#define CPX_STAT_OPTIMAL 1 +#define CPX_STAT_UNBOUNDED 2 +#define CPX_STAT_INFEASIBLE 3 +#define CPX_STAT_INForUNBD 4 +#define CPX_STAT_OPTIMAL_INFEAS 5 +#define CPX_STAT_ABORT_IT_LIM 10 +#define CPX_STAT_ABORT_OBJ_LIM 12 + +/* CPXgetmethod: */ +#define CPX_ALG_NONE 0 +#define CPX_ALG_PRIMAL 1 +#define CPX_ALG_DUAL 2 +#define CPX_ALG_BARRIER 4 + +/* CPXsolninfo: */ +#define CPX_NO_SOLN 0 +#define CPX_BASIC_SOLN 1 +#define CPX_NONBASIC_SOLN 2 +#define CPX_PRIMAL_SOLN 3 + +int CPXaddcols(CPXENV *env, CPXLP *lp, int ccnt, int nzcnt, + const double obj[], const int cmatbeg[], const int cmatind[], + const double cmatval[], const double lb[], const double ub[], + char *colname[]); + +int CPXaddrows(CPXENV *env, CPXLP *lp, int ccnt, int rcnt, int nzcnt, + const double rhs[], const char sense[], const int rmatbeg[], + const int rmatind[], const double rmatval[], char *colname[], + char *rowname[]); + +int CPXbaropt(CPXENV *env, CPXLP *lp); + +int CPXbinvrow(CPXENV *env, CPXLP *lp, int i, double y[]); + +int CPXchgbds(CPXENV *env, CPXLP *lp, int cnt, const int indices[], + const char lu[], const double bd[]); + +int CPXchgcoeflist(CPXENV *env, CPXLP *lp, int numcoefs, + const int rowlist[], const int collist[], const double vallist[]); + +void CPXchgobjsen(CPXENV *env, CPXLP *lp, int maxormin); + +int CPXchgsense(CPXENV *env, CPXLP *lp, int cnt, const int indices[], + const char sense[]); + +int CPXcloseCPLEX(CPXENV **env); + +int CPXcopybase(CPXENV *env, CPXLP *lp, const int cstat[], + const int rstat[]); + +int CPXcopybasednorms(CPXENV *env, CPXLP *lp, const int cstat[], + const int rstat[], const double dnorm[]); + +int CPXcopylp(CPXENV *env, CPXLP *lp, int numcols, int numrows, + int objsen, const double obj[], const double rhs[], + const char sense[], const int matbeg[], const int matcnt[], + const int matind[], const double matval[], const double lb[], + const double ub[], const double rngval[]); + +int CPXcopylpwnames(CPXENV *env, CPXLP *lp, int numcols, int numrows, + int objsen, const double obj[], const double rhs[], + const char sense[], const int matbeg[], const int matcnt[], + const int matind[], const double matval[], const double lb[], + const double ub[], const double rngval[], char *colname[], + char *rowname[]); + +CPXLP *CPXcreateprob(CPXENV *env, int *status, const char *probname); + +int CPXdelcols(CPXENV *env, CPXLP *lp, int begin, int end); + +int CPXdelrows(CPXENV *env, CPXLP *lp, int begin, int end); + +int CPXdelsetcols(CPXENV *env, CPXLP *lp, int delstat[]); + +int CPXdelsetrows(CPXENV *env, CPXLP *lp, int delstat[]); + +int CPXdualopt(CPXENV *env, CPXLP *lp); + +int CPXfreeprob(CPXENV *env, CPXLP **lp); + +int CPXgetbase(CPXENV *env, CPXLP *lp, int cstat[], int rstat[]); + +int CPXgetbasednorms(CPXENV *env, CPXLP *lp, int cstat[], int rstat[], + double dnorm[]); + +int CPXgetbhead(CPXENV *env, CPXLP *lp, int head[], double x[]); + +int CPXgetdblparam(CPXENV *env, int whichparam, double *value); + +int CPXgetdj(CPXENV *env, CPXLP *lp, double dj[], int begin, int end); + +char *CPXgeterrorstring(CPXENV *env, int errcode, char *buffer); + +int CPXgetijdiv(CPXENV *env, CPXLP *lp, int *idiv, int *jdiv); + +int CPXgetintparam(CPXENV *env, int whichparam, int *value); + +int CPXgetlb(CPXENV *env, CPXLP *lp, double lb[], int begin, int end); + +int CPXgetmethod(CPXENV *env, CPXLP *lp); + +int CPXgetnumcols(CPXENV *env, CPXLP *lp); + +int CPXgetnumnz(CPXENV *env, CPXLP *lp); + +int CPXgetnumrows(CPXENV *env, CPXLP *lp); + +int CPXgetobjval(CPXENV *env, CPXLP *lp, double *objval); + +int CPXgetpi(CPXENV *env, CPXLP *lp, double pi[], int begin, int end); + +int CPXgetsense(CPXENV *env, CPXLP *lp, char sense[], int begin, + int end); + +int CPXgetslack(CPXENV *env, CPXLP *lp, double slack[], int begin, + int end); + +int CPXgetstat(CPXENV *env, CPXLP *lp); + +int CPXgetub(CPXENV *env, CPXLP *lp, double ub[], int begin, int end); + +int CPXgetweight(CPXENV *env, CPXLP *lp, int rcnt, const int rmatbeg[], + const int rmatind[], const double rmatval[], double weight[], + int dpriind); + +int CPXgetx(CPXENV *env, CPXLP *lp, double x[], int begin, int end); + +int CPXinfodblparam(CPXENV *env, int whichparam, double *defvalue, + double *minvalue, double *maxvalue); + +int CPXinfointparam(CPXENV *env, int whichparam, int *defvalue, + int *minvalue, int *maxvalue); + +int CPXlpopt(CPXENV *env, CPXLP *lp); + +int CPXmdleave(const CPXENV *env, CPXLP *lp, const int goodlist[], + int goodlen, double downratio[], double upratio[]); + +int CPXnewcols(CPXENV *env, CPXLP *lp, int ccnt, const double obj[], + const double lb[], const double ub[], const char ctype[], + char *colname[]); + +int CPXnewrows(CPXENV *env, CPXLP *lp, int rcnt, const double rhs[], + const char sense[], const double rngval[], char *rowname[]); + +CPXENV *CPXopenCPLEX(int *status); + +int CPXpivotin(CPXENV *env, CPXLP *lp, const int rlist[], int rlen); + +int CPXpivotout(CPXENV *env, CPXLP *lp, const int clist[], int clen); + +int CPXprimopt(CPXENV *env, CPXLP *lp); + +int CPXsavwrite(CPXENV *env, CPXLP *lp, const char *filename); + +int CPXsetdblparam(CPXENV *env, int whichparam, double newvalue); + +int CPXsetintparam(CPXENV *env, int whichparam, int newvalue); + +int CPXsolninfo(CPXENV *env, CPXLP *lp, int *solnmethod, int *solntype, + int *pfeasind, int *dfeasind); + +int CPXsolution(CPXENV *env, CPXLP *lp, int *lpstat, double *objval, + double x[], double pi[], double slack[], double dj[]); + +int CPXstrongbranch(CPXENV *env, CPXLP *lp, const int goodlist[], + int goodlen, double downpen[], double uppen[], int itlim); + +int CPXwriteprob(CPXENV *env, CPXLP *lp, const char *filename, + const char *filetype); + +#ifdef __cplusplus +} +#endif + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/cpp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/cpp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,67 @@ +/* CPP, Critical Path Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* Note: Reduced costs of auxiliary variables phi[j,k] (see below) + can be only zero or one. The critical path is defined by the + constraints, whose reduced cost is one. */ + +set J; +/* set of jobs (activities) */ + +set P{j in J}, in J, default {}; +/* P[j] is a subset of jobs that immediately precede job j */ + +param t{j in J}, >= 0; +/* duration required to perform job j */ + +var x{j in J}, >= 0; +/* starting time of job j */ + +s.t. phi{j in J, k in P[j]}: x[j] >= x[k] + t[k]; +/* job j can start only after all immediately preceding jobs have been + completely performed */ + +var z; +/* project makespan */ + +s.t. fin{j in J}: z >= x[j] + t[j]; +/* which is the maximum of the completion times of all the jobs */ + +minimize obj: z; +/* the objective is make z as small as possible */ + +data; + +/* The optimal solution is 46 */ + +param : J : t := + A 3 /* Excavate */ + B 4 /* Lay foundation */ + C 3 /* Rough plumbing */ + D 10 /* Frame */ + E 8 /* Finish exterior */ + F 4 /* Install HVAC */ + G 6 /* Rough electric */ + H 8 /* Sheet rock */ + I 5 /* Install cabinets */ + J 5 /* Paint */ + K 4 /* Final plumbing */ + L 2 /* Final electric */ + M 4 /* Install flooring */ +; + +set P[B] := A; +set P[C] := B; +set P[D] := B; +set P[E] := D; +set P[F] := D; +set P[G] := D; +set P[H] := C E F G; +set P[I] := H; +set P[J] := H; +set P[K] := I; +set P[L] := J; +set P[M] := K L; + +end; diff -r d59bea55db9b -r c445c931472f examples/crypto.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/crypto.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,84 @@ +/* CRYPTO, a crypto-arithmetic puzzle */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* This problem comes from the newsgroup rec.puzzle. + The numbers from 1 to 26 are assigned to the letters of the alphabet. + The numbers beside each word are the total of the values assigned to + the letters in the word (e.g. for LYRE: L, Y, R, E might be to equal + 5, 9, 20 and 13, or any other combination that add up to 47). + Find the value of each letter under the equations: + + BALLET 45 GLEE 66 POLKA 59 SONG 61 + CELLO 43 JAZZ 58 QUARTET 50 SOPRANO 82 + CONCERT 74 LYRE 47 SAXOPHONE 134 THEME 72 + FLUTE 30 OBOE 53 SCALE 51 VIOLIN 100 + FUGUE 50 OPERA 65 SOLO 37 WALTZ 34 + + Solution: + A, B,C, D, E,F, G, H, I, J, K,L,M, N, O, P,Q, R, S,T,U, V,W, X, Y, Z + 5,13,9,16,20,4,24,21,25,17,23,2,8,12,10,19,7,11,15,3,1,26,6,22,14,18 + + Reference: + Koalog Constraint Solver , + Simple problems, the crypto-arithmetic puzzle ALPHACIPHER. */ + +set LETTERS := +{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' +}; +/* set of letters */ + +set VALUES := 1..card(LETTERS); +/* set of values assigned to the letters */ + +set WORDS; +/* set of words */ + +param total{word in WORDS}; +/* total[word] is the total of the values assigned to the letters in + the word */ + +var x{i in LETTERS, j in VALUES}, binary; +/* x[i,j] = 1 means that letter i is assigned value j */ + +s.t. phi{i in LETTERS}: sum{j in VALUES} x[i,j] = 1; + +s.t. psi{j in VALUES}: sum{i in LETTERS} x[i,j] = 1; + +s.t. eqn{word in WORDS}: sum{k in 1..length(word), j in VALUES} + j * x[substr(word,k,1), j] = total[word]; + +solve; + +printf{i in LETTERS} " %s", i; +printf "\n"; + +printf{i in LETTERS} " %2d", sum{j in VALUES} j * x[i,j]; +printf "\n"; + +data; + +param : WORDS : total := + BALLET 45 + CELLO 43 + CONCERT 74 + FLUTE 30 + FUGUE 50 + GLEE 66 + JAZZ 58 + LYRE 47 + OBOE 53 + OPERA 65 + POLKA 59 + QUARTET 50 + SAXOPHONE 134 + SCALE 51 + SOLO 37 + SONG 61 + SOPRANO 82 + THEME 72 + VIOLIN 100 + WALTZ 34 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/csv/distances.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/csv/distances.csv Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,7 @@ +plant,market,distance +"Seattle","New York",2.5 +"Seattle","Chicago",1.7 +"Seattle","Topeka",1.8 +"San Diego","New York",2.5 +"San Diego","Chicago",1.8 +"San Diego","Topeka",1.4 diff -r d59bea55db9b -r c445c931472f examples/csv/markets.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/csv/markets.csv Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,4 @@ +market,demand +"New York",325. +"Chicago",300. +"Topeka",275. diff -r d59bea55db9b -r c445c931472f examples/csv/parameters.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/csv/parameters.csv Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,2 @@ +parameter,value +"transport cost",90. diff -r d59bea55db9b -r c445c931472f examples/csv/plants.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/csv/plants.csv Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,3 @@ +plant,capacity +"Seattle",350. +"San Diego",600. diff -r d59bea55db9b -r c445c931472f examples/csv/transp_csv.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/csv/transp_csv.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,70 @@ +# A TRANSPORTATION PROBLEM +# +# This problem finds a least cost shipping schedule that meets +# requirements at markets and supplies at factories. +# +# References: +# Dantzig G B, "Linear Programming and Extensions." +# Princeton University Press, Princeton, New Jersey, 1963, +# Chapter 3-3. + +set I; +/* canning plants */ + +set J; +/* markets */ + +set K dimen 2; +/* transportation lane */ + +set L; +/* parameters */ + +param a{i in I}; +/* capacity of plant i in cases */ + +param b{j in J}; +/* demand at market j in cases */ + +param d{i in I, j in J}; +/* distance in thousands of miles */ + +param e{l in L}; +/* parameters */ + +param f; +/* freight in dollars per case per thousand miles */ + +table tab_plant IN "CSV" "plants.csv" : + I <- [plant], a ~ capacity; + +table tab_market IN "CSV" "markets.csv" : + J <- [market], b ~ demand; + +table tab_distance IN "CSV" "distances.csv" : + K <- [plant, market], d ~ distance; + +table tab_parameter IN "CSV" "parameters.csv" : + L <- [parameter], e ~ value ; + +param c{i in I, j in J} := e['transport cost'] * d[i,j] / 1000; +/* transport cost in thousands of dollars per case */ + +var x{(i,j) in K} >= 0; +/* shipment quantities in cases */ + +minimize cost: sum{(i,j) in K} c[i,j] * x[i,j]; +/* total transportation costs in thousands of dollars */ + +s.t. supply{i in I}: sum{(i,j) in K} x[i,j] <= a[i]; +/* observe supply limit at plant i */ + +s.t. demand{j in J}: sum{(i,j) in K} x[i,j] >= b[j]; +/* satisfy demand at market j */ + +solve; + +table tab_result{(i,j) in K} OUT "CSV" "result.csv" : + i ~ plant, j ~ market, x[i,j] ~ shipment; + +end; diff -r d59bea55db9b -r c445c931472f examples/dbf/ForestMgt_Model_I_GIS_dbf.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/dbf/ForestMgt_Model_I_GIS_dbf.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,226 @@ +# Model I Forest Estate Modelling using GLPK/MathProg +# Reading and writing dbf files + +# by Noli Sicad --- nsicad@gmail.com +# 18 December 2009 + +# Forest Management 4th Edition +# by Lawrence Davis, K. Norman Johnson, Pete Bettinger, Theodore Howard +# Chapter 11 - Daniel Pickett +# http://warnell.forestry.uga.edu/Warnell/Bettinger/mgtbook/index.htm + +# Model I Formulation + +/* Note: This is not the full LP model mentioned in the book. +Some of the constraints are deliberately omitted in this model for the purpose of clarity. + +The features of MathProg in this example are: +* reading and writing dbf from regular dbf files, +* reading dbf file (database of shapefile (stands.shp)) (e.g. area parameter), +* using the area data in the constraints and +* writing dbf file from result of LP model. + +Model I - Harvest Scheduling formulation for Sustainable Forest Management (SFM) + +Features are: +* Net Present Value for the objective function (Revenue - Cost) +* Harvest Constraints by period - Sustainable Yield per Period +* Even-Flow Constraint / Volume - Harvest Flow Constraint - Alpha (1-Apha) +* Even-Flow Constraint / Volume - Harvest Flow Constraint - Beta (1 +Beta) +* Forest / Land Constraint -- Total Area of the forest +* Forest Stand Constraint -- Individual stands + +What is next? -- Forest Mgt Carbon Accounting for Climate Change + +Note: The model file that the data containing in +the dbf files is public domain material (so it is compatible with the +GNU GPL) and data can be found in +http://warnell.forestry.uga.edu/Warnell/Bettinger/mgtbook/index.htm + +# Noli Sicad --- nsicad@gmail.com + +*/ + +set G_STAND_TYPE; # A, B, C + +set I_CULTURAL_PRES; +set J_MGT_YEAR; + +param K_PERIOD; +param Forest_Cost{G_STAND_TYPE,I_CULTURAL_PRES, J_MGT_YEAR}; # cost + +param Yield_Table_Vol{G_STAND_TYPE, I_CULTURAL_PRES, J_MGT_YEAR, 1..K_PERIOD} >= 0; + + +param Alpha >= 0; +param Beta >= 0; + +param TCost_Table{G_STAND_TYPE, I_CULTURAL_PRES, J_MGT_YEAR, 1..K_PERIOD} >= 0; + +param NetRev_Table{G_STAND_TYPE, I_CULTURAL_PRES, J_MGT_YEAR, 1..K_PERIOD}; + + +var XForestLand{g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} >= 0; + + +#reading dbf tables +table tab IN "xBASE" "standtype.dbf": G_STAND_TYPE <- [STAND]; +display G_STAND_TYPE; + + +table tab2 IN "xBASE" "cultural_pres.dbf": I_CULTURAL_PRES <- [CUL_PRES]; +display I_CULTURAL_PRES; + +table tab3 IN "xBASE" "mgt_year.dbf": J_MGT_YEAR <- [MGT_YEAR]; +display J_MGT_YEAR; + +/* +param Forest_Cost{G_STAND_TYPE,I_CULTURAL_PRES, J_MGT_YEAR} default 0; # cost +*/ + +set S1, dimen 3; +table tab4 IN "xBASE" "Forest_Cost.dbf": S1 <- [STAND, CUL_PRES, MGT_YEAR],Forest_Cost ~FCOST; +display Forest_Cost; + +set S2, dimen 4; +table tab5 IN "xBASE" "Yield_Table_Vol.dbf": S2 <- [STAND, CUL_PRES, MGT_YEAR, PERIOD],Yield_Table_Vol ~YIELD; +display Yield_Table_Vol; + +set S3, dimen 4; +table tab5 IN "xBASE" "TCost_Table.dbf": S3 <- [STAND, CUL_PRES, MGT_YEAR, PERIOD],TCost_Table ~TCOST; +display TCost_Table; + + +set S4, dimen 4; +table tab5 IN "xBASE" "NetRev_Table.dbf": S4 <- [STAND, CUL_PRES, MGT_YEAR, PERIOD],NetRev_Table ~NETREV; +display NetRev_Table; + + +param MGT; + +param Area_Stand_Indi{g in G_STAND_TYPE, m in 1..MGT} default 0; + +set ST, dimen 2; +table tab5 IN "xBASE" "stands.dbf": ST <- [VEG_TYPE, MGT], Area_Stand_Indi ~ACRES; +display Area_Stand_Indi; + +param Area_Stand_Type{g in G_STAND_TYPE}:= sum {m in 1..MGT } Area_Stand_Indi[g,m]; +display Area_Stand_Type; + + +param Total_Area := sum {g in G_STAND_TYPE, m in 1..MGT } Area_Stand_Indi[g,m]; +display Total_Area; + +param Harvest_Min_Vol_Period; + + +var NetPresentValue; + +# Objective function +maximize Net_Present_Value: NetPresentValue; + +subject to NPV: + NetPresentValue = sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} Forest_Cost[g,i,j] * XForestLand[g,i,j]; + +# Harvest Constraint by Period +subject to Harvest_Period_H {k in 1..K_PERIOD}: + sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k] * XForestLand[g,i,j] >= Harvest_Min_Vol_Period; + + +#Even-Flow Constraint / Volume - Harvest Flow Constraint - Alpha +subject to Even_Flow_Constaints_Alpha {k in 6..K_PERIOD-1}: + (1 - Alpha) * sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k] * XForestLand[g,i,j] - + sum {g in G_STAND_TYPE,i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k+1] * XForestLand[g,i,j] <= 0; + +# Even-Flow Constraint / Volume - Harvest Flow Constraint - Beta +subject to Even_Flow_Constaints_Beta {k in 6..K_PERIOD-1}: + (1 + Beta) * sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k] * XForestLand[g,i,j] - + sum {g in G_STAND_TYPE,i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k+1] * XForestLand[g,i,j] >= 0; + +# Forest / Land Constraints +subject to Total_Area_Constraint: + sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} XForestLand[g,i,j] <= Total_Area; +display Total_Area; + +# Forest / Land Constraints for A B C +subject to Area {g in G_STAND_TYPE}: + sum {i in I_CULTURAL_PRES,j in J_MGT_YEAR} XForestLand[g,i,j] = Area_Stand_Type[g]; + + + +solve; +#RESULT SECTION +printf '#################################\n'; +printf 'Forest Management Model I - Noli Sicad\n'; +printf '\n'; +printf 'Net Present Value = %.2f\n', NetPresentValue; +printf '\n'; + +printf '\n'; +printf 'Variables\n'; +printf 'Stand_Type Age_Class Mgt_Presc Sign Value \n'; +printf{g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR}:'%5s %10s %11s = %10.2f\n', g,i,j, XForestLand[g,i,j]; +printf '\n'; + +printf 'Constraints\n'; +printf 'Period Harvest Sign \n'; +for {k in 1..K_PERIOD} { + printf '%5s %10.2f >= %.3f\n', k, sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k] * XForestLand[g,i,j], Harvest_Min_Vol_Period; + } + +# xbase (dbf) output +table Harvest{k in 1..K_PERIOD} OUT "xBASE" "HarvestArea1.dbf" "N(5)N(15,2)" : k ~ Period, (sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k] * XForestLand[g,i,j]) ~ H_Area; + +# xbase (dbf) read +set S, dimen 2; +table tab2 IN "xBASE" "HarvestArea1.dbf": S <- [Period, H_Area]; +display S; + + + + +printf '\n'; +printf 'Constraint\n'; +printf 'Harvest Period\n'; +printf 'Type AgeClass PrescMgt Period Value\n'; +printf{g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR, k in 1..K_PERIOD}:'%5s %11s %11s %5s %10.2f\n', g,i,j, k, (Yield_Table_Vol[g,i,j,k] * XForestLand[g,i,j]); + + +printf 'Even_Flow_Constaint_Alpha (1-Alpha)\n'; +printf 'Period Sign \n'; +for {k in 6..K_PERIOD-1} { + printf "%s %10.2f <= %s\n", k, ((1 - Alpha) * sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k] * XForestLand[g,i,j] - sum {g in G_STAND_TYPE,i in I_CULTURAL_PRES, j in J_MGT_YEAR} Yield_Table_Vol[g,i,j,k+1] * XForestLand[g,i,j]),0; + } +printf '\n'; + + +# Forest / Land Constraints +printf '\n'; +printf 'Total Area Constraint\n'; +printf 'Type AgeClass PrescMgt Value Sign Total_Area \n'; +printf '%5s <= %.3f\n',sum {g in G_STAND_TYPE, i in I_CULTURAL_PRES, j in J_MGT_YEAR} XForestLand[g,i,j], Total_Area; + +printf 'Area\n'; +printf 'Area Value Sign Areas_Stand\n'; +for {g in G_STAND_TYPE} { + printf '%5s %10.2f <= %.3f\n', g, sum {i in I_CULTURAL_PRES,j in J_MGT_YEAR} XForestLand[g,i,j], Area_Stand_Type[g]; + } + + +#DATA SECTION + +data; + +# Most of the data has been moved to dbf format + +param MGT:=31; + +param K_PERIOD:= 7; + +param Alpha:= 0.20; +param Beta:= 0.20; + +param Harvest_Min_Vol_Period:= 12000; + +end; + diff -r d59bea55db9b -r c445c931472f examples/dbf/Forest_Cost.dbf Binary file examples/dbf/Forest_Cost.dbf has changed diff -r d59bea55db9b -r c445c931472f examples/dbf/NetRev_Table.dbf Binary file examples/dbf/NetRev_Table.dbf has changed diff -r d59bea55db9b -r c445c931472f examples/dbf/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/dbf/README Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,2 @@ +This subdirectory contains an example MathProg model that demonstrates +using data tables in DBF format. diff -r d59bea55db9b -r c445c931472f examples/dbf/TCost_Table.dbf Binary file examples/dbf/TCost_Table.dbf has changed diff -r d59bea55db9b -r c445c931472f examples/dbf/Yield_Table_Vol.dbf Binary file examples/dbf/Yield_Table_Vol.dbf has changed diff -r d59bea55db9b -r c445c931472f examples/dbf/cultural_pres.dbf Binary file examples/dbf/cultural_pres.dbf has changed diff -r d59bea55db9b -r c445c931472f examples/dbf/mgt_year.dbf Binary file examples/dbf/mgt_year.dbf has changed diff -r d59bea55db9b -r c445c931472f examples/dbf/stands.dbf Binary file examples/dbf/stands.dbf has changed diff -r d59bea55db9b -r c445c931472f examples/dbf/standtype.dbf Binary file examples/dbf/standtype.dbf has changed diff -r d59bea55db9b -r c445c931472f examples/dea.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/dea.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,222 @@ +/* Data Envelopment Analysis (DEA) + * + * DEA quantifies the relative efficiency of decision making units (DMUs) by + * finding the efficient frontier in multiple input multiple output data. The + * inputs are resources (eg. number of employees, available machines, ...), + * the outputs are productive outputs (eg. contracts made, total sales, ...). + * The method is non-parametric. More details are available in the paper + * below. + * + * Models according to: Seiford, Threall, "Recent developments in DEA", 1990. + * + * Implementation: Sebastian Nowozin + */ + +### SETS ### + +set dmus; # Decision Making Units (DMU) +set inputs; # Input parameters +set outputs; # Output parameters + + +### PARAMETERS ### + +param input_data{dmus,inputs} >= 0; +param output_data{dmus,outputs} >= 0; + + +### PROGRAM ### + +var theta{dmus} >= 0; +var lambda{dmus,dmus} >= 0; + +minimize inefficiency: sum{td in dmus} theta[td]; + +s.t. output_lower_limit{o in outputs, td in dmus}: + sum{d in dmus} lambda[d,td]*output_data[d,o] >= output_data[td,o]; +s.t. input_upper_limit{i in inputs, td in dmus}: + sum{d in dmus} lambda[d,td]*input_data[d,i] <= theta[td]*input_data[td,i]; + + s.t. PI1{td in dmus}: + sum{d in dmus} lambda[d,td] = 1; +/* +possibilities: + i) (no constraint) + ii) s.t. PI1{td in dmus}: + sum{d in dmus} lambda[d,td] <= 1; + iii) s.t. PI1{td in dmus}: + sum{d in dmus} lambda[d,td] >= 1; +*/ + + +### SOLVE AND PRINT SOLUTION ### + +solve; + +printf "DMU\tEfficiency\n"; +for {td in dmus} { + printf "%s\t%1.4f\n", td, theta[td]; +} + +### DATA ### + +data; + +set dmus := 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 + 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 + 61 62 63 64 65 66 67 68 69 ; +set inputs := AvgInventory LaborCost OperatingCost Population ; +set outputs := PrescrVol kDollarValue ; + +param input_data default 0.0 : + + AvgInventory LaborCost OperatingCost Population := + +1 8000 17030 1280 1410 +2 9000 25890 2779 1523 +3 13694 29076 2372 1354 +4 4250 17506 1385 822 +5 6500 23208 639 746 +6 7000 12946 802 1281 +7 4500 18001 1130 1016 +8 5000 14473 1097 1070 +9 27000 31760 5559 1694 +10 21560 50972 15010 1910 +11 15000 39523 4799 1745 +12 8500 13076 3489 1353 +13 35000 35427 1704 500 +14 18000 27554 2882 1016 +15 59750 53848 14208 2500 +16 19200 38253 1480 2293 +17 40000 109404 83016 2718 +18 8466 18198 1278 2877 +19 16000 40891 7599 4150 +20 10000 45444 5556 4421 +21 25000 35623 2121 3883 +22 14000 20192 5515 3519 +23 12500 34973 10475 32366 +24 17260 32284 14498 3393 +25 7000 17920 7585 4489 +26 14000 42094 3742 2217 +27 16400 35422 14236 4641 +28 13000 19100 3529 5968 +29 30000 72167 8656 8715 +30 12530 19970 1714 5968 +31 31500 39183 4919 5607 +32 10000 32048 3483 7324 +33 22000 68877 12279 8685 +34 10000 29812 3332 8685 +35 16000 47686 2507 5420 +36 10000 33415 4738 7703 +37 9000 12359 4603 4665 +38 16439 23614 2989 6317 +39 14500 36069 1793 31839 +40 39000 76307 9539 15619 +41 24927 40706 12661 30213 +42 13858 39267 4609 34719 +43 33375 29509 11323 31839 +44 29044 44482 5542 34719 +45 32257 61365 20550 32366 +46 8800 49671 3306 43561 +47 47000 40425 10396 31263 +48 12000 33034 4915 31263 +49 28000 69163 4688 15173 +50 13300 28931 16735 73064 +51 13500 29758 4260 62309 +52 24000 40927 8285 23166 +53 16000 40403 2131 99836 +54 17000 38730 2539 60348 +55 25000 35978 2502 99836 +56 16000 37509 6278 99836 +57 20000 46950 10715 85925 +58 14000 35966 3144 85925 +59 22000 68318 8015 108987 +60 21879 69537 7778 108987 +61 15000 25425 2812 201404 +62 10000 19508 2454 201404 +63 20000 28191 3367 201404 +64 18000 37073 8624 108987 +65 19051 23763 3496 201404 +66 15000 28642 3366 201404 +67 10000 35919 3868 201404 +68 24000 54653 26494 108987 +69 1800 6276 3413 60348 + ; + +param output_data default 0.0 : + + PrescrVol kDollarValue := + +1 12293 61.00 +2 18400 92.00 +3 16789 92.65 +4 10700 45.00 +5 9800 50.00 +6 6500 29.00 +7 8200 56.00 +8 8680 45.00 +9 33800 183.00 +10 23710 156.00 +11 24000 120.00 +12 17500 75.00 +13 25000 130.00 +14 26000 122.00 +15 26830 178.513 +16 16600 106.00 +17 90000 450.00 +18 11140 73.624 +19 25868 136.00 +20 32700 191.295 +21 29117 152.864 +22 18000 100.00 +23 11100 60.00 +24 23030 137.778 +25 10656 58.00 +26 24682 152.095 +27 26908 120.00 +28 16464 80.00 +29 57000 321.00 +30 17532 94.747 +31 30035 168.00 +32 16000 100.00 +33 63700 277.00 +34 18000 90.00 +35 27339 139.134 +36 19500 116.00 +37 13000 80.00 +38 15370 102.00 +39 18446 90.00 +40 56000 260.00 +41 73845 364.951 +42 28600 145.00 +43 27000 243.00 +44 52423 279.816 +45 73759 363.388 +46 20500 80.00 +47 27100 115.00 +48 15000 110.00 +49 50895 277.852 +50 19707 128.00 +51 17994 78.80 +52 36135 167.222 +53 30000 153.00 +54 26195 125.00 +55 28000 216.00 +56 24658 152.551 +57 36850 190.00 +58 29250 183.69 +59 50000 250.00 +60 40078 265.443 +61 20200 110.00 +62 12500 75.00 +63 30890 195.00 +64 31000 175.00 +65 31277 192.992 +66 11500 75.00 +67 30000 175.668 +68 38383 190.00 +69 2075 8.650 + ; + +end; diff -r d59bea55db9b -r c445c931472f examples/diet.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/diet.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,99 @@ +# STIGLER'S NUTRITION MODEL +# +# This model determines a least cost diet which meets the daily +# allowances of nutrients for a moderately active man weighing 154 lbs. +# +# References: +# Dantzig G B, "Linear Programming and Extensions." +# Princeton University Press, Princeton, New Jersey, 1963, +# Chapter 27-1. + +set N; +/* nutrients */ + +set F; +/* foods */ + +param b{N}; +/* required daily allowances of nutrients */ + +param a{F,N}; +/* nutritive value of foods (per dollar spent) */ + +var x{f in F} >= 0; +/* dollars of food f to be purchased daily */ + +s.t. nb{n in N}: sum{f in F} a[f,n] * x[f] = b[n]; +/* nutrient balance (units) */ + +minimize cost: sum{f in F} x[f]; +/* total food bill (dollars) */ + +data; + +param : N : b := + Calorie 3 /* thousands */ + Protein 70 /* grams */ + Calcium 0.8 /* grams */ + Iron 12 /* milligrams */ + Vitamin-A 5 /* thousands IUs */ + Vitamin-B1 1.8 /* milligrams */ + Vitamin-B2 2.7 /* milligrams */ + Niacin 18 /* milligrams */ + Vitamin-C 75 /* milligrams */ ; + +set F := Wheat Cornmeal Cannedmilk Margarine Cheese Peanut-B Lard + Liver Porkroast Salmon Greenbeans Cabbage Onions Potatoes + Spinach Sweet-Pot Peaches Prunes Limabeans Navybeans; + +param a default 0 + +: Calorie Protein Calcium Iron Vitamin-A Vitamin-B1 := +# (1000) (g) (g) (mg) (1000IU) (mg) + +Wheat 44.7 1411 2.0 365 . 55.4 +Cornmeal 36 897 1.7 99 30.9 17.4 +Cannedmilk 8.4 422 15.1 9 26 3 +Margarine 20.6 17 .6 6 55.8 .2 +Cheese 7.4 448 16.4 19 28.1 .8 +Peanut-B 15.7 661 1 48 . 9.6 +Lard 41.7 . . . .2 . +Liver 2.2 333 .2 139 169.2 6.4 +Porkroast 4.4 249 .3 37 . 18.2 +Salmon 5.8 705 6.8 45 3.5 1 +Greenbeans 2.4 138 3.7 80 69 4.3 +Cabbage 2.6 125 4 36 7.2 9 +Onions 5.8 166 3.8 59 16.6 4.7 +Potatoes 14.3 336 1.8 118 6.7 29.4 +Spinach 1.1 106 . 138 918.4 5.7 +Sweet-Pot 9.6 138 2.7 54 290.7 8.4 +Peaches 8.5 87 1.7 173 86.8 1.2 +Prunes 12.8 99 2.5 154 85.7 3.9 +Limabeans 17.4 1055 3.7 459 5.1 26.9 +Navybeans 26.9 1691 11.4 792 . 38.4 + +: Vitamin-B2 Niacin Vitamin-C := +# (mg) (mg) (mg) + +Wheat 33.3 441 . +Cornmeal 7.9 106 . +Cannedmilk 23.5 11 60 +Margarine . . . +Cheese 10.3 4 . +Peanut-B 8.1 471 . +Lard .5 5 . +Liver 50.8 316 525 +Porkroast 3.6 79 . +Salmon 4.9 209 . +Greenbeans 5.8 37 862 +Cabbage 4.5 26 5369 +Onions 5.9 21 1184 +Potatoes 7.1 198 2522 +Spinach 13.8 33 2755 +Sweet-Pot 5.4 83 1912 +Peaches 4.3 55 57 +Prunes 4.3 65 257 +Limabeans 38.2 93 . +Navybeans 24.6 217 . ; + +end; diff -r d59bea55db9b -r c445c931472f examples/dist.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/dist.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,565 @@ +# DIST, a product distribution model +# +# References: +# Robert Fourer, David M. Gay and Brian W. Kernighan, "A Modeling Language +# for Mathematical Programming." Management Science 36 (1990) 519-554. + +### SHIPPING SETS AND PARAMETERS ### + +set whse 'warehouses'; # Locations from which demand is satisfied + +set dctr 'distribution centers' within whse; + + # Locations from which product may be shipped + +param sc 'shipping cost' {dctr,whse} >= 0; + + # Shipping costs, to whse from dctr, in $ / 100 lb + +param huge 'largest shipping cost' > 0; + + # Largest cost allowed for a usable shipping route + +param msr 'minimum size restriction' {dctr,whse} logical; + + # True indicates a minimum-size restriction on + # direct shipments using this dctr --> whse route + +param dsr 'direct shipment requirement' {dctr} >= 0; + + # Minimum total demand, in pallets, needed to + # allow shipment on routes subject to the + # minimum size restriction + +### PLANT SETS AND PARAMETERS ### + +set fact 'factories' within dctr; + + # Locations where product is manufactured + +param rtmin 'regular-time total minimum' >= 0; + + # Lower limit on (average) total regular-time + # crews employed at all factories + +param rtmax 'regular-time total maximum' >= rtmin; + + # Upper limit on (average) total regular-time + # crews employed at all factories + +param otmin 'overtime total minimum' >= 0; + + # Lower limit on total overtime hours at all factories + +param otmax 'overtime total maximum' >= otmin; + + # Upper limit on total overtime hours at all factories + +param rmin 'regular-time minimums' {fact} >= 0; + + # Lower limits on (average) regular-time crews + +param rmax 'regular-time maximums' {f in fact} >= rmin[f]; + + # Upper limits on (average) regular-time crews + +param omin 'overtime minimums' {fact} >= 0; + + # Lower limits on overtime hours + +param omax 'overtime maximums' {f in fact} >= omin[f]; + + # Upper limits on overtime hours + +param hd 'hours per day' {fact} >= 0; + + # Regular-time hours per working day + +param dp 'days in period' {fact} > 0; + + # Working days in the current planning period + +### PRODUCT SETS AND PARAMETERS ### + +set prd 'products'; # Elements of the product group + +param wt 'weight' {prd} > 0; + + # Weight in 100 lb / 1000 cases + +param cpp 'cases per pallet' {prd} > 0; + + # Cases of product per shipping pallet + +param tc 'transshipment cost' {prd} >= 0; + + # Transshipment cost in $ / 1000 cases + +param pt 'production time' {prd,fact} >= 0; + + # Crew-hours to produce 1000 cases + +param rpc 'regular-time production cost' {prd,fact} >= 0; + + # Cost of production on regular time, + # in $ / 1000 cases + +param opc 'overtime production cost' {prd,fact} >= 0; + + # Cost of production on overtime, in $ / 1000 cases + +### DEMAND SETS AND PARAMETERS ### + +param dt 'total demand' {prd} >= 0; + + # Total demands for products, in 1000s + +param ds 'demand shares' {prd,whse} >= 0.0, <= 1.0; + + # Historical demand data, from which each + # warehouse's share of total demand is deduced + +param dstot {p in prd} := sum {w in whse} ds[p,w]; + + # Total of demand shares; should be 1, but often isn't + +param dem 'demand' {p in prd, w in whse} := dt[p] * ds[p,w] / dstot[p]; + + # Projected demands to be satisfied, in 1000s + +set rt 'shipping routes available' := + + {d in dctr, w in whse: + d <> w and sc[d,w] < huge and + (w in dctr or sum {p in prd} dem[p,w] > 0) and + not (msr[d,w] and sum {p in prd} 1000*dem[p,w]/cpp[p] < dsr[d]) }; + + # List of ordered pairs that represent routes + # on which shipments are allowed + +### VARIABLES ### + +var Rprd 'regular-time production' {prd,fact} >= 0; + + # Regular-time production of each product + # at each factory, in 1000s of cases + +var Oprd 'overtime production' {prd,fact} >= 0; + + # Overtime production of each product + # at each factory, in 1000s of cases + +var Ship 'shipments' {prd,rt} >= 0; + + # Shipments of each product on each allowed route, + # in 1000s of cases + +var Trans 'transshipments' {prd,dctr} >= 0; + + # Transshipments of each product at each + # distribution center, in 1000s of cases + +### OBJECTIVE ### + +minimize cost: sum {p in prd, f in fact} rpc[p,f] * Rprd[p,f] + + sum {p in prd, f in fact} opc[p,f] * Oprd[p,f] + + sum {p in prd, (d,w) in rt} sc[d,w] * wt[p] * Ship[p,d,w] + + sum {p in prd, d in dctr} tc[p] * Trans[p,d]; + + # Total cost: regular production, overtime + # production, shipping, and transshipment + +### CONSTRAINTS ### + +rtlim 'regular-time total limits': + + rtmin <= sum {p in prd, f in fact} + (pt[p,f] * Rprd[p,f]) / (dp[f] * hd[f]) <= rtmax; + + # Total crews must lie between limits + +otlim 'overtime total limits': + + otmin <= sum {p in prd, f in fact} pt[p,f] * Oprd[p,f] <= otmax; + + # Total overtime must lie between limits + +rlim 'regular-time limits' {f in fact}: + + rmin[f] <= sum {p in prd} + (pt[p,f] * Rprd[p,f]) / (dp[f] * hd[f]) <= rmax[f]; + + # Crews at each factory must lie between limits + +olim 'overtime limits' {f in fact}: + + omin[f] <= sum {p in prd} pt[p,f] * Oprd[p,f] <= omax[f]; + + # Overtime at each factory must lie between limits + +noRprd 'no regular production' {p in prd, f in fact: rpc[p,f] = 0}: + + Rprd[p,f] = 0; + +noOprd 'no overtime production' {p in prd, f in fact: opc[p,f] = 0}: + + Oprd[p,f] = 0; # Do not produce where specified cost is zero + +bal 'material balance' {p in prd, w in whse}: + + sum {(v,w) in rt} + Ship [p,v,w] + (if w in fact then Rprd[p,w] + Oprd[p,w]) = + + dem[p,w] + (if w in dctr then sum {(w,v) in rt} Ship[p,w,v]); + + # Demand is satisfied by shipment into warehouse + # plus production (if it is a factory) + # minus shipment out (if it is a distn. center) + +trdef 'transshipment definition' {p in prd, d in dctr}: + + Trans[p,d] >= sum {(d,w) in rt} Ship [p,d,w] - + (if d in fact then Rprd[p,d] + Oprd[p,d]); + + # Transshipment at a distribution center is + # shipments out less production (if any) + +### DATA -- 3 PRODUCTS ### + +data; + +set prd := 18REG 24REG 24PRO ; + +set whse := w01 w02 w03 w04 w05 w06 w08 w09 w12 w14 w15 w17 + w18 w19 w20 w21 w24 w25 w26 w27 w28 w29 w30 w31 + w32 w33 w34 w35 w36 w37 w38 w39 w40 w41 w42 w43 + w44 w45 w46 w47 w48 w49 w50 w51 w53 w54 w55 w56 + w57 w59 w60 w61 w62 w63 w64 w65 w66 w68 w69 w71 + w72 w73 w74 w75 w76 w77 w78 w79 w80 w81 w82 w83 + w84 w85 w86 w87 w89 w90 w91 w92 w93 w94 w95 w96 + w98 x22 x23 ; + +set dctr := w01 w02 w03 w04 w05 w62 w76 w96 ; + +set fact := w01 w05 w96 ; + +param huge := 99. ; + +param rtmin := 0.0 ; +param rtmax := 8.0 ; + +param otmin := 0.0 ; +param otmax := 96.0 ; + +param rmin := w01 0.00 w05 0.00 w96 0.00 ; +param rmax := w01 3.00 w05 2.00 w96 3.00 ; + +param omin := w01 0.0 w05 0.0 w96 0.0 ; +param omax := w01 48.0 w05 0.0 w96 48.0 ; + +param hd := w01 8.0 w05 8.0 w96 8.0 ; + +param dp := w01 19.0 w05 19.0 w96 19.0 ; + +param wt := 18REG 47.3 24REG 63.0 24PRO 63.0 ; + +param tc := 18REG 40.00 24REG 45.00 24PRO 45.00 ; + +param dt := 18REG 376.0 24REG 172.4 24PRO 316.3 ; + +param cpp := 18REG 102. 24REG 91. 24PRO 91. ; + +param dsr := w01 96. w02 96. w03 96. w04 96. w05 96. + w62 96. w76 96. w96 96. ; + +param pt (tr) : + + 18REG 24REG 24PRO := + +w01 1.194 1.429 1.429 +w05 1.194 1.509 1.509 +w96 0.000 1.600 1.600 ; + +param rpc (tr) : + + 18REG 24REG 24PRO := + +w01 2119. 2653. 2617. +w05 2489. 3182. 3176. +w96 0. 2925. 2918. ; + +param opc (tr) : + + 18REG 24REG 24PRO := + +w01 2903. 3585. 3579. +w05 0. 0. 0. +w96 0. 3629. 3622. ; + +param sc default 99.99 (tr) : + + w01 w02 w03 w04 w05 w62 w76 w96 := + +w01 . 2.97 1.14 2.08 2.37 1.26 2.42 1.43 +w02 4.74 . 4.17 6.12 7.41 3.78 7.04 5.21 +w03 2.45 4.74 . 3.67 2.84 0.90 2.41 2.55 +w04 1.74 5.03 2.43 . 3.19 2.45 2.69 0.58 +w05 2.70 5.16 2.84 2.85 . 3.26 3.34 2.71 +w06 1.99 4.17 2.13 2.19 2.52 2.06 2.00 1.51 +w08 0.21 2.92 1.24 2.07 2.29 1.25 2.32 1.55 +w09 0.66 3.76 1.41 2.47 1.82 1.66 . 1.87 +w12 1.38 3.83 1.68 2.53 2.39 . 1.96 1.94 +w14 2.47 1.58 2.40 3.59 3.85 2.25 . 3.05 +w15 1.06 4.95 2.48 1.39 3.41 1.96 . 1.02 +w17 0.88 3.39 1.46 2.00 2.67 1.45 . 1.46 +w18 7.90 6.57 7.79 9.59 10.81 . . 6.70 +w19 1.42 4.12 1.96 1.99 3.52 1.88 . 1.26 +w20 3.03 1.59 2.34 4.76 3.98 1.88 . 3.73 +w24 1.58 2.80 2.27 2.87 3.19 1.31 . 2.05 +w25 1.51 5.05 2.74 0.57 2.98 . 2.95 0.27 +w26 1.75 3.61 2.70 1.54 4.07 3.52 . 1.03 +w27 2.48 6.87 3.17 1.59 2.08 3.45 . 0.99 +w28 2.05 6.83 2.97 1.13 2.91 . . 1.26 +w29 4.03 3.68 4.46 3.20 5.50 . . 3.20 +w30 2.48 5.78 2.99 2.24 1.79 3.10 . 1.39 +w31 2.34 5.41 2.87 1.67 1.66 . . 1.39 +w32 14.36 . . . . . . . +w33 3.87 4.27 5.11 3.48 5.66 4.03 . 3.05 +w34 3.26 4.80 3.21 2.70 4.14 . . 1.77 +w35 2.34 2.84 2.89 3.35 3.78 2.68 . 2.52 +w36 2.43 5.69 2.96 2.95 1.02 2.61 1.07 2.54 +w37 2.23 4.64 2.41 1.99 4.30 2.61 . 1.44 +w38 4.66 4.36 5.23 3.04 4.46 . . 3.82 +w39 1.11 3.51 1.10 2.53 3.07 1.12 . 2.23 +w40 2.99 4.78 4.23 1.57 3.92 . . 1.80 +w41 4.93 4.00 5.43 4.45 6.31 . . 3.81 +w42 3.86 6.55 5.03 2.11 4.41 . . 2.63 +w43 4.61 4.45 3.77 1.22 4.31 . . 2.35 +w44 2.05 4.48 1.06 3.70 3.46 1.10 . 3.21 +w45 0.92 3.42 1.58 3.04 1.82 1.94 . 2.52 +w46 1.36 2.44 0.95 3.08 2.78 0.39 2.16 2.37 +w47 1.30 3.39 1.60 2.49 4.29 2.04 . 1.68 +w48 1.65 3.78 1.03 2.97 2.21 1.31 . 2.74 +w49 1.96 3.00 1.50 3.24 3.68 1.00 . 2.99 +w50 0.90 4.14 1.60 1.95 3.61 1.61 . 1.52 +w51 1.59 3.95 0.25 2.96 2.58 1.00 2.41 2.71 +w53 1.59 3.79 1.28 3.12 3.10 0.89 . 2.98 +w54 1.72 4.36 1.61 2.92 2.34 1.91 1.97 3.05 +w55 2.45 2.73 2.21 4.47 4.30 2.57 . 4.48 +w56 1.10 3.73 1.59 2.74 2.33 1.45 . 2.44 +w57 0.95 3.39 1.37 2.30 2.47 1.15 . 1.95 +w59 3.29 5.35 3.32 3.81 1.52 3.38 1.34 4.08 +w60 2.41 6.12 2.46 3.65 2.35 . 1.37 4.06 +w61 3.32 5.50 3.41 3.38 1.23 . 0.99 4.28 +w62 1.12 3.00 0.82 3.22 2.95 . 3.33 2.53 +w63 3.59 6.36 3.25 4.12 1.84 3.59 1.46 4.03 +w64 1.85 4.45 2.17 3.43 2.13 2.03 . 4.02 +w65 2.78 4.79 2.81 2.94 1.54 2.90 1.07 2.94 +w66 3.90 5.79 3.05 3.65 1.36 3.39 1.22 3.57 +w68 2.61 5.20 2.90 2.34 1.68 3.19 1.48 2.31 +w69 2.94 5.21 2.78 3.43 0.21 3.26 0.68 2.54 +w71 2.06 4.98 2.38 2.44 1.59 2.97 1.05 2.55 +w72 2.61 5.50 2.83 3.12 1.35 3.23 0.88 2.99 +w73 8.52 6.16 8.03 8.83 10.44 7.38 10.26 . +w74 6.11 5.46 9.07 9.38 10.80 . . 8.25 +w75 2.66 4.94 2.87 3.69 1.52 3.15 1.24 4.00 +w76 1.99 5.26 2.23 3.36 0.58 3.17 . 2.50 +w77 4.32 3.07 5.05 3.88 6.04 . . 4.15 +w78 5.60 2.59 5.78 5.56 7.10 . . 5.60 +w79 4.25 2.32 4.93 4.57 6.04 . . 4.58 +w80 5.94 4.00 5.60 7.02 9.46 . . 7.51 +w81 5.39 2.21 5.10 6.22 6.46 . . 6.58 +w82 8.80 5.69 9.29 9.88 11.69 8.63 11.52 . +w83 4.40 . 5.24 5.21 5.81 3.91 7.04 5.33 +w84 5.87 5.43 6.17 5.70 7.63 . . 5.70 +w85 3.90 3.65 3.38 4.57 5.64 3.05 . 5.04 +w86 5.48 2.10 5.70 6.37 7.33 . . 6.19 +w87 8.88 5.54 9.50 9.71 11.64 8.85 11.68 . +w89 4.62 4.01 4.03 6.30 6.30 3.81 . 7.77 +w90 4.35 2.72 4.61 4.01 5.60 . . 3.20 +w91 7.61 4.42 7.83 6.85 8.79 . . 7.66 +w92 7.15 2.69 6.91 7.20 . . . 7.06 +w93 3.17 3.95 4.37 3.74 5.05 . . 2.40 +w94 1.21 3.07 0.90 2.74 3.17 . 2.63 2.39 +w95 5.82 3.29 6.55 7.06 11.47 . . 7.83 +w96 1.77 5.20 2.72 0.59 3.47 2.48 . . +w98 3.04 1.92 3.64 3.70 4.90 3.05 . 3.88 +x22 4.08 6.25 4.15 4.30 1.77 . 1.77 . +x23 3.39 5.74 3.55 4.08 1.69 . 1.47 . ; + +param msr (tr) : + + w01 w02 w03 w04 w05 w62 w76 w96 := + +w01 0 0 0 0 0 0 1 0 +w02 0 0 0 0 0 0 1 0 +w03 0 0 0 0 0 0 1 0 +w04 0 0 0 0 0 0 1 0 +w05 0 0 0 0 0 0 0 0 +w06 0 1 1 1 1 1 1 1 +w08 0 1 1 1 1 1 1 1 +w09 0 1 1 1 1 1 0 1 +w12 0 1 1 1 1 0 1 1 +w14 1 1 1 1 1 0 0 1 +w15 0 1 1 1 1 1 0 1 +w17 0 1 1 1 1 1 0 1 +w18 0 1 1 1 1 0 0 1 +w19 0 1 1 1 1 0 0 1 +w20 1 1 1 1 1 0 0 1 +w24 0 1 1 1 1 0 0 1 +w25 0 1 1 1 1 0 1 0 +w26 1 1 1 0 1 1 0 1 +w27 1 1 1 0 1 1 0 1 +w28 1 1 1 0 1 0 0 1 +w29 0 1 1 1 1 0 0 1 +w30 1 1 1 0 1 1 0 1 +w31 1 1 1 0 1 0 0 1 +w32 0 0 0 0 0 0 0 0 +w33 1 0 1 1 1 1 0 1 +w34 1 1 1 0 1 0 0 1 +w35 1 1 1 1 1 0 0 1 +w36 0 1 1 1 0 1 1 1 +w37 1 1 1 0 1 1 0 1 +w38 1 1 1 0 1 0 0 1 +w39 0 1 1 1 1 1 0 1 +w40 1 1 1 0 1 0 0 1 +w41 1 0 1 1 1 0 0 1 +w42 1 1 1 0 1 0 0 1 +w43 1 1 1 0 1 0 0 1 +w44 1 1 1 1 1 0 0 1 +w45 0 1 1 1 1 1 0 1 +w46 0 1 1 1 1 0 1 1 +w47 0 1 1 1 1 1 0 1 +w48 0 1 1 1 1 0 0 1 +w49 1 1 1 1 1 0 0 1 +w50 0 1 1 1 1 1 0 1 +w51 0 1 1 1 1 0 1 1 +w53 1 1 1 1 1 0 0 1 +w54 0 1 1 1 1 1 1 1 +w55 0 1 1 1 1 0 0 1 +w56 0 1 1 1 1 1 0 1 +w57 0 1 1 1 1 1 0 1 +w59 0 1 1 1 0 1 1 1 +w60 0 1 1 1 1 0 1 1 +w61 0 1 1 1 0 0 1 1 +w62 0 0 0 0 0 0 1 0 +w63 0 1 1 1 0 1 1 1 +w64 0 1 1 1 1 1 0 1 +w65 0 1 1 1 0 1 1 1 +w66 0 1 1 1 0 1 1 1 +w68 0 1 1 1 0 1 1 1 +w69 0 1 1 1 0 1 1 1 +w71 0 1 1 1 0 1 1 1 +w72 0 1 1 1 0 1 1 1 +w73 0 1 1 1 0 1 1 0 +w74 0 1 1 1 0 0 0 1 +w75 0 1 1 1 0 1 1 1 +w76 0 0 0 0 0 0 0 0 +w77 1 0 1 1 1 0 0 1 +w78 1 0 1 1 1 0 0 1 +w79 1 0 1 1 1 0 0 1 +w80 1 0 1 1 1 0 0 1 +w81 1 0 1 1 1 0 0 1 +w82 1 0 1 1 1 1 1 0 +w83 1 0 1 1 1 0 1 1 +w84 1 0 1 1 1 0 0 1 +w85 1 1 1 1 1 0 0 1 +w86 1 0 1 1 1 0 0 1 +w87 1 0 1 1 1 1 1 0 +w89 1 0 1 1 1 1 0 1 +w90 0 1 1 1 1 0 0 1 +w91 1 0 1 1 1 0 0 1 +w92 1 0 1 1 1 0 0 1 +w93 1 1 1 0 1 0 0 1 +w94 0 0 1 1 1 0 1 1 +w95 1 0 1 1 1 0 0 1 +w96 0 0 0 0 0 0 0 0 +w98 1 0 1 1 1 1 0 1 +x22 1 1 1 1 0 0 1 0 +x23 1 1 1 1 0 0 1 0 ; + +param ds default 0.000 (tr) : + + 18REG 24REG 24PRO := + +w01 0.000 0.000 0.008 +w02 0.004 0.000 0.000 +w03 0.000 0.000 0.000 +w04 0.010 0.002 0.000 +w05 0.000 0.000 0.000 +w06 0.010 0.008 0.008 +w08 0.030 0.024 0.024 +w09 0.014 0.018 0.020 +w12 0.014 0.012 0.010 +w14 0.007 0.007 0.012 +w15 0.010 0.019 0.018 +w17 0.013 0.010 0.011 +w19 0.015 0.012 0.009 +w20 0.012 0.021 0.022 +w21 0.000 0.000 0.000 +w24 0.012 0.022 0.018 +w25 0.019 0.025 0.020 +w26 0.006 0.015 0.021 +w27 0.008 0.010 0.015 +w28 0.011 0.016 0.019 +w29 0.008 0.020 0.013 +w30 0.011 0.013 0.015 +w31 0.011 0.013 0.017 +w32 0.006 0.000 0.000 +w33 0.000 0.015 0.014 +w34 0.008 0.007 0.005 +w35 0.002 0.006 0.014 +w36 0.015 0.013 0.005 +w37 0.017 0.016 0.015 +w38 0.015 0.009 0.012 +w39 0.007 0.017 0.022 +w40 0.009 0.014 0.020 +w41 0.003 0.014 0.011 +w42 0.017 0.011 0.012 +w43 0.009 0.013 0.011 +w44 0.002 0.012 0.012 +w45 0.016 0.025 0.028 +w46 0.038 0.062 0.040 +w47 0.007 0.010 0.010 +w48 0.003 0.015 0.016 +w49 0.005 0.016 0.017 +w50 0.011 0.008 0.007 +w51 0.010 0.022 0.021 +w53 0.004 0.026 0.020 +w54 0.020 0.017 0.025 +w55 0.004 0.019 0.028 +w56 0.004 0.010 0.008 +w57 0.014 0.020 0.018 +w59 0.012 0.006 0.007 +w60 0.019 0.010 0.009 +w61 0.028 0.010 0.012 +w62 0.000 0.000 0.000 +w63 0.070 0.027 0.037 +w64 0.009 0.004 0.005 +w65 0.022 0.015 0.016 +w66 0.046 0.017 0.020 +w68 0.005 0.012 0.016 +w69 0.085 0.036 0.039 +w71 0.011 0.013 0.010 +w72 0.089 0.031 0.034 +w75 0.026 0.012 0.010 +w77 0.001 0.004 0.002 +w78 0.002 0.004 0.002 +w79 0.001 0.004 0.002 +w80 0.001 0.001 0.002 +w81 0.001 0.003 0.002 +w83 0.009 0.010 0.008 +w84 0.001 0.002 0.002 +w85 0.001 0.004 0.005 +w86 0.001 0.002 0.002 +w87 0.002 0.003 0.000 +w89 0.001 0.001 0.002 +w90 0.006 0.017 0.013 +w91 0.002 0.010 0.013 +w92 0.000 0.003 0.002 +w93 0.002 0.006 0.007 +w95 0.001 0.007 0.007 +w96 0.000 0.000 0.000 +w98 0.006 0.005 0.002 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/dragon.dat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/dragon.dat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,55 @@ +/* dragon.dat, a hard Paint-By-Numbers puzzle */ + +/* To reduce the solution time the clique cuts may be used. */ + +data; + +param m := 20; + +param n := 20; + +param row : 1 2 3 4 5 := + 1 7 1 . . . + 2 1 1 2 . . + 3 2 1 2 . . + 4 1 2 2 . . + 5 4 2 3 . . + 6 3 1 4 . . + 7 3 1 3 . . + 8 2 1 4 . . + 9 2 9 . . . + 10 2 1 5 . . + 11 2 7 . . . + 12 14 . . . . + 13 8 2 . . . + 14 6 2 2 . . + 15 2 8 1 3 . + 16 1 5 5 2 . + 17 1 3 2 4 1 + 18 3 1 2 4 1 + 19 1 1 3 1 3 + 20 2 1 1 2 . ; + +param col : 1 2 3 4 5 := + 1 1 1 1 2 . + 2 3 1 2 1 1 + 3 1 4 2 1 1 + 4 1 3 2 4 . + 5 1 4 6 1 . + 6 1 11 1 . . + 7 5 1 6 2 . + 8 14 . . . . + 9 7 2 . . . + 10 7 2 . . . + 11 6 1 1 . . + 12 9 2 . . . + 13 3 1 1 1 . + 14 3 1 3 . . + 15 2 1 3 . . + 16 2 1 5 . . + 17 3 2 2 . . + 18 3 3 2 . . + 19 2 3 2 . . + 20 2 6 . . . ; + +end; diff -r d59bea55db9b -r c445c931472f examples/egypt.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/egypt.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,519 @@ +# EGYPT, a static model of fertilizer production +# +# References: +# Robert Fourer, David M. Gay and Brian W. Kernighan, "A Modeling Language +# for Mathematical Programming." Management Science 36 (1990) 519-554. + +### SETS ### + +set center; # Locations from which final product may be shipped +set port within center; # Locations at which imports can be received +set plant within center; # Locations of plants + +set region; # Demand regions + +set unit; # Productive units +set proc; # Processes + +set nutr; # Nutrients + +set c_final; # Final products (fertilizers) +set c_inter; # Intermediate products +set c_ship within c_inter; # Intermediates for shipment +set c_raw; # Domestic raw materials and miscellaneous inputs + +set commod := c_final union c_inter union c_raw; + + # All commodities + +### PARAMETERS ### + +param cf75 {region,c_final} >= 0; + + # Consumption of fertilizer 1974-75 (1000 tpy) + +param fn {c_final,nutr} >= 0; + + # Nutrient content of fertilizers + +param cn75 {r in region, n in nutr} := sum {c in c_final} cf75[r,c] * fn[c,n]; + + # Consumption of nutrients 1974-75 (1000 tpy) + +param road {region,center} >= 0; + + # Road distances + +param rail_half {plant,plant} >= 0; +param rail {p1 in plant, p2 in plant} := + if rail_half[p1,p2] > 0 then rail_half[p1,p2] else rail_half[p2,p1]; + + # Interplant rail distances (kms) + +param impd_barg {plant} >= 0; +param impd_road {plant} >= 0; + + # Import distances (kms) by barge and road + +param tran_final {pl in plant, r in region} := + if road[r,pl] > 0 then .5 + .0144 * road[r,pl] else 0; + +param tran_import {r in region, po in port} := + if road[r,po] > 0 then .5 + .0144 * road[r,po] else 0; + +param tran_inter {p1 in plant, p2 in plant} := + if rail[p1,p2] > 0 then 3.5 + .03 * rail[p1,p2] else 0; + +param tran_raw {pl in plant} := + (if impd_barg[pl] > 0 then 1.0 + .0030 * impd_barg[pl] else 0) + + (if impd_road[pl] > 0 then 0.5 + .0144 * impd_road[pl] else 0); + + # Transport cost (le per ton) for: + # final products, imported final products, + # interplant shipment, imported raw materials + +param io {commod,proc}; # Input-output coefficients + +param util {unit,proc} >= 0; + + # Capacity utilization coefficients + +param p_imp {commod} >= 0; # Import Price (cif US$ per ton 1975) + +param p_r {c_raw} >= 0; +param p_pr {plant,c_raw} >= 0; + +param p_dom {pl in plant, c in c_raw} := + if p_r[c] > 0 then p_r[c] else p_pr[pl,c]; + + # Domestic raw material prices + +param dcap {plant,unit} >= 0; + + # Design capacity of plants (t/day) + +param icap {u in unit, pl in plant} := 0.33 * dcap[pl,u]; + + # Initial capacity of plants (t/day) + +param exch := 0.4; # Exchange rate + +param util_pct := 0.85; # Utilization percent for initial capacity + +### DERIVED SETS OF "POSSIBILITIES" ### + +set m_pos {pl in plant} := {u in unit: icap[u,pl] > 0}; + + # At each plant, set of units for which there is + # initial capacity + +set p_cap {pl in plant} := + {pr in proc: forall {u in unit: util[u,pr] > 0} u in m_pos[pl] }; + + # At each plant, set of processes for which + # all necessary units have some initial capacity + +set p_except {plant} within proc; + + # At each plant, list of processes that are + # arbitrarily ruled out + +set p_pos {pl in plant} := p_cap[pl] diff p_except[pl]; + + # At each plant, set of possible processes + +set cp_pos {c in commod} := {pl in plant: sum {pr in p_pos[pl]} io[c,pr] > 0}; + +set cc_pos {c in commod} := {pl in plant: sum {pr in p_pos[pl]} io[c,pr] < 0}; + +set c_pos {c in commod} := cp_pos[c] union cc_pos[c]; + + # For each commodity, set of plants that can + # produce it (cp_pos) or consume it (cc_pos), + # and their union (c_pos) + +### VARIABLES ### + +var Z {pl in plant, p_pos[pl]} >= 0; + + # Z[pl,pr] is level of process pr at plant pl + +var Xf {c in c_final, cp_pos[c], region} >= 0; + + # Xf[c,pl,r] is amount of final product c + # shipped from plant pl to region r + +var Xi {c in c_ship, cp_pos[c], cc_pos[c]} >= 0; + + # Xi[c,p1,p2] is amount of intermediate c + # shipped from plant p1 to plant p2 + +var Vf {c_final,region,port} >= 0; + + # Vf[c,r,po] is amount of final product c + # imported by region r from port po + +var Vr {c in c_raw, cc_pos[c]} >= 0; + + # Vr[c,pl] is amount of raw material c + # imported for use at plant pl + +var U {c in c_raw, cc_pos[c]} >= 0; + + # U[c,pl] is amount of raw material c + # purchased domestically for use at plant pl + +var Psip; # Domestic recurrent cost +var Psil; # Transport cost +var Psii; # Import cost + +### OBJECTIVE ### + +minimize Psi: Psip + Psil + Psii; + +### CONSTRAINTS ### + +subject to mbd {n in nutr, r in region}: + + sum {c in c_final} fn[c,n] * + (sum {po in port} Vf[c,r,po] + + sum {pl in cp_pos[c]} Xf[c,pl,r]) >= cn75[r,n]; + + # Total nutrients supplied to a region by all + # final products (sum of imports plus internal + # shipments from plants) must meet requirements + +subject to mbdb {c in c_final, r in region: cf75[r,c] > 0}: + + sum {po in port} Vf[c,r,po] + + sum {pl in cp_pos[c]} Xf[c,pl,r] >= cf75[r,c]; + + # Total of each final product supplied to each + # region (as in previous constraint) must meet + # requirements + +subject to mb {c in commod, pl in plant}: + + sum {pr in p_pos[pl]} io[c,pr] * Z[pl,pr] + + + ( if c in c_ship then + ( if pl in cp_pos[c] then sum {p2 in cc_pos[c]} Xi[c,pl,p2] ) + - ( if pl in cc_pos[c] then sum {p2 in cp_pos[c]} Xi[c,p2,pl] )) + + + ( if (c in c_raw and pl in cc_pos[c]) then + (( if p_imp[c] > 0 then Vr[c,pl] ) + + ( if p_dom[pl,c] > 0 then U[c,pl] ))) + + >= if (c in c_final and pl in cp_pos[c]) then sum {r in region} Xf[c,pl,r]; + + # For each commodity at each plant: sum of + # (1) production or consumption at plant, + # (2) inter-plant shipments in or out, + # (3) import and domestic purchases (raw only) + # is >= 0 for raw materials and intermediates; + # is >= the total shipped for final products + +subject to cc {pl in plant, u in m_pos[pl]}: + + sum {pr in p_pos[pl]} util[u,pr] * Z[pl,pr] <= util_pct * icap[u,pl]; + + # For each productive unit at each plant, + # total utilization by all processes + # may not exceed the unit's capacity + +subject to ap: + + Psip = sum {c in c_raw, pl in cc_pos[c]} p_dom[pl,c] * U[c,pl]; + + # Psip is the cost of domestic raw materials, + # summed over all plants that consume them + +subject to al: + + Psil = sum {c in c_final} ( + + sum {pl in cp_pos[c], r in region} + tran_final[pl,r] * Xf[c,pl,r] + + + sum {po in port, r in region} tran_import[r,po] * Vf[c,r,po] ) + + + sum {c in c_ship, p1 in cp_pos[c], p2 in cc_pos[c]} + tran_inter[p1,p2] * Xi[c,p1,p2] + + + sum {c in c_raw, pl in cc_pos[c]: p_imp[c] > 0} + tran_raw[pl] * Vr[c,pl]; + + # Total transport cost is sum of shipping costs for + # (1) all final products from all plants, + # (2) all imports of final products, + # (3) all intermediates shipped between plants, + # (4) all imports of raw materials + +subject to ai: + + Psii / exch = sum {c in c_final, r in region, po in port} + p_imp[c] * Vf[c,r,po] + + + sum {c in c_raw, pl in cc_pos[c]} p_imp[c] * Vr[c,pl]; + + # Total import cost -- at exchange rate -- + # is sum of import costs for final products + # in each region and raw materials at each plant + +### DATA ### + +data; + +set center := ASWAN HELWAN ASSIOUT KAFR_EL_ZT ABU_ZAABAL ABU_KIR TALKHA SUEZ ; + +set port := ABU_KIR ; + +set plant := ASWAN HELWAN ASSIOUT KAFR_EL_ZT ABU_ZAABAL ; + +set region := ALEXANDRIA BEHERA GHARBIA KAFR_EL_SH DAKAHLIA DAMIETTA + SHARKIA ISMAILIA SUEZ MENOUFIA KALUBIA GIZA BENI_SUEF FAYOUM + MINIA ASSIOUT NEW_VALLEY SOHAG QUENA ASWAN ; + +set unit := SULF_A_S SULF_A_P NITR_ACID AMM_ELEC AMM_C_GAS C_AMM_NITR + AMM_SULF SSP ; + +set proc := SULF_A_S SULF_A_P NITR_ACID AMM_ELEC AMM_C_GAS CAN_310 CAN_335 + AMM_SULF SSP_155 ; + +set nutr := N P205 ; + +set c_final := UREA CAN_260 CAN_310 CAN_335 AMM_SULF DAP SSP_155 C_250_55 + C_300_100 ; + +set c_inter := AMMONIA NITR_ACID SULF_ACID ; + +set c_ship := AMMONIA SULF_ACID ; + +set c_raw := EL_ASWAN COKE_GAS PHOS_ROCK LIMESTONE EL_SULFUR PYRITES + ELECTRIC BF_GAS WATER STEAM BAGS ; + +set p_except[ASWAN] := CAN_335 ; +set p_except[HELWAN] := CAN_310 ; +set p_except[ASSIOUT] := ; +set p_except[KAFR_EL_ZT] := ; +set p_except[ABU_ZAABAL] := ; + +param cf75 default 0.0 : + + CAN_260 CAN_310 CAN_335 AMM_SULF UREA := + +ALEXANDRIA . . 5.0 3.0 1.0 +ASSIOUT 1.0 20.0 26.0 1.0 27.0 +ASWAN . 40.0 . . . +BEHERA 1.0 . 25.0 90.0 35.0 +BENI_SUEF 1.0 . 15.0 1.0 20.0 +DAKAHLIA 1.0 . 26.0 60.0 20.0 +DAMIETTA . . 2.0 15.0 8.0 +FAYOUM 1.0 . 20.0 6.0 20.0 +GHARBIA . . 17.0 60.0 28.0 +GIZA . . 40.0 6.0 2.0 +ISMAILIA . . 4.0 6.0 2.0 +KAFR_EL_SH 1.0 . 10.0 45.0 22.0 +KALUBIA . . 25.0 16.0 7.0 +MENOUFIA 1.0 . 24.0 21.0 30.0 +MINIA 2.0 15.0 35.0 1.0 41.0 +NEW_VALLEY . . . . 1.0 +QUENA . 95.0 2.0 . 3.0 +SHARKIA 1.0 . 31.0 50.0 28.0 +SOHAG . 65.0 3.0 . 7.0 +SUEZ . . 1.0 . . + + : SSP_155 C_250_55 C_300_100 DAP := + +ALEXANDRIA 8.0 . . . +ASSIOUT 35.0 5.0 .1 . +ASWAN 8.0 . . . +BEHERA 64.0 1.0 .1 .1 +BENI_SUEF 13.0 3.0 . . +DAKAHLIA 52.0 1.0 . . +DAMIETTA 5.0 . . . +FAYOUM 17.0 1.0 . . +GHARBIA 57.0 1.0 .2 .1 +GIZA 14.0 1.0 .1 . +ISMAILIA 4.0 . . . +KAFR_EL_SH 25.0 2.0 .1 . +KALUBIA 22.0 1.0 . .1 +MENOUFIA 33.0 2.0 .1 .1 +MINIA 50.0 3.0 .2 .1 +NEW_VALLEY 1.0 . . . +QUENA 8.0 . . . +SHARKIA 43.0 1.0 .1 . +SOHAG 20.0 1.0 . . +SUEZ 1.0 . . . ; + +param fn default 0.0 : N P205 := + + AMM_SULF .206 . + CAN_260 .26 . + CAN_310 .31 . + CAN_335 .335 . + C_250_55 .25 .055 + C_300_100 .30 .10 + DAP .18 .46 + SSP_155 . .15 + UREA .46 . ; + +param road default 0.0 : + + ABU_KIR ABU_ZAABAL ASSIOUT ASWAN HELWAN KAFR_EL_ZT SUEZ TALKHA := + +ALEXANDRIA 16 210 607 1135 244 119 362 187 +ASSIOUT 616 420 . 518 362 504 527 518 +ASWAN 1134 938 518 . 880 1022 1045 1036 +BEHERA 76 50 547 1065 184 42 288 120 +BENI_SUEF 359 163 257 775 105 248 270 261 +DAKAHLIA 208 138 515 1033 152 58 219 3 +DAMIETTA 267 216 596 1114 233 131 286 66 +FAYOUM 341 145 308 826 88 230 252 243 +GHARBIA 150 65 485 1003 122 20 226 55 +GIZA 287 48 372 890 .9 133 169 146 +ISMAILIA 365 142 536 1054 173 241 89 146 +KAFR_EL_SH 145 105 525 1043 162 20 266 35 +KALUBIA 190 97 439 957 76 66 180 81 +MENOUFIA 157 154 472 990 109 33 213 90 +MINIA 384 288 132 650 230 372 394 386 +NEW_VALLEY 815 619 199 519 561 703 726 717 +QUENA 858 662 242 276 604 746 769 760 +SHARKIA 240 60 473 991 110 78 214 58 +SOHAG 715 519 99 419 461 603 626 617 +SUEZ 370 224 541 1059 178 246 . 298 ; + +param rail_half default 0 : + + KAFR_EL_ZT ABU_ZAABAL HELWAN ASSIOUT := + +ABU_ZAABAL 85 . . . +HELWAN 142 57 . . +ASSIOUT 504 420 362 . +ASWAN 1022 938 880 518 ; + +param : impd_barg impd_road := + +ABU_ZAABAL 210 .1 +ASSIOUT 583 0 +ASWAN 1087 10 +HELWAN 183 0 +KAFR_EL_ZT 104 6 ; + +param io default 0.0 := + + [*,AMM_C_GAS] AMMONIA 1.0 + BF_GAS -609. + COKE_GAS -2.0 + ELECTRIC -1960. + STEAM -4. + WATER -700. + + [*,AMM_ELEC] AMMONIA 1.0 + EL_ASWAN -12.0 + + [*,AMM_SULF] AMMONIA -.26 + AMM_SULF 1.0 + BAGS -22. + ELECTRIC -19. + SULF_ACID -.76 + WATER -17. + + [*,CAN_310] AMMONIA -.20 + BAGS -23. + CAN_310 1.0 + LIMESTONE -.12 + NITR_ACID -.71 + STEAM -.4 + WATER -49. + + [*,CAN_335] AMMONIA -.21 + BAGS -23. + CAN_335 1.0 + LIMESTONE -.04 + NITR_ACID -.76 + STEAM -.4 + WATER -49. + + [*,NITR_ACID] AMMONIA -.292 + ELECTRIC -231. + NITR_ACID 1.0 + WATER -.6 + + [*,SSP_155] BAGS -22. + ELECTRIC -14. + PHOS_ROCK -.62 + SSP_155 1.0 + SULF_ACID -.41 + WATER -6. + + [*,SULF_A_P] ELECTRIC -75. + PYRITES -.826 + SULF_ACID 1.0 + WATER -60. + + [*,SULF_A_S] ELECTRIC -50. + EL_SULFUR -.334 + SULF_ACID 1.0 + WATER -20. ; + +param util default 0 := + + [*,*] SULF_A_S SULF_A_S 1 SULF_A_P SULF_A_P 1 + NITR_ACID NITR_ACID 1 AMM_ELEC AMM_ELEC 1 + AMM_C_GAS AMM_C_GAS 1 SSP SSP_155 1 + C_AMM_NITR CAN_310 1 C_AMM_NITR CAN_335 1 + AMM_SULF AMM_SULF 1 ; + +param p_imp default 0.0 := + + PYRITES 17.5 AMM_SULF 75. + EL_SULFUR 55. DAP 175. + UREA 150. SSP_155 80. + CAN_260 75. C_250_55 100. + CAN_310 90. C_300_100 130. + CAN_335 100. ; + +param p_r default 0.0 := + + ELECTRIC .007 + BF_GAS .007 + WATER .031 + STEAM 1.25 + BAGS .28 ; + +param p_pr default 0.0 := + + [HELWAN,COKE_GAS] 16.0 + [ASWAN,EL_ASWAN] 1.0 + + [*,LIMESTONE] ASWAN 1.2 + HELWAN 1.2 + + [*,PHOS_ROCK] ABU_ZAABAL 4.0 + ASSIOUT 3.5 + KAFR_EL_ZT 5.0 ; + +param dcap default 0.0 := + + [ABU_ZAABAL,*] SSP 600 + SULF_A_P 227 + SULF_A_S 242 + + [ASSIOUT,*] SSP 600 + SULF_A_S 250 + + [ASWAN,*] AMM_ELEC 450 + C_AMM_NITR 1100 + NITR_ACID 800 + + [HELWAN,*] AMM_C_GAS 172 + AMM_SULF 24 + C_AMM_NITR 364 + NITR_ACID 282 + + [KAFR_EL_ZT,*] SSP 600 + SULF_A_P 50 + SULF_A_S 200 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/fctp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/fctp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,93 @@ +/* FCTP, Fixed-Charge Transportation Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Fixed-Charge Transportation Problem (FCTP) is obtained from + classical transportation problem by imposing a fixed cost on each + transportation link if there is a positive flow on that link. */ + +param m, integer, > 0; +/* number of sources */ + +param n, integer, > 0; +/* number of customers */ + +set I := 1..m; +/* set of sources */ + +set J := 1..n; +/* set of customers */ + +param supply{i in I}, >= 0; +/* supply at source i */ + +param demand{j in J}, >= 0; +/* demand at customer j */ + +param varcost{i in I, j in J}, >= 0; +/* variable cost (a cost per one unit shipped from i to j) */ + +param fixcost{i in I, j in J}, >= 0; +/* fixed cost (a cost for shipping any amount from i to j) */ + +var x{i in I, j in J}, >= 0; +/* amount shipped from source i to customer j */ + +s.t. f{i in I}: sum{j in J} x[i,j] = supply[i]; +/* observe supply at source i */ + +s.t. g{j in J}: sum{i in I} x[i,j] = demand[j]; +/* satisfy demand at customer j */ + +var y{i in I, j in J}, binary; +/* y[i,j] = 1 means some amount is shipped from i to j */ + +s.t. h{i in I, j in J}: x[i,j] <= min(supply[i], demand[j]) * y[i,j]; +/* if y[i,j] is 0, force x[i,j] to be 0 (may note that supply[i] and + demand[j] are implicit upper bounds for x[i,j] as follows from the + constraints f[i] and g[j]) */ + +minimize cost: sum{i in I, j in J} varcost[i,j] * x[i,j] + + sum{i in I, j in J} fixcost[i,j] * y[i,j]; +/* total transportation costs */ + +data; + +/* These data correspond to the instance bal8x12 from [Balinski]. */ + +/* The optimal solution is 471.55 */ + +param m := 8; + +param n := 12; + +param supply := 1 15.00, 2 20.00, 3 45.00, 4 35.00, + 5 25.00, 6 35.00, 7 10.00, 8 25.00; + +param demand := 1 20.00, 2 15.00, 3 20.00, 4 15.00, + 5 5.00, 6 20.00, 7 30.00, 8 10.00, + 9 35.00, 10 25.00, 11 10.00, 12 5.00; + +param varcost + : 1 2 3 4 5 6 7 8 9 10 11 12 := + 1 0.69 0.64 0.71 0.79 1.70 2.83 2.02 5.64 5.94 5.94 5.94 7.68 + 2 1.01 0.75 0.88 0.59 1.50 2.63 2.26 5.64 5.85 5.62 5.85 4.94 + 3 1.05 1.06 1.08 0.64 1.22 2.37 1.66 5.64 5.91 5.62 5.91 4.94 + 4 1.94 1.50 1.56 1.22 1.98 1.98 1.36 6.99 6.99 6.99 6.99 3.68 + 5 1.61 1.40 1.61 1.33 1.68 2.83 1.54 4.26 4.26 4.26 4.26 2.99 + 6 5.29 5.94 6.08 5.29 5.96 6.77 5.08 0.31 0.21 0.17 0.31 1.53 + 7 5.29 5.94 6.08 5.29 5.96 6.77 5.08 0.55 0.35 0.40 0.19 1.53 + 8 5.29 6.08 6.08 5.29 5.96 6.45 5.08 2.43 2.30 2.33 1.81 2.50 ; + +param fixcost + : 1 2 3 4 5 6 7 8 9 10 11 12 := + 1 11.0 16.0 18.0 17.0 10.0 20.0 17.0 13.0 15.0 12.0 14.0 14.0 + 2 14.0 17.0 17.0 13.0 15.0 13.0 16.0 11.0 20.0 11.0 15.0 10.0 + 3 12.0 13.0 20.0 17.0 13.0 15.0 16.0 13.0 12.0 13.0 10.0 18.0 + 4 16.0 19.0 16.0 11.0 15.0 12.0 18.0 12.0 18.0 13.0 13.0 14.0 + 5 19.0 18.0 15.0 16.0 12.0 14.0 20.0 19.0 11.0 17.0 16.0 18.0 + 6 13.0 20.0 20.0 17.0 15.0 12.0 14.0 11.0 12.0 19.0 15.0 16.0 + 7 11.0 12.0 15.0 10.0 17.0 11.0 11.0 16.0 10.0 18.0 17.0 12.0 + 8 17.0 10.0 20.0 12.0 17.0 20.0 16.0 15.0 10.0 12.0 16.0 18.0 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/food.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/food.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,127 @@ +/* Food Manufacture 1, section 12.1 in + * Williams, "Model Building in Mathematical Programming" + * + * Sebastian Nowozin + */ + +set oils; +set month; + +/* Buying prices of the raw oils in the next six month. */ +param buyingprices{month,oils}; + +/* Actual amount bought in each month. */ +var buys{month,oils} >= 0; + +/* Stock for each oil. */ +var stock{month,oils} >= 0; + +/* Price of the produced product */ +param productprice >= 0; +param storagecost; + +param oilhardness{oils} >= 0; + +/* Actual amount of output oil produced in each month */ +var production{m in month} >= 0; +var useoil{m in month, o in oils} >= 0; + +maximize totalprofit: + sum{m in month} productprice*production[m] + - sum{m in month, o in oils} buyingprices[m,o]*buys[m,o] + - sum{m in month, o in oils} storagecost*stock[m,o]; + +/* Constraints */ + +/* 1. Starting stock */ +s.t. startstock{o in oils}: + stock[1,o] = 500; +s.t. endstock{o in oils}: + stock[6,o] + buys[6,o] - useoil[6,o] >= 500; + +/* 2. Stock constraints */ +s.t. stocklimit{m in month, o in oils}: + stock[m,o] <= 1000; + +s.t. production1{m in month, o in oils}: + useoil[m,o] <= stock[m,o] + buys[m,o]; +s.t. production2{m1 in month, m2 in month, o in oils : m2 = m1+1}: + stock[m2,o] = stock[m1,o] + buys[m1,o] - useoil[m1,o]; + +s.t. production3a{m in month}: + sum{o in oils} oilhardness[o]*useoil[m,o] >= 3*production[m]; +s.t. production3b{m in month}: + sum{o in oils} oilhardness[o]*useoil[m,o] <= 6*production[m]; + +s.t. production4{m in month}: + production[m] = sum{o in oils} useoil[m,o]; + +/* 3. Refining constraints */ +s.t. refine1{m in month}: + useoil[m,"VEG1"]+useoil[m,"VEG2"] <= 200; +s.t. refine2{m in month}: + useoil[m,"OIL1"]+useoil[m,"OIL2"]+useoil[m,"OIL3"] <= 250; + +solve; + +for {m in month} { + printf "Month %d\n", m; + printf "PRODUCE %4.2f tons, hardness %4.2f\n", production[m], + (sum{o in oils} oilhardness[o]*useoil[m,o]) / (sum{o in oils} useoil[m,o]); + + printf "\tVEG1\tVEG2\tOIL1\tOIL2\tOIL3\n"; + printf "STOCK"; + printf "%d", m; + for {o in oils} { + printf "\t%4.2f", stock[m,o]; + } + printf "\nBUY"; + for {o in oils} { + printf "\t%4.2f", buys[m,o]; + } + printf "\nUSE"; + printf "%d", m; + for {o in oils} { + printf "\t%4.2f", useoil[m,o]; + } + printf "\n"; + printf "\n"; +} +printf "Total profit: %4.2f\n", + (sum{m in month} productprice*production[m] + - sum{m in month, o in oils} buyingprices[m,o]*buys[m,o] + - sum{m in month, o in oils} storagecost*stock[m,o]); +printf " turnover: %4.2f\n", + sum{m in month} productprice*production[m]; +printf " buying costs: %4.2f\n", + sum{m in month, o in oils} buyingprices[m,o]*buys[m,o]; +printf " storage costs: %4.2f\n", + sum{m in month, o in oils} storagecost*stock[m,o]; + + +data; + +param : oils : oilhardness := + VEG1 8.8 + VEG2 6.1 + OIL1 2.0 + OIL2 4.2 + OIL3 5.0 ; + +set month := 1 2 3 4 5 6; + +param buyingprices + +: VEG1 VEG2 OIL1 OIL2 OIL3 := + +1 110 120 130 110 115 +2 130 130 110 90 115 +3 110 140 130 100 95 +4 120 110 120 120 125 +5 100 120 150 110 105 +6 90 100 140 80 135 ; + +param productprice := 150; +param storagecost := 5; + +end; diff -r d59bea55db9b -r c445c931472f examples/food2.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/food2.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,150 @@ +/* Food Manufacture 2, section 12.2 in + * Williams, "Model Building in Mathematical Programming" + * + * Sebastian Nowozin + */ + +set oils; +set month; + +/* Buying prices of the raw oils in the next six month. */ +param buyingprices{month,oils}; + +/* Actual amount bought in each month. */ +var buys{month,oils} >= 0; + +/* Stock for each oil. */ +var stock{month,oils} >= 0; + +/* Price of the produced product */ +param productprice >= 0; +param storagecost; + +param oilhardness{oils} >= 0; +param M >= 0; + +/* Actual amount of output oil produced in each month */ +var production{m in month} >= 0; +var useoil{m in month, o in oils} >= 0, <= M; +var useoilb{m in month, o in oils}, binary; + +maximize totalprofit: + sum{m in month} productprice*production[m] + - sum{m in month, o in oils} buyingprices[m,o]*buys[m,o] + - sum{m in month, o in oils} storagecost*stock[m,o]; + +/* Constraints */ + +/* 1. Starting stock */ +s.t. startstock{o in oils}: + stock[1,o] = 500; +s.t. endstock{o in oils}: + stock[6,o] + buys[6,o] - useoil[6,o] >= 500; + +/* 2. Stock constraints */ +s.t. stocklimit{m in month, o in oils}: + stock[m,o] <= 1000; + +s.t. production1{m in month, o in oils}: + useoil[m,o] <= stock[m,o] + buys[m,o]; +s.t. production2{m1 in month, m2 in month, o in oils : m2 = m1+1}: + stock[m2,o] = stock[m1,o] + buys[m1,o] - useoil[m1,o]; + +s.t. production3a{m in month}: + sum{o in oils} oilhardness[o]*useoil[m,o] >= 3*production[m]; +s.t. production3b{m in month}: + sum{o in oils} oilhardness[o]*useoil[m,o] <= 6*production[m]; + +s.t. production4{m in month}: + production[m] = sum{o in oils} useoil[m,o]; + +/* 3. Refining constraints */ +s.t. refine1{m in month}: + useoil[m,"VEG1"]+useoil[m,"VEG2"] <= 200; +s.t. refine2{m in month}: + useoil[m,"OIL1"]+useoil[m,"OIL2"]+useoil[m,"OIL3"] <= 250; + +/* 4. Additional conditions: + * i) The food may never be made up of more than three oils every month + */ +s.t. useoilb_calc{m in month, o in oils}: + M*useoilb[m,o] >= useoil[m,o]; +s.t. useoilb_limit{m in month}: + sum{o in oils} useoilb[m,o] <= 3; + +/* ii) If an oil is used in a month, at least 20 tons must be used. + */ +s.t. useminimum{m in month, o in oils}: + 20*useoilb[m,o] <= useoil[m,o]; + +/* iii) If either of VEG1 or VEG2 is used in a month, OIL2 must also be used + */ +s.t. use_oil2a{m in month}: + useoilb[m,"VEG1"] <= useoilb[m,"OIL3"]; +s.t. use_oil2b{m in month}: + useoilb[m,"VEG2"] <= useoilb[m,"OIL3"]; + +solve; + +for {m in month} { + printf "Month %d\n", m; + printf "PRODUCE %4.2f tons, hardness %4.2f\n", production[m], + (sum{o in oils} oilhardness[o]*useoil[m,o]) / (sum{o in oils} useoil[m,o]); + + printf "\tVEG1\tVEG2\tOIL1\tOIL2\tOIL3\n"; + printf "STOCK"; + printf "%d", m; + for {o in oils} { + printf "\t%4.2f", stock[m,o]; + } + printf "\nBUY"; + for {o in oils} { + printf "\t%4.2f", buys[m,o]; + } + printf "\nUSE"; + printf "%d", m; + for {o in oils} { + printf "\t%4.2f", useoil[m,o]; + } + printf "\n"; + printf "\n"; +} +printf "Total profit: %4.2f\n", + (sum{m in month} productprice*production[m] + - sum{m in month, o in oils} buyingprices[m,o]*buys[m,o] + - sum{m in month, o in oils} storagecost*stock[m,o]); +printf " turnover: %4.2f\n", + sum{m in month} productprice*production[m]; +printf " buying costs: %4.2f\n", + sum{m in month, o in oils} buyingprices[m,o]*buys[m,o]; +printf " storage costs: %4.2f\n", + sum{m in month, o in oils} storagecost*stock[m,o]; + + +data; + +param : oils : oilhardness := + VEG1 8.8 + VEG2 6.1 + OIL1 2.0 + OIL2 4.2 + OIL3 5.0 ; + +set month := 1 2 3 4 5 6; + +param buyingprices + +: VEG1 VEG2 OIL1 OIL2 OIL3 := + +1 110 120 130 110 115 +2 130 130 110 90 115 +3 110 140 130 100 95 +4 120 110 120 120 125 +5 100 120 150 110 105 +6 90 100 140 80 135 ; + +param productprice := 150; +param storagecost := 5; +param M := 1000; + +end; diff -r d59bea55db9b -r c445c931472f examples/gap.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/gap.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,79 @@ +/* GAP, Generalized Assignment Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Generalized Assignment Problem (GAP) is to assign a set of jobs + to a set of agents subject to the constraints that each job must be + assigned exactly to one agent and the total resources consumed by all + jobs assigned to an agent must not exceed the agent's capacity. */ + +param m, integer, > 0; +/* number of agents */ + +param n, integer, > 0; +/* number of jobs */ + +set I := 1..m; +/* set of agents */ + +set J := 1..n; +/* set of jobs */ + +param a{i in I, j in J}, >= 0; +/* resource consumed in allocating job j to agent i */ + +param b{i in I}, >= 0; +/* resource capacity of agent i */ + +param c{i in I, j in J}, >= 0; +/* cost of allocating job j to agent i */ + +var x{i in I, j in J}, binary; +/* x[i,j] = 1 means job j is assigned to agent i */ + +s.t. one{j in J}: sum{i in I} x[i,j] = 1; +/* job j must be assigned exactly to one agent */ + +s.t. lim{i in I}: sum{j in J} a[i,j] * x[i,j] <= b[i]; +/* total amount of resources consumed by all jobs assigned to agent i + must not exceed the agent's capacity */ + +minimize obj: sum{i in I, j in J} c[i,j] * x[i,j]; +/* the objective is to find cheapest assignment (note that gap can also + be formulated as maximization problem) */ + +data; + +/* These data correspond to the instance c515-1 (gap1) from: + + I.H. Osman, "Heuristics for the Generalised Assignment Problem: + Simulated Annealing and Tabu Search Approaches", OR Spektrum, Volume + 17, 211-225, 1995 + + D. Cattrysse, M. Salomon and L.N. Van Wassenhove, "A set partitioning + heuristic for the generalized assignment problem", European Journal + of Operational Research, Volume 72, 167-174, 1994 */ + +/* The optimal solution is 261 (minimization) or 336 (maximization) */ + +param m := 5; + +param n := 15; + +param a : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 := + 1 8 15 14 23 8 16 8 25 9 17 25 15 10 8 24 + 2 15 7 23 22 11 11 12 10 17 16 7 16 10 18 22 + 3 21 20 6 22 24 10 24 9 21 14 11 14 11 19 16 + 4 20 11 8 14 9 5 6 19 19 7 6 6 13 9 18 + 5 8 13 13 13 10 20 25 16 16 17 10 10 5 12 23 ; + +param b := 1 36, 2 34, 3 38, 4 27, 5 33; + +param c : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 := + 1 17 21 22 18 24 15 20 18 19 18 16 22 24 24 16 + 2 23 16 21 16 17 16 19 25 18 21 17 15 25 17 24 + 3 16 20 16 25 24 16 17 19 19 18 20 16 17 21 24 + 4 19 19 22 22 20 16 19 17 21 19 25 23 25 25 25 + 5 18 19 15 15 21 25 16 16 23 15 22 17 19 22 24 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/glpsol.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/glpsol.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,10 @@ +/* glpsol.c */ + +#include + +int main(int argc, const char *argv[]) +{ /* stand-alone LP/MIP solver */ + return glp_main(argc, argv); +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/graph.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/graph.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,98 @@ +/* graph.mod - graph visualization */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* This model creates a picture in EPS format to visualize a graph. */ + +param file, symbolic, default "graph.eps"; +/* output file to write the picture */ + +param R, default 2; +/* radius to draw vertices, in mm */ + +param n, integer, > 0; +/* number of vertices */ + +set V, default 1..n; +/* set of vertices */ + +set E, within V cross V; +/* set of edges */ + +param x{i in V}, default 50 * cos((i - 1) / card(V) * 8 * atan(1)); +param y{i in V}, default 50 * sin((i - 1) / card(V) * 8 * atan(1)); +/* x[i] and y[i] are coordinates of node i, in mm */ + +param x0 := (min{i in V} x[i]) - R - 3.0; +param y0 := (min{i in V} y[i]) - R - 3.0; +param x1 := (max{i in V} x[i]) + R + 3.0; +param y1 := (max{i in V} y[i]) + R + 3.0; + +printf "%%!PS-Adobe-3.0 EPSF-3.0\n" > file; +printf "%%%%BoundingBox: 0 0 %d %d\n", + (72 / 25.4) * (x1 - x0), (72 / 25.4) * (y1 - y0) >> file; +printf "/Helvetica findfont 6 scalefont setfont\n" >> file; +printf "/mm { 72 mul 25.4 div } def\n" >> file; + +for {(i,j) in E} +{ printf "newpath\n" >> file; + printf "%g mm %g mm moveto\n", x[i] - x0, y[i] - y0 >> file; + printf "%g mm %g mm lineto\n", x[j] - x0, y[j] - y0 >> file; + printf "closepath\n" >> file; + printf "stroke\n" >> file; +} + +for {i in V} +{ printf "newpath\n" >> file; + printf "%g mm %g mm %g mm 0 360 arc\n", + x[i] - x0, y[i] - y0, R >> file; + printf "closepath\n" >> file; + printf "gsave 1 1 1 setrgbcolor fill grestore\n" >> file; + printf "stroke\n" >> file; + printf "%g mm %g mm moveto\n", + x[i] - (if i <= 9 then 1.2 else 1.8) - x0, + y[i] - 0.8 - y0 >> file; + printf "( %d ) show\n", i >> file; +} + +printf "showpage\n" >> file; +printf "%%%%EOF\n" >> file; + +data; + +param +: V : x y := + 1 0 40 + 2 38 12 + 3 24 -32 + 4 -24 -32 + 5 -38 12 + 6 -19 26 + 7 19 26 + 8 31 -10 + 9 0 -32 + 10 -31 -10 + 11 -9 12 + 12 9 12 + 13 14 -5 + 14 0 -15 + 15 -14 -5 + 16 0 0 ; + +set E := + (1,*) 6 10 16 12 7 + (2,*) 7 6 16 13 8 + (3,*) 8 7 16 14 9 + (4,*) 9 8 16 15 10 + (5,*) 10 9 16 11 6 + (6,*) 14 + (7,*) 15 + (8,*) 11 + (9,*) 12 + (10,*) 13 + (11,*) 12 15 + (12,*) 13 + (13,*) 14 + (14,*) 15 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/hashi.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/hashi.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,168 @@ +/* A solver for the Japanese number-puzzle Hashiwokakero + * (http://en.wikipedia.org/wiki/Hashiwokakero) + * + * Sebastian Nowozin , 13th January 2009 + */ + +param n := 25; +set rows := 1..n; +set cols := 1..n; +param givens{rows, cols}, integer, >= 0, <= 8, default 0; + +/* Set of vertices as (row,col) coordinates */ +set V := { (i,j) in { rows, cols }: givens[i,j] != 0 }; + +/* Set of feasible horizontal edges from (i,j) to (k,l) rightwards */ +set Eh := { (i,j,k,l) in { V, V }: + i = k and j < l and # Same row and left to right + card({ (s,t) in V: s = i and t > j and t < l }) = 0 # No vertex inbetween + }; + +/* Set of feasible vertical edges from (i,j) to (k,l) downwards */ +set Ev := { (i,j,k,l) in { V, V }: + j = l and i < k and # Same column and top to bottom + card({ (s,t) in V: t = j and s > i and s < k }) = 0 # No vertex inbetween + }; + +set E := Eh union Ev; + +/* Indicators: use edge once/twice */ +var xe1{E}, binary; +var xe2{E}, binary; + +/* Constraint: Do not use edge or do use once or do use twice */ +s.t. edge_sel{(i,j,k,l) in E}: + xe1[i,j,k,l] + xe2[i,j,k,l] <= 1; + +/* Constraint: There must be as many edges used as the node value */ +s.t. satisfy_vertex_demand{(s,t) in V}: + sum{(i,j,k,l) in E: (i = s and j = t) or (k = s and l = t)} + (xe1[i,j,k,l] + 2.0*xe2[i,j,k,l]) = givens[s,t]; + +/* Constraint: No crossings */ +s.t. no_crossing1{(i,j,k,l) in Eh, (s,t,u,v) in Ev: + s < i and u > i and j < t and l > t}: + xe1[i,j,k,l] + xe1[s,t,u,v] <= 1; +s.t. no_crossing2{(i,j,k,l) in Eh, (s,t,u,v) in Ev: + s < i and u > i and j < t and l > t}: + xe1[i,j,k,l] + xe2[s,t,u,v] <= 1; +s.t. no_crossing3{(i,j,k,l) in Eh, (s,t,u,v) in Ev: + s < i and u > i and j < t and l > t}: + xe2[i,j,k,l] + xe1[s,t,u,v] <= 1; +s.t. no_crossing4{(i,j,k,l) in Eh, (s,t,u,v) in Ev: + s < i and u > i and j < t and l > t}: + xe2[i,j,k,l] + xe2[s,t,u,v] <= 1; + + +/* Model connectivity by auxiliary network flow problem: + * One vertex becomes a target node and all other vertices send a unit flow + * to it. The edge selection variables xe1/xe2 are VUB constraints and + * therefore xe1/xe2 select the feasible graph for the max-flow problems. + */ +set node_target := { (s,t) in V: + card({ (i,j) in V: i < s or (i = s and j < t) }) = 0}; +set node_sources := { (s,t) in V: (s,t) not in node_target }; + +var flow_forward{ E }, >= 0; +var flow_backward{ E }, >= 0; +s.t. flow_conservation{ (s,t) in node_target, (p,q) in V }: + /* All incoming flows */ + - sum{(i,j,k,l) in E: k = p and l = q} flow_forward[i,j,k,l] + - sum{(i,j,k,l) in E: i = p and j = q} flow_backward[i,j,k,l] + /* All outgoing flows */ + + sum{(i,j,k,l) in E: k = p and l = q} flow_backward[i,j,k,l] + + sum{(i,j,k,l) in E: i = p and j = q} flow_forward[i,j,k,l] + = 0 + (if (p = s and q = t) then card(node_sources) else -1); + +/* Variable-Upper-Bound (VUB) constraints: xe1/xe2 bound the flows. + */ +s.t. connectivity_vub1{(i,j,k,l) in E}: + flow_forward[i,j,k,l] <= card(node_sources)*(xe1[i,j,k,l] + xe2[i,j,k,l]); +s.t. connectivity_vub2{(i,j,k,l) in E}: + flow_backward[i,j,k,l] <= card(node_sources)*(xe1[i,j,k,l] + xe2[i,j,k,l]); + +/* A feasible solution is enough + */ +minimize cost: 0; + +solve; + +/* Output solution graphically */ +printf "\nSolution:\n"; +for { row in rows } { + for { col in cols } { + /* First print this cell information: givens or space */ + printf{0..0: givens[row,col] != 0} "%d", givens[row,col]; + printf{0..0: givens[row,col] = 0 and + card({(i,j,k,l) in Eh: i = row and col >= j and col < l and + xe1[i,j,k,l] = 1}) = 1} "-"; + printf{0..0: givens[row,col] = 0 and + card({(i,j,k,l) in Eh: i = row and col >= j and col < l and + xe2[i,j,k,l] = 1}) = 1} "="; + printf{0..0: givens[row,col] = 0 + and card({(i,j,k,l) in Ev: j = col and row >= i and row < k and + xe1[i,j,k,l] = 1}) = 1} "|"; + printf{0..0: givens[row,col] = 0 + and card({(i,j,k,l) in Ev: j = col and row >= i and row < k and + xe2[i,j,k,l] = 1}) = 1} '"'; + printf{0..0: givens[row,col] = 0 + and card({(i,j,k,l) in Eh: i = row and col >= j and col < l and + (xe1[i,j,k,l] = 1 or xe2[i,j,k,l] = 1)}) = 0 + and card({(i,j,k,l) in Ev: j = col and row >= i and row < k and + (xe1[i,j,k,l] = 1 or xe2[i,j,k,l] = 1)}) = 0} " "; + + /* Now print any edges */ + printf{(i,j,k,l) in Eh: i = row and col >= j and col < l and xe1[i,j,k,l] = 1} "-"; + printf{(i,j,k,l) in Eh: i = row and col >= j and col < l and xe2[i,j,k,l] = 1} "="; + + printf{(i,j,k,l) in Eh: i = row and col >= j and col < l and + xe1[i,j,k,l] = 0 and xe2[i,j,k,l] = 0} " "; + printf{0..0: card({(i,j,k,l) in Eh: i = row and col >= j and col < l}) = 0} " "; + } + printf "\n"; + for { col in cols } { + printf{(i,j,k,l) in Ev: j = col and row >= i and row < k and xe1[i,j,k,l] = 1} "|"; + printf{(i,j,k,l) in Ev: j = col and row >= i and row < k and xe2[i,j,k,l] = 1} '"'; + printf{(i,j,k,l) in Ev: j = col and row >= i and row < k and + xe1[i,j,k,l] = 0 and xe2[i,j,k,l] = 0} " "; + /* No vertical edges: skip also a field */ + printf{0..0: card({(i,j,k,l) in Ev: j = col and row >= i and row < k}) = 0} " "; + printf " "; + } + printf "\n"; +} + +data; + +/* This is a difficult 25x25 Hashiwokakero. + */ +param givens : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 +25 := + 1 2 . 2 . 2 . . 2 . 2 . . 2 . . . . 2 . 2 . 2 . 2 . + 2 . 1 . . . . 2 . . . 4 . . 5 . 2 . . 1 . 2 . 2 . 1 + 3 2 . . 5 . 4 . . 3 . . . . . 1 . . 4 . 5 . 1 . 1 . + 4 . . . . . . . . . . . 1 . 3 . . 1 . . . . . . . . + 5 2 . . 6 . 6 . . 8 . 5 . 2 . . 3 . 5 . 7 . . 2 . . + 6 . 1 . . . . . . . . . 1 . . 2 . . . . . 1 . . . 3 + 7 2 . . . . 5 . . 6 . 4 . . 2 . . . 2 . 5 . 4 . 2 . + 8 . 2 . 2 . . . . . . . . . . . 3 . . 3 . . . 1 . 2 + 9 . . . . . . . . . . 4 . 2 . 2 . . 1 . . . 3 . 1 . + 10 2 . 3 . . 6 . . 2 . . . . . . . . . . 3 . . . . . + 11 . . . . 1 . . 2 . . 5 . . 1 . 4 . 3 . . . . 2 . 4 + 12 . . 2 . . 1 . . . . . . 5 . 4 . . . . 4 . 3 . . . + 13 2 . . . 3 . 1 . . . . . . . . 3 . . 5 . 5 . . 2 . + 14 . . . . . 2 . 5 . . 7 . 5 . 3 . 1 . . 1 . . 1 . 4 + 15 2 . 5 . 3 . . . . 1 . 2 . 1 . . . . 2 . 4 . . 2 . + 16 . . . . . 1 . . . . . . . . . . 2 . . 2 . 1 . . 3 + 17 2 . 6 . 6 . . 2 . . 2 . 2 . 5 . . . . . 2 . . . . + 18 . . . . . 1 . . . 3 . . . . . 1 . . 1 . . 4 . 3 . + 19 . . 4 . 5 . . 2 . . . 2 . . 6 . 6 . . 3 . . . . 3 + 20 2 . . . . . . . . . 2 . . 1 . . . . . . 1 . . 1 . + 21 . . 3 . . 3 . 5 . 5 . . 4 . 6 . 7 . . 4 . 6 . . 4 + 22 2 . . . 3 . 5 . 2 . 1 . . . . . . . . . . . . . . + 23 . . . . . . . . . 1 . . . . . . 3 . 2 . . 5 . . 5 + 24 2 . 3 . 3 . 5 . 4 . 3 . 3 . 4 . . 2 . 2 . . . 1 . + 25 . 1 . 2 . 2 . . . 2 . 2 . . . 2 . . . . 2 . 2 . 2 + ; + +end; diff -r d59bea55db9b -r c445c931472f examples/huge.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/huge.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,25 @@ +/*Arithmetic Mean of a large number of Integers + - or - solve a very large constraint matrix + over 1 million rows and columns + Nigel_Galloway@operamail.com + March 18th., 2008. +*/ + +param e := 20; +/* set Sample := {-2**e..2**e-1}; */ +set Sample := {1..2**e-1}; + +var Mean; +var E{z in Sample}; + +/* sum of variances is zero */ +zumVariance: sum{z in Sample} E[z] = 0; + +/* Mean + variance[n] = Sample[n] */ +variances{z in Sample}: Mean + E[z] = z; + +solve; + +printf "The arithmetic mean of the integers from 1 to %d is %f\n", 2**e-1, Mean; + +end; diff -r d59bea55db9b -r c445c931472f examples/iptsamp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/iptsamp.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,17 @@ +/* iptsamp.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *P; + P = glp_create_prob(); + glp_read_mps(P, GLP_MPS_DECK, NULL, "25fv47.mps"); + glp_interior(P, NULL); + glp_print_ipt(P, "25fv47.txt"); + glp_delete_prob(P); + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/jssp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/jssp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,114 @@ +/* JSSP, Job-Shop Scheduling Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Job-Shop Scheduling Problem (JSSP) is to schedule a set of jobs + on a set of machines, subject to the constraint that each machine can + handle at most one job at a time and the fact that each job has a + specified processing order through the machines. The objective is to + schedule the jobs so as to minimize the maximum of their completion + times. + + Reference: + D. Applegate and W. Cook, "A Computational Study of the Job-Shop + Scheduling Problem", ORSA J. On Comput., Vol. 3, No. 2, Spring 1991, + pp. 149-156. */ + +param n, integer, > 0; +/* number of jobs */ + +param m, integer, > 0; +/* number of machines */ + +set J := 1..n; +/* set of jobs */ + +set M := 1..m; +/* set of machines */ + +param sigma{j in J, t in 1..m}, in M; +/* permutation of the machines, which represents the processing order + of j through the machines: j must be processed first on sigma[j,1], + then on sigma[j,2], etc. */ + +check{j in J, t1 in 1..m, t2 in 1..m: t1 <> t2}: + sigma[j,t1] != sigma[j,t2]; +/* sigma must be permutation */ + +param p{j in J, a in M}, >= 0; +/* processing time of j on a */ + +var x{j in J, a in M}, >= 0; +/* starting time of j on a */ + +s.t. ord{j in J, t in 2..m}: + x[j, sigma[j,t]] >= x[j, sigma[j,t-1]] + p[j, sigma[j,t-1]]; +/* j can be processed on sigma[j,t] only after it has been completely + processed on sigma[j,t-1] */ + +/* The disjunctive condition that each machine can handle at most one + job at a time is the following: + + x[i,a] >= x[j,a] + p[j,a] or x[j,a] >= x[i,a] + p[i,a] + + for all i, j in J, a in M. This condition is modeled through binary + variables Y as shown below. */ + +var Y{i in J, j in J, a in M}, binary; +/* Y[i,j,a] is 1 if i scheduled before j on machine a, and 0 if j is + scheduled before i */ + +param K := sum{j in J, a in M} p[j,a]; +/* some large constant */ + +display K; + +s.t. phi{i in J, j in J, a in M: i <> j}: + x[i,a] >= x[j,a] + p[j,a] - K * Y[i,j,a]; +/* x[i,a] >= x[j,a] + p[j,a] iff Y[i,j,a] is 0 */ + +s.t. psi{i in J, j in J, a in M: i <> j}: + x[j,a] >= x[i,a] + p[i,a] - K * (1 - Y[i,j,a]); +/* x[j,a] >= x[i,a] + p[i,a] iff Y[i,j,a] is 1 */ + +var z; +/* so-called makespan */ + +s.t. fin{j in J}: z >= x[j, sigma[j,m]] + p[j, sigma[j,m]]; +/* which is the maximum of the completion times of all the jobs */ + +minimize obj: z; +/* the objective is to make z as small as possible */ + +data; + +/* These data correspond to the instance ft06 (mt06) from: + + H. Fisher, G.L. Thompson (1963), Probabilistic learning combinations + of local job-shop scheduling rules, J.F. Muth, G.L. Thompson (eds.), + Industrial Scheduling, Prentice Hall, Englewood Cliffs, New Jersey, + 225-251 */ + +/* The optimal solution is 55 */ + +param n := 6; + +param m := 6; + +param sigma : 1 2 3 4 5 6 := + 1 3 1 2 4 6 5 + 2 2 3 5 6 1 4 + 3 3 4 6 1 2 5 + 4 2 1 3 4 5 6 + 5 3 2 5 6 1 4 + 6 2 4 6 1 5 3 ; + +param p : 1 2 3 4 5 6 := + 1 3 6 1 7 6 3 + 2 10 8 5 4 10 10 + 3 9 1 5 4 7 8 + 4 5 5 5 3 8 9 + 5 3 3 9 1 5 4 + 6 10 3 1 3 4 9 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/magic.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/magic.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,54 @@ +/* MAGIC, Magic Square */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* In recreational mathematics, a magic square of order n is an + arrangement of n^2 numbers, usually distinct integers, in a square, + such that n numbers in all rows, all columns, and both diagonals sum + to the same constant. A normal magic square contains the integers + from 1 to n^2. + + (From Wikipedia, the free encyclopedia.) */ + +param n, integer, > 0, default 4; +/* square order */ + +set N := 1..n^2; +/* integers to be placed */ + +var x{i in 1..n, j in 1..n, k in N}, binary; +/* x[i,j,k] = 1 means that cell (i,j) contains integer k */ + +s.t. a{i in 1..n, j in 1..n}: sum{k in N} x[i,j,k] = 1; +/* each cell must be assigned exactly one integer */ + +s.t. b{k in N}: sum{i in 1..n, j in 1..n} x[i,j,k] = 1; +/* each integer must be assigned exactly to one cell */ + +var s; +/* the magic sum */ + +s.t. r{i in 1..n}: sum{j in 1..n, k in N} k * x[i,j,k] = s; +/* the sum in each row must be the magic sum */ + +s.t. c{j in 1..n}: sum{i in 1..n, k in N} k * x[i,j,k] = s; +/* the sum in each column must be the magic sum */ + +s.t. d: sum{i in 1..n, k in N} k * x[i,i,k] = s; +/* the sum in the diagonal must be the magic sum */ + +s.t. e: sum{i in 1..n, k in N} k * x[i,n-i+1,k] = s; +/* the sum in the co-diagonal must be the magic sum */ + +solve; + +printf "\n"; +printf "Magic sum is %d\n", s; +printf "\n"; +for{i in 1..n} +{ printf{j in 1..n} "%3d", sum{k in N} k * x[i,j,k]; + printf "\n"; +} +printf "\n"; + +end; diff -r d59bea55db9b -r c445c931472f examples/maxcut.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/maxcut.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,85 @@ +/* MAXCUT, Maximum Cut Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Maximum Cut Problem in a network G = (V, E), where V is a set + of nodes, E is a set of edges, is to find the partition of V into + disjoint sets V1 and V2, which maximizes the sum of edge weights + w(e), where edge e has one endpoint in V1 and other endpoint in V2. + + Reference: + Garey, M.R., and Johnson, D.S. (1979), Computers and Intractability: + A guide to the theory of NP-completeness [Network design, Cuts and + Connectivity, Maximum Cut, ND16]. */ + +set E, dimen 2; +/* set of edges */ + +param w{(i,j) in E}, >= 0, default 1; +/* w[i,j] is weight of edge (i,j) */ + +set V := (setof{(i,j) in E} i) union (setof{(i,j) in E} j); +/* set of nodes */ + +var x{i in V}, binary; +/* x[i] = 0 means that node i is in set V1 + x[i] = 1 means that node i is in set V2 */ + +/* We need to include in the objective function only that edges (i,j) + from E, for which x[i] != x[j]. This can be modeled through binary + variables s[i,j] as follows: + + s[i,j] = x[i] xor x[j] = (x[i] + x[j]) mod 2, (1) + + where s[i,j] = 1 iff x[i] != x[j], that leads to the following + objective function: + + z = sum{(i,j) in E} w[i,j] * s[i,j]. (2) + + To describe "exclusive or" (1) we could think that s[i,j] is a minor + bit of the sum x[i] + x[j]. Then introducing binary variables t[i,j], + which represent a major bit of the sum x[i] + x[j], we can write: + + x[i] + x[j] = s[i,j] + 2 * t[i,j]. (3) + + An easy check shows that conditions (1) and (3) are equivalent. + + Note that condition (3) can be simplified by eliminating variables + s[i,j]. Indeed, from (3) it follows that: + + s[i,j] = x[i] + x[j] - 2 * t[i,j]. (4) + + Since the expression in the right-hand side of (4) is integral, this + condition can be rewritten in the equivalent form: + + 0 <= x[i] + x[j] - 2 * t[i,j] <= 1. (5) + + (One might note that (5) means t[i,j] = x[i] and x[j].) + + Substituting s[i,j] from (4) to (2) leads to the following objective + function: + + z = sum{(i,j) in E} w[i,j] * (x[i] + x[j] - 2 * t[i,j]), (6) + + which does not include variables s[i,j]. */ + +var t{(i,j) in E}, binary; +/* t[i,j] = x[i] and x[j] = (x[i] + x[j]) div 2 */ + +s.t. xor{(i,j) in E}: 0 <= x[i] + x[j] - 2 * t[i,j] <= 1; +/* see (4) */ + +maximize z: sum{(i,j) in E} w[i,j] * (x[i] + x[j] - 2 * t[i,j]); +/* see (6) */ + +data; + +/* In this example the network has 15 nodes and 22 edges. */ + +/* Optimal solution is 20 */ + +set E := + 1 2, 1 5, 2 3, 2 6, 3 4, 3 8, 4 9, 5 6, 5 7, 6 8, 7 8, 7 12, 8 9, + 8 12, 9 10, 9 14, 10 11, 10 14, 11 15, 12 13, 13 14, 14 15; + +end; diff -r d59bea55db9b -r c445c931472f examples/maxflow.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/maxflow.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,83 @@ +/* MAXFLOW, Maximum Flow Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Maximum Flow Problem in a network G = (V, E), where V is a set + of nodes, E within V x V is a set of arcs, is to maximize the flow + from one given node s (source) to another given node t (sink) subject + to conservation of flow constraints at each node and flow capacities + on each arc. */ + +param n, integer, >= 2; +/* number of nodes */ + +set V, default {1..n}; +/* set of nodes */ + +set E, within V cross V; +/* set of arcs */ + +param a{(i,j) in E}, > 0; +/* a[i,j] is capacity of arc (i,j) */ + +param s, symbolic, in V, default 1; +/* source node */ + +param t, symbolic, in V, != s, default n; +/* sink node */ + +var x{(i,j) in E}, >= 0, <= a[i,j]; +/* x[i,j] is elementary flow through arc (i,j) to be found */ + +var flow, >= 0; +/* total flow from s to t */ + +s.t. node{i in V}: +/* node[i] is conservation constraint for node i */ + + sum{(j,i) in E} x[j,i] + (if i = s then flow) + /* summary flow into node i through all ingoing arcs */ + + = /* must be equal to */ + + sum{(i,j) in E} x[i,j] + (if i = t then flow); + /* summary flow from node i through all outgoing arcs */ + +maximize obj: flow; +/* objective is to maximize the total flow through the network */ + +solve; + +printf{1..56} "="; printf "\n"; +printf "Maximum flow from node %s to node %s is %g\n\n", s, t, flow; +printf "Starting node Ending node Arc capacity Flow in arc\n"; +printf "------------- ----------- ------------ -----------\n"; +printf{(i,j) in E: x[i,j] != 0}: "%13s %11s %12g %11g\n", i, j, + a[i,j], x[i,j]; +printf{1..56} "="; printf "\n"; + +data; + +/* These data correspond to an example from [Christofides]. */ + +/* Optimal solution is 29 */ + +param n := 9; + +param : E : a := + 1 2 14 + 1 4 23 + 2 3 10 + 2 4 9 + 3 5 12 + 3 8 18 + 4 5 26 + 5 2 11 + 5 6 25 + 5 7 4 + 6 7 7 + 6 8 8 + 7 9 15 + 8 9 20; + +end; diff -r d59bea55db9b -r c445c931472f examples/mfasp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mfasp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,62 @@ +/* MFASP, Minimum Feedback Arc Set Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Minimum Feedback Arc Set Problem for a given directed graph + G = (V, E), where V is a set of vertices and E is a set of arcs, is + to find a minimal subset of arcs, which being removed from the graph + make it acyclic. + + Reference: + Garey, M.R., and Johnson, D.S. (1979), Computers and Intractability: + A guide to the theory of NP-completeness [Graph Theory, Covering and + Partitioning, Minimum Feedback Arc Set, GT9]. */ + +param n, integer, >= 0; +/* number of vertices */ + +set V, default 1..n; +/* set of vertices */ + +set E, within V cross V, +default setof{i in V, j in V: i <> j and Uniform(0,1) <= 0.15} (i,j); +/* set of arcs */ + +printf "Graph has %d vertices and %d arcs\n", card(V), card(E); + +var x{(i,j) in E}, binary; +/* x[i,j] = 1 means that (i->j) is a feedback arc */ + +/* It is known that a digraph G = (V, E) is acyclic if and only if its + vertices can be assigned numbers from 1 to |V| in such a way that + k[i] + 1 <= k[j] for every arc (i->j) in E, where k[i] is a number + assigned to vertex i. We may use this condition to require that the + digraph G = (V, E \ E'), where E' is a subset of feedback arcs, is + acyclic. */ + +var k{i in V}, >= 1, <= card(V); +/* k[i] is a number assigned to vertex i */ + +s.t. r{(i,j) in E}: k[j] - k[i] >= 1 - card(V) * x[i,j]; +/* note that x[i,j] = 1 leads to a redundant constraint */ + +minimize obj: sum{(i,j) in E} x[i,j]; +/* the objective is to minimize the cardinality of a subset of feedback + arcs */ + +solve; + +printf "Minimum feedback arc set:\n"; +printf{(i,j) in E: x[i,j]} "%d %d\n", i, j; + +data; + +/* The optimal solution is 3 */ + +param n := 15; + +set E := 1 2, 2 3, 3 4, 3 8, 4 9, 5 1, 6 5, 7 5, 8 6, 8 7, 8 9, 9 10, + 10 11, 10 14, 11 15, 12 7, 12 8, 12 13, 13 8, 13 12, 13 14, + 14 9, 15 14; + +end; diff -r d59bea55db9b -r c445c931472f examples/mfvsp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mfvsp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,62 @@ +/* MFVSP, Minimum Feedback Vertex Set Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Minimum Feedback Vertex Set Problem for a given directed graph + G = (V, E), where V is a set of vertices and E is a set of arcs, is + to find a minimal subset of vertices, which being removed from the + graph make it acyclic. + + Reference: + Garey, M.R., and Johnson, D.S. (1979), Computers and Intractability: + A guide to the theory of NP-completeness [Graph Theory, Covering and + Partitioning, Minimum Feedback Vertex Set, GT8]. */ + +param n, integer, >= 0; +/* number of vertices */ + +set V, default 1..n; +/* set of vertices */ + +set E, within V cross V, +default setof{i in V, j in V: i <> j and Uniform(0,1) <= 0.15} (i,j); +/* set of arcs */ + +printf "Graph has %d vertices and %d arcs\n", card(V), card(E); + +var x{i in V}, binary; +/* x[i] = 1 means that i is a feedback vertex */ + +/* It is known that a digraph G = (V, E) is acyclic if and only if its + vertices can be assigned numbers from 1 to |V| in such a way that + k[i] + 1 <= k[j] for every arc (i->j) in E, where k[i] is a number + assigned to vertex i. We may use this condition to require that the + digraph G = (V, E \ E'), where E' is a subset of feedback arcs, is + acyclic. */ + +var k{i in V}, >= 1, <= card(V); +/* k[i] is a number assigned to vertex i */ + +s.t. r{(i,j) in E}: k[j] - k[i] >= 1 - card(V) * (x[i] + x[j]); +/* note that x[i] = 1 or x[j] = 1 leads to a redundant constraint */ + +minimize obj: sum{i in V} x[i]; +/* the objective is to minimize the cardinality of a subset of feedback + vertices */ + +solve; + +printf "Minimum feedback vertex set:\n"; +printf{i in V: x[i]} "%d\n", i; + +data; + +/* The optimal solution is 3 */ + +param n := 15; + +set E := 1 2, 2 3, 3 4, 3 8, 4 9, 5 1, 6 5, 7 5, 8 6, 8 7, 8 9, 9 10, + 10 11, 10 14, 11 15, 12 7, 12 8, 12 13, 13 8, 13 12, 13 14, + 14 9, 15 14; + +end; diff -r d59bea55db9b -r c445c931472f examples/min01ks.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/min01ks.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,111 @@ +/* min01ks.mod - finding minimal equivalent 0-1 knapsack inequality */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* It is obvious that for a given 0-1 knapsack inequality + + a[1] x[1] + ... + a[n] x[n] <= b, x[j] in {0, 1} (1) + + there exist infinitely many equivalent inequalities with exactly the + same feasible solutions. + + Given a[j]'s and b this model allows to find an inequality + + alfa[1] x[1] + ... + alfa[n] x[n] <= beta, x[j] in {0, 1}, (2) + + which is equivalent to (1) and where alfa[j]'s and beta are smallest + non-negative integers. + + This model has the following formulation: + + minimize + + z = |alfa[1]| + ... + |alfa[n]| + |beta| = (3) + + = alfa[1] + ... + alfa[n] + beta + + subject to + + alfa[1] x[1] + ... + alfa[n] x[n] <= beta (4) + + for all x satisfying to (1) + + alfa[1] x[1] + ... + alfa[n] x[n] >= beta + 1 (5) + + for all x not satisfying to (1) + + alfa[1], ..., alfa[n], beta are non-negative integers. + + Note that this model has n+1 variables and 2^n constraints. + + It is interesting, as noticed in [1] and explained in [2], that + in most cases LP relaxation of the MIP formulation above has integer + optimal solution. + + References + + 1. G.H.Bradley, P.L.Hammer, L.Wolsey, "Coefficient Reduction for + Inequalities in 0-1 Variables", Math.Prog.7 (1974), 263-282. + + 2. G.J.Koehler, "A Study on Coefficient Reduction of Binary Knapsack + Inequalities", University of Florida, 2001. */ + +param n, integer, > 0; +/* number of variables in the knapsack inequality */ + +set N := 1..n; +/* set of knapsack items */ + +/* all binary n-vectors are numbered by 0, 1, ..., 2^n-1, where vector + 0 is 00...00, vector 1 is 00...01, etc. */ + +set U := 0..2^n-1; +/* set of numbers of all binary n-vectors */ + +param x{i in U, j in N}, binary, := (i div 2^(j-1)) mod 2; +/* x[i,j] is j-th component of i-th binary n-vector */ + +param a{j in N}, >= 0; +/* original coefficients */ + +param b, >= 0; +/* original right-hand side */ + +set D := setof{i in U: sum{j in N} a[j] * x[i,j] <= b} i; +/* set of numbers of binary n-vectors, which (vectors) are feasible, + i.e. satisfy to the original knapsack inequality (1) */ + +var alfa{j in N}, integer, >= 0; +/* coefficients to be found */ + +var beta, integer, >= 0; +/* right-hand side to be found */ + +minimize z: sum{j in N} alfa[j] + beta; /* (3) */ + +phi{i in D}: sum{j in N} alfa[j] * x[i,j] <= beta; /* (4) */ + +psi{i in U diff D}: sum{j in N} alfa[j] * x[i,j] >= beta + 1; /* (5) */ + +solve; + +printf "\nOriginal 0-1 knapsack inequality:\n"; +for {j in 1..n} printf (if j = 1 then "" else " + ") & "%g x%d", + a[j], j; +printf " <= %g\n", b; +printf "\nMinimized equivalent inequality:\n"; +for {j in 1..n} printf (if j = 1 then "" else " + ") & "%g x%d", + alfa[j], j; +printf " <= %g\n\n", beta; + +data; + +/* These data correspond to the very first example from [1]. */ + +param n := 8; + +param a := [1]65, [2]64, [3]41, [4]22, [5]13, [6]12, [7]8, [8]2; + +param b := 80; + +end; diff -r d59bea55db9b -r c445c931472f examples/misp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/misp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,665 @@ +/* MISP, Maximum Independent Set Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* Let G = (V,E) be an undirected graph with vertex set V and edge set + E. Vertices u, v in V are non-adjacent if (u,v) not in E. A subset + of the vertices S within V is independent if all vertices in S are + pairwise non-adjacent. The Maximum Independent Set Problem (MISP) is + to find an independent set having the largest cardinality. */ + +param n, integer, > 0; +/* number of vertices */ + +set V := 1..n; +/* set of vertices */ + +set E within V cross V; +/* set of edges */ + +var x{i in V}, binary; +/* x[i] = 1 means vertex i belongs to independent set */ + +s.t. edge{(i,j) in E}: x[i] + x[j] <= 1; +/* if there is edge (i,j), vertices i and j cannot belong to the same + independent set */ + +maximize obj: sum{i in V} x[i]; +/* the objective is to maximize the cardinality of independent set */ + +data; + +/* These data corresponds to the test instance from: + + M.G.C. Resende, T.A.Feo, S.H.Smith, "Algorithm 787 -- FORTRAN + subroutines for approximate solution of the maximum independent set + problem using GRASP," Trans. on Math. Softw., Vol. 24, No. 4, + December 1998, pp. 386-394. */ + +/* The optimal solution is 7 */ + +param n := 50; + +set E := + 1 2 + 1 3 + 1 5 + 1 7 + 1 8 + 1 12 + 1 15 + 1 16 + 1 19 + 1 20 + 1 21 + 1 22 + 1 28 + 1 30 + 1 34 + 1 35 + 1 37 + 1 41 + 1 42 + 1 47 + 1 50 + 2 3 + 2 5 + 2 6 + 2 7 + 2 8 + 2 9 + 2 10 + 2 13 + 2 17 + 2 19 + 2 20 + 2 21 + 2 23 + 2 25 + 2 26 + 2 29 + 2 31 + 2 35 + 2 36 + 2 44 + 2 45 + 2 46 + 2 50 + 3 5 + 3 6 + 3 8 + 3 9 + 3 10 + 3 11 + 3 14 + 3 16 + 3 23 + 3 24 + 3 26 + 3 27 + 3 28 + 3 29 + 3 30 + 3 31 + 3 34 + 3 35 + 3 36 + 3 39 + 3 41 + 3 42 + 3 43 + 3 44 + 3 50 + 4 6 + 4 7 + 4 9 + 4 10 + 4 11 + 4 13 + 4 14 + 4 15 + 4 17 + 4 21 + 4 22 + 4 23 + 4 24 + 4 25 + 4 27 + 4 28 + 4 30 + 4 31 + 4 33 + 4 34 + 4 35 + 4 36 + 4 37 + 4 38 + 4 40 + 4 41 + 4 42 + 4 46 + 4 49 + 5 6 + 5 11 + 5 14 + 5 21 + 5 24 + 5 25 + 5 28 + 5 35 + 5 38 + 5 39 + 5 41 + 5 44 + 5 49 + 5 50 + 6 8 + 6 9 + 6 10 + 6 13 + 6 14 + 6 16 + 6 17 + 6 19 + 6 22 + 6 23 + 6 26 + 6 27 + 6 30 + 6 34 + 6 35 + 6 38 + 6 39 + 6 40 + 6 41 + 6 44 + 6 45 + 6 47 + 6 50 + 7 8 + 7 9 + 7 10 + 7 11 + 7 13 + 7 15 + 7 16 + 7 18 + 7 20 + 7 22 + 7 23 + 7 24 + 7 25 + 7 33 + 7 35 + 7 36 + 7 38 + 7 43 + 7 45 + 7 46 + 7 47 + 8 10 + 8 11 + 8 13 + 8 16 + 8 17 + 8 18 + 8 19 + 8 20 + 8 21 + 8 22 + 8 23 + 8 24 + 8 25 + 8 26 + 8 33 + 8 35 + 8 36 + 8 39 + 8 42 + 8 44 + 8 48 + 8 49 + 9 12 + 9 14 + 9 17 + 9 19 + 9 20 + 9 23 + 9 28 + 9 30 + 9 31 + 9 32 + 9 33 + 9 34 + 9 38 + 9 39 + 9 42 + 9 44 + 9 45 + 9 46 + 10 11 + 10 13 + 10 15 + 10 16 + 10 17 + 10 20 + 10 21 + 10 22 + 10 23 + 10 25 + 10 26 + 10 27 + 10 28 + 10 30 + 10 31 + 10 32 + 10 37 + 10 38 + 10 41 + 10 43 + 10 44 + 10 45 + 10 50 + 11 12 + 11 14 + 11 15 + 11 18 + 11 21 + 11 24 + 11 25 + 11 26 + 11 29 + 11 32 + 11 33 + 11 35 + 11 36 + 11 37 + 11 39 + 11 40 + 11 42 + 11 43 + 11 45 + 11 47 + 11 49 + 11 50 + 12 13 + 12 16 + 12 17 + 12 19 + 12 24 + 12 25 + 12 26 + 12 30 + 12 31 + 12 32 + 12 34 + 12 36 + 12 37 + 12 39 + 12 41 + 12 44 + 12 47 + 12 48 + 12 49 + 13 15 + 13 16 + 13 18 + 13 19 + 13 20 + 13 22 + 13 23 + 13 24 + 13 27 + 13 28 + 13 29 + 13 31 + 13 33 + 13 35 + 13 36 + 13 37 + 13 44 + 13 47 + 13 49 + 13 50 + 14 15 + 14 16 + 14 17 + 14 18 + 14 19 + 14 20 + 14 21 + 14 26 + 14 28 + 14 29 + 14 30 + 14 31 + 14 32 + 14 34 + 14 35 + 14 36 + 14 38 + 14 39 + 14 41 + 14 44 + 14 46 + 14 47 + 14 48 + 15 18 + 15 21 + 15 22 + 15 23 + 15 25 + 15 28 + 15 29 + 15 30 + 15 33 + 15 34 + 15 36 + 15 37 + 15 38 + 15 39 + 15 40 + 15 43 + 15 44 + 15 46 + 15 50 + 16 17 + 16 19 + 16 20 + 16 25 + 16 26 + 16 29 + 16 35 + 16 38 + 16 39 + 16 40 + 16 41 + 16 42 + 16 44 + 17 18 + 17 19 + 17 21 + 17 22 + 17 23 + 17 25 + 17 26 + 17 28 + 17 29 + 17 33 + 17 37 + 17 44 + 17 45 + 17 48 + 18 20 + 18 24 + 18 27 + 18 28 + 18 31 + 18 32 + 18 34 + 18 35 + 18 36 + 18 37 + 18 38 + 18 45 + 18 48 + 18 49 + 18 50 + 19 22 + 19 24 + 19 28 + 19 29 + 19 36 + 19 37 + 19 39 + 19 41 + 19 43 + 19 45 + 19 48 + 19 49 + 20 21 + 20 22 + 20 24 + 20 25 + 20 26 + 20 27 + 20 29 + 20 30 + 20 31 + 20 33 + 20 34 + 20 35 + 20 38 + 20 39 + 20 41 + 20 42 + 20 43 + 20 44 + 20 45 + 20 46 + 20 48 + 20 49 + 21 22 + 21 23 + 21 29 + 21 31 + 21 35 + 21 38 + 21 42 + 21 46 + 21 47 + 22 23 + 22 26 + 22 27 + 22 28 + 22 29 + 22 30 + 22 39 + 22 40 + 22 41 + 22 42 + 22 44 + 22 45 + 22 46 + 22 47 + 22 49 + 22 50 + 23 28 + 23 31 + 23 32 + 23 33 + 23 34 + 23 35 + 23 36 + 23 39 + 23 40 + 23 41 + 23 42 + 23 44 + 23 45 + 23 48 + 23 49 + 23 50 + 24 25 + 24 27 + 24 29 + 24 30 + 24 31 + 24 33 + 24 34 + 24 38 + 24 42 + 24 43 + 24 44 + 24 49 + 24 50 + 25 26 + 25 27 + 25 29 + 25 30 + 25 33 + 25 34 + 25 36 + 25 38 + 25 40 + 25 41 + 25 42 + 25 44 + 25 46 + 25 47 + 25 48 + 25 49 + 26 28 + 26 31 + 26 32 + 26 33 + 26 37 + 26 38 + 26 39 + 26 40 + 26 41 + 26 42 + 26 45 + 26 47 + 26 48 + 26 49 + 27 29 + 27 30 + 27 33 + 27 34 + 27 35 + 27 39 + 27 40 + 27 46 + 27 48 + 28 29 + 28 37 + 28 40 + 28 42 + 28 44 + 28 46 + 28 47 + 28 50 + 29 35 + 29 38 + 29 39 + 29 41 + 29 42 + 29 48 + 30 31 + 30 32 + 30 35 + 30 37 + 30 38 + 30 40 + 30 43 + 30 45 + 30 46 + 30 47 + 30 48 + 31 33 + 31 35 + 31 38 + 31 40 + 31 41 + 31 42 + 31 44 + 31 46 + 31 47 + 31 50 + 32 33 + 32 35 + 32 39 + 32 40 + 32 46 + 32 49 + 32 50 + 33 34 + 33 36 + 33 37 + 33 40 + 33 42 + 33 43 + 33 44 + 33 45 + 33 50 + 34 35 + 34 36 + 34 37 + 34 38 + 34 40 + 34 43 + 34 44 + 34 49 + 34 50 + 35 36 + 35 38 + 35 41 + 35 42 + 35 43 + 35 45 + 35 46 + 35 47 + 35 49 + 35 50 + 36 37 + 36 39 + 36 40 + 36 41 + 36 42 + 36 43 + 36 48 + 36 50 + 37 38 + 37 41 + 37 43 + 37 46 + 37 47 + 37 48 + 37 49 + 37 50 + 38 41 + 38 45 + 38 46 + 38 48 + 38 49 + 38 50 + 39 43 + 39 46 + 39 47 + 39 48 + 39 49 + 40 43 + 40 45 + 40 48 + 40 50 + 41 42 + 41 43 + 41 44 + 41 45 + 41 46 + 41 48 + 41 49 + 42 43 + 42 44 + 42 46 + 42 48 + 42 49 + 43 45 + 43 46 + 43 48 + 43 50 + 44 45 + 44 48 + 45 46 + 45 47 + 45 48 + 46 49 + 47 49 + 47 50 + 48 49 + 48 50 + 49 50 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/money.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/money.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,62 @@ +/* MONEY, a crypto-arithmetic puzzle */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* This is the classic example of a crypto-arithmetic puzzle published + in the Strand Magazine by Henry Dudeney: + + S E N D + + + M O R E + --------- + M O N E Y + + In this puzzle the same letters mean the same digits. The question + is: how to replace all the letters with the respective digits that + makes the calculation correct? + + The solution to this puzzle is: + O = 0, M = 1, Y = 2, E = 5, N = 6, D = 7, R = 8, and S = 9. + + References: + H. E. Dudeney, in Strand Magazine vol. 68 (July 1924), pp. 97, 214. + + (From Wikipedia, the free encyclopedia.) */ + +set LETTERS := { 'D', 'E', 'M', 'N', 'O', 'R', 'S', 'Y' }; +/* set of letters */ + +set DIGITS := 0..9; +/* set of digits */ + +var x{i in LETTERS, d in DIGITS}, binary; +/* x[i,d] = 1 means that letter i is digit d */ + +s.t. one{i in LETTERS}: sum{d in DIGITS} x[i,d] = 1; +/* each letter must correspond exactly to one digit */ + +s.t. alldiff{d in DIGITS}: sum{i in LETTERS} x[i,d] <= 1; +/* different letters must correspond to different digits; note that + some digits may not correspond to any letters at all */ + +var dig{i in LETTERS}; +/* dig[i] is a digit corresponding to letter i */ + +s.t. map{i in LETTERS}: dig[i] = sum{d in DIGITS} d * x[i,d]; + +var carry{1..3}, binary; +/* carry bits */ + +s.t. sum1: dig['D'] + dig['E'] = dig['Y'] + 10 * carry[1]; +s.t. sum2: dig['N'] + dig['R'] + carry[1] = dig['E'] + 10 * carry[2]; +s.t. sum3: dig['E'] + dig['O'] + carry[2] = dig['N'] + 10 * carry[3]; +s.t. sum4: dig['S'] + dig['M'] + carry[3] = dig['O'] + 10 * dig['M']; +s.t. note: dig['M'] >= 1; /* M must not be 0 */ + +solve; +/* solve the puzzle */ + +display dig; +/* and display its solution */ + +end; diff -r d59bea55db9b -r c445c931472f examples/mplsamp1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mplsamp1.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,32 @@ +/* mplsamp1.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *lp; + glp_tran *tran; + int ret; + lp = glp_create_prob(); + tran = glp_mpl_alloc_wksp(); + ret = glp_mpl_read_model(tran, "egypt.mod", 0); + if (ret != 0) + { fprintf(stderr, "Error on translating model\n"); + goto skip; + } + ret = glp_mpl_generate(tran, NULL); + if (ret != 0) + { fprintf(stderr, "Error on generating model\n"); + goto skip; + } + glp_mpl_build_prob(tran, lp); + ret = glp_write_mps(lp, GLP_MPS_FILE, NULL, "egypt.mps"); + if (ret != 0) + fprintf(stderr, "Error on writing MPS file\n"); +skip: glp_mpl_free_wksp(tran); + glp_delete_prob(lp); + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/mplsamp2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mplsamp2.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,39 @@ +/* mplsamp2.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *mip; + glp_tran *tran; + int ret; + mip = glp_create_prob(); + tran = glp_mpl_alloc_wksp(); + ret = glp_mpl_read_model(tran, "sudoku.mod", 1); + if (ret != 0) + { fprintf(stderr, "Error on translating model\n"); + goto skip; + } + ret = glp_mpl_read_data(tran, "sudoku.dat"); + if (ret != 0) + { fprintf(stderr, "Error on translating data\n"); + goto skip; + } + ret = glp_mpl_generate(tran, NULL); + if (ret != 0) + { fprintf(stderr, "Error on generating model\n"); + goto skip; + } + glp_mpl_build_prob(tran, mip); + glp_simplex(mip, NULL); + glp_intopt(mip, NULL); + ret = glp_mpl_postsolve(tran, mip, GLP_MIP); + if (ret != 0) + fprintf(stderr, "Error on postsolving model\n"); +skip: glp_mpl_free_wksp(tran); + glp_delete_prob(mip); + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/murtagh.mps --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/murtagh.mps Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,600 @@ +*NAME: OIL +*ROWS: 74 +*COLUMNS: 81 +*NONZERO: 504 +*OPT SOLN: 126.057 +*SOURCE: Bruce Murtagh, "Advanced Linear Programming" +*APPLICATION: oil refinery model +*COMMENTS: problem is maximization +* +NAME OIL REFINERY EXAMPLE +ROWS + N PROFIT + L MVOLBOL + L MVOLCOL + E MVOLLNC + E MVOLLNB + E MVOLSRK + E MVOLSRD + E MVOLVBB + E MVOLVBC + E MVOLRCR + E MVOLHVO + E UBALKWH + E UBALH2O + E UBALSTM + E UBALFUL + E MVOLB95 + E MVOLB90 + E MVOLLHG + E MVOLC3S + E MVOLNC4 + E MVOLLSR + E MVOLHSR + E MVOLIC4 + L VCAPSGP + E MVOLRFG + E MSCFHYL + E MVOLR90 + E MVOLR95 + E MVOLF90 + E MVOLF95 + L VCAPRFG + E MVOLLCO + E MVOLHHG + E MVOLHCD + L VCAPHVO + L VCAPHOL + E MVOLC3U + E MVOLC4U + E MVOLFCG + E MVOLSLR + L VCAPCCU + E MVOLLA3 + E MVOLLA4 + L VCAPALK + L XLPRPRE + L XHPRPRE + L XTELPRE + L XRVPPRE + L X200PRE + L X230PRE + E EVOLPRE + L XPSCPRE + L XRSCREG + L XLPRINT + L XHPRINT + L XTELINT + L XRVPINT + L X200INT + L X230INT + E EVOLINT + L XLPRREG + L XHPRREG + L XTELREG + L XRVPREG + L X200REG + L X230REG + E EVOLREG + E EVOLLPG + E EVOLJP4 + L XRVXJP4 + L XRVNJP4 + E EVOLDSL + E EVOLRSD + L XVISRSD +COLUMNS + VCRDBOL MVOLBOL 1.0 + VCRDBOL MVOLLNB -.537 + VCRDBOL MVOLSRK -.131 + VCRDBOL MVOLSRD -.1155 + VCRDBOL MVOLVBB -.037 + VCRDBOL MVOLRCR -.0365 + VCRDBOL MVOLHVO -.143 + VCRDBOL UBALKWH .302 + VCRDBOL UBALH2O .150 + VCRDBOL UBALSTM .003 + VCRDBOL UBALFUL .0587 + VCRDBOL PROFIT -12.8 + VCRDCOL MVOLCOL 1. + VCRDCOL MVOLLNC -.2931 + VCRDCOL MVOLSRK -.1170 + VCRDCOL MVOLSRD -.0649 + VCRDCOL MVOLVBC -.18 + VCRDCOL MVOLRCR -.1233 + VCRDCOL MVOLHVO -.2217 + VCRDCOL UBALKWH .384 + VCRDCOL UBALH2O .185 + VCRDCOL UBALSTM .003 + VCRDCOL UBALFUL .1053 + VCRDCOL PROFIT -11.48 + VSGPLNC MVOLLNC 1. + VSGPLNC MVOLC3S -.0112 + VSGPLNC MVOLNC4 -.0378 + VSGPLNC MVOLLSR -.1502 + VSGPLNC MVOLHSR -.7953 + VSGPLNC MVOLIC4 -.0099 + VSGPLNC UBALKWH .721 + VSGPLNC UBALH2O .185 + VSGPLNC UBALSTM .013 + VSGPLNC UBALFUL .0488 + VSGPLNC VCAPSGP 1. + VSGPLNB MVOLLNB 1. + VSGPLNB MVOLC3S -.0277 + VSGPLNB MVOLNC4 -.0563 + VSGPLNB MVOLLSR -.199 + VSGPLNB MVOLHSR -.6873 + VSGPLNB MVOLIC4 -.017 + VSGPLNB UBALKWH .495 + VSGPLNB UBALH2O .209 + VSGPLNB UBALSTM .013 + VSGPLNB UBALFUL .0506 + VSGPLNB VCAPSGP 1. + VSGPLHG MVOLLHG 1.0 + VSGPLHG MVOLC3S -.175 + VSGPLHG MVOLNC4 -.27 + VSGPLHG MVOLLSR -.028 + VSGPLHG MVOLIC4 -.455 + VSGPLHG UBALKWH .495 + VSGPLHG UBALH2O .209 + VSGPLHG UBALSTM .013 + VSGPLHG UBALFUL .0448 + VSGPB95 MVOLB95 1. + VSGPB95 MVOLC3S -.2836 + VSGPB95 MVOLNC4 -.3285 + VSGPB95 MVOLLSR -.0241 + VSGPB95 MVOLIC4 -.2502 + VSGPB95 UBALKWH .495 + VSGPB95 UBALH2O .209 + VSGPB95 UBALSTM .013 + VSGPB95 UBALFUL .0506 + VSGPB90 MVOLB90 1. + VSGPB90 MVOLC3S -.271 + VSGPB90 MVOLNC4 -.3289 + VSGPB90 MVOLLSR -.0255 + VSGPB90 MVOLIC4 -.2656 + VSGPB90 UBALKWH .495 + VSGPB90 UBALH2O .209 + VSGPB90 UBALSTM .013 + VSGPB90 UBALFUL .0506 + VH2RHSR MVOLHSR 1. + VH2RHSR MVOLRFG -1. + VH2RHSR MSCFHYL .0327 + VH2RHSR UBALKWH .793 + VH2RHSR UBALH2O .045 + VH2RHSR UBALFUL .094 + VH2RHSR PROFIT -.0176 + VRFFRF1 MVOLRFG 1.0 + VRFFRF1 MVOLR90 -1.0 + VRFFRF2 MVOLRFG 1.0 + VRFFRF2 MVOLR95 -1.0 + VRFFHH1 MVOLR90 -1.0 + VRFFHH1 MVOLHHG 1.0 + VRFFHH2 MVOLR95 -1.0 + VRFFHH2 MVOLHHG 1.0 + VRFGR90 MVOLR90 1.0 + VRFGR90 MVOLB90 -.0404 + VRFGR90 MVOLF90 -0.8564 + VRFGR90 MSCFHYL -0.8239 + VRFGR90 UBALKWH .792 + VRFGR90 UBALH2O .297 + VRFGR90 UBALSTM 0.0063 + VRFGR90 UBALFUL -0.156 + VRFGR90 VCAPRFG 1.0 + VRFGR90 PROFIT -0.1512 + VRFGR95 MVOLR95 1.0 + VRFGR95 MVOLB95 -0.0588 + VRFGR95 MVOLF95 -0.8145 + VRFGR95 MSCFHYL -.7689 + VRFGR95 UBALKWH 1.03 + VRFGR95 UBALH2O .387 + VRFGR95 UBALSTM 0.008 + VRFGR95 UBALFUL -.2112 + VRFGR95 VCAPRFG 1.3 + VRFGR95 PROFIT -0.304 + VHOLLCO MVOLLCO 1.0 + VHOLLCO MVOLHHG -.6627 + VHOLLCO MVOLLHG -0.2414 + VHOLLCO MVOLHCD -.2930 + VHOLLCO MSCFHYL 2.3 + VHOLLCO UBALFUL -.2054 + VHOLLCO UBALH2O 0.826 + VHOLLCO UBALKWH 14.61 + VHOLLCO VCAPHOL 1.0 + VHOLLCO PROFIT -0.2112 + VHOLSRD MVOLSRD 1.0 + VHOLSRD MVOLHHG -.6627 + VHOLSRD MVOLLHG -0.2414 + VHOLSRD MVOLHCD -.2930 + VHOLSRD MSCFHYL 2.3 + VHOLSRD UBALFUL -.2054 + VHOLSRD UBALH2O 0.826 + VHOLSRD UBALKWH 14.61 + VHOLSRD VCAPHOL 1.0 + VHOLSRD PROFIT -0.2112 + VHOLRCR MVOLRCR 1.0 + VHOLRCR MVOLHHG -.5875 + VHOLRCR MVOLLHG -0.3321 + VHOLRCR MVOLHCD -.3620 + VHOLRCR MSCFHYL 2.3 + VHOLRCR UBALFUL -.2054 + VHOLRCR UBALH2O 0.826 + VHOLRCR UBALKWH 14.61 + VHOLRCR VCAPHOL 1.0 + VHOLRCR PROFIT -0.2112 + VHOLHVO MVOLHVO 1.0 + VHOLHVO MVOLHHG -.5875 + VHOLHVO MVOLLHG -0.3321 + VHOLHVO MVOLHCD -.3620 + VHOLHVO MSCFHYL 2.3 + VHOLHVO UBALFUL -.2054 + VHOLHVO UBALH2O 0.826 + VHOLHVO UBALKWH 14.61 + VHOLHVO VCAPHVO 1.0 + VHOLHVO VCAPHOL 1.0 + VHOLHVO PROFIT -0.2112 + VCCUSRK MVOLSRK 1.0 + VCCUSRK MVOLNC4 -0.0184 + VCCUSRK MVOLC3S -0.0303 + VCCUSRK MVOLIC4 -0.0564 + VCCUSRK MVOLC3U -0.0655 + VCCUSRK MVOLC4U -0.0780 + VCCUSRK MVOLFCG -0.4750 + VCCUSRK MVOLLCO -0.3050 + VCCUSRK UBALSTM -.0654 + VCCUSRK UBALFUL -.2703 + VCCUSRK UBALH2O .632 + VCCUSRK UBALKWH .6807 + VCCUSRK VCAPCCU 1. + VCCUSRK PROFIT -.2112 + VCCUSRD MVOLSRD 1. + VCCUSRD MVOLNC4 -.0184 + VCCUSRD MVOLC3S -.0303 + VCCUSRD MVOLIC4 -.0564 + VCCUSRD MVOLC3U -.0655 + VCCUSRD MVOLC4U -.0780 + VCCUSRD MVOLFCG -.4750 + VCCUSRD MVOLLCO -.3050 + VCCUSRD UBALSTM -.0654 + VCCUSRD UBALFUL -.2703 + VCCUSRD UBALH2O 0.632 + VCCUSRD UBALKWH .6807 + VCCUSRD VCAPCCU 1. + VCCUSRD PROFIT -.2112 + VCCURCR MVOLRCR 1.0 + VCCURCR MVOLNC4 -.0185 + VCCURCR MVOLC3S -.0328 + VCCURCR MVOLIC4 -.0568 + VCCURCR MVOLC3U -.0658 + VCCURCR MVOLC4U -.0806 + VCCURCR MVOLFCG -.4934 + VCCURCR MVOLLCO -.2922 + VCCURCR MVOLSLR -.0096 + VCCURCR UBALSTM -.0654 + VCCURCR UBALFUL -.2703 + VCCURCR UBALH2O 0.632 + VCCURCR UBALKWH .6807 + VCCURCR VCAPCCU 1. + VCCURCR PROFIT -.2112 + VCCUHVO MVOLHVO 1.0 + VCCUHVO MVOLNC4 -.0185 + VCCUHVO MVOLC3S -.0328 + VCCUHVO MVOLIC4 -.0568 + VCCUHVO MVOLC3U -.0658 + VCCUHVO MVOLC4U -.0806 + VCCUHVO MVOLFCG -.4934 + VCCUHVO MVOLLCO -.2922 + VCCUHVO MVOLSLR -.0096 + VCCUHVO UBALSTM -.0654 + VCCUHVO UBALFUL -.2703 + VCCUHVO UBALH2O 0.632 + VCCUHVO UBALKWH .6807 + VCCUHVO VCAPHVO 1. + VCCUHVO VCAPCCU 1. + VCCUHVO PROFIT -.2112 + VALKLA3 MVOLIC4 .7600 + VALKLA3 MVOLC3U .5714 + VALKLA3 MVOLLA3 -1.0 + VALKLA3 UBALSTM .1869 + VALKLA3 UBALFUL .2796 + VALKLA3 UBALH2O 2.241 + VALKLA3 UBALKWH 2.766 + VALKLA3 VCAPALK 1.0 + VALKLA3 PROFIT -.512 + VALKLA4 MVOLIC4 .6571 + VALKLA4 MVOLC4U .5714 + VALKLA4 MVOLC3S -.0571 + VALKLA4 MVOLNC4 -.0114 + VALKLA4 MVOLLA4 -1.0 + VALKLA4 UBALSTM .1724 + VALKLA4 UBALFUL .2579 + VALKLA4 UBALH2O 2.067 + VALKLA4 UBALKWH 2.552 + VALKLA4 VCAPALK 1.0 + VALKLA4 PROFIT -.472 + VALKIC4 MVOLIC4 1.0 + VALKIC4 MVOLNC4 -1.0 + VALKC3U MVOLC3U 1.0 + VALKC3U MVOLC3S -1.0 + VALKC4U MVOLC4U 1.0 + VALKC4U MVOLNC4 -1.0 + UTILC3S MVOLC3S 1. + UTILC3S UBALFUL -3.814 + UTILNC4 MVOLNC4 1. + UTILNC4 UBALFUL -4.316 + UTILIC4 MVOLIC4 1. + UTILIC4 UBALFUL -4.153 + UTILC3U MVOLC3U 1. + UTILC3U UBALFUL -3.808 + UTILC4U MVOLC4U 1. + UTILC4U UBALFUL -4.44 + UTILHYL MSCFHYL 1. + UTILHYL UBALFUL -.305 + UTILSTM UBALSTM -1. + UTILSTM UBALFUL 1.42 + UTILSTM PROFIT -.16 + PURCPC4 MVOLIC4 -.5 + PURCPC4 MVOLNC4 -.5 + PURCPC4 PROFIT -12. + PURCH2O UBALH2O -1. + PURCH2O PROFIT -.0528 + PURCKWH UBALKWH -1. + PURCKWH PROFIT -.04 + PURCFUL UBALFUL -1. + PURCFUL PROFIT -1.6 + PURCFLR UBALFUL 1. + BLPGC3S MVOLC3S 1.0 + BLPGC3S EVOLLPG -1.0 + BLPGNC4 MVOLNC4 1.0 + BLPGNC4 EVOLLPG -1.0 + SELLLPG EVOLLPG 1.0 + SELLLPG PROFIT 11.0 + BUP4LSR MVOLLSR 1.0 + BUP4LSR EVOLJP4 -1.0 + BUP4LSR XRVXJP4 14.0 + BUP4LSR XRVNJP4 -14.0 + BUP4HSR MVOLHSR 1.0 + BUP4HSR EVOLJP4 -1.0 + BUP4HSR XRVXJP4 0.8 + BUP4HSR XRVNJP4 -0.8 + SELLJP4 EVOLJP4 1.0 + SELLJP4 XRVXJP4 -3.0 + SELLJP4 XRVNJP4 2.0 + SELLJP4 PROFIT 16.8 + BDSLSRK MVOLSRK 1.0 + BDSLSRK EVOLDSL -1.0 + BDSLSRD MVOLSRD 1.0 + BDSLSRD EVOLDSL -1.0 + SELLDSL EVOLDSL 1.0 + SELLDSL PROFIT 14.4 + BPRELSR MVOLLSR 1. + BPRELSR XLPRPRE -7.95 + BPRELSR XHPRPRE -8.70 + BPRELSR XTELPRE -3.00 + BPRELSR XRVPPRE 14.00 + BPRELSR X200PRE 1. + BPRELSR X230PRE -1. + BPRELSR EVOLPRE -1. + BPREHCD MVOLHCD 1.0 + BPREHCD XLPRPRE -8.84 + BPREHCD XHPRPRE -9.45 + BPREHCD XTELPRE -3.00 + BPREHCD XRVPPRE 12.00 + BPREHCD X200PRE 1. + BPREHCD X230PRE -1. + BPREHCD EVOLPRE -1. + BPREF95 MVOLF95 1.0 + BPREF95 XLPRPRE -9.43 + BPREF95 XHPRPRE -9.57 + BPREF95 XTELPRE -3. + BPREF95 XRVPPRE 3.5 + BPREF95 X200PRE .233 + BPREF95 X230PRE -.358 + BPREF95 EVOLPRE -1. + BPREF90 MVOLF90 1.0 + BPREF90 XLPRPRE -9.03 + BPREF90 XHPRPRE -9.32 + BPREF90 XTELPRE -3.0 + BPREF90 XRVPPRE 3.5 + BPREF90 X200PRE .205 + BPREF90 X230PRE -.333 + BPREF90 EVOLPRE -1. + BPREFCG MVOLFCG 1.0 + BPREFCG XLPRPRE -9.23 + BPREFCG XHPRPRE -9.22 + BPREFCG XTELPRE -3. + BPREFCG XRVPPRE 6. + BPREFCG X200PRE .381 + BPREFCG X230PRE -.509 + BPREFCG EVOLPRE -1. + BPRELA3 MVOLLA3 1.0 + BPRELA3 XLPRPRE -9.4 + BPRELA3 XHPRPRE -9.85 + BPRELA3 XTELPRE -3.0 + BPRELA3 XRVPPRE 2.5 + BPRELA3 X200PRE 0.39 + BPRELA3 X230PRE -0.77 + BPRELA3 EVOLPRE -1.0 + BPRELA4 MVOLLA4 1.0 + BPRELA4 XLPRPRE -9.74 + BPRELA4 XHPRPRE -10.1 + BPRELA4 XTELPRE -3.0 + BPRELA4 XRVPPRE 3.3 + BPRELA4 X200PRE 0.233 + BPRELA4 X230PRE -0.58 + BPRELA4 EVOLPRE -1.0 + BPRENC4 MVOLNC4 1.0 + BPRENC4 XLPRPRE -9.74 + BPRENC4 XHPRPRE -9.9 + BPRENC4 XTELPRE -3.0 + BPRENC4 XRVPPRE 66.0 + BPRENC4 X200PRE 1.0 + BPRENC4 X230PRE -1.0 + BPRENC4 EVOLPRE -1.0 + BPRETEL XLPRPRE -0.493 + BPRETEL XHPRPRE -0.165 + BPRETEL XTELPRE 1.0 + BPRETEL PROFIT -0.3696 + SELLPRE XLPRPRE 10.03 + SELLPRE XHPRPRE 10.03 + SELLPRE XRVPPRE -9.5 + SELLPRE X200PRE -0.5 + SELLPRE X230PRE 0.5 + SELLPRE XPSCPRE 0.64 + SELLPRE XRSCREG 0.35 + SELLPRE EVOLPRE 1.0 + SELLPRE PROFIT 21.44 + BINTLSR MVOLLSR 1.0 + BINTLSR XLPRINT -7.98 + BINTLSR XHPRINT -8.58 + BINTLSR XTELINT -3.0 + BINTLSR XRVPINT 14.0 + BINTLSR X200INT 1.0 + BINTLSR X230INT -1.0 + BINTLSR EVOLINT -1.0 + BINTHCD MVOLHCD 1. + BINTHCD XLPRINT -8.87 + BINTHCD XHPRINT -9.33 + BINTHCD XTELINT -3.0 + BINTHCD XRVPINT 12.0 + BINTHCD X200INT 1.0 + BINTHCD X230INT -1. + BINTHCD EVOLINT -1.0 + BINTF95 MVOLF95 1. + BINTF95 XLPRINT -9.46 + BINTF95 XHPRINT -9.45 + BINTF95 XTELINT -3.0 + BINTF95 XRVPINT 3.5 + BINTF95 X200INT .233 + BINTF95 X230INT -.358 + BINTF95 EVOLINT -1.0 + BINTF90 MVOLF90 1. + BINTF90 XLPRINT -9.06 + BINTF90 XHPRINT -9.20 + BINTF90 XTELINT -3.0 + BINTF90 XRVPINT 3.5 + BINTF90 X200INT .205 + BINTF90 X230INT -.333 + BINTF90 EVOLINT -1.0 + BINTFCG MVOLFCG 1. + BINTFCG XLPRINT -9.26 + BINTFCG XHPRINT -9.13 + BINTFCG XTELINT -3.0 + BINTFCG XRVPINT 6. + BINTFCG X200INT .318 + BINTFCG X230INT -.509 + BINTFCG EVOLINT -1.0 + BINTNC4 MVOLNC4 1. + BINTNC4 XLPRINT -9.77 + BINTNC4 XHPRINT -9.78 + BINTNC4 XTELINT -3.0 + BINTNC4 XRVPINT 66. + BINTNC4 X200INT 1.0 + BINTNC4 X230INT -1. + BINTNC4 EVOLINT -1.0 + BINTTEL XLPRINT -.435 + BINTTEL XHPRINT -.208 + BINTTEL XTELINT 1. + BINTTEL PROFIT -.3696 + SELLINT XLPRINT 9.65 + SELLINT XHPRINT 9.65 + SELLINT XRVPINT -9.5 + SELLINT X200INT -0.5 + SELLINT X230INT 0.5 + SELLINT XPSCPRE -.36 + SELLINT XRSCREG 0.35 + SELLINT EVOLINT 1.0 + SELLINT PROFIT 20.32 + BREGLSR MVOLLSR 1.0 + BREGLSR XLPRREG -7.99 + BREGLSR XHPRREG -8.59 + BREGLSR XTELREG -3.0 + BREGLSR XRVPREG 14.0 + BREGLSR X200REG 1.0 + BREGLSR X230REG -1.0 + BREGLSR EVOLREG -1.0 + BREGHCD MVOLHCD 1.0 + BREGHCD XLPRREG -8.88 + BREGHCD XHPRREG -9.34 + BREGHCD XTELREG -3.0 + BREGHCD XRVPREG 12.0 + BREGHCD X200REG 1.0 + BREGHCD X230REG -1.0 + BREGHCD EVOLREG -1.0 + BREGF95 MVOLF95 1.0 + BREGF95 XLPRREG -9.47 + BREGF95 XHPRREG -9.46 + BREGF95 XTELREG -3.0 + BREGF95 XRVPREG 3.5 + BREGF95 X200REG .233 + BREGF95 X230REG -0.358 + BREGF95 EVOLREG -1.0 + BREGF90 MVOLF90 1.0 + BREGF90 XLPRREG -9.07 + BREGF90 XHPRREG -9.21 + BREGF90 XTELREG -3.0 + BREGF90 XRVPREG 3.5 + BREGF90 X200REG .205 + BREGF90 X230REG -0.333 + BREGF90 EVOLREG -1.0 + BREGFCG MVOLFCG 1.0 + BREGFCG XLPRREG -9.27 + BREGFCG XHPRREG -9.14 + BREGFCG XTELREG -3.0 + BREGFCG XRVPREG 6.0 + BREGFCG X200REG 0.318 + BREGFCG X230REG -0.509 + BREGFCG EVOLREG -1.0 + BREGNC4 MVOLNC4 1.0 + BREGNC4 XLPRREG -9.78 + BREGNC4 XHPRREG -9.79 + BREGNC4 XTELREG -3.0 + BREGNC4 XRVPREG 66.0 + BREGNC4 X200REG 1.0 + BREGNC4 X230REG -1.0 + BREGNC4 EVOLREG -1.0 + BREGTEL XLPRREG -0.426 + BREGTEL XHPRREG -.204 + BREGTEL XTELREG 1.0 + BREGTEL PROFIT -0.3696 + SELLREG XLPRREG 9.05 + SELLREG XHPRREG 9.05 + SELLREG XRVPREG -9.5 + SELLREG X200REG -0.5 + SELLREG X230REG 0.5 + SELLREG XPSCPRE -0.36 + SELLREG XRSCREG -0.65 + SELLREG EVOLREG 1.0 + SELLREG PROFIT 18.04 + BRSDVBB MVOLVBB 1.0 + BRSDVBB EVOLRSD -1.0 + BRSDVBB XVISRSD 10.1 + BRSDVBC MVOLVBC 1.0 + BRSDVBC EVOLRSD -1.0 + BRSDVBC XVISRSD 12.63 + BRSDRCR MVOLRCR 1.0 + BRSDRCR EVOLRSD -1.0 + BRSDRCR XVISRSD 6.9 + BRSDHVO MVOLHVO 1.0 + BRSDHVO EVOLRSD -1.0 + BRSDHVO XVISRSD 8.05 + BRSDHVO VCAPHVO 1.0 + BRSDSLR MVOLSLR 1.0 + BRSDSLR EVOLRSD -1.0 + BRSDSLR XVISRSD 8.05 + BRSDLCO MVOLLCO 1.0 + BRSDLCO EVOLRSD -1.0 + BRSDLCO XVISRSD 4.4 + SELLRSD EVOLRSD 1.0 + SELLRSD XVISRSD -10.1 + SELLRSD PROFIT 8.00 +RHS + LIMITMAX MVOLBOL 26.316 + LIMITMAX MVOLCOL 21.052 + LIMITMAX VCAPSGP 23.25 + LIMITMAX VCAPHVO 5.25 + LIMITMAX VCAPRFG 13.455 + LIMITMAX VCAPHOL 3.87 + LIMITMAX VCAPCCU 7.26 + LIMITMAX VCAPALK 10. +ENDATA diff -r d59bea55db9b -r c445c931472f examples/mvcp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mvcp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,43 @@ +/* MVCP, Minimum Vertex Cover Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Minimum Vertex Cover Problem in a network G = (V, E), where V + is a set of nodes, E is a set of arcs, is to find a subset V' within + V such that each edge (i,j) in E has at least one its endpoint in V' + and which minimizes the sum of node weights w(i) over V'. + + Reference: + Garey, M.R., and Johnson, D.S. (1979), Computers and Intractability: + A guide to the theory of NP-completeness [Graph Theory, Covering and + Partitioning, Minimum Vertex Cover, GT1]. */ + +set E, dimen 2; +/* set of edges */ + +set V := (setof{(i,j) in E} i) union (setof{(i,j) in E} j); +/* set of nodes */ + +param w{i in V}, >= 0, default 1; +/* w[i] is weight of vertex i */ + +var x{i in V}, binary; +/* x[i] = 1 means that node i is included into V' */ + +s.t. cov{(i,j) in E}: x[i] + x[j] >= 1; +/* each edge (i,j) must have node i or j (or both) in V' */ + +minimize z: sum{i in V} w[i] * x[i]; +/* we need to minimize the sum of node weights over V' */ + +data; + +/* These data correspond to an example from [Papadimitriou]. */ + +/* Optimal solution is 6 (greedy heuristic gives 13) */ + +set E := a1 b1, b1 c1, a1 b2, b2 c2, a2 b3, b3 c3, a2 b4, b4 c4, a3 b5, + b5 c5, a3 b6, b6 c6, a4 b1, a4 b2, a4 b3, a5 b4, a5 b5, a5 b6, + a6 b1, a6 b2, a6 b3, a6 b4, a7 b2, a7 b3, a7 b4, a7 b5, a7 b6; + +end; diff -r d59bea55db9b -r c445c931472f examples/netgen.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/netgen.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,141 @@ +/* netgen.c */ + +/* This main program generates 50 original NETGEN instances of the + minimum cost flow problem and writes them in DIMACS format to the + current directory. */ + +#include +#include +#include +#include + +static int parm[50][15] = +{ {13502460, 101, + 5000, 2500, 2500, 25000, 1, 100, 250000, 0, 0, 0, 100, 1, 1000, + },{4281922, 102, + 5000, 2500, 2500, 25000, 1, 100, 2500000, 0, 0, 0, 100, 1, 1000, + },{44820113, 103, + 5000, 2500, 2500, 25000, 1, 100, 6250000, 0, 0, 0, 100, 1, 1000, + },{13450451, 104, + 5000, 2500, 2500, 25000, -100, -1, 250000, 0, 0, 0, 100, 1, 1000, + },{14719436, 105, + 5000, 2500, 2500, 25000, 101, 200, 250000, 0, 0, 0, 100, 1, 1000, + },{17365786, 106, + 5000, 2500, 2500, 12500, 1, 100, 125000, 0, 0, 0, 100, 1, 1000, + },{19540113, 107, + 5000, 2500, 2500, 37500, 1, 100, 375000, 0, 0, 0, 100, 1, 1000, + },{19560313, 108, + 5000, 2500, 2500, 50000, 1, 100, 500000, 0, 0, 0, 100, 1, 1000, + },{2403509, 109, + 5000, 2500, 2500, 75000, 1, 100, 750000, 0, 0, 0, 100, 1, 1000, + },{92480414, 110, + 5000, 2500, 2500, 12500, 1, 100, 250000, 0, 0, 0, 100, 1, 1000, + },{4230140, 111, + 5000, 2500, 2500, 37500, 1, 100, 250000, 0, 0, 0, 100, 1, 1000, + },{10032490, 112, + 5000, 2500, 2500, 50000, 1, 100, 250000, 0, 0, 0, 100, 1, 1000, + },{17307474, 113, + 5000, 2500, 2500, 75000, 1, 100, 250000, 0, 0, 0, 100, 1, 1000, + },{4925114, 114, + 5000, 500, 4500, 25000, 1, 100, 250000, 0, 0, 0, 100, 1, 1000, + },{19842704, 115, + 5000, 1500, 3500, 25000, 1, 100, 250000, 0, 0, 0, 100, 1, 1000, + },{88392060, 116, + 5000, 2500, 2500, 25000, 1, 100, 250000, 0, 0, 0, 0, 1, 1000, + },{12904407, 117, + 5000, 2500, 2500, 12500, 1, 100, 125000, 0, 0, 0, 0, 1, 1000, + },{11811811, 118, + 5000, 2500, 2500, 37500, 1, 100, 375000, 0, 0, 0, 0, 1, 1000, + },{90023593, 119, + 5000, 2500, 2500, 50000, 1, 100, 500000, 0, 0, 0, 0, 1, 1000, + },{93028922, 120, + 5000, 2500, 2500, 75000, 1, 100, 750000, 0, 0, 0, 0, 1, 1000, + },{72707401, 121, + 5000, 50, 50, 25000, 1, 100, 250000, 50, 50, 0, 100, 1, 1000, + },{93040771, 122, + 5000, 250, 250, 25000, 1, 100, 250000, 250, 250, 0, 100, 1, 1000, + },{70220611, 123, + 5000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{52774811, 124, + 5000, 1000, 1000, 25000, 1, 100, 250000, 1000, 1000, 0, 100, 1, + 1000, + },{22492311, 125, + 5000, 1500, 1500, 25000, 1, 100, 250000, 1500, 1500, 0, 100, 1, + 1000, + },{35269337, 126, + 5000, 500, 500, 12500, 1, 100, 125000, 500, 500, 0, 100, 1, 1000, + },{30140502, 127, + 5000, 500, 500, 37500, 1, 100, 375000, 500, 500, 0, 100, 1, 1000, + },{49205455, 128, + 5000, 500, 500, 50000, 1, 100, 500000, 500, 500, 0, 100, 1, 1000, + },{42958341, 129, + 5000, 500, 500, 75000, 1, 100, 750000, 500, 500, 0, 100, 1, 1000, + },{25440925, 130, + 5000, 500, 500, 12500, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{75294924, 131, + 5000, 500, 500, 37500, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{4463965, 132, + 5000, 500, 500, 50000, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{13390427, 133, + 5000, 500, 500, 75000, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{95250971, 134, + 1000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{54830522, 135, + 2500, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{520593, 136, + 7500, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{52900925, 137, + 10000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 1000, + },{22603395, 138, + 5000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 50, + },{55253099, 139, + 5000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 250, + },{75357001, 140, + 5000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 500, + },{10072459, 141, + 5000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 2500, + },{55728492, 142, + 5000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 100, 1, 5000, + },{593043, 143, + 5000, 500, 500, 25000, 1, 100, 250000, 500, 500, 0, 0, 1, 1000, + },{94236572, 144, + 5000, 500, 500, 25000, 1, 10, 250000, 500, 500, 0, 100, 1, 1000, + },{94882955, 145, + 5000, 500, 500, 25000, 1, 1000, 250000, 500, 500, 0, 100, 1, 1000, + },{48489922, 146, + 5000, 500, 500, 25000, 1, 10000, 250000, 500, 500, 0, 100, 1, + 1000, + },{75578374, 147, + 5000, 500, 500, 25000, -100, -1, 250000, 500, 500, 0, 100, 1, + 1000, + },{44821152, 148, + 5000, 500, 500, 25000, -50, 49, 250000, 500, 500, 0, 100, 1, 1000, + },{45224103, 149, + 5000, 500, 500, 25000, 101, 200, 250000, 500, 500, 0, 100, 1, + 1000, + },{63491741, 150, + 5000, 500, 500, 25000, 1001, 1100, 250000, 500, 500, 0, 100, 1, + 1000, + } +}; + +typedef struct { double rhs; } v_data; +typedef struct { double cap, cost; } a_data; + +int main(void) +{ glp_graph *G; + int k; + char fname[100+1]; + G = glp_create_graph(sizeof(v_data), sizeof(a_data)); + for (k = 1; k <= 50; k++) + { sprintf(fname, "netgn%03d.min", parm[k-1][1]); + glp_netgen(G, offsetof(v_data, rhs), offsetof(a_data, cap), + offsetof(a_data, cost), &parm[k-1][-1]); + glp_write_mincost(G, offsetof(v_data, rhs), -1, + offsetof(a_data, cap), offsetof(a_data, cost), fname); + } + glp_delete_graph(G); + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/numbrix.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/numbrix.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,84 @@ +/* Numbrix, Number Placement Puzzle */ + +/* Written in GNU MathProg by Robert Wood */ + +/* Numbrix is a logic-based number-placement puzzle.[1] + * The objective is to fill the grid so that each cell contains + * digits in sequential order taking a horizontal or vertical + * path; diagonal paths are not allowed. The puzzle setter + * provides a grid often with the outer most cells completed. + * + * Completed Numbrix puzzles are usually a square of numbers + * in order from 1 to 64 (8x8 grid) or from 1 to 81 (9x9 grid), + * following a continuous path in sequence. + * + * The modern puzzle was invented by Marilyn vos Savant in 2008 + * and published by Parade Magazine under the name "Numbrix", + * near her weekly Ask Marilyn article. + * + * http://en.wikipedia.org/wiki/Numbrix */ + +set I := {1..9}; +set J := {1..9}; +set VALS := {1..81}; + +param givens{I, J}, integer, >= 0, <= 81, default 0; +/* the "givens" */ + +param neighbors{i in I,j in J, i2 in I, j2 in J} , binary := +(if abs(i - i2) + abs(j -j2) == 1 then + 1 + else + 0 +); +/* defines which spots are the boards are neighbors */ + +var x{i in I, j in J, k in VALS}, binary; +/* x[i,j,k] = 1 means cell [i,j] is assigned number k */ + +s.t. fa{i in I, j in J, k in VALS: givens[i,j] != 0}: + x[i,j,k] = (if givens[i,j] = k then 1 else 0); +/* assign pre-defined numbers using the "givens" */ + +s.t. fb{i in I, j in J}: sum{k in VALS} x[i,j,k] = 1; +/* each cell must be assigned exactly one number */ + +s.t. singleNum {k in VALS}: sum{i in I, j in J} x[i,j,k] = 1; +/* a value can only occur once */ + +s.t. neighborContraint {i in I, j in J, k in 1..80}: + x[i,j,k] <= sum{i2 in I, j2 in J} x[i2,j2,k+1] * neighbors[i,j,i2,j2]; +/* each cell must have a neighbor with the next higher value */ + + +/* there is no need for an objective function here */ + + +solve; + +for {i in I} +{ for {0..0: i = 1 or i = 4 or i = 7} + printf " +----------+----------+----------+\n"; + for {j in J} + { for {0..0: j = 1 or j = 4 or j = 7} printf(" |"); + printf " %2d", sum{k in VALS} x[i,j,k] * k; + for {0..0: j = 9} printf(" |\n"); + } + for {0..0: i = 9} + printf " +----------+----------+----------+\n"; +} + +data; + +param givens : 1 2 3 4 5 6 7 8 9 := + 1 . . . . . . . . . + 2 . 11 12 15 18 21 62 61 . + 3 . 6 . . . . . 60 . + 4 . 33 . . . . . 57 . + 5 . 32 . . . . . 56 . + 6 . 37 . . . . . 73 . + 7 . 38 . . . . . 72 . + 8 . 43 44 47 48 51 76 77 . + 9 . . . . . . . . . ; + +end; diff -r d59bea55db9b -r c445c931472f examples/pbn.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/pbn.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,194 @@ +/* PBN, Paint-By-Numbers Puzzle */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* A paint-by-number puzzle consists of an m*n grid of pixels (the + canvas) together with m+n cluster-size sequences, one for each row + and column. The goal is to paint the canvas with a picture that + satisfies the following constraints: + + 1. Each pixel must be blank or white. + + 2. If a row or column has cluster-size sequence s1, s2, ..., sk, + then it must contain k clusters of black pixels - the first with + s1 black pixels, the second with s2 black pixels, and so on. + + It should be noted that "first" means "leftmost" for rows and + "topmost" for columns, and that rows and columns need not begin or + end with black pixels. + + Example: + 1 1 + 1 1 + 2 1 1 1 1 1 2 3 + 3 2 1 2 1 2 3 4 8 9 + + 3 6 # # # . # # # # # # + 1 4 # . . . . . # # # # + 1 1 3 . . # . # . . # # # + 2 . . . . . . . . # # + 3 3 . . # # # . . # # # + 1 4 # . . . . . # # # # + 2 5 # # . . . # # # # # + 2 5 # # . . . # # # # # + 1 1 . . . # . . . . . # + 3 . . # # # . . . . . + + (In Russia this sort of puzzles is known as "Japanese crossword".) + + References: + Robert A. Bosch, "Painting by Numbers", 2000. + */ + +param m, integer, >= 1; +/* the number of rows */ + +param n, integer, >= 1; +/* the number of columns */ + +param row{i in 1..m, 1..n div 2}, integer, >= 0, default 0; +/* the cluster-size sequence for row i (raw data) */ + +param col{j in 1..n, 1..m div 2}, integer, >= 0, default 0; +/* the cluster-size sequence for column j (raw data) */ + +param kr{i in 1..m} := sum{t in 1..n div 2: row[i,t] > 0} 1; +/* the number of clusters in row i */ + +param kc{j in 1..n} := sum{t in 1..m div 2: col[j,t] > 0} 1; +/* the number of clusters in column j */ + +param sr{i in 1..m, t in 1..kr[i]} := row[i,t], integer, >= 1; +/* the cluster-size sequence for row i */ + +param sc{j in 1..n, t in 1..kc[j]} := col[j,t], integer, >= 1; +/* the cluster-size sequence for column j */ + +check{i in 1..m}: sum{t in 1..kr[i]} sr[i,t] <= n - (kr[i] - 1); +/* check that the sum of the cluster sizes in each row is valid */ + +check{j in 1..n}: sum{t in 1..kc[j]} sc[j,t] <= m - (kc[j] - 1); +/* check that the sum of the cluster sizes in each column is valid */ + +check: sum{i in 1..m, t in 1..kr[i]} sr[i,t] = + sum{j in 1..n, t in 1..kc[j]} sc[j,t]; +/* check that the sum of the cluster sizes in all rows is equal to the + sum of the cluster sizes in all columns */ + +param er{i in 1..m, t in 1..kr[i]} := + if t = 1 then 1 else er[i,t-1] + sr[i,t-1] + 1; +/* the smallest value of j such that row i's t-th cluster can be + placed in row i with its leftmost pixel occupying pixel j */ + +param lr{i in 1..m, t in 1..kr[i]} := + if t = kr[i] then n + 1 - sr[i,t] else lr[i,t+1] - sr[i,t] - 1; +/* the largest value of j such that row i's t-th cluster can be + placed in row i with its leftmost pixel occupying pixel j */ + +param ec{j in 1..n, t in 1..kc[j]} := + if t = 1 then 1 else ec[j,t-1] + sc[j,t-1] + 1; +/* the smallest value of i such that column j's t-th cluster can be + placed in column j with its topmost pixel occupying pixel i */ + +param lc{j in 1..n, t in 1..kc[j]} := + if t = kc[j] then m + 1 - sc[j,t] else lc[j,t+1] - sc[j,t] - 1; +/* the largest value of i such that column j's t-th cluster can be + placed in column j with its topmost pixel occupying pixel i */ + +var z{i in 1..m, j in 1..n}, binary; +/* z[i,j] = 1, if row i's j-th pixel is painted black + z[i,j] = 0, if row i's j-th pixel is painted white */ + +var y{i in 1..m, t in 1..kr[i], j in er[i,t]..lr[i,t]}, binary; +/* y[i,t,j] = 1, if row i's t-th cluster is placed in row i with its + leftmost pixel occupying pixel j + y[i,t,j] = 0, if not */ + +var x{j in 1..n, t in 1..kc[j], i in ec[j,t]..lc[j,t]}, binary; +/* x[j,t,i] = 1, if column j's t-th cluster is placed in column j with + its topmost pixel occupying pixel i + x[j,t,i] = 0, if not */ + +s.t. fa{i in 1..m, t in 1..kr[i]}: + sum{j in er[i,t]..lr[i,t]} y[i,t,j] = 1; +/* row i's t-th cluster must appear in row i exactly once */ + +s.t. fb{i in 1..m, t in 1..kr[i]-1, j in er[i,t]..lr[i,t]}: + y[i,t,j] <= sum{jp in j+sr[i,t]+1..lr[i,t+1]} y[i,t+1,jp]; +/* row i's (t+1)-th cluster must be placed to the right of its t-th + cluster */ + +s.t. fc{j in 1..n, t in 1..kc[j]}: + sum{i in ec[j,t]..lc[j,t]} x[j,t,i] = 1; +/* column j's t-th cluster must appear in column j exactly once */ + +s.t. fd{j in 1..n, t in 1..kc[j]-1, i in ec[j,t]..lc[j,t]}: + x[j,t,i] <= sum{ip in i+sc[j,t]+1..lc[j,t+1]} x[j,t+1,ip]; +/* column j's (t+1)-th cluster must be placed below its t-th cluster */ + +s.t. fe{i in 1..m, j in 1..n}: + z[i,j] <= sum{t in 1..kr[i], jp in er[i,t]..lr[i,t]: + j-sr[i,t]+1 <= jp and jp <= j} y[i,t,jp]; +/* the double coverage constraint stating that if row i's j-th pixel + is painted black, then at least one of row i's clusters must be + placed in such a way that it covers row i's j-th pixel */ + +s.t. ff{i in 1..m, j in 1..n}: + z[i,j] <= sum{t in 1..kc[j], ip in ec[j,t]..lc[j,t]: + i-sc[j,t]+1 <= ip and ip <= i} x[j,t,ip]; +/* the double coverage constraint making sure that if row i's j-th + pixel is painted black, then at least one of column j's clusters + covers it */ + +s.t. fg{i in 1..m, j in 1..n, t in 1..kr[i], jp in er[i,t]..lr[i,t]: + j-sr[i,t]+1 <= jp and jp <= j}: z[i,j] >= y[i,t,jp]; +/* the constraint to prevent white pixels from being covered by the + row clusters */ + +s.t. fh{i in 1..m, j in 1..n, t in 1..kc[j], ip in ec[j,t]..lc[j,t]: + i-sc[j,t]+1 <= ip and ip <= i}: z[i,j] >= x[j,t,ip]; +/* the constraint to prevent white pixels from being covered by the + column clusters */ + +/* there is no need for an objective function here */ + +solve; + +for {i in 1..m} +{ printf{j in 1..n} " %s", if z[i,j] then "#" else "."; + printf "\n"; +} + +data; + +/* These data correspond to the example above. */ + +param m := 10; + +param n := 10; + +param row : 1 2 3 4 := + 1 3 6 . . + 2 1 4 . . + 3 1 1 3 . + 4 2 . . . + 5 3 3 . . + 6 1 4 . . + 7 2 5 . . + 8 2 5 . . + 9 1 1 . . + 10 3 . . . ; + +param col : 1 2 3 4 := + 1 2 3 . . + 2 1 2 . . + 3 1 1 1 1 + 4 1 2 . . + 5 1 1 1 1 + 6 1 2 . . + 7 2 3 . . + 8 3 4 . . + 9 8 . . . + 10 9 . . . ; + +end; diff -r d59bea55db9b -r c445c931472f examples/plan.lp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/plan.lp Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,39 @@ +\* plan.lp *\ + +Minimize + value: .03 bin1 + .08 bin2 + .17 bin3 + .12 bin4 + .15 bin5 + + .21 alum + .38 silicon + +Subject To + yield: bin1 + bin2 + bin3 + bin4 + bin5 + + alum + silicon = 2000 + + fe: .15 bin1 + .04 bin2 + .02 bin3 + .04 bin4 + .02 bin5 + + .01 alum + .03 silicon <= 60 + + cu: .03 bin1 + .05 bin2 + .08 bin3 + .02 bin4 + .06 bin5 + + .01 alum <= 100 + + mn: .02 bin1 + .04 bin2 + .01 bin3 + .02 bin4 + .02 bin5 <= 40 + + mg: .02 bin1 + .03 bin2 + .01 bin5 <= 30 + + al: .70 bin1 + .75 bin2 + .80 bin3 + .75 bin4 + .80 bin5 + + .97 alum >= 1500 + + si1: .02 bin1 + .06 bin2 + .08 bin3 + .12 bin4 + .02 bin5 + + .01 alum + .97 silicon >= 250 + + si2: .02 bin1 + .06 bin2 + .08 bin3 + .12 bin4 + .02 bin5 + + .01 alum + .97 silicon <= 300 + +Bounds + bin1 <= 200 + bin2 <= 2500 + 400 <= bin3 <= 800 + 100 <= bin4 <= 700 + bin5 <= 1500 + +End + +\* eof *\ diff -r d59bea55db9b -r c445c931472f examples/plan.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/plan.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,39 @@ +/* plan.mod */ + +var bin1, >= 0, <= 200; +var bin2, >= 0, <= 2500; +var bin3, >= 400, <= 800; +var bin4, >= 100, <= 700; +var bin5, >= 0, <= 1500; +var alum, >= 0; +var silicon, >= 0; + +minimize + +value: .03 * bin1 + .08 * bin2 + .17 * bin3 + .12 * bin4 + .15 * bin5 + + .21 * alum + .38 * silicon; + +subject to + +yield: bin1 + bin2 + bin3 + bin4 + bin5 + alum + silicon = 2000; + +fe: .15 * bin1 + .04 * bin2 + .02 * bin3 + .04 * bin4 + .02 * bin5 + + .01 * alum + .03 * silicon <= 60; + +cu: .03 * bin1 + .05 * bin2 + .08 * bin3 + .02 * bin4 + .06 * bin5 + + .01 * alum <= 100; + +mn: .02 * bin1 + .04 * bin2 + .01 * bin3 + .02 * bin4 + .02 * bin5 + <= 40; + +mg: .02 * bin1 + .03 * bin2 + .01 * bin5 <= 30; + +al: .70 * bin1 + .75 * bin2 + .80 * bin3 + .75 * bin4 + .80 * bin5 + + .97 * alum >= 1500; + +si: 250 <= .02 * bin1 + .06 * bin2 + .08 * bin3 + .12 * bin4 + + .02 * bin5 + .01 * alum + .97 * silicon <= 300; + +end; + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/plan.mps --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/plan.mps Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,54 @@ +*000000001111111111222222222233333333334444444444555555555566 +*234567890123456789012345678901234567890123456789012345678901 +NAME PLAN +ROWS + N VALUE + E YIELD + L FE + L CU + L MN + L MG + G AL + L SI +COLUMNS + BIN1 VALUE .03000 YIELD 1.00000 + FE .15000 CU .03000 + MN .02000 MG .02000 + AL .70000 SI .02000 + BIN2 VALUE .08000 YIELD 1.00000 + FE .04000 CU .05000 + MN .04000 MG .03000 + AL .75000 SI .06000 + BIN3 VALUE .17000 YIELD 1.00000 + FE .02000 CU .08000 + MN .01000 AL .80000 + SI .08000 + BIN4 VALUE .12000 YIELD 1.00000 + FE .04000 CU .02000 + MN .02000 AL .75000 + SI .12000 + BIN5 VALUE .15000 YIELD 1.00000 + FE .02000 CU .06000 + MN .02000 MG .01000 + AL .80000 SI .02000 + ALUM VALUE .21000 YIELD 1.00000 + FE .01000 CU .01000 + AL .97000 SI .01000 + SILICON VALUE .38000 YIELD 1.00000 + FE .03000 SI .97000 +RHS + RHS1 YIELD 2000.00000 FE 60.00000 + CU 100.00000 MN 40.00000 + SI 300.00000 + MG 30.00000 AL 1500.00000 +RANGES + RNG1 SI 50.00000 +BOUNDS + UP BND1 BIN1 200.00000 + UP BIN2 2500.00000 + LO BIN3 400.00000 + UP BIN3 800.00000 + LO BIN4 100.00000 + UP BIN4 700.00000 + UP BIN5 1500.00000 +ENDATA diff -r d59bea55db9b -r c445c931472f examples/prod.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/prod.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,331 @@ +# PROD, a multiperiod production model +# +# References: +# Robert Fourer, David M. Gay and Brian W. Kernighan, "A Modeling Language +# for Mathematical Programming." Management Science 36 (1990) 519-554. + +### PRODUCTION SETS AND PARAMETERS ### + +set prd 'products'; # Members of the product group + +param pt 'production time' {prd} > 0; + + # Crew-hours to produce 1000 units + +param pc 'production cost' {prd} > 0; + + # Nominal production cost per 1000, used + # to compute inventory and shortage costs + +### TIME PERIOD SETS AND PARAMETERS ### + +param first > 0 integer; + # Index of first production period to be modeled + +param last > first integer; + + # Index of last production period to be modeled + +set time 'planning horizon' := first..last; + +### EMPLOYMENT PARAMETERS ### + +param cs 'crew size' > 0 integer; + + # Workers per crew + +param sl 'shift length' > 0; + + # Regular-time hours per shift + +param rtr 'regular time rate' > 0; + + # Wage per hour for regular-time labor + +param otr 'overtime rate' > rtr; + + # Wage per hour for overtime labor + +param iw 'initial workforce' >= 0 integer; + + # Crews employed at start of first period + +param dpp 'days per period' {time} > 0; + + # Regular working days in a production period + +param ol 'overtime limit' {time} >= 0; + + # Maximum crew-hours of overtime in a period + +param cmin 'crew minimum' {time} >= 0; + + # Lower limit on average employment in a period + +param cmax 'crew maximum' {t in time} >= cmin[t]; + + # Upper limit on average employment in a period + +param hc 'hiring cost' {time} >= 0; + + # Penalty cost of hiring a crew + +param lc 'layoff cost' {time} >= 0; + + # Penalty cost of laying off a crew + +### DEMAND PARAMETERS ### + +param dem 'demand' {prd,first..last+1} >= 0; + + # Requirements (in 1000s) + # to be met from current production and inventory + +param pro 'promoted' {prd,first..last+1} logical; + + # true if product will be the subject + # of a special promotion in the period + +### INVENTORY AND SHORTAGE PARAMETERS ### + +param rir 'regular inventory ratio' >= 0; + + # Proportion of non-promoted demand + # that must be in inventory the previous period + +param pir 'promotional inventory ratio' >= 0; + + # Proportion of promoted demand + # that must be in inventory the previous period + +param life 'inventory lifetime' > 0 integer; + + # Upper limit on number of periods that + # any product may sit in inventory + +param cri 'inventory cost ratio' {prd} > 0; + + # Inventory cost per 1000 units is + # cri times nominal production cost + +param crs 'shortage cost ratio' {prd} > 0; + + # Shortage cost per 1000 units is + # crs times nominal production cost + +param iinv 'initial inventory' {prd} >= 0; + + # Inventory at start of first period; age unknown + +param iil 'initial inventory left' {p in prd, t in time} + := iinv[p] less sum {v in first..t} dem[p,v]; + + # Initial inventory still available for allocation + # at end of period t + +param minv 'minimum inventory' {p in prd, t in time} + := dem[p,t+1] * (if pro[p,t+1] then pir else rir); + + # Lower limit on inventory at end of period t + +### VARIABLES ### + +var Crews{first-1..last} >= 0; + + # Average number of crews employed in each period + +var Hire{time} >= 0; # Crews hired from previous to current period + +var Layoff{time} >= 0; # Crews laid off from previous to current period + +var Rprd 'regular production' {prd,time} >= 0; + + # Production using regular-time labor, in 1000s + +var Oprd 'overtime production' {prd,time} >= 0; + + # Production using overtime labor, in 1000s + +var Inv 'inventory' {prd,time,1..life} >= 0; + + # Inv[p,t,a] is the amount of product p that is + # a periods old -- produced in period (t+1)-a -- + # and still in storage at the end of period t + +var Short 'shortage' {prd,time} >= 0; + + # Accumulated unsatisfied demand at the end of period t + +### OBJECTIVE ### + +minimize cost: + + sum {t in time} rtr * sl * dpp[t] * cs * Crews[t] + + sum {t in time} hc[t] * Hire[t] + + sum {t in time} lc[t] * Layoff[t] + + sum {t in time, p in prd} otr * cs * pt[p] * Oprd[p,t] + + sum {t in time, p in prd, a in 1..life} cri[p] * pc[p] * Inv[p,t,a] + + sum {t in time, p in prd} crs[p] * pc[p] * Short[p,t]; + + # Full regular wages for all crews employed, plus + # penalties for hiring and layoffs, plus + # wages for any overtime worked, plus + # inventory and shortage costs + + # (All other production costs are assumed + # to depend on initial inventory and on demands, + # and so are not included explicitly.) + +### CONSTRAINTS ### + +rlim 'regular-time limit' {t in time}: + + sum {p in prd} pt[p] * Rprd[p,t] <= sl * dpp[t] * Crews[t]; + + # Hours needed to accomplish all regular-time + # production in a period must not exceed + # hours available on all shifts + +olim 'overtime limit' {t in time}: + + sum {p in prd} pt[p] * Oprd[p,t] <= ol[t]; + + # Hours needed to accomplish all overtime + # production in a period must not exceed + # the specified overtime limit + +empl0 'initial crew level': Crews[first-1] = iw; + + # Use given initial workforce + +empl 'crew levels' {t in time}: Crews[t] = Crews[t-1] + Hire[t] - Layoff[t]; + + # Workforce changes by hiring or layoffs + +emplbnd 'crew limits' {t in time}: cmin[t] <= Crews[t] <= cmax[t]; + + # Workforce must remain within specified bounds + +dreq1 'first demand requirement' {p in prd}: + + Rprd[p,first] + Oprd[p,first] + Short[p,first] + - Inv[p,first,1] = dem[p,first] less iinv[p]; + +dreq 'demand requirements' {p in prd, t in first+1..last}: + + Rprd[p,t] + Oprd[p,t] + Short[p,t] - Short[p,t-1] + + sum {a in 1..life} (Inv[p,t-1,a] - Inv[p,t,a]) + = dem[p,t] less iil[p,t-1]; + + # Production plus increase in shortage plus + # decrease in inventory must equal demand + +ireq 'inventory requirements' {p in prd, t in time}: + + sum {a in 1..life} Inv[p,t,a] + iil[p,t] >= minv[p,t]; + + # Inventory in storage at end of period t + # must meet specified minimum + +izero 'impossible inventories' {p in prd, v in 1..life-1, a in v+1..life}: + + Inv[p,first+v-1,a] = 0; + + # In the vth period (starting from first) + # no inventory may be more than v periods old + # (initial inventories are handled separately) + +ilim1 'new-inventory limits' {p in prd, t in time}: + + Inv[p,t,1] <= Rprd[p,t] + Oprd[p,t]; + + # New inventory cannot exceed + # production in the most recent period + +ilim 'inventory limits' {p in prd, t in first+1..last, a in 2..life}: + + Inv[p,t,a] <= Inv[p,t-1,a-1]; + + # Inventory left from period (t+1)-p + # can only decrease as time goes on + +### DATA ### + +data; + +set prd := 18REG 24REG 24PRO ; + +param first := 1 ; +param last := 13 ; +param life := 2 ; + +param cs := 18 ; +param sl := 8 ; +param iw := 8 ; + +param rtr := 16.00 ; +param otr := 43.85 ; +param rir := 0.75 ; +param pir := 0.80 ; + +param : pt pc cri crs iinv := + + 18REG 1.194 2304. 0.015 1.100 82.0 + 24REG 1.509 2920. 0.015 1.100 792.2 + 24PRO 1.509 2910. 0.015 1.100 0.0 ; + +param : dpp ol cmin cmax hc lc := + + 1 19.5 96.0 0.0 8.0 7500 7500 + 2 19.0 96.0 0.0 8.0 7500 7500 + 3 20.0 96.0 0.0 8.0 7500 7500 + 4 19.0 96.0 0.0 8.0 7500 7500 + 5 19.5 96.0 0.0 8.0 15000 15000 + 6 19.0 96.0 0.0 8.0 15000 15000 + 7 19.0 96.0 0.0 8.0 15000 15000 + 8 20.0 96.0 0.0 8.0 15000 15000 + 9 19.0 96.0 0.0 8.0 15000 15000 + 10 20.0 96.0 0.0 8.0 15000 15000 + 11 20.0 96.0 0.0 8.0 7500 7500 + 12 18.0 96.0 0.0 8.0 7500 7500 + 13 18.0 96.0 0.0 8.0 7500 7500 ; + +param dem (tr) : + + 18REG 24REG 24PRO := + + 1 63.8 1212.0 0.0 + 2 76.0 306.2 0.0 + 3 88.4 319.0 0.0 + 4 913.8 208.4 0.0 + 5 115.0 298.0 0.0 + 6 133.8 328.2 0.0 + 7 79.6 959.6 0.0 + 8 111.0 257.6 0.0 + 9 121.6 335.6 0.0 + 10 470.0 118.0 1102.0 + 11 78.4 284.8 0.0 + 12 99.4 970.0 0.0 + 13 140.4 343.8 0.0 + 14 63.8 1212.0 0.0 ; + +param pro (tr) : + + 18REG 24REG 24PRO := + + 1 0 1 0 + 2 0 0 0 + 3 0 0 0 + 4 1 0 0 + 5 0 0 0 + 6 0 0 0 + 7 0 1 0 + 8 0 0 0 + 9 0 0 0 + 10 1 0 1 + 11 0 0 0 + 12 0 0 0 + 13 0 1 0 + 14 0 1 0 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/qfit.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/qfit.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,49 @@ +/*Quadratic Curve Fitting Solution + + Find a plausable quadratic fit to a sample of points + + Nigel_Galloway@operamail.com + February 1st., 2009 +*/ +set Sample; +param Sx {z in Sample}; +param Sy {z in Sample}; + +var a; +var b; +var c; + +equalz1 :sum{z in Sample} a*Sx[z]*Sx[z]*Sx[z]*Sx[z] + sum{z in Sample} b*Sx[z]*Sx[z]*Sx[z] + sum{z in Sample} c*Sx[z]*Sx[z] = sum{z in Sample} Sy[z]*Sx[z]*Sx[z]; +equalz2 :sum{z in Sample} a*Sx[z]*Sx[z]*Sx[z] + sum{z in Sample} b*Sx[z]*Sx[z] + sum{z in Sample} c*Sx[z] = sum{z in Sample} Sy[z]*Sx[z]; +equalz3 :sum{z in Sample} a*Sx[z]*Sx[z] + sum{z in Sample} b*Sx[z] + sum{z in Sample} c = sum{z in Sample} Sy[z]; + +solve; + +printf "\nbest quadratic fit is:\n\ty = %f %s %fx %s %fx^2\n\n", c, if b < 0 then "-" else "+", abs(b), if a < 0 then "-" else "+", abs(a); + +data; + +param: +Sample: Sx Sy := + 1 0 1 + 2 0.5 0.9 + 3 1 0.7 + 4 1.5 1.5 + 5 1.9 2 + 6 2.5 2.4 + 7 3 3.2 + 8 3.5 2 + 9 4 2.7 + 10 4.5 3.5 + 11 5 1 + 12 5.5 4 + 13 6 3.6 + 14 6.6 2.7 + 15 7 5.7 + 16 7.6 4.6 + 17 8.5 6 + 18 9 6.8 + 19 10 7.3 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/queens.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/queens.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,41 @@ +/* QUEENS, a classic combinatorial optimization problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Queens Problem is to place as many queens as possible on the 8x8 + (or more generally, nxn) chess board in a way that they do not fight + each other. This problem is probably as old as the chess game itself, + and thus its origin is not known, but it is known that Gauss studied + this problem. */ + +param n, integer, > 0, default 8; +/* size of the chess board */ + +var x{1..n, 1..n}, binary; +/* x[i,j] = 1 means that a queen is placed in square [i,j] */ + +s.t. a{i in 1..n}: sum{j in 1..n} x[i,j] <= 1; +/* at most one queen can be placed in each row */ + +s.t. b{j in 1..n}: sum{i in 1..n} x[i,j] <= 1; +/* at most one queen can be placed in each column */ + +s.t. c{k in 2-n..n-2}: sum{i in 1..n, j in 1..n: i-j == k} x[i,j] <= 1; +/* at most one queen can be placed in each "\"-diagonal */ + +s.t. d{k in 3..n+n-1}: sum{i in 1..n, j in 1..n: i+j == k} x[i,j] <= 1; +/* at most one queen can be placed in each "/"-diagonal */ + +maximize obj: sum{i in 1..n, j in 1..n} x[i,j]; +/* objective is to place as many queens as possible */ + +/* solve the problem */ +solve; + +/* and print its optimal solution */ +for {i in 1..n} +{ for {j in 1..n} printf " %s", if x[i,j] then "Q" else "."; + printf("\n"); +} + +end; diff -r d59bea55db9b -r c445c931472f examples/samp1.mps --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/samp1.mps Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,29 @@ +NAME SAMP1 +ROWS + N Z + G R1 + G R2 + G R3 +COLUMNS + X1 R1 2.0 R2 1.0 + X1 R3 5.0 Z 3.0 + MARK0001 'MARKER' 'INTORG' + X2 R1 -1.0 R2 -1.0 + X2 R3 3.0 Z 7.0 + X3 R1 1.0 R2 -6.0 + X3 Z -1.0 + MARK0002 'MARKER' 'INTEND' + X4 R1 -1.0 R2 4.0 + X4 R3 1.0 Z 1.0 +RHS + RHS1 R1 1.0 + RHS1 R2 8.0 + RHS1 R3 5.0 +BOUNDS + UP BND1 X1 4.0 + LO BND1 X2 2.0 + UP BND1 X2 5.0 + UP BND1 X3 1.0 + LO BND1 X4 3.0 + UP BND1 X4 8.0 +ENDATA diff -r d59bea55db9b -r c445c931472f examples/samp2.mps --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/samp2.mps Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,27 @@ +NAME SAMP2 +ROWS + N Z + G R1 + G R2 + G R3 +COLUMNS + X1 R1 2.0 R2 1.0 + X1 R3 5.0 Z 3.0 + X2 R1 -1.0 R2 -1.0 + X2 R3 3.0 Z 7.0 + X3 R1 1.0 R2 -6.0 + X3 Z -1.0 + X4 R1 -1.0 R2 4.0 + X4 R3 1.0 Z 1.0 +RHS + RHS1 R1 1.0 + RHS1 R2 8.0 + RHS1 R3 5.0 +BOUNDS + UP BND1 X1 4.0 + LO BND1 X2 2.0 + UI BND1 X2 5.0 + BV BND1 X3 + LO BND1 X4 3.0 + UP BND1 X4 8.0 +ENDATA diff -r d59bea55db9b -r c445c931472f examples/sample.asn --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sample.asn Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,40 @@ +c sample.asn +c +c This is an example of the assignment problem data +c in DIMACS format. +c +p asn 17 22 +c +n 1 +n 2 +n 3 +n 4 +n 5 +n 6 +n 7 +n 8 +c +a 1 9 13 +a 1 10 21 +a 1 12 20 +a 2 10 12 +a 2 12 8 +a 2 13 26 +a 3 11 22 +a 3 13 11 +a 4 9 12 +a 4 12 36 +a 4 14 25 +a 5 11 41 +a 5 12 40 +a 5 13 11 +a 5 14 4 +a 5 15 8 +a 5 16 35 +a 5 17 32 +a 6 9 13 +a 7 10 19 +a 8 10 39 +a 8 11 15 +c +c eof diff -r d59bea55db9b -r c445c931472f examples/sample.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sample.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,52 @@ +/* sample.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *lp; + int ia[1+1000], ja[1+1000]; + double ar[1+1000], z, x1, x2, x3; +s1: lp = glp_create_prob(); +s2: glp_set_prob_name(lp, "sample"); +s3: glp_set_obj_dir(lp, GLP_MAX); +s4: glp_add_rows(lp, 3); +s5: glp_set_row_name(lp, 1, "p"); +s6: glp_set_row_bnds(lp, 1, GLP_UP, 0.0, 100.0); +s7: glp_set_row_name(lp, 2, "q"); +s8: glp_set_row_bnds(lp, 2, GLP_UP, 0.0, 600.0); +s9: glp_set_row_name(lp, 3, "r"); +s10: glp_set_row_bnds(lp, 3, GLP_UP, 0.0, 300.0); +s11: glp_add_cols(lp, 3); +s12: glp_set_col_name(lp, 1, "x1"); +s13: glp_set_col_bnds(lp, 1, GLP_LO, 0.0, 0.0); +s14: glp_set_obj_coef(lp, 1, 10.0); +s15: glp_set_col_name(lp, 2, "x2"); +s16: glp_set_col_bnds(lp, 2, GLP_LO, 0.0, 0.0); +s17: glp_set_obj_coef(lp, 2, 6.0); +s18: glp_set_col_name(lp, 3, "x3"); +s19: glp_set_col_bnds(lp, 3, GLP_LO, 0.0, 0.0); +s20: glp_set_obj_coef(lp, 3, 4.0); +s21: ia[1] = 1, ja[1] = 1, ar[1] = 1.0; /* a[1,1] = 1 */ +s22: ia[2] = 1, ja[2] = 2, ar[2] = 1.0; /* a[1,2] = 1 */ +s23: ia[3] = 1, ja[3] = 3, ar[3] = 1.0; /* a[1,3] = 1 */ +s24: ia[4] = 2, ja[4] = 1, ar[4] = 10.0; /* a[2,1] = 10 */ +s25: ia[5] = 3, ja[5] = 1, ar[5] = 2.0; /* a[3,1] = 2 */ +s26: ia[6] = 2, ja[6] = 2, ar[6] = 4.0; /* a[2,2] = 4 */ +s27: ia[7] = 3, ja[7] = 2, ar[7] = 2.0; /* a[3,2] = 2 */ +s28: ia[8] = 2, ja[8] = 3, ar[8] = 5.0; /* a[2,3] = 5 */ +s29: ia[9] = 3, ja[9] = 3, ar[9] = 6.0; /* a[3,3] = 6 */ +s30: glp_load_matrix(lp, 9, ia, ja, ar); +s31: glp_simplex(lp, NULL); +s32: z = glp_get_obj_val(lp); +s33: x1 = glp_get_col_prim(lp, 1); +s34: x2 = glp_get_col_prim(lp, 2); +s35: x3 = glp_get_col_prim(lp, 3); +s36: printf("\nz = %g; x1 = %g; x2 = %g; x3 = %g\n", + z, x1, x2, x3); +s37: glp_delete_prob(lp); + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/sample.clq --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sample.clq Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,30 @@ +c sample.clq +c +c This is an example of the Maximum Weight Clique +c Problem in DIMACS clique/coloring format. +c +p edge 8 16 +n 1 3 +n 2 4 +n 3 8 +n 5 5 +n 6 2 +n 8 3 +e 1 4 +e 1 5 +e 1 6 +e 1 8 +e 2 3 +e 2 6 +e 2 7 +e 2 8 +e 3 4 +e 3 6 +e 3 7 +e 4 5 +e 4 8 +e 5 7 +e 5 8 +e 6 7 +c +c eof diff -r d59bea55db9b -r c445c931472f examples/sample.col --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sample.col Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,30 @@ +c sample.col +c +c This is an example of the vertex coloring problem data +c in DIMACS format. +c +p edge 10 21 +c +e 1 2 +e 1 6 +e 1 7 +e 1 10 +e 2 3 +e 2 7 +e 2 8 +e 3 4 +e 3 8 +e 4 5 +e 4 8 +e 4 9 +e 5 6 +e 5 9 +e 5 10 +e 6 10 +e 7 8 +e 7 10 +e 8 9 +e 8 10 +e 9 10 +c +c eof diff -r d59bea55db9b -r c445c931472f examples/sample.max --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sample.max Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,26 @@ +c sample.max +c +c This is an example of the maximum flow problem data +c in DIMACS format. +c +p max 9 14 +c +n 1 s +n 9 t +c +a 1 2 14 +a 1 4 23 +a 2 3 10 +a 2 4 9 +a 3 5 12 +a 3 8 18 +a 4 5 26 +a 5 2 11 +a 5 6 25 +a 5 7 4 +a 6 7 7 +a 6 8 8 +a 7 9 15 +a 8 9 20 +c +c eof diff -r d59bea55db9b -r c445c931472f examples/sample.min --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sample.min Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,26 @@ +c sample.min +c +c This is an example of the minimum cost flow problem data +c in DIMACS format. +c +p min 9 14 +c +n 1 20 +n 9 -20 +c +a 1 2 0 14 0 +a 1 4 0 23 0 +a 2 3 0 10 2 +a 2 4 0 9 3 +a 3 5 2 12 1 +a 3 8 0 18 0 +a 4 5 0 26 0 +a 5 2 0 11 1 +a 5 6 0 25 5 +a 5 7 0 4 7 +a 6 7 0 7 0 +a 6 8 4 8 0 +a 7 9 0 15 3 +a 8 9 0 20 9 +c +c eof diff -r d59bea55db9b -r c445c931472f examples/sat.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sat.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,201 @@ +/* SAT, Satisfiability Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +param m, integer, > 0; +/* number of clauses */ + +param n, integer, > 0; +/* number of variables */ + +set C{1..m}; +/* clauses; each clause C[i], i = 1, ..., m, is disjunction of some + variables or their negations; in the data section each clause is + coded as a set of indices of corresponding variables, where negative + indices mean negation; for example, the clause (x3 or not x7 or x11) + is coded as the set { 3, -7, 11 } */ + +var x{1..n}, binary; +/* main variables */ + +/* To solve the satisfiability problem means to determine all variables + x[j] such that conjunction of all clauses C[1] and ... and C[m] takes + on the value true, i.e. all clauses are satisfied. + + Let the clause C[i] be (t or t' or ... or t''), where t, t', ..., t'' + are either variables or their negations. The condition of satisfying + C[i] can be most naturally written as: + + t + t' + ... + t'' >= 1, (1) + + where t, t', t'' have to be replaced by either x[j] or (1 - x[j]). + The formulation (1) leads to the mip problem with no objective, i.e. + to a feasibility problem. + + Another, more practical way is to write the condition for C[i] as: + + t + t' + ... + t'' + y[i] >= 1, (2) + + where y[i] is an auxiliary binary variable, and minimize the sum of + y[i]. If the sum is zero, all y[i] are also zero, and therefore all + clauses are satisfied. If the sum is minimal but non-zero, its value + shows the number of clauses which cannot be satisfied. */ + +var y{1..m}, binary, >= 0; +/* auxiliary variables */ + +s.t. c{i in 1..m}: + sum{j in C[i]} (if j > 0 then x[j] else (1 - x[-j])) + y[i] >= 1; +/* the condition (2) */ + +minimize unsat: sum{i in 1..m} y[i]; +/* number of unsatisfied clauses */ + +data; + +/* These data correspond to the instance hole6 (pigeon hole problem for + 6 holes) from SATLIB, the Satisfiability Library, which is part of + the collection at the Forschungsinstitut fuer anwendungsorientierte + Wissensverarbeitung in Ulm Germany */ + +/* The optimal solution is 1 (one clause cannot be satisfied) */ + +param m := 133; + +param n := 42; + +set C[1] := -1 -7; +set C[2] := -1 -13; +set C[3] := -1 -19; +set C[4] := -1 -25; +set C[5] := -1 -31; +set C[6] := -1 -37; +set C[7] := -7 -13; +set C[8] := -7 -19; +set C[9] := -7 -25; +set C[10] := -7 -31; +set C[11] := -7 -37; +set C[12] := -13 -19; +set C[13] := -13 -25; +set C[14] := -13 -31; +set C[15] := -13 -37; +set C[16] := -19 -25; +set C[17] := -19 -31; +set C[18] := -19 -37; +set C[19] := -25 -31; +set C[20] := -25 -37; +set C[21] := -31 -37; +set C[22] := -2 -8; +set C[23] := -2 -14; +set C[24] := -2 -20; +set C[25] := -2 -26; +set C[26] := -2 -32; +set C[27] := -2 -38; +set C[28] := -8 -14; +set C[29] := -8 -20; +set C[30] := -8 -26; +set C[31] := -8 -32; +set C[32] := -8 -38; +set C[33] := -14 -20; +set C[34] := -14 -26; +set C[35] := -14 -32; +set C[36] := -14 -38; +set C[37] := -20 -26; +set C[38] := -20 -32; +set C[39] := -20 -38; +set C[40] := -26 -32; +set C[41] := -26 -38; +set C[42] := -32 -38; +set C[43] := -3 -9; +set C[44] := -3 -15; +set C[45] := -3 -21; +set C[46] := -3 -27; +set C[47] := -3 -33; +set C[48] := -3 -39; +set C[49] := -9 -15; +set C[50] := -9 -21; +set C[51] := -9 -27; +set C[52] := -9 -33; +set C[53] := -9 -39; +set C[54] := -15 -21; +set C[55] := -15 -27; +set C[56] := -15 -33; +set C[57] := -15 -39; +set C[58] := -21 -27; +set C[59] := -21 -33; +set C[60] := -21 -39; +set C[61] := -27 -33; +set C[62] := -27 -39; +set C[63] := -33 -39; +set C[64] := -4 -10; +set C[65] := -4 -16; +set C[66] := -4 -22; +set C[67] := -4 -28; +set C[68] := -4 -34; +set C[69] := -4 -40; +set C[70] := -10 -16; +set C[71] := -10 -22; +set C[72] := -10 -28; +set C[73] := -10 -34; +set C[74] := -10 -40; +set C[75] := -16 -22; +set C[76] := -16 -28; +set C[77] := -16 -34; +set C[78] := -16 -40; +set C[79] := -22 -28; +set C[80] := -22 -34; +set C[81] := -22 -40; +set C[82] := -28 -34; +set C[83] := -28 -40; +set C[84] := -34 -40; +set C[85] := -5 -11; +set C[86] := -5 -17; +set C[87] := -5 -23; +set C[88] := -5 -29; +set C[89] := -5 -35; +set C[90] := -5 -41; +set C[91] := -11 -17; +set C[92] := -11 -23; +set C[93] := -11 -29; +set C[94] := -11 -35; +set C[95] := -11 -41; +set C[96] := -17 -23; +set C[97] := -17 -29; +set C[98] := -17 -35; +set C[99] := -17 -41; +set C[100] := -23 -29; +set C[101] := -23 -35; +set C[102] := -23 -41; +set C[103] := -29 -35; +set C[104] := -29 -41; +set C[105] := -35 -41; +set C[106] := -6 -12; +set C[107] := -6 -18; +set C[108] := -6 -24; +set C[109] := -6 -30; +set C[110] := -6 -36; +set C[111] := -6 -42; +set C[112] := -12 -18; +set C[113] := -12 -24; +set C[114] := -12 -30; +set C[115] := -12 -36; +set C[116] := -12 -42; +set C[117] := -18 -24; +set C[118] := -18 -30; +set C[119] := -18 -36; +set C[120] := -18 -42; +set C[121] := -24 -30; +set C[122] := -24 -36; +set C[123] := -24 -42; +set C[124] := -30 -36; +set C[125] := -30 -42; +set C[126] := -36 -42; +set C[127] := 6 5 4 3 2 1; +set C[128] := 12 11 10 9 8 7; +set C[129] := 18 17 16 15 14 13; +set C[130] := 24 23 22 21 20 19; +set C[131] := 30 29 28 27 26 25; +set C[132] := 36 35 34 33 32 31; +set C[133] := 42 41 40 39 38 37; + +end; diff -r d59bea55db9b -r c445c931472f examples/shiftcover.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/shiftcover.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,244 @@ +/* File: shiftcover.mod */ + +/* WORKFORCE SHIFT COVERAGE assignment problem */ + +/* Written by Larry D'Agostino + + Maximize Productivity with Industrial Engineer and Operations Research Tools + http://industrialengineertools.blogspot.com + + +/* The WORKFORCE SHIFT COVERAGE is an assigment problem that determines + the schedule of crew given available time and shifts. + + The objective is to cover the available time given hourly demand with the minimum + number of crew members. + + This is a set covering problem that is very common among finding crew + and shift allocations. Notice in the data section the workforce shift allocation + per day of the week.*/ + + +/* ----- Model PARAMTERS and SETS -----*/ + +param numhrs; +/* number of hours of operations in a given day */ + +param dys; +/* number of days in a week */ + +set S; +/* set of crew shifts */ + +set H := 1..numhrs; +/* set of hours of a day*/ + +set D; +/* set of days of a week*/ + +param dmnd{h in H, d in D}; +/* demand for crew members given h hour and d day */ + +param shifts{d in D, h in H, s in S}; +/* shifts to assign to crew members given d day, h hour, and s shift schedule + +/*----- Model VARIABLES -----*/ + +var crew{s in S}, integer, >=0; +/* number of crew assigned to shift S */ + + +/*----- Model CONSTRAINTS -----*/ + +s.t. Coverage{h in H, d in D}: sum{s in S} crew[s]*shifts[d,h,s] >= dmnd[h,d]; +/* number of crew to cover with a shift given hourly demand and day */ + + +/*----- Model OBJECTIVE -----*/ + +minimize obj: sum{s in S} crew[s]; +/* minimize number of crew to cover demand*/ + +solve; +display crew; + +printf "\n"; +printf "Total Crew: %3d\n\n", sum{s in S} crew[s]; + + + +printf "\n\n"; +printf "Weekly Crew Schedule\n\n"; +printf "Hour "; +printf{d in D} " %s ", d; +printf "\n"; +for {h in H} { + printf " %2s ",h; + printf{d in D} " %3d ", sum{s in S} crew[s]*shifts[d,h,s]; + printf "\n"; +} +printf"\n"; + + + +data; + +param numhrs := 16; + +set D := SUN, MON, TUE, WED, THU, FRI, SAT; + +set S := Sh1, Sh2, Sh3, Sh4, Sh5, Sh6, Sh7, Sh8, Sh9; + +param dmnd : SUN MON TUE WED THU FRI SAT := +1 0 3 3 4 3 2 0 +2 0 14 14 16 14 12 12 +3 0 24 24 27 24 20 15 +4 0 28 28 32 28 23 15 +5 0 33 33 37 33 24 16 +6 0 34 34 38 34 24 15 +7 0 35 35 39 35 25 11 +8 0 35 35 40 35 27 0 +9 0 34 34 39 34 25 0 +10 0 31 31 35 31 24 0 +11 2 24 24 27 24 25 0 +12 3 19 19 21 19 21 0 +13 2 24 24 27 24 13 0 +14 2 16 16 18 16 0 0 +15 0 7 7 7 7 0 0 +16 0 5 5 5 5 0 0; + + +param shifts := +['SUN',*,*]: + Sh1 Sh2 Sh3 Sh4 Sh5 Sh6 Sh7 Sh8 Sh9 := +1 0 0 0 0 0 0 0 0 0 +2 0 0 0 0 0 0 0 0 0 +3 0 0 0 0 0 0 0 0 0 +4 0 0 0 0 0 0 0 0 0 +5 0 0 0 0 0 0 0 0 0 +6 0 0 0 0 0 0 0 0 0 +7 0 0 0 0 0 0 0 0 0 +8 0 0 0 0 0 0 0 0 0 +9 0 0 0 0 0 0 0 0 0 +10 0 0 0 0 0 0 0 0 0 +11 0 0 0 0 0 0 0 0 1 +12 0 0 0 0 0 0 0 0 1 +13 0 0 0 0 0 0 0 0 1 +14 0 0 0 0 0 0 0 0 1 +15 0 0 0 0 0 0 0 0 0 +16 0 0 0 0 0 0 0 0 0 + + +['MON',*,*]: + Sh1 Sh2 Sh3 Sh4 Sh5 Sh6 Sh7 Sh8 Sh9 := +1 1 0 0 0 0 0 0 0 0 +2 1 1 0 0 0 0 0 0 0 +3 1 1 1 0 0 0 0 0 0 +4 1 1 1 1 0 0 0 0 0 +5 0 1 1 1 1 0 0 0 0 +6 1 0 1 1 1 1 0 0 1 +7 1 1 0 1 1 1 1 0 1 +8 1 1 1 0 1 1 1 1 1 +9 1 1 1 1 0 1 1 1 1 +10 0 1 1 1 1 0 1 1 1 +11 0 0 1 1 1 1 0 1 0 +12 0 0 0 1 1 1 1 0 1 +13 0 0 0 0 1 1 1 1 1 +14 0 0 0 0 0 1 1 1 1 +15 0 0 0 0 0 0 1 1 1 +16 0 0 0 0 0 0 0 1 1 + +['TUE',*,*]: + Sh1 Sh2 Sh3 Sh4 Sh5 Sh6 Sh7 Sh8 Sh9 := +1 1 0 0 0 0 0 0 0 0 +2 1 1 0 0 0 0 0 0 0 +3 1 1 1 0 0 0 0 0 0 +4 1 1 1 1 0 0 0 0 0 +5 0 1 1 1 1 0 0 0 0 +6 1 0 1 1 1 1 0 0 1 +7 1 1 0 1 1 1 1 0 1 +8 1 1 1 0 1 1 1 1 1 +9 1 1 1 1 0 1 1 1 1 +10 0 1 1 1 1 0 1 1 1 +11 0 0 1 1 1 1 0 1 0 +12 0 0 0 1 1 1 1 0 1 +13 0 0 0 0 1 1 1 1 1 +14 0 0 0 0 0 1 1 1 1 +15 0 0 0 0 0 0 1 1 1 +16 0 0 0 0 0 0 0 1 1 + +['WED',*,*]: + Sh1 Sh2 Sh3 Sh4 Sh5 Sh6 Sh7 Sh8 Sh9 := +1 1 0 0 0 0 0 0 0 0 +2 1 1 0 0 0 0 0 0 0 +3 1 1 1 0 0 0 0 0 0 +4 1 1 1 1 0 0 0 0 0 +5 0 1 1 1 1 0 0 0 0 +6 1 0 1 1 1 1 0 0 1 +7 1 1 0 1 1 1 1 0 1 +8 1 1 1 0 1 1 1 1 1 +9 1 1 1 1 0 1 1 1 1 +10 0 1 1 1 1 0 1 1 1 +11 0 0 1 1 1 1 0 1 0 +12 0 0 0 1 1 1 1 0 1 +13 0 0 0 0 1 1 1 1 1 +14 0 0 0 0 0 1 1 1 1 +15 0 0 0 0 0 0 1 1 1 +16 0 0 0 0 0 0 0 1 1 + +['THU',*,*]: + Sh1 Sh2 Sh3 Sh4 Sh5 Sh6 Sh7 Sh8 Sh9 := +1 1 0 0 0 0 0 0 0 0 +2 1 1 0 0 0 0 0 0 0 +3 1 1 1 0 0 0 0 0 0 +4 1 1 1 1 0 0 0 0 0 +5 0 1 1 1 1 0 0 0 0 +6 1 0 1 1 1 1 0 0 0 +7 1 1 0 1 1 1 1 0 0 +8 1 1 1 0 1 1 1 1 0 +9 1 1 1 1 0 1 1 1 0 +10 0 1 1 1 1 0 1 1 0 +11 0 0 1 1 1 1 0 1 0 +12 0 0 0 1 1 1 1 0 0 +13 0 0 0 0 1 1 1 1 0 +14 0 0 0 0 0 1 1 1 0 +15 0 0 0 0 0 0 1 1 0 +16 0 0 0 0 0 0 0 1 0 + +['FRI',*,*]: + Sh1 Sh2 Sh3 Sh4 Sh5 Sh6 Sh7 Sh8 Sh9 := +1 1 0 0 0 0 0 0 0 0 +2 1 1 0 0 0 0 0 0 0 +3 1 1 1 0 0 0 0 0 0 +4 1 1 1 1 0 0 0 0 0 +5 0 1 1 1 1 0 0 0 0 +6 1 0 1 1 1 1 0 0 0 +7 1 1 0 1 1 1 1 0 0 +8 1 1 1 0 1 1 1 1 0 +9 1 1 1 1 0 1 1 1 0 +10 0 1 1 1 1 0 1 1 0 +11 0 0 1 1 1 1 0 1 0 +12 0 0 0 1 1 1 1 0 0 +13 0 0 0 0 1 1 1 1 0 +14 0 0 0 0 0 1 1 1 0 +15 0 0 0 0 0 0 1 1 0 +16 0 0 0 0 0 0 0 1 0 + +['SAT',*,*]: + Sh1 Sh2 Sh3 Sh4 Sh5 Sh6 Sh7 Sh8 Sh9 := +1 0 0 0 0 0 0 0 0 0 +2 0 0 0 0 0 0 0 0 1 +3 0 0 0 0 0 0 0 0 1 +4 0 0 0 0 0 0 0 0 1 +5 0 0 0 0 0 0 0 0 1 +6 0 0 0 0 0 0 0 0 1 +7 0 0 0 0 0 0 0 0 1 +8 0 0 0 0 0 0 0 0 0 +9 0 0 0 0 0 0 0 0 0 +10 0 0 0 0 0 0 0 0 0 +11 0 0 0 0 0 0 0 0 0 +12 0 0 0 0 0 0 0 0 0 +13 0 0 0 0 0 0 0 0 0 +14 0 0 0 0 0 0 0 0 0 +15 0 0 0 0 0 0 0 0 0 +16 0 0 0 0 0 0 0 0 0; diff -r d59bea55db9b -r c445c931472f examples/shikaku.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/shikaku.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,107 @@ +/* A solver for the Japanese number-puzzle Shikaku + * http://en.wikipedia.org/wiki/Shikaku + * + * Sebastian Nowozin , 27th January 2009 + */ + +param ndim := 10; +set rows := 1..ndim; +set rows1 := 1..(ndim+1); +set cols := 1..ndim; +set cols1 := 1..(ndim+1); +param givens{rows, cols}, integer, >= 0, default 0; + +/* Set of vertices as (row,col) coordinates */ +set V := { (i,j) in { rows, cols }: givens[i,j] != 0 }; + +/* Set of all feasible boxes of the right size: only this boxes are possible. + * The box contains (i,j) and ranges from (k,l) to (m,n) + */ +set B := { (i,j,k,l,m,n) in { V, rows, cols, rows1, cols1 }: + i >= k and i < m and j >= l and j < n and /* Contains (i,j) */ + ((m-k)*(n-l)) = givens[i,j] and /* Right size */ + card({ (s,t) in V: s >= k and s < m and t >= l and t < n }) = 1 + /* Contains only (i,j), no other number */ +}; + +var x{B}, binary; + +/* Cover each square exactly once */ +s.t. cover_once{ (s,t) in { rows, cols } }: + sum{(i,j,k,l,m,n) in B: s >= k and s < m and t >= l and t < n} + x[i,j,k,l,m,n] = 1; + +minimize cost: 0; + +solve; + +/* Output solution graphically */ +printf "\nSolution:\n"; +for { row in rows1 } { + for { col in cols1 } { + printf{0..0: card({(i,j,k,l,m,n) in B: + col >= l and col <= n and (row = k or row = m) and + x[i,j,k,l,m,n] = 1}) > 0 and + card({(i,j,k,l,m,n) in B: + row >= k and row <= m and (col = l or col = n) and + x[i,j,k,l,m,n] = 1}) > 0} "+"; + printf{0..0: card({(i,j,k,l,m,n) in B: + col >= l and col <= n and (row = k or row = m) and + x[i,j,k,l,m,n] = 1}) = 0 and + card({(i,j,k,l,m,n) in B: + row >= k and row <= m and (col = l or col = n) and + x[i,j,k,l,m,n] = 1}) > 0} "|"; + printf{0..0: card({(i,j,k,l,m,n) in B: + row >= k and row <= m and (col = l or col = n) and + x[i,j,k,l,m,n] = 1}) = 0 and + card({(i,j,k,l,m,n) in B: + col >= l and col <= n and (row = k or row = m) and + x[i,j,k,l,m,n] = 1}) > 0} "-"; + printf{0..0: card({(i,j,k,l,m,n) in B: + row >= k and row <= m and (col = l or col = n) and + x[i,j,k,l,m,n] = 1}) = 0 and + card({(i,j,k,l,m,n) in B: + col >= l and col <= n and (row = k or row = m) and + x[i,j,k,l,m,n] = 1}) = 0} " "; + + printf{0..0: card({(i,j,k,l,m,n) in B: + col >= l and col < n and (row = k or row = m) and + x[i,j,k,l,m,n] = 1}) > 0} "---"; + printf{0..0: card({(i,j,k,l,m,n) in B: + col >= l and col < n and (row = k or row = m) and + x[i,j,k,l,m,n] = 1}) = 0} " "; + } + printf "\n"; + + for { (col,p) in { cols, 1 }: card({ s in rows: s = row }) = 1 } { + printf{0..0: card({(i,j,k,l,m,n) in B: + row >= k and row < m and (col = l or col = n) and + x[i,j,k,l,m,n] = 1}) > 0} "|"; + printf{0..0: card({(i,j,k,l,m,n) in B: + row >= k and row < m and (col = l or col = n) and + x[i,j,k,l,m,n] = 1}) = 0} " "; + printf{0..0: card({ (i,j) in V: i = row and j = col}) > 0} " %2d", givens[row,col]; + printf{0..0: card({ (i,j) in V: i = row and j = col}) = 0} " ."; + } + printf{0..0: card({ r in rows: r = row }) = 1} "|\n"; +} + +data; + +/* This Shikaku is from + * http://www.emn.fr/x-info/sdemasse/gccat/KShikaku.html#uid5449 + */ +param givens : 1 2 3 4 5 6 7 8 9 10 := + 1 9 . . . 12 . . 5 . . + 2 . . . . . . . . . . + 3 . . . . . . . . . 6 + 4 8 . 6 . 8 . . . . . + 5 . . . . . . . . . . + 6 . . . . . . . . . . + 7 . . . . . 6 . 8 . 12 + 8 4 . . . . . . . . . + 9 . . . . . . . . . . + 10 . . 3 . . 9 . . . 4 + ; + +end; diff -r d59bea55db9b -r c445c931472f examples/sorting.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sorting.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,67 @@ +/* sorting.mod - how to sort arrays in MathProg */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +# Sometimes it is necessary to print parameters or variables in the +# order of ascending or descending their values. Suppose, for example, +# that we have the following subscripted parameter: + +set I := 1..12; + +param a{i in I} := Uniform(2, 7); + +# If we print all its members: + +printf{i in I} "a[%d] = %g\n", i, a[i]; + +# the output may look like follows: +# +# a[1] = 2.64156 +# a[2] = 2.04798 +# a[3] = 2.14843 +# a[4] = 4.76896 +# a[5] = 6.09132 +# a[6] = 3.27780 +# a[7] = 4.06113 +# a[8] = 4.05898 +# a[9] = 6.63120 +# a[10] = 6.50318 +# a[11] = 3.46065 +# a[12] = 4.69845 +# +# However, we would like the parameter members to appear in the order +# of ascending their values. +# +# Introduce the following auxiliary parameter: + +param pos{i in I} := + 1 + card({j in I: a[j] < a[i] or a[j] = a[i] and j < i}); + +# where pos[i] = k means that in the sorted list member a[i] would +# have k-th position, 1 <= k <= |I|. Then introduce another auxiliary +# parameter: + +param ind{k in 1..card(I)} := sum{i in I: pos[i] = k} i; + +# where ind[k] = i iff pos[k] = i. +# +# Now, the following statement: + +printf{k in 1..card(I)} "a[%d] = %g\n", ind[k], a[ind[k]]; + +# prints the parameter members in the desired order: +# +# a[2] = 2.04798 +# a[3] = 2.14843 +# a[1] = 2.64156 +# a[6] = 3.27780 +# a[11] = 3.46065 +# a[8] = 4.05898 +# a[7] = 4.06113 +# a[12] = 4.69845 +# a[4] = 4.76896 +# a[5] = 6.09132 +# a[10] = 6.50318 +# a[9] = 6.63120 + +end; diff -r d59bea55db9b -r c445c931472f examples/spp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/spp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,67 @@ +/* SPP, Shortest Path Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* Given a directed graph G = (V,E), its edge lengths c(i,j) for all + (i,j) in E, and two nodes s, t in V, the Shortest Path Problem (SPP) + is to find a directed path from s to t whose length is minimal. */ + +param n, integer, > 0; +/* number of nodes */ + +set E, within {i in 1..n, j in 1..n}; +/* set of edges */ + +param c{(i,j) in E}; +/* c[i,j] is length of edge (i,j); note that edge lengths are allowed + to be of any sign (positive, negative, or zero) */ + +param s, in {1..n}; +/* source node */ + +param t, in {1..n}; +/* target node */ + +var x{(i,j) in E}, >= 0; +/* x[i,j] = 1 means that edge (i,j) belong to shortest path; + x[i,j] = 0 means that edge (i,j) does not belong to shortest path; + note that variables x[i,j] are binary, however, there is no need to + declare them so due to the totally unimodular constraint matrix */ + +s.t. r{i in 1..n}: sum{(j,i) in E} x[j,i] + (if i = s then 1) = + sum{(i,j) in E} x[i,j] + (if i = t then 1); +/* conservation conditions for unity flow from s to t; every feasible + solution is a path from s to t */ + +minimize Z: sum{(i,j) in E} c[i,j] * x[i,j]; +/* objective function is the path length to be minimized */ + +data; + +/* Optimal solution is 20 that corresponds to the following shortest + path: s = 1 -> 2 -> 4 -> 8 -> 6 = t */ + +param n := 8; + +param s := 1; + +param t := 6; + +param : E : c := + 1 2 1 + 1 4 8 + 1 7 6 + 2 4 2 + 3 2 14 + 3 4 10 + 3 5 6 + 3 6 19 + 4 5 8 + 4 8 13 + 5 8 12 + 6 5 7 + 7 4 5 + 8 6 4 + 8 7 10; + +end; diff -r d59bea55db9b -r c445c931472f examples/spxsamp1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/spxsamp1.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,18 @@ +/* spxsamp1.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *P; + P = glp_create_prob(); + glp_read_mps(P, GLP_MPS_DECK, NULL, "25fv47.mps"); + glp_adv_basis(P, 0); + glp_simplex(P, NULL); + glp_print_sol(P, "25fv47.txt"); + glp_delete_prob(P); + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/spxsamp2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/spxsamp2.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,20 @@ +/* spxsamp2.c */ + +#include +#include +#include + +int main(void) +{ glp_prob *P; + glp_smcp parm; + P = glp_create_prob(); + glp_read_mps(P, GLP_MPS_DECK, NULL, "25fv47.mps"); + glp_init_smcp(&parm); + parm.meth = GLP_DUAL; + glp_simplex(P, &parm); + glp_print_sol(P, "25fv47.txt"); + glp_delete_prob(P); + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f examples/sql/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sql/README Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,5 @@ +This subdirectory contains files which demonstrate using data tables +in MathProg models for MySQL and iODBC. + +Script mysql_setup.sh is used to load the data from the *.sql files to +a MySQL database. Change the username, if necessary. diff -r d59bea55db9b -r c445c931472f examples/sql/mysql_setup.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sql/mysql_setup.sh Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,6 @@ +#!/bin/sh +# This file can be used to create database glpk in MySQL. +echo MySQL is called for user root. +mysql -f -u root -p < sudoku.sql +echo MySQL is called for user root. +mysql -f -u root -p < transp.sql diff -r d59bea55db9b -r c445c931472f examples/sql/sudoku.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sql/sudoku.sql Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,101 @@ +CREATE DATABASE glpk; +CREATE USER glpk@localhost IDENTIFIED BY 'gnu'; +GRANT ALL PRIVILEGES ON glpk.* TO glpk@localhost; +USE glpk; +DROP TABLE sudoku; +CREATE TABLE sudoku ( + ID INT , + COL INT , + LIN INT , + VAL INT , + PRIMARY KEY ( ID, COL, LIN ) + ); +DROP TABLE sudoku_solution; +CREATE TABLE sudoku_solution ( + ID INT , + COL INT , + LIN INT , + VAL INT , + PRIMARY KEY ( ID, COL, LIN ) + ); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 1, 5); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 2, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 3, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 4, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 5, 4); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 6, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 7, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 8, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 1, 9, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 1, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 2, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 3, 3); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 4, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 5, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 6, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 7, 6); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 8, 2); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 2, 9, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 1, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 2, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 3, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 4, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 5, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 6, 9); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 7, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 8, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 3, 9, 4); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 1, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 2, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 3, 6); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 4, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 5, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 6, 7); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 7, 2); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 8, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 4, 9, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 1, 8); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 2, 1); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 3, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 4, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 5, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 6, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 7, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 8, 4); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 5, 9, 3); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 1, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 2, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 3, 9); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 4, 1); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 5, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 6, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 7, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 8, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 6, 9, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 1, 7); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 2, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 3, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 4, 5); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 5, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 6, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 7, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 8, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 7, 9, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 1, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 2, 9); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 3, 2); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 4, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 5, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 6, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 7, 8); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 8, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 8, 9, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 1, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 2, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 3, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 4, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 5, 3); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 6, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 7, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 8, 0); +INSERT INTO sudoku (ID, COL, LIN, VAL) VALUES (1, 9, 9, 6); diff -r d59bea55db9b -r c445c931472f examples/sql/sudoku_mysql.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sql/sudoku_mysql.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,113 @@ +/* SUDOKU, Number Placement Puzzle */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* This example shows how to use the table statement. + The sudoku to be solves is read from file sudoku_in.csv. + The solution is written to sudoku_out.csv. + The file format is CSV as defined in + RFC 4180 - Common Format and MIME Type for + Comma-Separated Values (CSV) Files */ + +/* Sudoku, also known as Number Place, is a logic-based placement + puzzle. The aim of the canonical puzzle is to enter a numerical + digit from 1 through 9 in each cell of a 9x9 grid made up of 3x3 + subgrids (called "regions"), starting with various digits given in + some cells (the "givens"). Each row, column, and region must contain + only one instance of each numeral. + + Example: + + +-------+-------+-------+ + | 5 3 . | . 7 . | . . . | + | 6 . . | 1 9 5 | . . . | + | . 9 8 | . . . | . 6 . | + +-------+-------+-------+ + | 8 . . | . 6 . | . . 3 | + | 4 . . | 8 . 3 | . . 1 | + | 7 . . | . 2 . | . . 6 | + +-------+-------+-------+ + | . 6 . | . . . | 2 8 . | + | . . . | 4 1 9 | . . 5 | + | . . . | . 8 . | . 7 9 | + +-------+-------+-------+ + + (From Wikipedia, the free encyclopedia.) */ +set fields dimen 2; + +param id; + +param givens{1..9, 1..9}, integer, >= 0, <= 9, default 0; +/* the "givens" */ + +/* +table ti IN 'MySQL' 'Database=glpk;UID=glpk;PWD=gnu' + 'sudoku' : + fields <- [COL, LIN], givens ~ VAL; +*/ +table ti IN 'MySQL' 'Database=glpk;UID=glpk;PWD=gnu' + 'SELECT * FROM sudoku WHERE ID = ' & id : + fields <- [COL, LIN], givens ~ VAL; + +var x{i in 1..9, j in 1..9, k in 1..9}, binary; +/* x[i,j,k] = 1 means cell [i,j] is assigned number k */ + +s.t. fa{i in 1..9, j in 1..9, k in 1..9: givens[i,j] != 0}: + x[i,j,k] = (if givens[i,j] = k then 1 else 0); +/* assign pre-defined numbers using the "givens" */ + +s.t. fb{i in 1..9, j in 1..9}: sum{k in 1..9} x[i,j,k] = 1; +/* each cell must be assigned exactly one number */ + +s.t. fc{i in 1..9, k in 1..9}: sum{j in 1..9} x[i,j,k] = 1; +/* cells in the same row must be assigned distinct numbers */ + +s.t. fd{j in 1..9, k in 1..9}: sum{i in 1..9} x[i,j,k] = 1; +/* cells in the same column must be assigned distinct numbers */ + +s.t. fe{I in 1..9 by 3, J in 1..9 by 3, k in 1..9}: + sum{i in I..I+2, j in J..J+2} x[i,j,k] = 1; +/* cells in the same region must be assigned distinct numbers */ + +/* there is no need for an objective function here */ + +solve; + +table ta{(i,j) in fields} OUT + 'MySQL' 'Database=glpk;UID=glpk;PWD=gnu' + 'DELETE FROM sudoku_solution' + 'WHERE ID = ' & id & ';' + 'INSERT INTO sudoku_solution' + '(ID, COL, LIN, VAL)' + 'VALUES(?, ?, ?, ?);' : + id ~ ID, i ~ COL, j ~ LIN, (sum{k in 1..9} x[i,j,k] * k) ~ VAL; + +printf "\nSudoku to be solved\n"; +for {i in 1..9} +{ for {0..0: i = 1 or i = 4 or i = 7} + printf " +-------+-------+-------+\n"; + for {j in 1..9} + { for {0..0: j = 1 or j = 4 or j = 7} printf(" |"); + printf " %d", givens[i,j]; + for {0..0: j = 9} printf(" |\n"); + } + for {0..0: i = 9} + printf " +-------+-------+-------+\n"; + } +printf "\nSolution\n"; +for {i in 1..9} +{ for {0..0: i = 1 or i = 4 or i = 7} + printf " +-------+-------+-------+\n"; + for {j in 1..9} + { for {0..0: j = 1 or j = 4 or j = 7} printf(" |"); + printf " %d", sum{k in 1..9} x[i,j,k] * k; + for {0..0: j = 9} printf(" |\n"); + } + for {0..0: i = 9} + printf " +-------+-------+-------+\n"; +} + +data; + +param id := 1; +end; diff -r d59bea55db9b -r c445c931472f examples/sql/sudoku_odbc.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sql/sudoku_odbc.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,111 @@ +/* SUDOKU, Number Placement Puzzle */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* This example shows how to use the table statement. + The sudoku to be solves is read from file sudoku_in.csv. + The solution is written to sudoku_out.csv. + The file format is CSV as defined in + RFC 4180 - Common Format and MIME Type for + Comma-Separated Values (CSV) Files */ + +/* Sudoku, also known as Number Place, is a logic-based placement + puzzle. The aim of the canonical puzzle is to enter a numerical + digit from 1 through 9 in each cell of a 9x9 grid made up of 3x3 + subgrids (called "regions"), starting with various digits given in + some cells (the "givens"). Each row, column, and region must contain + only one instance of each numeral. + + Example: + + +-------+-------+-------+ + | 5 3 . | . 7 . | . . . | + | 6 . . | 1 9 5 | . . . | + | . 9 8 | . . . | . 6 . | + +-------+-------+-------+ + | 8 . . | . 6 . | . . 3 | + | 4 . . | 8 . 3 | . . 1 | + | 7 . . | . 2 . | . . 6 | + +-------+-------+-------+ + | . 6 . | . . . | 2 8 . | + | . . . | 4 1 9 | . . 5 | + | . . . | . 8 . | . 7 9 | + +-------+-------+-------+ + + (From Wikipedia, the free encyclopedia.) */ +set fields dimen 2; + +param id; + +param givens{1..9, 1..9}, integer, >= 0, <= 9, default 0; +/* the "givens" */ + +table ti IN 'iODBC' + 'DSN=glpk;UID=glpk;PWD=gnu' + 'SELECT * FROM sudoku' + 'WHERE ID = ' & id : + fields <- [COL, LIN], givens ~ VAL; + +var x{i in 1..9, j in 1..9, k in 1..9}, binary; +/* x[i,j,k] = 1 means cell [i,j] is assigned number k */ + +s.t. fa{i in 1..9, j in 1..9, k in 1..9: givens[i,j] != 0}: + x[i,j,k] = (if givens[i,j] = k then 1 else 0); +/* assign pre-defined numbers using the "givens" */ + +s.t. fb{i in 1..9, j in 1..9}: sum{k in 1..9} x[i,j,k] = 1; +/* each cell must be assigned exactly one number */ + +s.t. fc{i in 1..9, k in 1..9}: sum{j in 1..9} x[i,j,k] = 1; +/* cells in the same row must be assigned distinct numbers */ + +s.t. fd{j in 1..9, k in 1..9}: sum{i in 1..9} x[i,j,k] = 1; +/* cells in the same column must be assigned distinct numbers */ + +s.t. fe{I in 1..9 by 3, J in 1..9 by 3, k in 1..9}: + sum{i in I..I+2, j in J..J+2} x[i,j,k] = 1; +/* cells in the same region must be assigned distinct numbers */ + +/* there is no need for an objective function here */ + + +solve; + +table ta {(i, j) in {i1 in 1..9} cross {i2 in 1..9}} OUT + 'iODBC' 'DSN=glpk;UID=glpk;PWD=gnu' + 'DELETE FROM sudoku_solution' + 'WHERE ID = ' & id & ';' + 'INSERT INTO sudoku_solution' + '(ID, COL, LIN, VAL)' + 'VALUES(?, ?, ?, ?);' : + id ~ ID, i ~ COL, j ~ LIN, (sum{k in 1..9} x[i,j,k] * k) ~ VAL; + +printf "\nSudoku to be solved\n"; +for {i in 1..9} +{ for {0..0: i = 1 or i = 4 or i = 7} + printf " +-------+-------+-------+\n"; + for {j in 1..9} + { for {0..0: j = 1 or j = 4 or j = 7} printf(" |"); + printf " %d", givens[i,j]; + for {0..0: j = 9} printf(" |\n"); + } + for {0..0: i = 9} + printf " +-------+-------+-------+\n"; + } +printf "\nSolution\n"; +for {i in 1..9} +{ for {0..0: i = 1 or i = 4 or i = 7} + printf " +-------+-------+-------+\n"; + for {j in 1..9} + { for {0..0: j = 1 or j = 4 or j = 7} printf(" |"); + printf " %d", sum{k in 1..9} x[i,j,k] * k; + for {0..0: j = 9} printf(" |\n"); + } + for {0..0: i = 9} + printf " +-------+-------+-------+\n"; +} + +data; + +param id := 1; +end; diff -r d59bea55db9b -r c445c931472f examples/sql/transp.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sql/transp.sql Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,45 @@ +CREATE DATABASE glpk; +CREATE USER glpk@localhost IDENTIFIED BY 'gnu'; +GRANT ALL PRIVILEGES ON glpk.* TO glpk@localhost; +USE glpk; +# production capacity +DROP TABLE transp_capa; +CREATE TABLE transp_capa ( + PLANT TEXT(127), + CAPA REAL, + PRIMARY KEY ( PLANT(127) ) + ); +INSERT INTO transp_capa ( PLANT, CAPA ) VALUES ( 'Seattle', 350 ); +INSERT INTO transp_capa ( PLANT, CAPA ) VALUES ( 'San Diego', 600 ); +# demand +DROP TABLE transp_demand; +CREATE TABLE transp_demand ( + MARKET TEXT(127), + DEMAND REAL, + PRIMARY KEY ( MARKET(127) ) + ); +INSERT INTO transp_demand ( MARKET, DEMAND ) VALUES ( 'New York', 325 ); +INSERT INTO transp_demand ( MARKET, DEMAND ) VALUES ( 'Chicago', 300 ); +INSERT INTO transp_demand ( MARKET, DEMAND ) VALUES ( 'Topeka', 275 ); +# distance +DROP TABLE transp_dist; +CREATE TABLE transp_dist ( + LOC1 TEXT(127), + LOC2 TEXT(127), + DIST REAL, + PRIMARY KEY ( LOC1(127), LOC2(127) ) + ); +INSERT INTO transp_dist ( LOC1, LOC2, DIST ) VALUES ( 'Seattle', 'New York', 2.5 ); +INSERT INTO transp_dist ( LOC1, LOC2, DIST ) VALUES ( 'Seattle', 'Chicago', 1.7 ); +INSERT INTO transp_dist ( LOC1, LOC2, DIST ) VALUES ( 'Seattle', 'Topeka', 1.8 ); +INSERT INTO transp_dist ( LOC1, LOC2, DIST ) VALUES ( 'San Diego', 'New York', 2.5 ); +INSERT INTO transp_dist ( LOC1, LOC2, DIST ) VALUES ( 'San Diego', 'Chicago', 1.8 ); +INSERT INTO transp_dist ( LOC1, LOC2, DIST ) VALUES ( 'San Diego', 'Topeka', 1.4 ); +# result +DROP TABLE transp_result; +CREATE TABLE transp_result ( + LOC1 TEXT(127), + LOC2 TEXT(127), + QUANTITY REAL, + PRIMARY KEY ( LOC1(127), LOC2(127) ) + ); diff -r d59bea55db9b -r c445c931472f examples/sql/transp_mysql.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sql/transp_mysql.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,71 @@ +# A TRANSPORTATION PROBLEM +# +# This problem finds a least cost shipping schedule that meets +# requirements at markets and supplies at factories. +# +# References: +# Dantzig G B, "Linear Programming and Extensions." +# Princeton University Press, Princeton, New Jersey, 1963, +# Chapter 3-3. + +set I; +/* canning plants */ + +param a{i in I}; +/* capacity of plant i in cases */ + +table plants IN "MySQL" + 'Database=glpk;UID=glpk;PWD=gnu' + 'SELECT PLANT, CAPA AS CAPACITY FROM transp_capa' : + I <- [ PLANT ], a ~ CAPACITY; + +set J; +/* markets */ + +param b{j in J}; +/* demand at market j in cases */ + +table markets IN "MySQL" + 'Database=glpk;UID=glpk;PWD=gnu' + 'transp_demand' : + J <- [ MARKET ], b ~ DEMAND; + +param d{i in I, j in J}; +/* distance in thousands of miles */ + +table dist IN "MySQL" + 'Database=glpk;UID=glpk;PWD=gnu' + 'transp_dist' : + [ LOC1, LOC2 ], d ~ DIST; + +param f; +/* freight in dollars per case per thousand miles */ + +param c{i in I, j in J} := f * d[i,j] / 1000; +/* transport cost in thousands of dollars per case */ + +var x{i in I, j in J} >= 0; +/* shipment quantities in cases */ + +minimize cost: sum{i in I, j in J} c[i,j] * x[i,j]; +/* total transportation costs in thousands of dollars */ + +s.t. supply{i in I}: sum{j in J} x[i,j] <= a[i]; +/* observe supply limit at plant i */ + +s.t. demand{j in J}: sum{i in I} x[i,j] >= b[j]; +/* satisfy demand at market j */ + +solve; + +table result{i in I, j in J: x[i,j]} OUT "MySQL" + 'Database=glpk;UID=glpk;PWD=gnu' + 'DELETE FROM transp_result;' + 'INSERT INTO transp_result VALUES (?,?,?)' : + i ~ LOC1, j ~ LOC2, x[i,j] ~ QUANTITY; + +data; + +param f := 90; + +end; diff -r d59bea55db9b -r c445c931472f examples/sql/transp_odbc.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sql/transp_odbc.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,72 @@ +# A TRANSPORTATION PROBLEM +# +# This problem finds a least cost shipping schedule that meets +# requirements at markets and supplies at factories. +# +# References: +# Dantzig G B, "Linear Programming and Extensions." +# Princeton University Press, Princeton, New Jersey, 1963, +# Chapter 3-3. + +set I; +/* canning plants */ + +param a{i in I}; +/* capacity of plant i in cases */ + +table plants IN "iODBC" + 'DSN=glpk;UID=glpk;PWD=gnu' + 'SELECT PLANT, CAPA AS CAPACITY' + 'FROM transp_capa' : + I <- [ PLANT ], a ~ CAPACITY; + +set J; +/* markets */ + +param b{j in J}; +/* demand at market j in cases */ + +table markets IN "iODBC" + 'DSN=glpk;UID=glpk;PWD=gnu' + 'transp_demand' : + J <- [ MARKET ], b ~ DEMAND; + +param d{i in I, j in J}; +/* distance in thousands of miles */ + +table dist IN "iODBC" + 'DSN=glpk;UID=glpk;PWD=gnu' + 'transp_dist' : + [ LOC1, LOC2 ], d ~ DIST; + +param f; +/* freight in dollars per case per thousand miles */ + +param c{i in I, j in J} := f * d[i,j] / 1000; +/* transport cost in thousands of dollars per case */ + +var x{i in I, j in J} >= 0; +/* shipment quantities in cases */ + +minimize cost: sum{i in I, j in J} c[i,j] * x[i,j]; +/* total transportation costs in thousands of dollars */ + +s.t. supply{i in I}: sum{j in J} x[i,j] <= a[i]; +/* observe supply limit at plant i */ + +s.t. demand{j in J}: sum{i in I} x[i,j] >= b[j]; +/* satisfy demand at market j */ + +solve; + +table result{i in I, j in J: x[i,j]} OUT "iODBC" + 'DSN=glpk;UID=glpk;PWD=gnu' + 'DELETE FROM transp_result;' + 'INSERT INTO transp_result VALUES (?,?,?)' : + i ~ LOC1, j ~ LOC2, x[i,j] ~ QUANTITY; + +data; + +param f := 90; + +end; diff -r d59bea55db9b -r c445c931472f examples/stigler.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/stigler.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,411 @@ +/* STIGLER, original Stigler's 1939 diet problem */ + +/* The Stigler Diet is an optimization problem named for George Stigler, + a 1982 Nobel Laureate in economics, who posed the following problem: + For a moderately active man weighing 154 pounds, how much of each of + 77 foods should be eaten on a daily basis so that the man's intake of + nine nutrients will be at least equal to the recommended dietary + allowances (RDSs) suggested by the National Research Council in 1943, + with the cost of the diet being minimal? + + The nutrient RDAs required to be met in Stigler's experiment were + calories, protein, calcium, iron, vitamin A, thiamine, riboflavin, + niacin, and ascorbic acid. The result was an annual budget allocated + to foods such as evaporated milk, cabbage, dried navy beans, and beef + liver at a cost of approximately $0.11 a day in 1939 U.S. dollars. + + While the name "Stigler Diet" was applied after the experiment by + outsiders, according to Stigler, "No one recommends these diets for + anyone, let alone everyone." The Stigler diet has been much ridiculed + for its lack of variety and palatability, however his methodology has + received praise and is considered to be some of the earliest work in + linear programming. + + The Stigler diet question is a linear programming problem. Lacking + any sophisticated method of solving such a problem, Stigler was + forced to utilize heuristic methods in order to find a solution. The + diet question originally asked in which quantities a 154 pound male + would have to consume 77 different foods in order to fulfill the + recommended intake of 9 different nutrients while keeping expense at + a minimum. Through "trial and error, mathematical insight and + agility," Stigler was able to eliminate 62 of the foods from the + original 77 (these foods were removed based because they lacked + nutrients in comparison to the remaining 15). From the reduced list, + Stigler calculated the required amounts of each of the remaining 15 + foods to arrive at a cost-minimizing solution to his question. + According to Stigler's calculations, the annual cost of his solution + was $39.93 in 1939 dollars. When corrected for inflation using the + consumer price index, the cost of the diet in 2005 dollars is + $561.43. The specific combination of foods and quantities is as + follows: + + Stigler's 1939 Diet + + Food Annual Quantities Annual Cost + ---------------- ----------------- ----------- + Wheat Flour 370 lb. $13.33 + Evaporated Milk 57 cans 3.84 + Cabbage 111 lb. 4.11 + Spinach 23 lb. 1.85 + Dried Navy Beans 285 lb. 16.80 + ---------------------------------------------- + Total Annual Cost $39.93 + + The 9 nutrients that Stigler's diet took into consideration and their + respective recommended daily amounts were: + + Table of nutrients considered in Stigler's diet + + Nutrient Daily Recommended Intake + ------------------------- ------------------------ + Calories 3,000 Calories + Protein 70 grams + Calcium .8 grams + Iron 12 milligrams + Vitamin A 5,000 IU + Thiamine (Vitamin B1) 1.8 milligrams + Riboflavin (Vitamin B2) 2.7 milligrams + Niacin 18 milligrams + Ascorbic Acid (Vitamin C) 75 milligrams + + Seven years after Stigler made his initial estimates, the development + of George Dantzig's Simplex algorithm made it possible to solve the + problem without relying on heuristic methods. The exact value was + determined to be $39.69 (using the original 1939 data). Dantzig's + algorithm describes a method of traversing the vertices of a polytope + of N+1 dimensions in order to find the optimal solution to a specific + situation. + + (From Wikipedia, the free encyclopedia.) */ + +/* Translated from GAMS by Andrew Makhorin . + + For the original GAMS model stigler1939.gms see [3]. + + References: + + 1. George J. Stigler, "The Cost of Subsistence," J. Farm Econ. 27, + 1945, pp. 303-14. + + 2. National Research Council, "Recommended Daily Allowances," Reprint + and Circular Series No. 115, January, 1943. + + 3. Erwin Kalvelagen, "Model building with GAMS," Chapter 2, "Building + linear programming models," pp. 128-34. */ + +set C; +/* commodities */ + +check card(C) = 77; +/* there must be 77 commodities */ + +set N; +/* nutrients */ + +param data{c in C, {"price", "weight"} union N}; +/* nutritive values per dollar of expenditure */ + +param allowance{n in N}; +/* recommended daily allowance for a moderately active man */ + +var x{c in C}, >= 0; +/* dollars of food to be purchased daily */ + +s.t. nb{n in N}: sum{c in C} data[c,n] * x[c] >= allowance[n]; +/* nutrient balance */ + +minimize cost: sum{c in C} x[c]; +/* total food bill */ + +solve; + +param days := 365.25; +/* days in a year */ + +param commodity{c in C}, symbolic; + +param unit{c in C}, symbolic; + +printf "\n"; +printf "MINIMUM COST ANNUAL DIET\n"; +printf "\n"; +printf " Commodity Unit Quantity Cost \n"; +printf "------------------------- ---------- ---------- ----------\n"; +printf{c in C: x[c] != 0} "%-25s %10s %10.2f $%7.2f\n", commodity[c], + unit[c], 100 * days * x[c] / data[c,"price"], days * x[c]; +printf " -----------------\n"; +printf " Total: $%7.2f\n", + days * sum{c in C} x[c]; +printf "\n"; + +data; + +param : C : commodity unit := +flour "Wheat Flour (Enriched)" "10 lb." +macaroni "Macaroni" "1 lb." +cereal "Wheat Cereal (Enriched)" "28 oz." +cornflakes "Corn Flakes" "8 oz." +cornmeal "Corn Meal" "1 lb." +grits "Hominy Grits" "24 oz." +rice "Rice" "1 lb." +oats "Rolled Oats" "1 lb." +whitebread "White Bread (Enriched)" "1 lb." +wheatbread "Whole Wheat Bread" "1 lb." +ryebread "Rye Bread" "1 lb." +poundcake "Pound Cake" "1 lb." +crackers "Soda Crackers" "1 lb." +milk "Milk" "1 qt." +evapmild "Evaporated Milk (can)" "14.5 oz." +butter "Butter" "1 lb." +margarine "Oleomargarine" "1 lb." +eggs "Eggs" "1 doz." +cheese "Cheese (Cheddar)" "1 lb." +cream "Cream" "1/2 pt." +peanutbutter "Peanut Butter" "1 lb." +mayonnaise "Mayonnaise" "1/2 pt." +crisco "Crisco" "1 lb." +lard "Lard" "1 lb." +sirloinsteak "Sirloin Steak" "1 lb." +roundsteak "Round Steak" "1 lb." +ribroast "Rib Roast" "1 lb." +chuckroast "Chuck Roast" "1 lb." +plate "Plate" "1 lb." +liver "Liver (Beef)" "1 lb." +lambleg "Leg of Lamb" "1 lb." +lambchops "Lamb Chops (Rib)" "1 lb." +porkchops "Pork Chops" "1 lb." +porkroast "Pork Loin Roast" "1 lb." +bacon "Bacon" "1 lb." +ham "Ham - smoked" "1 lb." +saltpork "Salt Pork" "1 lb." +chicken "Roasting Chicken" "1 lb." +veal "Veal Cutlets" "1 lb." +salmon "Salmon, Pink (can)" "16 oz." +apples "Apples" "1 lb." +bananas "Bananas" "1 lb." +lemons "Lemons" "1 doz." +oranges "Oranges" "1 doz." +greenbeans "Green Beans" "1 lb." +cabbage "Cabbage" "1 lb." +carrots "Carrots" "1 bunch" +celery "Celery" "1 stalk" +lettuce "Lettuce" "1 head" +onions "Onions" "1 lb." +potatoes "Potatoes" "15 lb." +spinach "Spinach" "1 lb." +sweetpotato "Sweet Potatoes" "1 lb." +peaches "Peaches (can)" "No. 2 1/2" +pears "Pears (can)" "No. 2 1/2" +pineapple "Pineapple (can)" "No. 2 1/2" +asparagus "Asparagus (can)" "No. 2" +cannedgrbn "Grean Beans (can)" "No. 2" +porkbeans "Pork and Beans (can)" "16 oz." +corn "Corn (can)" "No. 2" +peas "Peas (can)" "No. 2" +tomatoes "Tomatoes (can)" "No. 2" +tomatosoup "Tomato Soup (can)" "10 1/2 oz." +driedpeach "Peaches, Dried" "1 lb." +prunes "Prunes, Dried" "1 lb." +raisins "Raisins, Dried" "15 oz." +driedpeas "Peas, Dried" "1 lb." +limabeans "Lima Beans, Dried" "1 lb." +navybeans "Navy Beans, Dried" "1 lb." +coffee "Coffee" "1 lb." +tea "Tea" "1/4 lb." +cocoa "Cocoa" "8 oz." +chocolate "Chocolate" "8 oz." +sugar "Sugar" "10 lb." +cornsirup "Corn Sirup" "24 oz." +molasses "Molasses" "18 oz." +stawberry "Strawberry Preserve" "1 lb." +; + +set N := +calories /* Calories, unit = 1000 */ +protein /* Protein, unit = grams */ +calcium /* Calcium, unit = grams */ +iron /* Iron, unit = milligrams */ +vitaminA /* Vitamin A, unit = 1000 International Units */ +thiamine /* Thiamine, Vit. B1, unit = milligrams */ +riboflavin /* Riboflavin, Vit. B2, unit = milligrams */ +niacin /* Niacin (Nicotinic Acid), unit = milligrams */ +ascorbicAcid /* Ascorbic Acid, Vit. C, unit = milligrams */ +; + +param data +: price weight calories protein calcium iron := +# aug. 15 edible +# 1939 per $1 +# (cents) (grams) (1000) (grams) (grams) (mg.) +flour 36.0 12600 44.7 1411 2.0 365 +macaroni 14.1 3217 11.6 418 .7 54 +cereal 24.2 3280 11.8 377 14.4 175 +cornflakes 7.1 3194 11.4 252 .1 56 +cornmeal 4.6 9861 36.0 897 1.7 99 +grits 8.5 8005 28.6 680 .8 80 +rice 7.5 6048 21.2 460 .6 41 +oats 7.1 6389 25.3 907 5.1 341 +whitebread 7.9 5742 15.6 488 2.5 115 +wheatbread 9.1 4985 12.2 484 2.7 125 +ryebread 9.2 4930 12.4 439 1.1 82 +poundcake 24.8 1829 8.0 130 .4 31 +crackers 15.1 3004 12.5 288 .5 50 +milk 11.0 8867 6.1 310 10.5 18 +evapmild 6.7 6035 8.4 422 15.1 9 +butter 20.8 1473 10.8 9 .2 3 +margarine 16.1 2817 20.6 17 .6 6 +eggs 32.6 1857 2.9 238 1.0 52 +cheese 24.2 1874 7.4 448 16.4 19 +cream 14.1 1689 3.5 49 1.7 3 +peanutbutter 17.9 2534 15.7 661 1.0 48 +mayonnaise 16.7 1198 8.6 18 .2 8 +crisco 20.3 2234 20.1 0 .0 0 +lard 9.8 4628 41.7 0 .0 0 +sirloinsteak 39.6 1145 2.9 166 .1 34 +roundsteak 36.4 1246 2.2 214 .1 32 +ribroast 29.2 1553 3.4 213 .1 33 +chuckroast 22.6 2007 3.6 309 .2 46 +plate 14.6 3107 8.5 404 .2 62 +liver 26.8 1692 2.2 333 .2 139 +lambleg 27.6 1643 3.1 245 .1 20 +lambchops 36.6 1239 3.3 140 .1 15 +porkchops 30.7 1477 3.5 196 .2 80 +porkroast 24.2 1874 4.4 249 .3 37 +bacon 25.6 1772 10.4 152 .2 23 +ham 27.4 1655 6.7 212 .2 31 +saltpork 16.0 2835 18.8 164 .1 26 +chicken 30.3 1497 1.8 184 .1 30 +veal 42.3 1072 1.7 156 .1 24 +salmon 13.0 3489 5.8 705 6.8 45 +apples 4.4 9072 5.8 27 .5 36 +bananas 6.1 4982 4.9 60 .4 30 +lemons 26.0 2380 1.0 21 .5 14 +oranges 30.9 4439 2.2 40 1.1 18 +greenbeans 7.1 5750 2.4 138 3.7 80 +cabbage 3.7 8949 2.6 125 4.0 36 +carrots 4.7 6080 2.7 73 2.8 43 +celery 7.3 3915 .9 51 3.0 23 +lettuce 8.2 2247 .4 27 1.1 22 +onions 3.6 11844 5.8 166 3.8 59 +potatoes 34.0 16810 14.3 336 1.8 118 +spinach 8.1 4592 1.1 106 .0 138 +sweetpotato 5.1 7649 9.6 138 2.7 54 +peaches 16.8 4894 3.7 20 .4 10 +pears 20.4 4030 3.0 8 .3 8 +pineapple 21.3 3993 2.4 16 .4 8 +asparagus 27.7 1945 .4 33 .3 12 +cannedgrbn 10.0 5386 1.0 54 2.0 65 +porkbeans 7.1 6389 7.5 364 4.0 134 +corn 10.4 5452 5.2 136 .2 16 +peas 13.8 4109 2.3 136 .6 45 +tomatoes 8.6 6263 1.3 63 .7 38 +tomatosoup 7.6 3917 1.6 71 .6 43 +driedpeach 15.7 2889 8.5 87 1.7 173 +prunes 9.0 4284 12.8 99 2.5 154 +raisins 9.4 4524 13.5 104 2.5 136 +driedpeas 7.9 5742 20.0 1367 4.2 345 +limabeans 8.9 5097 17.4 1055 3.7 459 +navybeans 5.9 7688 26.9 1691 11.4 792 +coffee 22.4 2025 .0 0 .0 0 +tea 17.4 652 .0 0 .0 0 +cocoa 8.6 2637 8.7 237 3.0 72 +chocolate 16.2 1400 8.0 77 1.3 39 +sugar 51.7 8773 34.9 0 .0 0 +cornsirup 13.7 4996 14.7 0 .5 74 +molasses 13.6 3752 9.0 0 10.3 244 +stawberry 20.5 2213 6.4 11 .4 7 + +: vitaminA thiamine riboflavin niacin ascorbicAcid := +# (1000 IU) (mg.) (mg.) (mg.) (mg.) +flour .0 55.4 33.3 441 0 +macaroni .0 3.2 1.9 68 0 +cereal .0 14.4 8.8 114 0 +cornflakes .0 13.5 2.3 68 0 +cornmeal 30.9 17.4 7.9 106 0 +grits .0 10.6 1.6 110 0 +rice .0 2.0 4.8 60 0 +oats .0 37.1 8.9 64 0 +whitebread .0 13.8 8.5 126 0 +wheatbread .0 13.9 6.4 160 0 +ryebread .0 9.9 3.0 66 0 +poundcake 18.9 2.8 3.0 17 0 +crackers .0 .0 .0 0 0 +milk 16.8 4.0 16.0 7 177 +evapmild 26.0 3.0 23.5 11 60 +butter 44.2 .0 .2 2 0 +margarine 55.8 .2 .0 0 0 +eggs 18.6 2.8 6.5 1 0 +cheese 28.1 .8 10.3 4 0 +cream 16.9 .6 2.5 0 17 +peanutbutter .0 9.6 8.1 471 0 +mayonnaise 2.7 .4 .5 0 0 +crisco .0 .0 .0 0 0 +lard .2 .0 .5 5 0 +sirloinsteak .2 2.1 2.9 69 0 +roundsteak .4 2.5 2.4 87 0 +ribroast .0 .0 2.0 0 0 +chuckroast .4 1.0 4.0 120 0 +plate .0 .9 .0 0 0 +liver 169.2 6.4 50.8 316 525 +lambleg .0 2.8 3.0 86 0 +lambchops .0 1.7 2.7 54 0 +porkchops .0 17.4 2.7 60 0 +porkroast .0 18.2 3.6 79 0 +bacon .0 1.8 1.8 71 0 +ham .0 9.9 3.3 50 0 +saltpork .0 1.4 1.8 0 0 +chicken .1 .9 1.8 68 46 +veal .0 1.4 2.4 57 0 +salmon 3.5 1.0 4.9 209 0 +apples 7.3 3.6 2.7 5 544 +bananas 17.4 2.5 3.5 28 498 +lemons .0 .5 .0 4 952 +oranges 11.1 3.6 1.3 10 1993 +greenbeans 69.0 4.3 5.8 37 862 +cabbage 7.2 9.0 4.5 26 5369 +carrots 188.5 6.1 4.3 89 608 +celery .9 1.4 1.4 9 313 +lettuce 112.4 1.8 3.4 11 449 +onions 16.6 4.7 5.9 21 1184 +potatoes 6.7 29.4 7.1 198 2522 +spinach 918.4 5.7 13.8 33 2755 +sweetpotato 290.7 8.4 5.4 83 1912 +peaches 21.5 .5 1.0 31 196 +pears .8 .8 .8 5 81 +pineapple 2.0 2.8 .8 7 399 +asparagus 16.3 1.4 2.1 17 272 +cannedgrbn 53.9 1.6 4.3 32 431 +porkbeans 3.5 8.3 7.7 56 0 +corn 12.0 1.6 2.7 42 218 +peas 34.9 4.9 2.5 37 370 +tomatoes 53.2 3.4 2.5 36 1253 +tomatosoup 57.9 3.5 2.4 67 862 +driedpeach 86.8 1.2 4.3 55 57 +prunes 85.7 3.9 4.3 65 257 +raisins 4.5 6.3 1.4 24 136 +driedpeas 2.9 28.7 18.4 162 0 +limabeans 5.1 26.9 38.2 93 0 +navybeans .0 38.4 24.6 217 0 +coffee .0 4.0 5.1 50 0 +tea .0 .0 2.3 42 0 +cocoa .0 2.0 11.9 40 0 +chocolate .0 .9 3.4 14 0 +sugar .0 .0 .0 0 0 +cornsirup .0 .0 .0 5 0 +molasses .0 1.9 7.5 146 0 +stawberry .2 .2 .4 3 0 +; + +param allowance := +calories 3 +protein 70 +calcium .8 +iron 12 +vitaminA 5 +thiamine 1.8 +riboflavin 2.7 +niacin 18 +ascorbicAcid 75 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/sudoku.dat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sudoku.dat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,16 @@ +/* sudoku.dat, a hard Sudoku puzzle which causes branching */ + +data; + +param givens : 1 2 3 4 5 6 7 8 9 := + 1 1 . . . . . 7 . . + 2 . 2 . . . . 5 . . + 3 6 . . 3 8 . . . . + 4 . 7 8 . . . . . . + 5 . . . 6 . 9 . . . + 6 . . . . . . 1 4 . + 7 . . . . 2 5 . . 9 + 8 . . 3 . . . . 6 . + 9 . . 4 . . . . . 2 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/sudoku.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/sudoku.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,84 @@ +/* SUDOKU, Number Placement Puzzle */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* Sudoku, also known as Number Place, is a logic-based placement + puzzle. The aim of the canonical puzzle is to enter a numerical + digit from 1 through 9 in each cell of a 9x9 grid made up of 3x3 + subgrids (called "regions"), starting with various digits given in + some cells (the "givens"). Each row, column, and region must contain + only one instance of each numeral. + + Example: + + +-------+-------+-------+ + | 5 3 . | . 7 . | . . . | + | 6 . . | 1 9 5 | . . . | + | . 9 8 | . . . | . 6 . | + +-------+-------+-------+ + | 8 . . | . 6 . | . . 3 | + | 4 . . | 8 . 3 | . . 1 | + | 7 . . | . 2 . | . . 6 | + +-------+-------+-------+ + | . 6 . | . . . | 2 8 . | + | . . . | 4 1 9 | . . 5 | + | . . . | . 8 . | . 7 9 | + +-------+-------+-------+ + + (From Wikipedia, the free encyclopedia.) */ + +param givens{1..9, 1..9}, integer, >= 0, <= 9, default 0; +/* the "givens" */ + +var x{i in 1..9, j in 1..9, k in 1..9}, binary; +/* x[i,j,k] = 1 means cell [i,j] is assigned number k */ + +s.t. fa{i in 1..9, j in 1..9, k in 1..9: givens[i,j] != 0}: + x[i,j,k] = (if givens[i,j] = k then 1 else 0); +/* assign pre-defined numbers using the "givens" */ + +s.t. fb{i in 1..9, j in 1..9}: sum{k in 1..9} x[i,j,k] = 1; +/* each cell must be assigned exactly one number */ + +s.t. fc{i in 1..9, k in 1..9}: sum{j in 1..9} x[i,j,k] = 1; +/* cells in the same row must be assigned distinct numbers */ + +s.t. fd{j in 1..9, k in 1..9}: sum{i in 1..9} x[i,j,k] = 1; +/* cells in the same column must be assigned distinct numbers */ + +s.t. fe{I in 1..9 by 3, J in 1..9 by 3, k in 1..9}: + sum{i in I..I+2, j in J..J+2} x[i,j,k] = 1; +/* cells in the same region must be assigned distinct numbers */ + +/* there is no need for an objective function here */ + +solve; + +for {i in 1..9} +{ for {0..0: i = 1 or i = 4 or i = 7} + printf " +-------+-------+-------+\n"; + for {j in 1..9} + { for {0..0: j = 1 or j = 4 or j = 7} printf(" |"); + printf " %d", sum{k in 1..9} x[i,j,k] * k; + for {0..0: j = 9} printf(" |\n"); + } + for {0..0: i = 9} + printf " +-------+-------+-------+\n"; +} + +data; + +/* These data correspond to the example above. */ + +param givens : 1 2 3 4 5 6 7 8 9 := + 1 5 3 . . 7 . . . . + 2 6 . . 1 9 5 . . . + 3 . 9 8 . . . . 6 . + 4 8 . . . 6 . . . 3 + 5 4 . . 8 . 3 . . 1 + 6 7 . . . 2 . . . 6 + 7 . 6 . . . . 2 8 . + 8 . . . 4 1 9 . . 5 + 9 . . . . 8 . . 7 9 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/t1.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/t1.cs Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,99 @@ +/*Find the minimum value which satisfies the linear inequality: + a*x + b*y >= 1 {a,b,x,y Integers} {a,b > 0} {a,b entered on command line} + + Nigel_Galloway@operamail.com + February 2008 +*/ +using System; +using System.Runtime.InteropServices; + +unsafe class GLPK{ + double *lp; + public int a; + public int b; + + const string glpkLibrary = "libglpk.so"; + readonly int GLP_FR = 1; + readonly int GLP_IV = 2; + readonly int GLP_DB = 4; + struct ConstraintMatrix{ + public fixed int ia[3]; + public fixed int ja[3]; + public fixed double ar[3]; + } + [DllImport(glpkLibrary, SetLastError=true)] + static extern double* glp_create_prob(); + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_add_rows(double* lp, int rows); + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_add_cols(double* lp, int cols); + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_set_col_bnds(double* lp, int col, int bound_type, double lower_bound, double upper_bound); + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_set_col_kind(double* lp, int col, int kind); + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_load_matrix(double* lp, int elements, int* ia, int* ja, double* ar); + public GLPK(int a, int b){ + this.a = a; + this.b = b; + lp = glp_create_prob(); + //Col 1 is x, Col 2 is y + glp_add_rows(lp, 1); + glp_add_cols(lp, 2); + glp_set_col_bnds(lp, 1, GLP_FR, 0.0, 0.0); + glp_set_col_bnds(lp, 2, GLP_FR, 0.0, 0.0); + glp_set_col_kind(lp, 1, GLP_IV); + glp_set_col_kind(lp, 2, GLP_IV); + //Row 1 is a*x + b*y + ConstraintMatrix CM = new ConstraintMatrix(); + CM.ia[1]=1; CM.ja[1]=1; CM.ar[1]=a; + CM.ia[2]=1; CM.ja[2]=2; CM.ar[2]=b; + glp_load_matrix(lp, 2, CM.ia, CM.ja, CM.ar); + Console.WriteLine("Hello Nigel"); + } + + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_set_row_bnds(double* lp, int row, int bound_type, double lower_bound, double upper_bound); + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_simplex(double* lp, void* options); + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_intopt(double* lp, void* options); + [DllImport(glpkLibrary, SetLastError=true)] + static extern double glp_mip_col_val(double* lp, int col); + public int betterGuess(int upper_bound){ + //Find a new guess less than the old guess: 1 <= (a*x + b*y) <= (old guess - 1) + glp_set_row_bnds(lp, 1, GLP_DB, 1.0, ((double)upper_bound)-0.5); + glp_simplex(lp, null); + glp_intopt(lp, null); + int x = (int)glp_mip_col_val(lp, 1); + int y = (int)glp_mip_col_val(lp, 2); + int nextGuess = a*x + b*y; + Console.WriteLine("x = {0}, y = {1}, a*x + b*y = {2}",x,y,nextGuess); + return nextGuess; + } + + [DllImport(glpkLibrary, SetLastError=true)] + static extern void glp_delete_prob(double* lp); + ~GLPK(){ + glp_delete_prob(lp); + Console.WriteLine("Goodbye Nigel"); + } + +} + +class test{ + static bool isMinimum(int a, int b, int guess){ + Console.WriteLine("Trying {0}",guess); + if (a%guess > 0) return false; + if (b%guess > 0) return false; + Console.WriteLine("Solution is {0}",guess); + return true; + } + + public static void Main(string[] args){ + Console.WriteLine("a = {0}, b = {1}",args[0], args[1]); + GLPK lp = new GLPK(Int32.Parse(args[0]),Int32.Parse(args[1])); + int guess = (lp.a > lp.b) ? lp.b : lp.a; + while (!isMinimum(lp.a,lp.b,guess)) guess = lp.betterGuess(guess); + } +} diff -r d59bea55db9b -r c445c931472f examples/tas.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/tas.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,486 @@ +/* TAS, Tail Assignment Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Tail Assignment Problem (TAS) is to construct rosters for a set + of aircrafts (tails), which cover all flights for a given scheduling + period. + + This model includes only flight connection constraints while other + constraints (for example, maintenance constraints) are ignored. Such + simplification allows using a single commodity network to model the + problem, where commodity corresponds to the set of aircrafts. + + Nodes of the network are activities. They include all flights plus + two dummy nodes (activities): source node, s, corresponding to + initial activity of each aircraft, and sink node t, corresponding to + final activity of each aircraft. Arc v->v' exists in the network if + and only if the same aircraft is able to operate activity v and then + immediately activity v'. In partucular, arcs s->f and f->t exist for + all flights f. Arcs f->f', where f and f' are some flights, exist + only if the connection time (which is the difference between the + departure time of f' and the arrival time of f) is not less than a + given minimal connection time. + + Reference: + M. Groenkvist, "The Tail Assignment Problem," Dept. of Comp. Sci. + and Eng., Chalmers University of Technology and Goeteborg University, + Goeteborg, Sweden, August 2005. */ + +######################################################################## + +param nf, integer, > 0; +/* number of flights */ + +set F := 1..nf; +/* set of flights (for a given period from timetable) */ + +param hub{f in F}, in {1, 2}; +/* hub[f] = 1: Sheremetyevo-1 + hub[f] = 2: Sheremetyevo-2 */ + +param dest{f in F}, symbolic; +/* destination airport (IATA code) */ + +param fno1{f in F}, integer; +/* first leg flight number */ + +param dep1{f in F}, integer, >= 0; +/* departure time from Sheremetyevo airport, in minutes */ + +check{f in F: f < nf}: dep1[f] <= dep1[f+1]; +/* all flights must be ordered by ascending of the departure time */ + +param arr1{f in F}, integer, >= 0; +/* arrival time to destination airport, in minutes */ + +param fno2{f in F}, integer; +/* second leg flight number */ + +param dep2{f in F}, integer, >= 0; +/* departure time from destination airport, in minutes */ + +param arr2{f in F}, integer, >= 0; +/* arrival time to Sheremetyevo airport, in minutes */ + +param mct1, integer, >= 0, default 80; +/* minimal connection time (within SVO1 or SVO2), in minutes */ + +param mct2, integer, >= 0, default 150; +/* minimal connection time (between SVO1 and SVO2), in minutes */ + +set E := setof{f in F, ff in F: arr2[f] + (if hub[f] = hub[ff] then + mct1 else mct2) <= dep1[ff]} (f, ff); +/* connection network; arc f->ff is in E, iff the same aircraft can be + assigned to flight f and then immediately to flight ff */ + +var s{f in F}, >= 0; +/* s[f] is a flow from source node to node f */ + +var x{(f,ff) in E}, >= 0; +/* x[f,ff] is a flow from node f to node ff */ + +var t{f in F}, >= 0; +/* t[f] is a flow from node f to sink node */ + +s.t. into{f in F}: s[f] + sum{(ff,f) in E} x[ff,f] = 1; +/* exactly one aircraft must come into each node f */ + +s.t. from{f in F}: t[f] + sum{(f,ff) in E} x[f,ff] = 1; +/* exactly one aircraft must come from each node f */ + +minimize obj: sum{f in F} s[f]; +/* minimize the number aircrafts sufficient to cover all flights */ + +solve; + +######################################################################## + +param na := floor(sum{f in F} s[f] + .5); +/* minimal number of aircrafts found */ + +printf "At least %d aircrafts needed\n", na; + +set A := 1..na; +/* set of aircrafts */ + +printf "Building rosters...\n"; + +param tail{f in F}, in A, := +/* tail[f] is the number of an aircraft assigned to flight f */ + + if f = 1 then 1 + /* assign aircraft 1 to the earliest flight */ + + else if s[f] >= 0.9 then (max{ff in 1..f-1} tail[ff]) + 1 + /* if f is the first flight in a roster, assign to it a next + aircraft */ + + else sum{(ff,f) in E} tail[ff] * (if x[ff,f] >= 0.9 then 1); + /* otherwise, assign to flight f the same aircraft, which is + assigned to a preceding flight in the roster */ + +######################################################################## + +param file, symbolic, default "tas.ps"; +/* file to output the assignment chart in postscript format */ + +param title, symbolic, default "(no title)"; +/* chart title */ + +param left, default 25; +/* left margin, in mm */ + +param top, default 25; +/* top margin, in mm */ + +param right, default 20; +/* right margin, in mm */ + +param bottom, default 15; +/* bottom margin, in mm */ + +param sx := 297 - left - right; +/* chart area horizontal size, in mm */ + +param sy := 210 - top - bottom; +/* chart area vertical size, in mm */ + +param gap, default sy / (na - 1); +/* gap between rosters, in mm */ + +printf "Writing assignment chart to %s...\n", file; + +printf "%%!PS-Adobe-3.0\n" > file; +printf "%%%%Title: Tail Assignment Chart\n" >> file; +printf "%%%%Creator: GLPK MathProg\n" >> file; +printf "%%%%BoundingBox: 0 0 595 842\n" >> file; +printf "%%%%EndComments\n" >> file; +printf "<> setpagedevice\n" >> file; +printf "72 25.4 div dup scale\n" >> file; +printf "210 %.3f sub %.3f translate\n", bottom, left >> file; +printf "90 rotate\n" >> file; + +printf "/HelveticaBold findfont 5 scalefont setfont\n" >> file; +printf "%.3f %.3f moveto (%s) dup show\n", 0, sy + 5, title >> file; + +param period := floor((max{f in F} arr2[f]) / 60. + .5); +/* period duration, in hours */ + +/* vertical bars */ +printf ".8 .8 .8 setrgbcolor\n" >> file; +for {tt in 0..period} +{ printf "%s setlinewidth\n", + if tt mod 24 = 0 then ".5" else "0" >> file; + printf "newpath %.3f %.3f moveto %.3f %.3f lineto stroke\n", + tt * (sx / period), 0, tt * (sx / period), + sy + (if tt mod 24 = 0 then 2) >> file; +} + +/* rosters */ +for {a in A} +{ printf "0 0 0 setrgbcolor\n" >> file; + printf "0 setlinewidth\n" >> file; + printf "newpath %.3f %.3f moveto %.3f %.3f lineto stroke\n", + 0, sy - gap * (a - 1), sx, sy - gap * (a - 1) >> file; + printf "/Dingbats findfont 4 scalefont setfont\n" >> file; + printf "%.3f %.3f moveto <28> dup show\n", + -4, sy - gap * (a - 1) - 1.4, a >> file; + printf "/Helvetica findfont 3 scalefont setfont\n" >> file; + printf "%.3f %.3f moveto (%2d) dup show\n", + -9, sy - gap * (a - 1) - 1.2, a >> file; + for {f in F: tail[f] == a} + { printf "0 0 %s setrgbcolor\n", + if hub[f] = 1 then "0" else ".8" >> file; + printf "1 setlinewidth\n" >> file; + printf "newpath %.3f %.3f moveto %.3f %.3f lineto stroke\n", + dep1[f] / 60 * (sx / period), sy - gap * (a - 1), + arr2[f] / 60 * (sx / period), sy - gap * (a - 1) >> file; + printf "/Helvetica findfont 1.8 scalefont setfont\n" >> file; + printf "%.3f %.3f moveto (%02d:%02d %s) dup show\n", + dep1[f] / 60 * (sx / period), sy - gap * (a - 1) + .8, + (dep1[f] mod 1440) div 60, (dep1[f] mod 1440) mod 60, + dest[f] >> file; + printf "%.3f %.3f moveto (%d %02d:%02d) dup show\n", + dep1[f] / 60 * (sx / period), sy - gap * (a - 1) - 2.1, + fno1[f], + (arr2[f] mod 1440) div 60, (arr2[f] mod 1440) mod 60 >> file; + } +} + +printf "showpage\n" >> file; +printf "%%%%EOF\n" >> file; + +######################################################################## + +data; + +param title := "Tu-154 [from 2008-08-18 to 2008-08-24]"; + +param nf := 261; + +param : hub dest fno1 dep1 arr1 fno2 dep2 arr2 := + 1 1 IKT 743 195 520 744 610 970 + 2 1 OMS 815 205 405 816 485 700 + 3 1 CEK 897 205 360 898 430 595 + 4 1 KRR 763 260 400 764 480 610 + 5 2 SIP 133 280 420 134 500 620 + 6 2 BUD 131 290 450 132 520 675 + 7 1 AAQ 701 305 440 702 510 640 + 8 1 MRV 785 310 440 786 520 650 + 9 2 WAW 101 355 475 102 540 660 + 10 2 GYD 147 370 550 148 675 860 + 11 1 AER 869 385 530 870 655 795 + 12 1 KRR 765 430 560 766 630 760 + 13 1 AAQ 703 520 660 704 740 850 + 14 1 LED 845 530 620 846 690 775 + 15 1 KRR 767 540 675 768 765 895 + 16 2 KBP 183 665 760 184 850 940 + 17 1 MRV 787 755 905 788 985 1135 + 18 1 KRR 771 810 940 772 1030 1165 + 19 1 LED 849 825 900 850 960 1095 + 20 2 IST 209 880 1050 210 1120 1280 + 21 1 AER 873 885 1030 874 1760 1900 + 22 1 ASF 711 995 1145 712 1640 1795 + 23 2 ULN 563 995 1335 564 1415 1815 + 24 2 OTP 151 1020 1175 152 1800 1940 + 25 2 BEY 509 1025 1265 510 1350 1580 + 26 2 OSL 211 1060 1220 212 1860 2015 + 27 1 IKT 739 1085 1420 740 1510 1870 + 28 1 KRR 773 1095 1240 774 1620 1765 + 29 1 SGC 877 1120 1315 878 1395 1625 + 30 1 LED 857 1150 1230 858 1610 1690 + 31 1 CEK 899 1230 1385 900 1455 1620 + 32 1 PEE 821 1235 1390 822 1450 1600 + 33 2 TBS 197 1240 1405 198 1560 1715 + 34 1 UFA 891 1275 1405 892 1475 1610 + 35 1 KJA 781 1300 1570 782 1680 1990 + 36 1 IKT 743 1635 1960 744 2050 2410 + 37 1 OMS 815 1645 1845 816 1925 2140 + 38 1 CEK 897 1645 1800 898 1870 2035 + 39 1 KRR 763 1700 1840 764 1920 2050 + 40 2 SIP 133 1720 1860 134 1940 2060 + 41 2 BUD 131 1730 1890 132 1960 2115 + 42 1 AAQ 701 1745 1880 702 1950 2080 + 43 1 MRV 785 1750 1880 786 1960 2090 + 44 2 WAW 101 1795 1915 102 1980 2100 + 45 2 GYD 147 1810 1990 148 2115 2300 + 46 1 AER 869 1825 1970 870 2095 2235 + 47 2 EVN 193 1850 2030 194 2105 2275 + 48 1 KRR 765 1870 2000 766 2070 2200 + 49 1 AAQ 703 1960 2100 704 2180 2290 + 50 1 LED 845 1970 2060 846 2130 2215 + 51 1 KRR 767 1980 2115 768 2205 2335 + 52 2 KBP 183 2105 2200 184 2290 2380 + 53 1 MRV 787 2195 2345 788 2425 2575 + 54 1 KRR 771 2250 2380 772 2470 2605 + 55 1 LED 849 2265 2340 850 2400 2535 + 56 2 IST 209 2320 2490 210 2560 2720 + 57 1 AER 873 2325 2470 874 3200 3340 + 58 2 ULN 563 2435 2775 564 2855 3255 + 59 1 ASF 711 2435 2585 712 3080 3235 + 60 2 DAM 517 2465 2705 518 2790 3020 + 61 2 OSL 211 2500 2660 212 3300 3455 + 62 2 KBP 185 2510 2610 186 3160 3250 + 63 1 IKT 739 2525 2860 740 2950 3310 + 64 1 KRR 773 2535 2680 774 3060 3205 + 65 1 SGC 877 2560 2755 878 2835 3065 + 66 1 LED 857 2590 2670 858 3050 3130 + 67 1 CEK 899 2670 2825 900 2895 3060 + 68 1 PEE 821 2675 2830 822 2890 3040 + 69 2 TBS 197 2680 2845 198 3000 3155 + 70 1 UFA 891 2715 2845 892 2915 3050 + 71 1 KJA 781 2740 3010 782 3120 3430 + 72 1 IKT 743 3075 3400 744 3490 3850 + 73 1 CEK 897 3085 3240 898 3310 3475 + 74 1 OMS 815 3085 3285 816 3365 3580 + 75 1 KRR 763 3140 3280 764 3360 3490 + 76 2 SIP 133 3160 3300 134 3380 3500 + 77 2 BUD 131 3170 3330 132 3400 3555 + 78 1 AAQ 701 3185 3320 702 3390 3520 + 79 1 MRV 785 3190 3320 786 3400 3530 + 80 2 WAW 101 3235 3355 102 3420 3540 + 81 2 FRU 181 3245 3495 182 3590 3860 + 82 2 GYD 147 3250 3430 148 3555 3740 + 83 1 AER 869 3265 3410 870 3535 3675 + 84 1 KRR 765 3310 3440 766 3510 3640 + 85 1 AAQ 703 3400 3540 704 3620 3730 + 86 1 LED 845 3410 3500 846 3570 3655 + 87 1 KRR 767 3420 3555 768 3645 3775 + 88 2 KBP 183 3545 3640 184 3730 3820 + 89 1 MRV 787 3635 3785 788 3865 4015 + 90 1 KRR 771 3690 3820 772 3910 4045 + 91 1 LED 849 3705 3780 850 3840 3975 + 92 2 IST 209 3760 3930 210 4000 4160 + 93 1 AER 873 3765 3910 874 4640 4780 + 94 2 ULN 563 3875 4215 564 4295 4695 + 95 1 ASF 711 3875 4025 712 4520 4675 + 96 2 OTP 151 3900 4055 152 4680 4820 + 97 2 BEY 509 3905 4145 510 4230 4460 + 98 2 OSL 211 3940 4100 212 4740 4895 + 99 2 KBP 185 3950 4050 186 4600 4690 + 100 1 IKT 739 3965 4300 740 4390 4750 + 101 1 KRR 773 3975 4120 774 4500 4645 + 102 1 SGC 877 4000 4195 878 4275 4505 + 103 1 LED 857 4030 4110 858 4490 4570 + 104 1 CEK 899 4110 4265 900 4335 4500 + 105 1 PEE 821 4115 4270 822 4330 4480 + 106 2 TBS 197 4120 4285 198 4440 4595 + 107 1 UFA 891 4155 4285 892 4355 4490 + 108 1 KJA 781 4180 4450 782 4560 4870 + 109 1 IKT 743 4515 4840 744 4930 5290 + 110 1 OMS 815 4525 4725 816 4805 5020 + 111 1 CEK 897 4525 4680 898 4750 4915 + 112 1 KRR 763 4580 4720 764 4800 4930 + 113 2 SIP 133 4600 4740 134 4820 4940 + 114 2 BUD 131 4610 4770 132 4840 4995 + 115 1 AAQ 701 4625 4760 702 4830 4960 + 116 1 MRV 785 4630 4760 786 4840 4970 + 117 2 WAW 101 4675 4795 102 4860 4980 + 118 2 GYD 147 4690 4870 148 4995 5180 + 119 1 AER 869 4705 4850 870 4975 5115 + 120 2 EVN 193 4730 4910 194 4985 5155 + 121 1 KRR 765 4750 4880 766 4950 5080 + 122 1 AAQ 703 4840 4980 704 5060 5170 + 123 1 LED 845 4850 4940 846 5010 5095 + 124 1 KRR 767 4860 4995 768 5085 5215 + 125 2 KBP 183 4985 5080 184 5170 5260 + 126 1 MRV 787 5075 5225 788 5305 5455 + 127 1 KRR 771 5130 5260 772 5350 5485 + 128 1 LED 849 5145 5220 850 5280 5415 + 129 2 IST 209 5200 5370 210 5440 5600 + 130 1 AER 873 5205 5350 874 6080 6220 + 131 1 ASF 711 5315 5465 712 5960 6115 + 132 2 ULN 563 5315 5655 564 5735 6135 + 133 2 DAM 517 5345 5585 518 5670 5900 + 134 2 OSL 211 5380 5540 212 6180 6335 + 135 2 KBP 185 5390 5490 186 6040 6130 + 136 1 IKT 739 5405 5740 740 5830 6190 + 137 1 KRR 773 5415 5560 774 5940 6085 + 138 1 SGC 877 5440 5635 878 5715 5945 + 139 1 LED 857 5470 5550 858 5930 6010 + 140 1 CEK 899 5550 5705 900 5775 5940 + 141 1 PEE 821 5555 5710 822 5770 5920 + 142 2 TBS 197 5560 5725 198 5880 6035 + 143 1 UFA 891 5595 5725 892 5795 5930 + 144 1 KJA 781 5620 5890 782 6000 6310 + 145 1 IKT 743 5955 6280 744 6370 6730 + 146 1 OMS 815 5965 6165 816 6245 6460 + 147 1 CEK 897 5965 6120 898 6190 6355 + 148 1 KRR 763 6020 6160 764 6240 6370 + 149 2 SIP 133 6040 6180 134 6260 6380 + 150 2 BUD 131 6050 6210 132 6280 6435 + 151 1 AAQ 701 6065 6200 702 6270 6400 + 152 1 MRV 785 6070 6200 786 6280 6410 + 153 2 WAW 101 6115 6235 102 6300 6420 + 154 2 FRU 181 6125 6375 182 6470 6740 + 155 2 GYD 147 6130 6310 148 6435 6620 + 156 1 AER 869 6145 6290 870 6415 6555 + 157 2 EVN 193 6170 6350 194 6425 6595 + 158 1 KRR 765 6190 6320 766 6390 6520 + 159 1 AAQ 703 6280 6420 704 6500 6610 + 160 1 LED 845 6290 6380 846 6450 6535 + 161 1 KRR 767 6300 6435 768 6525 6655 + 162 2 KBP 183 6425 6520 184 6610 6700 + 163 2 AYT 223 6500 6690 224 6750 6940 + 164 1 AER 867 6510 6660 868 6730 6880 + 165 1 MRV 787 6515 6665 788 6745 6895 + 166 1 KRR 771 6570 6700 772 6790 6925 + 167 1 LED 849 6585 6660 850 6720 6855 + 168 2 IST 209 6640 6810 210 6880 7040 + 169 1 AER 873 6645 6790 874 7520 7660 + 170 1 ASF 711 6755 6905 712 7400 7555 + 171 2 ULN 563 6755 7095 564 7175 7575 + 172 2 OTP 151 6780 6935 152 7560 7700 + 173 2 BEY 509 6785 7025 510 7110 7340 + 174 2 OSL 211 6820 6980 212 7620 7775 + 175 2 KBP 185 6830 6930 186 7480 7570 + 176 1 IKT 739 6845 7180 740 7270 7630 + 177 1 KRR 773 6855 7000 774 7380 7525 + 178 1 SGC 877 6880 7075 878 7155 7385 + 179 1 LED 857 6910 6990 858 7370 7450 + 180 1 CEK 899 6990 7145 900 7215 7380 + 181 1 PEE 821 6995 7150 822 7210 7360 + 182 2 TBS 197 7000 7165 198 7320 7475 + 183 1 UFA 891 7035 7165 892 7235 7370 + 184 1 KJA 781 7060 7330 782 7440 7750 + 185 1 IKT 743 7395 7720 744 7810 8170 + 186 1 CEK 897 7405 7560 898 7630 7795 + 187 1 KRR 763 7460 7600 764 7680 7810 + 188 2 SIP 133 7480 7620 134 7700 7820 + 189 2 BUD 131 7490 7650 132 7720 7875 + 190 1 AAQ 701 7505 7640 702 7710 7840 + 191 1 MRV 785 7510 7640 786 7720 7850 + 192 2 IST 207 7545 7720 208 7795 7985 + 193 2 WAW 101 7555 7675 102 7740 7860 + 194 2 GYD 147 7570 7750 148 7875 8060 + 195 1 AER 869 7585 7730 870 7855 7995 + 196 2 AYT 221 7610 7800 222 7895 8085 + 197 2 EVN 193 7610 7790 194 7865 8035 + 198 1 KRR 765 7630 7760 766 7830 7960 + 199 1 AAQ 703 7720 7860 704 7940 8050 + 200 1 LED 845 7730 7820 846 7890 7975 + 201 1 KRR 767 7740 7875 768 7965 8095 + 202 2 KBP 183 7865 7960 184 8050 8140 + 203 2 AYT 223 7940 8130 224 8190 8380 + 204 1 MRV 787 7955 8105 788 8185 8335 + 205 1 KRR 771 8010 8140 772 8230 8365 + 206 1 LED 849 8025 8100 850 8160 8295 + 207 2 IST 209 8080 8250 210 8320 8480 + 208 1 AER 873 8085 8230 874 8960 9100 + 209 1 ASF 711 8195 8345 712 8840 8995 + 210 2 ULN 563 8195 8535 564 8615 9015 + 211 1 KJA 779 8230 8500 780 8575 8870 + 212 2 OSL 211 8260 8420 212 9060 9215 + 213 2 KBP 185 8270 8370 186 8920 9010 + 214 1 IKT 739 8285 8620 740 8710 9070 + 215 1 KRR 773 8295 8440 774 8820 8965 + 216 1 SGC 877 8320 8515 878 8595 8825 + 217 1 LED 857 8350 8430 858 8810 8890 + 218 1 CEK 899 8430 8585 900 8655 8820 + 219 1 PEE 821 8435 8590 822 8650 8800 + 220 2 TBS 197 8440 8605 198 8760 8915 + 221 1 UFA 891 8475 8605 892 8675 8810 + 222 1 KJA 781 8500 8770 782 8880 9190 + 223 1 IKT 743 8835 9160 744 9250 9610 + 224 1 OMS 815 8845 9045 816 9125 9340 + 225 1 CEK 897 8845 9000 898 9070 9235 + 226 1 KRR 763 8900 9040 764 9120 9250 + 227 2 SIP 133 8920 9060 134 9140 9260 + 228 2 BUD 131 8930 9090 132 9160 9315 + 229 1 AAQ 701 8945 9080 702 9150 9280 + 230 1 MRV 785 8950 9080 786 9160 9290 + 231 2 IST 207 8985 9160 208 9235 9425 + 232 2 WAW 101 8995 9115 102 9180 9300 + 233 2 FRU 181 9005 9255 182 9350 9620 + 234 2 GYD 147 9010 9190 148 9315 9500 + 235 1 AER 869 9025 9170 870 9295 9435 + 236 2 EVN 193 9050 9230 194 9305 9475 + 237 1 KRR 765 9070 9200 766 9270 9400 + 238 1 AAQ 703 9160 9300 704 9380 9490 + 239 1 LED 845 9170 9260 846 9330 9415 + 240 1 KRR 767 9180 9315 768 9405 9535 + 241 2 KBP 183 9305 9400 184 9490 9580 + 242 2 AYT 223 9380 9570 224 9630 9820 + 243 1 MRV 787 9395 9545 788 9625 9775 + 244 1 KRR 771 9450 9580 772 9670 9805 + 245 1 LED 849 9465 9540 850 9600 9735 + 246 2 IST 209 9520 9690 210 9760 9920 + 247 1 AER 873 9525 9670 874 10400 10540 + 248 1 ASF 711 9635 9785 712 10280 10435 + 249 2 ULN 563 9635 9975 564 10055 10455 + 250 2 OTP 151 9660 9815 152 10440 10580 + 251 2 DAM 517 9665 9905 518 9990 10220 + 252 2 OSL 211 9700 9860 212 10500 10655 + 253 2 KBP 185 9710 9810 186 10360 10450 + 254 1 IKT 739 9725 10060 740 10150 10510 + 255 1 KRR 773 9735 9880 774 10260 10405 + 256 1 SGC 877 9760 9955 878 10035 10265 + 257 1 LED 857 9790 9870 858 10250 10330 + 258 1 CEK 899 9870 10025 900 10095 10260 + 259 1 PEE 821 9875 10030 822 10090 10240 + 260 1 UFA 891 9915 10045 892 10115 10250 + 261 1 KJA 781 9940 10210 782 10320 10630 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/todd.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/todd.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,36 @@ +/* TODD, a class of hard instances of zero-one knapsack problems */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* Chvatal describes a class of instances of zero-one knapsack problems + due to Todd. He shows that a wide class of algorithms - including all + based on branch and bound or dynamic programming - find it difficult + to solve problems in the Todd class. More exactly, the time required + by these algorithms to solve instances of problems that belong to the + Todd class grows as an exponential function of the problem size. + + Reference: + Chvatal V. (1980), Hard knapsack problems, Op. Res. 28, 1402-1411. */ + +param n > 0 integer; + +param log2_n := log(n) / log(2); + +param k := floor(log2_n); + +param a{j in 1..n} := 2 ** (k + n + 1) + 2 ** (k + n + 1 - j) + 1; + +param b := 0.5 * floor(sum{j in 1..n} a[j]); + +var x{1..n} binary; + +maximize obj: sum{j in 1..n} a[j] * x[j]; + +s.t. cap: sum{j in 1..n} a[j] * x[j] <= b; + +data; + +param n := 15; +/* change this parameter to choose a particular instance */ + +end; diff -r d59bea55db9b -r c445c931472f examples/train.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/train.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,281 @@ +# TRAIN, a model of railroad passenger car allocation +# +# References: +# Robert Fourer, David M. Gay and Brian W. Kernighan, "A Modeling Language +# for Mathematical Programming." Management Science 36 (1990) 519-554. + +### SCHEDULE SETS AND PARAMETERS ### + +set cities; + +set links within {c1 in cities, c2 in cities: c1 <> c2}; + + # Set of cities, and set of intercity links + +param last > 0 integer; # Number of time intervals in a day + +set times := 1..last; # Set of time intervals in a day + +set schedule within + {c1 in cities, t1 in times, + c2 in cities, t2 in times: (c1,c2) in links}; + + # Member (c1,t1,c2,t2) of this set represents + # a train that leaves city c1 at time t1 + # and arrives in city c2 at time t2 + +### DEMAND PARAMETERS ### + +param section > 0 integer; + + # Maximum number of cars in one section of a train + +param demand {schedule} > 0; + + # For each scheduled train: + # the smallest number of cars that + # can meet demand for the train + +param low {(c1,t1,c2,t2) in schedule} := ceil(demand[c1,t1,c2,t2]); + + # Minimum number of cars needed to meet demand + +param high {(c1,t1,c2,t2) in schedule} + + := max (2, min (ceil(2*demand[c1,t1,c2,t2]), + section*ceil(demand[c1,t1,c2,t2]/section) )); + + # Maximum number of cars allowed on a train: + # 2 if demand is for less than one car; + # otherwise, lesser of + # number of cars needed to hold twice the demand, and + # number of cars in minimum number of sections needed + +### DISTANCE PARAMETERS ### + +param dist_table {links} >= 0 default 0.0; + +param distance {(c1,c2) in links} > 0 + := if dist_table[c1,c2] > 0 then dist_table[c1,c2] else dist_table[c2,c1]; + + # Inter-city distances: distance[c1,c2] is miles + # between city c1 and city c2 + +### VARIABLES ### + +var U 'cars stored' {cities,times} >= 0; + + # u[c,t] is the number of unused cars stored + # at city c in the interval beginning at time t + +var X 'cars in train' {schedule} >= 0; + + # x[c1,t1,c2,t2] is the number of cars assigned to + # the scheduled train that leaves c1 at t1 and + # arrives in c2 at t2 + +### OBJECTIVES ### + +minimize cars: + sum {c in cities} U[c,last] + + sum {(c1,t1,c2,t2) in schedule: t2 < t1} X[c1,t1,c2,t2]; + + # Number of cars in the system: + # sum of unused cars and cars in trains during + # the last time interval of the day + +minimize miles: + sum {(c1,t1,c2,t2) in schedule} distance[c1,c2] * X[c1,t1,c2,t2]; + + # Total car-miles run by all scheduled trains in a day + +### CONSTRAINTS ### + +account {c in cities, t in times}: + + U[c,t] = U[c, if t > 1 then t-1 else last] + + + sum {(c1,t1,c,t) in schedule} X[c1,t1,c,t] - + sum {(c,t,c2,t2) in schedule} X[c,t,c2,t2]; + + # For every city and time: + # unused cars in the present interval must equal + # unused cars in the previous interval, + # plus cars just arriving in trains, + # minus cars just leaving in trains + +satisfy {(c1,t1,c2,t2) in schedule}: + + low[c1,t1,c2,t2] <= X[c1,t1,c2,t2] <= high[c1,t1,c2,t2]; + + # For each scheduled train: + # number of cars must meet demand, + # but must not be so great that unnecessary + # sections are run + +### DATA ### + +data; + +set cities := BO NY PH WA ; + +set links := (BO,NY) (NY,PH) (PH,WA) + (NY,BO) (PH,NY) (WA,PH) ; + +param dist_table := [*,*] BO NY 232 + NY PH 90 + PH WA 135 ; + +param last := 48 ; + +param section := 14 ; + +set schedule := + + (WA,*,PH,*) 2 5 6 9 8 11 10 13 + 12 15 13 16 14 17 15 18 + 16 19 17 20 18 21 19 22 + 20 23 21 24 22 25 23 26 + 24 27 25 28 26 29 27 30 + 28 31 29 32 30 33 31 34 + 32 35 33 36 34 37 35 38 + 36 39 37 40 38 41 39 42 + 40 43 41 44 42 45 44 47 + 46 1 + + (PH,*,NY,*) 1 3 5 7 9 11 11 13 + 13 15 14 16 15 17 16 18 + 17 19 18 20 19 21 20 22 + 21 23 22 24 23 25 24 26 + 25 27 26 28 27 29 28 30 + 29 31 30 32 31 33 32 34 + 33 35 34 36 35 37 36 38 + 37 39 38 40 39 41 40 42 + 41 43 42 44 43 45 44 46 + 45 47 47 1 + + (NY,*,BO,*) 10 16 12 18 14 20 15 21 + 16 22 17 23 18 24 19 25 + 20 26 21 27 22 28 23 29 + 24 30 25 31 26 32 27 33 + 28 34 29 35 30 36 31 37 + 32 38 33 39 34 40 35 41 + 36 42 37 43 38 44 39 45 + 40 46 41 47 42 48 43 1 + 44 2 45 3 46 4 48 6 + + (BO,*,NY,*) 7 13 9 15 11 17 12 18 + 13 19 14 20 15 21 16 22 + 17 23 18 24 19 25 20 26 + 21 27 22 28 23 29 24 30 + 25 31 26 32 27 33 28 34 + 29 35 30 36 31 37 32 38 + 33 39 34 40 35 41 36 42 + 37 43 38 44 39 45 40 46 + 41 47 43 1 45 3 47 5 + + (NY,*,PH,*) 1 3 12 14 13 15 14 16 + 15 17 16 18 17 19 18 20 + 19 21 20 22 21 23 22 24 + 23 25 24 26 25 27 26 28 + 27 29 28 30 29 31 30 32 + 31 33 32 34 33 35 34 36 + 35 37 36 38 37 39 38 40 + 39 41 40 42 41 43 42 44 + 43 45 44 46 45 47 46 48 + 47 1 + + (PH,*,WA,*) 1 4 14 17 15 18 16 19 + 17 20 18 21 19 22 20 23 + 21 24 22 25 23 26 24 27 + 25 28 26 29 27 30 28 31 + 29 32 30 33 31 34 32 35 + 33 36 34 37 35 38 36 39 + 37 40 38 41 39 42 40 43 + 41 44 42 45 43 46 44 47 + 45 48 46 1 47 2 ; + +param demand := + + [WA,*,PH,*] 2 5 .55 6 9 .01 8 11 .01 + 10 13 .13 12 15 1.59 13 16 1.69 + 14 17 5.19 15 18 3.55 16 19 6.29 + 17 20 4.00 18 21 5.80 19 22 3.40 + 20 23 4.88 21 24 2.92 22 25 4.37 + 23 26 2.80 24 27 4.23 25 28 2.88 + 26 29 4.33 27 30 3.11 28 31 4.64 + 29 32 3.44 30 33 4.95 31 34 3.73 + 32 35 5.27 33 36 3.77 34 37 4.80 + 35 38 3.31 36 39 3.89 37 40 2.65 + 38 41 3.01 39 42 2.04 40 43 2.31 + 41 44 1.52 42 45 1.75 44 47 1.88 + 46 1 1.05 + + [PH,*,NY,*] 1 3 1.05 5 7 .43 9 11 .20 + 11 13 .21 13 15 .40 14 16 6.49 + 15 17 16.40 16 18 9.48 17 19 17.15 + 18 20 9.31 19 21 15.20 20 22 8.21 + 21 23 13.32 22 24 7.35 23 25 11.83 + 24 26 6.61 25 27 10.61 26 28 6.05 + 27 29 9.65 28 30 5.61 29 31 9.25 + 30 32 5.40 31 33 8.24 32 34 4.84 + 33 35 7.44 34 36 4.44 35 37 6.80 + 36 38 4.11 37 39 6.25 38 40 3.69 + 39 41 5.55 40 42 3.29 41 43 4.77 + 42 44 2.91 43 45 4.19 44 46 2.53 + 45 47 4.00 47 1 1.65 + + [NY,*,BO,*] 10 16 1.23 12 18 3.84 14 20 4.08 + 15 21 1.47 16 22 2.96 17 23 1.60 + 18 24 2.95 19 25 1.71 20 26 2.81 + 21 27 1.77 22 28 2.87 23 29 1.84 + 24 30 2.95 25 31 1.91 26 32 3.12 + 27 33 1.93 28 34 3.31 29 35 2.00 + 30 36 3.40 31 37 2.08 32 38 3.41 + 33 39 2.69 34 40 4.45 35 41 2.32 + 36 42 3.40 37 43 1.80 38 44 2.63 + 39 45 1.52 40 46 2.23 41 47 1.25 + 42 48 1.79 43 1 .97 44 2 1.28 + 45 3 .48 46 4 .68 48 6 .08 + + [BO,*,NY,*] 7 13 .03 9 15 1.29 11 17 4.59 + 12 18 2.56 13 19 3.92 14 20 2.37 + 15 21 3.81 16 22 2.24 17 23 3.51 + 18 24 2.13 19 25 3.28 20 26 2.05 + 21 27 3.15 22 28 1.99 23 29 3.09 + 24 30 1.93 25 31 3.19 26 32 1.91 + 27 33 3.21 28 34 1.85 29 35 3.21 + 30 36 1.71 31 37 3.04 32 38 2.08 + 33 39 3.13 34 40 1.96 35 41 2.53 + 36 42 1.43 37 43 2.04 38 44 1.12 + 39 45 1.71 40 46 .91 41 47 1.32 + 43 1 1.80 45 3 1.13 47 5 .23 + + [NY,*,PH,*] 1 3 .04 12 14 4.68 13 15 5.61 + 14 16 3.56 15 17 5.81 16 18 3.81 + 17 19 6.31 18 20 4.07 19 21 7.33 + 20 22 4.55 21 23 7.37 22 24 4.73 + 23 25 7.61 24 26 4.92 25 27 7.91 + 26 28 5.19 27 29 8.40 28 30 5.53 + 29 31 9.32 30 32 5.51 31 33 10.33 + 32 34 9.21 33 35 18.95 34 36 11.23 + 35 37 16.85 36 38 7.29 37 39 10.89 + 38 40 5.41 39 41 8.21 40 42 4.52 + 41 43 6.99 42 44 3.92 43 45 6.21 + 44 46 3.44 45 47 5.17 46 48 2.55 + 47 1 1.24 + + [PH,*,WA,*] 1 4 .20 14 17 4.49 15 18 3.53 + 16 19 2.67 17 20 3.83 18 21 3.01 + 19 22 4.12 20 23 3.15 21 24 4.67 + 22 25 3.20 23 26 4.23 24 27 2.87 + 25 28 3.84 26 29 2.60 27 30 3.80 + 28 31 2.77 29 32 4.31 30 33 3.16 + 31 34 4.88 32 35 3.45 33 36 5.55 + 34 37 3.52 35 38 6.11 36 39 3.32 + 37 40 5.53 38 41 3.03 39 42 4.51 + 40 43 2.53 41 44 3.39 42 45 1.93 + 43 46 2.52 44 47 1.20 45 48 1.75 + 46 1 .88 47 2 .87 ; + +end; diff -r d59bea55db9b -r c445c931472f examples/transp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/transp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,63 @@ +# A TRANSPORTATION PROBLEM +# +# This problem finds a least cost shipping schedule that meets +# requirements at markets and supplies at factories. +# +# References: +# Dantzig G B, "Linear Programming and Extensions." +# Princeton University Press, Princeton, New Jersey, 1963, +# Chapter 3-3. + +set I; +/* canning plants */ + +set J; +/* markets */ + +param a{i in I}; +/* capacity of plant i in cases */ + +param b{j in J}; +/* demand at market j in cases */ + +param d{i in I, j in J}; +/* distance in thousands of miles */ + +param f; +/* freight in dollars per case per thousand miles */ + +param c{i in I, j in J} := f * d[i,j] / 1000; +/* transport cost in thousands of dollars per case */ + +var x{i in I, j in J} >= 0; +/* shipment quantities in cases */ + +minimize cost: sum{i in I, j in J} c[i,j] * x[i,j]; +/* total transportation costs in thousands of dollars */ + +s.t. supply{i in I}: sum{j in J} x[i,j] <= a[i]; +/* observe supply limit at plant i */ + +s.t. demand{j in J}: sum{i in I} x[i,j] >= b[j]; +/* satisfy demand at market j */ + +data; + +set I := Seattle San-Diego; + +set J := New-York Chicago Topeka; + +param a := Seattle 350 + San-Diego 600; + +param b := New-York 325 + Chicago 300 + Topeka 275; + +param d : New-York Chicago Topeka := + Seattle 2.5 1.7 1.8 + San-Diego 2.5 1.8 1.4 ; + +param f := 90; + +end; diff -r d59bea55db9b -r c445c931472f examples/trick.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/trick.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,72 @@ +/* TRICK, A Transportation Design Problem */ + +/* Translated from the Mosel modeling language to GNU MathProg by + Andrew Makhorin */ + +/* This example model is described in the article "Formulations and + Reformulations in Integer Programming" by Michael Trick (it is + publicly available at http://mat.gsia.cmu.edu/trick/formul04.pdf). + + This model demonstrates an amazing effect when including in the + formulation an additional constraint, which is redundant even for + LP relaxation, makes the model easy for solving with the B&B. */ + +set TRUCKS := 1..10; + +set PACKAGES := 1..20; + +param capacity{TRUCKS}; + +param size{PACKAGES}; + +param cost{TRUCKS}; + +param can_use{PACKAGES, TRUCKS}; + +var x{PACKAGES, TRUCKS}, binary; + +var y{TRUCKS}, binary; + +minimize total: sum{i in TRUCKS} cost[i] * y[i]; + +f1{i in TRUCKS}: + sum{j in PACKAGES} size[j] * x[j,i] <= capacity[i] * y[i]; + +f2{i in TRUCKS, j in PACKAGES}: + x[j,i] <= y[i]; + +f3{j in PACKAGES}: + sum{i in TRUCKS} can_use[j,i] * x[j,i] = 1; + +redundant_constraint: + sum{i in TRUCKS} capacity[i] * y[i] >= sum{j in PACKAGES} size[j]; + +data; + +param capacity := + [1] 100 [2] 200 [3] 100 [4] 200 [5] 100 [6] 200 [7] 100 [8] 200 + [9] 100 [10] 200; + +param size := + [1] 17 [2] 21 [3] 54 [4] 45 [5] 87 [6] 34 [7] 23 [8] 45 [9] 12 + [10] 43 [11] 54 [12] 39 [13] 31 [14] 26 [15] 75 [16] 48 [17] 16 + [18] 32 [19] 45 [20] 55; + +param cost := + [1] 1 [2] 1.8 [3] 1 [4] 1.8 [5] 1 [6] 1.8 [7] 1 [8] 1.8 [9] 1 + [10] 1.8; + +param can_use (tr): + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 := + 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 + 2 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0 + 3 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 + 4 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 + 5 0 0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 0 + 6 0 0 0 1 1 1 1 0 0 0 0 0 0 1 1 1 0 0 0 0 + 7 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 + 8 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 + 9 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 + 10 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1; + +end; diff -r d59bea55db9b -r c445c931472f examples/tsp.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/tsp.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,335 @@ +/* TSP, Traveling Salesman Problem */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +/* The Traveling Salesman Problem (TSP) is stated as follows. + Let a directed graph G = (V, E) be given, where V = {1, ..., n} is + a set of nodes, E <= V x V is a set of arcs. Let also each arc + e = (i,j) be assigned a number c[i,j], which is the length of the + arc e. The problem is to find a closed path of minimal length going + through each node of G exactly once. */ + +param n, integer, >= 3; +/* number of nodes */ + +set V := 1..n; +/* set of nodes */ + +set E, within V cross V; +/* set of arcs */ + +param c{(i,j) in E}; +/* distance from node i to node j */ + +var x{(i,j) in E}, binary; +/* x[i,j] = 1 means that the salesman goes from node i to node j */ + +minimize total: sum{(i,j) in E} c[i,j] * x[i,j]; +/* the objective is to make the path length as small as possible */ + +s.t. leave{i in V}: sum{(i,j) in E} x[i,j] = 1; +/* the salesman leaves each node i exactly once */ + +s.t. enter{j in V}: sum{(i,j) in E} x[i,j] = 1; +/* the salesman enters each node j exactly once */ + +/* Constraints above are not sufficient to describe valid tours, so we + need to add constraints to eliminate subtours, i.e. tours which have + disconnected components. Although there are many known ways to do + that, I invented yet another way. The general idea is the following. + Let the salesman sells, say, cars, starting the travel from node 1, + where he has n cars. If we require the salesman to sell exactly one + car in each node, he will need to go through all nodes to satisfy + this requirement, thus, all subtours will be eliminated. */ + +var y{(i,j) in E}, >= 0; +/* y[i,j] is the number of cars, which the salesman has after leaving + node i and before entering node j; in terms of the network analysis, + y[i,j] is a flow through arc (i,j) */ + +s.t. cap{(i,j) in E}: y[i,j] <= (n-1) * x[i,j]; +/* if arc (i,j) does not belong to the salesman's tour, its capacity + must be zero; it is obvious that on leaving a node, it is sufficient + to have not more than n-1 cars */ + +s.t. node{i in V}: +/* node[i] is a conservation constraint for node i */ + + sum{(j,i) in E} y[j,i] + /* summary flow into node i through all ingoing arcs */ + + + (if i = 1 then n) + /* plus n cars which the salesman has at starting node */ + + = /* must be equal to */ + + sum{(i,j) in E} y[i,j] + /* summary flow from node i through all outgoing arcs */ + + + 1; + /* plus one car which the salesman sells at node i */ + +solve; + +printf "Optimal tour has length %d\n", + sum{(i,j) in E} c[i,j] * x[i,j]; +printf("From node To node Distance\n"); +printf{(i,j) in E: x[i,j]} " %3d %3d %8g\n", + i, j, c[i,j]; + +data; + +/* These data correspond to the symmetric instance ulysses16 from: + + Reinelt, G.: TSPLIB - A travelling salesman problem library. + ORSA-Journal of the Computing 3 (1991) 376-84; + http://elib.zib.de/pub/Packages/mp-testdata/tsp/tsplib */ + +/* The optimal solution is 6859 */ + +param n := 16; + +param : E : c := + 1 2 509 + 1 3 501 + 1 4 312 + 1 5 1019 + 1 6 736 + 1 7 656 + 1 8 60 + 1 9 1039 + 1 10 726 + 1 11 2314 + 1 12 479 + 1 13 448 + 1 14 479 + 1 15 619 + 1 16 150 + 2 1 509 + 2 3 126 + 2 4 474 + 2 5 1526 + 2 6 1226 + 2 7 1133 + 2 8 532 + 2 9 1449 + 2 10 1122 + 2 11 2789 + 2 12 958 + 2 13 941 + 2 14 978 + 2 15 1127 + 2 16 542 + 3 1 501 + 3 2 126 + 3 4 541 + 3 5 1516 + 3 6 1184 + 3 7 1084 + 3 8 536 + 3 9 1371 + 3 10 1045 + 3 11 2728 + 3 12 913 + 3 13 904 + 3 14 946 + 3 15 1115 + 3 16 499 + 4 1 312 + 4 2 474 + 4 3 541 + 4 5 1157 + 4 6 980 + 4 7 919 + 4 8 271 + 4 9 1333 + 4 10 1029 + 4 11 2553 + 4 12 751 + 4 13 704 + 4 14 720 + 4 15 783 + 4 16 455 + 5 1 1019 + 5 2 1526 + 5 3 1516 + 5 4 1157 + 5 6 478 + 5 7 583 + 5 8 996 + 5 9 858 + 5 10 855 + 5 11 1504 + 5 12 677 + 5 13 651 + 5 14 600 + 5 15 401 + 5 16 1033 + 6 1 736 + 6 2 1226 + 6 3 1184 + 6 4 980 + 6 5 478 + 6 7 115 + 6 8 740 + 6 9 470 + 6 10 379 + 6 11 1581 + 6 12 271 + 6 13 289 + 6 14 261 + 6 15 308 + 6 16 687 + 7 1 656 + 7 2 1133 + 7 3 1084 + 7 4 919 + 7 5 583 + 7 6 115 + 7 8 667 + 7 9 455 + 7 10 288 + 7 11 1661 + 7 12 177 + 7 13 216 + 7 14 207 + 7 15 343 + 7 16 592 + 8 1 60 + 8 2 532 + 8 3 536 + 8 4 271 + 8 5 996 + 8 6 740 + 8 7 667 + 8 9 1066 + 8 10 759 + 8 11 2320 + 8 12 493 + 8 13 454 + 8 14 479 + 8 15 598 + 8 16 206 + 9 1 1039 + 9 2 1449 + 9 3 1371 + 9 4 1333 + 9 5 858 + 9 6 470 + 9 7 455 + 9 8 1066 + 9 10 328 + 9 11 1387 + 9 12 591 + 9 13 650 + 9 14 656 + 9 15 776 + 9 16 933 + 10 1 726 + 10 2 1122 + 10 3 1045 + 10 4 1029 + 10 5 855 + 10 6 379 + 10 7 288 + 10 8 759 + 10 9 328 + 10 11 1697 + 10 12 333 + 10 13 400 + 10 14 427 + 10 15 622 + 10 16 610 + 11 1 2314 + 11 2 2789 + 11 3 2728 + 11 4 2553 + 11 5 1504 + 11 6 1581 + 11 7 1661 + 11 8 2320 + 11 9 1387 + 11 10 1697 + 11 12 1838 + 11 13 1868 + 11 14 1841 + 11 15 1789 + 11 16 2248 + 12 1 479 + 12 2 958 + 12 3 913 + 12 4 751 + 12 5 677 + 12 6 271 + 12 7 177 + 12 8 493 + 12 9 591 + 12 10 333 + 12 11 1838 + 12 13 68 + 12 14 105 + 12 15 336 + 12 16 417 + 13 1 448 + 13 2 941 + 13 3 904 + 13 4 704 + 13 5 651 + 13 6 289 + 13 7 216 + 13 8 454 + 13 9 650 + 13 10 400 + 13 11 1868 + 13 12 68 + 13 14 52 + 13 15 287 + 13 16 406 + 14 1 479 + 14 2 978 + 14 3 946 + 14 4 720 + 14 5 600 + 14 6 261 + 14 7 207 + 14 8 479 + 14 9 656 + 14 10 427 + 14 11 1841 + 14 12 105 + 14 13 52 + 14 15 237 + 14 16 449 + 15 1 619 + 15 2 1127 + 15 3 1115 + 15 4 783 + 15 5 401 + 15 6 308 + 15 7 343 + 15 8 598 + 15 9 776 + 15 10 622 + 15 11 1789 + 15 12 336 + 15 13 287 + 15 14 237 + 15 16 636 + 16 1 150 + 16 2 542 + 16 3 499 + 16 4 455 + 16 5 1033 + 16 6 687 + 16 7 592 + 16 8 206 + 16 9 933 + 16 10 610 + 16 11 2248 + 16 12 417 + 16 13 406 + 16 14 449 + 16 15 636 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/xyacfs.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/xyacfs.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,56 @@ +/*Extended Yet Another Curve Fitting Solution (The poor man's RMA) + + An extension of yacfs.mod adding a Weight parameter: + When set to 1 the model produces best fit by least squares with all error in y and none in x (YonX); + When set to zero the model produces best fit by least squares with all error in x and none in y (XonY); + When set to 0.5 the model assumes equal error in x and y producing results similar to fitting by Reduced Major Axis Analysis. + + Nigel_Galloway@operamail.com + November 5th., 2009 +*/ +set Sample; +param Sx {z in Sample}; +param Sy {z in Sample}; +param Weight := 0.5; + +var a; +var b; +var p; +var q; + +XonY1 :sum{z in Sample} q*Sy[z]*Sy[z] + sum{z in Sample} p*Sy[z] = sum{z in Sample} Sy[z]*Sx[z]; +XonY2 :sum{z in Sample} q*Sy[z] + sum{z in Sample} p = sum{z in Sample} Sx[z]; +YonX1 :sum{z in Sample} a*Sx[z]*Sx[z] + sum{z in Sample} b*Sx[z] = sum{z in Sample} Sy[z]*Sx[z]; +YonX2 :sum{z in Sample} a*Sx[z] + sum{z in Sample} b = sum{z in Sample} Sy[z]; + +solve; + +param W := Weight*a + (1-Weight)*(1/q); +printf "\nbest linear fit is:\n\ty = %f %s %fx\n\n", b*Weight - (1-Weight)*(p/q), if W < 0 then "-" else "+", abs(W); + +data; + +param: +Sample: Sx Sy := + 1 0 1 + 2 0.5 0.9 + 3 1 0.7 + 4 1.5 1.5 + 5 1.9 2 + 6 2.5 2.4 + 7 3 3.2 + 8 3.5 2 + 9 4 2.7 + 10 4.5 3.5 + 11 5 1 + 12 5.5 4 + 13 6 3.6 + 14 6.6 2.7 + 15 7 5.7 + 16 7.6 4.6 + 17 8.5 6 + 18 9 6.8 + 19 10 7.3 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/yacfs.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/yacfs.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,48 @@ +/*Yet Another Curve Fitting Solution + + Obviously this solution produces the same answer + as examples/cflsq.mod + + Nigel_Galloway@operamail.com + February 1st., 2009 +*/ +set Sample; +param Sx {z in Sample}; +param Sy {z in Sample}; + +var a; +var b; + +equalz1 :sum{z in Sample} a*Sx[z]*Sx[z] + sum{z in Sample} b*Sx[z] = sum{z in Sample} Sy[z]*Sx[z]; +equalz2 :sum{z in Sample} a*Sx[z] + sum{z in Sample} b = sum{z in Sample} Sy[z]; + +solve; + +printf "\nbest linear fit is:\n\ty = %f %s %fx\n\n", b, if a < 0 then "-" else "+", abs(a); + +data; + +param: +Sample: Sx Sy := + 1 0 1 + 2 0.5 0.9 + 3 1 0.7 + 4 1.5 1.5 + 5 1.9 2 + 6 2.5 2.4 + 7 3 3.2 + 8 3.5 2 + 9 4 2.7 + 10 4.5 3.5 + 11 5 1 + 12 5.5 4 + 13 6 3.6 + 14 6.6 2.7 + 15 7 5.7 + 16 7.6 4.6 + 17 8.5 6 + 18 9 6.8 + 19 10 7.3 +; + +end; diff -r d59bea55db9b -r c445c931472f examples/zebra.mod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/zebra.mod Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,151 @@ +/* ZEBRA, Who Owns the Zebra? */ + +/* Written in GNU MathProg by Andrew Makhorin */ + +######################################################################## +# The Zebra Puzzle is a well-known logic puzzle. +# +# It is often called "Einstein's Puzzle" or "Einstein's Riddle" +# because it is said to have been invented by Albert Einstein as a boy, +# with the common claim that Einstein said "only 2 percent of the +# world's population can solve". It is also sometimes attributed to +# Lewis Carroll. However, there is no known evidence for Einstein's or +# Carroll's authorship. +# +# There are several versions of this puzzle. The version below is +# quoted from the first known publication in Life International +# magazine on December 17, 1962. +# +# 1. There are five houses. +# 2. The Englishman lives in the red house. +# 3. The Spaniard owns the dog. +# 4. Coffee is drunk in the green house. +# 5. The Ukrainian drinks tea. +# 6. The green house is immediately to the right of the ivory house. +# 7. The Old Gold smoker owns snails. +# 8. Kools are smoked in the yellow house. +# 9. Milk is drunk in the middle house. +# 10. The Norwegian lives in the first house. +# 11. The man who smokes Chesterfields lives in the house next to the +# man with the fox. +# 12. Kools are smoked in the house next to the house where the horse +# is kept. +# 13. The Lucky Strike smoker drinks orange juice. +# 14. The Japanese smokes Parliaments. +# 15. The Norwegian lives next to the blue house. +# +# Now, who drinks water? Who owns the zebra? +# +# In the interest of clarity, it must be added that each of the five +# houses is painted a different color, and their inhabitants are of +# different national extractions, own different pets, drink different +# beverages and smoke different brands of American cigarettes. One +# other thing: In statement 6, right means your right. +# +# (From Wikipedia, the free encyclopedia.) +######################################################################## + +set HOUSE := { 1..5 }; + +set COLOR := { "blue", "green", "ivory", "red", "yellow" }; + +set NATIONALITY := { "Englishman", "Japanese", "Norwegian", "Spaniard", + "Ukranian" }; + +set DRINK := { "coffee", "milk", "orange_juice", "tea", "water" }; + +set SMOKE := { "Chesterfield", "Kools", "Lucky_Strike", "Old_Gold", + "Parliament" }; + +set PET := { "dog", "fox", "horse", "snails", "zebra" }; + +var color{HOUSE, COLOR}, binary; +c1{h in HOUSE}: sum{c in COLOR} color[h,c] = 1; +c2{c in COLOR}: sum{h in HOUSE} color[h,c] = 1; + +var nationality{HOUSE, NATIONALITY}, binary; +n1{h in HOUSE}: sum{n in NATIONALITY} nationality[h,n] = 1; +n2{n in NATIONALITY}: sum{h in HOUSE} nationality[h,n] = 1; + +var drink{HOUSE, DRINK}, binary; +d1{h in HOUSE}: sum{d in DRINK} drink[h,d] = 1; +d2{d in DRINK}: sum{h in HOUSE} drink[h,d] = 1; + +var smoke{HOUSE, SMOKE}, binary; +s1{h in HOUSE}: sum{s in SMOKE} smoke[h,s] = 1; +s2{s in SMOKE}: sum{h in HOUSE} smoke[h,s] = 1; + +var pet{HOUSE, PET}, binary; +p1{h in HOUSE}: sum{p in PET} pet[h,p] = 1; +p2{p in PET}: sum{h in HOUSE} pet[h,p] = 1; + +/* the Englishman lives in the red house */ +f2{h in HOUSE}: nationality[h,"Englishman"] = color[h,"red"]; + +/* the Spaniard owns the dog */ +f3{h in HOUSE}: nationality[h,"Spaniard"] = pet[h,"dog"]; + +/* coffee is drunk in the green house */ +f4{h in HOUSE}: drink[h,"coffee"] = color[h,"green"]; + +/* the Ukrainian drinks tea */ +f5{h in HOUSE}: nationality[h,"Ukranian"] = drink[h,"tea"]; + +/* the green house is immediately to the right of the ivory house */ +f6{h in HOUSE}: + color[h,"green"] = if h = 1 then 0 else color[h-1,"ivory"]; + +/* the Old Gold smoker owns snails */ +f7{h in HOUSE}: smoke[h,"Old_Gold"] = pet[h,"snails"]; + +/* Kools are smoked in the yellow house */ +f8{h in HOUSE}: smoke[h,"Kools"] = color[h,"yellow"]; + +/* milk is drunk in the middle house */ +f9: drink[3,"milk"] = 1; + +/* the Norwegian lives in the first house */ +f10: nationality[1,"Norwegian"] = 1; + +/* the man who smokes Chesterfields lives in the house next to the man + with the fox */ +f11{h in HOUSE}: + (1 - smoke[h,"Chesterfield"]) + + (if h = 1 then 0 else pet[h-1,"fox"]) + + (if h = 5 then 0 else pet[h+1,"fox"]) >= 1; + +/* Kools are smoked in the house next to the house where the horse is + kept */ +f12{h in HOUSE}: + (1 - smoke[h,"Kools"]) + + (if h = 1 then 0 else pet[h-1,"horse"]) + + (if h = 5 then 0 else pet[h+1,"horse"]) >= 1; + +/* the Lucky Strike smoker drinks orange juice */ +f13{h in HOUSE}: smoke[h,"Lucky_Strike"] = drink[h,"orange_juice"]; + +/* the Japanese smokes Parliaments */ +f14{h in HOUSE}: nationality[h,"Japanese"] = smoke[h,"Parliament"]; + +/* the Norwegian lives next to the blue house */ +f15{h in HOUSE}: + (1 - nationality[h,"Norwegian"]) + + (if h = 1 then 0 else color[h-1,"blue"]) + + (if h = 5 then 0 else color[h+1,"blue"]) >= 1; + +solve; + +printf "\n"; +printf "HOUSE COLOR NATIONALITY DRINK SMOKE PET\n"; +for {h in HOUSE} +{ printf "%5d", h; + printf{c in COLOR: color[h,c]} " %-6s", c; + printf{n in NATIONALITY: nationality[h,n]} " %-11s", n; + printf{d in DRINK: drink[h,d]} " %-12s", d; + printf{s in SMOKE: smoke[h,s]} " %-12s", s; + printf{p in PET: pet[h,p]} " %-6s", p; + printf "\n"; +} +printf "\n"; + +end; diff -r d59bea55db9b -r c445c931472f include/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/Makefile.am Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,5 @@ +## Process this file with automake to produce Makefile.in ## + +include_HEADERS = glpk.h + +## eof ## diff -r d59bea55db9b -r c445c931472f include/glpk.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/glpk.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1750 @@ +/* glpk.h */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPK_H +#define GLPK_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* library version numbers: */ +#define GLP_MAJOR_VERSION 4 +#define GLP_MINOR_VERSION 45 + +#ifndef GLP_PROB_DEFINED +#define GLP_PROB_DEFINED +typedef struct { double _opaque_prob[100]; } glp_prob; +/* LP/MIP problem object */ +#endif + +/* optimization direction flag: */ +#define GLP_MIN 1 /* minimization */ +#define GLP_MAX 2 /* maximization */ + +/* kind of structural variable: */ +#define GLP_CV 1 /* continuous variable */ +#define GLP_IV 2 /* integer variable */ +#define GLP_BV 3 /* binary variable */ + +/* type of auxiliary/structural variable: */ +#define GLP_FR 1 /* free variable */ +#define GLP_LO 2 /* variable with lower bound */ +#define GLP_UP 3 /* variable with upper bound */ +#define GLP_DB 4 /* double-bounded variable */ +#define GLP_FX 5 /* fixed variable */ + +/* status of auxiliary/structural variable: */ +#define GLP_BS 1 /* basic variable */ +#define GLP_NL 2 /* non-basic variable on lower bound */ +#define GLP_NU 3 /* non-basic variable on upper bound */ +#define GLP_NF 4 /* non-basic free variable */ +#define GLP_NS 5 /* non-basic fixed variable */ + +/* scaling options: */ +#define GLP_SF_GM 0x01 /* perform geometric mean scaling */ +#define GLP_SF_EQ 0x10 /* perform equilibration scaling */ +#define GLP_SF_2N 0x20 /* round scale factors to power of two */ +#define GLP_SF_SKIP 0x40 /* skip if problem is well scaled */ +#define GLP_SF_AUTO 0x80 /* choose scaling options automatically */ + +/* solution indicator: */ +#define GLP_SOL 1 /* basic solution */ +#define GLP_IPT 2 /* interior-point solution */ +#define GLP_MIP 3 /* mixed integer solution */ + +/* solution status: */ +#define GLP_UNDEF 1 /* solution is undefined */ +#define GLP_FEAS 2 /* solution is feasible */ +#define GLP_INFEAS 3 /* solution is infeasible */ +#define GLP_NOFEAS 4 /* no feasible solution exists */ +#define GLP_OPT 5 /* solution is optimal */ +#define GLP_UNBND 6 /* solution is unbounded */ + +typedef struct +{ /* basis factorization control parameters */ + int msg_lev; /* (reserved) */ + int type; /* factorization type: */ +#define GLP_BF_FT 1 /* LUF + Forrest-Tomlin */ +#define GLP_BF_BG 2 /* LUF + Schur compl. + Bartels-Golub */ +#define GLP_BF_GR 3 /* LUF + Schur compl. + Givens rotation */ + int lu_size; /* luf.sv_size */ + double piv_tol; /* luf.piv_tol */ + int piv_lim; /* luf.piv_lim */ + int suhl; /* luf.suhl */ + double eps_tol; /* luf.eps_tol */ + double max_gro; /* luf.max_gro */ + int nfs_max; /* fhv.hh_max */ + double upd_tol; /* fhv.upd_tol */ + int nrs_max; /* lpf.n_max */ + int rs_size; /* lpf.v_size */ + double foo_bar[38]; /* (reserved) */ +} glp_bfcp; + +typedef struct +{ /* simplex method control parameters */ + int msg_lev; /* message level: */ +#define GLP_MSG_OFF 0 /* no output */ +#define GLP_MSG_ERR 1 /* warning and error messages only */ +#define GLP_MSG_ON 2 /* normal output */ +#define GLP_MSG_ALL 3 /* full output */ +#define GLP_MSG_DBG 4 /* debug output */ + int meth; /* simplex method option: */ +#define GLP_PRIMAL 1 /* use primal simplex */ +#define GLP_DUALP 2 /* use dual; if it fails, use primal */ +#define GLP_DUAL 3 /* use dual simplex */ + int pricing; /* pricing technique: */ +#define GLP_PT_STD 0x11 /* standard (Dantzig rule) */ +#define GLP_PT_PSE 0x22 /* projected steepest edge */ + int r_test; /* ratio test technique: */ +#define GLP_RT_STD 0x11 /* standard (textbook) */ +#define GLP_RT_HAR 0x22 /* two-pass Harris' ratio test */ + double tol_bnd; /* spx.tol_bnd */ + double tol_dj; /* spx.tol_dj */ + double tol_piv; /* spx.tol_piv */ + double obj_ll; /* spx.obj_ll */ + double obj_ul; /* spx.obj_ul */ + int it_lim; /* spx.it_lim */ + int tm_lim; /* spx.tm_lim (milliseconds) */ + int out_frq; /* spx.out_frq */ + int out_dly; /* spx.out_dly (milliseconds) */ + int presolve; /* enable/disable using LP presolver */ + double foo_bar[36]; /* (reserved) */ +} glp_smcp; + +typedef struct +{ /* interior-point solver control parameters */ + int msg_lev; /* message level (see glp_smcp) */ + int ord_alg; /* ordering algorithm: */ +#define GLP_ORD_NONE 0 /* natural (original) ordering */ +#define GLP_ORD_QMD 1 /* quotient minimum degree (QMD) */ +#define GLP_ORD_AMD 2 /* approx. minimum degree (AMD) */ +#define GLP_ORD_SYMAMD 3 /* approx. minimum degree (SYMAMD) */ + double foo_bar[48]; /* (reserved) */ +} glp_iptcp; + +#ifndef GLP_TREE_DEFINED +#define GLP_TREE_DEFINED +typedef struct { double _opaque_tree[100]; } glp_tree; +/* branch-and-bound tree */ +#endif + +typedef struct +{ /* integer optimizer control parameters */ + int msg_lev; /* message level (see glp_smcp) */ + int br_tech; /* branching technique: */ +#define GLP_BR_FFV 1 /* first fractional variable */ +#define GLP_BR_LFV 2 /* last fractional variable */ +#define GLP_BR_MFV 3 /* most fractional variable */ +#define GLP_BR_DTH 4 /* heuristic by Driebeck and Tomlin */ +#define GLP_BR_PCH 5 /* hybrid pseudocost heuristic */ + int bt_tech; /* backtracking technique: */ +#define GLP_BT_DFS 1 /* depth first search */ +#define GLP_BT_BFS 2 /* breadth first search */ +#define GLP_BT_BLB 3 /* best local bound */ +#define GLP_BT_BPH 4 /* best projection heuristic */ + double tol_int; /* mip.tol_int */ + double tol_obj; /* mip.tol_obj */ + int tm_lim; /* mip.tm_lim (milliseconds) */ + int out_frq; /* mip.out_frq (milliseconds) */ + int out_dly; /* mip.out_dly (milliseconds) */ + void (*cb_func)(glp_tree *T, void *info); + /* mip.cb_func */ + void *cb_info; /* mip.cb_info */ + int cb_size; /* mip.cb_size */ + int pp_tech; /* preprocessing technique: */ +#define GLP_PP_NONE 0 /* disable preprocessing */ +#define GLP_PP_ROOT 1 /* preprocessing only on root level */ +#define GLP_PP_ALL 2 /* preprocessing on all levels */ + double mip_gap; /* relative MIP gap tolerance */ + int mir_cuts; /* MIR cuts (GLP_ON/GLP_OFF) */ + int gmi_cuts; /* Gomory's cuts (GLP_ON/GLP_OFF) */ + int cov_cuts; /* cover cuts (GLP_ON/GLP_OFF) */ + int clq_cuts; /* clique cuts (GLP_ON/GLP_OFF) */ + int presolve; /* enable/disable using MIP presolver */ + int binarize; /* try to binarize integer variables */ + int fp_heur; /* feasibility pump heuristic */ +#if 1 /* 28/V-2010 */ + int alien; /* use alien solver */ +#endif + double foo_bar[29]; /* (reserved) */ +} glp_iocp; + +typedef struct +{ /* additional row attributes */ + int level; + /* subproblem level at which the row was added */ + int origin; + /* row origin flag: */ +#define GLP_RF_REG 0 /* regular constraint */ +#define GLP_RF_LAZY 1 /* "lazy" constraint */ +#define GLP_RF_CUT 2 /* cutting plane constraint */ + int klass; + /* row class descriptor: */ +#define GLP_RF_GMI 1 /* Gomory's mixed integer cut */ +#define GLP_RF_MIR 2 /* mixed integer rounding cut */ +#define GLP_RF_COV 3 /* mixed cover cut */ +#define GLP_RF_CLQ 4 /* clique cut */ + double foo_bar[7]; + /* (reserved) */ +} glp_attr; + +/* enable/disable flag: */ +#define GLP_ON 1 /* enable something */ +#define GLP_OFF 0 /* disable something */ + +/* reason codes: */ +#define GLP_IROWGEN 0x01 /* request for row generation */ +#define GLP_IBINGO 0x02 /* better integer solution found */ +#define GLP_IHEUR 0x03 /* request for heuristic solution */ +#define GLP_ICUTGEN 0x04 /* request for cut generation */ +#define GLP_IBRANCH 0x05 /* request for branching */ +#define GLP_ISELECT 0x06 /* request for subproblem selection */ +#define GLP_IPREPRO 0x07 /* request for preprocessing */ + +/* branch selection indicator: */ +#define GLP_NO_BRNCH 0 /* select no branch */ +#define GLP_DN_BRNCH 1 /* select down-branch */ +#define GLP_UP_BRNCH 2 /* select up-branch */ + +/* return codes: */ +#define GLP_EBADB 0x01 /* invalid basis */ +#define GLP_ESING 0x02 /* singular matrix */ +#define GLP_ECOND 0x03 /* ill-conditioned matrix */ +#define GLP_EBOUND 0x04 /* invalid bounds */ +#define GLP_EFAIL 0x05 /* solver failed */ +#define GLP_EOBJLL 0x06 /* objective lower limit reached */ +#define GLP_EOBJUL 0x07 /* objective upper limit reached */ +#define GLP_EITLIM 0x08 /* iteration limit exceeded */ +#define GLP_ETMLIM 0x09 /* time limit exceeded */ +#define GLP_ENOPFS 0x0A /* no primal feasible solution */ +#define GLP_ENODFS 0x0B /* no dual feasible solution */ +#define GLP_EROOT 0x0C /* root LP optimum not provided */ +#define GLP_ESTOP 0x0D /* search terminated by application */ +#define GLP_EMIPGAP 0x0E /* relative mip gap tolerance reached */ +#define GLP_ENOFEAS 0x0F /* no primal/dual feasible solution */ +#define GLP_ENOCVG 0x10 /* no convergence */ +#define GLP_EINSTAB 0x11 /* numerical instability */ +#define GLP_EDATA 0x12 /* invalid data */ +#define GLP_ERANGE 0x13 /* result out of range */ + +/* condition indicator: */ +#define GLP_KKT_PE 1 /* primal equalities */ +#define GLP_KKT_PB 2 /* primal bounds */ +#define GLP_KKT_DE 3 /* dual equalities */ +#define GLP_KKT_DB 4 /* dual bounds */ +#define GLP_KKT_CS 5 /* complementary slackness */ + +/* MPS file format: */ +#define GLP_MPS_DECK 1 /* fixed (ancient) */ +#define GLP_MPS_FILE 2 /* free (modern) */ + +typedef struct +{ /* MPS format control parameters */ + int blank; + /* character code to replace blanks in symbolic names */ + char *obj_name; + /* objective row name */ + double tol_mps; + /* zero tolerance for MPS data */ + double foo_bar[17]; + /* (reserved for use in the future) */ +} glp_mpscp; + +typedef struct +{ /* CPLEX LP format control parameters */ + double foo_bar[20]; + /* (reserved for use in the future) */ +} glp_cpxcp; + +#ifndef GLP_TRAN_DEFINED +#define GLP_TRAN_DEFINED +typedef struct { double _opaque_tran[100]; } glp_tran; +/* MathProg translator workspace */ +#endif + +glp_prob *glp_create_prob(void); +/* create problem object */ + +void glp_set_prob_name(glp_prob *P, const char *name); +/* assign (change) problem name */ + +void glp_set_obj_name(glp_prob *P, const char *name); +/* assign (change) objective function name */ + +void glp_set_obj_dir(glp_prob *P, int dir); +/* set (change) optimization direction flag */ + +int glp_add_rows(glp_prob *P, int nrs); +/* add new rows to problem object */ + +int glp_add_cols(glp_prob *P, int ncs); +/* add new columns to problem object */ + +void glp_set_row_name(glp_prob *P, int i, const char *name); +/* assign (change) row name */ + +void glp_set_col_name(glp_prob *P, int j, const char *name); +/* assign (change) column name */ + +void glp_set_row_bnds(glp_prob *P, int i, int type, double lb, + double ub); +/* set (change) row bounds */ + +void glp_set_col_bnds(glp_prob *P, int j, int type, double lb, + double ub); +/* set (change) column bounds */ + +void glp_set_obj_coef(glp_prob *P, int j, double coef); +/* set (change) obj. coefficient or constant term */ + +void glp_set_mat_row(glp_prob *P, int i, int len, const int ind[], + const double val[]); +/* set (replace) row of the constraint matrix */ + +void glp_set_mat_col(glp_prob *P, int j, int len, const int ind[], + const double val[]); +/* set (replace) column of the constraint matrix */ + +void glp_load_matrix(glp_prob *P, int ne, const int ia[], + const int ja[], const double ar[]); +/* load (replace) the whole constraint matrix */ + +int glp_check_dup(int m, int n, int ne, const int ia[], const int ja[]); +/* check for duplicate elements in sparse matrix */ + +void glp_sort_matrix(glp_prob *P); +/* sort elements of the constraint matrix */ + +void glp_del_rows(glp_prob *P, int nrs, const int num[]); +/* delete specified rows from problem object */ + +void glp_del_cols(glp_prob *P, int ncs, const int num[]); +/* delete specified columns from problem object */ + +void glp_copy_prob(glp_prob *dest, glp_prob *prob, int names); +/* copy problem object content */ + +void glp_erase_prob(glp_prob *P); +/* erase problem object content */ + +void glp_delete_prob(glp_prob *P); +/* delete problem object */ + +const char *glp_get_prob_name(glp_prob *P); +/* retrieve problem name */ + +const char *glp_get_obj_name(glp_prob *P); +/* retrieve objective function name */ + +int glp_get_obj_dir(glp_prob *P); +/* retrieve optimization direction flag */ + +int glp_get_num_rows(glp_prob *P); +/* retrieve number of rows */ + +int glp_get_num_cols(glp_prob *P); +/* retrieve number of columns */ + +const char *glp_get_row_name(glp_prob *P, int i); +/* retrieve row name */ + +const char *glp_get_col_name(glp_prob *P, int j); +/* retrieve column name */ + +int glp_get_row_type(glp_prob *P, int i); +/* retrieve row type */ + +double glp_get_row_lb(glp_prob *P, int i); +/* retrieve row lower bound */ + +double glp_get_row_ub(glp_prob *P, int i); +/* retrieve row upper bound */ + +int glp_get_col_type(glp_prob *P, int j); +/* retrieve column type */ + +double glp_get_col_lb(glp_prob *P, int j); +/* retrieve column lower bound */ + +double glp_get_col_ub(glp_prob *P, int j); +/* retrieve column upper bound */ + +double glp_get_obj_coef(glp_prob *P, int j); +/* retrieve obj. coefficient or constant term */ + +int glp_get_num_nz(glp_prob *P); +/* retrieve number of constraint coefficients */ + +int glp_get_mat_row(glp_prob *P, int i, int ind[], double val[]); +/* retrieve row of the constraint matrix */ + +int glp_get_mat_col(glp_prob *P, int j, int ind[], double val[]); +/* retrieve column of the constraint matrix */ + +void glp_create_index(glp_prob *P); +/* create the name index */ + +int glp_find_row(glp_prob *P, const char *name); +/* find row by its name */ + +int glp_find_col(glp_prob *P, const char *name); +/* find column by its name */ + +void glp_delete_index(glp_prob *P); +/* delete the name index */ + +void glp_set_rii(glp_prob *P, int i, double rii); +/* set (change) row scale factor */ + +void glp_set_sjj(glp_prob *P, int j, double sjj); +/* set (change) column scale factor */ + +double glp_get_rii(glp_prob *P, int i); +/* retrieve row scale factor */ + +double glp_get_sjj(glp_prob *P, int j); +/* retrieve column scale factor */ + +void glp_scale_prob(glp_prob *P, int flags); +/* scale problem data */ + +void glp_unscale_prob(glp_prob *P); +/* unscale problem data */ + +void glp_set_row_stat(glp_prob *P, int i, int stat); +/* set (change) row status */ + +void glp_set_col_stat(glp_prob *P, int j, int stat); +/* set (change) column status */ + +void glp_std_basis(glp_prob *P); +/* construct standard initial LP basis */ + +void glp_adv_basis(glp_prob *P, int flags); +/* construct advanced initial LP basis */ + +void glp_cpx_basis(glp_prob *P); +/* construct Bixby's initial LP basis */ + +int glp_simplex(glp_prob *P, const glp_smcp *parm); +/* solve LP problem with the simplex method */ + +int glp_exact(glp_prob *P, const glp_smcp *parm); +/* solve LP problem in exact arithmetic */ + +void glp_init_smcp(glp_smcp *parm); +/* initialize simplex method control parameters */ + +int glp_get_status(glp_prob *P); +/* retrieve generic status of basic solution */ + +int glp_get_prim_stat(glp_prob *P); +/* retrieve status of primal basic solution */ + +int glp_get_dual_stat(glp_prob *P); +/* retrieve status of dual basic solution */ + +double glp_get_obj_val(glp_prob *P); +/* retrieve objective value (basic solution) */ + +int glp_get_row_stat(glp_prob *P, int i); +/* retrieve row status */ + +double glp_get_row_prim(glp_prob *P, int i); +/* retrieve row primal value (basic solution) */ + +double glp_get_row_dual(glp_prob *P, int i); +/* retrieve row dual value (basic solution) */ + +int glp_get_col_stat(glp_prob *P, int j); +/* retrieve column status */ + +double glp_get_col_prim(glp_prob *P, int j); +/* retrieve column primal value (basic solution) */ + +double glp_get_col_dual(glp_prob *P, int j); +/* retrieve column dual value (basic solution) */ + +int glp_get_unbnd_ray(glp_prob *P); +/* determine variable causing unboundedness */ + +int glp_interior(glp_prob *P, const glp_iptcp *parm); +/* solve LP problem with the interior-point method */ + +void glp_init_iptcp(glp_iptcp *parm); +/* initialize interior-point solver control parameters */ + +int glp_ipt_status(glp_prob *P); +/* retrieve status of interior-point solution */ + +double glp_ipt_obj_val(glp_prob *P); +/* retrieve objective value (interior point) */ + +double glp_ipt_row_prim(glp_prob *P, int i); +/* retrieve row primal value (interior point) */ + +double glp_ipt_row_dual(glp_prob *P, int i); +/* retrieve row dual value (interior point) */ + +double glp_ipt_col_prim(glp_prob *P, int j); +/* retrieve column primal value (interior point) */ + +double glp_ipt_col_dual(glp_prob *P, int j); +/* retrieve column dual value (interior point) */ + +void glp_set_col_kind(glp_prob *P, int j, int kind); +/* set (change) column kind */ + +int glp_get_col_kind(glp_prob *P, int j); +/* retrieve column kind */ + +int glp_get_num_int(glp_prob *P); +/* retrieve number of integer columns */ + +int glp_get_num_bin(glp_prob *P); +/* retrieve number of binary columns */ + +int glp_intopt(glp_prob *P, const glp_iocp *parm); +/* solve MIP problem with the branch-and-bound method */ + +void glp_init_iocp(glp_iocp *parm); +/* initialize integer optimizer control parameters */ + +int glp_mip_status(glp_prob *P); +/* retrieve status of MIP solution */ + +double glp_mip_obj_val(glp_prob *P); +/* retrieve objective value (MIP solution) */ + +double glp_mip_row_val(glp_prob *P, int i); +/* retrieve row value (MIP solution) */ + +double glp_mip_col_val(glp_prob *P, int j); +/* retrieve column value (MIP solution) */ + +int glp_print_sol(glp_prob *P, const char *fname); +/* write basic solution in printable format */ + +int glp_read_sol(glp_prob *P, const char *fname); +/* read basic solution from text file */ + +int glp_write_sol(glp_prob *P, const char *fname); +/* write basic solution to text file */ + +int glp_print_ranges(glp_prob *P, int len, const int list[], + int flags, const char *fname); +/* print sensitivity analysis report */ + +int glp_print_ipt(glp_prob *P, const char *fname); +/* write interior-point solution in printable format */ + +int glp_read_ipt(glp_prob *P, const char *fname); +/* read interior-point solution from text file */ + +int glp_write_ipt(glp_prob *P, const char *fname); +/* write interior-point solution to text file */ + +int glp_print_mip(glp_prob *P, const char *fname); +/* write MIP solution in printable format */ + +int glp_read_mip(glp_prob *P, const char *fname); +/* read MIP solution from text file */ + +int glp_write_mip(glp_prob *P, const char *fname); +/* write MIP solution to text file */ + +int glp_bf_exists(glp_prob *P); +/* check if the basis factorization exists */ + +int glp_factorize(glp_prob *P); +/* compute the basis factorization */ + +int glp_bf_updated(glp_prob *P); +/* check if the basis factorization has been updated */ + +void glp_get_bfcp(glp_prob *P, glp_bfcp *parm); +/* retrieve basis factorization control parameters */ + +void glp_set_bfcp(glp_prob *P, const glp_bfcp *parm); +/* change basis factorization control parameters */ + +int glp_get_bhead(glp_prob *P, int k); +/* retrieve the basis header information */ + +int glp_get_row_bind(glp_prob *P, int i); +/* retrieve row index in the basis header */ + +int glp_get_col_bind(glp_prob *P, int j); +/* retrieve column index in the basis header */ + +void glp_ftran(glp_prob *P, double x[]); +/* perform forward transformation (solve system B*x = b) */ + +void glp_btran(glp_prob *P, double x[]); +/* perform backward transformation (solve system B'*x = b) */ + +int glp_warm_up(glp_prob *P); +/* "warm up" LP basis */ + +int glp_eval_tab_row(glp_prob *P, int k, int ind[], double val[]); +/* compute row of the simplex tableau */ + +int glp_eval_tab_col(glp_prob *P, int k, int ind[], double val[]); +/* compute column of the simplex tableau */ + +int glp_transform_row(glp_prob *P, int len, int ind[], double val[]); +/* transform explicitly specified row */ + +int glp_transform_col(glp_prob *P, int len, int ind[], double val[]); +/* transform explicitly specified column */ + +int glp_prim_rtest(glp_prob *P, int len, const int ind[], + const double val[], int dir, double eps); +/* perform primal ratio test */ + +int glp_dual_rtest(glp_prob *P, int len, const int ind[], + const double val[], int dir, double eps); +/* perform dual ratio test */ + +void glp_analyze_bound(glp_prob *P, int k, double *value1, int *var1, + double *value2, int *var2); +/* analyze active bound of non-basic variable */ + +void glp_analyze_coef(glp_prob *P, int k, double *coef1, int *var1, + double *value1, double *coef2, int *var2, double *value2); +/* analyze objective coefficient at basic variable */ + +int glp_ios_reason(glp_tree *T); +/* determine reason for calling the callback routine */ + +glp_prob *glp_ios_get_prob(glp_tree *T); +/* access the problem object */ + +void glp_ios_tree_size(glp_tree *T, int *a_cnt, int *n_cnt, + int *t_cnt); +/* determine size of the branch-and-bound tree */ + +int glp_ios_curr_node(glp_tree *T); +/* determine current active subproblem */ + +int glp_ios_next_node(glp_tree *T, int p); +/* determine next active subproblem */ + +int glp_ios_prev_node(glp_tree *T, int p); +/* determine previous active subproblem */ + +int glp_ios_up_node(glp_tree *T, int p); +/* determine parent subproblem */ + +int glp_ios_node_level(glp_tree *T, int p); +/* determine subproblem level */ + +double glp_ios_node_bound(glp_tree *T, int p); +/* determine subproblem local bound */ + +int glp_ios_best_node(glp_tree *T); +/* find active subproblem with best local bound */ + +double glp_ios_mip_gap(glp_tree *T); +/* compute relative MIP gap */ + +void *glp_ios_node_data(glp_tree *T, int p); +/* access subproblem application-specific data */ + +void glp_ios_row_attr(glp_tree *T, int i, glp_attr *attr); +/* retrieve additional row attributes */ + +int glp_ios_pool_size(glp_tree *T); +/* determine current size of the cut pool */ + +int glp_ios_add_row(glp_tree *T, + const char *name, int klass, int flags, int len, const int ind[], + const double val[], int type, double rhs); +/* add row (constraint) to the cut pool */ + +void glp_ios_del_row(glp_tree *T, int i); +/* remove row (constraint) from the cut pool */ + +void glp_ios_clear_pool(glp_tree *T); +/* remove all rows (constraints) from the cut pool */ + +int glp_ios_can_branch(glp_tree *T, int j); +/* check if can branch upon specified variable */ + +void glp_ios_branch_upon(glp_tree *T, int j, int sel); +/* choose variable to branch upon */ + +void glp_ios_select_node(glp_tree *T, int p); +/* select subproblem to continue the search */ + +int glp_ios_heur_sol(glp_tree *T, const double x[]); +/* provide solution found by heuristic */ + +void glp_ios_terminate(glp_tree *T); +/* terminate the solution process */ + +void glp_init_mpscp(glp_mpscp *parm); +/* initialize MPS format control parameters */ + +int glp_read_mps(glp_prob *P, int fmt, const glp_mpscp *parm, + const char *fname); +/* read problem data in MPS format */ + +int glp_write_mps(glp_prob *P, int fmt, const glp_mpscp *parm, + const char *fname); +/* write problem data in MPS format */ + +void glp_init_cpxcp(glp_cpxcp *parm); +/* initialize CPLEX LP format control parameters */ + +int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname); +/* read problem data in CPLEX LP format */ + +int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname); +/* write problem data in CPLEX LP format */ + +int glp_read_prob(glp_prob *P, int flags, const char *fname); +/* read problem data in GLPK format */ + +int glp_write_prob(glp_prob *P, int flags, const char *fname); +/* write problem data in GLPK format */ + +glp_tran *glp_mpl_alloc_wksp(void); +/* allocate the MathProg translator workspace */ + +int glp_mpl_read_model(glp_tran *tran, const char *fname, int skip); +/* read and translate model section */ + +int glp_mpl_read_data(glp_tran *tran, const char *fname); +/* read and translate data section */ + +int glp_mpl_generate(glp_tran *tran, const char *fname); +/* generate the model */ + +void glp_mpl_build_prob(glp_tran *tran, glp_prob *prob); +/* build LP/MIP problem instance from the model */ + +int glp_mpl_postsolve(glp_tran *tran, glp_prob *prob, int sol); +/* postsolve the model */ + +void glp_mpl_free_wksp(glp_tran *tran); +/* free the MathProg translator workspace */ + +int glp_main(int argc, const char *argv[]); +/* stand-alone LP/MIP solver */ + +/**********************************************************************/ + +#ifndef GLP_LONG_DEFINED +#define GLP_LONG_DEFINED +typedef struct { int lo, hi; } glp_long; +/* long integer data type */ +#endif + +int glp_init_env(void); +/* initialize GLPK environment */ + +const char *glp_version(void); +/* determine library version */ + +int glp_free_env(void); +/* free GLPK environment */ + +void glp_printf(const char *fmt, ...); +/* write formatted output to terminal */ + +void glp_vprintf(const char *fmt, va_list arg); +/* write formatted output to terminal */ + +int glp_term_out(int flag); +/* enable/disable terminal output */ + +void glp_term_hook(int (*func)(void *info, const char *s), void *info); +/* install hook to intercept terminal output */ + +int glp_open_tee(const char *fname); +/* start copying terminal output to text file */ + +int glp_close_tee(void); +/* stop copying terminal output to text file */ + +#ifndef GLP_ERROR_DEFINED +#define GLP_ERROR_DEFINED +typedef void (*_glp_error)(const char *fmt, ...); +#endif + +#define glp_error glp_error_(__FILE__, __LINE__) +_glp_error glp_error_(const char *file, int line); +/* display error message and terminate execution */ + +#define glp_assert(expr) \ + ((void)((expr) || (glp_assert_(#expr, __FILE__, __LINE__), 1))) +void glp_assert_(const char *expr, const char *file, int line); +/* check for logical condition */ + +void glp_error_hook(void (*func)(void *info), void *info); +/* install hook to intercept abnormal termination */ + +void *glp_malloc(int size); +/* allocate memory block */ + +void *glp_calloc(int n, int size); +/* allocate memory block */ + +void glp_free(void *ptr); +/* free memory block */ + +void glp_mem_limit(int limit); +/* set memory usage limit */ + +void glp_mem_usage(int *count, int *cpeak, glp_long *total, + glp_long *tpeak); +/* get memory usage information */ + +glp_long glp_time(void); +/* determine current universal time */ + +double glp_difftime(glp_long t1, glp_long t0); +/* compute difference between two time values */ + +/**********************************************************************/ + +#ifndef GLP_DATA_DEFINED +#define GLP_DATA_DEFINED +typedef struct { double _opaque_data[100]; } glp_data; +/* plain data file */ +#endif + +glp_data *glp_sdf_open_file(const char *fname); +/* open plain data file */ + +void glp_sdf_set_jump(glp_data *data, void *jump); +/* set up error handling */ + +void glp_sdf_error(glp_data *data, const char *fmt, ...); +/* print error message */ + +void glp_sdf_warning(glp_data *data, const char *fmt, ...); +/* print warning message */ + +int glp_sdf_read_int(glp_data *data); +/* read integer number */ + +double glp_sdf_read_num(glp_data *data); +/* read floating-point number */ + +const char *glp_sdf_read_item(glp_data *data); +/* read data item */ + +const char *glp_sdf_read_text(glp_data *data); +/* read text until end of line */ + +int glp_sdf_line(glp_data *data); +/* determine current line number */ + +void glp_sdf_close_file(glp_data *data); +/* close plain data file */ + +/**********************************************************************/ + +typedef struct _glp_graph glp_graph; +typedef struct _glp_vertex glp_vertex; +typedef struct _glp_arc glp_arc; + +struct _glp_graph +{ /* graph descriptor */ + void *pool; /* DMP *pool; */ + /* memory pool to store graph components */ + char *name; + /* graph name (1 to 255 chars); NULL means no name is assigned + to the graph */ + int nv_max; + /* length of the vertex list (enlarged automatically) */ + int nv; + /* number of vertices in the graph, 0 <= nv <= nv_max */ + int na; + /* number of arcs in the graph, na >= 0 */ + glp_vertex **v; /* glp_vertex *v[1+nv_max]; */ + /* v[i], 1 <= i <= nv, is a pointer to i-th vertex */ + void *index; /* AVL *index; */ + /* vertex index to find vertices by their names; NULL means the + index does not exist */ + int v_size; + /* size of data associated with each vertex (0 to 256 bytes) */ + int a_size; + /* size of data associated with each arc (0 to 256 bytes) */ +}; + +struct _glp_vertex +{ /* vertex descriptor */ + int i; + /* vertex ordinal number, 1 <= i <= nv */ + char *name; + /* vertex name (1 to 255 chars); NULL means no name is assigned + to the vertex */ + void *entry; /* AVLNODE *entry; */ + /* pointer to corresponding entry in the vertex index; NULL means + that either the index does not exist or the vertex has no name + assigned */ + void *data; + /* pointer to data associated with the vertex */ + void *temp; + /* working pointer */ + glp_arc *in; + /* pointer to the (unordered) list of incoming arcs */ + glp_arc *out; + /* pointer to the (unordered) list of outgoing arcs */ +}; + +struct _glp_arc +{ /* arc descriptor */ + glp_vertex *tail; + /* pointer to the tail endpoint */ + glp_vertex *head; + /* pointer to the head endpoint */ + void *data; + /* pointer to data associated with the arc */ + void *temp; + /* working pointer */ + glp_arc *t_prev; + /* pointer to previous arc having the same tail endpoint */ + glp_arc *t_next; + /* pointer to next arc having the same tail endpoint */ + glp_arc *h_prev; + /* pointer to previous arc having the same head endpoint */ + glp_arc *h_next; + /* pointer to next arc having the same head endpoint */ +}; + +glp_graph *glp_create_graph(int v_size, int a_size); +/* create graph */ + +void glp_set_graph_name(glp_graph *G, const char *name); +/* assign (change) graph name */ + +int glp_add_vertices(glp_graph *G, int nadd); +/* add new vertices to graph */ + +void glp_set_vertex_name(glp_graph *G, int i, const char *name); +/* assign (change) vertex name */ + +glp_arc *glp_add_arc(glp_graph *G, int i, int j); +/* add new arc to graph */ + +void glp_del_vertices(glp_graph *G, int ndel, const int num[]); +/* delete vertices from graph */ + +void glp_del_arc(glp_graph *G, glp_arc *a); +/* delete arc from graph */ + +void glp_erase_graph(glp_graph *G, int v_size, int a_size); +/* erase graph content */ + +void glp_delete_graph(glp_graph *G); +/* delete graph */ + +void glp_create_v_index(glp_graph *G); +/* create vertex name index */ + +int glp_find_vertex(glp_graph *G, const char *name); +/* find vertex by its name */ + +void glp_delete_v_index(glp_graph *G); +/* delete vertex name index */ + +int glp_read_graph(glp_graph *G, const char *fname); +/* read graph from plain text file */ + +int glp_write_graph(glp_graph *G, const char *fname); +/* write graph to plain text file */ + +void glp_mincost_lp(glp_prob *P, glp_graph *G, int names, int v_rhs, + int a_low, int a_cap, int a_cost); +/* convert minimum cost flow problem to LP */ + +int glp_mincost_okalg(glp_graph *G, int v_rhs, int a_low, int a_cap, + int a_cost, double *sol, int a_x, int v_pi); +/* find minimum-cost flow with out-of-kilter algorithm */ + +void glp_maxflow_lp(glp_prob *P, glp_graph *G, int names, int s, + int t, int a_cap); +/* convert maximum flow problem to LP */ + +int glp_maxflow_ffalg(glp_graph *G, int s, int t, int a_cap, + double *sol, int a_x, int v_cut); +/* find maximal flow with Ford-Fulkerson algorithm */ + +int glp_check_asnprob(glp_graph *G, int v_set); +/* check correctness of assignment problem data */ + +/* assignment problem formulation: */ +#define GLP_ASN_MIN 1 /* perfect matching (minimization) */ +#define GLP_ASN_MAX 2 /* perfect matching (maximization) */ +#define GLP_ASN_MMP 3 /* maximum matching */ + +int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names, + int v_set, int a_cost); +/* convert assignment problem to LP */ + +int glp_asnprob_okalg(int form, glp_graph *G, int v_set, int a_cost, + double *sol, int a_x); +/* solve assignment problem with out-of-kilter algorithm */ + +int glp_asnprob_hall(glp_graph *G, int v_set, int a_x); +/* find bipartite matching of maximum cardinality */ + +double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls); +/* solve critical path problem */ + +int glp_read_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, + int a_cost, const char *fname); +/* read min-cost flow problem data in DIMACS format */ + +int glp_write_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, + int a_cost, const char *fname); +/* write min-cost flow problem data in DIMACS format */ + +int glp_read_maxflow(glp_graph *G, int *s, int *t, int a_cap, + const char *fname); +/* read maximum flow problem data in DIMACS format */ + +int glp_write_maxflow(glp_graph *G, int s, int t, int a_cap, + const char *fname); +/* write maximum flow problem data in DIMACS format */ + +int glp_read_asnprob(glp_graph *G, int v_set, int a_cost, const char + *fname); +/* read assignment problem data in DIMACS format */ + +int glp_write_asnprob(glp_graph *G, int v_set, int a_cost, const char + *fname); +/* write assignment problem data in DIMACS format */ + +int glp_read_ccdata(glp_graph *G, int v_wgt, const char *fname); +/* read graph in DIMACS clique/coloring format */ + +int glp_write_ccdata(glp_graph *G, int v_wgt, const char *fname); +/* write graph in DIMACS clique/coloring format */ + +int glp_netgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, + const int parm[1+15]); +/* Klingman's network problem generator */ + +int glp_gridgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, + const int parm[1+14]); +/* grid-like network problem generator */ + +int glp_rmfgen(glp_graph *G, int *s, int *t, int a_cap, + const int parm[1+5]); +/* Goldfarb's maximum flow problem generator */ + +int glp_weak_comp(glp_graph *G, int v_num); +/* find all weakly connected components of graph */ + +int glp_strong_comp(glp_graph *G, int v_num); +/* find all strongly connected components of graph */ + +int glp_top_sort(glp_graph *G, int v_num); +/* topological sorting of acyclic digraph */ + +int glp_wclique_exact(glp_graph *G, int v_wgt, double *sol, int v_set); +/* find maximum weight clique with exact algorithm */ + +/*********************************************************************** +* NOTE: All symbols defined below are obsolete and kept here only for +* backward compatibility. +***********************************************************************/ + +#define LPX glp_prob + +/* problem class: */ +#define LPX_LP 100 /* linear programming (LP) */ +#define LPX_MIP 101 /* mixed integer programming (MIP) */ + +/* type of auxiliary/structural variable: */ +#define LPX_FR 110 /* free variable */ +#define LPX_LO 111 /* variable with lower bound */ +#define LPX_UP 112 /* variable with upper bound */ +#define LPX_DB 113 /* double-bounded variable */ +#define LPX_FX 114 /* fixed variable */ + +/* optimization direction flag: */ +#define LPX_MIN 120 /* minimization */ +#define LPX_MAX 121 /* maximization */ + +/* status of primal basic solution: */ +#define LPX_P_UNDEF 132 /* primal solution is undefined */ +#define LPX_P_FEAS 133 /* solution is primal feasible */ +#define LPX_P_INFEAS 134 /* solution is primal infeasible */ +#define LPX_P_NOFEAS 135 /* no primal feasible solution exists */ + +/* status of dual basic solution: */ +#define LPX_D_UNDEF 136 /* dual solution is undefined */ +#define LPX_D_FEAS 137 /* solution is dual feasible */ +#define LPX_D_INFEAS 138 /* solution is dual infeasible */ +#define LPX_D_NOFEAS 139 /* no dual feasible solution exists */ + +/* status of auxiliary/structural variable: */ +#define LPX_BS 140 /* basic variable */ +#define LPX_NL 141 /* non-basic variable on lower bound */ +#define LPX_NU 142 /* non-basic variable on upper bound */ +#define LPX_NF 143 /* non-basic free variable */ +#define LPX_NS 144 /* non-basic fixed variable */ + +/* status of interior-point solution: */ +#define LPX_T_UNDEF 150 /* interior solution is undefined */ +#define LPX_T_OPT 151 /* interior solution is optimal */ + +/* kind of structural variable: */ +#define LPX_CV 160 /* continuous variable */ +#define LPX_IV 161 /* integer variable */ + +/* status of integer solution: */ +#define LPX_I_UNDEF 170 /* integer solution is undefined */ +#define LPX_I_OPT 171 /* integer solution is optimal */ +#define LPX_I_FEAS 172 /* integer solution is feasible */ +#define LPX_I_NOFEAS 173 /* no integer solution exists */ + +/* status codes reported by the routine lpx_get_status: */ +#define LPX_OPT 180 /* optimal */ +#define LPX_FEAS 181 /* feasible */ +#define LPX_INFEAS 182 /* infeasible */ +#define LPX_NOFEAS 183 /* no feasible */ +#define LPX_UNBND 184 /* unbounded */ +#define LPX_UNDEF 185 /* undefined */ + +/* exit codes returned by solver routines: */ +#define LPX_E_OK 200 /* success */ +#define LPX_E_EMPTY 201 /* empty problem */ +#define LPX_E_BADB 202 /* invalid initial basis */ +#define LPX_E_INFEAS 203 /* infeasible initial solution */ +#define LPX_E_FAULT 204 /* unable to start the search */ +#define LPX_E_OBJLL 205 /* objective lower limit reached */ +#define LPX_E_OBJUL 206 /* objective upper limit reached */ +#define LPX_E_ITLIM 207 /* iterations limit exhausted */ +#define LPX_E_TMLIM 208 /* time limit exhausted */ +#define LPX_E_NOFEAS 209 /* no feasible solution */ +#define LPX_E_INSTAB 210 /* numerical instability */ +#define LPX_E_SING 211 /* problems with basis matrix */ +#define LPX_E_NOCONV 212 /* no convergence (interior) */ +#define LPX_E_NOPFS 213 /* no primal feas. sol. (LP presolver) */ +#define LPX_E_NODFS 214 /* no dual feas. sol. (LP presolver) */ +#define LPX_E_MIPGAP 215 /* relative mip gap tolerance reached */ + +/* control parameter identifiers: */ +#define LPX_K_MSGLEV 300 /* lp->msg_lev */ +#define LPX_K_SCALE 301 /* lp->scale */ +#define LPX_K_DUAL 302 /* lp->dual */ +#define LPX_K_PRICE 303 /* lp->price */ +#define LPX_K_RELAX 304 /* lp->relax */ +#define LPX_K_TOLBND 305 /* lp->tol_bnd */ +#define LPX_K_TOLDJ 306 /* lp->tol_dj */ +#define LPX_K_TOLPIV 307 /* lp->tol_piv */ +#define LPX_K_ROUND 308 /* lp->round */ +#define LPX_K_OBJLL 309 /* lp->obj_ll */ +#define LPX_K_OBJUL 310 /* lp->obj_ul */ +#define LPX_K_ITLIM 311 /* lp->it_lim */ +#define LPX_K_ITCNT 312 /* lp->it_cnt */ +#define LPX_K_TMLIM 313 /* lp->tm_lim */ +#define LPX_K_OUTFRQ 314 /* lp->out_frq */ +#define LPX_K_OUTDLY 315 /* lp->out_dly */ +#define LPX_K_BRANCH 316 /* lp->branch */ +#define LPX_K_BTRACK 317 /* lp->btrack */ +#define LPX_K_TOLINT 318 /* lp->tol_int */ +#define LPX_K_TOLOBJ 319 /* lp->tol_obj */ +#define LPX_K_MPSINFO 320 /* lp->mps_info */ +#define LPX_K_MPSOBJ 321 /* lp->mps_obj */ +#define LPX_K_MPSORIG 322 /* lp->mps_orig */ +#define LPX_K_MPSWIDE 323 /* lp->mps_wide */ +#define LPX_K_MPSFREE 324 /* lp->mps_free */ +#define LPX_K_MPSSKIP 325 /* lp->mps_skip */ +#define LPX_K_LPTORIG 326 /* lp->lpt_orig */ +#define LPX_K_PRESOL 327 /* lp->presol */ +#define LPX_K_BINARIZE 328 /* lp->binarize */ +#define LPX_K_USECUTS 329 /* lp->use_cuts */ +#define LPX_K_BFTYPE 330 /* lp->bfcp->type */ +#define LPX_K_MIPGAP 331 /* lp->mip_gap */ + +#define LPX_C_COVER 0x01 /* mixed cover cuts */ +#define LPX_C_CLIQUE 0x02 /* clique cuts */ +#define LPX_C_GOMORY 0x04 /* Gomory's mixed integer cuts */ +#define LPX_C_MIR 0x08 /* mixed integer rounding cuts */ +#define LPX_C_ALL 0xFF /* all cuts */ + +typedef struct +{ /* this structure contains results reported by the routines which + checks Karush-Kuhn-Tucker conditions (for details see comments + to those routines) */ + /*--------------------------------------------------------------*/ + /* xR - A * xS = 0 (KKT.PE) */ + double pe_ae_max; + /* largest absolute error */ + int pe_ae_row; + /* number of row with largest absolute error */ + double pe_re_max; + /* largest relative error */ + int pe_re_row; + /* number of row with largest relative error */ + int pe_quality; + /* quality of primal solution: + 'H' - high + 'M' - medium + 'L' - low + '?' - primal solution is wrong */ + /*--------------------------------------------------------------*/ + /* l[k] <= x[k] <= u[k] (KKT.PB) */ + double pb_ae_max; + /* largest absolute error */ + int pb_ae_ind; + /* number of variable with largest absolute error */ + double pb_re_max; + /* largest relative error */ + int pb_re_ind; + /* number of variable with largest relative error */ + int pb_quality; + /* quality of primal feasibility: + 'H' - high + 'M' - medium + 'L' - low + '?' - primal solution is infeasible */ + /*--------------------------------------------------------------*/ + /* A' * (dR - cR) + (dS - cS) = 0 (KKT.DE) */ + double de_ae_max; + /* largest absolute error */ + int de_ae_col; + /* number of column with largest absolute error */ + double de_re_max; + /* largest relative error */ + int de_re_col; + /* number of column with largest relative error */ + int de_quality; + /* quality of dual solution: + 'H' - high + 'M' - medium + 'L' - low + '?' - dual solution is wrong */ + /*--------------------------------------------------------------*/ + /* d[k] >= 0 or d[k] <= 0 (KKT.DB) */ + double db_ae_max; + /* largest absolute error */ + int db_ae_ind; + /* number of variable with largest absolute error */ + double db_re_max; + /* largest relative error */ + int db_re_ind; + /* number of variable with largest relative error */ + int db_quality; + /* quality of dual feasibility: + 'H' - high + 'M' - medium + 'L' - low + '?' - dual solution is infeasible */ + /*--------------------------------------------------------------*/ + /* (x[k] - bound of x[k]) * d[k] = 0 (KKT.CS) */ + double cs_ae_max; + /* largest absolute error */ + int cs_ae_ind; + /* number of variable with largest absolute error */ + double cs_re_max; + /* largest relative error */ + int cs_re_ind; + /* number of variable with largest relative error */ + int cs_quality; + /* quality of complementary slackness: + 'H' - high + 'M' - medium + 'L' - low + '?' - primal and dual solutions are not complementary */ +} LPXKKT; + +#define lpx_create_prob _glp_lpx_create_prob +LPX *lpx_create_prob(void); +/* create problem object */ + +#define lpx_set_prob_name _glp_lpx_set_prob_name +void lpx_set_prob_name(LPX *lp, const char *name); +/* assign (change) problem name */ + +#define lpx_set_obj_name _glp_lpx_set_obj_name +void lpx_set_obj_name(LPX *lp, const char *name); +/* assign (change) objective function name */ + +#define lpx_set_obj_dir _glp_lpx_set_obj_dir +void lpx_set_obj_dir(LPX *lp, int dir); +/* set (change) optimization direction flag */ + +#define lpx_add_rows _glp_lpx_add_rows +int lpx_add_rows(LPX *lp, int nrs); +/* add new rows to problem object */ + +#define lpx_add_cols _glp_lpx_add_cols +int lpx_add_cols(LPX *lp, int ncs); +/* add new columns to problem object */ + +#define lpx_set_row_name _glp_lpx_set_row_name +void lpx_set_row_name(LPX *lp, int i, const char *name); +/* assign (change) row name */ + +#define lpx_set_col_name _glp_lpx_set_col_name +void lpx_set_col_name(LPX *lp, int j, const char *name); +/* assign (change) column name */ + +#define lpx_set_row_bnds _glp_lpx_set_row_bnds +void lpx_set_row_bnds(LPX *lp, int i, int type, double lb, double ub); +/* set (change) row bounds */ + +#define lpx_set_col_bnds _glp_lpx_set_col_bnds +void lpx_set_col_bnds(LPX *lp, int j, int type, double lb, double ub); +/* set (change) column bounds */ + +#define lpx_set_obj_coef _glp_lpx_set_obj_coef +void lpx_set_obj_coef(glp_prob *lp, int j, double coef); +/* set (change) obj. coefficient or constant term */ + +#define lpx_set_mat_row _glp_lpx_set_mat_row +void lpx_set_mat_row(LPX *lp, int i, int len, const int ind[], + const double val[]); +/* set (replace) row of the constraint matrix */ + +#define lpx_set_mat_col _glp_lpx_set_mat_col +void lpx_set_mat_col(LPX *lp, int j, int len, const int ind[], + const double val[]); +/* set (replace) column of the constraint matrix */ + +#define lpx_load_matrix _glp_lpx_load_matrix +void lpx_load_matrix(LPX *lp, int ne, const int ia[], const int ja[], + const double ar[]); +/* load (replace) the whole constraint matrix */ + +#define lpx_del_rows _glp_lpx_del_rows +void lpx_del_rows(LPX *lp, int nrs, const int num[]); +/* delete specified rows from problem object */ + +#define lpx_del_cols _glp_lpx_del_cols +void lpx_del_cols(LPX *lp, int ncs, const int num[]); +/* delete specified columns from problem object */ + +#define lpx_delete_prob _glp_lpx_delete_prob +void lpx_delete_prob(LPX *lp); +/* delete problem object */ + +#define lpx_get_prob_name _glp_lpx_get_prob_name +const char *lpx_get_prob_name(LPX *lp); +/* retrieve problem name */ + +#define lpx_get_obj_name _glp_lpx_get_obj_name +const char *lpx_get_obj_name(LPX *lp); +/* retrieve objective function name */ + +#define lpx_get_obj_dir _glp_lpx_get_obj_dir +int lpx_get_obj_dir(LPX *lp); +/* retrieve optimization direction flag */ + +#define lpx_get_num_rows _glp_lpx_get_num_rows +int lpx_get_num_rows(LPX *lp); +/* retrieve number of rows */ + +#define lpx_get_num_cols _glp_lpx_get_num_cols +int lpx_get_num_cols(LPX *lp); +/* retrieve number of columns */ + +#define lpx_get_row_name _glp_lpx_get_row_name +const char *lpx_get_row_name(LPX *lp, int i); +/* retrieve row name */ + +#define lpx_get_col_name _glp_lpx_get_col_name +const char *lpx_get_col_name(LPX *lp, int j); +/* retrieve column name */ + +#define lpx_get_row_type _glp_lpx_get_row_type +int lpx_get_row_type(LPX *lp, int i); +/* retrieve row type */ + +#define lpx_get_row_lb _glp_lpx_get_row_lb +double lpx_get_row_lb(LPX *lp, int i); +/* retrieve row lower bound */ + +#define lpx_get_row_ub _glp_lpx_get_row_ub +double lpx_get_row_ub(LPX *lp, int i); +/* retrieve row upper bound */ + +#define lpx_get_row_bnds _glp_lpx_get_row_bnds +void lpx_get_row_bnds(LPX *lp, int i, int *typx, double *lb, + double *ub); +/* retrieve row bounds */ + +#define lpx_get_col_type _glp_lpx_get_col_type +int lpx_get_col_type(LPX *lp, int j); +/* retrieve column type */ + +#define lpx_get_col_lb _glp_lpx_get_col_lb +double lpx_get_col_lb(LPX *lp, int j); +/* retrieve column lower bound */ + +#define lpx_get_col_ub _glp_lpx_get_col_ub +double lpx_get_col_ub(LPX *lp, int j); +/* retrieve column upper bound */ + +#define lpx_get_col_bnds _glp_lpx_get_col_bnds +void lpx_get_col_bnds(LPX *lp, int j, int *typx, double *lb, + double *ub); +/* retrieve column bounds */ + +#define lpx_get_obj_coef _glp_lpx_get_obj_coef +double lpx_get_obj_coef(LPX *lp, int j); +/* retrieve obj. coefficient or constant term */ + +#define lpx_get_num_nz _glp_lpx_get_num_nz +int lpx_get_num_nz(LPX *lp); +/* retrieve number of constraint coefficients */ + +#define lpx_get_mat_row _glp_lpx_get_mat_row +int lpx_get_mat_row(LPX *lp, int i, int ind[], double val[]); +/* retrieve row of the constraint matrix */ + +#define lpx_get_mat_col _glp_lpx_get_mat_col +int lpx_get_mat_col(LPX *lp, int j, int ind[], double val[]); +/* retrieve column of the constraint matrix */ + +#define lpx_create_index _glp_lpx_create_index +void lpx_create_index(LPX *lp); +/* create the name index */ + +#define lpx_find_row _glp_lpx_find_row +int lpx_find_row(LPX *lp, const char *name); +/* find row by its name */ + +#define lpx_find_col _glp_lpx_find_col +int lpx_find_col(LPX *lp, const char *name); +/* find column by its name */ + +#define lpx_delete_index _glp_lpx_delete_index +void lpx_delete_index(LPX *lp); +/* delete the name index */ + +#define lpx_scale_prob _glp_lpx_scale_prob +void lpx_scale_prob(LPX *lp); +/* scale problem data */ + +#define lpx_unscale_prob _glp_lpx_unscale_prob +void lpx_unscale_prob(LPX *lp); +/* unscale problem data */ + +#define lpx_set_row_stat _glp_lpx_set_row_stat +void lpx_set_row_stat(LPX *lp, int i, int stat); +/* set (change) row status */ + +#define lpx_set_col_stat _glp_lpx_set_col_stat +void lpx_set_col_stat(LPX *lp, int j, int stat); +/* set (change) column status */ + +#define lpx_std_basis _glp_lpx_std_basis +void lpx_std_basis(LPX *lp); +/* construct standard initial LP basis */ + +#define lpx_adv_basis _glp_lpx_adv_basis +void lpx_adv_basis(LPX *lp); +/* construct advanced initial LP basis */ + +#define lpx_cpx_basis _glp_lpx_cpx_basis +void lpx_cpx_basis(LPX *lp); +/* construct Bixby's initial LP basis */ + +#define lpx_simplex _glp_lpx_simplex +int lpx_simplex(LPX *lp); +/* easy-to-use driver to the simplex method */ + +#define lpx_exact _glp_lpx_exact +int lpx_exact(LPX *lp); +/* easy-to-use driver to the exact simplex method */ + +#define lpx_get_status _glp_lpx_get_status +int lpx_get_status(LPX *lp); +/* retrieve generic status of basic solution */ + +#define lpx_get_prim_stat _glp_lpx_get_prim_stat +int lpx_get_prim_stat(LPX *lp); +/* retrieve primal status of basic solution */ + +#define lpx_get_dual_stat _glp_lpx_get_dual_stat +int lpx_get_dual_stat(LPX *lp); +/* retrieve dual status of basic solution */ + +#define lpx_get_obj_val _glp_lpx_get_obj_val +double lpx_get_obj_val(LPX *lp); +/* retrieve objective value (basic solution) */ + +#define lpx_get_row_stat _glp_lpx_get_row_stat +int lpx_get_row_stat(LPX *lp, int i); +/* retrieve row status (basic solution) */ + +#define lpx_get_row_prim _glp_lpx_get_row_prim +double lpx_get_row_prim(LPX *lp, int i); +/* retrieve row primal value (basic solution) */ + +#define lpx_get_row_dual _glp_lpx_get_row_dual +double lpx_get_row_dual(LPX *lp, int i); +/* retrieve row dual value (basic solution) */ + +#define lpx_get_row_info _glp_lpx_get_row_info +void lpx_get_row_info(LPX *lp, int i, int *tagx, double *vx, + double *dx); +/* obtain row solution information */ + +#define lpx_get_col_stat _glp_lpx_get_col_stat +int lpx_get_col_stat(LPX *lp, int j); +/* retrieve column status (basic solution) */ + +#define lpx_get_col_prim _glp_lpx_get_col_prim +double lpx_get_col_prim(LPX *lp, int j); +/* retrieve column primal value (basic solution) */ + +#define lpx_get_col_dual _glp_lpx_get_col_dual +double lpx_get_col_dual(glp_prob *lp, int j); +/* retrieve column dual value (basic solution) */ + +#define lpx_get_col_info _glp_lpx_get_col_info +void lpx_get_col_info(LPX *lp, int j, int *tagx, double *vx, + double *dx); +/* obtain column solution information (obsolete) */ + +#define lpx_get_ray_info _glp_lpx_get_ray_info +int lpx_get_ray_info(LPX *lp); +/* determine what causes primal unboundness */ + +#define lpx_check_kkt _glp_lpx_check_kkt +void lpx_check_kkt(LPX *lp, int scaled, LPXKKT *kkt); +/* check Karush-Kuhn-Tucker conditions */ + +#define lpx_warm_up _glp_lpx_warm_up +int lpx_warm_up(LPX *lp); +/* "warm up" LP basis */ + +#define lpx_eval_tab_row _glp_lpx_eval_tab_row +int lpx_eval_tab_row(LPX *lp, int k, int ind[], double val[]); +/* compute row of the simplex table */ + +#define lpx_eval_tab_col _glp_lpx_eval_tab_col +int lpx_eval_tab_col(LPX *lp, int k, int ind[], double val[]); +/* compute column of the simplex table */ + +#define lpx_transform_row _glp_lpx_transform_row +int lpx_transform_row(LPX *lp, int len, int ind[], double val[]); +/* transform explicitly specified row */ + +#define lpx_transform_col _glp_lpx_transform_col +int lpx_transform_col(LPX *lp, int len, int ind[], double val[]); +/* transform explicitly specified column */ + +#define lpx_prim_ratio_test _glp_lpx_prim_ratio_test +int lpx_prim_ratio_test(LPX *lp, int len, const int ind[], + const double val[], int how, double tol); +/* perform primal ratio test */ + +#define lpx_dual_ratio_test _glp_lpx_dual_ratio_test +int lpx_dual_ratio_test(LPX *lp, int len, const int ind[], + const double val[], int how, double tol); +/* perform dual ratio test */ + +#define lpx_interior _glp_lpx_interior +int lpx_interior(LPX *lp); +/* easy-to-use driver to the interior point method */ + +#define lpx_ipt_status _glp_lpx_ipt_status +int lpx_ipt_status(LPX *lp); +/* retrieve status of interior-point solution */ + +#define lpx_ipt_obj_val _glp_lpx_ipt_obj_val +double lpx_ipt_obj_val(LPX *lp); +/* retrieve objective value (interior point) */ + +#define lpx_ipt_row_prim _glp_lpx_ipt_row_prim +double lpx_ipt_row_prim(LPX *lp, int i); +/* retrieve row primal value (interior point) */ + +#define lpx_ipt_row_dual _glp_lpx_ipt_row_dual +double lpx_ipt_row_dual(LPX *lp, int i); +/* retrieve row dual value (interior point) */ + +#define lpx_ipt_col_prim _glp_lpx_ipt_col_prim +double lpx_ipt_col_prim(LPX *lp, int j); +/* retrieve column primal value (interior point) */ + +#define lpx_ipt_col_dual _glp_lpx_ipt_col_dual +double lpx_ipt_col_dual(LPX *lp, int j); +/* retrieve column dual value (interior point) */ + +#define lpx_set_class _glp_lpx_set_class +void lpx_set_class(LPX *lp, int klass); +/* set problem class */ + +#define lpx_get_class _glp_lpx_get_class +int lpx_get_class(LPX *lp); +/* determine problem klass */ + +#define lpx_set_col_kind _glp_lpx_set_col_kind +void lpx_set_col_kind(LPX *lp, int j, int kind); +/* set (change) column kind */ + +#define lpx_get_col_kind _glp_lpx_get_col_kind +int lpx_get_col_kind(LPX *lp, int j); +/* retrieve column kind */ + +#define lpx_get_num_int _glp_lpx_get_num_int +int lpx_get_num_int(LPX *lp); +/* retrieve number of integer columns */ + +#define lpx_get_num_bin _glp_lpx_get_num_bin +int lpx_get_num_bin(LPX *lp); +/* retrieve number of binary columns */ + +#define lpx_integer _glp_lpx_integer +int lpx_integer(LPX *lp); +/* easy-to-use driver to the branch-and-bound method */ + +#define lpx_intopt _glp_lpx_intopt +int lpx_intopt(LPX *lp); +/* easy-to-use driver to the branch-and-bound method */ + +#define lpx_mip_status _glp_lpx_mip_status +int lpx_mip_status(LPX *lp); +/* retrieve status of MIP solution */ + +#define lpx_mip_obj_val _glp_lpx_mip_obj_val +double lpx_mip_obj_val(LPX *lp); +/* retrieve objective value (MIP solution) */ + +#define lpx_mip_row_val _glp_lpx_mip_row_val +double lpx_mip_row_val(LPX *lp, int i); +/* retrieve row value (MIP solution) */ + +#define lpx_mip_col_val _glp_lpx_mip_col_val +double lpx_mip_col_val(LPX *lp, int j); +/* retrieve column value (MIP solution) */ + +#define lpx_check_int _glp_lpx_check_int +void lpx_check_int(LPX *lp, LPXKKT *kkt); +/* check integer feasibility conditions */ + +#define lpx_reset_parms _glp_lpx_reset_parms +void lpx_reset_parms(LPX *lp); +/* reset control parameters to default values */ + +#define lpx_set_int_parm _glp_lpx_set_int_parm +void lpx_set_int_parm(LPX *lp, int parm, int val); +/* set (change) integer control parameter */ + +#define lpx_get_int_parm _glp_lpx_get_int_parm +int lpx_get_int_parm(LPX *lp, int parm); +/* query integer control parameter */ + +#define lpx_set_real_parm _glp_lpx_set_real_parm +void lpx_set_real_parm(LPX *lp, int parm, double val); +/* set (change) real control parameter */ + +#define lpx_get_real_parm _glp_lpx_get_real_parm +double lpx_get_real_parm(LPX *lp, int parm); +/* query real control parameter */ + +#define lpx_read_mps _glp_lpx_read_mps +LPX *lpx_read_mps(const char *fname); +/* read problem data in fixed MPS format */ + +#define lpx_write_mps _glp_lpx_write_mps +int lpx_write_mps(LPX *lp, const char *fname); +/* write problem data in fixed MPS format */ + +#define lpx_read_bas _glp_lpx_read_bas +int lpx_read_bas(LPX *lp, const char *fname); +/* read LP basis in fixed MPS format */ + +#define lpx_write_bas _glp_lpx_write_bas +int lpx_write_bas(LPX *lp, const char *fname); +/* write LP basis in fixed MPS format */ + +#define lpx_read_freemps _glp_lpx_read_freemps +LPX *lpx_read_freemps(const char *fname); +/* read problem data in free MPS format */ + +#define lpx_write_freemps _glp_lpx_write_freemps +int lpx_write_freemps(LPX *lp, const char *fname); +/* write problem data in free MPS format */ + +#define lpx_read_cpxlp _glp_lpx_read_cpxlp +LPX *lpx_read_cpxlp(const char *fname); +/* read problem data in CPLEX LP format */ + +#define lpx_write_cpxlp _glp_lpx_write_cpxlp +int lpx_write_cpxlp(LPX *lp, const char *fname); +/* write problem data in CPLEX LP format */ + +#define lpx_read_model _glp_lpx_read_model +LPX *lpx_read_model(const char *model, const char *data, + const char *output); +/* read LP/MIP model written in GNU MathProg language */ + +#define lpx_print_prob _glp_lpx_print_prob +int lpx_print_prob(LPX *lp, const char *fname); +/* write problem data in plain text format */ + +#define lpx_print_sol _glp_lpx_print_sol +int lpx_print_sol(LPX *lp, const char *fname); +/* write LP problem solution in printable format */ + +#define lpx_print_sens_bnds _glp_lpx_print_sens_bnds +int lpx_print_sens_bnds(LPX *lp, const char *fname); +/* write bounds sensitivity information */ + +#define lpx_print_ips _glp_lpx_print_ips +int lpx_print_ips(LPX *lp, const char *fname); +/* write interior point solution in printable format */ + +#define lpx_print_mip _glp_lpx_print_mip +int lpx_print_mip(LPX *lp, const char *fname); +/* write MIP problem solution in printable format */ + +#define lpx_is_b_avail _glp_lpx_is_b_avail +int lpx_is_b_avail(LPX *lp); +/* check if LP basis is available */ + +#define lpx_write_pb _glp_lpx_write_pb +int lpx_write_pb(LPX *lp, const char *fname, int normalized, + int binarize); +/* write problem data in (normalized) OPB format */ + +#define lpx_main _glp_lpx_main +int lpx_main(int argc, const char *argv[]); +/* stand-alone LP/MIP solver */ + +#ifdef __cplusplus +} +#endif + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Makefile.am Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,121 @@ +## Process this file with automake to produce Makefile.in ## + +INCLUDES = -I$(srcdir)/../include + +lib_LTLIBRARIES = libglpk.la + +libglpk_la_LDFLAGS = -version-info 30:0:30 \ +-export-symbols-regex '^(glp_|_glp_lpx_).*' + +libglpk_la_SOURCES = \ +glpapi01.c \ +glpapi02.c \ +glpapi03.c \ +glpapi04.c \ +glpapi05.c \ +glpapi06.c \ +glpapi07.c \ +glpapi08.c \ +glpapi09.c \ +glpapi10.c \ +glpapi11.c \ +glpapi12.c \ +glpapi13.c \ +glpapi14.c \ +glpapi15.c \ +glpapi16.c \ +glpapi17.c \ +glpapi18.c \ +glpapi19.c \ +glpavl.c \ +glpbfd.c \ +glpbfx.c \ +glpcpx.c \ +glpdmp.c \ +glpdmx.c \ +glpenv01.c \ +glpenv02.c \ +glpenv03.c \ +glpenv04.c \ +glpenv05.c \ +glpenv06.c \ +glpenv07.c \ +glpenv08.c \ +glpfhv.c \ +glpgmp.c \ +glphbm.c \ +glpini01.c \ +glpini02.c \ +glpios01.c \ +glpios02.c \ +glpios03.c \ +glpios04.c \ +glpios05.c \ +glpios06.c \ +glpios07.c \ +glpios08.c \ +glpios09.c \ +glpios10.c \ +glpios11.c \ +glpios12.c \ +glpipm.c \ +glplib01.c \ +glplib02.c \ +glplib03.c \ +glplpf.c \ +glplpx01.c \ +glplpx02.c \ +glplpx03.c \ +glpluf.c \ +glplux.c \ +glpmat.c \ +glpmpl01.c \ +glpmpl02.c \ +glpmpl03.c \ +glpmpl04.c \ +glpmpl05.c \ +glpmpl06.c \ +glpmps.c \ +glpnet01.c \ +glpnet02.c \ +glpnet03.c \ +glpnet04.c \ +glpnet05.c \ +glpnet06.c \ +glpnet07.c \ +glpnet08.c \ +glpnet09.c \ +glpnpp01.c \ +glpnpp02.c \ +glpnpp03.c \ +glpnpp04.c \ +glpnpp05.c \ +glpqmd.c \ +glprgr.c \ +glprng01.c \ +glprng02.c \ +glpscf.c \ +glpscl.c \ +glpsdf.c \ +glpspm.c \ +glpspx01.c \ +glpspx02.c \ +glpsql.c \ +glpssx01.c \ +glpssx02.c \ +glptsp.c \ +amd/amd_1.c \ +amd/amd_2.c \ +amd/amd_aat.c \ +amd/amd_control.c \ +amd/amd_defaults.c \ +amd/amd_dump.c \ +amd/amd_info.c \ +amd/amd_order.c \ +amd/amd_post_tree.c \ +amd/amd_postorder.c \ +amd/amd_preprocess.c \ +amd/amd_valid.c \ +colamd/colamd.c + +## eof ## diff -r d59bea55db9b -r c445c931472f src/amd/COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/COPYING Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff -r d59bea55db9b -r c445c931472f src/amd/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/README Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,58 @@ +NOTE: Files in this subdirectory are NOT part of the GLPK package, but + are used with GLPK. + + The original code was modified according to GLPK requirements by + Andrew Makhorin . +************************************************************************ +AMD Version 2.2, Copyright (C) 2007 by Timothy A. Davis, +Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. + +Description: + + AMD is a set of routines for pre-ordering sparse matrices prior to + Cholesky or LU factorization, using the approximate minimum degree + ordering algorithm. Written in ANSI/ISO C with a MATLAB interface, + and in Fortran 77. + +Authors: + + Timothy A. Davis (davis at cise.ufl.edu), University of Florida. + Patrick R. Amestoy, ENSEEIHT, Toulouse, France. + Iain S. Duff, Rutherford Appleton Laboratory, UK. + +AMD License: + + Your use or distribution of AMD or any modified version of AMD + implies that you agree to this License. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA. + + Permission is hereby granted to use or copy this program under the + terms of the GNU LGPL, provided that the Copyright, this License, + and the Availability of the original version is retained on all + copies. User documentation of any code that uses this code or any + modified version of this code must cite the Copyright, this License, + the Availability note, and "Used by permission." Permission to + modify the code and to distribute modified code is granted, provided + the Copyright, this License, and the Availability note are retained, + and a notice that the code was modified is included. + + AMD is available under alternate licences; contact T. Davis for + details. + +Availability: + + http://www.cise.ufl.edu/research/sparse/amd diff -r d59bea55db9b -r c445c931472f src/amd/amd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,67 @@ +/* amd.h */ + +/* Written by Andrew Makhorin . */ + +#ifndef GLPAMD_H +#define GLPAMD_H + +#define AMD_DATE "May 31, 2007" +#define AMD_VERSION_CODE(main, sub) ((main) * 1000 + (sub)) +#define AMD_MAIN_VERSION 2 +#define AMD_SUB_VERSION 2 +#define AMD_SUBSUB_VERSION 0 +#define AMD_VERSION AMD_VERSION_CODE(AMD_MAIN_VERSION, AMD_SUB_VERSION) + +#define AMD_CONTROL 5 +#define AMD_INFO 20 + +#define AMD_DENSE 0 +#define AMD_AGGRESSIVE 1 + +#define AMD_DEFAULT_DENSE 10.0 +#define AMD_DEFAULT_AGGRESSIVE 1 + +#define AMD_STATUS 0 +#define AMD_N 1 +#define AMD_NZ 2 +#define AMD_SYMMETRY 3 +#define AMD_NZDIAG 4 +#define AMD_NZ_A_PLUS_AT 5 +#define AMD_NDENSE 6 +#define AMD_MEMORY 7 +#define AMD_NCMPA 8 +#define AMD_LNZ 9 +#define AMD_NDIV 10 +#define AMD_NMULTSUBS_LDL 11 +#define AMD_NMULTSUBS_LU 12 +#define AMD_DMAX 13 + +#define AMD_OK 0 +#define AMD_OUT_OF_MEMORY (-1) +#define AMD_INVALID (-2) +#define AMD_OK_BUT_JUMBLED 1 + +#define amd_order _glp_amd_order +int amd_order(int n, const int Ap[], const int Ai[], int P[], + double Control[], double Info[]); + +#define amd_2 _glp_amd_2 +void amd_2(int n, int Pe[], int Iw[], int Len[], int iwlen, int pfree, + int Nv[], int Next[], int Last[], int Head[], int Elen[], + int Degree[], int W[], double Control[], double Info[]); + +#define amd_valid _glp_amd_valid +int amd_valid(int n_row, int n_col, const int Ap[], const int Ai[]); + +#define amd_defaults _glp_amd_defaults +void amd_defaults(double Control[]); + +#define amd_control _glp_amd_control +void amd_control(double Control[]); + +#define amd_info _glp_amd_info +void amd_info(double Info[]); + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/amd/amd_1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_1.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,181 @@ +/* ========================================================================= */ +/* === AMD_1 =============================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* AMD_1: Construct A+A' for a sparse matrix A and perform the AMD ordering. + * + * The n-by-n sparse matrix A can be unsymmetric. It is stored in MATLAB-style + * compressed-column form, with sorted row indices in each column, and no + * duplicate entries. Diagonal entries may be present, but they are ignored. + * Row indices of column j of A are stored in Ai [Ap [j] ... Ap [j+1]-1]. + * Ap [0] must be zero, and nz = Ap [n] is the number of entries in A. The + * size of the matrix, n, must be greater than or equal to zero. + * + * This routine must be preceded by a call to AMD_aat, which computes the + * number of entries in each row/column in A+A', excluding the diagonal. + * Len [j], on input, is the number of entries in row/column j of A+A'. This + * routine constructs the matrix A+A' and then calls AMD_2. No error checking + * is performed (this was done in AMD_valid). + */ + +#include "amd_internal.h" + +GLOBAL void AMD_1 +( + Int n, /* n > 0 */ + const Int Ap [ ], /* input of size n+1, not modified */ + const Int Ai [ ], /* input of size nz = Ap [n], not modified */ + Int P [ ], /* size n output permutation */ + Int Pinv [ ], /* size n output inverse permutation */ + Int Len [ ], /* size n input, undefined on output */ + Int slen, /* slen >= sum (Len [0..n-1]) + 7n, + * ideally slen = 1.2 * sum (Len) + 8n */ + Int S [ ], /* size slen workspace */ + double Control [ ], /* input array of size AMD_CONTROL */ + double Info [ ] /* output array of size AMD_INFO */ +) +{ + Int i, j, k, p, pfree, iwlen, pj, p1, p2, pj2, *Iw, *Pe, *Nv, *Head, + *Elen, *Degree, *s, *W, *Sp, *Tp ; + + /* --------------------------------------------------------------------- */ + /* construct the matrix for AMD_2 */ + /* --------------------------------------------------------------------- */ + + ASSERT (n > 0) ; + + iwlen = slen - 6*n ; + s = S ; + Pe = s ; s += n ; + Nv = s ; s += n ; + Head = s ; s += n ; + Elen = s ; s += n ; + Degree = s ; s += n ; + W = s ; s += n ; + Iw = s ; s += iwlen ; + + ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ; + + /* construct the pointers for A+A' */ + Sp = Nv ; /* use Nv and W as workspace for Sp and Tp [ */ + Tp = W ; + pfree = 0 ; + for (j = 0 ; j < n ; j++) + { + Pe [j] = pfree ; + Sp [j] = pfree ; + pfree += Len [j] ; + } + + /* Note that this restriction on iwlen is slightly more restrictive than + * what is strictly required in AMD_2. AMD_2 can operate with no elbow + * room at all, but it will be very slow. For better performance, at + * least size-n elbow room is enforced. */ + ASSERT (iwlen >= pfree + n) ; + +#ifndef NDEBUG + for (p = 0 ; p < iwlen ; p++) Iw [p] = EMPTY ; +#endif + + for (k = 0 ; k < n ; k++) + { + AMD_DEBUG1 (("Construct row/column k= "ID" of A+A'\n", k)) ; + p1 = Ap [k] ; + p2 = Ap [k+1] ; + + /* construct A+A' */ + for (p = p1 ; p < p2 ; ) + { + /* scan the upper triangular part of A */ + j = Ai [p] ; + ASSERT (j >= 0 && j < n) ; + if (j < k) + { + /* entry A (j,k) in the strictly upper triangular part */ + ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; + ASSERT (Sp [k] < (k == n-1 ? pfree : Pe [k+1])) ; + Iw [Sp [j]++] = k ; + Iw [Sp [k]++] = j ; + p++ ; + } + else if (j == k) + { + /* skip the diagonal */ + p++ ; + break ; + } + else /* j > k */ + { + /* first entry below the diagonal */ + break ; + } + /* scan lower triangular part of A, in column j until reaching + * row k. Start where last scan left off. */ + ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; + pj2 = Ap [j+1] ; + for (pj = Tp [j] ; pj < pj2 ; ) + { + i = Ai [pj] ; + ASSERT (i >= 0 && i < n) ; + if (i < k) + { + /* A (i,j) is only in the lower part, not in upper */ + ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; + ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; + Iw [Sp [i]++] = j ; + Iw [Sp [j]++] = i ; + pj++ ; + } + else if (i == k) + { + /* entry A (k,j) in lower part and A (j,k) in upper */ + pj++ ; + break ; + } + else /* i > k */ + { + /* consider this entry later, when k advances to i */ + break ; + } + } + Tp [j] = pj ; + } + Tp [k] = p ; + } + + /* clean up, for remaining mismatched entries */ + for (j = 0 ; j < n ; j++) + { + for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) + { + i = Ai [pj] ; + ASSERT (i >= 0 && i < n) ; + /* A (i,j) is only in the lower part, not in upper */ + ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; + ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; + Iw [Sp [i]++] = j ; + Iw [Sp [j]++] = i ; + } + } + +#ifndef NDEBUG + for (j = 0 ; j < n-1 ; j++) ASSERT (Sp [j] == Pe [j+1]) ; + ASSERT (Sp [n-1] == pfree) ; +#endif + + /* Tp and Sp no longer needed ] */ + + /* --------------------------------------------------------------------- */ + /* order the matrix */ + /* --------------------------------------------------------------------- */ + + AMD_2 (n, Pe, Iw, Len, iwlen, pfree, + Nv, Pinv, P, Head, Elen, Degree, W, Control, Info) ; +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_2.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1842 @@ +/* ========================================================================= */ +/* === AMD_2 =============================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* AMD_2: performs the AMD ordering on a symmetric sparse matrix A, followed + * by a postordering (via depth-first search) of the assembly tree using the + * AMD_postorder routine. + */ + +#include "amd_internal.h" + +/* ========================================================================= */ +/* === clear_flag ========================================================== */ +/* ========================================================================= */ + +static Int clear_flag (Int wflg, Int wbig, Int W [ ], Int n) +{ + Int x ; + if (wflg < 2 || wflg >= wbig) + { + for (x = 0 ; x < n ; x++) + { + if (W [x] != 0) W [x] = 1 ; + } + wflg = 2 ; + } + /* at this point, W [0..n-1] < wflg holds */ + return (wflg) ; +} + + +/* ========================================================================= */ +/* === AMD_2 =============================================================== */ +/* ========================================================================= */ + +GLOBAL void AMD_2 +( + Int n, /* A is n-by-n, where n > 0 */ + Int Pe [ ], /* Pe [0..n-1]: index in Iw of row i on input */ + Int Iw [ ], /* workspace of size iwlen. Iw [0..pfree-1] + * holds the matrix on input */ + Int Len [ ], /* Len [0..n-1]: length for row/column i on input */ + Int iwlen, /* length of Iw. iwlen >= pfree + n */ + Int pfree, /* Iw [pfree ... iwlen-1] is empty on input */ + + /* 7 size-n workspaces, not defined on input: */ + Int Nv [ ], /* the size of each supernode on output */ + Int Next [ ], /* the output inverse permutation */ + Int Last [ ], /* the output permutation */ + Int Head [ ], + Int Elen [ ], /* the size columns of L for each supernode */ + Int Degree [ ], + Int W [ ], + + /* control parameters and output statistics */ + double Control [ ], /* array of size AMD_CONTROL */ + double Info [ ] /* array of size AMD_INFO */ +) +{ + +/* + * Given a representation of the nonzero pattern of a symmetric matrix, A, + * (excluding the diagonal) perform an approximate minimum (UMFPACK/MA38-style) + * degree ordering to compute a pivot order such that the introduction of + * nonzeros (fill-in) in the Cholesky factors A = LL' is kept low. At each + * step, the pivot selected is the one with the minimum UMFAPACK/MA38-style + * upper-bound on the external degree. This routine can optionally perform + * aggresive absorption (as done by MC47B in the Harwell Subroutine + * Library). + * + * The approximate degree algorithm implemented here is the symmetric analog of + * the degree update algorithm in MA38 and UMFPACK (the Unsymmetric-pattern + * MultiFrontal PACKage, both by Davis and Duff). The routine is based on the + * MA27 minimum degree ordering algorithm by Iain Duff and John Reid. + * + * This routine is a translation of the original AMDBAR and MC47B routines, + * in Fortran, with the following modifications: + * + * (1) dense rows/columns are removed prior to ordering the matrix, and placed + * last in the output order. The presence of a dense row/column can + * increase the ordering time by up to O(n^2), unless they are removed + * prior to ordering. + * + * (2) the minimum degree ordering is followed by a postordering (depth-first + * search) of the assembly tree. Note that mass elimination (discussed + * below) combined with the approximate degree update can lead to the mass + * elimination of nodes with lower exact degree than the current pivot + * element. No additional fill-in is caused in the representation of the + * Schur complement. The mass-eliminated nodes merge with the current + * pivot element. They are ordered prior to the current pivot element. + * Because they can have lower exact degree than the current element, the + * merger of two or more of these nodes in the current pivot element can + * lead to a single element that is not a "fundamental supernode". The + * diagonal block can have zeros in it. Thus, the assembly tree used here + * is not guaranteed to be the precise supernodal elemination tree (with + * "funadmental" supernodes), and the postordering performed by this + * routine is not guaranteed to be a precise postordering of the + * elimination tree. + * + * (3) input parameters are added, to control aggressive absorption and the + * detection of "dense" rows/columns of A. + * + * (4) additional statistical information is returned, such as the number of + * nonzeros in L, and the flop counts for subsequent LDL' and LU + * factorizations. These are slight upper bounds, because of the mass + * elimination issue discussed above. + * + * (5) additional routines are added to interface this routine to MATLAB + * to provide a simple C-callable user-interface, to check inputs for + * errors, compute the symmetry of the pattern of A and the number of + * nonzeros in each row/column of A+A', to compute the pattern of A+A', + * to perform the assembly tree postordering, and to provide debugging + * ouput. Many of these functions are also provided by the Fortran + * Harwell Subroutine Library routine MC47A. + * + * (6) both int and UF_long versions are provided. In the descriptions below + * and integer is and int or UF_long depending on which version is + * being used. + + ********************************************************************** + ***** CAUTION: ARGUMENTS ARE NOT CHECKED FOR ERRORS ON INPUT. ****** + ********************************************************************** + ** If you want error checking, a more versatile input format, and a ** + ** simpler user interface, use amd_order or amd_l_order instead. ** + ** This routine is not meant to be user-callable. ** + ********************************************************************** + + * ---------------------------------------------------------------------------- + * References: + * ---------------------------------------------------------------------------- + * + * [1] Timothy A. Davis and Iain Duff, "An unsymmetric-pattern multifrontal + * method for sparse LU factorization", SIAM J. Matrix Analysis and + * Applications, vol. 18, no. 1, pp. 140-158. Discusses UMFPACK / MA38, + * which first introduced the approximate minimum degree used by this + * routine. + * + * [2] Patrick Amestoy, Timothy A. Davis, and Iain S. Duff, "An approximate + * minimum degree ordering algorithm," SIAM J. Matrix Analysis and + * Applications, vol. 17, no. 4, pp. 886-905, 1996. Discusses AMDBAR and + * MC47B, which are the Fortran versions of this routine. + * + * [3] Alan George and Joseph Liu, "The evolution of the minimum degree + * ordering algorithm," SIAM Review, vol. 31, no. 1, pp. 1-19, 1989. + * We list below the features mentioned in that paper that this code + * includes: + * + * mass elimination: + * Yes. MA27 relied on supervariable detection for mass elimination. + * + * indistinguishable nodes: + * Yes (we call these "supervariables"). This was also in the MA27 + * code - although we modified the method of detecting them (the + * previous hash was the true degree, which we no longer keep track + * of). A supervariable is a set of rows with identical nonzero + * pattern. All variables in a supervariable are eliminated together. + * Each supervariable has as its numerical name that of one of its + * variables (its principal variable). + * + * quotient graph representation: + * Yes. We use the term "element" for the cliques formed during + * elimination. This was also in the MA27 code. The algorithm can + * operate in place, but it will work more efficiently if given some + * "elbow room." + * + * element absorption: + * Yes. This was also in the MA27 code. + * + * external degree: + * Yes. The MA27 code was based on the true degree. + * + * incomplete degree update and multiple elimination: + * No. This was not in MA27, either. Our method of degree update + * within MC47B is element-based, not variable-based. It is thus + * not well-suited for use with incomplete degree update or multiple + * elimination. + * + * Authors, and Copyright (C) 2004 by: + * Timothy A. Davis, Patrick Amestoy, Iain S. Duff, John K. Reid. + * + * Acknowledgements: This work (and the UMFPACK package) was supported by the + * National Science Foundation (ASC-9111263, DMS-9223088, and CCR-0203270). + * The UMFPACK/MA38 approximate degree update algorithm, the unsymmetric analog + * which forms the basis of AMD, was developed while Tim Davis was supported by + * CERFACS (Toulouse, France) in a post-doctoral position. This C version, and + * the etree postorder, were written while Tim Davis was on sabbatical at + * Stanford University and Lawrence Berkeley National Laboratory. + + * ---------------------------------------------------------------------------- + * INPUT ARGUMENTS (unaltered): + * ---------------------------------------------------------------------------- + + * n: The matrix order. Restriction: n >= 1. + * + * iwlen: The size of the Iw array. On input, the matrix is stored in + * Iw [0..pfree-1]. However, Iw [0..iwlen-1] should be slightly larger + * than what is required to hold the matrix, at least iwlen >= pfree + n. + * Otherwise, excessive compressions will take place. The recommended + * value of iwlen is 1.2 * pfree + n, which is the value used in the + * user-callable interface to this routine (amd_order.c). The algorithm + * will not run at all if iwlen < pfree. Restriction: iwlen >= pfree + n. + * Note that this is slightly more restrictive than the actual minimum + * (iwlen >= pfree), but AMD_2 will be very slow with no elbow room. + * Thus, this routine enforces a bare minimum elbow room of size n. + * + * pfree: On input the tail end of the array, Iw [pfree..iwlen-1], is empty, + * and the matrix is stored in Iw [0..pfree-1]. During execution, + * additional data is placed in Iw, and pfree is modified so that + * Iw [pfree..iwlen-1] is always the unused part of Iw. + * + * Control: A double array of size AMD_CONTROL containing input parameters + * that affect how the ordering is computed. If NULL, then default + * settings are used. + * + * Control [AMD_DENSE] is used to determine whether or not a given input + * row is "dense". A row is "dense" if the number of entries in the row + * exceeds Control [AMD_DENSE] times sqrt (n), except that rows with 16 or + * fewer entries are never considered "dense". To turn off the detection + * of dense rows, set Control [AMD_DENSE] to a negative number, or to a + * number larger than sqrt (n). The default value of Control [AMD_DENSE] + * is AMD_DEFAULT_DENSE, which is defined in amd.h as 10. + * + * Control [AMD_AGGRESSIVE] is used to determine whether or not aggressive + * absorption is to be performed. If nonzero, then aggressive absorption + * is performed (this is the default). + + * ---------------------------------------------------------------------------- + * INPUT/OUPUT ARGUMENTS: + * ---------------------------------------------------------------------------- + * + * Pe: An integer array of size n. On input, Pe [i] is the index in Iw of + * the start of row i. Pe [i] is ignored if row i has no off-diagonal + * entries. Thus Pe [i] must be in the range 0 to pfree-1 for non-empty + * rows. + * + * During execution, it is used for both supervariables and elements: + * + * Principal supervariable i: index into Iw of the description of + * supervariable i. A supervariable represents one or more rows of + * the matrix with identical nonzero pattern. In this case, + * Pe [i] >= 0. + * + * Non-principal supervariable i: if i has been absorbed into another + * supervariable j, then Pe [i] = FLIP (j), where FLIP (j) is defined + * as (-(j)-2). Row j has the same pattern as row i. Note that j + * might later be absorbed into another supervariable j2, in which + * case Pe [i] is still FLIP (j), and Pe [j] = FLIP (j2) which is + * < EMPTY, where EMPTY is defined as (-1) in amd_internal.h. + * + * Unabsorbed element e: the index into Iw of the description of element + * e, if e has not yet been absorbed by a subsequent element. Element + * e is created when the supervariable of the same name is selected as + * the pivot. In this case, Pe [i] >= 0. + * + * Absorbed element e: if element e is absorbed into element e2, then + * Pe [e] = FLIP (e2). This occurs when the pattern of e (which we + * refer to as Le) is found to be a subset of the pattern of e2 (that + * is, Le2). In this case, Pe [i] < EMPTY. If element e is "null" + * (it has no nonzeros outside its pivot block), then Pe [e] = EMPTY, + * and e is the root of an assembly subtree (or the whole tree if + * there is just one such root). + * + * Dense variable i: if i is "dense", then Pe [i] = EMPTY. + * + * On output, Pe holds the assembly tree/forest, which implicitly + * represents a pivot order with identical fill-in as the actual order + * (via a depth-first search of the tree), as follows. If Nv [i] > 0, + * then i represents a node in the assembly tree, and the parent of i is + * Pe [i], or EMPTY if i is a root. If Nv [i] = 0, then (i, Pe [i]) + * represents an edge in a subtree, the root of which is a node in the + * assembly tree. Note that i refers to a row/column in the original + * matrix, not the permuted matrix. + * + * Info: A double array of size AMD_INFO. If present, (that is, not NULL), + * then statistics about the ordering are returned in the Info array. + * See amd.h for a description. + + * ---------------------------------------------------------------------------- + * INPUT/MODIFIED (undefined on output): + * ---------------------------------------------------------------------------- + * + * Len: An integer array of size n. On input, Len [i] holds the number of + * entries in row i of the matrix, excluding the diagonal. The contents + * of Len are undefined on output. + * + * Iw: An integer array of size iwlen. On input, Iw [0..pfree-1] holds the + * description of each row i in the matrix. The matrix must be symmetric, + * and both upper and lower triangular parts must be present. The + * diagonal must not be present. Row i is held as follows: + * + * Len [i]: the length of the row i data structure in the Iw array. + * Iw [Pe [i] ... Pe [i] + Len [i] - 1]: + * the list of column indices for nonzeros in row i (simple + * supervariables), excluding the diagonal. All supervariables + * start with one row/column each (supervariable i is just row i). + * If Len [i] is zero on input, then Pe [i] is ignored on input. + * + * Note that the rows need not be in any particular order, and there + * may be empty space between the rows. + * + * During execution, the supervariable i experiences fill-in. This is + * represented by placing in i a list of the elements that cause fill-in + * in supervariable i: + * + * Len [i]: the length of supervariable i in the Iw array. + * Iw [Pe [i] ... Pe [i] + Elen [i] - 1]: + * the list of elements that contain i. This list is kept short + * by removing absorbed elements. + * Iw [Pe [i] + Elen [i] ... Pe [i] + Len [i] - 1]: + * the list of supervariables in i. This list is kept short by + * removing nonprincipal variables, and any entry j that is also + * contained in at least one of the elements (j in Le) in the list + * for i (e in row i). + * + * When supervariable i is selected as pivot, we create an element e of + * the same name (e=i): + * + * Len [e]: the length of element e in the Iw array. + * Iw [Pe [e] ... Pe [e] + Len [e] - 1]: + * the list of supervariables in element e. + * + * An element represents the fill-in that occurs when supervariable i is + * selected as pivot (which represents the selection of row i and all + * non-principal variables whose principal variable is i). We use the + * term Le to denote the set of all supervariables in element e. Absorbed + * supervariables and elements are pruned from these lists when + * computationally convenient. + * + * CAUTION: THE INPUT MATRIX IS OVERWRITTEN DURING COMPUTATION. + * The contents of Iw are undefined on output. + + * ---------------------------------------------------------------------------- + * OUTPUT (need not be set on input): + * ---------------------------------------------------------------------------- + * + * Nv: An integer array of size n. During execution, ABS (Nv [i]) is equal to + * the number of rows that are represented by the principal supervariable + * i. If i is a nonprincipal or dense variable, then Nv [i] = 0. + * Initially, Nv [i] = 1 for all i. Nv [i] < 0 signifies that i is a + * principal variable in the pattern Lme of the current pivot element me. + * After element me is constructed, Nv [i] is set back to a positive + * value. + * + * On output, Nv [i] holds the number of pivots represented by super + * row/column i of the original matrix, or Nv [i] = 0 for non-principal + * rows/columns. Note that i refers to a row/column in the original + * matrix, not the permuted matrix. + * + * Elen: An integer array of size n. See the description of Iw above. At the + * start of execution, Elen [i] is set to zero for all rows i. During + * execution, Elen [i] is the number of elements in the list for + * supervariable i. When e becomes an element, Elen [e] = FLIP (esize) is + * set, where esize is the size of the element (the number of pivots, plus + * the number of nonpivotal entries). Thus Elen [e] < EMPTY. + * Elen (i) = EMPTY set when variable i becomes nonprincipal. + * + * For variables, Elen (i) >= EMPTY holds until just before the + * postordering and permutation vectors are computed. For elements, + * Elen [e] < EMPTY holds. + * + * On output, Elen [i] is the degree of the row/column in the Cholesky + * factorization of the permuted matrix, corresponding to the original row + * i, if i is a super row/column. It is equal to EMPTY if i is + * non-principal. Note that i refers to a row/column in the original + * matrix, not the permuted matrix. + * + * Note that the contents of Elen on output differ from the Fortran + * version (Elen holds the inverse permutation in the Fortran version, + * which is instead returned in the Next array in this C version, + * described below). + * + * Last: In a degree list, Last [i] is the supervariable preceding i, or EMPTY + * if i is the head of the list. In a hash bucket, Last [i] is the hash + * key for i. + * + * Last [Head [hash]] is also used as the head of a hash bucket if + * Head [hash] contains a degree list (see the description of Head, + * below). + * + * On output, Last [0..n-1] holds the permutation. That is, if + * i = Last [k], then row i is the kth pivot row (where k ranges from 0 to + * n-1). Row Last [k] of A is the kth row in the permuted matrix, PAP'. + * + * Next: Next [i] is the supervariable following i in a link list, or EMPTY if + * i is the last in the list. Used for two kinds of lists: degree lists + * and hash buckets (a supervariable can be in only one kind of list at a + * time). + * + * On output Next [0..n-1] holds the inverse permutation. That is, if + * k = Next [i], then row i is the kth pivot row. Row i of A appears as + * the (Next[i])-th row in the permuted matrix, PAP'. + * + * Note that the contents of Next on output differ from the Fortran + * version (Next is undefined on output in the Fortran version). + + * ---------------------------------------------------------------------------- + * LOCAL WORKSPACE (not input or output - used only during execution): + * ---------------------------------------------------------------------------- + * + * Degree: An integer array of size n. If i is a supervariable, then + * Degree [i] holds the current approximation of the external degree of + * row i (an upper bound). The external degree is the number of nonzeros + * in row i, minus ABS (Nv [i]), the diagonal part. The bound is equal to + * the exact external degree if Elen [i] is less than or equal to two. + * + * We also use the term "external degree" for elements e to refer to + * |Le \ Lme|. If e is an element, then Degree [e] is |Le|, which is the + * degree of the off-diagonal part of the element e (not including the + * diagonal part). + * + * Head: An integer array of size n. Head is used for degree lists. + * Head [deg] is the first supervariable in a degree list. All + * supervariables i in a degree list Head [deg] have the same approximate + * degree, namely, deg = Degree [i]. If the list Head [deg] is empty then + * Head [deg] = EMPTY. + * + * During supervariable detection Head [hash] also serves as a pointer to + * a hash bucket. If Head [hash] >= 0, there is a degree list of degree + * hash. The hash bucket head pointer is Last [Head [hash]]. If + * Head [hash] = EMPTY, then the degree list and hash bucket are both + * empty. If Head [hash] < EMPTY, then the degree list is empty, and + * FLIP (Head [hash]) is the head of the hash bucket. After supervariable + * detection is complete, all hash buckets are empty, and the + * (Last [Head [hash]] = EMPTY) condition is restored for the non-empty + * degree lists. + * + * W: An integer array of size n. The flag array W determines the status of + * elements and variables, and the external degree of elements. + * + * for elements: + * if W [e] = 0, then the element e is absorbed. + * if W [e] >= wflg, then W [e] - wflg is the size of the set + * |Le \ Lme|, in terms of nonzeros (the sum of ABS (Nv [i]) for + * each principal variable i that is both in the pattern of + * element e and NOT in the pattern of the current pivot element, + * me). + * if wflg > W [e] > 0, then e is not absorbed and has not yet been + * seen in the scan of the element lists in the computation of + * |Le\Lme| in Scan 1 below. + * + * for variables: + * during supervariable detection, if W [j] != wflg then j is + * not in the pattern of variable i. + * + * The W array is initialized by setting W [i] = 1 for all i, and by + * setting wflg = 2. It is reinitialized if wflg becomes too large (to + * ensure that wflg+n does not cause integer overflow). + + * ---------------------------------------------------------------------------- + * LOCAL INTEGERS: + * ---------------------------------------------------------------------------- + */ + + Int deg, degme, dext, lemax, e, elenme, eln, i, ilast, inext, j, + jlast, jnext, k, knt1, knt2, knt3, lenj, ln, me, mindeg, nel, nleft, + nvi, nvj, nvpiv, slenme, wbig, we, wflg, wnvi, ok, ndense, ncmpa, + dense, aggressive ; + + unsigned Int hash ; /* unsigned, so that hash % n is well defined.*/ + +/* + * deg: the degree of a variable or element + * degme: size, |Lme|, of the current element, me (= Degree [me]) + * dext: external degree, |Le \ Lme|, of some element e + * lemax: largest |Le| seen so far (called dmax in Fortran version) + * e: an element + * elenme: the length, Elen [me], of element list of pivotal variable + * eln: the length, Elen [...], of an element list + * hash: the computed value of the hash function + * i: a supervariable + * ilast: the entry in a link list preceding i + * inext: the entry in a link list following i + * j: a supervariable + * jlast: the entry in a link list preceding j + * jnext: the entry in a link list, or path, following j + * k: the pivot order of an element or variable + * knt1: loop counter used during element construction + * knt2: loop counter used during element construction + * knt3: loop counter used during compression + * lenj: Len [j] + * ln: length of a supervariable list + * me: current supervariable being eliminated, and the current + * element created by eliminating that supervariable + * mindeg: current minimum degree + * nel: number of pivots selected so far + * nleft: n - nel, the number of nonpivotal rows/columns remaining + * nvi: the number of variables in a supervariable i (= Nv [i]) + * nvj: the number of variables in a supervariable j (= Nv [j]) + * nvpiv: number of pivots in current element + * slenme: number of variables in variable list of pivotal variable + * wbig: = INT_MAX - n for the int version, UF_long_max - n for the + * UF_long version. wflg is not allowed to be >= wbig. + * we: W [e] + * wflg: used for flagging the W array. See description of Iw. + * wnvi: wflg - Nv [i] + * x: either a supervariable or an element + * + * ok: true if supervariable j can be absorbed into i + * ndense: number of "dense" rows/columns + * dense: rows/columns with initial degree > dense are considered "dense" + * aggressive: true if aggressive absorption is being performed + * ncmpa: number of garbage collections + + * ---------------------------------------------------------------------------- + * LOCAL DOUBLES, used for statistical output only (except for alpha): + * ---------------------------------------------------------------------------- + */ + + double f, r, ndiv, s, nms_lu, nms_ldl, dmax, alpha, lnz, lnzme ; + +/* + * f: nvpiv + * r: degme + nvpiv + * ndiv: number of divisions for LU or LDL' factorizations + * s: number of multiply-subtract pairs for LU factorization, for the + * current element me + * nms_lu number of multiply-subtract pairs for LU factorization + * nms_ldl number of multiply-subtract pairs for LDL' factorization + * dmax: the largest number of entries in any column of L, including the + * diagonal + * alpha: "dense" degree ratio + * lnz: the number of nonzeros in L (excluding the diagonal) + * lnzme: the number of nonzeros in L (excl. the diagonal) for the + * current element me + + * ---------------------------------------------------------------------------- + * LOCAL "POINTERS" (indices into the Iw array) + * ---------------------------------------------------------------------------- +*/ + + Int p, p1, p2, p3, p4, pdst, pend, pj, pme, pme1, pme2, pn, psrc ; + +/* + * Any parameter (Pe [...] or pfree) or local variable starting with "p" (for + * Pointer) is an index into Iw, and all indices into Iw use variables starting + * with "p." The only exception to this rule is the iwlen input argument. + * + * p: pointer into lots of things + * p1: Pe [i] for some variable i (start of element list) + * p2: Pe [i] + Elen [i] - 1 for some variable i + * p3: index of first supervariable in clean list + * p4: + * pdst: destination pointer, for compression + * pend: end of memory to compress + * pj: pointer into an element or variable + * pme: pointer into the current element (pme1...pme2) + * pme1: the current element, me, is stored in Iw [pme1...pme2] + * pme2: the end of the current element + * pn: pointer into a "clean" variable, also used to compress + * psrc: source pointer, for compression +*/ + +/* ========================================================================= */ +/* INITIALIZATIONS */ +/* ========================================================================= */ + + /* Note that this restriction on iwlen is slightly more restrictive than + * what is actually required in AMD_2. AMD_2 can operate with no elbow + * room at all, but it will be slow. For better performance, at least + * size-n elbow room is enforced. */ + ASSERT (iwlen >= pfree + n) ; + ASSERT (n > 0) ; + + /* initialize output statistics */ + lnz = 0 ; + ndiv = 0 ; + nms_lu = 0 ; + nms_ldl = 0 ; + dmax = 1 ; + me = EMPTY ; + + mindeg = 0 ; + ncmpa = 0 ; + nel = 0 ; + lemax = 0 ; + + /* get control parameters */ + if (Control != (double *) NULL) + { + alpha = Control [AMD_DENSE] ; + aggressive = (Control [AMD_AGGRESSIVE] != 0) ; + } + else + { + alpha = AMD_DEFAULT_DENSE ; + aggressive = AMD_DEFAULT_AGGRESSIVE ; + } + /* Note: if alpha is NaN, this is undefined: */ + if (alpha < 0) + { + /* only remove completely dense rows/columns */ + dense = n-2 ; + } + else + { + dense = alpha * sqrt ((double) n) ; + } + dense = MAX (16, dense) ; + dense = MIN (n, dense) ; + AMD_DEBUG1 (("\n\nAMD (debug), alpha %g, aggr. "ID"\n", + alpha, aggressive)) ; + + for (i = 0 ; i < n ; i++) + { + Last [i] = EMPTY ; + Head [i] = EMPTY ; + Next [i] = EMPTY ; + /* if separate Hhead array is used for hash buckets: * + Hhead [i] = EMPTY ; + */ + Nv [i] = 1 ; + W [i] = 1 ; + Elen [i] = 0 ; + Degree [i] = Len [i] ; + } + +#ifndef NDEBUG + AMD_DEBUG1 (("\n======Nel "ID" initial\n", nel)) ; + AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, Last, + Head, Elen, Degree, W, -1) ; +#endif + + /* initialize wflg */ + wbig = Int_MAX - n ; + wflg = clear_flag (0, wbig, W, n) ; + + /* --------------------------------------------------------------------- */ + /* initialize degree lists and eliminate dense and empty rows */ + /* --------------------------------------------------------------------- */ + + ndense = 0 ; + + for (i = 0 ; i < n ; i++) + { + deg = Degree [i] ; + ASSERT (deg >= 0 && deg < n) ; + if (deg == 0) + { + + /* ------------------------------------------------------------- + * we have a variable that can be eliminated at once because + * there is no off-diagonal non-zero in its row. Note that + * Nv [i] = 1 for an empty variable i. It is treated just + * the same as an eliminated element i. + * ------------------------------------------------------------- */ + + Elen [i] = FLIP (1) ; + nel++ ; + Pe [i] = EMPTY ; + W [i] = 0 ; + + } + else if (deg > dense) + { + + /* ------------------------------------------------------------- + * Dense variables are not treated as elements, but as unordered, + * non-principal variables that have no parent. They do not take + * part in the postorder, since Nv [i] = 0. Note that the Fortran + * version does not have this option. + * ------------------------------------------------------------- */ + + AMD_DEBUG1 (("Dense node "ID" degree "ID"\n", i, deg)) ; + ndense++ ; + Nv [i] = 0 ; /* do not postorder this node */ + Elen [i] = EMPTY ; + nel++ ; + Pe [i] = EMPTY ; + + } + else + { + + /* ------------------------------------------------------------- + * place i in the degree list corresponding to its degree + * ------------------------------------------------------------- */ + + inext = Head [deg] ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = i ; + Next [i] = inext ; + Head [deg] = i ; + + } + } + +/* ========================================================================= */ +/* WHILE (selecting pivots) DO */ +/* ========================================================================= */ + + while (nel < n) + { + +#ifndef NDEBUG + AMD_DEBUG1 (("\n======Nel "ID"\n", nel)) ; + if (AMD_debug >= 2) + { + AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, + Last, Head, Elen, Degree, W, nel) ; + } +#endif + +/* ========================================================================= */ +/* GET PIVOT OF MINIMUM DEGREE */ +/* ========================================================================= */ + + /* ----------------------------------------------------------------- */ + /* find next supervariable for elimination */ + /* ----------------------------------------------------------------- */ + + ASSERT (mindeg >= 0 && mindeg < n) ; + for (deg = mindeg ; deg < n ; deg++) + { + me = Head [deg] ; + if (me != EMPTY) break ; + } + mindeg = deg ; + ASSERT (me >= 0 && me < n) ; + AMD_DEBUG1 (("=================me: "ID"\n", me)) ; + + /* ----------------------------------------------------------------- */ + /* remove chosen variable from link list */ + /* ----------------------------------------------------------------- */ + + inext = Next [me] ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = EMPTY ; + Head [deg] = inext ; + + /* ----------------------------------------------------------------- */ + /* me represents the elimination of pivots nel to nel+Nv[me]-1. */ + /* place me itself as the first in this set. */ + /* ----------------------------------------------------------------- */ + + elenme = Elen [me] ; + nvpiv = Nv [me] ; + ASSERT (nvpiv > 0) ; + nel += nvpiv ; + +/* ========================================================================= */ +/* CONSTRUCT NEW ELEMENT */ +/* ========================================================================= */ + + /* ----------------------------------------------------------------- + * At this point, me is the pivotal supervariable. It will be + * converted into the current element. Scan list of the pivotal + * supervariable, me, setting tree pointers and constructing new list + * of supervariables for the new element, me. p is a pointer to the + * current position in the old list. + * ----------------------------------------------------------------- */ + + /* flag the variable "me" as being in Lme by negating Nv [me] */ + Nv [me] = -nvpiv ; + degme = 0 ; + ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ; + + if (elenme == 0) + { + + /* ------------------------------------------------------------- */ + /* construct the new element in place */ + /* ------------------------------------------------------------- */ + + pme1 = Pe [me] ; + pme2 = pme1 - 1 ; + + for (p = pme1 ; p <= pme1 + Len [me] - 1 ; p++) + { + i = Iw [p] ; + ASSERT (i >= 0 && i < n && Nv [i] >= 0) ; + nvi = Nv [i] ; + if (nvi > 0) + { + + /* ----------------------------------------------------- */ + /* i is a principal variable not yet placed in Lme. */ + /* store i in new list */ + /* ----------------------------------------------------- */ + + /* flag i as being in Lme by negating Nv [i] */ + degme += nvi ; + Nv [i] = -nvi ; + Iw [++pme2] = i ; + + /* ----------------------------------------------------- */ + /* remove variable i from degree list. */ + /* ----------------------------------------------------- */ + + ilast = Last [i] ; + inext = Next [i] ; + ASSERT (ilast >= EMPTY && ilast < n) ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = ilast ; + if (ilast != EMPTY) + { + Next [ilast] = inext ; + } + else + { + /* i is at the head of the degree list */ + ASSERT (Degree [i] >= 0 && Degree [i] < n) ; + Head [Degree [i]] = inext ; + } + } + } + } + else + { + + /* ------------------------------------------------------------- */ + /* construct the new element in empty space, Iw [pfree ...] */ + /* ------------------------------------------------------------- */ + + p = Pe [me] ; + pme1 = pfree ; + slenme = Len [me] - elenme ; + + for (knt1 = 1 ; knt1 <= elenme + 1 ; knt1++) + { + + if (knt1 > elenme) + { + /* search the supervariables in me. */ + e = me ; + pj = p ; + ln = slenme ; + AMD_DEBUG2 (("Search sv: "ID" "ID" "ID"\n", me,pj,ln)) ; + } + else + { + /* search the elements in me. */ + e = Iw [p++] ; + ASSERT (e >= 0 && e < n) ; + pj = Pe [e] ; + ln = Len [e] ; + AMD_DEBUG2 (("Search element e "ID" in me "ID"\n", e,me)) ; + ASSERT (Elen [e] < EMPTY && W [e] > 0 && pj >= 0) ; + } + ASSERT (ln >= 0 && (ln == 0 || (pj >= 0 && pj < iwlen))) ; + + /* --------------------------------------------------------- + * search for different supervariables and add them to the + * new list, compressing when necessary. this loop is + * executed once for each element in the list and once for + * all the supervariables in the list. + * --------------------------------------------------------- */ + + for (knt2 = 1 ; knt2 <= ln ; knt2++) + { + i = Iw [pj++] ; + ASSERT (i >= 0 && i < n && (i == me || Elen [i] >= EMPTY)); + nvi = Nv [i] ; + AMD_DEBUG2 ((": "ID" "ID" "ID" "ID"\n", + i, Elen [i], Nv [i], wflg)) ; + + if (nvi > 0) + { + + /* ------------------------------------------------- */ + /* compress Iw, if necessary */ + /* ------------------------------------------------- */ + + if (pfree >= iwlen) + { + + AMD_DEBUG1 (("GARBAGE COLLECTION\n")) ; + + /* prepare for compressing Iw by adjusting pointers + * and lengths so that the lists being searched in + * the inner and outer loops contain only the + * remaining entries. */ + + Pe [me] = p ; + Len [me] -= knt1 ; + /* check if nothing left of supervariable me */ + if (Len [me] == 0) Pe [me] = EMPTY ; + Pe [e] = pj ; + Len [e] = ln - knt2 ; + /* nothing left of element e */ + if (Len [e] == 0) Pe [e] = EMPTY ; + + ncmpa++ ; /* one more garbage collection */ + + /* store first entry of each object in Pe */ + /* FLIP the first entry in each object */ + for (j = 0 ; j < n ; j++) + { + pn = Pe [j] ; + if (pn >= 0) + { + ASSERT (pn >= 0 && pn < iwlen) ; + Pe [j] = Iw [pn] ; + Iw [pn] = FLIP (j) ; + } + } + + /* psrc/pdst point to source/destination */ + psrc = 0 ; + pdst = 0 ; + pend = pme1 - 1 ; + + while (psrc <= pend) + { + /* search for next FLIP'd entry */ + j = FLIP (Iw [psrc++]) ; + if (j >= 0) + { + AMD_DEBUG2 (("Got object j: "ID"\n", j)) ; + Iw [pdst] = Pe [j] ; + Pe [j] = pdst++ ; + lenj = Len [j] ; + /* copy from source to destination */ + for (knt3 = 0 ; knt3 <= lenj - 2 ; knt3++) + { + Iw [pdst++] = Iw [psrc++] ; + } + } + } + + /* move the new partially-constructed element */ + p1 = pdst ; + for (psrc = pme1 ; psrc <= pfree-1 ; psrc++) + { + Iw [pdst++] = Iw [psrc] ; + } + pme1 = p1 ; + pfree = pdst ; + pj = Pe [e] ; + p = Pe [me] ; + + } + + /* ------------------------------------------------- */ + /* i is a principal variable not yet placed in Lme */ + /* store i in new list */ + /* ------------------------------------------------- */ + + /* flag i as being in Lme by negating Nv [i] */ + degme += nvi ; + Nv [i] = -nvi ; + Iw [pfree++] = i ; + AMD_DEBUG2 ((" s: "ID" nv "ID"\n", i, Nv [i])); + + /* ------------------------------------------------- */ + /* remove variable i from degree link list */ + /* ------------------------------------------------- */ + + ilast = Last [i] ; + inext = Next [i] ; + ASSERT (ilast >= EMPTY && ilast < n) ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = ilast ; + if (ilast != EMPTY) + { + Next [ilast] = inext ; + } + else + { + /* i is at the head of the degree list */ + ASSERT (Degree [i] >= 0 && Degree [i] < n) ; + Head [Degree [i]] = inext ; + } + } + } + + if (e != me) + { + /* set tree pointer and flag to indicate element e is + * absorbed into new element me (the parent of e is me) */ + AMD_DEBUG1 ((" Element "ID" => "ID"\n", e, me)) ; + Pe [e] = FLIP (me) ; + W [e] = 0 ; + } + } + + pme2 = pfree - 1 ; + } + + /* ----------------------------------------------------------------- */ + /* me has now been converted into an element in Iw [pme1..pme2] */ + /* ----------------------------------------------------------------- */ + + /* degme holds the external degree of new element */ + Degree [me] = degme ; + Pe [me] = pme1 ; + Len [me] = pme2 - pme1 + 1 ; + ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ; + + Elen [me] = FLIP (nvpiv + degme) ; + /* FLIP (Elen (me)) is now the degree of pivot (including + * diagonal part). */ + +#ifndef NDEBUG + AMD_DEBUG2 (("New element structure: length= "ID"\n", pme2-pme1+1)) ; + for (pme = pme1 ; pme <= pme2 ; pme++) AMD_DEBUG3 ((" "ID"", Iw[pme])); + AMD_DEBUG3 (("\n")) ; +#endif + + /* ----------------------------------------------------------------- */ + /* make sure that wflg is not too large. */ + /* ----------------------------------------------------------------- */ + + /* With the current value of wflg, wflg+n must not cause integer + * overflow */ + + wflg = clear_flag (wflg, wbig, W, n) ; + +/* ========================================================================= */ +/* COMPUTE (W [e] - wflg) = |Le\Lme| FOR ALL ELEMENTS */ +/* ========================================================================= */ + + /* ----------------------------------------------------------------- + * Scan 1: compute the external degrees of previous elements with + * respect to the current element. That is: + * (W [e] - wflg) = |Le \ Lme| + * for each element e that appears in any supervariable in Lme. The + * notation Le refers to the pattern (list of supervariables) of a + * previous element e, where e is not yet absorbed, stored in + * Iw [Pe [e] + 1 ... Pe [e] + Len [e]]. The notation Lme + * refers to the pattern of the current element (stored in + * Iw [pme1..pme2]). If aggressive absorption is enabled, and + * (W [e] - wflg) becomes zero, then the element e will be absorbed + * in Scan 2. + * ----------------------------------------------------------------- */ + + AMD_DEBUG2 (("me: ")) ; + for (pme = pme1 ; pme <= pme2 ; pme++) + { + i = Iw [pme] ; + ASSERT (i >= 0 && i < n) ; + eln = Elen [i] ; + AMD_DEBUG3 ((""ID" Elen "ID": \n", i, eln)) ; + if (eln > 0) + { + /* note that Nv [i] has been negated to denote i in Lme: */ + nvi = -Nv [i] ; + ASSERT (nvi > 0 && Pe [i] >= 0 && Pe [i] < iwlen) ; + wnvi = wflg - nvi ; + for (p = Pe [i] ; p <= Pe [i] + eln - 1 ; p++) + { + e = Iw [p] ; + ASSERT (e >= 0 && e < n) ; + we = W [e] ; + AMD_DEBUG4 ((" e "ID" we "ID" ", e, we)) ; + if (we >= wflg) + { + /* unabsorbed element e has been seen in this loop */ + AMD_DEBUG4 ((" unabsorbed, first time seen")) ; + we -= nvi ; + } + else if (we != 0) + { + /* e is an unabsorbed element */ + /* this is the first we have seen e in all of Scan 1 */ + AMD_DEBUG4 ((" unabsorbed")) ; + we = Degree [e] + wnvi ; + } + AMD_DEBUG4 (("\n")) ; + W [e] = we ; + } + } + } + AMD_DEBUG2 (("\n")) ; + +/* ========================================================================= */ +/* DEGREE UPDATE AND ELEMENT ABSORPTION */ +/* ========================================================================= */ + + /* ----------------------------------------------------------------- + * Scan 2: for each i in Lme, sum up the degree of Lme (which is + * degme), plus the sum of the external degrees of each Le for the + * elements e appearing within i, plus the supervariables in i. + * Place i in hash list. + * ----------------------------------------------------------------- */ + + for (pme = pme1 ; pme <= pme2 ; pme++) + { + i = Iw [pme] ; + ASSERT (i >= 0 && i < n && Nv [i] < 0 && Elen [i] >= 0) ; + AMD_DEBUG2 (("Updating: i "ID" "ID" "ID"\n", i, Elen[i], Len [i])); + p1 = Pe [i] ; + p2 = p1 + Elen [i] - 1 ; + pn = p1 ; + hash = 0 ; + deg = 0 ; + ASSERT (p1 >= 0 && p1 < iwlen && p2 >= -1 && p2 < iwlen) ; + + /* ------------------------------------------------------------- */ + /* scan the element list associated with supervariable i */ + /* ------------------------------------------------------------- */ + + /* UMFPACK/MA38-style approximate degree: */ + if (aggressive) + { + for (p = p1 ; p <= p2 ; p++) + { + e = Iw [p] ; + ASSERT (e >= 0 && e < n) ; + we = W [e] ; + if (we != 0) + { + /* e is an unabsorbed element */ + /* dext = | Le \ Lme | */ + dext = we - wflg ; + if (dext > 0) + { + deg += dext ; + Iw [pn++] = e ; + hash += e ; + AMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ; + } + else + { + /* external degree of e is zero, absorb e into me*/ + AMD_DEBUG1 ((" Element "ID" =>"ID" (aggressive)\n", + e, me)) ; + ASSERT (dext == 0) ; + Pe [e] = FLIP (me) ; + W [e] = 0 ; + } + } + } + } + else + { + for (p = p1 ; p <= p2 ; p++) + { + e = Iw [p] ; + ASSERT (e >= 0 && e < n) ; + we = W [e] ; + if (we != 0) + { + /* e is an unabsorbed element */ + dext = we - wflg ; + ASSERT (dext >= 0) ; + deg += dext ; + Iw [pn++] = e ; + hash += e ; + AMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ; + } + } + } + + /* count the number of elements in i (including me): */ + Elen [i] = pn - p1 + 1 ; + + /* ------------------------------------------------------------- */ + /* scan the supervariables in the list associated with i */ + /* ------------------------------------------------------------- */ + + /* The bulk of the AMD run time is typically spent in this loop, + * particularly if the matrix has many dense rows that are not + * removed prior to ordering. */ + p3 = pn ; + p4 = p1 + Len [i] ; + for (p = p2 + 1 ; p < p4 ; p++) + { + j = Iw [p] ; + ASSERT (j >= 0 && j < n) ; + nvj = Nv [j] ; + if (nvj > 0) + { + /* j is unabsorbed, and not in Lme. */ + /* add to degree and add to new list */ + deg += nvj ; + Iw [pn++] = j ; + hash += j ; + AMD_DEBUG4 ((" s: "ID" hash "ID" Nv[j]= "ID"\n", + j, hash, nvj)) ; + } + } + + /* ------------------------------------------------------------- */ + /* update the degree and check for mass elimination */ + /* ------------------------------------------------------------- */ + + /* with aggressive absorption, deg==0 is identical to the + * Elen [i] == 1 && p3 == pn test, below. */ + ASSERT (IMPLIES (aggressive, (deg==0) == (Elen[i]==1 && p3==pn))) ; + + if (Elen [i] == 1 && p3 == pn) + { + + /* --------------------------------------------------------- */ + /* mass elimination */ + /* --------------------------------------------------------- */ + + /* There is nothing left of this node except for an edge to + * the current pivot element. Elen [i] is 1, and there are + * no variables adjacent to node i. Absorb i into the + * current pivot element, me. Note that if there are two or + * more mass eliminations, fillin due to mass elimination is + * possible within the nvpiv-by-nvpiv pivot block. It is this + * step that causes AMD's analysis to be an upper bound. + * + * The reason is that the selected pivot has a lower + * approximate degree than the true degree of the two mass + * eliminated nodes. There is no edge between the two mass + * eliminated nodes. They are merged with the current pivot + * anyway. + * + * No fillin occurs in the Schur complement, in any case, + * and this effect does not decrease the quality of the + * ordering itself, just the quality of the nonzero and + * flop count analysis. It also means that the post-ordering + * is not an exact elimination tree post-ordering. */ + + AMD_DEBUG1 ((" MASS i "ID" => parent e "ID"\n", i, me)) ; + Pe [i] = FLIP (me) ; + nvi = -Nv [i] ; + degme -= nvi ; + nvpiv += nvi ; + nel += nvi ; + Nv [i] = 0 ; + Elen [i] = EMPTY ; + + } + else + { + + /* --------------------------------------------------------- */ + /* update the upper-bound degree of i */ + /* --------------------------------------------------------- */ + + /* the following degree does not yet include the size + * of the current element, which is added later: */ + + Degree [i] = MIN (Degree [i], deg) ; + + /* --------------------------------------------------------- */ + /* add me to the list for i */ + /* --------------------------------------------------------- */ + + /* move first supervariable to end of list */ + Iw [pn] = Iw [p3] ; + /* move first element to end of element part of list */ + Iw [p3] = Iw [p1] ; + /* add new element, me, to front of list. */ + Iw [p1] = me ; + /* store the new length of the list in Len [i] */ + Len [i] = pn - p1 + 1 ; + + /* --------------------------------------------------------- */ + /* place in hash bucket. Save hash key of i in Last [i]. */ + /* --------------------------------------------------------- */ + + /* NOTE: this can fail if hash is negative, because the ANSI C + * standard does not define a % b when a and/or b are negative. + * That's why hash is defined as an unsigned Int, to avoid this + * problem. */ + hash = hash % n ; + ASSERT (((Int) hash) >= 0 && ((Int) hash) < n) ; + + /* if the Hhead array is not used: */ + j = Head [hash] ; + if (j <= EMPTY) + { + /* degree list is empty, hash head is FLIP (j) */ + Next [i] = FLIP (j) ; + Head [hash] = FLIP (i) ; + } + else + { + /* degree list is not empty, use Last [Head [hash]] as + * hash head. */ + Next [i] = Last [j] ; + Last [j] = i ; + } + + /* if a separate Hhead array is used: * + Next [i] = Hhead [hash] ; + Hhead [hash] = i ; + */ + + Last [i] = hash ; + } + } + + Degree [me] = degme ; + + /* ----------------------------------------------------------------- */ + /* Clear the counter array, W [...], by incrementing wflg. */ + /* ----------------------------------------------------------------- */ + + /* make sure that wflg+n does not cause integer overflow */ + lemax = MAX (lemax, degme) ; + wflg += lemax ; + wflg = clear_flag (wflg, wbig, W, n) ; + /* at this point, W [0..n-1] < wflg holds */ + +/* ========================================================================= */ +/* SUPERVARIABLE DETECTION */ +/* ========================================================================= */ + + AMD_DEBUG1 (("Detecting supervariables:\n")) ; + for (pme = pme1 ; pme <= pme2 ; pme++) + { + i = Iw [pme] ; + ASSERT (i >= 0 && i < n) ; + AMD_DEBUG2 (("Consider i "ID" nv "ID"\n", i, Nv [i])) ; + if (Nv [i] < 0) + { + /* i is a principal variable in Lme */ + + /* --------------------------------------------------------- + * examine all hash buckets with 2 or more variables. We do + * this by examing all unique hash keys for supervariables in + * the pattern Lme of the current element, me + * --------------------------------------------------------- */ + + /* let i = head of hash bucket, and empty the hash bucket */ + ASSERT (Last [i] >= 0 && Last [i] < n) ; + hash = Last [i] ; + + /* if Hhead array is not used: */ + j = Head [hash] ; + if (j == EMPTY) + { + /* hash bucket and degree list are both empty */ + i = EMPTY ; + } + else if (j < EMPTY) + { + /* degree list is empty */ + i = FLIP (j) ; + Head [hash] = EMPTY ; + } + else + { + /* degree list is not empty, restore Last [j] of head j */ + i = Last [j] ; + Last [j] = EMPTY ; + } + + /* if separate Hhead array is used: * + i = Hhead [hash] ; + Hhead [hash] = EMPTY ; + */ + + ASSERT (i >= EMPTY && i < n) ; + AMD_DEBUG2 (("----i "ID" hash "ID"\n", i, hash)) ; + + while (i != EMPTY && Next [i] != EMPTY) + { + + /* ----------------------------------------------------- + * this bucket has one or more variables following i. + * scan all of them to see if i can absorb any entries + * that follow i in hash bucket. Scatter i into w. + * ----------------------------------------------------- */ + + ln = Len [i] ; + eln = Elen [i] ; + ASSERT (ln >= 0 && eln >= 0) ; + ASSERT (Pe [i] >= 0 && Pe [i] < iwlen) ; + /* do not flag the first element in the list (me) */ + for (p = Pe [i] + 1 ; p <= Pe [i] + ln - 1 ; p++) + { + ASSERT (Iw [p] >= 0 && Iw [p] < n) ; + W [Iw [p]] = wflg ; + } + + /* ----------------------------------------------------- */ + /* scan every other entry j following i in bucket */ + /* ----------------------------------------------------- */ + + jlast = i ; + j = Next [i] ; + ASSERT (j >= EMPTY && j < n) ; + + while (j != EMPTY) + { + /* ------------------------------------------------- */ + /* check if j and i have identical nonzero pattern */ + /* ------------------------------------------------- */ + + AMD_DEBUG3 (("compare i "ID" and j "ID"\n", i,j)) ; + + /* check if i and j have the same Len and Elen */ + ASSERT (Len [j] >= 0 && Elen [j] >= 0) ; + ASSERT (Pe [j] >= 0 && Pe [j] < iwlen) ; + ok = (Len [j] == ln) && (Elen [j] == eln) ; + /* skip the first element in the list (me) */ + for (p = Pe [j] + 1 ; ok && p <= Pe [j] + ln - 1 ; p++) + { + ASSERT (Iw [p] >= 0 && Iw [p] < n) ; + if (W [Iw [p]] != wflg) ok = 0 ; + } + if (ok) + { + /* --------------------------------------------- */ + /* found it! j can be absorbed into i */ + /* --------------------------------------------- */ + + AMD_DEBUG1 (("found it! j "ID" => i "ID"\n", j,i)); + Pe [j] = FLIP (i) ; + /* both Nv [i] and Nv [j] are negated since they */ + /* are in Lme, and the absolute values of each */ + /* are the number of variables in i and j: */ + Nv [i] += Nv [j] ; + Nv [j] = 0 ; + Elen [j] = EMPTY ; + /* delete j from hash bucket */ + ASSERT (j != Next [j]) ; + j = Next [j] ; + Next [jlast] = j ; + + } + else + { + /* j cannot be absorbed into i */ + jlast = j ; + ASSERT (j != Next [j]) ; + j = Next [j] ; + } + ASSERT (j >= EMPTY && j < n) ; + } + + /* ----------------------------------------------------- + * no more variables can be absorbed into i + * go to next i in bucket and clear flag array + * ----------------------------------------------------- */ + + wflg++ ; + i = Next [i] ; + ASSERT (i >= EMPTY && i < n) ; + + } + } + } + AMD_DEBUG2 (("detect done\n")) ; + +/* ========================================================================= */ +/* RESTORE DEGREE LISTS AND REMOVE NONPRINCIPAL SUPERVARIABLES FROM ELEMENT */ +/* ========================================================================= */ + + p = pme1 ; + nleft = n - nel ; + for (pme = pme1 ; pme <= pme2 ; pme++) + { + i = Iw [pme] ; + ASSERT (i >= 0 && i < n) ; + nvi = -Nv [i] ; + AMD_DEBUG3 (("Restore i "ID" "ID"\n", i, nvi)) ; + if (nvi > 0) + { + /* i is a principal variable in Lme */ + /* restore Nv [i] to signify that i is principal */ + Nv [i] = nvi ; + + /* --------------------------------------------------------- */ + /* compute the external degree (add size of current element) */ + /* --------------------------------------------------------- */ + + deg = Degree [i] + degme - nvi ; + deg = MIN (deg, nleft - nvi) ; + ASSERT (IMPLIES (aggressive, deg > 0) && deg >= 0 && deg < n) ; + + /* --------------------------------------------------------- */ + /* place the supervariable at the head of the degree list */ + /* --------------------------------------------------------- */ + + inext = Head [deg] ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = i ; + Next [i] = inext ; + Last [i] = EMPTY ; + Head [deg] = i ; + + /* --------------------------------------------------------- */ + /* save the new degree, and find the minimum degree */ + /* --------------------------------------------------------- */ + + mindeg = MIN (mindeg, deg) ; + Degree [i] = deg ; + + /* --------------------------------------------------------- */ + /* place the supervariable in the element pattern */ + /* --------------------------------------------------------- */ + + Iw [p++] = i ; + + } + } + AMD_DEBUG2 (("restore done\n")) ; + +/* ========================================================================= */ +/* FINALIZE THE NEW ELEMENT */ +/* ========================================================================= */ + + AMD_DEBUG2 (("ME = "ID" DONE\n", me)) ; + Nv [me] = nvpiv ; + /* save the length of the list for the new element me */ + Len [me] = p - pme1 ; + if (Len [me] == 0) + { + /* there is nothing left of the current pivot element */ + /* it is a root of the assembly tree */ + Pe [me] = EMPTY ; + W [me] = 0 ; + } + if (elenme != 0) + { + /* element was not constructed in place: deallocate part of */ + /* it since newly nonprincipal variables may have been removed */ + pfree = p ; + } + + /* The new element has nvpiv pivots and the size of the contribution + * block for a multifrontal method is degme-by-degme, not including + * the "dense" rows/columns. If the "dense" rows/columns are included, + * the frontal matrix is no larger than + * (degme+ndense)-by-(degme+ndense). + */ + + if (Info != (double *) NULL) + { + f = nvpiv ; + r = degme + ndense ; + dmax = MAX (dmax, f + r) ; + + /* number of nonzeros in L (excluding the diagonal) */ + lnzme = f*r + (f-1)*f/2 ; + lnz += lnzme ; + + /* number of divide operations for LDL' and for LU */ + ndiv += lnzme ; + + /* number of multiply-subtract pairs for LU */ + s = f*r*r + r*(f-1)*f + (f-1)*f*(2*f-1)/6 ; + nms_lu += s ; + + /* number of multiply-subtract pairs for LDL' */ + nms_ldl += (s + lnzme)/2 ; + } + +#ifndef NDEBUG + AMD_DEBUG2 (("finalize done nel "ID" n "ID"\n ::::\n", nel, n)) ; + for (pme = Pe [me] ; pme <= Pe [me] + Len [me] - 1 ; pme++) + { + AMD_DEBUG3 ((" "ID"", Iw [pme])) ; + } + AMD_DEBUG3 (("\n")) ; +#endif + + } + +/* ========================================================================= */ +/* DONE SELECTING PIVOTS */ +/* ========================================================================= */ + + if (Info != (double *) NULL) + { + + /* count the work to factorize the ndense-by-ndense submatrix */ + f = ndense ; + dmax = MAX (dmax, (double) ndense) ; + + /* number of nonzeros in L (excluding the diagonal) */ + lnzme = (f-1)*f/2 ; + lnz += lnzme ; + + /* number of divide operations for LDL' and for LU */ + ndiv += lnzme ; + + /* number of multiply-subtract pairs for LU */ + s = (f-1)*f*(2*f-1)/6 ; + nms_lu += s ; + + /* number of multiply-subtract pairs for LDL' */ + nms_ldl += (s + lnzme)/2 ; + + /* number of nz's in L (excl. diagonal) */ + Info [AMD_LNZ] = lnz ; + + /* number of divide ops for LU and LDL' */ + Info [AMD_NDIV] = ndiv ; + + /* number of multiply-subtract pairs for LDL' */ + Info [AMD_NMULTSUBS_LDL] = nms_ldl ; + + /* number of multiply-subtract pairs for LU */ + Info [AMD_NMULTSUBS_LU] = nms_lu ; + + /* number of "dense" rows/columns */ + Info [AMD_NDENSE] = ndense ; + + /* largest front is dmax-by-dmax */ + Info [AMD_DMAX] = dmax ; + + /* number of garbage collections in AMD */ + Info [AMD_NCMPA] = ncmpa ; + + /* successful ordering */ + Info [AMD_STATUS] = AMD_OK ; + } + +/* ========================================================================= */ +/* POST-ORDERING */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- + * Variables at this point: + * + * Pe: holds the elimination tree. The parent of j is FLIP (Pe [j]), + * or EMPTY if j is a root. The tree holds both elements and + * non-principal (unordered) variables absorbed into them. + * Dense variables are non-principal and unordered. + * + * Elen: holds the size of each element, including the diagonal part. + * FLIP (Elen [e]) > 0 if e is an element. For unordered + * variables i, Elen [i] is EMPTY. + * + * Nv: Nv [e] > 0 is the number of pivots represented by the element e. + * For unordered variables i, Nv [i] is zero. + * + * Contents no longer needed: + * W, Iw, Len, Degree, Head, Next, Last. + * + * The matrix itself has been destroyed. + * + * n: the size of the matrix. + * No other scalars needed (pfree, iwlen, etc.) + * ------------------------------------------------------------------------- */ + + /* restore Pe */ + for (i = 0 ; i < n ; i++) + { + Pe [i] = FLIP (Pe [i]) ; + } + + /* restore Elen, for output information, and for postordering */ + for (i = 0 ; i < n ; i++) + { + Elen [i] = FLIP (Elen [i]) ; + } + +/* Now the parent of j is Pe [j], or EMPTY if j is a root. Elen [e] > 0 + * is the size of element e. Elen [i] is EMPTY for unordered variable i. */ + +#ifndef NDEBUG + AMD_DEBUG2 (("\nTree:\n")) ; + for (i = 0 ; i < n ; i++) + { + AMD_DEBUG2 ((" "ID" parent: "ID" ", i, Pe [i])) ; + ASSERT (Pe [i] >= EMPTY && Pe [i] < n) ; + if (Nv [i] > 0) + { + /* this is an element */ + e = i ; + AMD_DEBUG2 ((" element, size is "ID"\n", Elen [i])) ; + ASSERT (Elen [e] > 0) ; + } + AMD_DEBUG2 (("\n")) ; + } + AMD_DEBUG2 (("\nelements:\n")) ; + for (e = 0 ; e < n ; e++) + { + if (Nv [e] > 0) + { + AMD_DEBUG3 (("Element e= "ID" size "ID" nv "ID" \n", e, + Elen [e], Nv [e])) ; + } + } + AMD_DEBUG2 (("\nvariables:\n")) ; + for (i = 0 ; i < n ; i++) + { + Int cnt ; + if (Nv [i] == 0) + { + AMD_DEBUG3 (("i unordered: "ID"\n", i)) ; + j = Pe [i] ; + cnt = 0 ; + AMD_DEBUG3 ((" j: "ID"\n", j)) ; + if (j == EMPTY) + { + AMD_DEBUG3 ((" i is a dense variable\n")) ; + } + else + { + ASSERT (j >= 0 && j < n) ; + while (Nv [j] == 0) + { + AMD_DEBUG3 ((" j : "ID"\n", j)) ; + j = Pe [j] ; + AMD_DEBUG3 ((" j:: "ID"\n", j)) ; + cnt++ ; + if (cnt > n) break ; + } + e = j ; + AMD_DEBUG3 ((" got to e: "ID"\n", e)) ; + } + } + } +#endif + +/* ========================================================================= */ +/* compress the paths of the variables */ +/* ========================================================================= */ + + for (i = 0 ; i < n ; i++) + { + if (Nv [i] == 0) + { + + /* ------------------------------------------------------------- + * i is an un-ordered row. Traverse the tree from i until + * reaching an element, e. The element, e, was the principal + * supervariable of i and all nodes in the path from i to when e + * was selected as pivot. + * ------------------------------------------------------------- */ + + AMD_DEBUG1 (("Path compression, i unordered: "ID"\n", i)) ; + j = Pe [i] ; + ASSERT (j >= EMPTY && j < n) ; + AMD_DEBUG3 ((" j: "ID"\n", j)) ; + if (j == EMPTY) + { + /* Skip a dense variable. It has no parent. */ + AMD_DEBUG3 ((" i is a dense variable\n")) ; + continue ; + } + + /* while (j is a variable) */ + while (Nv [j] == 0) + { + AMD_DEBUG3 ((" j : "ID"\n", j)) ; + j = Pe [j] ; + AMD_DEBUG3 ((" j:: "ID"\n", j)) ; + ASSERT (j >= 0 && j < n) ; + } + /* got to an element e */ + e = j ; + AMD_DEBUG3 (("got to e: "ID"\n", e)) ; + + /* ------------------------------------------------------------- + * traverse the path again from i to e, and compress the path + * (all nodes point to e). Path compression allows this code to + * compute in O(n) time. + * ------------------------------------------------------------- */ + + j = i ; + /* while (j is a variable) */ + while (Nv [j] == 0) + { + jnext = Pe [j] ; + AMD_DEBUG3 (("j "ID" jnext "ID"\n", j, jnext)) ; + Pe [j] = e ; + j = jnext ; + ASSERT (j >= 0 && j < n) ; + } + } + } + +/* ========================================================================= */ +/* postorder the assembly tree */ +/* ========================================================================= */ + + AMD_postorder (n, Pe, Nv, Elen, + W, /* output order */ + Head, Next, Last) ; /* workspace */ + +/* ========================================================================= */ +/* compute output permutation and inverse permutation */ +/* ========================================================================= */ + + /* W [e] = k means that element e is the kth element in the new + * order. e is in the range 0 to n-1, and k is in the range 0 to + * the number of elements. Use Head for inverse order. */ + + for (k = 0 ; k < n ; k++) + { + Head [k] = EMPTY ; + Next [k] = EMPTY ; + } + for (e = 0 ; e < n ; e++) + { + k = W [e] ; + ASSERT ((k == EMPTY) == (Nv [e] == 0)) ; + if (k != EMPTY) + { + ASSERT (k >= 0 && k < n) ; + Head [k] = e ; + } + } + + /* construct output inverse permutation in Next, + * and permutation in Last */ + nel = 0 ; + for (k = 0 ; k < n ; k++) + { + e = Head [k] ; + if (e == EMPTY) break ; + ASSERT (e >= 0 && e < n && Nv [e] > 0) ; + Next [e] = nel ; + nel += Nv [e] ; + } + ASSERT (nel == n - ndense) ; + + /* order non-principal variables (dense, & those merged into supervar's) */ + for (i = 0 ; i < n ; i++) + { + if (Nv [i] == 0) + { + e = Pe [i] ; + ASSERT (e >= EMPTY && e < n) ; + if (e != EMPTY) + { + /* This is an unordered variable that was merged + * into element e via supernode detection or mass + * elimination of i when e became the pivot element. + * Place i in order just before e. */ + ASSERT (Next [i] == EMPTY && Nv [e] > 0) ; + Next [i] = Next [e] ; + Next [e]++ ; + } + else + { + /* This is a dense unordered variable, with no parent. + * Place it last in the output order. */ + Next [i] = nel++ ; + } + } + } + ASSERT (nel == n) ; + + AMD_DEBUG2 (("\n\nPerm:\n")) ; + for (i = 0 ; i < n ; i++) + { + k = Next [i] ; + ASSERT (k >= 0 && k < n) ; + Last [k] = i ; + AMD_DEBUG2 ((" perm ["ID"] = "ID"\n", k, i)) ; + } +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_aat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_aat.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,185 @@ +/* ========================================================================= */ +/* === AMD_aat ============================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* AMD_aat: compute the symmetry of the pattern of A, and count the number of + * nonzeros each column of A+A' (excluding the diagonal). Assumes the input + * matrix has no errors, with sorted columns and no duplicates + * (AMD_valid (n, n, Ap, Ai) must be AMD_OK, but this condition is not + * checked). + */ + +#include "amd_internal.h" + +GLOBAL size_t AMD_aat /* returns nz in A+A' */ +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int Len [ ], /* Len [j]: length of column j of A+A', excl diagonal*/ + Int Tp [ ], /* workspace of size n */ + double Info [ ] +) +{ + Int p1, p2, p, i, j, pj, pj2, k, nzdiag, nzboth, nz ; + double sym ; + size_t nzaat ; + +#ifndef NDEBUG + AMD_debug_init ("AMD AAT") ; + for (k = 0 ; k < n ; k++) Tp [k] = EMPTY ; + ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ; +#endif + + if (Info != (double *) NULL) + { + /* clear the Info array, if it exists */ + for (i = 0 ; i < AMD_INFO ; i++) + { + Info [i] = EMPTY ; + } + Info [AMD_STATUS] = AMD_OK ; + } + + for (k = 0 ; k < n ; k++) + { + Len [k] = 0 ; + } + + nzdiag = 0 ; + nzboth = 0 ; + nz = Ap [n] ; + + for (k = 0 ; k < n ; k++) + { + p1 = Ap [k] ; + p2 = Ap [k+1] ; + AMD_DEBUG2 (("\nAAT Column: "ID" p1: "ID" p2: "ID"\n", k, p1, p2)) ; + + /* construct A+A' */ + for (p = p1 ; p < p2 ; ) + { + /* scan the upper triangular part of A */ + j = Ai [p] ; + if (j < k) + { + /* entry A (j,k) is in the strictly upper triangular part, + * add both A (j,k) and A (k,j) to the matrix A+A' */ + Len [j]++ ; + Len [k]++ ; + AMD_DEBUG3 ((" upper ("ID","ID") ("ID","ID")\n", j,k, k,j)); + p++ ; + } + else if (j == k) + { + /* skip the diagonal */ + p++ ; + nzdiag++ ; + break ; + } + else /* j > k */ + { + /* first entry below the diagonal */ + break ; + } + /* scan lower triangular part of A, in column j until reaching + * row k. Start where last scan left off. */ + ASSERT (Tp [j] != EMPTY) ; + ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; + pj2 = Ap [j+1] ; + for (pj = Tp [j] ; pj < pj2 ; ) + { + i = Ai [pj] ; + if (i < k) + { + /* A (i,j) is only in the lower part, not in upper. + * add both A (i,j) and A (j,i) to the matrix A+A' */ + Len [i]++ ; + Len [j]++ ; + AMD_DEBUG3 ((" lower ("ID","ID") ("ID","ID")\n", + i,j, j,i)) ; + pj++ ; + } + else if (i == k) + { + /* entry A (k,j) in lower part and A (j,k) in upper */ + pj++ ; + nzboth++ ; + break ; + } + else /* i > k */ + { + /* consider this entry later, when k advances to i */ + break ; + } + } + Tp [j] = pj ; + } + /* Tp [k] points to the entry just below the diagonal in column k */ + Tp [k] = p ; + } + + /* clean up, for remaining mismatched entries */ + for (j = 0 ; j < n ; j++) + { + for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) + { + i = Ai [pj] ; + /* A (i,j) is only in the lower part, not in upper. + * add both A (i,j) and A (j,i) to the matrix A+A' */ + Len [i]++ ; + Len [j]++ ; + AMD_DEBUG3 ((" lower cleanup ("ID","ID") ("ID","ID")\n", + i,j, j,i)) ; + } + } + + /* --------------------------------------------------------------------- */ + /* compute the symmetry of the nonzero pattern of A */ + /* --------------------------------------------------------------------- */ + + /* Given a matrix A, the symmetry of A is: + * B = tril (spones (A), -1) + triu (spones (A), 1) ; + * sym = nnz (B & B') / nnz (B) ; + * or 1 if nnz (B) is zero. + */ + + if (nz == nzdiag) + { + sym = 1 ; + } + else + { + sym = (2 * (double) nzboth) / ((double) (nz - nzdiag)) ; + } + + nzaat = 0 ; + for (k = 0 ; k < n ; k++) + { + nzaat += Len [k] ; + } + + AMD_DEBUG1 (("AMD nz in A+A', excluding diagonal (nzaat) = %g\n", + (double) nzaat)) ; + AMD_DEBUG1 ((" nzboth: "ID" nz: "ID" nzdiag: "ID" symmetry: %g\n", + nzboth, nz, nzdiag, sym)) ; + + if (Info != (double *) NULL) + { + Info [AMD_STATUS] = AMD_OK ; + Info [AMD_N] = n ; + Info [AMD_NZ] = nz ; + Info [AMD_SYMMETRY] = sym ; /* symmetry of pattern of A */ + Info [AMD_NZDIAG] = nzdiag ; /* nonzeros on diagonal of A */ + Info [AMD_NZ_A_PLUS_AT] = nzaat ; /* nonzeros in A+A' */ + } + + return (nzaat) ; +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_control.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_control.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,64 @@ +/* ========================================================================= */ +/* === AMD_control ========================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* User-callable. Prints the control parameters for AMD. See amd.h + * for details. If the Control array is not present, the defaults are + * printed instead. + */ + +#include "amd_internal.h" + +GLOBAL void AMD_control +( + double Control [ ] +) +{ + double alpha ; + Int aggressive ; + + if (Control != (double *) NULL) + { + alpha = Control [AMD_DENSE] ; + aggressive = Control [AMD_AGGRESSIVE] != 0 ; + } + else + { + alpha = AMD_DEFAULT_DENSE ; + aggressive = AMD_DEFAULT_AGGRESSIVE ; + } + + PRINTF (("\nAMD version %d.%d.%d, %s: approximate minimum degree ordering\n" + " dense row parameter: %g\n", AMD_MAIN_VERSION, AMD_SUB_VERSION, + AMD_SUBSUB_VERSION, AMD_DATE, alpha)) ; + + if (alpha < 0) + { + PRINTF ((" no rows treated as dense\n")) ; + } + else + { + PRINTF (( + " (rows with more than max (%g * sqrt (n), 16) entries are\n" + " considered \"dense\", and placed last in output permutation)\n", + alpha)) ; + } + + if (aggressive) + { + PRINTF ((" aggressive absorption: yes\n")) ; + } + else + { + PRINTF ((" aggressive absorption: no\n")) ; + } + + PRINTF ((" size of AMD integer: %d\n\n", sizeof (Int))) ; +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_defaults.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_defaults.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,38 @@ +/* ========================================================================= */ +/* === AMD_defaults ======================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* User-callable. Sets default control parameters for AMD. See amd.h + * for details. + */ + +#include "amd_internal.h" + +/* ========================================================================= */ +/* === AMD defaults ======================================================== */ +/* ========================================================================= */ + +GLOBAL void AMD_defaults +( + double Control [ ] +) +{ + Int i ; + + if (Control != (double *) NULL) + { + for (i = 0 ; i < AMD_CONTROL ; i++) + { + Control [i] = 0 ; + } + Control [AMD_DENSE] = AMD_DEFAULT_DENSE ; + Control [AMD_AGGRESSIVE] = AMD_DEFAULT_AGGRESSIVE ; + } +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_dump.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_dump.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,180 @@ +/* ========================================================================= */ +/* === AMD_dump ============================================================ */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Debugging routines for AMD. Not used if NDEBUG is not defined at compile- + * time (the default). See comments in amd_internal.h on how to enable + * debugging. Not user-callable. + */ + +#include "amd_internal.h" + +#ifndef NDEBUG + +/* This global variable is present only when debugging */ +GLOBAL Int AMD_debug = -999 ; /* default is no debug printing */ + +/* ========================================================================= */ +/* === AMD_debug_init ====================================================== */ +/* ========================================================================= */ + +/* Sets the debug print level, by reading the file debug.amd (if it exists) */ + +GLOBAL void AMD_debug_init ( char *s ) +{ + FILE *f ; + f = fopen ("debug.amd", "r") ; + if (f == (FILE *) NULL) + { + AMD_debug = -999 ; + } + else + { + fscanf (f, ID, &AMD_debug) ; + fclose (f) ; + } + if (AMD_debug >= 0) + { + printf ("%s: AMD_debug_init, D= "ID"\n", s, AMD_debug) ; + } +} + +/* ========================================================================= */ +/* === AMD_dump ============================================================ */ +/* ========================================================================= */ + +/* Dump AMD's data structure, except for the hash buckets. This routine + * cannot be called when the hash buckets are non-empty. + */ + +GLOBAL void AMD_dump ( + Int n, /* A is n-by-n */ + Int Pe [ ], /* pe [0..n-1]: index in iw of start of row i */ + Int Iw [ ], /* workspace of size iwlen, iwlen [0..pfree-1] + * holds the matrix on input */ + Int Len [ ], /* len [0..n-1]: length for row i */ + Int iwlen, /* length of iw */ + Int pfree, /* iw [pfree ... iwlen-1] is empty on input */ + Int Nv [ ], /* nv [0..n-1] */ + Int Next [ ], /* next [0..n-1] */ + Int Last [ ], /* last [0..n-1] */ + Int Head [ ], /* head [0..n-1] */ + Int Elen [ ], /* size n */ + Int Degree [ ], /* size n */ + Int W [ ], /* size n */ + Int nel +) +{ + Int i, pe, elen, nv, len, e, p, k, j, deg, w, cnt, ilast ; + + if (AMD_debug < 0) return ; + ASSERT (pfree <= iwlen) ; + AMD_DEBUG3 (("\nAMD dump, pfree: "ID"\n", pfree)) ; + for (i = 0 ; i < n ; i++) + { + pe = Pe [i] ; + elen = Elen [i] ; + nv = Nv [i] ; + len = Len [i] ; + w = W [i] ; + + if (elen >= EMPTY) + { + if (nv == 0) + { + AMD_DEBUG3 (("\nI "ID": nonprincipal: ", i)) ; + ASSERT (elen == EMPTY) ; + if (pe == EMPTY) + { + AMD_DEBUG3 ((" dense node\n")) ; + ASSERT (w == 1) ; + } + else + { + ASSERT (pe < EMPTY) ; + AMD_DEBUG3 ((" i "ID" -> parent "ID"\n", i, FLIP (Pe[i]))); + } + } + else + { + AMD_DEBUG3 (("\nI "ID": active principal supervariable:\n",i)); + AMD_DEBUG3 ((" nv(i): "ID" Flag: %d\n", nv, (nv < 0))) ; + ASSERT (elen >= 0) ; + ASSERT (nv > 0 && pe >= 0) ; + p = pe ; + AMD_DEBUG3 ((" e/s: ")) ; + if (elen == 0) AMD_DEBUG3 ((" : ")) ; + ASSERT (pe + len <= pfree) ; + for (k = 0 ; k < len ; k++) + { + j = Iw [p] ; + AMD_DEBUG3 ((" "ID"", j)) ; + ASSERT (j >= 0 && j < n) ; + if (k == elen-1) AMD_DEBUG3 ((" : ")) ; + p++ ; + } + AMD_DEBUG3 (("\n")) ; + } + } + else + { + e = i ; + if (w == 0) + { + AMD_DEBUG3 (("\nE "ID": absorbed element: w "ID"\n", e, w)) ; + ASSERT (nv > 0 && pe < 0) ; + AMD_DEBUG3 ((" e "ID" -> parent "ID"\n", e, FLIP (Pe [e]))) ; + } + else + { + AMD_DEBUG3 (("\nE "ID": unabsorbed element: w "ID"\n", e, w)) ; + ASSERT (nv > 0 && pe >= 0) ; + p = pe ; + AMD_DEBUG3 ((" : ")) ; + ASSERT (pe + len <= pfree) ; + for (k = 0 ; k < len ; k++) + { + j = Iw [p] ; + AMD_DEBUG3 ((" "ID"", j)) ; + ASSERT (j >= 0 && j < n) ; + p++ ; + } + AMD_DEBUG3 (("\n")) ; + } + } + } + + /* this routine cannot be called when the hash buckets are non-empty */ + AMD_DEBUG3 (("\nDegree lists:\n")) ; + if (nel >= 0) + { + cnt = 0 ; + for (deg = 0 ; deg < n ; deg++) + { + if (Head [deg] == EMPTY) continue ; + ilast = EMPTY ; + AMD_DEBUG3 ((ID": \n", deg)) ; + for (i = Head [deg] ; i != EMPTY ; i = Next [i]) + { + AMD_DEBUG3 ((" "ID" : next "ID" last "ID" deg "ID"\n", + i, Next [i], Last [i], Degree [i])) ; + ASSERT (i >= 0 && i < n && ilast == Last [i] && + deg == Degree [i]) ; + cnt += Nv [i] ; + ilast = i ; + } + AMD_DEBUG3 (("\n")) ; + } + ASSERT (cnt == n - nel) ; + } + +} + +#endif diff -r d59bea55db9b -r c445c931472f src/amd/amd_info.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_info.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,120 @@ +/* ========================================================================= */ +/* === AMD_info ============================================================ */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* User-callable. Prints the output statistics for AMD. See amd.h + * for details. If the Info array is not present, nothing is printed. + */ + +#include "amd_internal.h" + +#define PRI(format,x) { if (x >= 0) { PRINTF ((format, x)) ; }} + +GLOBAL void AMD_info +( + double Info [ ] +) +{ + double n, ndiv, nmultsubs_ldl, nmultsubs_lu, lnz, lnzd ; + + PRINTF (("\nAMD version %d.%d.%d, %s, results:\n", + AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION, AMD_DATE)) ; + + if (!Info) + { + return ; + } + + n = Info [AMD_N] ; + ndiv = Info [AMD_NDIV] ; + nmultsubs_ldl = Info [AMD_NMULTSUBS_LDL] ; + nmultsubs_lu = Info [AMD_NMULTSUBS_LU] ; + lnz = Info [AMD_LNZ] ; + lnzd = (n >= 0 && lnz >= 0) ? (n + lnz) : (-1) ; + + /* AMD return status */ + PRINTF ((" status: ")) ; + if (Info [AMD_STATUS] == AMD_OK) + { + PRINTF (("OK\n")) ; + } + else if (Info [AMD_STATUS] == AMD_OUT_OF_MEMORY) + { + PRINTF (("out of memory\n")) ; + } + else if (Info [AMD_STATUS] == AMD_INVALID) + { + PRINTF (("invalid matrix\n")) ; + } + else if (Info [AMD_STATUS] == AMD_OK_BUT_JUMBLED) + { + PRINTF (("OK, but jumbled\n")) ; + } + else + { + PRINTF (("unknown\n")) ; + } + + /* statistics about the input matrix */ + PRI (" n, dimension of A: %.20g\n", n); + PRI (" nz, number of nonzeros in A: %.20g\n", + Info [AMD_NZ]) ; + PRI (" symmetry of A: %.4f\n", + Info [AMD_SYMMETRY]) ; + PRI (" number of nonzeros on diagonal: %.20g\n", + Info [AMD_NZDIAG]) ; + PRI (" nonzeros in pattern of A+A' (excl. diagonal): %.20g\n", + Info [AMD_NZ_A_PLUS_AT]) ; + PRI (" # dense rows/columns of A+A': %.20g\n", + Info [AMD_NDENSE]) ; + + /* statistics about AMD's behavior */ + PRI (" memory used, in bytes: %.20g\n", + Info [AMD_MEMORY]) ; + PRI (" # of memory compactions: %.20g\n", + Info [AMD_NCMPA]) ; + + /* statistics about the ordering quality */ + PRINTF (("\n" + " The following approximate statistics are for a subsequent\n" + " factorization of A(P,P) + A(P,P)'. They are slight upper\n" + " bounds if there are no dense rows/columns in A+A', and become\n" + " looser if dense rows/columns exist.\n\n")) ; + + PRI (" nonzeros in L (excluding diagonal): %.20g\n", + lnz) ; + PRI (" nonzeros in L (including diagonal): %.20g\n", + lnzd) ; + PRI (" # divide operations for LDL' or LU: %.20g\n", + ndiv) ; + PRI (" # multiply-subtract operations for LDL': %.20g\n", + nmultsubs_ldl) ; + PRI (" # multiply-subtract operations for LU: %.20g\n", + nmultsubs_lu) ; + PRI (" max nz. in any column of L (incl. diagonal): %.20g\n", + Info [AMD_DMAX]) ; + + /* total flop counts for various factorizations */ + + if (n >= 0 && ndiv >= 0 && nmultsubs_ldl >= 0 && nmultsubs_lu >= 0) + { + PRINTF (("\n" + " chol flop count for real A, sqrt counted as 1 flop: %.20g\n" + " LDL' flop count for real A: %.20g\n" + " LDL' flop count for complex A: %.20g\n" + " LU flop count for real A (with no pivoting): %.20g\n" + " LU flop count for complex A (with no pivoting): %.20g\n\n", + n + ndiv + 2*nmultsubs_ldl, + ndiv + 2*nmultsubs_ldl, + 9*ndiv + 8*nmultsubs_ldl, + ndiv + 2*nmultsubs_lu, + 9*ndiv + 8*nmultsubs_lu)) ; + } +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_internal.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,113 @@ +/* amd_internal.h */ + +/* Written by Andrew Makhorin . */ + +#ifndef AMD_INTERNAL_H +#define AMD_INTERNAL_H + +/* AMD will be exceedingly slow when running in debug mode. */ +#if 1 +#define NDEBUG +#endif + +#include "amd.h" +#define _GLPSTD_STDIO +#include "glpenv.h" + +#define Int int +#define ID "%d" +#define Int_MAX INT_MAX + +#define SIZE_T_MAX ((size_t)(-1)) + +#define EMPTY (-1) +#define FLIP(i) (-(i)-2) +#define UNFLIP(i) ((i < EMPTY) ? FLIP (i) : (i)) + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define IMPLIES(p, q) (!(p) || (q)) + +#define GLOBAL + +#define AMD_order amd_order +#define AMD_defaults amd_defaults +#define AMD_control amd_control +#define AMD_info amd_info +#define AMD_1 amd_1 +#define AMD_2 amd_2 +#define AMD_valid amd_valid +#define AMD_aat amd_aat +#define AMD_postorder amd_postorder +#define AMD_post_tree amd_post_tree +#define AMD_dump amd_dump +#define AMD_debug amd_debug +#define AMD_debug_init amd_debug_init +#define AMD_preprocess amd_preprocess + +#define amd_malloc xmalloc +#if 0 /* 24/V-2009 */ +#define amd_free xfree +#else +#define amd_free(ptr) { if ((ptr) != NULL) xfree(ptr); } +#endif +#define amd_printf xprintf + +#define PRINTF(params) { amd_printf params; } + +#ifndef NDEBUG +#define ASSERT(expr) xassert(expr) +#define AMD_DEBUG0(params) { PRINTF(params); } +#define AMD_DEBUG1(params) { if (AMD_debug >= 1) PRINTF(params); } +#define AMD_DEBUG2(params) { if (AMD_debug >= 2) PRINTF(params); } +#define AMD_DEBUG3(params) { if (AMD_debug >= 3) PRINTF(params); } +#define AMD_DEBUG4(params) { if (AMD_debug >= 4) PRINTF(params); } +#else +#define ASSERT(expression) +#define AMD_DEBUG0(params) +#define AMD_DEBUG1(params) +#define AMD_DEBUG2(params) +#define AMD_DEBUG3(params) +#define AMD_DEBUG4(params) +#endif + +#define amd_aat _glp_amd_aat +size_t AMD_aat(Int n, const Int Ap[], const Int Ai[], Int Len[], + Int Tp[], double Info[]); + +#define amd_1 _glp_amd_1 +void AMD_1(Int n, const Int Ap[], const Int Ai[], Int P[], Int Pinv[], + Int Len[], Int slen, Int S[], double Control[], double Info[]); + +#define amd_postorder _glp_amd_postorder +void AMD_postorder(Int nn, Int Parent[], Int Npiv[], Int Fsize[], + Int Order[], Int Child[], Int Sibling[], Int Stack[]); + +#define amd_post_tree _glp_amd_post_tree +#ifndef NDEBUG +Int AMD_post_tree(Int root, Int k, Int Child[], const Int Sibling[], + Int Order[], Int Stack[], Int nn); +#else +Int AMD_post_tree(Int root, Int k, Int Child[], const Int Sibling[], + Int Order[], Int Stack[]); +#endif + +#define amd_preprocess _glp_amd_preprocess +void AMD_preprocess(Int n, const Int Ap[], const Int Ai[], Int Rp[], + Int Ri[], Int W[], Int Flag[]); + +#define amd_debug _glp_amd_debug +extern Int AMD_debug; + +#define amd_debug_init _glp_amd_debug_init +void AMD_debug_init(char *s); + +#define amd_dump _glp_amd_dump +void AMD_dump(Int n, Int Pe[], Int Iw[], Int Len[], Int iwlen, + Int pfree, Int Nv[], Int Next[], Int Last[], Int Head[], + Int Elen[], Int Degree[], Int W[], Int nel); + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/amd/amd_order.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_order.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,200 @@ +/* ========================================================================= */ +/* === AMD_order =========================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* User-callable AMD minimum degree ordering routine. See amd.h for + * documentation. + */ + +#include "amd_internal.h" + +/* ========================================================================= */ +/* === AMD_order =========================================================== */ +/* ========================================================================= */ + +GLOBAL Int AMD_order +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int P [ ], + double Control [ ], + double Info [ ] +) +{ + Int *Len, *S, nz, i, *Pinv, info, status, *Rp, *Ri, *Cp, *Ci, ok ; + size_t nzaat, slen ; + double mem = 0 ; + +#ifndef NDEBUG + AMD_debug_init ("amd") ; +#endif + + /* clear the Info array, if it exists */ + info = Info != (double *) NULL ; + if (info) + { + for (i = 0 ; i < AMD_INFO ; i++) + { + Info [i] = EMPTY ; + } + Info [AMD_N] = n ; + Info [AMD_STATUS] = AMD_OK ; + } + + /* make sure inputs exist and n is >= 0 */ + if (Ai == (Int *) NULL || Ap == (Int *) NULL || P == (Int *) NULL || n < 0) + { + if (info) Info [AMD_STATUS] = AMD_INVALID ; + return (AMD_INVALID) ; /* arguments are invalid */ + } + + if (n == 0) + { + return (AMD_OK) ; /* n is 0 so there's nothing to do */ + } + + nz = Ap [n] ; + if (info) + { + Info [AMD_NZ] = nz ; + } + if (nz < 0) + { + if (info) Info [AMD_STATUS] = AMD_INVALID ; + return (AMD_INVALID) ; + } + + /* check if n or nz will cause size_t overflow */ + if (((size_t) n) >= SIZE_T_MAX / sizeof (Int) + || ((size_t) nz) >= SIZE_T_MAX / sizeof (Int)) + { + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; /* problem too large */ + } + + /* check the input matrix: AMD_OK, AMD_INVALID, or AMD_OK_BUT_JUMBLED */ + status = AMD_valid (n, n, Ap, Ai) ; + + if (status == AMD_INVALID) + { + if (info) Info [AMD_STATUS] = AMD_INVALID ; + return (AMD_INVALID) ; /* matrix is invalid */ + } + + /* allocate two size-n integer workspaces */ + Len = amd_malloc (n * sizeof (Int)) ; + Pinv = amd_malloc (n * sizeof (Int)) ; + mem += n ; + mem += n ; + if (!Len || !Pinv) + { + /* :: out of memory :: */ + amd_free (Len) ; + amd_free (Pinv) ; + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + + if (status == AMD_OK_BUT_JUMBLED) + { + /* sort the input matrix and remove duplicate entries */ + AMD_DEBUG1 (("Matrix is jumbled\n")) ; + Rp = amd_malloc ((n+1) * sizeof (Int)) ; + Ri = amd_malloc (MAX (nz,1) * sizeof (Int)) ; + mem += (n+1) ; + mem += MAX (nz,1) ; + if (!Rp || !Ri) + { + /* :: out of memory :: */ + amd_free (Rp) ; + amd_free (Ri) ; + amd_free (Len) ; + amd_free (Pinv) ; + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + /* use Len and Pinv as workspace to create R = A' */ + AMD_preprocess (n, Ap, Ai, Rp, Ri, Len, Pinv) ; + Cp = Rp ; + Ci = Ri ; + } + else + { + /* order the input matrix as-is. No need to compute R = A' first */ + Rp = NULL ; + Ri = NULL ; + Cp = (Int *) Ap ; + Ci = (Int *) Ai ; + } + + /* --------------------------------------------------------------------- */ + /* determine the symmetry and count off-diagonal nonzeros in A+A' */ + /* --------------------------------------------------------------------- */ + + nzaat = AMD_aat (n, Cp, Ci, Len, P, Info) ; + AMD_DEBUG1 (("nzaat: %g\n", (double) nzaat)) ; + ASSERT ((MAX (nz-n, 0) <= nzaat) && (nzaat <= 2 * (size_t) nz)) ; + + /* --------------------------------------------------------------------- */ + /* allocate workspace for matrix, elbow room, and 6 size-n vectors */ + /* --------------------------------------------------------------------- */ + + S = NULL ; + slen = nzaat ; /* space for matrix */ + ok = ((slen + nzaat/5) >= slen) ; /* check for size_t overflow */ + slen += nzaat/5 ; /* add elbow room */ + for (i = 0 ; ok && i < 7 ; i++) + { + ok = ((slen + n) > slen) ; /* check for size_t overflow */ + slen += n ; /* size-n elbow room, 6 size-n work */ + } + mem += slen ; + ok = ok && (slen < SIZE_T_MAX / sizeof (Int)) ; /* check for overflow */ + ok = ok && (slen < Int_MAX) ; /* S[i] for Int i must be OK */ + if (ok) + { + S = amd_malloc (slen * sizeof (Int)) ; + } + AMD_DEBUG1 (("slen %g\n", (double) slen)) ; + if (!S) + { + /* :: out of memory :: (or problem too large) */ + amd_free (Rp) ; + amd_free (Ri) ; + amd_free (Len) ; + amd_free (Pinv) ; + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + if (info) + { + /* memory usage, in bytes. */ + Info [AMD_MEMORY] = mem * sizeof (Int) ; + } + + /* --------------------------------------------------------------------- */ + /* order the matrix */ + /* --------------------------------------------------------------------- */ + + AMD_1 (n, Cp, Ci, P, Pinv, Len, slen, S, Control, Info) ; + + /* --------------------------------------------------------------------- */ + /* free the workspace */ + /* --------------------------------------------------------------------- */ + + amd_free (Rp) ; + amd_free (Ri) ; + amd_free (Len) ; + amd_free (Pinv) ; + amd_free (S) ; + if (info) Info [AMD_STATUS] = status ; + return (status) ; /* successful ordering */ +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_post_tree.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_post_tree.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,121 @@ +/* ========================================================================= */ +/* === AMD_post_tree ======================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Post-ordering of a supernodal elimination tree. */ + +#include "amd_internal.h" + +GLOBAL Int AMD_post_tree +( + Int root, /* root of the tree */ + Int k, /* start numbering at k */ + Int Child [ ], /* input argument of size nn, undefined on + * output. Child [i] is the head of a link + * list of all nodes that are children of node + * i in the tree. */ + const Int Sibling [ ], /* input argument of size nn, not modified. + * If f is a node in the link list of the + * children of node i, then Sibling [f] is the + * next child of node i. + */ + Int Order [ ], /* output order, of size nn. Order [i] = k + * if node i is the kth node of the reordered + * tree. */ + Int Stack [ ] /* workspace of size nn */ +#ifndef NDEBUG + , Int nn /* nodes are in the range 0..nn-1. */ +#endif +) +{ + Int f, head, h, i ; + +#if 0 + /* --------------------------------------------------------------------- */ + /* recursive version (Stack [ ] is not used): */ + /* --------------------------------------------------------------------- */ + + /* this is simple, but can caouse stack overflow if nn is large */ + i = root ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + k = AMD_post_tree (f, k, Child, Sibling, Order, Stack, nn) ; + } + Order [i] = k++ ; + return (k) ; +#endif + + /* --------------------------------------------------------------------- */ + /* non-recursive version, using an explicit stack */ + /* --------------------------------------------------------------------- */ + + /* push root on the stack */ + head = 0 ; + Stack [0] = root ; + + while (head >= 0) + { + /* get head of stack */ + ASSERT (head < nn) ; + i = Stack [head] ; + AMD_DEBUG1 (("head of stack "ID" \n", i)) ; + ASSERT (i >= 0 && i < nn) ; + + if (Child [i] != EMPTY) + { + /* the children of i are not yet ordered */ + /* push each child onto the stack in reverse order */ + /* so that small ones at the head of the list get popped first */ + /* and the biggest one at the end of the list gets popped last */ + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + head++ ; + ASSERT (head < nn) ; + ASSERT (f >= 0 && f < nn) ; + } + h = head ; + ASSERT (head < nn) ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + ASSERT (h > 0) ; + Stack [h--] = f ; + AMD_DEBUG1 (("push "ID" on stack\n", f)) ; + ASSERT (f >= 0 && f < nn) ; + } + ASSERT (Stack [h] == i) ; + + /* delete child list so that i gets ordered next time we see it */ + Child [i] = EMPTY ; + } + else + { + /* the children of i (if there were any) are already ordered */ + /* remove i from the stack and order it. Front i is kth front */ + head-- ; + AMD_DEBUG1 (("pop "ID" order "ID"\n", i, k)) ; + Order [i] = k++ ; + ASSERT (k <= nn) ; + } + +#ifndef NDEBUG + AMD_DEBUG1 (("\nStack:")) ; + for (h = head ; h >= 0 ; h--) + { + Int j = Stack [h] ; + AMD_DEBUG1 ((" "ID, j)) ; + ASSERT (j >= 0 && j < nn) ; + } + AMD_DEBUG1 (("\n\n")) ; + ASSERT (head < nn) ; +#endif + + } + return (k) ; +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_postorder.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_postorder.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,207 @@ +/* ========================================================================= */ +/* === AMD_postorder ======================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Perform a postordering (via depth-first search) of an assembly tree. */ + +#include "amd_internal.h" + +GLOBAL void AMD_postorder +( + /* inputs, not modified on output: */ + Int nn, /* nodes are in the range 0..nn-1 */ + Int Parent [ ], /* Parent [j] is the parent of j, or EMPTY if root */ + Int Nv [ ], /* Nv [j] > 0 number of pivots represented by node j, + * or zero if j is not a node. */ + Int Fsize [ ], /* Fsize [j]: size of node j */ + + /* output, not defined on input: */ + Int Order [ ], /* output post-order */ + + /* workspaces of size nn: */ + Int Child [ ], + Int Sibling [ ], + Int Stack [ ] +) +{ + Int i, j, k, parent, frsize, f, fprev, maxfrsize, bigfprev, bigf, fnext ; + + for (j = 0 ; j < nn ; j++) + { + Child [j] = EMPTY ; + Sibling [j] = EMPTY ; + } + + /* --------------------------------------------------------------------- */ + /* place the children in link lists - bigger elements tend to be last */ + /* --------------------------------------------------------------------- */ + + for (j = nn-1 ; j >= 0 ; j--) + { + if (Nv [j] > 0) + { + /* this is an element */ + parent = Parent [j] ; + if (parent != EMPTY) + { + /* place the element in link list of the children its parent */ + /* bigger elements will tend to be at the end of the list */ + Sibling [j] = Child [parent] ; + Child [parent] = j ; + } + } + } + +#ifndef NDEBUG + { + Int nels, ff, nchild ; + AMD_DEBUG1 (("\n\n================================ AMD_postorder:\n")); + nels = 0 ; + for (j = 0 ; j < nn ; j++) + { + if (Nv [j] > 0) + { + AMD_DEBUG1 (( ""ID" : nels "ID" npiv "ID" size "ID + " parent "ID" maxfr "ID"\n", j, nels, + Nv [j], Fsize [j], Parent [j], Fsize [j])) ; + /* this is an element */ + /* dump the link list of children */ + nchild = 0 ; + AMD_DEBUG1 ((" Children: ")) ; + for (ff = Child [j] ; ff != EMPTY ; ff = Sibling [ff]) + { + AMD_DEBUG1 ((ID" ", ff)) ; + ASSERT (Parent [ff] == j) ; + nchild++ ; + ASSERT (nchild < nn) ; + } + AMD_DEBUG1 (("\n")) ; + parent = Parent [j] ; + if (parent != EMPTY) + { + ASSERT (Nv [parent] > 0) ; + } + nels++ ; + } + } + } + AMD_DEBUG1 (("\n\nGo through the children of each node, and put\n" + "the biggest child last in each list:\n")) ; +#endif + + /* --------------------------------------------------------------------- */ + /* place the largest child last in the list of children for each node */ + /* --------------------------------------------------------------------- */ + + for (i = 0 ; i < nn ; i++) + { + if (Nv [i] > 0 && Child [i] != EMPTY) + { + +#ifndef NDEBUG + Int nchild ; + AMD_DEBUG1 (("Before partial sort, element "ID"\n", i)) ; + nchild = 0 ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + ASSERT (f >= 0 && f < nn) ; + AMD_DEBUG1 ((" f: "ID" size: "ID"\n", f, Fsize [f])) ; + nchild++ ; + ASSERT (nchild <= nn) ; + } +#endif + + /* find the biggest element in the child list */ + fprev = EMPTY ; + maxfrsize = EMPTY ; + bigfprev = EMPTY ; + bigf = EMPTY ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + ASSERT (f >= 0 && f < nn) ; + frsize = Fsize [f] ; + if (frsize >= maxfrsize) + { + /* this is the biggest seen so far */ + maxfrsize = frsize ; + bigfprev = fprev ; + bigf = f ; + } + fprev = f ; + } + ASSERT (bigf != EMPTY) ; + + fnext = Sibling [bigf] ; + + AMD_DEBUG1 (("bigf "ID" maxfrsize "ID" bigfprev "ID" fnext "ID + " fprev " ID"\n", bigf, maxfrsize, bigfprev, fnext, fprev)) ; + + if (fnext != EMPTY) + { + /* if fnext is EMPTY then bigf is already at the end of list */ + + if (bigfprev == EMPTY) + { + /* delete bigf from the element of the list */ + Child [i] = fnext ; + } + else + { + /* delete bigf from the middle of the list */ + Sibling [bigfprev] = fnext ; + } + + /* put bigf at the end of the list */ + Sibling [bigf] = EMPTY ; + ASSERT (Child [i] != EMPTY) ; + ASSERT (fprev != bigf) ; + ASSERT (fprev != EMPTY) ; + Sibling [fprev] = bigf ; + } + +#ifndef NDEBUG + AMD_DEBUG1 (("After partial sort, element "ID"\n", i)) ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + ASSERT (f >= 0 && f < nn) ; + AMD_DEBUG1 ((" "ID" "ID"\n", f, Fsize [f])) ; + ASSERT (Nv [f] > 0) ; + nchild-- ; + } + ASSERT (nchild == 0) ; +#endif + + } + } + + /* --------------------------------------------------------------------- */ + /* postorder the assembly tree */ + /* --------------------------------------------------------------------- */ + + for (i = 0 ; i < nn ; i++) + { + Order [i] = EMPTY ; + } + + k = 0 ; + + for (i = 0 ; i < nn ; i++) + { + if (Parent [i] == EMPTY && Nv [i] > 0) + { + AMD_DEBUG1 (("Root of assembly tree "ID"\n", i)) ; + k = AMD_post_tree (i, k, Child, Sibling, Order, Stack +#ifndef NDEBUG + , nn +#endif + ) ; + } + } +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_preprocess.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_preprocess.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,119 @@ +/* ========================================================================= */ +/* === AMD_preprocess ====================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Sorts, removes duplicate entries, and transposes from the nonzero pattern of + * a column-form matrix A, to obtain the matrix R. The input matrix can have + * duplicate entries and/or unsorted columns (AMD_valid (n,Ap,Ai) must not be + * AMD_INVALID). + * + * This input condition is NOT checked. This routine is not user-callable. + */ + +#include "amd_internal.h" + +/* ========================================================================= */ +/* === AMD_preprocess ====================================================== */ +/* ========================================================================= */ + +/* AMD_preprocess does not check its input for errors or allocate workspace. + * On input, the condition (AMD_valid (n,n,Ap,Ai) != AMD_INVALID) must hold. + */ + +GLOBAL void AMD_preprocess +( + Int n, /* input matrix: A is n-by-n */ + const Int Ap [ ], /* size n+1 */ + const Int Ai [ ], /* size nz = Ap [n] */ + + /* output matrix R: */ + Int Rp [ ], /* size n+1 */ + Int Ri [ ], /* size nz (or less, if duplicates present) */ + + Int W [ ], /* workspace of size n */ + Int Flag [ ] /* workspace of size n */ +) +{ + + /* --------------------------------------------------------------------- */ + /* local variables */ + /* --------------------------------------------------------------------- */ + + Int i, j, p, p2 ; + + ASSERT (AMD_valid (n, n, Ap, Ai) != AMD_INVALID) ; + + /* --------------------------------------------------------------------- */ + /* count the entries in each row of A (excluding duplicates) */ + /* --------------------------------------------------------------------- */ + + for (i = 0 ; i < n ; i++) + { + W [i] = 0 ; /* # of nonzeros in row i (excl duplicates) */ + Flag [i] = EMPTY ; /* Flag [i] = j if i appears in column j */ + } + for (j = 0 ; j < n ; j++) + { + p2 = Ap [j+1] ; + for (p = Ap [j] ; p < p2 ; p++) + { + i = Ai [p] ; + if (Flag [i] != j) + { + /* row index i has not yet appeared in column j */ + W [i]++ ; /* one more entry in row i */ + Flag [i] = j ; /* flag row index i as appearing in col j*/ + } + } + } + + /* --------------------------------------------------------------------- */ + /* compute the row pointers for R */ + /* --------------------------------------------------------------------- */ + + Rp [0] = 0 ; + for (i = 0 ; i < n ; i++) + { + Rp [i+1] = Rp [i] + W [i] ; + } + for (i = 0 ; i < n ; i++) + { + W [i] = Rp [i] ; + Flag [i] = EMPTY ; + } + + /* --------------------------------------------------------------------- */ + /* construct the row form matrix R */ + /* --------------------------------------------------------------------- */ + + /* R = row form of pattern of A */ + for (j = 0 ; j < n ; j++) + { + p2 = Ap [j+1] ; + for (p = Ap [j] ; p < p2 ; p++) + { + i = Ai [p] ; + if (Flag [i] != j) + { + /* row index i has not yet appeared in column j */ + Ri [W [i]++] = j ; /* put col j in row i */ + Flag [i] = j ; /* flag row index i as appearing in col j*/ + } + } + } + +#ifndef NDEBUG + ASSERT (AMD_valid (n, n, Rp, Ri) == AMD_OK) ; + for (j = 0 ; j < n ; j++) + { + ASSERT (W [j] == Rp [j+1]) ; + } +#endif +} diff -r d59bea55db9b -r c445c931472f src/amd/amd_valid.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/amd/amd_valid.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,93 @@ +/* ========================================================================= */ +/* === AMD_valid =========================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Check if a column-form matrix is valid or not. The matrix A is + * n_row-by-n_col. The row indices of entries in column j are in + * Ai [Ap [j] ... Ap [j+1]-1]. Required conditions are: + * + * n_row >= 0 + * n_col >= 0 + * nz = Ap [n_col] >= 0 number of entries in the matrix + * Ap [0] == 0 + * Ap [j] <= Ap [j+1] for all j in the range 0 to n_col. + * Ai [0 ... nz-1] must be in the range 0 to n_row-1. + * + * If any of the above conditions hold, AMD_INVALID is returned. If the + * following condition holds, AMD_OK_BUT_JUMBLED is returned (a warning, + * not an error): + * + * row indices in Ai [Ap [j] ... Ap [j+1]-1] are not sorted in ascending + * order, and/or duplicate entries exist. + * + * Otherwise, AMD_OK is returned. + * + * In v1.2 and earlier, this function returned TRUE if the matrix was valid + * (now returns AMD_OK), or FALSE otherwise (now returns AMD_INVALID or + * AMD_OK_BUT_JUMBLED). + */ + +#include "amd_internal.h" + +GLOBAL Int AMD_valid +( + /* inputs, not modified on output: */ + Int n_row, /* A is n_row-by-n_col */ + Int n_col, + const Int Ap [ ], /* column pointers of A, of size n_col+1 */ + const Int Ai [ ] /* row indices of A, of size nz = Ap [n_col] */ +) +{ + Int nz, j, p1, p2, ilast, i, p, result = AMD_OK ; + + if (n_row < 0 || n_col < 0 || Ap == NULL || Ai == NULL) + { + return (AMD_INVALID) ; + } + nz = Ap [n_col] ; + if (Ap [0] != 0 || nz < 0) + { + /* column pointers must start at Ap [0] = 0, and Ap [n] must be >= 0 */ + AMD_DEBUG0 (("column 0 pointer bad or nz < 0\n")) ; + return (AMD_INVALID) ; + } + for (j = 0 ; j < n_col ; j++) + { + p1 = Ap [j] ; + p2 = Ap [j+1] ; + AMD_DEBUG2 (("\nColumn: "ID" p1: "ID" p2: "ID"\n", j, p1, p2)) ; + if (p1 > p2) + { + /* column pointers must be ascending */ + AMD_DEBUG0 (("column "ID" pointer bad\n", j)) ; + return (AMD_INVALID) ; + } + ilast = EMPTY ; + for (p = p1 ; p < p2 ; p++) + { + i = Ai [p] ; + AMD_DEBUG3 (("row: "ID"\n", i)) ; + if (i < 0 || i >= n_row) + { + /* row index out of range */ + AMD_DEBUG0 (("index out of range, col "ID" row "ID"\n", j, i)); + return (AMD_INVALID) ; + } + if (i <= ilast) + { + /* row index unsorted, or duplicate entry present */ + AMD_DEBUG1 (("index unsorted/dupl col "ID" row "ID"\n", j, i)); + result = AMD_OK_BUT_JUMBLED ; + } + ilast = i ; + } + } + return (result) ; +} diff -r d59bea55db9b -r c445c931472f src/colamd/COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/colamd/COPYING Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff -r d59bea55db9b -r c445c931472f src/colamd/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/colamd/README Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,98 @@ +NOTE: Files in this subdirectory are NOT part of the GLPK package, but + are used with GLPK. + + The original code was modified according to GLPK requirements by + Andrew Makhorin . +************************************************************************ +COLAMD/SYMAMD Version 2.7, Copyright (C) 1998-2007, Timothy A. Davis, +All Rights Reserved. + +Description: + + colamd: an approximate minimum degree column ordering algorithm, + for LU factorization of symmetric or unsymmetric matrices, + QR factorization, least squares, interior point methods for + linear programming problems, and other related problems. + + symamd: an approximate minimum degree ordering algorithm for + Cholesky factorization of symmetric matrices. + +Purpose: + + Colamd computes a permutation Q such that the Cholesky factorization + of (AQ)'(AQ) has less fill-in and requires fewer floating point + operations than A'A. This also provides a good ordering for sparse + partial pivoting methods, P(AQ) = LU, where Q is computed prior to + numerical factorization, and P is computed during numerical + factorization via conventional partial pivoting with row + interchanges. Colamd is the column ordering method used in SuperLU, + part of the ScaLAPACK library. It is also available as built-in + function in MATLAB Version 6, available from MathWorks, Inc. + (http://www.mathworks.com). This routine can be used in place of + colmmd in MATLAB. + + Symamd computes a permutation P of a symmetric matrix A such that + the Cholesky factorization of PAP' has less fill-in and requires + fewer floating point operations than A. Symamd constructs a matrix + M such that M'M has the same nonzero pattern of A, and then orders + the columns of M using colmmd. The column ordering of M is then + returned as the row and column ordering P of A. + +Authors: + + The authors of the code itself are Stefan I. Larimore and Timothy A. + Davis (davis at cise.ufl.edu), University of Florida. The algorithm + was developed in collaboration with John Gilbert, Xerox PARC, and + Esmond Ng, Oak Ridge National Laboratory. + +Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974 and DMS-9803599. + +License: + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA. + + Permission is hereby granted to use or copy this program under the + terms of the GNU LGPL, provided that the Copyright, this License, + and the Availability of the original version is retained on all + copies. User documentation of any code that uses this code or any + modified version of this code must cite the Copyright, this License, + the Availability note, and "Used by permission." Permission to + modify the code and to distribute modified code is granted, provided + the Copyright, this License, and the Availability note are retained, + and a notice that the code was modified is included. + + COLAMD is also available under alternate licenses, contact T. Davis + for details. + +Availability: + + The colamd/symamd library is available at: + + http://www.cise.ufl.edu/research/sparse/colamd/ + +References: + + T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, An approximate + column minimum degree ordering algorithm, ACM Transactions on + Mathematical Software, vol. 30, no. 3., pp. 353-376, 2004. + + T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: + COLAMD, an approximate column minimum degree ordering algorithm, ACM + Transactions on Mathematical Software, vol. 30, no. 3., pp. 377-380, + 2004. diff -r d59bea55db9b -r c445c931472f src/colamd/colamd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/colamd/colamd.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,3622 @@ +/* ========================================================================== */ +/* === colamd/symamd - a sparse matrix column ordering algorithm ============ */ +/* ========================================================================== */ + +/* COLAMD / SYMAMD + + colamd: an approximate minimum degree column ordering algorithm, + for LU factorization of symmetric or unsymmetric matrices, + QR factorization, least squares, interior point methods for + linear programming problems, and other related problems. + + symamd: an approximate minimum degree ordering algorithm for Cholesky + factorization of symmetric matrices. + + Purpose: + + Colamd computes a permutation Q such that the Cholesky factorization of + (AQ)'(AQ) has less fill-in and requires fewer floating point operations + than A'A. This also provides a good ordering for sparse partial + pivoting methods, P(AQ) = LU, where Q is computed prior to numerical + factorization, and P is computed during numerical factorization via + conventional partial pivoting with row interchanges. Colamd is the + column ordering method used in SuperLU, part of the ScaLAPACK library. + It is also available as built-in function in MATLAB Version 6, + available from MathWorks, Inc. (http://www.mathworks.com). This + routine can be used in place of colmmd in MATLAB. + + Symamd computes a permutation P of a symmetric matrix A such that the + Cholesky factorization of PAP' has less fill-in and requires fewer + floating point operations than A. Symamd constructs a matrix M such + that M'M has the same nonzero pattern of A, and then orders the columns + of M using colmmd. The column ordering of M is then returned as the + row and column ordering P of A. + + Authors: + + The authors of the code itself are Stefan I. Larimore and Timothy A. + Davis (davis at cise.ufl.edu), University of Florida. The algorithm was + developed in collaboration with John Gilbert, Xerox PARC, and Esmond + Ng, Oak Ridge National Laboratory. + + Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974 and DMS-9803599. + + Copyright and License: + + Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved. + COLAMD is also available under alternate licenses, contact T. Davis + for details. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + + Permission is hereby granted to use or copy this program under the + terms of the GNU LGPL, provided that the Copyright, this License, + and the Availability of the original version is retained on all copies. + User documentation of any code that uses this code or any modified + version of this code must cite the Copyright, this License, the + Availability note, and "Used by permission." Permission to modify + the code and to distribute modified code is granted, provided the + Copyright, this License, and the Availability note are retained, + and a notice that the code was modified is included. + + Availability: + + The colamd/symamd library is available at + + http://www.cise.ufl.edu/research/sparse/colamd/ + + This is the http://www.cise.ufl.edu/research/sparse/colamd/colamd.c + file. It requires the colamd.h file. It is required by the colamdmex.c + and symamdmex.c files, for the MATLAB interface to colamd and symamd. + Appears as ACM Algorithm 836. + + See the ChangeLog file for changes since Version 1.0. + + References: + + T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, An approximate column + minimum degree ordering algorithm, ACM Transactions on Mathematical + Software, vol. 30, no. 3., pp. 353-376, 2004. + + T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD, + an approximate column minimum degree ordering algorithm, ACM + Transactions on Mathematical Software, vol. 30, no. 3., pp. 377-380, + 2004. + +*/ + +/* ========================================================================== */ +/* === Description of user-callable routines ================================ */ +/* ========================================================================== */ + +/* COLAMD includes both int and UF_long versions of all its routines. The + * description below is for the int version. For UF_long, all int arguments + * become UF_long. UF_long is normally defined as long, except for WIN64. + + ---------------------------------------------------------------------------- + colamd_recommended: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + size_t colamd_recommended (int nnz, int n_row, int n_col) ; + size_t colamd_l_recommended (UF_long nnz, UF_long n_row, + UF_long n_col) ; + + Purpose: + + Returns recommended value of Alen for use by colamd. Returns 0 + if any input argument is negative. The use of this routine + is optional. Not needed for symamd, which dynamically allocates + its own memory. + + Note that in v2.4 and earlier, these routines returned int or long. + They now return a value of type size_t. + + Arguments (all input arguments): + + int nnz ; Number of nonzeros in the matrix A. This must + be the same value as p [n_col] in the call to + colamd - otherwise you will get a wrong value + of the recommended memory to use. + + int n_row ; Number of rows in the matrix A. + + int n_col ; Number of columns in the matrix A. + + ---------------------------------------------------------------------------- + colamd_set_defaults: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + colamd_set_defaults (double knobs [COLAMD_KNOBS]) ; + colamd_l_set_defaults (double knobs [COLAMD_KNOBS]) ; + + Purpose: + + Sets the default parameters. The use of this routine is optional. + + Arguments: + + double knobs [COLAMD_KNOBS] ; Output only. + + NOTE: the meaning of the dense row/col knobs has changed in v2.4 + + knobs [0] and knobs [1] control dense row and col detection: + + Colamd: rows with more than + max (16, knobs [COLAMD_DENSE_ROW] * sqrt (n_col)) + entries are removed prior to ordering. Columns with more than + max (16, knobs [COLAMD_DENSE_COL] * sqrt (MIN (n_row,n_col))) + entries are removed prior to + ordering, and placed last in the output column ordering. + + Symamd: uses only knobs [COLAMD_DENSE_ROW], which is knobs [0]. + Rows and columns with more than + max (16, knobs [COLAMD_DENSE_ROW] * sqrt (n)) + entries are removed prior to ordering, and placed last in the + output ordering. + + COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1, + respectively, in colamd.h. Default values of these two knobs + are both 10. Currently, only knobs [0] and knobs [1] are + used, but future versions may use more knobs. If so, they will + be properly set to their defaults by the future version of + colamd_set_defaults, so that the code that calls colamd will + not need to change, assuming that you either use + colamd_set_defaults, or pass a (double *) NULL pointer as the + knobs array to colamd or symamd. + + knobs [2]: aggressive absorption + + knobs [COLAMD_AGGRESSIVE] controls whether or not to do + aggressive absorption during the ordering. Default is TRUE. + + + ---------------------------------------------------------------------------- + colamd: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + int colamd (int n_row, int n_col, int Alen, int *A, int *p, + double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS]) ; + UF_long colamd_l (UF_long n_row, UF_long n_col, UF_long Alen, + UF_long *A, UF_long *p, double knobs [COLAMD_KNOBS], + UF_long stats [COLAMD_STATS]) ; + + Purpose: + + Computes a column ordering (Q) of A such that P(AQ)=LU or + (AQ)'AQ=LL' have less fill-in and require fewer floating point + operations than factorizing the unpermuted matrix A or A'A, + respectively. + + Returns: + + TRUE (1) if successful, FALSE (0) otherwise. + + Arguments: + + int n_row ; Input argument. + + Number of rows in the matrix A. + Restriction: n_row >= 0. + Colamd returns FALSE if n_row is negative. + + int n_col ; Input argument. + + Number of columns in the matrix A. + Restriction: n_col >= 0. + Colamd returns FALSE if n_col is negative. + + int Alen ; Input argument. + + Restriction (see note): + Alen >= 2*nnz + 6*(n_col+1) + 4*(n_row+1) + n_col + Colamd returns FALSE if these conditions are not met. + + Note: this restriction makes an modest assumption regarding + the size of the two typedef's structures in colamd.h. + We do, however, guarantee that + + Alen >= colamd_recommended (nnz, n_row, n_col) + + will be sufficient. Note: the macro version does not check + for integer overflow, and thus is not recommended. Use + the colamd_recommended routine instead. + + int A [Alen] ; Input argument, undefined on output. + + A is an integer array of size Alen. Alen must be at least as + large as the bare minimum value given above, but this is very + low, and can result in excessive run time. For best + performance, we recommend that Alen be greater than or equal to + colamd_recommended (nnz, n_row, n_col), which adds + nnz/5 to the bare minimum value given above. + + On input, the row indices of the entries in column c of the + matrix are held in A [(p [c]) ... (p [c+1]-1)]. The row indices + in a given column c need not be in ascending order, and + duplicate row indices may be be present. However, colamd will + work a little faster if both of these conditions are met + (Colamd puts the matrix into this format, if it finds that the + the conditions are not met). + + The matrix is 0-based. That is, rows are in the range 0 to + n_row-1, and columns are in the range 0 to n_col-1. Colamd + returns FALSE if any row index is out of range. + + The contents of A are modified during ordering, and are + undefined on output. + + int p [n_col+1] ; Both input and output argument. + + p is an integer array of size n_col+1. On input, it holds the + "pointers" for the column form of the matrix A. Column c of + the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first + entry, p [0], must be zero, and p [c] <= p [c+1] must hold + for all c in the range 0 to n_col-1. The value p [n_col] is + thus the total number of entries in the pattern of the matrix A. + Colamd returns FALSE if these conditions are not met. + + On output, if colamd returns TRUE, the array p holds the column + permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is + the first column index in the new ordering, and p [n_col-1] is + the last. That is, p [k] = j means that column j of A is the + kth pivot column, in AQ, where k is in the range 0 to n_col-1 + (p [0] = j means that column j of A is the first column in AQ). + + If colamd returns FALSE, then no permutation is returned, and + p is undefined on output. + + double knobs [COLAMD_KNOBS] ; Input argument. + + See colamd_set_defaults for a description. + + int stats [COLAMD_STATS] ; Output argument. + + Statistics on the ordering, and error status. + See colamd.h for related definitions. + Colamd returns FALSE if stats is not present. + + stats [0]: number of dense or empty rows ignored. + + stats [1]: number of dense or empty columns ignored (and + ordered last in the output permutation p) + Note that a row can become "empty" if it + contains only "dense" and/or "empty" columns, + and similarly a column can become "empty" if it + only contains "dense" and/or "empty" rows. + + stats [2]: number of garbage collections performed. + This can be excessively high if Alen is close + to the minimum required value. + + stats [3]: status code. < 0 is an error code. + > 1 is a warning or notice. + + 0 OK. Each column of the input matrix contained + row indices in increasing order, with no + duplicates. + + 1 OK, but columns of input matrix were jumbled + (unsorted columns or duplicate entries). Colamd + had to do some extra work to sort the matrix + first and remove duplicate entries, but it + still was able to return a valid permutation + (return value of colamd was TRUE). + + stats [4]: highest numbered column that + is unsorted or has duplicate + entries. + stats [5]: last seen duplicate or + unsorted row index. + stats [6]: number of duplicate or + unsorted row indices. + + -1 A is a null pointer + + -2 p is a null pointer + + -3 n_row is negative + + stats [4]: n_row + + -4 n_col is negative + + stats [4]: n_col + + -5 number of nonzeros in matrix is negative + + stats [4]: number of nonzeros, p [n_col] + + -6 p [0] is nonzero + + stats [4]: p [0] + + -7 A is too small + + stats [4]: required size + stats [5]: actual size (Alen) + + -8 a column has a negative number of entries + + stats [4]: column with < 0 entries + stats [5]: number of entries in col + + -9 a row index is out of bounds + + stats [4]: column with bad row index + stats [5]: bad row index + stats [6]: n_row, # of rows of matrx + + -10 (unused; see symamd.c) + + -999 (unused; see symamd.c) + + Future versions may return more statistics in the stats array. + + Example: + + See http://www.cise.ufl.edu/research/sparse/colamd/example.c + for a complete example. + + To order the columns of a 5-by-4 matrix with 11 nonzero entries in + the following nonzero pattern + + x 0 x 0 + x 0 x x + 0 x x 0 + 0 0 x x + x x 0 0 + + with default knobs and no output statistics, do the following: + + #include "colamd.h" + #define ALEN 100 + int A [ALEN] = {0, 1, 4, 2, 4, 0, 1, 2, 3, 1, 3} ; + int p [ ] = {0, 3, 5, 9, 11} ; + int stats [COLAMD_STATS] ; + colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ; + + The permutation is returned in the array p, and A is destroyed. + + ---------------------------------------------------------------------------- + symamd: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + int symamd (int n, int *A, int *p, int *perm, + double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS], + void (*allocate) (size_t, size_t), void (*release) (void *)) ; + UF_long symamd_l (UF_long n, UF_long *A, UF_long *p, UF_long *perm, + double knobs [COLAMD_KNOBS], UF_long stats [COLAMD_STATS], + void (*allocate) (size_t, size_t), void (*release) (void *)) ; + + Purpose: + + The symamd routine computes an ordering P of a symmetric sparse + matrix A such that the Cholesky factorization PAP' = LL' remains + sparse. It is based on a column ordering of a matrix M constructed + so that the nonzero pattern of M'M is the same as A. The matrix A + is assumed to be symmetric; only the strictly lower triangular part + is accessed. You must pass your selected memory allocator (usually + calloc/free or mxCalloc/mxFree) to symamd, for it to allocate + memory for the temporary matrix M. + + Returns: + + TRUE (1) if successful, FALSE (0) otherwise. + + Arguments: + + int n ; Input argument. + + Number of rows and columns in the symmetrix matrix A. + Restriction: n >= 0. + Symamd returns FALSE if n is negative. + + int A [nnz] ; Input argument. + + A is an integer array of size nnz, where nnz = p [n]. + + The row indices of the entries in column c of the matrix are + held in A [(p [c]) ... (p [c+1]-1)]. The row indices in a + given column c need not be in ascending order, and duplicate + row indices may be present. However, symamd will run faster + if the columns are in sorted order with no duplicate entries. + + The matrix is 0-based. That is, rows are in the range 0 to + n-1, and columns are in the range 0 to n-1. Symamd + returns FALSE if any row index is out of range. + + The contents of A are not modified. + + int p [n+1] ; Input argument. + + p is an integer array of size n+1. On input, it holds the + "pointers" for the column form of the matrix A. Column c of + the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first + entry, p [0], must be zero, and p [c] <= p [c+1] must hold + for all c in the range 0 to n-1. The value p [n] is + thus the total number of entries in the pattern of the matrix A. + Symamd returns FALSE if these conditions are not met. + + The contents of p are not modified. + + int perm [n+1] ; Output argument. + + On output, if symamd returns TRUE, the array perm holds the + permutation P, where perm [0] is the first index in the new + ordering, and perm [n-1] is the last. That is, perm [k] = j + means that row and column j of A is the kth column in PAP', + where k is in the range 0 to n-1 (perm [0] = j means + that row and column j of A are the first row and column in + PAP'). The array is used as a workspace during the ordering, + which is why it must be of length n+1, not just n. + + double knobs [COLAMD_KNOBS] ; Input argument. + + See colamd_set_defaults for a description. + + int stats [COLAMD_STATS] ; Output argument. + + Statistics on the ordering, and error status. + See colamd.h for related definitions. + Symamd returns FALSE if stats is not present. + + stats [0]: number of dense or empty row and columns ignored + (and ordered last in the output permutation + perm). Note that a row/column can become + "empty" if it contains only "dense" and/or + "empty" columns/rows. + + stats [1]: (same as stats [0]) + + stats [2]: number of garbage collections performed. + + stats [3]: status code. < 0 is an error code. + > 1 is a warning or notice. + + 0 OK. Each column of the input matrix contained + row indices in increasing order, with no + duplicates. + + 1 OK, but columns of input matrix were jumbled + (unsorted columns or duplicate entries). Symamd + had to do some extra work to sort the matrix + first and remove duplicate entries, but it + still was able to return a valid permutation + (return value of symamd was TRUE). + + stats [4]: highest numbered column that + is unsorted or has duplicate + entries. + stats [5]: last seen duplicate or + unsorted row index. + stats [6]: number of duplicate or + unsorted row indices. + + -1 A is a null pointer + + -2 p is a null pointer + + -3 (unused, see colamd.c) + + -4 n is negative + + stats [4]: n + + -5 number of nonzeros in matrix is negative + + stats [4]: # of nonzeros (p [n]). + + -6 p [0] is nonzero + + stats [4]: p [0] + + -7 (unused) + + -8 a column has a negative number of entries + + stats [4]: column with < 0 entries + stats [5]: number of entries in col + + -9 a row index is out of bounds + + stats [4]: column with bad row index + stats [5]: bad row index + stats [6]: n_row, # of rows of matrx + + -10 out of memory (unable to allocate temporary + workspace for M or count arrays using the + "allocate" routine passed into symamd). + + Future versions may return more statistics in the stats array. + + void * (*allocate) (size_t, size_t) + + A pointer to a function providing memory allocation. The + allocated memory must be returned initialized to zero. For a + C application, this argument should normally be a pointer to + calloc. For a MATLAB mexFunction, the routine mxCalloc is + passed instead. + + void (*release) (size_t, size_t) + + A pointer to a function that frees memory allocated by the + memory allocation routine above. For a C application, this + argument should normally be a pointer to free. For a MATLAB + mexFunction, the routine mxFree is passed instead. + + + ---------------------------------------------------------------------------- + colamd_report: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + colamd_report (int stats [COLAMD_STATS]) ; + colamd_l_report (UF_long stats [COLAMD_STATS]) ; + + Purpose: + + Prints the error status and statistics recorded in the stats + array on the standard error output (for a standard C routine) + or on the MATLAB output (for a mexFunction). + + Arguments: + + int stats [COLAMD_STATS] ; Input only. Statistics from colamd. + + + ---------------------------------------------------------------------------- + symamd_report: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + symamd_report (int stats [COLAMD_STATS]) ; + symamd_l_report (UF_long stats [COLAMD_STATS]) ; + + Purpose: + + Prints the error status and statistics recorded in the stats + array on the standard error output (for a standard C routine) + or on the MATLAB output (for a mexFunction). + + Arguments: + + int stats [COLAMD_STATS] ; Input only. Statistics from symamd. + + +*/ + +/* ========================================================================== */ +/* === Scaffolding code definitions ======================================== */ +/* ========================================================================== */ + +/* Ensure that debugging is turned off: */ +#ifndef NDEBUG +#define NDEBUG +#endif + +/* turn on debugging by uncommenting the following line + #undef NDEBUG +*/ + +/* + Our "scaffolding code" philosophy: In our opinion, well-written library + code should keep its "debugging" code, and just normally have it turned off + by the compiler so as not to interfere with performance. This serves + several purposes: + + (1) assertions act as comments to the reader, telling you what the code + expects at that point. All assertions will always be true (unless + there really is a bug, of course). + + (2) leaving in the scaffolding code assists anyone who would like to modify + the code, or understand the algorithm (by reading the debugging output, + one can get a glimpse into what the code is doing). + + (3) (gasp!) for actually finding bugs. This code has been heavily tested + and "should" be fully functional and bug-free ... but you never know... + + The code will become outrageously slow when debugging is + enabled. To control the level of debugging output, set an environment + variable D to 0 (little), 1 (some), 2, 3, or 4 (lots). When debugging, + you should see the following message on the standard output: + + colamd: debug version, D = 1 (THIS WILL BE SLOW!) + + or a similar message for symamd. If you don't, then debugging has not + been enabled. + +*/ + +/* ========================================================================== */ +/* === Include files ======================================================== */ +/* ========================================================================== */ + +#include "colamd.h" + +#if 0 /* by mao */ +#include +#include + +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#include "matrix.h" +#endif /* MATLAB_MEX_FILE */ + +#if !defined (NPRINT) || !defined (NDEBUG) +#include +#endif + +#ifndef NULL +#define NULL ((void *) 0) +#endif +#endif + +/* ========================================================================== */ +/* === int or UF_long ======================================================= */ +/* ========================================================================== */ + +#if 0 /* by mao */ +/* define UF_long */ +#include "UFconfig.h" +#endif + +#ifdef DLONG + +#define Int UF_long +#define ID UF_long_id +#define Int_MAX UF_long_max + +#define COLAMD_recommended colamd_l_recommended +#define COLAMD_set_defaults colamd_l_set_defaults +#define COLAMD_MAIN colamd_l +#define SYMAMD_MAIN symamd_l +#define COLAMD_report colamd_l_report +#define SYMAMD_report symamd_l_report + +#else + +#define Int int +#define ID "%d" +#define Int_MAX INT_MAX + +#define COLAMD_recommended colamd_recommended +#define COLAMD_set_defaults colamd_set_defaults +#define COLAMD_MAIN colamd +#define SYMAMD_MAIN symamd +#define COLAMD_report colamd_report +#define SYMAMD_report symamd_report + +#endif + +/* ========================================================================== */ +/* === Row and Column structures ============================================ */ +/* ========================================================================== */ + +/* User code that makes use of the colamd/symamd routines need not directly */ +/* reference these structures. They are used only for colamd_recommended. */ + +typedef struct Colamd_Col_struct +{ + Int start ; /* index for A of first row in this column, or DEAD */ + /* if column is dead */ + Int length ; /* number of rows in this column */ + union + { + Int thickness ; /* number of original columns represented by this */ + /* col, if the column is alive */ + Int parent ; /* parent in parent tree super-column structure, if */ + /* the column is dead */ + } shared1 ; + union + { + Int score ; /* the score used to maintain heap, if col is alive */ + Int order ; /* pivot ordering of this column, if col is dead */ + } shared2 ; + union + { + Int headhash ; /* head of a hash bucket, if col is at the head of */ + /* a degree list */ + Int hash ; /* hash value, if col is not in a degree list */ + Int prev ; /* previous column in degree list, if col is in a */ + /* degree list (but not at the head of a degree list) */ + } shared3 ; + union + { + Int degree_next ; /* next column, if col is in a degree list */ + Int hash_next ; /* next column, if col is in a hash list */ + } shared4 ; + +} Colamd_Col ; + +typedef struct Colamd_Row_struct +{ + Int start ; /* index for A of first col in this row */ + Int length ; /* number of principal columns in this row */ + union + { + Int degree ; /* number of principal & non-principal columns in row */ + Int p ; /* used as a row pointer in init_rows_cols () */ + } shared1 ; + union + { + Int mark ; /* for computing set differences and marking dead rows*/ + Int first_column ;/* first column in row (used in garbage collection) */ + } shared2 ; + +} Colamd_Row ; + +/* ========================================================================== */ +/* === Definitions ========================================================== */ +/* ========================================================================== */ + +/* Routines are either PUBLIC (user-callable) or PRIVATE (not user-callable) */ +#define PUBLIC +#define PRIVATE static + +#define DENSE_DEGREE(alpha,n) \ + ((Int) MAX (16.0, (alpha) * sqrt ((double) (n)))) + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define ONES_COMPLEMENT(r) (-(r)-1) + +/* -------------------------------------------------------------------------- */ +/* Change for version 2.1: define TRUE and FALSE only if not yet defined */ +/* -------------------------------------------------------------------------- */ + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +/* -------------------------------------------------------------------------- */ + +#define EMPTY (-1) + +/* Row and column status */ +#define ALIVE (0) +#define DEAD (-1) + +/* Column status */ +#define DEAD_PRINCIPAL (-1) +#define DEAD_NON_PRINCIPAL (-2) + +/* Macros for row and column status update and checking. */ +#define ROW_IS_DEAD(r) ROW_IS_MARKED_DEAD (Row[r].shared2.mark) +#define ROW_IS_MARKED_DEAD(row_mark) (row_mark < ALIVE) +#define ROW_IS_ALIVE(r) (Row [r].shared2.mark >= ALIVE) +#define COL_IS_DEAD(c) (Col [c].start < ALIVE) +#define COL_IS_ALIVE(c) (Col [c].start >= ALIVE) +#define COL_IS_DEAD_PRINCIPAL(c) (Col [c].start == DEAD_PRINCIPAL) +#define KILL_ROW(r) { Row [r].shared2.mark = DEAD ; } +#define KILL_PRINCIPAL_COL(c) { Col [c].start = DEAD_PRINCIPAL ; } +#define KILL_NON_PRINCIPAL_COL(c) { Col [c].start = DEAD_NON_PRINCIPAL ; } + +/* ========================================================================== */ +/* === Colamd reporting mechanism =========================================== */ +/* ========================================================================== */ + +#if defined (MATLAB_MEX_FILE) || defined (MATHWORKS) +/* In MATLAB, matrices are 1-based to the user, but 0-based internally */ +#define INDEX(i) ((i)+1) +#else +/* In C, matrices are 0-based and indices are reported as such in *_report */ +#define INDEX(i) (i) +#endif + +/* All output goes through the PRINTF macro. */ +#define PRINTF(params) { if (colamd_printf != NULL) (void) colamd_printf params ; } + +/* ========================================================================== */ +/* === Prototypes of PRIVATE routines ======================================= */ +/* ========================================================================== */ + +PRIVATE Int init_rows_cols +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int p [], + Int stats [COLAMD_STATS] +) ; + +PRIVATE void init_scoring +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int head [], + double knobs [COLAMD_KNOBS], + Int *p_n_row2, + Int *p_n_col2, + Int *p_max_deg +) ; + +PRIVATE Int find_ordering +( + Int n_row, + Int n_col, + Int Alen, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int head [], + Int n_col2, + Int max_deg, + Int pfree, + Int aggressive +) ; + +PRIVATE void order_children +( + Int n_col, + Colamd_Col Col [], + Int p [] +) ; + +PRIVATE void detect_super_cols +( + +#ifndef NDEBUG + Int n_col, + Colamd_Row Row [], +#endif /* NDEBUG */ + + Colamd_Col Col [], + Int A [], + Int head [], + Int row_start, + Int row_length +) ; + +PRIVATE Int garbage_collection +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int *pfree +) ; + +PRIVATE Int clear_mark +( + Int tag_mark, + Int max_mark, + Int n_row, + Colamd_Row Row [] +) ; + +PRIVATE void print_report +( + char *method, + Int stats [COLAMD_STATS] +) ; + +/* ========================================================================== */ +/* === Debugging prototypes and definitions ================================= */ +/* ========================================================================== */ + +#ifndef NDEBUG + +#if 0 /* by mao */ +#include +#endif + +/* colamd_debug is the *ONLY* global variable, and is only */ +/* present when debugging */ + +PRIVATE Int colamd_debug = 0 ; /* debug print level */ + +#define DEBUG0(params) { PRINTF (params) ; } +#define DEBUG1(params) { if (colamd_debug >= 1) PRINTF (params) ; } +#define DEBUG2(params) { if (colamd_debug >= 2) PRINTF (params) ; } +#define DEBUG3(params) { if (colamd_debug >= 3) PRINTF (params) ; } +#define DEBUG4(params) { if (colamd_debug >= 4) PRINTF (params) ; } + +#if 0 /* by mao */ +#ifdef MATLAB_MEX_FILE +#define ASSERT(expression) (mxAssert ((expression), "")) +#else +#define ASSERT(expression) (assert (expression)) +#endif /* MATLAB_MEX_FILE */ +#else +#define ASSERT xassert +#endif + +PRIVATE void colamd_get_debug /* gets the debug print level from getenv */ +( + char *method +) ; + +PRIVATE void debug_deg_lists +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int head [], + Int min_score, + Int should, + Int max_deg +) ; + +PRIVATE void debug_mark +( + Int n_row, + Colamd_Row Row [], + Int tag_mark, + Int max_mark +) ; + +PRIVATE void debug_matrix +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [] +) ; + +PRIVATE void debug_structures +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int n_col2 +) ; + +#else /* NDEBUG */ + +/* === No debugging ========================================================= */ + +#define DEBUG0(params) ; +#define DEBUG1(params) ; +#define DEBUG2(params) ; +#define DEBUG3(params) ; +#define DEBUG4(params) ; + +#define ASSERT(expression) + +#endif /* NDEBUG */ + +/* ========================================================================== */ +/* === USER-CALLABLE ROUTINES: ============================================== */ +/* ========================================================================== */ + +/* ========================================================================== */ +/* === colamd_recommended =================================================== */ +/* ========================================================================== */ + +/* + The colamd_recommended routine returns the suggested size for Alen. This + value has been determined to provide good balance between the number of + garbage collections and the memory requirements for colamd. If any + argument is negative, or if integer overflow occurs, a 0 is returned as an + error condition. 2*nnz space is required for the row and column + indices of the matrix. COLAMD_C (n_col) + COLAMD_R (n_row) space is + required for the Col and Row arrays, respectively, which are internal to + colamd (roughly 6*n_col + 4*n_row). An additional n_col space is the + minimal amount of "elbow room", and nnz/5 more space is recommended for + run time efficiency. + + Alen is approximately 2.2*nnz + 7*n_col + 4*n_row + 10. + + This function is not needed when using symamd. +*/ + +/* add two values of type size_t, and check for integer overflow */ +static size_t t_add (size_t a, size_t b, int *ok) +{ + (*ok) = (*ok) && ((a + b) >= MAX (a,b)) ; + return ((*ok) ? (a + b) : 0) ; +} + +/* compute a*k where k is a small integer, and check for integer overflow */ +static size_t t_mult (size_t a, size_t k, int *ok) +{ + size_t i, s = 0 ; + for (i = 0 ; i < k ; i++) + { + s = t_add (s, a, ok) ; + } + return (s) ; +} + +/* size of the Col and Row structures */ +#define COLAMD_C(n_col,ok) \ + ((t_mult (t_add (n_col, 1, ok), sizeof (Colamd_Col), ok) / sizeof (Int))) + +#define COLAMD_R(n_row,ok) \ + ((t_mult (t_add (n_row, 1, ok), sizeof (Colamd_Row), ok) / sizeof (Int))) + + +PUBLIC size_t COLAMD_recommended /* returns recommended value of Alen. */ +( + /* === Parameters ======================================================= */ + + Int nnz, /* number of nonzeros in A */ + Int n_row, /* number of rows in A */ + Int n_col /* number of columns in A */ +) +{ + size_t s, c, r ; + int ok = TRUE ; + if (nnz < 0 || n_row < 0 || n_col < 0) + { + return (0) ; + } + s = t_mult (nnz, 2, &ok) ; /* 2*nnz */ + c = COLAMD_C (n_col, &ok) ; /* size of column structures */ + r = COLAMD_R (n_row, &ok) ; /* size of row structures */ + s = t_add (s, c, &ok) ; + s = t_add (s, r, &ok) ; + s = t_add (s, n_col, &ok) ; /* elbow room */ + s = t_add (s, nnz/5, &ok) ; /* elbow room */ + ok = ok && (s < Int_MAX) ; + return (ok ? s : 0) ; +} + + +/* ========================================================================== */ +/* === colamd_set_defaults ================================================== */ +/* ========================================================================== */ + +/* + The colamd_set_defaults routine sets the default values of the user- + controllable parameters for colamd and symamd: + + Colamd: rows with more than max (16, knobs [0] * sqrt (n_col)) + entries are removed prior to ordering. Columns with more than + max (16, knobs [1] * sqrt (MIN (n_row,n_col))) entries are removed + prior to ordering, and placed last in the output column ordering. + + Symamd: Rows and columns with more than max (16, knobs [0] * sqrt (n)) + entries are removed prior to ordering, and placed last in the + output ordering. + + knobs [0] dense row control + + knobs [1] dense column control + + knobs [2] if nonzero, do aggresive absorption + + knobs [3..19] unused, but future versions might use this + +*/ + +PUBLIC void COLAMD_set_defaults +( + /* === Parameters ======================================================= */ + + double knobs [COLAMD_KNOBS] /* knob array */ +) +{ + /* === Local variables ================================================== */ + + Int i ; + + if (!knobs) + { + return ; /* no knobs to initialize */ + } + for (i = 0 ; i < COLAMD_KNOBS ; i++) + { + knobs [i] = 0 ; + } + knobs [COLAMD_DENSE_ROW] = 10 ; + knobs [COLAMD_DENSE_COL] = 10 ; + knobs [COLAMD_AGGRESSIVE] = TRUE ; /* default: do aggressive absorption*/ +} + + +/* ========================================================================== */ +/* === symamd =============================================================== */ +/* ========================================================================== */ + +PUBLIC Int SYMAMD_MAIN /* return TRUE if OK, FALSE otherwise */ +( + /* === Parameters ======================================================= */ + + Int n, /* number of rows and columns of A */ + Int A [], /* row indices of A */ + Int p [], /* column pointers of A */ + Int perm [], /* output permutation, size n+1 */ + double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ + Int stats [COLAMD_STATS], /* output statistics and error codes */ + void * (*allocate) (size_t, size_t), + /* pointer to calloc (ANSI C) or */ + /* mxCalloc (for MATLAB mexFunction) */ + void (*release) (void *) + /* pointer to free (ANSI C) or */ + /* mxFree (for MATLAB mexFunction) */ +) +{ + /* === Local variables ================================================== */ + + Int *count ; /* length of each column of M, and col pointer*/ + Int *mark ; /* mark array for finding duplicate entries */ + Int *M ; /* row indices of matrix M */ + size_t Mlen ; /* length of M */ + Int n_row ; /* number of rows in M */ + Int nnz ; /* number of entries in A */ + Int i ; /* row index of A */ + Int j ; /* column index of A */ + Int k ; /* row index of M */ + Int mnz ; /* number of nonzeros in M */ + Int pp ; /* index into a column of A */ + Int last_row ; /* last row seen in the current column */ + Int length ; /* number of nonzeros in a column */ + + double cknobs [COLAMD_KNOBS] ; /* knobs for colamd */ + double default_knobs [COLAMD_KNOBS] ; /* default knobs for colamd */ + +#ifndef NDEBUG + colamd_get_debug ("symamd") ; +#endif /* NDEBUG */ + + /* === Check the input arguments ======================================== */ + + if (!stats) + { + DEBUG0 (("symamd: stats not present\n")) ; + return (FALSE) ; + } + for (i = 0 ; i < COLAMD_STATS ; i++) + { + stats [i] = 0 ; + } + stats [COLAMD_STATUS] = COLAMD_OK ; + stats [COLAMD_INFO1] = -1 ; + stats [COLAMD_INFO2] = -1 ; + + if (!A) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; + DEBUG0 (("symamd: A not present\n")) ; + return (FALSE) ; + } + + if (!p) /* p is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; + DEBUG0 (("symamd: p not present\n")) ; + return (FALSE) ; + } + + if (n < 0) /* n must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; + stats [COLAMD_INFO1] = n ; + DEBUG0 (("symamd: n negative %d\n", n)) ; + return (FALSE) ; + } + + nnz = p [n] ; + if (nnz < 0) /* nnz must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; + stats [COLAMD_INFO1] = nnz ; + DEBUG0 (("symamd: number of entries negative %d\n", nnz)) ; + return (FALSE) ; + } + + if (p [0] != 0) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; + stats [COLAMD_INFO1] = p [0] ; + DEBUG0 (("symamd: p[0] not zero %d\n", p [0])) ; + return (FALSE) ; + } + + /* === If no knobs, set default knobs =================================== */ + + if (!knobs) + { + COLAMD_set_defaults (default_knobs) ; + knobs = default_knobs ; + } + + /* === Allocate count and mark ========================================== */ + + count = (Int *) ((*allocate) (n+1, sizeof (Int))) ; + if (!count) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + DEBUG0 (("symamd: allocate count (size %d) failed\n", n+1)) ; + return (FALSE) ; + } + + mark = (Int *) ((*allocate) (n+1, sizeof (Int))) ; + if (!mark) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + (*release) ((void *) count) ; + DEBUG0 (("symamd: allocate mark (size %d) failed\n", n+1)) ; + return (FALSE) ; + } + + /* === Compute column counts of M, check if A is valid ================== */ + + stats [COLAMD_INFO3] = 0 ; /* number of duplicate or unsorted row indices*/ + + for (i = 0 ; i < n ; i++) + { + mark [i] = -1 ; + } + + for (j = 0 ; j < n ; j++) + { + last_row = -1 ; + + length = p [j+1] - p [j] ; + if (length < 0) + { + /* column pointers must be non-decreasing */ + stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = length ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: col %d negative length %d\n", j, length)) ; + return (FALSE) ; + } + + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + if (i < 0 || i >= n) + { + /* row index i, in column j, is out of bounds */ + stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = i ; + stats [COLAMD_INFO3] = n ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: row %d col %d out of bounds\n", i, j)) ; + return (FALSE) ; + } + + if (i <= last_row || mark [i] == j) + { + /* row index is unsorted or repeated (or both), thus col */ + /* is jumbled. This is a notice, not an error condition. */ + stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = i ; + (stats [COLAMD_INFO3]) ++ ; + DEBUG1 (("symamd: row %d col %d unsorted/duplicate\n", i, j)) ; + } + + if (i > j && mark [i] != j) + { + /* row k of M will contain column indices i and j */ + count [i]++ ; + count [j]++ ; + } + + /* mark the row as having been seen in this column */ + mark [i] = j ; + + last_row = i ; + } + } + + /* v2.4: removed free(mark) */ + + /* === Compute column pointers of M ===================================== */ + + /* use output permutation, perm, for column pointers of M */ + perm [0] = 0 ; + for (j = 1 ; j <= n ; j++) + { + perm [j] = perm [j-1] + count [j-1] ; + } + for (j = 0 ; j < n ; j++) + { + count [j] = perm [j] ; + } + + /* === Construct M ====================================================== */ + + mnz = perm [n] ; + n_row = mnz / 2 ; + Mlen = COLAMD_recommended (mnz, n_row, n) ; + M = (Int *) ((*allocate) (Mlen, sizeof (Int))) ; + DEBUG0 (("symamd: M is %d-by-%d with %d entries, Mlen = %g\n", + n_row, n, mnz, (double) Mlen)) ; + + if (!M) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: allocate M (size %g) failed\n", (double) Mlen)) ; + return (FALSE) ; + } + + k = 0 ; + + if (stats [COLAMD_STATUS] == COLAMD_OK) + { + /* Matrix is OK */ + for (j = 0 ; j < n ; j++) + { + ASSERT (p [j+1] - p [j] >= 0) ; + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + ASSERT (i >= 0 && i < n) ; + if (i > j) + { + /* row k of M contains column indices i and j */ + M [count [i]++] = k ; + M [count [j]++] = k ; + k++ ; + } + } + } + } + else + { + /* Matrix is jumbled. Do not add duplicates to M. Unsorted cols OK. */ + DEBUG0 (("symamd: Duplicates in A.\n")) ; + for (i = 0 ; i < n ; i++) + { + mark [i] = -1 ; + } + for (j = 0 ; j < n ; j++) + { + ASSERT (p [j+1] - p [j] >= 0) ; + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + ASSERT (i >= 0 && i < n) ; + if (i > j && mark [i] != j) + { + /* row k of M contains column indices i and j */ + M [count [i]++] = k ; + M [count [j]++] = k ; + k++ ; + mark [i] = j ; + } + } + } + /* v2.4: free(mark) moved below */ + } + + /* count and mark no longer needed */ + (*release) ((void *) count) ; + (*release) ((void *) mark) ; /* v2.4: free (mark) moved here */ + ASSERT (k == n_row) ; + + /* === Adjust the knobs for M =========================================== */ + + for (i = 0 ; i < COLAMD_KNOBS ; i++) + { + cknobs [i] = knobs [i] ; + } + + /* there are no dense rows in M */ + cknobs [COLAMD_DENSE_ROW] = -1 ; + cknobs [COLAMD_DENSE_COL] = knobs [COLAMD_DENSE_ROW] ; + + /* === Order the columns of M =========================================== */ + + /* v2.4: colamd cannot fail here, so the error check is removed */ + (void) COLAMD_MAIN (n_row, n, (Int) Mlen, M, perm, cknobs, stats) ; + + /* Note that the output permutation is now in perm */ + + /* === get the statistics for symamd from colamd ======================== */ + + /* a dense column in colamd means a dense row and col in symamd */ + stats [COLAMD_DENSE_ROW] = stats [COLAMD_DENSE_COL] ; + + /* === Free M =========================================================== */ + + (*release) ((void *) M) ; + DEBUG0 (("symamd: done.\n")) ; + return (TRUE) ; + +} + +/* ========================================================================== */ +/* === colamd =============================================================== */ +/* ========================================================================== */ + +/* + The colamd routine computes a column ordering Q of a sparse matrix + A such that the LU factorization P(AQ) = LU remains sparse, where P is + selected via partial pivoting. The routine can also be viewed as + providing a permutation Q such that the Cholesky factorization + (AQ)'(AQ) = LL' remains sparse. +*/ + +PUBLIC Int COLAMD_MAIN /* returns TRUE if successful, FALSE otherwise*/ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows in A */ + Int n_col, /* number of columns in A */ + Int Alen, /* length of A */ + Int A [], /* row indices of A */ + Int p [], /* pointers to columns in A */ + double knobs [COLAMD_KNOBS],/* parameters (uses defaults if NULL) */ + Int stats [COLAMD_STATS] /* output statistics and error codes */ +) +{ + /* === Local variables ================================================== */ + + Int i ; /* loop index */ + Int nnz ; /* nonzeros in A */ + size_t Row_size ; /* size of Row [], in integers */ + size_t Col_size ; /* size of Col [], in integers */ + size_t need ; /* minimum required length of A */ + Colamd_Row *Row ; /* pointer into A of Row [0..n_row] array */ + Colamd_Col *Col ; /* pointer into A of Col [0..n_col] array */ + Int n_col2 ; /* number of non-dense, non-empty columns */ + Int n_row2 ; /* number of non-dense, non-empty rows */ + Int ngarbage ; /* number of garbage collections performed */ + Int max_deg ; /* maximum row degree */ + double default_knobs [COLAMD_KNOBS] ; /* default knobs array */ + Int aggressive ; /* do aggressive absorption */ + int ok ; + +#ifndef NDEBUG + colamd_get_debug ("colamd") ; +#endif /* NDEBUG */ + + /* === Check the input arguments ======================================== */ + + if (!stats) + { + DEBUG0 (("colamd: stats not present\n")) ; + return (FALSE) ; + } + for (i = 0 ; i < COLAMD_STATS ; i++) + { + stats [i] = 0 ; + } + stats [COLAMD_STATUS] = COLAMD_OK ; + stats [COLAMD_INFO1] = -1 ; + stats [COLAMD_INFO2] = -1 ; + + if (!A) /* A is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; + DEBUG0 (("colamd: A not present\n")) ; + return (FALSE) ; + } + + if (!p) /* p is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; + DEBUG0 (("colamd: p not present\n")) ; + return (FALSE) ; + } + + if (n_row < 0) /* n_row must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ; + stats [COLAMD_INFO1] = n_row ; + DEBUG0 (("colamd: nrow negative %d\n", n_row)) ; + return (FALSE) ; + } + + if (n_col < 0) /* n_col must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; + stats [COLAMD_INFO1] = n_col ; + DEBUG0 (("colamd: ncol negative %d\n", n_col)) ; + return (FALSE) ; + } + + nnz = p [n_col] ; + if (nnz < 0) /* nnz must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; + stats [COLAMD_INFO1] = nnz ; + DEBUG0 (("colamd: number of entries negative %d\n", nnz)) ; + return (FALSE) ; + } + + if (p [0] != 0) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; + stats [COLAMD_INFO1] = p [0] ; + DEBUG0 (("colamd: p[0] not zero %d\n", p [0])) ; + return (FALSE) ; + } + + /* === If no knobs, set default knobs =================================== */ + + if (!knobs) + { + COLAMD_set_defaults (default_knobs) ; + knobs = default_knobs ; + } + + aggressive = (knobs [COLAMD_AGGRESSIVE] != FALSE) ; + + /* === Allocate the Row and Col arrays from array A ===================== */ + + ok = TRUE ; + Col_size = COLAMD_C (n_col, &ok) ; /* size of Col array of structs */ + Row_size = COLAMD_R (n_row, &ok) ; /* size of Row array of structs */ + + /* need = 2*nnz + n_col + Col_size + Row_size ; */ + need = t_mult (nnz, 2, &ok) ; + need = t_add (need, n_col, &ok) ; + need = t_add (need, Col_size, &ok) ; + need = t_add (need, Row_size, &ok) ; + + if (!ok || need > (size_t) Alen || need > Int_MAX) + { + /* not enough space in array A to perform the ordering */ + stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ; + stats [COLAMD_INFO1] = need ; + stats [COLAMD_INFO2] = Alen ; + DEBUG0 (("colamd: Need Alen >= %d, given only Alen = %d\n", need,Alen)); + return (FALSE) ; + } + + Alen -= Col_size + Row_size ; + Col = (Colamd_Col *) &A [Alen] ; + Row = (Colamd_Row *) &A [Alen + Col_size] ; + + /* === Construct the row and column data structures ===================== */ + + if (!init_rows_cols (n_row, n_col, Row, Col, A, p, stats)) + { + /* input matrix is invalid */ + DEBUG0 (("colamd: Matrix invalid\n")) ; + return (FALSE) ; + } + + /* === Initialize scores, kill dense rows/columns ======================= */ + + init_scoring (n_row, n_col, Row, Col, A, p, knobs, + &n_row2, &n_col2, &max_deg) ; + + /* === Order the supercolumns =========================================== */ + + ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p, + n_col2, max_deg, 2*nnz, aggressive) ; + + /* === Order the non-principal columns ================================== */ + + order_children (n_col, Col, p) ; + + /* === Return statistics in stats ======================================= */ + + stats [COLAMD_DENSE_ROW] = n_row - n_row2 ; + stats [COLAMD_DENSE_COL] = n_col - n_col2 ; + stats [COLAMD_DEFRAG_COUNT] = ngarbage ; + DEBUG0 (("colamd: done.\n")) ; + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === colamd_report ======================================================== */ +/* ========================================================================== */ + +PUBLIC void COLAMD_report +( + Int stats [COLAMD_STATS] +) +{ + print_report ("colamd", stats) ; +} + + +/* ========================================================================== */ +/* === symamd_report ======================================================== */ +/* ========================================================================== */ + +PUBLIC void SYMAMD_report +( + Int stats [COLAMD_STATS] +) +{ + print_report ("symamd", stats) ; +} + + + +/* ========================================================================== */ +/* === NON-USER-CALLABLE ROUTINES: ========================================== */ +/* ========================================================================== */ + +/* There are no user-callable routines beyond this point in the file */ + + +/* ========================================================================== */ +/* === init_rows_cols ======================================================= */ +/* ========================================================================== */ + +/* + Takes the column form of the matrix in A and creates the row form of the + matrix. Also, row and column attributes are stored in the Col and Row + structs. If the columns are un-sorted or contain duplicate row indices, + this routine will also sort and remove duplicate row indices from the + column form of the matrix. Returns FALSE if the matrix is invalid, + TRUE otherwise. Not user-callable. +*/ + +PRIVATE Int init_rows_cols /* returns TRUE if OK, or FALSE otherwise */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* row indices of A, of size Alen */ + Int p [], /* pointers to columns in A, of size n_col+1 */ + Int stats [COLAMD_STATS] /* colamd statistics */ +) +{ + /* === Local variables ================================================== */ + + Int col ; /* a column index */ + Int row ; /* a row index */ + Int *cp ; /* a column pointer */ + Int *cp_end ; /* a pointer to the end of a column */ + Int *rp ; /* a row pointer */ + Int *rp_end ; /* a pointer to the end of a row */ + Int last_row ; /* previous row */ + + /* === Initialize columns, and check column pointers ==================== */ + + for (col = 0 ; col < n_col ; col++) + { + Col [col].start = p [col] ; + Col [col].length = p [col+1] - p [col] ; + + if (Col [col].length < 0) + { + /* column pointers must be non-decreasing */ + stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = Col [col].length ; + DEBUG0 (("colamd: col %d length %d < 0\n", col, Col [col].length)) ; + return (FALSE) ; + } + + Col [col].shared1.thickness = 1 ; + Col [col].shared2.score = 0 ; + Col [col].shared3.prev = EMPTY ; + Col [col].shared4.degree_next = EMPTY ; + } + + /* p [0..n_col] no longer needed, used as "head" in subsequent routines */ + + /* === Scan columns, compute row degrees, and check row indices ========= */ + + stats [COLAMD_INFO3] = 0 ; /* number of duplicate or unsorted row indices*/ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].length = 0 ; + Row [row].shared2.mark = -1 ; + } + + for (col = 0 ; col < n_col ; col++) + { + last_row = -1 ; + + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + + while (cp < cp_end) + { + row = *cp++ ; + + /* make sure row indices within range */ + if (row < 0 || row >= n_row) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + stats [COLAMD_INFO3] = n_row ; + DEBUG0 (("colamd: row %d col %d out of bounds\n", row, col)) ; + return (FALSE) ; + } + + if (row <= last_row || Row [row].shared2.mark == col) + { + /* row index are unsorted or repeated (or both), thus col */ + /* is jumbled. This is a notice, not an error condition. */ + stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + (stats [COLAMD_INFO3]) ++ ; + DEBUG1 (("colamd: row %d col %d unsorted/duplicate\n",row,col)); + } + + if (Row [row].shared2.mark != col) + { + Row [row].length++ ; + } + else + { + /* this is a repeated entry in the column, */ + /* it will be removed */ + Col [col].length-- ; + } + + /* mark the row as having been seen in this column */ + Row [row].shared2.mark = col ; + + last_row = row ; + } + } + + /* === Compute row pointers ============================================= */ + + /* row form of the matrix starts directly after the column */ + /* form of matrix in A */ + Row [0].start = p [n_col] ; + Row [0].shared1.p = Row [0].start ; + Row [0].shared2.mark = -1 ; + for (row = 1 ; row < n_row ; row++) + { + Row [row].start = Row [row-1].start + Row [row-1].length ; + Row [row].shared1.p = Row [row].start ; + Row [row].shared2.mark = -1 ; + } + + /* === Create row form ================================================== */ + + if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) + { + /* if cols jumbled, watch for repeated row indices */ + for (col = 0 ; col < n_col ; col++) + { + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + while (cp < cp_end) + { + row = *cp++ ; + if (Row [row].shared2.mark != col) + { + A [(Row [row].shared1.p)++] = col ; + Row [row].shared2.mark = col ; + } + } + } + } + else + { + /* if cols not jumbled, we don't need the mark (this is faster) */ + for (col = 0 ; col < n_col ; col++) + { + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + while (cp < cp_end) + { + A [(Row [*cp++].shared1.p)++] = col ; + } + } + } + + /* === Clear the row marks and set row degrees ========================== */ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].shared2.mark = 0 ; + Row [row].shared1.degree = Row [row].length ; + } + + /* === See if we need to re-create columns ============================== */ + + if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) + { + DEBUG0 (("colamd: reconstructing column form, matrix jumbled\n")) ; + +#ifndef NDEBUG + /* make sure column lengths are correct */ + for (col = 0 ; col < n_col ; col++) + { + p [col] = Col [col].length ; + } + for (row = 0 ; row < n_row ; row++) + { + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + p [*rp++]-- ; + } + } + for (col = 0 ; col < n_col ; col++) + { + ASSERT (p [col] == 0) ; + } + /* now p is all zero (different than when debugging is turned off) */ +#endif /* NDEBUG */ + + /* === Compute col pointers ========================================= */ + + /* col form of the matrix starts at A [0]. */ + /* Note, we may have a gap between the col form and the row */ + /* form if there were duplicate entries, if so, it will be */ + /* removed upon the first garbage collection */ + Col [0].start = 0 ; + p [0] = Col [0].start ; + for (col = 1 ; col < n_col ; col++) + { + /* note that the lengths here are for pruned columns, i.e. */ + /* no duplicate row indices will exist for these columns */ + Col [col].start = Col [col-1].start + Col [col-1].length ; + p [col] = Col [col].start ; + } + + /* === Re-create col form =========================================== */ + + for (row = 0 ; row < n_row ; row++) + { + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + A [(p [*rp++])++] = row ; + } + } + } + + /* === Done. Matrix is not (or no longer) jumbled ====================== */ + + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === init_scoring ========================================================= */ +/* ========================================================================== */ + +/* + Kills dense or empty columns and rows, calculates an initial score for + each column, and places all columns in the degree lists. Not user-callable. +*/ + +PRIVATE void init_scoring +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* column form and row form of A */ + Int head [], /* of size n_col+1 */ + double knobs [COLAMD_KNOBS],/* parameters */ + Int *p_n_row2, /* number of non-dense, non-empty rows */ + Int *p_n_col2, /* number of non-dense, non-empty columns */ + Int *p_max_deg /* maximum row degree */ +) +{ + /* === Local variables ================================================== */ + + Int c ; /* a column index */ + Int r, row ; /* a row index */ + Int *cp ; /* a column pointer */ + Int deg ; /* degree of a row or column */ + Int *cp_end ; /* a pointer to the end of a column */ + Int *new_cp ; /* new column pointer */ + Int col_length ; /* length of pruned column */ + Int score ; /* current column score */ + Int n_col2 ; /* number of non-dense, non-empty columns */ + Int n_row2 ; /* number of non-dense, non-empty rows */ + Int dense_row_count ; /* remove rows with more entries than this */ + Int dense_col_count ; /* remove cols with more entries than this */ + Int min_score ; /* smallest column score */ + Int max_deg ; /* maximum row degree */ + Int next_col ; /* Used to add to degree list.*/ + +#ifndef NDEBUG + Int debug_count ; /* debug only. */ +#endif /* NDEBUG */ + + /* === Extract knobs ==================================================== */ + + /* Note: if knobs contains a NaN, this is undefined: */ + if (knobs [COLAMD_DENSE_ROW] < 0) + { + /* only remove completely dense rows */ + dense_row_count = n_col-1 ; + } + else + { + dense_row_count = DENSE_DEGREE (knobs [COLAMD_DENSE_ROW], n_col) ; + } + if (knobs [COLAMD_DENSE_COL] < 0) + { + /* only remove completely dense columns */ + dense_col_count = n_row-1 ; + } + else + { + dense_col_count = + DENSE_DEGREE (knobs [COLAMD_DENSE_COL], MIN (n_row, n_col)) ; + } + + DEBUG1 (("colamd: densecount: %d %d\n", dense_row_count, dense_col_count)) ; + max_deg = 0 ; + n_col2 = n_col ; + n_row2 = n_row ; + + /* === Kill empty columns =============================================== */ + + /* Put the empty columns at the end in their natural order, so that LU */ + /* factorization can proceed as far as possible. */ + for (c = n_col-1 ; c >= 0 ; c--) + { + deg = Col [c].length ; + if (deg == 0) + { + /* this is a empty column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + } + } + DEBUG1 (("colamd: null columns killed: %d\n", n_col - n_col2)) ; + + /* === Kill dense columns =============================================== */ + + /* Put the dense columns at the end, in their natural order */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip any dead columns */ + if (COL_IS_DEAD (c)) + { + continue ; + } + deg = Col [c].length ; + if (deg > dense_col_count) + { + /* this is a dense column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + /* decrement the row degrees */ + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + Row [*cp++].shared1.degree-- ; + } + KILL_PRINCIPAL_COL (c) ; + } + } + DEBUG1 (("colamd: Dense and null columns killed: %d\n", n_col - n_col2)) ; + + /* === Kill dense and empty rows ======================================== */ + + for (r = 0 ; r < n_row ; r++) + { + deg = Row [r].shared1.degree ; + ASSERT (deg >= 0 && deg <= n_col) ; + if (deg > dense_row_count || deg == 0) + { + /* kill a dense or empty row */ + KILL_ROW (r) ; + --n_row2 ; + } + else + { + /* keep track of max degree of remaining rows */ + max_deg = MAX (max_deg, deg) ; + } + } + DEBUG1 (("colamd: Dense and null rows killed: %d\n", n_row - n_row2)) ; + + /* === Compute initial column scores ==================================== */ + + /* At this point the row degrees are accurate. They reflect the number */ + /* of "live" (non-dense) columns in each row. No empty rows exist. */ + /* Some "live" columns may contain only dead rows, however. These are */ + /* pruned in the code below. */ + + /* now find the initial matlab score for each column */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip dead column */ + if (COL_IS_DEAD (c)) + { + continue ; + } + score = 0 ; + cp = &A [Col [c].start] ; + new_cp = cp ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + /* skip if dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + /* compact the column */ + *new_cp++ = row ; + /* add row's external degree */ + score += Row [row].shared1.degree - 1 ; + /* guard against integer overflow */ + score = MIN (score, n_col) ; + } + /* determine pruned column length */ + col_length = (Int) (new_cp - &A [Col [c].start]) ; + if (col_length == 0) + { + /* a newly-made null column (all rows in this col are "dense" */ + /* and have already been killed) */ + DEBUG2 (("Newly null killed: %d\n", c)) ; + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + } + else + { + /* set column length and set score */ + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + Col [c].length = col_length ; + Col [c].shared2.score = score ; + } + } + DEBUG1 (("colamd: Dense, null, and newly-null columns killed: %d\n", + n_col-n_col2)) ; + + /* At this point, all empty rows and columns are dead. All live columns */ + /* are "clean" (containing no dead rows) and simplicial (no supercolumns */ + /* yet). Rows may contain dead columns, but all live rows contain at */ + /* least one live column. */ + +#ifndef NDEBUG + debug_structures (n_row, n_col, Row, Col, A, n_col2) ; +#endif /* NDEBUG */ + + /* === Initialize degree lists ========================================== */ + +#ifndef NDEBUG + debug_count = 0 ; +#endif /* NDEBUG */ + + /* clear the hash buckets */ + for (c = 0 ; c <= n_col ; c++) + { + head [c] = EMPTY ; + } + min_score = n_col ; + /* place in reverse order, so low column indices are at the front */ + /* of the lists. This is to encourage natural tie-breaking */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* only add principal columns to degree lists */ + if (COL_IS_ALIVE (c)) + { + DEBUG4 (("place %d score %d minscore %d ncol %d\n", + c, Col [c].shared2.score, min_score, n_col)) ; + + /* === Add columns score to DList =============================== */ + + score = Col [c].shared2.score ; + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + ASSERT (head [score] >= EMPTY) ; + + /* now add this column to dList at proper score location */ + next_col = head [score] ; + Col [c].shared3.prev = EMPTY ; + Col [c].shared4.degree_next = next_col ; + + /* if there already was a column with the same score, set its */ + /* previous pointer to this new column */ + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = c ; + } + head [score] = c ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, score) ; + +#ifndef NDEBUG + debug_count++ ; +#endif /* NDEBUG */ + + } + } + +#ifndef NDEBUG + DEBUG1 (("colamd: Live cols %d out of %d, non-princ: %d\n", + debug_count, n_col, n_col-debug_count)) ; + ASSERT (debug_count == n_col2) ; + debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ; +#endif /* NDEBUG */ + + /* === Return number of remaining columns, and max row degree =========== */ + + *p_n_col2 = n_col2 ; + *p_n_row2 = n_row2 ; + *p_max_deg = max_deg ; +} + + +/* ========================================================================== */ +/* === find_ordering ======================================================== */ +/* ========================================================================== */ + +/* + Order the principal columns of the supercolumn form of the matrix + (no supercolumns on input). Uses a minimum approximate column minimum + degree ordering method. Not user-callable. +*/ + +PRIVATE Int find_ordering /* return the number of garbage collections */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Int Alen, /* size of A, 2*nnz + n_col or larger */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* column form and row form of A */ + Int head [], /* of size n_col+1 */ + Int n_col2, /* Remaining columns to order */ + Int max_deg, /* Maximum row degree */ + Int pfree, /* index of first free slot (2*nnz on entry) */ + Int aggressive +) +{ + /* === Local variables ================================================== */ + + Int k ; /* current pivot ordering step */ + Int pivot_col ; /* current pivot column */ + Int *cp ; /* a column pointer */ + Int *rp ; /* a row pointer */ + Int pivot_row ; /* current pivot row */ + Int *new_cp ; /* modified column pointer */ + Int *new_rp ; /* modified row pointer */ + Int pivot_row_start ; /* pointer to start of pivot row */ + Int pivot_row_degree ; /* number of columns in pivot row */ + Int pivot_row_length ; /* number of supercolumns in pivot row */ + Int pivot_col_score ; /* score of pivot column */ + Int needed_memory ; /* free space needed for pivot row */ + Int *cp_end ; /* pointer to the end of a column */ + Int *rp_end ; /* pointer to the end of a row */ + Int row ; /* a row index */ + Int col ; /* a column index */ + Int max_score ; /* maximum possible score */ + Int cur_score ; /* score of current column */ + unsigned Int hash ; /* hash value for supernode detection */ + Int head_column ; /* head of hash bucket */ + Int first_col ; /* first column in hash bucket */ + Int tag_mark ; /* marker value for mark array */ + Int row_mark ; /* Row [row].shared2.mark */ + Int set_difference ; /* set difference size of row with pivot row */ + Int min_score ; /* smallest column score */ + Int col_thickness ; /* "thickness" (no. of columns in a supercol) */ + Int max_mark ; /* maximum value of tag_mark */ + Int pivot_col_thickness ; /* number of columns represented by pivot col */ + Int prev_col ; /* Used by Dlist operations. */ + Int next_col ; /* Used by Dlist operations. */ + Int ngarbage ; /* number of garbage collections performed */ + +#ifndef NDEBUG + Int debug_d ; /* debug loop counter */ + Int debug_step = 0 ; /* debug loop counter */ +#endif /* NDEBUG */ + + /* === Initialization and clear mark ==================================== */ + + max_mark = INT_MAX - n_col ; /* INT_MAX defined in */ + tag_mark = clear_mark (0, max_mark, n_row, Row) ; + min_score = 0 ; + ngarbage = 0 ; + DEBUG1 (("colamd: Ordering, n_col2=%d\n", n_col2)) ; + + /* === Order the columns ================================================ */ + + for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */) + { + +#ifndef NDEBUG + if (debug_step % 100 == 0) + { + DEBUG2 (("\n... Step k: %d out of n_col2: %d\n", k, n_col2)) ; + } + else + { + DEBUG3 (("\n----------Step k: %d out of n_col2: %d\n", k, n_col2)) ; + } + debug_step++ ; + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + + /* === Select pivot column, and order it ============================ */ + + /* make sure degree list isn't empty */ + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (head [min_score] >= EMPTY) ; + +#ifndef NDEBUG + for (debug_d = 0 ; debug_d < min_score ; debug_d++) + { + ASSERT (head [debug_d] == EMPTY) ; + } +#endif /* NDEBUG */ + + /* get pivot column from head of minimum degree list */ + while (head [min_score] == EMPTY && min_score < n_col) + { + min_score++ ; + } + pivot_col = head [min_score] ; + ASSERT (pivot_col >= 0 && pivot_col <= n_col) ; + next_col = Col [pivot_col].shared4.degree_next ; + head [min_score] = next_col ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = EMPTY ; + } + + ASSERT (COL_IS_ALIVE (pivot_col)) ; + + /* remember score for defrag check */ + pivot_col_score = Col [pivot_col].shared2.score ; + + /* the pivot column is the kth column in the pivot order */ + Col [pivot_col].shared2.order = k ; + + /* increment order count by column thickness */ + pivot_col_thickness = Col [pivot_col].shared1.thickness ; + k += pivot_col_thickness ; + ASSERT (pivot_col_thickness > 0) ; + DEBUG3 (("Pivot col: %d thick %d\n", pivot_col, pivot_col_thickness)) ; + + /* === Garbage_collection, if necessary ============================= */ + + needed_memory = MIN (pivot_col_score, n_col - k) ; + if (pfree + needed_memory >= Alen) + { + pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ; + ngarbage++ ; + /* after garbage collection we will have enough */ + ASSERT (pfree + needed_memory < Alen) ; + /* garbage collection has wiped out the Row[].shared2.mark array */ + tag_mark = clear_mark (0, max_mark, n_row, Row) ; + +#ifndef NDEBUG + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + } + + /* === Compute pivot row pattern ==================================== */ + + /* get starting location for this new merged row */ + pivot_row_start = pfree ; + + /* initialize new row counts to zero */ + pivot_row_degree = 0 ; + + /* tag pivot column as having been visited so it isn't included */ + /* in merged pivot row */ + Col [pivot_col].shared1.thickness = -pivot_col_thickness ; + + /* pivot row is the union of all rows in the pivot column pattern */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + DEBUG4 (("Pivot col pattern %d %d\n", ROW_IS_ALIVE (row), row)) ; + /* skip if row is dead */ + if (ROW_IS_ALIVE (row)) + { + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + /* add the column, if alive and untagged */ + col_thickness = Col [col].shared1.thickness ; + if (col_thickness > 0 && COL_IS_ALIVE (col)) + { + /* tag column in pivot row */ + Col [col].shared1.thickness = -col_thickness ; + ASSERT (pfree < Alen) ; + /* place column in pivot row */ + A [pfree++] = col ; + pivot_row_degree += col_thickness ; + } + } + } + } + + /* clear tag on pivot column */ + Col [pivot_col].shared1.thickness = pivot_col_thickness ; + max_deg = MAX (max_deg, pivot_row_degree) ; + +#ifndef NDEBUG + DEBUG3 (("check2\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Kill all rows used to construct pivot row ==================== */ + + /* also kill pivot row, temporarily */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* may be killing an already dead row */ + row = *cp++ ; + DEBUG3 (("Kill row in pivot col: %d\n", row)) ; + KILL_ROW (row) ; + } + + /* === Select a row index to use as the new pivot row =============== */ + + pivot_row_length = pfree - pivot_row_start ; + if (pivot_row_length > 0) + { + /* pick the "pivot" row arbitrarily (first row in col) */ + pivot_row = A [Col [pivot_col].start] ; + DEBUG3 (("Pivotal row is %d\n", pivot_row)) ; + } + else + { + /* there is no pivot row, since it is of zero length */ + pivot_row = EMPTY ; + ASSERT (pivot_row_length == 0) ; + } + ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ; + + /* === Approximate degree computation =============================== */ + + /* Here begins the computation of the approximate degree. The column */ + /* score is the sum of the pivot row "length", plus the size of the */ + /* set differences of each row in the column minus the pattern of the */ + /* pivot row itself. The column ("thickness") itself is also */ + /* excluded from the column score (we thus use an approximate */ + /* external degree). */ + + /* The time taken by the following code (compute set differences, and */ + /* add them up) is proportional to the size of the data structure */ + /* being scanned - that is, the sum of the sizes of each column in */ + /* the pivot row. Thus, the amortized time to compute a column score */ + /* is proportional to the size of that column (where size, in this */ + /* context, is the column "length", or the number of row indices */ + /* in that column). The number of row indices in a column is */ + /* monotonically non-decreasing, from the length of the original */ + /* column on input to colamd. */ + + /* === Compute set differences ====================================== */ + + DEBUG3 (("** Computing set differences phase. **\n")) ; + + /* pivot row is currently dead - it will be revived later. */ + + DEBUG3 (("Pivot row: ")) ; + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + DEBUG3 (("Col: %d\n", col)) ; + + /* clear tags used to construct pivot row pattern */ + col_thickness = -Col [col].shared1.thickness ; + ASSERT (col_thickness > 0) ; + Col [col].shared1.thickness = col_thickness ; + + /* === Remove column from degree list =========================== */ + + cur_score = Col [col].shared2.score ; + prev_col = Col [col].shared3.prev ; + next_col = Col [col].shared4.degree_next ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (cur_score >= EMPTY) ; + if (prev_col == EMPTY) + { + head [cur_score] = next_col ; + } + else + { + Col [prev_col].shared4.degree_next = next_col ; + } + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = prev_col ; + } + + /* === Scan the column ========================================== */ + + cp = &A [Col [col].start] ; + cp_end = cp + Col [col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + continue ; + } + ASSERT (row != pivot_row) ; + set_difference = row_mark - tag_mark ; + /* check if the row has been seen yet */ + if (set_difference < 0) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + set_difference = Row [row].shared1.degree ; + } + /* subtract column thickness from this row's set difference */ + set_difference -= col_thickness ; + ASSERT (set_difference >= 0) ; + /* absorb this row if the set difference becomes zero */ + if (set_difference == 0 && aggressive) + { + DEBUG3 (("aggressive absorption. Row: %d\n", row)) ; + KILL_ROW (row) ; + } + else + { + /* save the new mark */ + Row [row].shared2.mark = set_difference + tag_mark ; + } + } + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k-pivot_row_degree, max_deg) ; +#endif /* NDEBUG */ + + /* === Add up set differences for each column ======================= */ + + DEBUG3 (("** Adding set differences phase. **\n")) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + hash = 0 ; + cur_score = 0 ; + cp = &A [Col [col].start] ; + /* compact the column */ + new_cp = cp ; + cp_end = cp + Col [col].length ; + + DEBUG4 (("Adding set diffs for Col: %d.\n", col)) ; + + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + ASSERT(row >= 0 && row < n_row) ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + DEBUG4 ((" Row %d, dead\n", row)) ; + continue ; + } + DEBUG4 ((" Row %d, set diff %d\n", row, row_mark-tag_mark)); + ASSERT (row_mark >= tag_mark) ; + /* compact the column */ + *new_cp++ = row ; + /* compute hash function */ + hash += row ; + /* add set difference */ + cur_score += row_mark - tag_mark ; + /* integer overflow... */ + cur_score = MIN (cur_score, n_col) ; + } + + /* recompute the column's length */ + Col [col].length = (Int) (new_cp - &A [Col [col].start]) ; + + /* === Further mass elimination ================================= */ + + if (Col [col].length == 0) + { + DEBUG4 (("further mass elimination. Col: %d\n", col)) ; + /* nothing left but the pivot row in this column */ + KILL_PRINCIPAL_COL (col) ; + pivot_row_degree -= Col [col].shared1.thickness ; + ASSERT (pivot_row_degree >= 0) ; + /* order it */ + Col [col].shared2.order = k ; + /* increment order count by column thickness */ + k += Col [col].shared1.thickness ; + } + else + { + /* === Prepare for supercolumn detection ==================== */ + + DEBUG4 (("Preparing supercol detection for Col: %d.\n", col)) ; + + /* save score so far */ + Col [col].shared2.score = cur_score ; + + /* add column to hash table, for supercolumn detection */ + hash %= n_col + 1 ; + + DEBUG4 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ; + ASSERT (((Int) hash) <= n_col) ; + + head_column = head [hash] ; + if (head_column > EMPTY) + { + /* degree list "hash" is non-empty, use prev (shared3) of */ + /* first column in degree list as head of hash bucket */ + first_col = Col [head_column].shared3.headhash ; + Col [head_column].shared3.headhash = col ; + } + else + { + /* degree list "hash" is empty, use head as hash bucket */ + first_col = - (head_column + 2) ; + head [hash] = - (col + 2) ; + } + Col [col].shared4.hash_next = first_col ; + + /* save hash function in Col [col].shared3.hash */ + Col [col].shared3.hash = (Int) hash ; + ASSERT (COL_IS_ALIVE (col)) ; + } + } + + /* The approximate external column degree is now computed. */ + + /* === Supercolumn detection ======================================== */ + + DEBUG3 (("** Supercolumn detection phase. **\n")) ; + + detect_super_cols ( + +#ifndef NDEBUG + n_col, Row, +#endif /* NDEBUG */ + + Col, A, head, pivot_row_start, pivot_row_length) ; + + /* === Kill the pivotal column ====================================== */ + + KILL_PRINCIPAL_COL (pivot_col) ; + + /* === Clear mark =================================================== */ + + tag_mark = clear_mark (tag_mark+max_deg+1, max_mark, n_row, Row) ; + +#ifndef NDEBUG + DEBUG3 (("check3\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Finalize the new pivot row, and column scores ================ */ + + DEBUG3 (("** Finalize scores phase. **\n")) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + /* compact the pivot row */ + new_rp = rp ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + /* skip dead columns */ + if (COL_IS_DEAD (col)) + { + continue ; + } + *new_rp++ = col ; + /* add new pivot row to column */ + A [Col [col].start + (Col [col].length++)] = pivot_row ; + + /* retrieve score so far and add on pivot row's degree. */ + /* (we wait until here for this in case the pivot */ + /* row's degree was reduced due to mass elimination). */ + cur_score = Col [col].shared2.score + pivot_row_degree ; + + /* calculate the max possible score as the number of */ + /* external columns minus the 'k' value minus the */ + /* columns thickness */ + max_score = n_col - k - Col [col].shared1.thickness ; + + /* make the score the external degree of the union-of-rows */ + cur_score -= Col [col].shared1.thickness ; + + /* make sure score is less or equal than the max score */ + cur_score = MIN (cur_score, max_score) ; + ASSERT (cur_score >= 0) ; + + /* store updated score */ + Col [col].shared2.score = cur_score ; + + /* === Place column back in degree list ========================= */ + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (head [cur_score] >= EMPTY) ; + next_col = head [cur_score] ; + Col [col].shared4.degree_next = next_col ; + Col [col].shared3.prev = EMPTY ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = col ; + } + head [cur_score] = col ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, cur_score) ; + + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; +#endif /* NDEBUG */ + + /* === Resurrect the new pivot row ================================== */ + + if (pivot_row_degree > 0) + { + /* update pivot row length to reflect any cols that were killed */ + /* during super-col detection and mass elimination */ + Row [pivot_row].start = pivot_row_start ; + Row [pivot_row].length = (Int) (new_rp - &A[pivot_row_start]) ; + ASSERT (Row [pivot_row].length > 0) ; + Row [pivot_row].shared1.degree = pivot_row_degree ; + Row [pivot_row].shared2.mark = 0 ; + /* pivot row is no longer dead */ + + DEBUG1 (("Resurrect Pivot_row %d deg: %d\n", + pivot_row, pivot_row_degree)) ; + } + } + + /* === All principal columns have now been ordered ====================== */ + + return (ngarbage) ; +} + + +/* ========================================================================== */ +/* === order_children ======================================================= */ +/* ========================================================================== */ + +/* + The find_ordering routine has ordered all of the principal columns (the + representatives of the supercolumns). The non-principal columns have not + yet been ordered. This routine orders those columns by walking up the + parent tree (a column is a child of the column which absorbed it). The + final permutation vector is then placed in p [0 ... n_col-1], with p [0] + being the first column, and p [n_col-1] being the last. It doesn't look + like it at first glance, but be assured that this routine takes time linear + in the number of columns. Although not immediately obvious, the time + taken by this routine is O (n_col), that is, linear in the number of + columns. Not user-callable. +*/ + +PRIVATE void order_children +( + /* === Parameters ======================================================= */ + + Int n_col, /* number of columns of A */ + Colamd_Col Col [], /* of size n_col+1 */ + Int p [] /* p [0 ... n_col-1] is the column permutation*/ +) +{ + /* === Local variables ================================================== */ + + Int i ; /* loop counter for all columns */ + Int c ; /* column index */ + Int parent ; /* index of column's parent */ + Int order ; /* column's order */ + + /* === Order each non-principal column ================================== */ + + for (i = 0 ; i < n_col ; i++) + { + /* find an un-ordered non-principal column */ + ASSERT (COL_IS_DEAD (i)) ; + if (!COL_IS_DEAD_PRINCIPAL (i) && Col [i].shared2.order == EMPTY) + { + parent = i ; + /* once found, find its principal parent */ + do + { + parent = Col [parent].shared1.parent ; + } while (!COL_IS_DEAD_PRINCIPAL (parent)) ; + + /* now, order all un-ordered non-principal columns along path */ + /* to this parent. collapse tree at the same time */ + c = i ; + /* get order of parent */ + order = Col [parent].shared2.order ; + + do + { + ASSERT (Col [c].shared2.order == EMPTY) ; + + /* order this column */ + Col [c].shared2.order = order++ ; + /* collaps tree */ + Col [c].shared1.parent = parent ; + + /* get immediate parent of this column */ + c = Col [c].shared1.parent ; + + /* continue until we hit an ordered column. There are */ + /* guarranteed not to be anymore unordered columns */ + /* above an ordered column */ + } while (Col [c].shared2.order == EMPTY) ; + + /* re-order the super_col parent to largest order for this group */ + Col [parent].shared2.order = order ; + } + } + + /* === Generate the permutation ========================================= */ + + for (c = 0 ; c < n_col ; c++) + { + p [Col [c].shared2.order] = c ; + } +} + + +/* ========================================================================== */ +/* === detect_super_cols ==================================================== */ +/* ========================================================================== */ + +/* + Detects supercolumns by finding matches between columns in the hash buckets. + Check amongst columns in the set A [row_start ... row_start + row_length-1]. + The columns under consideration are currently *not* in the degree lists, + and have already been placed in the hash buckets. + + The hash bucket for columns whose hash function is equal to h is stored + as follows: + + if head [h] is >= 0, then head [h] contains a degree list, so: + + head [h] is the first column in degree bucket h. + Col [head [h]].headhash gives the first column in hash bucket h. + + otherwise, the degree list is empty, and: + + -(head [h] + 2) is the first column in hash bucket h. + + For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous + column" pointer. Col [c].shared3.hash is used instead as the hash number + for that column. The value of Col [c].shared4.hash_next is the next column + in the same hash bucket. + + Assuming no, or "few" hash collisions, the time taken by this routine is + linear in the sum of the sizes (lengths) of each column whose score has + just been computed in the approximate degree computation. + Not user-callable. +*/ + +PRIVATE void detect_super_cols +( + /* === Parameters ======================================================= */ + +#ifndef NDEBUG + /* these two parameters are only needed when debugging is enabled: */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ +#endif /* NDEBUG */ + + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* row indices of A */ + Int head [], /* head of degree lists and hash buckets */ + Int row_start, /* pointer to set of columns to check */ + Int row_length /* number of columns to check */ +) +{ + /* === Local variables ================================================== */ + + Int hash ; /* hash value for a column */ + Int *rp ; /* pointer to a row */ + Int c ; /* a column index */ + Int super_c ; /* column index of the column to absorb into */ + Int *cp1 ; /* column pointer for column super_c */ + Int *cp2 ; /* column pointer for column c */ + Int length ; /* length of column super_c */ + Int prev_c ; /* column preceding c in hash bucket */ + Int i ; /* loop counter */ + Int *rp_end ; /* pointer to the end of the row */ + Int col ; /* a column index in the row to check */ + Int head_column ; /* first column in hash bucket or degree list */ + Int first_col ; /* first column in hash bucket */ + + /* === Consider each column in the row ================================== */ + + rp = &A [row_start] ; + rp_end = rp + row_length ; + while (rp < rp_end) + { + col = *rp++ ; + if (COL_IS_DEAD (col)) + { + continue ; + } + + /* get hash number for this column */ + hash = Col [col].shared3.hash ; + ASSERT (hash <= n_col) ; + + /* === Get the first column in this hash bucket ===================== */ + + head_column = head [hash] ; + if (head_column > EMPTY) + { + first_col = Col [head_column].shared3.headhash ; + } + else + { + first_col = - (head_column + 2) ; + } + + /* === Consider each column in the hash bucket ====================== */ + + for (super_c = first_col ; super_c != EMPTY ; + super_c = Col [super_c].shared4.hash_next) + { + ASSERT (COL_IS_ALIVE (super_c)) ; + ASSERT (Col [super_c].shared3.hash == hash) ; + length = Col [super_c].length ; + + /* prev_c is the column preceding column c in the hash bucket */ + prev_c = super_c ; + + /* === Compare super_c with all columns after it ================ */ + + for (c = Col [super_c].shared4.hash_next ; + c != EMPTY ; c = Col [c].shared4.hash_next) + { + ASSERT (c != super_c) ; + ASSERT (COL_IS_ALIVE (c)) ; + ASSERT (Col [c].shared3.hash == hash) ; + + /* not identical if lengths or scores are different */ + if (Col [c].length != length || + Col [c].shared2.score != Col [super_c].shared2.score) + { + prev_c = c ; + continue ; + } + + /* compare the two columns */ + cp1 = &A [Col [super_c].start] ; + cp2 = &A [Col [c].start] ; + + for (i = 0 ; i < length ; i++) + { + /* the columns are "clean" (no dead rows) */ + ASSERT (ROW_IS_ALIVE (*cp1)) ; + ASSERT (ROW_IS_ALIVE (*cp2)) ; + /* row indices will same order for both supercols, */ + /* no gather scatter nessasary */ + if (*cp1++ != *cp2++) + { + break ; + } + } + + /* the two columns are different if the for-loop "broke" */ + if (i != length) + { + prev_c = c ; + continue ; + } + + /* === Got it! two columns are identical =================== */ + + ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ; + + Col [super_c].shared1.thickness += Col [c].shared1.thickness ; + Col [c].shared1.parent = super_c ; + KILL_NON_PRINCIPAL_COL (c) ; + /* order c later, in order_children() */ + Col [c].shared2.order = EMPTY ; + /* remove c from hash bucket */ + Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ; + } + } + + /* === Empty this hash bucket ======================================= */ + + if (head_column > EMPTY) + { + /* corresponding degree list "hash" is not empty */ + Col [head_column].shared3.headhash = EMPTY ; + } + else + { + /* corresponding degree list "hash" is empty */ + head [hash] = EMPTY ; + } + } +} + + +/* ========================================================================== */ +/* === garbage_collection =================================================== */ +/* ========================================================================== */ + +/* + Defragments and compacts columns and rows in the workspace A. Used when + all avaliable memory has been used while performing row merging. Returns + the index of the first free position in A, after garbage collection. The + time taken by this routine is linear is the size of the array A, which is + itself linear in the number of nonzeros in the input matrix. + Not user-callable. +*/ + +PRIVATE Int garbage_collection /* returns the new value of pfree */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows */ + Int n_col, /* number of columns */ + Colamd_Row Row [], /* row info */ + Colamd_Col Col [], /* column info */ + Int A [], /* A [0 ... Alen-1] holds the matrix */ + Int *pfree /* &A [0] ... pfree is in use */ +) +{ + /* === Local variables ================================================== */ + + Int *psrc ; /* source pointer */ + Int *pdest ; /* destination pointer */ + Int j ; /* counter */ + Int r ; /* a row index */ + Int c ; /* a column index */ + Int length ; /* length of a row or column */ + +#ifndef NDEBUG + Int debug_rows ; + DEBUG2 (("Defrag..\n")) ; + for (psrc = &A[0] ; psrc < pfree ; psrc++) ASSERT (*psrc >= 0) ; + debug_rows = 0 ; +#endif /* NDEBUG */ + + /* === Defragment the columns =========================================== */ + + pdest = &A[0] ; + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + psrc = &A [Col [c].start] ; + + /* move and compact the column */ + ASSERT (pdest <= psrc) ; + Col [c].start = (Int) (pdest - &A [0]) ; + length = Col [c].length ; + for (j = 0 ; j < length ; j++) + { + r = *psrc++ ; + if (ROW_IS_ALIVE (r)) + { + *pdest++ = r ; + } + } + Col [c].length = (Int) (pdest - &A [Col [c].start]) ; + } + } + + /* === Prepare to defragment the rows =================================== */ + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_DEAD (r) || (Row [r].length == 0)) + { + /* This row is already dead, or is of zero length. Cannot compact + * a row of zero length, so kill it. NOTE: in the current version, + * there are no zero-length live rows. Kill the row (for the first + * time, or again) just to be safe. */ + KILL_ROW (r) ; + } + else + { + /* save first column index in Row [r].shared2.first_column */ + psrc = &A [Row [r].start] ; + Row [r].shared2.first_column = *psrc ; + ASSERT (ROW_IS_ALIVE (r)) ; + /* flag the start of the row with the one's complement of row */ + *psrc = ONES_COMPLEMENT (r) ; +#ifndef NDEBUG + debug_rows++ ; +#endif /* NDEBUG */ + } + } + + /* === Defragment the rows ============================================== */ + + psrc = pdest ; + while (psrc < pfree) + { + /* find a negative number ... the start of a row */ + if (*psrc++ < 0) + { + psrc-- ; + /* get the row index */ + r = ONES_COMPLEMENT (*psrc) ; + ASSERT (r >= 0 && r < n_row) ; + /* restore first column index */ + *psrc = Row [r].shared2.first_column ; + ASSERT (ROW_IS_ALIVE (r)) ; + ASSERT (Row [r].length > 0) ; + /* move and compact the row */ + ASSERT (pdest <= psrc) ; + Row [r].start = (Int) (pdest - &A [0]) ; + length = Row [r].length ; + for (j = 0 ; j < length ; j++) + { + c = *psrc++ ; + if (COL_IS_ALIVE (c)) + { + *pdest++ = c ; + } + } + Row [r].length = (Int) (pdest - &A [Row [r].start]) ; + ASSERT (Row [r].length > 0) ; +#ifndef NDEBUG + debug_rows-- ; +#endif /* NDEBUG */ + } + } + /* ensure we found all the rows */ + ASSERT (debug_rows == 0) ; + + /* === Return the new value of pfree ==================================== */ + + return ((Int) (pdest - &A [0])) ; +} + + +/* ========================================================================== */ +/* === clear_mark =========================================================== */ +/* ========================================================================== */ + +/* + Clears the Row [].shared2.mark array, and returns the new tag_mark. + Return value is the new tag_mark. Not user-callable. +*/ + +PRIVATE Int clear_mark /* return the new value for tag_mark */ +( + /* === Parameters ======================================================= */ + + Int tag_mark, /* new value of tag_mark */ + Int max_mark, /* max allowed value of tag_mark */ + + Int n_row, /* number of rows in A */ + Colamd_Row Row [] /* Row [0 ... n_row-1].shared2.mark is set to zero */ +) +{ + /* === Local variables ================================================== */ + + Int r ; + + if (tag_mark <= 0 || tag_mark >= max_mark) + { + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + Row [r].shared2.mark = 0 ; + } + } + tag_mark = 1 ; + } + + return (tag_mark) ; +} + + +/* ========================================================================== */ +/* === print_report ========================================================= */ +/* ========================================================================== */ + +PRIVATE void print_report +( + char *method, + Int stats [COLAMD_STATS] +) +{ + + Int i1, i2, i3 ; + + PRINTF (("\n%s version %d.%d, %s: ", method, + COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE)) ; + + if (!stats) + { + PRINTF (("No statistics available.\n")) ; + return ; + } + + i1 = stats [COLAMD_INFO1] ; + i2 = stats [COLAMD_INFO2] ; + i3 = stats [COLAMD_INFO3] ; + + if (stats [COLAMD_STATUS] >= 0) + { + PRINTF (("OK. ")) ; + } + else + { + PRINTF (("ERROR. ")) ; + } + + switch (stats [COLAMD_STATUS]) + { + + case COLAMD_OK_BUT_JUMBLED: + + PRINTF(("Matrix has unsorted or duplicate row indices.\n")) ; + + PRINTF(("%s: number of duplicate or out-of-order row indices: %d\n", + method, i3)) ; + + PRINTF(("%s: last seen duplicate or out-of-order row index: %d\n", + method, INDEX (i2))) ; + + PRINTF(("%s: last seen in column: %d", + method, INDEX (i1))) ; + + /* no break - fall through to next case instead */ + + case COLAMD_OK: + + PRINTF(("\n")) ; + + PRINTF(("%s: number of dense or empty rows ignored: %d\n", + method, stats [COLAMD_DENSE_ROW])) ; + + PRINTF(("%s: number of dense or empty columns ignored: %d\n", + method, stats [COLAMD_DENSE_COL])) ; + + PRINTF(("%s: number of garbage collections performed: %d\n", + method, stats [COLAMD_DEFRAG_COUNT])) ; + break ; + + case COLAMD_ERROR_A_not_present: + + PRINTF(("Array A (row indices of matrix) not present.\n")) ; + break ; + + case COLAMD_ERROR_p_not_present: + + PRINTF(("Array p (column pointers for matrix) not present.\n")) ; + break ; + + case COLAMD_ERROR_nrow_negative: + + PRINTF(("Invalid number of rows (%d).\n", i1)) ; + break ; + + case COLAMD_ERROR_ncol_negative: + + PRINTF(("Invalid number of columns (%d).\n", i1)) ; + break ; + + case COLAMD_ERROR_nnz_negative: + + PRINTF(("Invalid number of nonzero entries (%d).\n", i1)) ; + break ; + + case COLAMD_ERROR_p0_nonzero: + + PRINTF(("Invalid column pointer, p [0] = %d, must be zero.\n", i1)); + break ; + + case COLAMD_ERROR_A_too_small: + + PRINTF(("Array A too small.\n")) ; + PRINTF((" Need Alen >= %d, but given only Alen = %d.\n", + i1, i2)) ; + break ; + + case COLAMD_ERROR_col_length_negative: + + PRINTF + (("Column %d has a negative number of nonzero entries (%d).\n", + INDEX (i1), i2)) ; + break ; + + case COLAMD_ERROR_row_index_out_of_bounds: + + PRINTF + (("Row index (row %d) out of bounds (%d to %d) in column %d.\n", + INDEX (i2), INDEX (0), INDEX (i3-1), INDEX (i1))) ; + break ; + + case COLAMD_ERROR_out_of_memory: + + PRINTF(("Out of memory.\n")) ; + break ; + + /* v2.4: internal-error case deleted */ + } +} + + + + +/* ========================================================================== */ +/* === colamd debugging routines ============================================ */ +/* ========================================================================== */ + +/* When debugging is disabled, the remainder of this file is ignored. */ + +#ifndef NDEBUG + + +/* ========================================================================== */ +/* === debug_structures ===================================================== */ +/* ========================================================================== */ + +/* + At this point, all empty rows and columns are dead. All live columns + are "clean" (containing no dead rows) and simplicial (no supercolumns + yet). Rows may contain dead columns, but all live rows contain at + least one live column. +*/ + +PRIVATE void debug_structures +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int n_col2 +) +{ + /* === Local variables ================================================== */ + + Int i ; + Int c ; + Int *cp ; + Int *cp_end ; + Int len ; + Int score ; + Int r ; + Int *rp ; + Int *rp_end ; + Int deg ; + + /* === Check A, Row, and Col ============================================ */ + + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + len = Col [c].length ; + score = Col [c].shared2.score ; + DEBUG4 (("initial live col %5d %5d %5d\n", c, len, score)) ; + ASSERT (len > 0) ; + ASSERT (score >= 0) ; + ASSERT (Col [c].shared1.thickness == 1) ; + cp = &A [Col [c].start] ; + cp_end = cp + len ; + while (cp < cp_end) + { + r = *cp++ ; + ASSERT (ROW_IS_ALIVE (r)) ; + } + } + else + { + i = Col [c].shared2.order ; + ASSERT (i >= n_col2 && i < n_col) ; + } + } + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + i = 0 ; + len = Row [r].length ; + deg = Row [r].shared1.degree ; + ASSERT (len > 0) ; + ASSERT (deg > 0) ; + rp = &A [Row [r].start] ; + rp_end = rp + len ; + while (rp < rp_end) + { + c = *rp++ ; + if (COL_IS_ALIVE (c)) + { + i++ ; + } + } + ASSERT (i > 0) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_deg_lists ====================================================== */ +/* ========================================================================== */ + +/* + Prints the contents of the degree lists. Counts the number of columns + in the degree list and compares it to the total it should have. Also + checks the row degrees. +*/ + +PRIVATE void debug_deg_lists +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int head [], + Int min_score, + Int should, + Int max_deg +) +{ + /* === Local variables ================================================== */ + + Int deg ; + Int col ; + Int have ; + Int row ; + + /* === Check the degree lists =========================================== */ + + if (n_col > 10000 && colamd_debug <= 0) + { + return ; + } + have = 0 ; + DEBUG4 (("Degree lists: %d\n", min_score)) ; + for (deg = 0 ; deg <= n_col ; deg++) + { + col = head [deg] ; + if (col == EMPTY) + { + continue ; + } + DEBUG4 (("%d:", deg)) ; + while (col != EMPTY) + { + DEBUG4 ((" %d", col)) ; + have += Col [col].shared1.thickness ; + ASSERT (COL_IS_ALIVE (col)) ; + col = Col [col].shared4.degree_next ; + } + DEBUG4 (("\n")) ; + } + DEBUG4 (("should %d have %d\n", should, have)) ; + ASSERT (should == have) ; + + /* === Check the row degrees ============================================ */ + + if (n_row > 10000 && colamd_debug <= 0) + { + return ; + } + for (row = 0 ; row < n_row ; row++) + { + if (ROW_IS_ALIVE (row)) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_mark =========================================================== */ +/* ========================================================================== */ + +/* + Ensures that the tag_mark is less that the maximum and also ensures that + each entry in the mark array is less than the tag mark. +*/ + +PRIVATE void debug_mark +( + /* === Parameters ======================================================= */ + + Int n_row, + Colamd_Row Row [], + Int tag_mark, + Int max_mark +) +{ + /* === Local variables ================================================== */ + + Int r ; + + /* === Check the Row marks ============================================== */ + + ASSERT (tag_mark > 0 && tag_mark <= max_mark) ; + if (n_row > 10000 && colamd_debug <= 0) + { + return ; + } + for (r = 0 ; r < n_row ; r++) + { + ASSERT (Row [r].shared2.mark < tag_mark) ; + } +} + + +/* ========================================================================== */ +/* === debug_matrix ========================================================= */ +/* ========================================================================== */ + +/* + Prints out the contents of the columns and the rows. +*/ + +PRIVATE void debug_matrix +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [] +) +{ + /* === Local variables ================================================== */ + + Int r ; + Int c ; + Int *rp ; + Int *rp_end ; + Int *cp ; + Int *cp_end ; + + /* === Dump the rows and columns of the matrix ========================== */ + + if (colamd_debug < 3) + { + return ; + } + DEBUG3 (("DUMP MATRIX:\n")) ; + for (r = 0 ; r < n_row ; r++) + { + DEBUG3 (("Row %d alive? %d\n", r, ROW_IS_ALIVE (r))) ; + if (ROW_IS_DEAD (r)) + { + continue ; + } + DEBUG3 (("start %d length %d degree %d\n", + Row [r].start, Row [r].length, Row [r].shared1.degree)) ; + rp = &A [Row [r].start] ; + rp_end = rp + Row [r].length ; + while (rp < rp_end) + { + c = *rp++ ; + DEBUG4 ((" %d col %d\n", COL_IS_ALIVE (c), c)) ; + } + } + + for (c = 0 ; c < n_col ; c++) + { + DEBUG3 (("Col %d alive? %d\n", c, COL_IS_ALIVE (c))) ; + if (COL_IS_DEAD (c)) + { + continue ; + } + DEBUG3 (("start %d length %d shared1 %d shared2 %d\n", + Col [c].start, Col [c].length, + Col [c].shared1.thickness, Col [c].shared2.score)) ; + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + r = *cp++ ; + DEBUG4 ((" %d row %d\n", ROW_IS_ALIVE (r), r)) ; + } + } +} + +PRIVATE void colamd_get_debug +( + char *method +) +{ + FILE *f ; + colamd_debug = 0 ; /* no debug printing */ + f = fopen ("debug", "r") ; + if (f == (FILE *) NULL) + { + colamd_debug = 0 ; + } + else + { + fscanf (f, "%d", &colamd_debug) ; + fclose (f) ; + } + DEBUG0 (("%s: debug version, D = %d (THIS WILL BE SLOW!)\n", + method, colamd_debug)) ; +} + +#endif /* NDEBUG */ diff -r d59bea55db9b -r c445c931472f src/colamd/colamd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/colamd/colamd.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,69 @@ +/* colamd.h */ + +/* Written by Andrew Makhorin . */ + +#ifndef COLAMD_H +#define COLAMD_H + +#define _GLPSTD_STDIO +#include "glpenv.h" + +#define COLAMD_DATE "Nov 1, 2007" +#define COLAMD_VERSION_CODE(main, sub) ((main) * 1000 + (sub)) +#define COLAMD_MAIN_VERSION 2 +#define COLAMD_SUB_VERSION 7 +#define COLAMD_SUBSUB_VERSION 1 +#define COLAMD_VERSION \ + COLAMD_VERSION_CODE(COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION) + +#define COLAMD_KNOBS 20 +#define COLAMD_STATS 20 +#define COLAMD_DENSE_ROW 0 +#define COLAMD_DENSE_COL 1 +#define COLAMD_AGGRESSIVE 2 +#define COLAMD_DEFRAG_COUNT 2 +#define COLAMD_STATUS 3 +#define COLAMD_INFO1 4 +#define COLAMD_INFO2 5 +#define COLAMD_INFO3 6 + +#define COLAMD_OK (0) +#define COLAMD_OK_BUT_JUMBLED (1) +#define COLAMD_ERROR_A_not_present (-1) +#define COLAMD_ERROR_p_not_present (-2) +#define COLAMD_ERROR_nrow_negative (-3) +#define COLAMD_ERROR_ncol_negative (-4) +#define COLAMD_ERROR_nnz_negative (-5) +#define COLAMD_ERROR_p0_nonzero (-6) +#define COLAMD_ERROR_A_too_small (-7) +#define COLAMD_ERROR_col_length_negative (-8) +#define COLAMD_ERROR_row_index_out_of_bounds (-9) +#define COLAMD_ERROR_out_of_memory (-10) +#define COLAMD_ERROR_internal_error (-999) + +#define colamd_recommended _glp_colamd_recommended +size_t colamd_recommended(int nnz, int n_row, int n_col); + +#define colamd_set_defaults _glp_colamd_set_defaults +void colamd_set_defaults(double knobs [COLAMD_KNOBS]); + +#define colamd _glp_colamd +int colamd(int n_row, int n_col, int Alen, int A[], int p[], + double knobs[COLAMD_KNOBS], int stats[COLAMD_STATS]); + +#define symamd _glp_symamd +int symamd(int n, int A[], int p[], int perm[], + double knobs[COLAMD_KNOBS], int stats[COLAMD_STATS], + void *(*allocate)(size_t, size_t), void(*release)(void *)); + +#define colamd_report _glp_colamd_report +void colamd_report(int stats[COLAMD_STATS]); + +#define symamd_report _glp_symamd_report +void symamd_report(int stats[COLAMD_STATS]); + +#define colamd_printf xprintf + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,314 @@ +/* glpapi.h (application program interface) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPAPI_H +#define GLPAPI_H + +#define GLP_PROB_DEFINED +typedef struct glp_prob glp_prob; + +#include "glpk.h" +#include "glpavl.h" +#include "glpbfd.h" + +typedef struct GLPROW GLPROW; +typedef struct GLPCOL GLPCOL; +typedef struct GLPAIJ GLPAIJ; + +#define GLP_PROB_MAGIC 0xD7D9D6C2 + +struct glp_prob +{ /* LP/MIP problem object */ + int magic; + /* magic value used for debugging */ + DMP *pool; + /* memory pool to store problem object components */ + glp_tree *tree; + /* pointer to the search tree; set by the MIP solver when this + object is used in the tree as a core MIP object */ + void *parms; + /* reserved for backward compatibility */ + /*--------------------------------------------------------------*/ + /* LP/MIP data */ + char *name; + /* problem name (1 to 255 chars); NULL means no name is assigned + to the problem */ + char *obj; + /* objective function name (1 to 255 chars); NULL means no name + is assigned to the objective function */ + int dir; + /* optimization direction flag (objective "sense"): + GLP_MIN - minimization + GLP_MAX - maximization */ + double c0; + /* constant term of the objective function ("shift") */ + int m_max; + /* length of the array of rows (enlarged automatically) */ + int n_max; + /* length of the array of columns (enlarged automatically) */ + int m; + /* number of rows, 0 <= m <= m_max */ + int n; + /* number of columns, 0 <= n <= n_max */ + int nnz; + /* number of non-zero constraint coefficients, nnz >= 0 */ + GLPROW **row; /* GLPROW *row[1+m_max]; */ + /* row[i], 1 <= i <= m, is a pointer to i-th row */ + GLPCOL **col; /* GLPCOL *col[1+n_max]; */ + /* col[j], 1 <= j <= n, is a pointer to j-th column */ + AVL *r_tree; + /* row index to find rows by their names; NULL means this index + does not exist */ + AVL *c_tree; + /* column index to find columns by their names; NULL means this + index does not exist */ + /*--------------------------------------------------------------*/ + /* basis factorization (LP) */ + int valid; + /* the factorization is valid only if this flag is set */ + int *head; /* int head[1+m_max]; */ + /* basis header (valid only if the factorization is valid); + head[i] = k is the ordinal number of auxiliary (1 <= k <= m) + or structural (m+1 <= k <= m+n) variable which corresponds to + i-th basic variable xB[i], 1 <= i <= m */ + glp_bfcp *bfcp; + /* basis factorization control parameters; may be NULL */ + BFD *bfd; /* BFD bfd[1:m,1:m]; */ + /* basis factorization driver; may be NULL */ + /*--------------------------------------------------------------*/ + /* basic solution (LP) */ + int pbs_stat; + /* primal basic solution status: + GLP_UNDEF - primal solution is undefined + GLP_FEAS - primal solution is feasible + GLP_INFEAS - primal solution is infeasible + GLP_NOFEAS - no primal feasible solution exists */ + int dbs_stat; + /* dual basic solution status: + GLP_UNDEF - dual solution is undefined + GLP_FEAS - dual solution is feasible + GLP_INFEAS - dual solution is infeasible + GLP_NOFEAS - no dual feasible solution exists */ + double obj_val; + /* objective function value */ + int it_cnt; + /* simplex method iteration count; increased by one on performing + one simplex iteration */ + int some; + /* ordinal number of some auxiliary or structural variable having + certain property, 0 <= some <= m+n */ + /*--------------------------------------------------------------*/ + /* interior-point solution (LP) */ + int ipt_stat; + /* interior-point solution status: + GLP_UNDEF - interior solution is undefined + GLP_OPT - interior solution is optimal + GLP_INFEAS - interior solution is infeasible + GLP_NOFEAS - no feasible solution exists */ + double ipt_obj; + /* objective function value */ + /*--------------------------------------------------------------*/ + /* integer solution (MIP) */ + int mip_stat; + /* integer solution status: + GLP_UNDEF - integer solution is undefined + GLP_OPT - integer solution is optimal + GLP_FEAS - integer solution is feasible + GLP_NOFEAS - no integer solution exists */ + double mip_obj; + /* objective function value */ +}; + +struct GLPROW +{ /* LP/MIP row (auxiliary variable) */ + int i; + /* ordinal number (1 to m) assigned to this row */ + char *name; + /* row name (1 to 255 chars); NULL means no name is assigned to + this row */ + AVLNODE *node; + /* pointer to corresponding node in the row index; NULL means + that either the row index does not exist or this row has no + name assigned */ +#if 1 /* 20/IX-2008 */ + int level; + unsigned char origin; + unsigned char klass; +#endif + int type; + /* type of the auxiliary variable: + GLP_FR - free variable + GLP_LO - variable with lower bound + GLP_UP - variable with upper bound + GLP_DB - double-bounded variable + GLP_FX - fixed variable */ + double lb; /* non-scaled */ + /* lower bound; if the row has no lower bound, lb is zero */ + double ub; /* non-scaled */ + /* upper bound; if the row has no upper bound, ub is zero */ + /* if the row type is GLP_FX, ub is equal to lb */ + GLPAIJ *ptr; /* non-scaled */ + /* pointer to doubly linked list of constraint coefficients which + are placed in this row */ + double rii; + /* diagonal element r[i,i] of scaling matrix R for this row; + if the scaling is not used, r[i,i] is 1 */ + int stat; + /* status of the auxiliary variable: + GLP_BS - basic variable + GLP_NL - non-basic variable on lower bound + GLP_NU - non-basic variable on upper bound + GLP_NF - non-basic free variable + GLP_NS - non-basic fixed variable */ + int bind; + /* if the auxiliary variable is basic, head[bind] refers to this + row, otherwise, bind is 0; this attribute is valid only if the + basis factorization is valid */ + double prim; /* non-scaled */ + /* primal value of the auxiliary variable in basic solution */ + double dual; /* non-scaled */ + /* dual value of the auxiliary variable in basic solution */ + double pval; /* non-scaled */ + /* primal value of the auxiliary variable in interior solution */ + double dval; /* non-scaled */ + /* dual value of the auxiliary variable in interior solution */ + double mipx; /* non-scaled */ + /* primal value of the auxiliary variable in integer solution */ +}; + +struct GLPCOL +{ /* LP/MIP column (structural variable) */ + int j; + /* ordinal number (1 to n) assigned to this column */ + char *name; + /* column name (1 to 255 chars); NULL means no name is assigned + to this column */ + AVLNODE *node; + /* pointer to corresponding node in the column index; NULL means + that either the column index does not exist or the column has + no name assigned */ + int kind; + /* kind of the structural variable: + GLP_CV - continuous variable + GLP_IV - integer or binary variable */ + int type; + /* type of the structural variable: + GLP_FR - free variable + GLP_LO - variable with lower bound + GLP_UP - variable with upper bound + GLP_DB - double-bounded variable + GLP_FX - fixed variable */ + double lb; /* non-scaled */ + /* lower bound; if the column has no lower bound, lb is zero */ + double ub; /* non-scaled */ + /* upper bound; if the column has no upper bound, ub is zero */ + /* if the column type is GLP_FX, ub is equal to lb */ + double coef; /* non-scaled */ + /* objective coefficient at the structural variable */ + GLPAIJ *ptr; /* non-scaled */ + /* pointer to doubly linked list of constraint coefficients which + are placed in this column */ + double sjj; + /* diagonal element s[j,j] of scaling matrix S for this column; + if the scaling is not used, s[j,j] is 1 */ + int stat; + /* status of the structural variable: + GLP_BS - basic variable + GLP_NL - non-basic variable on lower bound + GLP_NU - non-basic variable on upper bound + GLP_NF - non-basic free variable + GLP_NS - non-basic fixed variable */ + int bind; + /* if the structural variable is basic, head[bind] refers to + this column; otherwise, bind is 0; this attribute is valid only + if the basis factorization is valid */ + double prim; /* non-scaled */ + /* primal value of the structural variable in basic solution */ + double dual; /* non-scaled */ + /* dual value of the structural variable in basic solution */ + double pval; /* non-scaled */ + /* primal value of the structural variable in interior solution */ + double dval; /* non-scaled */ + /* dual value of the structural variable in interior solution */ + double mipx; /* non-scaled */ + /* primal value of the structural variable in integer solution */ +}; + +struct GLPAIJ +{ /* constraint coefficient a[i,j] */ + GLPROW *row; + /* pointer to row, where this coefficient is placed */ + GLPCOL *col; + /* pointer to column, where this coefficient is placed */ + double val; + /* numeric (non-zero) value of this coefficient */ + GLPAIJ *r_prev; + /* pointer to previous coefficient in the same row */ + GLPAIJ *r_next; + /* pointer to next coefficient in the same row */ + GLPAIJ *c_prev; + /* pointer to previous coefficient in the same column */ + GLPAIJ *c_next; + /* pointer to next coefficient in the same column */ +}; + +void _glp_check_kkt(glp_prob *P, int sol, int cond, double *ae_max, + int *ae_ind, double *re_max, int *re_ind); +/* check feasibility and optimality conditions */ + +#define lpx_put_solution _glp_put_solution +void lpx_put_solution(glp_prob *lp, int inval, const int *p_stat, + const int *d_stat, const double *obj_val, const int r_stat[], + const double r_prim[], const double r_dual[], const int c_stat[], + const double c_prim[], const double c_dual[]); +/* store basic solution components */ + +#define lpx_put_mip_soln _glp_put_mip_soln +void lpx_put_mip_soln(LPX *lp, int i_stat, double row_mipx[], + double col_mipx[]); +/* store mixed integer solution components */ + +#if 1 /* 28/XI-2009 */ +int _glp_analyze_row(glp_prob *P, int len, const int ind[], + const double val[], int type, double rhs, double eps, int *_piv, + double *_x, double *_dx, double *_y, double *_dy, double *_dz); +/* simulate one iteration of dual simplex method */ +#endif + +#if 1 /* 08/XII-2009 */ +void _glp_mpl_init_rand(glp_tran *tran, int seed); +#endif + +#define glp_skpgen _glp_skpgen +void glp_skpgen(int n, int r, int type, int v, int s, int a[], + int *b, int c[]); +/* Pisinger's 0-1 single knapsack problem generator */ + +#if 1 /* 28/V-2010 */ +int _glp_intopt1(glp_prob *P, const glp_iocp *parm); +#endif + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1570 @@ +/* glpapi01.c (problem creating and modifying routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/* CAUTION: DO NOT CHANGE THE LIMITS BELOW */ + +#define M_MAX 100000000 /* = 100*10^6 */ +/* maximal number of rows in the problem object */ + +#define N_MAX 100000000 /* = 100*10^6 */ +/* maximal number of columns in the problem object */ + +#define NNZ_MAX 500000000 /* = 500*10^6 */ +/* maximal number of constraint coefficients in the problem object */ + +/*********************************************************************** +* NAME +* +* glp_create_prob - create problem object +* +* SYNOPSIS +* +* glp_prob *glp_create_prob(void); +* +* DESCRIPTION +* +* The routine glp_create_prob creates a new problem object, which is +* initially "empty", i.e. has no rows and columns. +* +* RETURNS +* +* The routine returns a pointer to the object created, which should be +* used in any subsequent operations on this object. */ + +static void create_prob(glp_prob *lp) +{ lp->magic = GLP_PROB_MAGIC; + lp->pool = dmp_create_pool(); +#if 0 /* 17/XI-2009 */ + lp->cps = xmalloc(sizeof(struct LPXCPS)); + lpx_reset_parms(lp); +#else + lp->parms = NULL; +#endif + lp->tree = NULL; +#if 0 + lp->lwa = 0; + lp->cwa = NULL; +#endif + /* LP/MIP data */ + lp->name = NULL; + lp->obj = NULL; + lp->dir = GLP_MIN; + lp->c0 = 0.0; + lp->m_max = 100; + lp->n_max = 200; + lp->m = lp->n = 0; + lp->nnz = 0; + lp->row = xcalloc(1+lp->m_max, sizeof(GLPROW *)); + lp->col = xcalloc(1+lp->n_max, sizeof(GLPCOL *)); + lp->r_tree = lp->c_tree = NULL; + /* basis factorization */ + lp->valid = 0; + lp->head = xcalloc(1+lp->m_max, sizeof(int)); + lp->bfcp = NULL; + lp->bfd = NULL; + /* basic solution (LP) */ + lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; + lp->obj_val = 0.0; + lp->it_cnt = 0; + lp->some = 0; + /* interior-point solution (LP) */ + lp->ipt_stat = GLP_UNDEF; + lp->ipt_obj = 0.0; + /* integer solution (MIP) */ + lp->mip_stat = GLP_UNDEF; + lp->mip_obj = 0.0; + return; +} + +glp_prob *glp_create_prob(void) +{ glp_prob *lp; + lp = xmalloc(sizeof(glp_prob)); + create_prob(lp); + return lp; +} + +/*********************************************************************** +* NAME +* +* glp_set_prob_name - assign (change) problem name +* +* SYNOPSIS +* +* void glp_set_prob_name(glp_prob *lp, const char *name); +* +* DESCRIPTION +* +* The routine glp_set_prob_name assigns a given symbolic name (1 up to +* 255 characters) to the specified problem object. +* +* If the parameter name is NULL or empty string, the routine erases an +* existing symbolic name of the problem object. */ + +void glp_set_prob_name(glp_prob *lp, const char *name) +{ glp_tree *tree = lp->tree; + if (tree != NULL && tree->reason != 0) + xerror("glp_set_prob_name: operation not allowed\n"); + if (lp->name != NULL) + { dmp_free_atom(lp->pool, lp->name, strlen(lp->name)+1); + lp->name = NULL; + } + if (!(name == NULL || name[0] == '\0')) + { int k; + for (k = 0; name[k] != '\0'; k++) + { if (k == 256) + xerror("glp_set_prob_name: problem name too long\n"); + if (iscntrl((unsigned char)name[k])) + xerror("glp_set_prob_name: problem name contains invalid" + " character(s)\n"); + } + lp->name = dmp_get_atom(lp->pool, strlen(name)+1); + strcpy(lp->name, name); + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_obj_name - assign (change) objective function name +* +* SYNOPSIS +* +* void glp_set_obj_name(glp_prob *lp, const char *name); +* +* DESCRIPTION +* +* The routine glp_set_obj_name assigns a given symbolic name (1 up to +* 255 characters) to the objective function of the specified problem +* object. +* +* If the parameter name is NULL or empty string, the routine erases an +* existing name of the objective function. */ + +void glp_set_obj_name(glp_prob *lp, const char *name) +{ glp_tree *tree = lp->tree; + if (tree != NULL && tree->reason != 0) + xerror("glp_set_obj_name: operation not allowed\n"); + if (lp->obj != NULL) + { dmp_free_atom(lp->pool, lp->obj, strlen(lp->obj)+1); + lp->obj = NULL; + } + if (!(name == NULL || name[0] == '\0')) + { int k; + for (k = 0; name[k] != '\0'; k++) + { if (k == 256) + xerror("glp_set_obj_name: objective name too long\n"); + if (iscntrl((unsigned char)name[k])) + xerror("glp_set_obj_name: objective name contains invali" + "d character(s)\n"); + } + lp->obj = dmp_get_atom(lp->pool, strlen(name)+1); + strcpy(lp->obj, name); + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_obj_dir - set (change) optimization direction flag +* +* SYNOPSIS +* +* void glp_set_obj_dir(glp_prob *lp, int dir); +* +* DESCRIPTION +* +* The routine glp_set_obj_dir sets (changes) optimization direction +* flag (i.e. "sense" of the objective function) as specified by the +* parameter dir: +* +* GLP_MIN - minimization; +* GLP_MAX - maximization. */ + +void glp_set_obj_dir(glp_prob *lp, int dir) +{ glp_tree *tree = lp->tree; + if (tree != NULL && tree->reason != 0) + xerror("glp_set_obj_dir: operation not allowed\n"); + if (!(dir == GLP_MIN || dir == GLP_MAX)) + xerror("glp_set_obj_dir: dir = %d; invalid direction flag\n", + dir); + lp->dir = dir; + return; +} + +/*********************************************************************** +* NAME +* +* glp_add_rows - add new rows to problem object +* +* SYNOPSIS +* +* int glp_add_rows(glp_prob *lp, int nrs); +* +* DESCRIPTION +* +* The routine glp_add_rows adds nrs rows (constraints) to the specified +* problem object. New rows are always added to the end of the row list, +* so the ordinal numbers of existing rows remain unchanged. +* +* Being added each new row is initially free (unbounded) and has empty +* list of the constraint coefficients. +* +* RETURNS +* +* The routine glp_add_rows returns the ordinal number of the first new +* row added to the problem object. */ + +int glp_add_rows(glp_prob *lp, int nrs) +{ glp_tree *tree = lp->tree; + GLPROW *row; + int m_new, i; + /* determine new number of rows */ + if (nrs < 1) + xerror("glp_add_rows: nrs = %d; invalid number of rows\n", + nrs); + if (nrs > M_MAX - lp->m) + xerror("glp_add_rows: nrs = %d; too many rows\n", nrs); + m_new = lp->m + nrs; + /* increase the room, if necessary */ + if (lp->m_max < m_new) + { GLPROW **save = lp->row; + while (lp->m_max < m_new) + { lp->m_max += lp->m_max; + xassert(lp->m_max > 0); + } + lp->row = xcalloc(1+lp->m_max, sizeof(GLPROW *)); + memcpy(&lp->row[1], &save[1], lp->m * sizeof(GLPROW *)); + xfree(save); + /* do not forget about the basis header */ + xfree(lp->head); + lp->head = xcalloc(1+lp->m_max, sizeof(int)); + } + /* add new rows to the end of the row list */ + for (i = lp->m+1; i <= m_new; i++) + { /* create row descriptor */ + lp->row[i] = row = dmp_get_atom(lp->pool, sizeof(GLPROW)); + row->i = i; + row->name = NULL; + row->node = NULL; +#if 1 /* 20/IX-2008 */ + row->level = 0; + row->origin = 0; + row->klass = 0; + if (tree != NULL) + { switch (tree->reason) + { case 0: + break; + case GLP_IROWGEN: + xassert(tree->curr != NULL); + row->level = tree->curr->level; + row->origin = GLP_RF_LAZY; + break; + case GLP_ICUTGEN: + xassert(tree->curr != NULL); + row->level = tree->curr->level; + row->origin = GLP_RF_CUT; + break; + default: + xassert(tree != tree); + } + } +#endif + row->type = GLP_FR; + row->lb = row->ub = 0.0; + row->ptr = NULL; + row->rii = 1.0; + row->stat = GLP_BS; +#if 0 + row->bind = -1; +#else + row->bind = 0; +#endif + row->prim = row->dual = 0.0; + row->pval = row->dval = 0.0; + row->mipx = 0.0; + } + /* set new number of rows */ + lp->m = m_new; + /* invalidate the basis factorization */ + lp->valid = 0; +#if 1 + if (tree != NULL && tree->reason != 0) tree->reopt = 1; +#endif + /* return the ordinal number of the first row added */ + return m_new - nrs + 1; +} + +/*********************************************************************** +* NAME +* +* glp_add_cols - add new columns to problem object +* +* SYNOPSIS +* +* int glp_add_cols(glp_prob *lp, int ncs); +* +* DESCRIPTION +* +* The routine glp_add_cols adds ncs columns (structural variables) to +* the specified problem object. New columns are always added to the end +* of the column list, so the ordinal numbers of existing columns remain +* unchanged. +* +* Being added each new column is initially fixed at zero and has empty +* list of the constraint coefficients. +* +* RETURNS +* +* The routine glp_add_cols returns the ordinal number of the first new +* column added to the problem object. */ + +int glp_add_cols(glp_prob *lp, int ncs) +{ glp_tree *tree = lp->tree; + GLPCOL *col; + int n_new, j; + if (tree != NULL && tree->reason != 0) + xerror("glp_add_cols: operation not allowed\n"); + /* determine new number of columns */ + if (ncs < 1) + xerror("glp_add_cols: ncs = %d; invalid number of columns\n", + ncs); + if (ncs > N_MAX - lp->n) + xerror("glp_add_cols: ncs = %d; too many columns\n", ncs); + n_new = lp->n + ncs; + /* increase the room, if necessary */ + if (lp->n_max < n_new) + { GLPCOL **save = lp->col; + while (lp->n_max < n_new) + { lp->n_max += lp->n_max; + xassert(lp->n_max > 0); + } + lp->col = xcalloc(1+lp->n_max, sizeof(GLPCOL *)); + memcpy(&lp->col[1], &save[1], lp->n * sizeof(GLPCOL *)); + xfree(save); + } + /* add new columns to the end of the column list */ + for (j = lp->n+1; j <= n_new; j++) + { /* create column descriptor */ + lp->col[j] = col = dmp_get_atom(lp->pool, sizeof(GLPCOL)); + col->j = j; + col->name = NULL; + col->node = NULL; + col->kind = GLP_CV; + col->type = GLP_FX; + col->lb = col->ub = 0.0; + col->coef = 0.0; + col->ptr = NULL; + col->sjj = 1.0; + col->stat = GLP_NS; +#if 0 + col->bind = -1; +#else + col->bind = 0; /* the basis may remain valid */ +#endif + col->prim = col->dual = 0.0; + col->pval = col->dval = 0.0; + col->mipx = 0.0; + } + /* set new number of columns */ + lp->n = n_new; + /* return the ordinal number of the first column added */ + return n_new - ncs + 1; +} + +/*********************************************************************** +* NAME +* +* glp_set_row_name - assign (change) row name +* +* SYNOPSIS +* +* void glp_set_row_name(glp_prob *lp, int i, const char *name); +* +* DESCRIPTION +* +* The routine glp_set_row_name assigns a given symbolic name (1 up to +* 255 characters) to i-th row (auxiliary variable) of the specified +* problem object. +* +* If the parameter name is NULL or empty string, the routine erases an +* existing name of i-th row. */ + +void glp_set_row_name(glp_prob *lp, int i, const char *name) +{ glp_tree *tree = lp->tree; + GLPROW *row; + if (!(1 <= i && i <= lp->m)) + xerror("glp_set_row_name: i = %d; row number out of range\n", + i); + row = lp->row[i]; + if (tree != NULL && tree->reason != 0) + { xassert(tree->curr != NULL); + xassert(row->level == tree->curr->level); + } + if (row->name != NULL) + { if (row->node != NULL) + { xassert(lp->r_tree != NULL); + avl_delete_node(lp->r_tree, row->node); + row->node = NULL; + } + dmp_free_atom(lp->pool, row->name, strlen(row->name)+1); + row->name = NULL; + } + if (!(name == NULL || name[0] == '\0')) + { int k; + for (k = 0; name[k] != '\0'; k++) + { if (k == 256) + xerror("glp_set_row_name: i = %d; row name too long\n", + i); + if (iscntrl((unsigned char)name[k])) + xerror("glp_set_row_name: i = %d: row name contains inva" + "lid character(s)\n", i); + } + row->name = dmp_get_atom(lp->pool, strlen(name)+1); + strcpy(row->name, name); + if (lp->r_tree != NULL) + { xassert(row->node == NULL); + row->node = avl_insert_node(lp->r_tree, row->name); + avl_set_node_link(row->node, row); + } + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_col_name - assign (change) column name +* +* SYNOPSIS +* +* void glp_set_col_name(glp_prob *lp, int j, const char *name); +* +* DESCRIPTION +* +* The routine glp_set_col_name assigns a given symbolic name (1 up to +* 255 characters) to j-th column (structural variable) of the specified +* problem object. +* +* If the parameter name is NULL or empty string, the routine erases an +* existing name of j-th column. */ + +void glp_set_col_name(glp_prob *lp, int j, const char *name) +{ glp_tree *tree = lp->tree; + GLPCOL *col; + if (tree != NULL && tree->reason != 0) + xerror("glp_set_col_name: operation not allowed\n"); + if (!(1 <= j && j <= lp->n)) + xerror("glp_set_col_name: j = %d; column number out of range\n" + , j); + col = lp->col[j]; + if (col->name != NULL) + { if (col->node != NULL) + { xassert(lp->c_tree != NULL); + avl_delete_node(lp->c_tree, col->node); + col->node = NULL; + } + dmp_free_atom(lp->pool, col->name, strlen(col->name)+1); + col->name = NULL; + } + if (!(name == NULL || name[0] == '\0')) + { int k; + for (k = 0; name[k] != '\0'; k++) + { if (k == 256) + xerror("glp_set_col_name: j = %d; column name too long\n" + , j); + if (iscntrl((unsigned char)name[k])) + xerror("glp_set_col_name: j = %d: column name contains i" + "nvalid character(s)\n", j); + } + col->name = dmp_get_atom(lp->pool, strlen(name)+1); + strcpy(col->name, name); + if (lp->c_tree != NULL && col->name != NULL) + { xassert(col->node == NULL); + col->node = avl_insert_node(lp->c_tree, col->name); + avl_set_node_link(col->node, col); + } + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_row_bnds - set (change) row bounds +* +* SYNOPSIS +* +* void glp_set_row_bnds(glp_prob *lp, int i, int type, double lb, +* double ub); +* +* DESCRIPTION +* +* The routine glp_set_row_bnds sets (changes) the type and bounds of +* i-th row (auxiliary variable) of the specified problem object. +* +* Parameters type, lb, and ub specify the type, lower bound, and upper +* bound, respectively, as follows: +* +* Type Bounds Comments +* ------------------------------------------------------ +* GLP_FR -inf < x < +inf Free variable +* GLP_LO lb <= x < +inf Variable with lower bound +* GLP_UP -inf < x <= ub Variable with upper bound +* GLP_DB lb <= x <= ub Double-bounded variable +* GLP_FX x = lb Fixed variable +* +* where x is the auxiliary variable associated with i-th row. +* +* If the row has no lower bound, the parameter lb is ignored. If the +* row has no upper bound, the parameter ub is ignored. If the row is +* an equality constraint (i.e. the corresponding auxiliary variable is +* of fixed type), only the parameter lb is used while the parameter ub +* is ignored. */ + +void glp_set_row_bnds(glp_prob *lp, int i, int type, double lb, + double ub) +{ GLPROW *row; + if (!(1 <= i && i <= lp->m)) + xerror("glp_set_row_bnds: i = %d; row number out of range\n", + i); + row = lp->row[i]; + row->type = type; + switch (type) + { case GLP_FR: + row->lb = row->ub = 0.0; + if (row->stat != GLP_BS) row->stat = GLP_NF; + break; + case GLP_LO: + row->lb = lb, row->ub = 0.0; + if (row->stat != GLP_BS) row->stat = GLP_NL; + break; + case GLP_UP: + row->lb = 0.0, row->ub = ub; + if (row->stat != GLP_BS) row->stat = GLP_NU; + break; + case GLP_DB: + row->lb = lb, row->ub = ub; + if (!(row->stat == GLP_BS || + row->stat == GLP_NL || row->stat == GLP_NU)) + row->stat = (fabs(lb) <= fabs(ub) ? GLP_NL : GLP_NU); + break; + case GLP_FX: + row->lb = row->ub = lb; + if (row->stat != GLP_BS) row->stat = GLP_NS; + break; + default: + xerror("glp_set_row_bnds: i = %d; type = %d; invalid row ty" + "pe\n", i, type); + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_col_bnds - set (change) column bounds +* +* SYNOPSIS +* +* void glp_set_col_bnds(glp_prob *lp, int j, int type, double lb, +* double ub); +* +* DESCRIPTION +* +* The routine glp_set_col_bnds sets (changes) the type and bounds of +* j-th column (structural variable) of the specified problem object. +* +* Parameters type, lb, and ub specify the type, lower bound, and upper +* bound, respectively, as follows: +* +* Type Bounds Comments +* ------------------------------------------------------ +* GLP_FR -inf < x < +inf Free variable +* GLP_LO lb <= x < +inf Variable with lower bound +* GLP_UP -inf < x <= ub Variable with upper bound +* GLP_DB lb <= x <= ub Double-bounded variable +* GLP_FX x = lb Fixed variable +* +* where x is the structural variable associated with j-th column. +* +* If the column has no lower bound, the parameter lb is ignored. If the +* column has no upper bound, the parameter ub is ignored. If the column +* is of fixed type, only the parameter lb is used while the parameter +* ub is ignored. */ + +void glp_set_col_bnds(glp_prob *lp, int j, int type, double lb, + double ub) +{ GLPCOL *col; + if (!(1 <= j && j <= lp->n)) + xerror("glp_set_col_bnds: j = %d; column number out of range\n" + , j); + col = lp->col[j]; + col->type = type; + switch (type) + { case GLP_FR: + col->lb = col->ub = 0.0; + if (col->stat != GLP_BS) col->stat = GLP_NF; + break; + case GLP_LO: + col->lb = lb, col->ub = 0.0; + if (col->stat != GLP_BS) col->stat = GLP_NL; + break; + case GLP_UP: + col->lb = 0.0, col->ub = ub; + if (col->stat != GLP_BS) col->stat = GLP_NU; + break; + case GLP_DB: + col->lb = lb, col->ub = ub; + if (!(col->stat == GLP_BS || + col->stat == GLP_NL || col->stat == GLP_NU)) + col->stat = (fabs(lb) <= fabs(ub) ? GLP_NL : GLP_NU); + break; + case GLP_FX: + col->lb = col->ub = lb; + if (col->stat != GLP_BS) col->stat = GLP_NS; + break; + default: + xerror("glp_set_col_bnds: j = %d; type = %d; invalid column" + " type\n", j, type); + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_obj_coef - set (change) obj. coefficient or constant term +* +* SYNOPSIS +* +* void glp_set_obj_coef(glp_prob *lp, int j, double coef); +* +* DESCRIPTION +* +* The routine glp_set_obj_coef sets (changes) objective coefficient at +* j-th column (structural variable) of the specified problem object. +* +* If the parameter j is 0, the routine sets (changes) the constant term +* ("shift") of the objective function. */ + +void glp_set_obj_coef(glp_prob *lp, int j, double coef) +{ glp_tree *tree = lp->tree; + if (tree != NULL && tree->reason != 0) + xerror("glp_set_obj_coef: operation not allowed\n"); + if (!(0 <= j && j <= lp->n)) + xerror("glp_set_obj_coef: j = %d; column number out of range\n" + , j); + if (j == 0) + lp->c0 = coef; + else + lp->col[j]->coef = coef; + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_mat_row - set (replace) row of the constraint matrix +* +* SYNOPSIS +* +* void glp_set_mat_row(glp_prob *lp, int i, int len, const int ind[], +* const double val[]); +* +* DESCRIPTION +* +* The routine glp_set_mat_row stores (replaces) the contents of i-th +* row of the constraint matrix of the specified problem object. +* +* Column indices and numeric values of new row elements must be placed +* in locations ind[1], ..., ind[len] and val[1], ..., val[len], where +* 0 <= len <= n is the new length of i-th row, n is the current number +* of columns in the problem object. Elements with identical column +* indices are not allowed. Zero elements are allowed, but they are not +* stored in the constraint matrix. +* +* If the parameter len is zero, the parameters ind and/or val can be +* specified as NULL. */ + +void glp_set_mat_row(glp_prob *lp, int i, int len, const int ind[], + const double val[]) +{ glp_tree *tree = lp->tree; + GLPROW *row; + GLPCOL *col; + GLPAIJ *aij, *next; + int j, k; + /* obtain pointer to i-th row */ + if (!(1 <= i && i <= lp->m)) + xerror("glp_set_mat_row: i = %d; row number out of range\n", + i); + row = lp->row[i]; + if (tree != NULL && tree->reason != 0) + { xassert(tree->curr != NULL); + xassert(row->level == tree->curr->level); + } + /* remove all existing elements from i-th row */ + while (row->ptr != NULL) + { /* take next element in the row */ + aij = row->ptr; + /* remove the element from the row list */ + row->ptr = aij->r_next; + /* obtain pointer to corresponding column */ + col = aij->col; + /* remove the element from the column list */ + if (aij->c_prev == NULL) + col->ptr = aij->c_next; + else + aij->c_prev->c_next = aij->c_next; + if (aij->c_next == NULL) + ; + else + aij->c_next->c_prev = aij->c_prev; + /* return the element to the memory pool */ + dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; + /* if the corresponding column is basic, invalidate the basis + factorization */ + if (col->stat == GLP_BS) lp->valid = 0; + } + /* store new contents of i-th row */ + if (!(0 <= len && len <= lp->n)) + xerror("glp_set_mat_row: i = %d; len = %d; invalid row length " + "\n", i, len); + if (len > NNZ_MAX - lp->nnz) + xerror("glp_set_mat_row: i = %d; len = %d; too many constraint" + " coefficients\n", i, len); + for (k = 1; k <= len; k++) + { /* take number j of corresponding column */ + j = ind[k]; + /* obtain pointer to j-th column */ + if (!(1 <= j && j <= lp->n)) + xerror("glp_set_mat_row: i = %d; ind[%d] = %d; column index" + " out of range\n", i, k, j); + col = lp->col[j]; + /* if there is element with the same column index, it can only + be found in the beginning of j-th column list */ + if (col->ptr != NULL && col->ptr->row->i == i) + xerror("glp_set_mat_row: i = %d; ind[%d] = %d; duplicate co" + "lumn indices not allowed\n", i, k, j); + /* create new element */ + aij = dmp_get_atom(lp->pool, sizeof(GLPAIJ)), lp->nnz++; + aij->row = row; + aij->col = col; + aij->val = val[k]; + /* add the new element to the beginning of i-th row and j-th + column lists */ + aij->r_prev = NULL; + aij->r_next = row->ptr; + aij->c_prev = NULL; + aij->c_next = col->ptr; + if (aij->r_next != NULL) aij->r_next->r_prev = aij; + if (aij->c_next != NULL) aij->c_next->c_prev = aij; + row->ptr = col->ptr = aij; + /* if the corresponding column is basic, invalidate the basis + factorization */ + if (col->stat == GLP_BS && aij->val != 0.0) lp->valid = 0; + } + /* remove zero elements from i-th row */ + for (aij = row->ptr; aij != NULL; aij = next) + { next = aij->r_next; + if (aij->val == 0.0) + { /* remove the element from the row list */ + if (aij->r_prev == NULL) + row->ptr = next; + else + aij->r_prev->r_next = next; + if (next == NULL) + ; + else + next->r_prev = aij->r_prev; + /* remove the element from the column list */ + xassert(aij->c_prev == NULL); + aij->col->ptr = aij->c_next; + if (aij->c_next != NULL) aij->c_next->c_prev = NULL; + /* return the element to the memory pool */ + dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; + } + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_mat_col - set (replace) column of the constraint matrix +* +* SYNOPSIS +* +* void glp_set_mat_col(glp_prob *lp, int j, int len, const int ind[], +* const double val[]); +* +* DESCRIPTION +* +* The routine glp_set_mat_col stores (replaces) the contents of j-th +* column of the constraint matrix of the specified problem object. +* +* Row indices and numeric values of new column elements must be placed +* in locations ind[1], ..., ind[len] and val[1], ..., val[len], where +* 0 <= len <= m is the new length of j-th column, m is the current +* number of rows in the problem object. Elements with identical column +* indices are not allowed. Zero elements are allowed, but they are not +* stored in the constraint matrix. +* +* If the parameter len is zero, the parameters ind and/or val can be +* specified as NULL. */ + +void glp_set_mat_col(glp_prob *lp, int j, int len, const int ind[], + const double val[]) +{ glp_tree *tree = lp->tree; + GLPROW *row; + GLPCOL *col; + GLPAIJ *aij, *next; + int i, k; + if (tree != NULL && tree->reason != 0) + xerror("glp_set_mat_col: operation not allowed\n"); + /* obtain pointer to j-th column */ + if (!(1 <= j && j <= lp->n)) + xerror("glp_set_mat_col: j = %d; column number out of range\n", + j); + col = lp->col[j]; + /* remove all existing elements from j-th column */ + while (col->ptr != NULL) + { /* take next element in the column */ + aij = col->ptr; + /* remove the element from the column list */ + col->ptr = aij->c_next; + /* obtain pointer to corresponding row */ + row = aij->row; + /* remove the element from the row list */ + if (aij->r_prev == NULL) + row->ptr = aij->r_next; + else + aij->r_prev->r_next = aij->r_next; + if (aij->r_next == NULL) + ; + else + aij->r_next->r_prev = aij->r_prev; + /* return the element to the memory pool */ + dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; + } + /* store new contents of j-th column */ + if (!(0 <= len && len <= lp->m)) + xerror("glp_set_mat_col: j = %d; len = %d; invalid column leng" + "th\n", j, len); + if (len > NNZ_MAX - lp->nnz) + xerror("glp_set_mat_col: j = %d; len = %d; too many constraint" + " coefficients\n", j, len); + for (k = 1; k <= len; k++) + { /* take number i of corresponding row */ + i = ind[k]; + /* obtain pointer to i-th row */ + if (!(1 <= i && i <= lp->m)) + xerror("glp_set_mat_col: j = %d; ind[%d] = %d; row index ou" + "t of range\n", j, k, i); + row = lp->row[i]; + /* if there is element with the same row index, it can only be + found in the beginning of i-th row list */ + if (row->ptr != NULL && row->ptr->col->j == j) + xerror("glp_set_mat_col: j = %d; ind[%d] = %d; duplicate ro" + "w indices not allowed\n", j, k, i); + /* create new element */ + aij = dmp_get_atom(lp->pool, sizeof(GLPAIJ)), lp->nnz++; + aij->row = row; + aij->col = col; + aij->val = val[k]; + /* add the new element to the beginning of i-th row and j-th + column lists */ + aij->r_prev = NULL; + aij->r_next = row->ptr; + aij->c_prev = NULL; + aij->c_next = col->ptr; + if (aij->r_next != NULL) aij->r_next->r_prev = aij; + if (aij->c_next != NULL) aij->c_next->c_prev = aij; + row->ptr = col->ptr = aij; + } + /* remove zero elements from j-th column */ + for (aij = col->ptr; aij != NULL; aij = next) + { next = aij->c_next; + if (aij->val == 0.0) + { /* remove the element from the row list */ + xassert(aij->r_prev == NULL); + aij->row->ptr = aij->r_next; + if (aij->r_next != NULL) aij->r_next->r_prev = NULL; + /* remove the element from the column list */ + if (aij->c_prev == NULL) + col->ptr = next; + else + aij->c_prev->c_next = next; + if (next == NULL) + ; + else + next->c_prev = aij->c_prev; + /* return the element to the memory pool */ + dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; + } + } + /* if j-th column is basic, invalidate the basis factorization */ + if (col->stat == GLP_BS) lp->valid = 0; + return; +} + +/*********************************************************************** +* NAME +* +* glp_load_matrix - load (replace) the whole constraint matrix +* +* SYNOPSIS +* +* void glp_load_matrix(glp_prob *lp, int ne, const int ia[], +* const int ja[], const double ar[]); +* +* DESCRIPTION +* +* The routine glp_load_matrix loads the constraint matrix passed in +* the arrays ia, ja, and ar into the specified problem object. Before +* loading the current contents of the constraint matrix is destroyed. +* +* Constraint coefficients (elements of the constraint matrix) must be +* specified as triplets (ia[k], ja[k], ar[k]) for k = 1, ..., ne, +* where ia[k] is the row index, ja[k] is the column index, ar[k] is a +* numeric value of corresponding constraint coefficient. The parameter +* ne specifies the total number of (non-zero) elements in the matrix +* to be loaded. Coefficients with identical indices are not allowed. +* Zero coefficients are allowed, however, they are not stored in the +* constraint matrix. +* +* If the parameter ne is zero, the parameters ia, ja, and ar can be +* specified as NULL. */ + +void glp_load_matrix(glp_prob *lp, int ne, const int ia[], + const int ja[], const double ar[]) +{ glp_tree *tree = lp->tree; + GLPROW *row; + GLPCOL *col; + GLPAIJ *aij, *next; + int i, j, k; + if (tree != NULL && tree->reason != 0) + xerror("glp_load_matrix: operation not allowed\n"); + /* clear the constraint matrix */ + for (i = 1; i <= lp->m; i++) + { row = lp->row[i]; + while (row->ptr != NULL) + { aij = row->ptr; + row->ptr = aij->r_next; + dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; + } + } + xassert(lp->nnz == 0); + for (j = 1; j <= lp->n; j++) lp->col[j]->ptr = NULL; + /* load the new contents of the constraint matrix and build its + row lists */ + if (ne < 0) + xerror("glp_load_matrix: ne = %d; invalid number of constraint" + " coefficients\n", ne); + if (ne > NNZ_MAX) + xerror("glp_load_matrix: ne = %d; too many constraint coeffici" + "ents\n", ne); + for (k = 1; k <= ne; k++) + { /* take indices of new element */ + i = ia[k], j = ja[k]; + /* obtain pointer to i-th row */ + if (!(1 <= i && i <= lp->m)) + xerror("glp_load_matrix: ia[%d] = %d; row index out of rang" + "e\n", k, i); + row = lp->row[i]; + /* obtain pointer to j-th column */ + if (!(1 <= j && j <= lp->n)) + xerror("glp_load_matrix: ja[%d] = %d; column index out of r" + "ange\n", k, j); + col = lp->col[j]; + /* create new element */ + aij = dmp_get_atom(lp->pool, sizeof(GLPAIJ)), lp->nnz++; + aij->row = row; + aij->col = col; + aij->val = ar[k]; + /* add the new element to the beginning of i-th row list */ + aij->r_prev = NULL; + aij->r_next = row->ptr; + if (aij->r_next != NULL) aij->r_next->r_prev = aij; + row->ptr = aij; + } + xassert(lp->nnz == ne); + /* build column lists of the constraint matrix and check elements + with identical indices */ + for (i = 1; i <= lp->m; i++) + { for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) + { /* obtain pointer to corresponding column */ + col = aij->col; + /* if there is element with identical indices, it can only + be found in the beginning of j-th column list */ + if (col->ptr != NULL && col->ptr->row->i == i) + { for (k = 1; k <= ne; k++) + if (ia[k] == i && ja[k] == col->j) break; + xerror("glp_load_mat: ia[%d] = %d; ja[%d] = %d; duplicat" + "e indices not allowed\n", k, i, k, col->j); + } + /* add the element to the beginning of j-th column list */ + aij->c_prev = NULL; + aij->c_next = col->ptr; + if (aij->c_next != NULL) aij->c_next->c_prev = aij; + col->ptr = aij; + } + } + /* remove zero elements from the constraint matrix */ + for (i = 1; i <= lp->m; i++) + { row = lp->row[i]; + for (aij = row->ptr; aij != NULL; aij = next) + { next = aij->r_next; + if (aij->val == 0.0) + { /* remove the element from the row list */ + if (aij->r_prev == NULL) + row->ptr = next; + else + aij->r_prev->r_next = next; + if (next == NULL) + ; + else + next->r_prev = aij->r_prev; + /* remove the element from the column list */ + if (aij->c_prev == NULL) + aij->col->ptr = aij->c_next; + else + aij->c_prev->c_next = aij->c_next; + if (aij->c_next == NULL) + ; + else + aij->c_next->c_prev = aij->c_prev; + /* return the element to the memory pool */ + dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; + } + } + } + /* invalidate the basis factorization */ + lp->valid = 0; + return; +} + +/*********************************************************************** +* NAME +* +* glp_check_dup - check for duplicate elements in sparse matrix +* +* SYNOPSIS +* +* int glp_check_dup(int m, int n, int ne, const int ia[], +* const int ja[]); +* +* DESCRIPTION +* +* The routine glp_check_dup checks for duplicate elements (that is, +* elements with identical indices) in a sparse matrix specified in the +* coordinate format. +* +* The parameters m and n specifies, respectively, the number of rows +* and columns in the matrix, m >= 0, n >= 0. +* +* The parameter ne specifies the number of (structurally) non-zero +* elements in the matrix, ne >= 0. +* +* Elements of the matrix are specified as doublets (ia[k],ja[k]) for +* k = 1,...,ne, where ia[k] is a row index, ja[k] is a column index. +* +* The routine glp_check_dup can be used prior to a call to the routine +* glp_load_matrix to check that the constraint matrix to be loaded has +* no duplicate elements. +* +* RETURNS +* +* The routine glp_check_dup returns one of the following values: +* +* 0 - the matrix has no duplicate elements; +* +* -k - indices ia[k] or/and ja[k] are out of range; +* +* +k - element (ia[k],ja[k]) is duplicate. */ + +int glp_check_dup(int m, int n, int ne, const int ia[], const int ja[]) +{ int i, j, k, *ptr, *next, ret; + char *flag; + if (m < 0) + xerror("glp_check_dup: m = %d; invalid parameter\n"); + if (n < 0) + xerror("glp_check_dup: n = %d; invalid parameter\n"); + if (ne < 0) + xerror("glp_check_dup: ne = %d; invalid parameter\n"); + if (ne > 0 && ia == NULL) + xerror("glp_check_dup: ia = %p; invalid parameter\n", ia); + if (ne > 0 && ja == NULL) + xerror("glp_check_dup: ja = %p; invalid parameter\n", ja); + for (k = 1; k <= ne; k++) + { i = ia[k], j = ja[k]; + if (!(1 <= i && i <= m && 1 <= j && j <= n)) + { ret = -k; + goto done; + } + } + if (m == 0 || n == 0) + { ret = 0; + goto done; + } + /* allocate working arrays */ + ptr = xcalloc(1+m, sizeof(int)); + next = xcalloc(1+ne, sizeof(int)); + flag = xcalloc(1+n, sizeof(char)); + /* build row lists */ + for (i = 1; i <= m; i++) + ptr[i] = 0; + for (k = 1; k <= ne; k++) + { i = ia[k]; + next[k] = ptr[i]; + ptr[i] = k; + } + /* clear column flags */ + for (j = 1; j <= n; j++) + flag[j] = 0; + /* check for duplicate elements */ + for (i = 1; i <= m; i++) + { for (k = ptr[i]; k != 0; k = next[k]) + { j = ja[k]; + if (flag[j]) + { /* find first element (i,j) */ + for (k = 1; k <= ne; k++) + if (ia[k] == i && ja[k] == j) break; + xassert(k <= ne); + /* find next (duplicate) element (i,j) */ + for (k++; k <= ne; k++) + if (ia[k] == i && ja[k] == j) break; + xassert(k <= ne); + ret = +k; + goto skip; + } + flag[j] = 1; + } + /* clear column flags */ + for (k = ptr[i]; k != 0; k = next[k]) + flag[ja[k]] = 0; + } + /* no duplicate element found */ + ret = 0; +skip: /* free working arrays */ + xfree(ptr); + xfree(next); + xfree(flag); +done: return ret; +} + +/*********************************************************************** +* NAME +* +* glp_sort_matrix - sort elements of the constraint matrix +* +* SYNOPSIS +* +* void glp_sort_matrix(glp_prob *P); +* +* DESCRIPTION +* +* The routine glp_sort_matrix sorts elements of the constraint matrix +* rebuilding its row and column linked lists. On exit from the routine +* the constraint matrix is not changed, however, elements in the row +* linked lists become ordered by ascending column indices, and the +* elements in the column linked lists become ordered by ascending row +* indices. */ + +void glp_sort_matrix(glp_prob *P) +{ GLPAIJ *aij; + int i, j; + if (P == NULL || P->magic != GLP_PROB_MAGIC) + xerror("glp_sort_matrix: P = %p; invalid problem object\n", + P); + /* rebuild row linked lists */ + for (i = P->m; i >= 1; i--) + P->row[i]->ptr = NULL; + for (j = P->n; j >= 1; j--) + { for (aij = P->col[j]->ptr; aij != NULL; aij = aij->c_next) + { i = aij->row->i; + aij->r_prev = NULL; + aij->r_next = P->row[i]->ptr; + if (aij->r_next != NULL) aij->r_next->r_prev = aij; + P->row[i]->ptr = aij; + } + } + /* rebuild column linked lists */ + for (j = P->n; j >= 1; j--) + P->col[j]->ptr = NULL; + for (i = P->m; i >= 1; i--) + { for (aij = P->row[i]->ptr; aij != NULL; aij = aij->r_next) + { j = aij->col->j; + aij->c_prev = NULL; + aij->c_next = P->col[j]->ptr; + if (aij->c_next != NULL) aij->c_next->c_prev = aij; + P->col[j]->ptr = aij; + } + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_del_rows - delete rows from problem object +* +* SYNOPSIS +* +* void glp_del_rows(glp_prob *lp, int nrs, const int num[]); +* +* DESCRIPTION +* +* The routine glp_del_rows deletes rows from the specified problem +* object. Ordinal numbers of rows to be deleted should be placed in +* locations num[1], ..., num[nrs], where nrs > 0. +* +* Note that deleting rows involves changing ordinal numbers of other +* rows remaining in the problem object. New ordinal numbers of the +* remaining rows are assigned under the assumption that the original +* order of rows is not changed. */ + +void glp_del_rows(glp_prob *lp, int nrs, const int num[]) +{ glp_tree *tree = lp->tree; + GLPROW *row; + int i, k, m_new; + /* mark rows to be deleted */ + if (!(1 <= nrs && nrs <= lp->m)) + xerror("glp_del_rows: nrs = %d; invalid number of rows\n", + nrs); + for (k = 1; k <= nrs; k++) + { /* take the number of row to be deleted */ + i = num[k]; + /* obtain pointer to i-th row */ + if (!(1 <= i && i <= lp->m)) + xerror("glp_del_rows: num[%d] = %d; row number out of range" + "\n", k, i); + row = lp->row[i]; + if (tree != NULL && tree->reason != 0) + { if (!(tree->reason == GLP_IROWGEN || + tree->reason == GLP_ICUTGEN)) + xerror("glp_del_rows: operation not allowed\n"); + xassert(tree->curr != NULL); + if (row->level != tree->curr->level) + xerror("glp_del_rows: num[%d] = %d; invalid attempt to d" + "elete row created not in current subproblem\n", k,i); + if (row->stat != GLP_BS) + xerror("glp_del_rows: num[%d] = %d; invalid attempt to d" + "elete active row (constraint)\n", k, i); + tree->reinv = 1; + } + /* check that the row is not marked yet */ + if (row->i == 0) + xerror("glp_del_rows: num[%d] = %d; duplicate row numbers n" + "ot allowed\n", k, i); + /* erase symbolic name assigned to the row */ + glp_set_row_name(lp, i, NULL); + xassert(row->node == NULL); + /* erase corresponding row of the constraint matrix */ + glp_set_mat_row(lp, i, 0, NULL, NULL); + xassert(row->ptr == NULL); + /* mark the row to be deleted */ + row->i = 0; + } + /* delete all marked rows from the row list */ + m_new = 0; + for (i = 1; i <= lp->m; i++) + { /* obtain pointer to i-th row */ + row = lp->row[i]; + /* check if the row is marked */ + if (row->i == 0) + { /* it is marked, delete it */ + dmp_free_atom(lp->pool, row, sizeof(GLPROW)); + } + else + { /* it is not marked; keep it */ + row->i = ++m_new; + lp->row[row->i] = row; + } + } + /* set new number of rows */ + lp->m = m_new; + /* invalidate the basis factorization */ + lp->valid = 0; + return; +} + +/*********************************************************************** +* NAME +* +* glp_del_cols - delete columns from problem object +* +* SYNOPSIS +* +* void glp_del_cols(glp_prob *lp, int ncs, const int num[]); +* +* DESCRIPTION +* +* The routine glp_del_cols deletes columns from the specified problem +* object. Ordinal numbers of columns to be deleted should be placed in +* locations num[1], ..., num[ncs], where ncs > 0. +* +* Note that deleting columns involves changing ordinal numbers of +* other columns remaining in the problem object. New ordinal numbers +* of the remaining columns are assigned under the assumption that the +* original order of columns is not changed. */ + +void glp_del_cols(glp_prob *lp, int ncs, const int num[]) +{ glp_tree *tree = lp->tree; + GLPCOL *col; + int j, k, n_new; + if (tree != NULL && tree->reason != 0) + xerror("glp_del_cols: operation not allowed\n"); + /* mark columns to be deleted */ + if (!(1 <= ncs && ncs <= lp->n)) + xerror("glp_del_cols: ncs = %d; invalid number of columns\n", + ncs); + for (k = 1; k <= ncs; k++) + { /* take the number of column to be deleted */ + j = num[k]; + /* obtain pointer to j-th column */ + if (!(1 <= j && j <= lp->n)) + xerror("glp_del_cols: num[%d] = %d; column number out of ra" + "nge", k, j); + col = lp->col[j]; + /* check that the column is not marked yet */ + if (col->j == 0) + xerror("glp_del_cols: num[%d] = %d; duplicate column number" + "s not allowed\n", k, j); + /* erase symbolic name assigned to the column */ + glp_set_col_name(lp, j, NULL); + xassert(col->node == NULL); + /* erase corresponding column of the constraint matrix */ + glp_set_mat_col(lp, j, 0, NULL, NULL); + xassert(col->ptr == NULL); + /* mark the column to be deleted */ + col->j = 0; + /* if it is basic, invalidate the basis factorization */ + if (col->stat == GLP_BS) lp->valid = 0; + } + /* delete all marked columns from the column list */ + n_new = 0; + for (j = 1; j <= lp->n; j++) + { /* obtain pointer to j-th column */ + col = lp->col[j]; + /* check if the column is marked */ + if (col->j == 0) + { /* it is marked; delete it */ + dmp_free_atom(lp->pool, col, sizeof(GLPCOL)); + } + else + { /* it is not marked; keep it */ + col->j = ++n_new; + lp->col[col->j] = col; + } + } + /* set new number of columns */ + lp->n = n_new; + /* if the basis header is still valid, adjust it */ + if (lp->valid) + { int m = lp->m; + int *head = lp->head; + for (j = 1; j <= n_new; j++) + { k = lp->col[j]->bind; + if (k != 0) + { xassert(1 <= k && k <= m); + head[k] = m + j; + } + } + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_copy_prob - copy problem object content +* +* SYNOPSIS +* +* void glp_copy_prob(glp_prob *dest, glp_prob *prob, int names); +* +* DESCRIPTION +* +* The routine glp_copy_prob copies the content of the problem object +* prob to the problem object dest. +* +* The parameter names is a flag. If it is non-zero, the routine also +* copies all symbolic names; otherwise, if it is zero, symbolic names +* are not copied. */ + +void glp_copy_prob(glp_prob *dest, glp_prob *prob, int names) +{ glp_tree *tree = dest->tree; + glp_bfcp bfcp; + int i, j, len, *ind; + double *val; + if (tree != NULL && tree->reason != 0) + xerror("glp_copy_prob: operation not allowed\n"); + if (dest == prob) + xerror("glp_copy_prob: copying problem object to itself not al" + "lowed\n"); + if (!(names == GLP_ON || names == GLP_OFF)) + xerror("glp_copy_prob: names = %d; invalid parameter\n", + names); + glp_erase_prob(dest); + if (names && prob->name != NULL) + glp_set_prob_name(dest, prob->name); + if (names && prob->obj != NULL) + glp_set_obj_name(dest, prob->obj); + dest->dir = prob->dir; + dest->c0 = prob->c0; + if (prob->m > 0) + glp_add_rows(dest, prob->m); + if (prob->n > 0) + glp_add_cols(dest, prob->n); + glp_get_bfcp(prob, &bfcp); + glp_set_bfcp(dest, &bfcp); + dest->pbs_stat = prob->pbs_stat; + dest->dbs_stat = prob->dbs_stat; + dest->obj_val = prob->obj_val; + dest->some = prob->some; + dest->ipt_stat = prob->ipt_stat; + dest->ipt_obj = prob->ipt_obj; + dest->mip_stat = prob->mip_stat; + dest->mip_obj = prob->mip_obj; + for (i = 1; i <= prob->m; i++) + { GLPROW *to = dest->row[i]; + GLPROW *from = prob->row[i]; + if (names && from->name != NULL) + glp_set_row_name(dest, i, from->name); + to->type = from->type; + to->lb = from->lb; + to->ub = from->ub; + to->rii = from->rii; + to->stat = from->stat; + to->prim = from->prim; + to->dual = from->dual; + to->pval = from->pval; + to->dval = from->dval; + to->mipx = from->mipx; + } + ind = xcalloc(1+prob->m, sizeof(int)); + val = xcalloc(1+prob->m, sizeof(double)); + for (j = 1; j <= prob->n; j++) + { GLPCOL *to = dest->col[j]; + GLPCOL *from = prob->col[j]; + if (names && from->name != NULL) + glp_set_col_name(dest, j, from->name); + to->kind = from->kind; + to->type = from->type; + to->lb = from->lb; + to->ub = from->ub; + to->coef = from->coef; + len = glp_get_mat_col(prob, j, ind, val); + glp_set_mat_col(dest, j, len, ind, val); + to->sjj = from->sjj; + to->stat = from->stat; + to->prim = from->prim; + to->dual = from->dual; + to->pval = from->pval; + to->dval = from->dval; + to->mipx = from->mipx; + } + xfree(ind); + xfree(val); + return; +} + +/*********************************************************************** +* NAME +* +* glp_erase_prob - erase problem object content +* +* SYNOPSIS +* +* void glp_erase_prob(glp_prob *lp); +* +* DESCRIPTION +* +* The routine glp_erase_prob erases the content of the specified +* problem object. The effect of this operation is the same as if the +* problem object would be deleted with the routine glp_delete_prob and +* then created anew with the routine glp_create_prob, with exception +* that the handle (pointer) to the problem object remains valid. */ + +static void delete_prob(glp_prob *lp); + +void glp_erase_prob(glp_prob *lp) +{ glp_tree *tree = lp->tree; + if (tree != NULL && tree->reason != 0) + xerror("glp_erase_prob: operation not allowed\n"); + delete_prob(lp); + create_prob(lp); + return; +} + +/*********************************************************************** +* NAME +* +* glp_delete_prob - delete problem object +* +* SYNOPSIS +* +* void glp_delete_prob(glp_prob *lp); +* +* DESCRIPTION +* +* The routine glp_delete_prob deletes the specified problem object and +* frees all the memory allocated to it. */ + +static void delete_prob(glp_prob *lp) +{ lp->magic = 0x3F3F3F3F; + dmp_delete_pool(lp->pool); +#if 0 /* 17/XI-2009 */ + xfree(lp->cps); +#else + if (lp->parms != NULL) xfree(lp->parms); +#endif + xassert(lp->tree == NULL); +#if 0 + if (lp->cwa != NULL) xfree(lp->cwa); +#endif + xfree(lp->row); + xfree(lp->col); + if (lp->r_tree != NULL) avl_delete_tree(lp->r_tree); + if (lp->c_tree != NULL) avl_delete_tree(lp->c_tree); + xfree(lp->head); + if (lp->bfcp != NULL) xfree(lp->bfcp); + if (lp->bfd != NULL) bfd_delete_it(lp->bfd); + return; +} + +void glp_delete_prob(glp_prob *lp) +{ glp_tree *tree = lp->tree; + if (tree != NULL && tree->reason != 0) + xerror("glp_delete_prob: operation not allowed\n"); + delete_prob(lp); + xfree(lp); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,491 @@ +/* glpapi02.c (problem retrieving routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_get_prob_name - retrieve problem name +* +* SYNOPSIS +* +* const char *glp_get_prob_name(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_prob_name returns a pointer to an internal +* buffer, which contains symbolic name of the problem. However, if the +* problem has no assigned name, the routine returns NULL. */ + +const char *glp_get_prob_name(glp_prob *lp) +{ char *name; + name = lp->name; + return name; +} + +/*********************************************************************** +* NAME +* +* glp_get_obj_name - retrieve objective function name +* +* SYNOPSIS +* +* const char *glp_get_obj_name(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_obj_name returns a pointer to an internal +* buffer, which contains a symbolic name of the objective function. +* However, if the objective function has no assigned name, the routine +* returns NULL. */ + +const char *glp_get_obj_name(glp_prob *lp) +{ char *name; + name = lp->obj; + return name; +} + +/*********************************************************************** +* NAME +* +* glp_get_obj_dir - retrieve optimization direction flag +* +* SYNOPSIS +* +* int glp_get_obj_dir(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_obj_dir returns the optimization direction flag +* (i.e. "sense" of the objective function): +* +* GLP_MIN - minimization; +* GLP_MAX - maximization. */ + +int glp_get_obj_dir(glp_prob *lp) +{ int dir = lp->dir; + return dir; +} + +/*********************************************************************** +* NAME +* +* glp_get_num_rows - retrieve number of rows +* +* SYNOPSIS +* +* int glp_get_num_rows(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_num_rows returns the current number of rows in +* the specified problem object. */ + +int glp_get_num_rows(glp_prob *lp) +{ int m = lp->m; + return m; +} + +/*********************************************************************** +* NAME +* +* glp_get_num_cols - retrieve number of columns +* +* SYNOPSIS +* +* int glp_get_num_cols(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_num_cols returns the current number of columns +* in the specified problem object. */ + +int glp_get_num_cols(glp_prob *lp) +{ int n = lp->n; + return n; +} + +/*********************************************************************** +* NAME +* +* glp_get_row_name - retrieve row name +* +* SYNOPSIS +* +* const char *glp_get_row_name(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_row_name returns a pointer to an internal +* buffer, which contains symbolic name of i-th row. However, if i-th +* row has no assigned name, the routine returns NULL. */ + +const char *glp_get_row_name(glp_prob *lp, int i) +{ char *name; + if (!(1 <= i && i <= lp->m)) + xerror("glp_get_row_name: i = %d; row number out of range\n", + i); + name = lp->row[i]->name; + return name; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_name - retrieve column name +* +* SYNOPSIS +* +* const char *glp_get_col_name(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_col_name returns a pointer to an internal +* buffer, which contains symbolic name of j-th column. However, if j-th +* column has no assigned name, the routine returns NULL. */ + +const char *glp_get_col_name(glp_prob *lp, int j) +{ char *name; + if (!(1 <= j && j <= lp->n)) + xerror("glp_get_col_name: j = %d; column number out of range\n" + , j); + name = lp->col[j]->name; + return name; +} + +/*********************************************************************** +* NAME +* +* glp_get_row_type - retrieve row type +* +* SYNOPSIS +* +* int glp_get_row_type(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_row_type returns the type of i-th row, i.e. the +* type of corresponding auxiliary variable, as follows: +* +* GLP_FR - free (unbounded) variable; +* GLP_LO - variable with lower bound; +* GLP_UP - variable with upper bound; +* GLP_DB - double-bounded variable; +* GLP_FX - fixed variable. */ + +int glp_get_row_type(glp_prob *lp, int i) +{ if (!(1 <= i && i <= lp->m)) + xerror("glp_get_row_type: i = %d; row number out of range\n", + i); + return lp->row[i]->type; +} + +/*********************************************************************** +* NAME +* +* glp_get_row_lb - retrieve row lower bound +* +* SYNOPSIS +* +* double glp_get_row_lb(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_row_lb returns the lower bound of i-th row, i.e. +* the lower bound of corresponding auxiliary variable. However, if the +* row has no lower bound, the routine returns -DBL_MAX. */ + +double glp_get_row_lb(glp_prob *lp, int i) +{ double lb; + if (!(1 <= i && i <= lp->m)) + xerror("glp_get_row_lb: i = %d; row number out of range\n", i); + switch (lp->row[i]->type) + { case GLP_FR: + case GLP_UP: + lb = -DBL_MAX; break; + case GLP_LO: + case GLP_DB: + case GLP_FX: + lb = lp->row[i]->lb; break; + default: + xassert(lp != lp); + } + return lb; +} + +/*********************************************************************** +* NAME +* +* glp_get_row_ub - retrieve row upper bound +* +* SYNOPSIS +* +* double glp_get_row_ub(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_row_ub returns the upper bound of i-th row, i.e. +* the upper bound of corresponding auxiliary variable. However, if the +* row has no upper bound, the routine returns +DBL_MAX. */ + +double glp_get_row_ub(glp_prob *lp, int i) +{ double ub; + if (!(1 <= i && i <= lp->m)) + xerror("glp_get_row_ub: i = %d; row number out of range\n", i); + switch (lp->row[i]->type) + { case GLP_FR: + case GLP_LO: + ub = +DBL_MAX; break; + case GLP_UP: + case GLP_DB: + case GLP_FX: + ub = lp->row[i]->ub; break; + default: + xassert(lp != lp); + } + return ub; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_type - retrieve column type +* +* SYNOPSIS +* +* int glp_get_col_type(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_col_type returns the type of j-th column, i.e. +* the type of corresponding structural variable, as follows: +* +* GLP_FR - free (unbounded) variable; +* GLP_LO - variable with lower bound; +* GLP_UP - variable with upper bound; +* GLP_DB - double-bounded variable; +* GLP_FX - fixed variable. */ + +int glp_get_col_type(glp_prob *lp, int j) +{ if (!(1 <= j && j <= lp->n)) + xerror("glp_get_col_type: j = %d; column number out of range\n" + , j); + return lp->col[j]->type; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_lb - retrieve column lower bound +* +* SYNOPSIS +* +* double glp_get_col_lb(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_col_lb returns the lower bound of j-th column, +* i.e. the lower bound of corresponding structural variable. However, +* if the column has no lower bound, the routine returns -DBL_MAX. */ + +double glp_get_col_lb(glp_prob *lp, int j) +{ double lb; + if (!(1 <= j && j <= lp->n)) + xerror("glp_get_col_lb: j = %d; column number out of range\n", + j); + switch (lp->col[j]->type) + { case GLP_FR: + case GLP_UP: + lb = -DBL_MAX; break; + case GLP_LO: + case GLP_DB: + case GLP_FX: + lb = lp->col[j]->lb; break; + default: + xassert(lp != lp); + } + return lb; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_ub - retrieve column upper bound +* +* SYNOPSIS +* +* double glp_get_col_ub(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_col_ub returns the upper bound of j-th column, +* i.e. the upper bound of corresponding structural variable. However, +* if the column has no upper bound, the routine returns +DBL_MAX. */ + +double glp_get_col_ub(glp_prob *lp, int j) +{ double ub; + if (!(1 <= j && j <= lp->n)) + xerror("glp_get_col_ub: j = %d; column number out of range\n", + j); + switch (lp->col[j]->type) + { case GLP_FR: + case GLP_LO: + ub = +DBL_MAX; break; + case GLP_UP: + case GLP_DB: + case GLP_FX: + ub = lp->col[j]->ub; break; + default: + xassert(lp != lp); + } + return ub; +} + +/*********************************************************************** +* NAME +* +* glp_get_obj_coef - retrieve obj. coefficient or constant term +* +* SYNOPSIS +* +* double glp_get_obj_coef(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_obj_coef returns the objective coefficient at +* j-th structural variable (column) of the specified problem object. +* +* If the parameter j is zero, the routine returns the constant term +* ("shift") of the objective function. */ + +double glp_get_obj_coef(glp_prob *lp, int j) +{ if (!(0 <= j && j <= lp->n)) + xerror("glp_get_obj_coef: j = %d; column number out of range\n" + , j); + return j == 0 ? lp->c0 : lp->col[j]->coef; +} + +/*********************************************************************** +* NAME +* +* glp_get_num_nz - retrieve number of constraint coefficients +* +* SYNOPSIS +* +* int glp_get_num_nz(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_num_nz returns the number of (non-zero) elements +* in the constraint matrix of the specified problem object. */ + +int glp_get_num_nz(glp_prob *lp) +{ int nnz = lp->nnz; + return nnz; +} + +/*********************************************************************** +* NAME +* +* glp_get_mat_row - retrieve row of the constraint matrix +* +* SYNOPSIS +* +* int glp_get_mat_row(glp_prob *lp, int i, int ind[], double val[]); +* +* DESCRIPTION +* +* The routine glp_get_mat_row scans (non-zero) elements of i-th row +* of the constraint matrix of the specified problem object and stores +* their column indices and numeric values to locations ind[1], ..., +* ind[len] and val[1], ..., val[len], respectively, where 0 <= len <= n +* is the number of elements in i-th row, n is the number of columns. +* +* The parameter ind and/or val can be specified as NULL, in which case +* corresponding information is not stored. +* +* RETURNS +* +* The routine glp_get_mat_row returns the length len, i.e. the number +* of (non-zero) elements in i-th row. */ + +int glp_get_mat_row(glp_prob *lp, int i, int ind[], double val[]) +{ GLPAIJ *aij; + int len; + if (!(1 <= i && i <= lp->m)) + xerror("glp_get_mat_row: i = %d; row number out of range\n", + i); + len = 0; + for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) + { len++; + if (ind != NULL) ind[len] = aij->col->j; + if (val != NULL) val[len] = aij->val; + } + xassert(len <= lp->n); + return len; +} + +/*********************************************************************** +* NAME +* +* glp_get_mat_col - retrieve column of the constraint matrix +* +* SYNOPSIS +* +* int glp_get_mat_col(glp_prob *lp, int j, int ind[], double val[]); +* +* DESCRIPTION +* +* The routine glp_get_mat_col scans (non-zero) elements of j-th column +* of the constraint matrix of the specified problem object and stores +* their row indices and numeric values to locations ind[1], ..., +* ind[len] and val[1], ..., val[len], respectively, where 0 <= len <= m +* is the number of elements in j-th column, m is the number of rows. +* +* The parameter ind or/and val can be specified as NULL, in which case +* corresponding information is not stored. +* +* RETURNS +* +* The routine glp_get_mat_col returns the length len, i.e. the number +* of (non-zero) elements in j-th column. */ + +int glp_get_mat_col(glp_prob *lp, int j, int ind[], double val[]) +{ GLPAIJ *aij; + int len; + if (!(1 <= j && j <= lp->n)) + xerror("glp_get_mat_col: j = %d; column number out of range\n", + j); + len = 0; + for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) + { len++; + if (ind != NULL) ind[len] = aij->row->i; + if (val != NULL) val[len] = aij->val; + } + xassert(len <= lp->m); + return len; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi03.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi03.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,166 @@ +/* glpapi03.c (row and column searching routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_create_index - create the name index +* +* SYNOPSIS +* +* void glp_create_index(glp_prob *lp); +* +* DESCRIPTION +* +* The routine glp_create_index creates the name index for the +* specified problem object. The name index is an auxiliary data +* structure, which is intended to quickly (i.e. for logarithmic time) +* find rows and columns by their names. +* +* This routine can be called at any time. If the name index already +* exists, the routine does nothing. */ + +void glp_create_index(glp_prob *lp) +{ GLPROW *row; + GLPCOL *col; + int i, j; + /* create row name index */ + if (lp->r_tree == NULL) + { lp->r_tree = avl_create_tree(avl_strcmp, NULL); + for (i = 1; i <= lp->m; i++) + { row = lp->row[i]; + xassert(row->node == NULL); + if (row->name != NULL) + { row->node = avl_insert_node(lp->r_tree, row->name); + avl_set_node_link(row->node, row); + } + } + } + /* create column name index */ + if (lp->c_tree == NULL) + { lp->c_tree = avl_create_tree(avl_strcmp, NULL); + for (j = 1; j <= lp->n; j++) + { col = lp->col[j]; + xassert(col->node == NULL); + if (col->name != NULL) + { col->node = avl_insert_node(lp->c_tree, col->name); + avl_set_node_link(col->node, col); + } + } + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_find_row - find row by its name +* +* SYNOPSIS +* +* int glp_find_row(glp_prob *lp, const char *name); +* +* RETURNS +* +* The routine glp_find_row returns the ordinal number of a row, +* which is assigned (by the routine glp_set_row_name) the specified +* symbolic name. If no such row exists, the routine returns 0. */ + +int glp_find_row(glp_prob *lp, const char *name) +{ AVLNODE *node; + int i = 0; + if (lp->r_tree == NULL) + xerror("glp_find_row: row name index does not exist\n"); + if (!(name == NULL || name[0] == '\0' || strlen(name) > 255)) + { node = avl_find_node(lp->r_tree, name); + if (node != NULL) + i = ((GLPROW *)avl_get_node_link(node))->i; + } + return i; +} + +/*********************************************************************** +* NAME +* +* glp_find_col - find column by its name +* +* SYNOPSIS +* +* int glp_find_col(glp_prob *lp, const char *name); +* +* RETURNS +* +* The routine glp_find_col returns the ordinal number of a column, +* which is assigned (by the routine glp_set_col_name) the specified +* symbolic name. If no such column exists, the routine returns 0. */ + +int glp_find_col(glp_prob *lp, const char *name) +{ AVLNODE *node; + int j = 0; + if (lp->c_tree == NULL) + xerror("glp_find_col: column name index does not exist\n"); + if (!(name == NULL || name[0] == '\0' || strlen(name) > 255)) + { node = avl_find_node(lp->c_tree, name); + if (node != NULL) + j = ((GLPCOL *)avl_get_node_link(node))->j; + } + return j; +} + +/*********************************************************************** +* NAME +* +* glp_delete_index - delete the name index +* +* SYNOPSIS +* +* void glp_delete_index(glp_prob *lp); +* +* DESCRIPTION +* +* The routine glp_delete_index deletes the name index previously +* created by the routine glp_create_index and frees the memory +* allocated to this auxiliary data structure. +* +* This routine can be called at any time. If the name index does not +* exist, the routine does nothing. */ + +void glp_delete_index(glp_prob *lp) +{ int i, j; + /* delete row name index */ + if (lp->r_tree != NULL) + { for (i = 1; i <= lp->m; i++) lp->row[i]->node = NULL; + avl_delete_tree(lp->r_tree), lp->r_tree = NULL; + } + /* delete column name index */ + if (lp->c_tree != NULL) + { for (j = 1; j <= lp->n; j++) lp->col[j]->node = NULL; + avl_delete_tree(lp->c_tree), lp->c_tree = NULL; + } + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi04.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi04.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,156 @@ +/* glpapi04.c (problem scaling routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_set_rii - set (change) row scale factor +* +* SYNOPSIS +* +* void glp_set_rii(glp_prob *lp, int i, double rii); +* +* DESCRIPTION +* +* The routine glp_set_rii sets (changes) the scale factor r[i,i] for +* i-th row of the specified problem object. */ + +void glp_set_rii(glp_prob *lp, int i, double rii) +{ if (!(1 <= i && i <= lp->m)) + xerror("glp_set_rii: i = %d; row number out of range\n", i); + if (rii <= 0.0) + xerror("glp_set_rii: i = %d; rii = %g; invalid scale factor\n", + i, rii); + if (lp->valid && lp->row[i]->rii != rii) + { GLPAIJ *aij; + for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) + { if (aij->col->stat == GLP_BS) + { /* invalidate the basis factorization */ + lp->valid = 0; + break; + } + } + } + lp->row[i]->rii = rii; + return; +} + +/*********************************************************************** +* NAME +* +* glp_set sjj - set (change) column scale factor +* +* SYNOPSIS +* +* void glp_set_sjj(glp_prob *lp, int j, double sjj); +* +* DESCRIPTION +* +* The routine glp_set_sjj sets (changes) the scale factor s[j,j] for +* j-th column of the specified problem object. */ + +void glp_set_sjj(glp_prob *lp, int j, double sjj) +{ if (!(1 <= j && j <= lp->n)) + xerror("glp_set_sjj: j = %d; column number out of range\n", j); + if (sjj <= 0.0) + xerror("glp_set_sjj: j = %d; sjj = %g; invalid scale factor\n", + j, sjj); + if (lp->valid && lp->col[j]->sjj != sjj && lp->col[j]->stat == + GLP_BS) + { /* invalidate the basis factorization */ + lp->valid = 0; + } + lp->col[j]->sjj = sjj; + return; +} + +/*********************************************************************** +* NAME +* +* glp_get_rii - retrieve row scale factor +* +* SYNOPSIS +* +* double glp_get_rii(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_rii returns current scale factor r[i,i] for i-th +* row of the specified problem object. */ + +double glp_get_rii(glp_prob *lp, int i) +{ if (!(1 <= i && i <= lp->m)) + xerror("glp_get_rii: i = %d; row number out of range\n", i); + return lp->row[i]->rii; +} + +/*********************************************************************** +* NAME +* +* glp_get_sjj - retrieve column scale factor +* +* SYNOPSIS +* +* double glp_get_sjj(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_sjj returns current scale factor s[j,j] for j-th +* column of the specified problem object. */ + +double glp_get_sjj(glp_prob *lp, int j) +{ if (!(1 <= j && j <= lp->n)) + xerror("glp_get_sjj: j = %d; column number out of range\n", j); + return lp->col[j]->sjj; +} + +/*********************************************************************** +* NAME +* +* glp_unscale_prob - unscale problem data +* +* SYNOPSIS +* +* void glp_unscale_prob(glp_prob *lp); +* +* DESCRIPTION +* +* The routine glp_unscale_prob performs unscaling of problem data for +* the specified problem object. +* +* "Unscaling" means replacing the current scaling matrices R and S by +* unity matrices that cancels the scaling effect. */ + +void glp_unscale_prob(glp_prob *lp) +{ int m = glp_get_num_rows(lp); + int n = glp_get_num_cols(lp); + int i, j; + for (i = 1; i <= m; i++) glp_set_rii(lp, i, 1.0); + for (j = 1; j <= n; j++) glp_set_sjj(lp, j, 1.0); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi05.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi05.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,168 @@ +/* glpapi05.c (LP basis constructing routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_set_row_stat - set (change) row status +* +* SYNOPSIS +* +* void glp_set_row_stat(glp_prob *lp, int i, int stat); +* +* DESCRIPTION +* +* The routine glp_set_row_stat sets (changes) status of the auxiliary +* variable associated with i-th row. +* +* The new status of the auxiliary variable should be specified by the +* parameter stat as follows: +* +* GLP_BS - basic variable; +* GLP_NL - non-basic variable; +* GLP_NU - non-basic variable on its upper bound; if the variable is +* not double-bounded, this means the same as GLP_NL (only in +* case of this routine); +* GLP_NF - the same as GLP_NL (only in case of this routine); +* GLP_NS - the same as GLP_NL (only in case of this routine). */ + +void glp_set_row_stat(glp_prob *lp, int i, int stat) +{ GLPROW *row; + if (!(1 <= i && i <= lp->m)) + xerror("glp_set_row_stat: i = %d; row number out of range\n", + i); + if (!(stat == GLP_BS || stat == GLP_NL || stat == GLP_NU || + stat == GLP_NF || stat == GLP_NS)) + xerror("glp_set_row_stat: i = %d; stat = %d; invalid status\n", + i, stat); + row = lp->row[i]; + if (stat != GLP_BS) + { switch (row->type) + { case GLP_FR: stat = GLP_NF; break; + case GLP_LO: stat = GLP_NL; break; + case GLP_UP: stat = GLP_NU; break; + case GLP_DB: if (stat != GLP_NU) stat = GLP_NL; break; + case GLP_FX: stat = GLP_NS; break; + default: xassert(row != row); + } + } + if (row->stat == GLP_BS && stat != GLP_BS || + row->stat != GLP_BS && stat == GLP_BS) + { /* invalidate the basis factorization */ + lp->valid = 0; + } + row->stat = stat; + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_col_stat - set (change) column status +* +* SYNOPSIS +* +* void glp_set_col_stat(glp_prob *lp, int j, int stat); +* +* DESCRIPTION +* +* The routine glp_set_col_stat sets (changes) status of the structural +* variable associated with j-th column. +* +* The new status of the structural variable should be specified by the +* parameter stat as follows: +* +* GLP_BS - basic variable; +* GLP_NL - non-basic variable; +* GLP_NU - non-basic variable on its upper bound; if the variable is +* not double-bounded, this means the same as GLP_NL (only in +* case of this routine); +* GLP_NF - the same as GLP_NL (only in case of this routine); +* GLP_NS - the same as GLP_NL (only in case of this routine). */ + +void glp_set_col_stat(glp_prob *lp, int j, int stat) +{ GLPCOL *col; + if (!(1 <= j && j <= lp->n)) + xerror("glp_set_col_stat: j = %d; column number out of range\n" + , j); + if (!(stat == GLP_BS || stat == GLP_NL || stat == GLP_NU || + stat == GLP_NF || stat == GLP_NS)) + xerror("glp_set_col_stat: j = %d; stat = %d; invalid status\n", + j, stat); + col = lp->col[j]; + if (stat != GLP_BS) + { switch (col->type) + { case GLP_FR: stat = GLP_NF; break; + case GLP_LO: stat = GLP_NL; break; + case GLP_UP: stat = GLP_NU; break; + case GLP_DB: if (stat != GLP_NU) stat = GLP_NL; break; + case GLP_FX: stat = GLP_NS; break; + default: xassert(col != col); + } + } + if (col->stat == GLP_BS && stat != GLP_BS || + col->stat != GLP_BS && stat == GLP_BS) + { /* invalidate the basis factorization */ + lp->valid = 0; + } + col->stat = stat; + return; +} + +/*********************************************************************** +* NAME +* +* glp_std_basis - construct standard initial LP basis +* +* SYNOPSIS +* +* void glp_std_basis(glp_prob *lp); +* +* DESCRIPTION +* +* The routine glp_std_basis builds the "standard" (trivial) initial +* basis for the specified problem object. +* +* In the "standard" basis all auxiliary variables are basic, and all +* structural variables are non-basic. */ + +void glp_std_basis(glp_prob *lp) +{ int i, j; + /* make all auxiliary variables basic */ + for (i = 1; i <= lp->m; i++) + glp_set_row_stat(lp, i, GLP_BS); + /* make all structural variables non-basic */ + for (j = 1; j <= lp->n; j++) + { GLPCOL *col = lp->col[j]; + if (col->type == GLP_DB && fabs(col->lb) > fabs(col->ub)) + glp_set_col_stat(lp, j, GLP_NU); + else + glp_set_col_stat(lp, j, GLP_NL); + } + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi06.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi06.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,806 @@ +/* glpapi06.c (simplex method routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" +#include "glpnpp.h" +#include "glpspx.h" + +/*********************************************************************** +* NAME +* +* glp_simplex - solve LP problem with the simplex method +* +* SYNOPSIS +* +* int glp_simplex(glp_prob *P, const glp_smcp *parm); +* +* DESCRIPTION +* +* The routine glp_simplex is a driver to the LP solver based on the +* simplex method. This routine retrieves problem data from the +* specified problem object, calls the solver to solve the problem +* instance, and stores results of computations back into the problem +* object. +* +* The simplex solver has a set of control parameters. Values of the +* control parameters can be passed in a structure glp_smcp, which the +* parameter parm points to. +* +* The parameter parm can be specified as NULL, in which case the LP +* solver uses default settings. +* +* RETURNS +* +* 0 The LP problem instance has been successfully solved. This code +* does not necessarily mean that the solver has found optimal +* solution. It only means that the solution process was successful. +* +* GLP_EBADB +* Unable to start the search, because the initial basis specified +* in the problem object is invalid--the number of basic (auxiliary +* and structural) variables is not the same as the number of rows in +* the problem object. +* +* GLP_ESING +* Unable to start the search, because the basis matrix correspodning +* to the initial basis is singular within the working precision. +* +* GLP_ECOND +* Unable to start the search, because the basis matrix correspodning +* to the initial basis is ill-conditioned, i.e. its condition number +* is too large. +* +* GLP_EBOUND +* Unable to start the search, because some double-bounded variables +* have incorrect bounds. +* +* GLP_EFAIL +* The search was prematurely terminated due to the solver failure. +* +* GLP_EOBJLL +* The search was prematurely terminated, because the objective +* function being maximized has reached its lower limit and continues +* decreasing (dual simplex only). +* +* GLP_EOBJUL +* The search was prematurely terminated, because the objective +* function being minimized has reached its upper limit and continues +* increasing (dual simplex only). +* +* GLP_EITLIM +* The search was prematurely terminated, because the simplex +* iteration limit has been exceeded. +* +* GLP_ETMLIM +* The search was prematurely terminated, because the time limit has +* been exceeded. +* +* GLP_ENOPFS +* The LP problem instance has no primal feasible solution (only if +* the LP presolver is used). +* +* GLP_ENODFS +* The LP problem instance has no dual feasible solution (only if the +* LP presolver is used). */ + +static void trivial_lp(glp_prob *P, const glp_smcp *parm) +{ /* solve trivial LP which has empty constraint matrix */ + GLPROW *row; + GLPCOL *col; + int i, j; + double p_infeas, d_infeas, zeta; + P->valid = 0; + P->pbs_stat = P->dbs_stat = GLP_FEAS; + P->obj_val = P->c0; + P->some = 0; + p_infeas = d_infeas = 0.0; + /* make all auxiliary variables basic */ + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + row->stat = GLP_BS; + row->prim = row->dual = 0.0; + /* check primal feasibility */ + if (row->type == GLP_LO || row->type == GLP_DB || + row->type == GLP_FX) + { /* row has lower bound */ + if (row->lb > + parm->tol_bnd) + { P->pbs_stat = GLP_NOFEAS; + if (P->some == 0 && parm->meth != GLP_PRIMAL) + P->some = i; + } + if (p_infeas < + row->lb) + p_infeas = + row->lb; + } + if (row->type == GLP_UP || row->type == GLP_DB || + row->type == GLP_FX) + { /* row has upper bound */ + if (row->ub < - parm->tol_bnd) + { P->pbs_stat = GLP_NOFEAS; + if (P->some == 0 && parm->meth != GLP_PRIMAL) + P->some = i; + } + if (p_infeas < - row->ub) + p_infeas = - row->ub; + } + } + /* determine scale factor for the objective row */ + zeta = 1.0; + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (zeta < fabs(col->coef)) zeta = fabs(col->coef); + } + zeta = (P->dir == GLP_MIN ? +1.0 : -1.0) / zeta; + /* make all structural variables non-basic */ + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->type == GLP_FR) + col->stat = GLP_NF, col->prim = 0.0; + else if (col->type == GLP_LO) +lo: col->stat = GLP_NL, col->prim = col->lb; + else if (col->type == GLP_UP) +up: col->stat = GLP_NU, col->prim = col->ub; + else if (col->type == GLP_DB) + { if (zeta * col->coef > 0.0) + goto lo; + else if (zeta * col->coef < 0.0) + goto up; + else if (fabs(col->lb) <= fabs(col->ub)) + goto lo; + else + goto up; + } + else if (col->type == GLP_FX) + col->stat = GLP_NS, col->prim = col->lb; + col->dual = col->coef; + P->obj_val += col->coef * col->prim; + /* check dual feasibility */ + if (col->type == GLP_FR || col->type == GLP_LO) + { /* column has no upper bound */ + if (zeta * col->dual < - parm->tol_dj) + { P->dbs_stat = GLP_NOFEAS; + if (P->some == 0 && parm->meth == GLP_PRIMAL) + P->some = P->m + j; + } + if (d_infeas < - zeta * col->dual) + d_infeas = - zeta * col->dual; + } + if (col->type == GLP_FR || col->type == GLP_UP) + { /* column has no lower bound */ + if (zeta * col->dual > + parm->tol_dj) + { P->dbs_stat = GLP_NOFEAS; + if (P->some == 0 && parm->meth == GLP_PRIMAL) + P->some = P->m + j; + } + if (d_infeas < + zeta * col->dual) + d_infeas = + zeta * col->dual; + } + } + /* simulate the simplex solver output */ + if (parm->msg_lev >= GLP_MSG_ON && parm->out_dly == 0) + { xprintf("~%6d: obj = %17.9e infeas = %10.3e\n", P->it_cnt, + P->obj_val, parm->meth == GLP_PRIMAL ? p_infeas : d_infeas); + } + if (parm->msg_lev >= GLP_MSG_ALL && parm->out_dly == 0) + { if (P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS) + xprintf("OPTIMAL SOLUTION FOUND\n"); + else if (P->pbs_stat == GLP_NOFEAS) + xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n"); + else if (parm->meth == GLP_PRIMAL) + xprintf("PROBLEM HAS UNBOUNDED SOLUTION\n"); + else + xprintf("PROBLEM HAS NO DUAL FEASIBLE SOLUTION\n"); + } + return; +} + +static int solve_lp(glp_prob *P, const glp_smcp *parm) +{ /* solve LP directly without using the preprocessor */ + int ret; + if (!glp_bf_exists(P)) + { ret = glp_factorize(P); + if (ret == 0) + ; + else if (ret == GLP_EBADB) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_simplex: initial basis is invalid\n"); + } + else if (ret == GLP_ESING) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_simplex: initial basis is singular\n"); + } + else if (ret == GLP_ECOND) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf( + "glp_simplex: initial basis is ill-conditioned\n"); + } + else + xassert(ret != ret); + if (ret != 0) goto done; + } + if (parm->meth == GLP_PRIMAL) + ret = spx_primal(P, parm); + else if (parm->meth == GLP_DUALP) + { ret = spx_dual(P, parm); + if (ret == GLP_EFAIL && P->valid) + ret = spx_primal(P, parm); + } + else if (parm->meth == GLP_DUAL) + ret = spx_dual(P, parm); + else + xassert(parm != parm); +done: return ret; +} + +static int preprocess_and_solve_lp(glp_prob *P, const glp_smcp *parm) +{ /* solve LP using the preprocessor */ + NPP *npp; + glp_prob *lp = NULL; + glp_bfcp bfcp; + int ret; + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("Preprocessing...\n"); + /* create preprocessor workspace */ + npp = npp_create_wksp(); + /* load original problem into the preprocessor workspace */ + npp_load_prob(npp, P, GLP_OFF, GLP_SOL, GLP_OFF); + /* process LP prior to applying primal/dual simplex method */ + ret = npp_simplex(npp, parm); + if (ret == 0) + ; + else if (ret == GLP_ENOPFS) + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION\n"); + } + else if (ret == GLP_ENODFS) + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS NO DUAL FEASIBLE SOLUTION\n"); + } + else + xassert(ret != ret); + if (ret != 0) goto done; + /* build transformed LP */ + lp = glp_create_prob(); + npp_build_prob(npp, lp); + /* if the transformed LP is empty, it has empty solution, which + is optimal */ + if (lp->m == 0 && lp->n == 0) + { lp->pbs_stat = lp->dbs_stat = GLP_FEAS; + lp->obj_val = lp->c0; + if (parm->msg_lev >= GLP_MSG_ON && parm->out_dly == 0) + { xprintf("~%6d: obj = %17.9e infeas = %10.3e\n", P->it_cnt, + lp->obj_val, 0.0); + } + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("OPTIMAL SOLUTION FOUND BY LP PREPROCESSOR\n"); + goto post; + } + if (parm->msg_lev >= GLP_MSG_ALL) + { xprintf("%d row%s, %d column%s, %d non-zero%s\n", + lp->m, lp->m == 1 ? "" : "s", lp->n, lp->n == 1 ? "" : "s", + lp->nnz, lp->nnz == 1 ? "" : "s"); + } + /* inherit basis factorization control parameters */ + glp_get_bfcp(P, &bfcp); + glp_set_bfcp(lp, &bfcp); + /* scale the transformed problem */ + { ENV *env = get_env_ptr(); + int term_out = env->term_out; + if (!term_out || parm->msg_lev < GLP_MSG_ALL) + env->term_out = GLP_OFF; + else + env->term_out = GLP_ON; + glp_scale_prob(lp, GLP_SF_AUTO); + env->term_out = term_out; + } + /* build advanced initial basis */ + { ENV *env = get_env_ptr(); + int term_out = env->term_out; + if (!term_out || parm->msg_lev < GLP_MSG_ALL) + env->term_out = GLP_OFF; + else + env->term_out = GLP_ON; + glp_adv_basis(lp, 0); + env->term_out = term_out; + } + /* solve the transformed LP */ + lp->it_cnt = P->it_cnt; + ret = solve_lp(lp, parm); + P->it_cnt = lp->it_cnt; + /* only optimal solution can be postprocessed */ + if (!(ret == 0 && lp->pbs_stat == GLP_FEAS && lp->dbs_stat == + GLP_FEAS)) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_simplex: unable to recover undefined or non-op" + "timal solution\n"); + if (ret == 0) + { if (lp->pbs_stat == GLP_NOFEAS) + ret = GLP_ENOPFS; + else if (lp->dbs_stat == GLP_NOFEAS) + ret = GLP_ENODFS; + else + xassert(lp != lp); + } + goto done; + } +post: /* postprocess solution from the transformed LP */ + npp_postprocess(npp, lp); + /* the transformed LP is no longer needed */ + glp_delete_prob(lp), lp = NULL; + /* store solution to the original problem */ + npp_unload_sol(npp, P); + /* the original LP has been successfully solved */ + ret = 0; +done: /* delete the transformed LP, if it exists */ + if (lp != NULL) glp_delete_prob(lp); + /* delete preprocessor workspace */ + npp_delete_wksp(npp); + return ret; +} + +int glp_simplex(glp_prob *P, const glp_smcp *parm) +{ /* solve LP problem with the simplex method */ + glp_smcp _parm; + int i, j, ret; + /* check problem object */ + if (P == NULL || P->magic != GLP_PROB_MAGIC) + xerror("glp_simplex: P = %p; invalid problem object\n", P); + if (P->tree != NULL && P->tree->reason != 0) + xerror("glp_simplex: operation not allowed\n"); + /* check control parameters */ + if (parm == NULL) + parm = &_parm, glp_init_smcp((glp_smcp *)parm); + if (!(parm->msg_lev == GLP_MSG_OFF || + parm->msg_lev == GLP_MSG_ERR || + parm->msg_lev == GLP_MSG_ON || + parm->msg_lev == GLP_MSG_ALL || + parm->msg_lev == GLP_MSG_DBG)) + xerror("glp_simplex: msg_lev = %d; invalid parameter\n", + parm->msg_lev); + if (!(parm->meth == GLP_PRIMAL || + parm->meth == GLP_DUALP || + parm->meth == GLP_DUAL)) + xerror("glp_simplex: meth = %d; invalid parameter\n", + parm->meth); + if (!(parm->pricing == GLP_PT_STD || + parm->pricing == GLP_PT_PSE)) + xerror("glp_simplex: pricing = %d; invalid parameter\n", + parm->pricing); + if (!(parm->r_test == GLP_RT_STD || + parm->r_test == GLP_RT_HAR)) + xerror("glp_simplex: r_test = %d; invalid parameter\n", + parm->r_test); + if (!(0.0 < parm->tol_bnd && parm->tol_bnd < 1.0)) + xerror("glp_simplex: tol_bnd = %g; invalid parameter\n", + parm->tol_bnd); + if (!(0.0 < parm->tol_dj && parm->tol_dj < 1.0)) + xerror("glp_simplex: tol_dj = %g; invalid parameter\n", + parm->tol_dj); + if (!(0.0 < parm->tol_piv && parm->tol_piv < 1.0)) + xerror("glp_simplex: tol_piv = %g; invalid parameter\n", + parm->tol_piv); + if (parm->it_lim < 0) + xerror("glp_simplex: it_lim = %d; invalid parameter\n", + parm->it_lim); + if (parm->tm_lim < 0) + xerror("glp_simplex: tm_lim = %d; invalid parameter\n", + parm->tm_lim); + if (parm->out_frq < 1) + xerror("glp_simplex: out_frq = %d; invalid parameter\n", + parm->out_frq); + if (parm->out_dly < 0) + xerror("glp_simplex: out_dly = %d; invalid parameter\n", + parm->out_dly); + if (!(parm->presolve == GLP_ON || parm->presolve == GLP_OFF)) + xerror("glp_simplex: presolve = %d; invalid parameter\n", + parm->presolve); + /* basic solution is currently undefined */ + P->pbs_stat = P->dbs_stat = GLP_UNDEF; + P->obj_val = 0.0; + P->some = 0; + /* check bounds of double-bounded variables */ + for (i = 1; i <= P->m; i++) + { GLPROW *row = P->row[i]; + if (row->type == GLP_DB && row->lb >= row->ub) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_simplex: row %d: lb = %g, ub = %g; incorrec" + "t bounds\n", i, row->lb, row->ub); + ret = GLP_EBOUND; + goto done; + } + } + for (j = 1; j <= P->n; j++) + { GLPCOL *col = P->col[j]; + if (col->type == GLP_DB && col->lb >= col->ub) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_simplex: column %d: lb = %g, ub = %g; incor" + "rect bounds\n", j, col->lb, col->ub); + ret = GLP_EBOUND; + goto done; + } + } + /* solve LP problem */ + if (parm->msg_lev >= GLP_MSG_ALL) + { xprintf("GLPK Simplex Optimizer, v%s\n", glp_version()); + xprintf("%d row%s, %d column%s, %d non-zero%s\n", + P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", + P->nnz, P->nnz == 1 ? "" : "s"); + } + if (P->nnz == 0) + trivial_lp(P, parm), ret = 0; + else if (!parm->presolve) + ret = solve_lp(P, parm); + else + ret = preprocess_and_solve_lp(P, parm); +done: /* return to the application program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_init_smcp - initialize simplex method control parameters +* +* SYNOPSIS +* +* void glp_init_smcp(glp_smcp *parm); +* +* DESCRIPTION +* +* The routine glp_init_smcp initializes control parameters, which are +* used by the simplex solver, with default values. +* +* Default values of the control parameters are stored in a glp_smcp +* structure, which the parameter parm points to. */ + +void glp_init_smcp(glp_smcp *parm) +{ parm->msg_lev = GLP_MSG_ALL; + parm->meth = GLP_PRIMAL; + parm->pricing = GLP_PT_PSE; + parm->r_test = GLP_RT_HAR; + parm->tol_bnd = 1e-7; + parm->tol_dj = 1e-7; + parm->tol_piv = 1e-10; + parm->obj_ll = -DBL_MAX; + parm->obj_ul = +DBL_MAX; + parm->it_lim = INT_MAX; + parm->tm_lim = INT_MAX; + parm->out_frq = 500; + parm->out_dly = 0; + parm->presolve = GLP_OFF; + return; +} + +/*********************************************************************** +* NAME +* +* glp_get_status - retrieve generic status of basic solution +* +* SYNOPSIS +* +* int glp_get_status(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_status reports the generic status of the basic +* solution for the specified problem object as follows: +* +* GLP_OPT - solution is optimal; +* GLP_FEAS - solution is feasible; +* GLP_INFEAS - solution is infeasible; +* GLP_NOFEAS - problem has no feasible solution; +* GLP_UNBND - problem has unbounded solution; +* GLP_UNDEF - solution is undefined. */ + +int glp_get_status(glp_prob *lp) +{ int status; + status = glp_get_prim_stat(lp); + switch (status) + { case GLP_FEAS: + switch (glp_get_dual_stat(lp)) + { case GLP_FEAS: + status = GLP_OPT; + break; + case GLP_NOFEAS: + status = GLP_UNBND; + break; + case GLP_UNDEF: + case GLP_INFEAS: + status = status; + break; + default: + xassert(lp != lp); + } + break; + case GLP_UNDEF: + case GLP_INFEAS: + case GLP_NOFEAS: + status = status; + break; + default: + xassert(lp != lp); + } + return status; +} + +/*********************************************************************** +* NAME +* +* glp_get_prim_stat - retrieve status of primal basic solution +* +* SYNOPSIS +* +* int glp_get_prim_stat(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_prim_stat reports the status of the primal basic +* solution for the specified problem object as follows: +* +* GLP_UNDEF - primal solution is undefined; +* GLP_FEAS - primal solution is feasible; +* GLP_INFEAS - primal solution is infeasible; +* GLP_NOFEAS - no primal feasible solution exists. */ + +int glp_get_prim_stat(glp_prob *lp) +{ int pbs_stat = lp->pbs_stat; + return pbs_stat; +} + +/*********************************************************************** +* NAME +* +* glp_get_dual_stat - retrieve status of dual basic solution +* +* SYNOPSIS +* +* int glp_get_dual_stat(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_dual_stat reports the status of the dual basic +* solution for the specified problem object as follows: +* +* GLP_UNDEF - dual solution is undefined; +* GLP_FEAS - dual solution is feasible; +* GLP_INFEAS - dual solution is infeasible; +* GLP_NOFEAS - no dual feasible solution exists. */ + +int glp_get_dual_stat(glp_prob *lp) +{ int dbs_stat = lp->dbs_stat; + return dbs_stat; +} + +/*********************************************************************** +* NAME +* +* glp_get_obj_val - retrieve objective value (basic solution) +* +* SYNOPSIS +* +* double glp_get_obj_val(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_obj_val returns value of the objective function +* for basic solution. */ + +double glp_get_obj_val(glp_prob *lp) +{ /*struct LPXCPS *cps = lp->cps;*/ + double z; + z = lp->obj_val; + /*if (cps->round && fabs(z) < 1e-9) z = 0.0;*/ + return z; +} + +/*********************************************************************** +* NAME +* +* glp_get_row_stat - retrieve row status +* +* SYNOPSIS +* +* int glp_get_row_stat(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_row_stat returns current status assigned to the +* auxiliary variable associated with i-th row as follows: +* +* GLP_BS - basic variable; +* GLP_NL - non-basic variable on its lower bound; +* GLP_NU - non-basic variable on its upper bound; +* GLP_NF - non-basic free (unbounded) variable; +* GLP_NS - non-basic fixed variable. */ + +int glp_get_row_stat(glp_prob *lp, int i) +{ if (!(1 <= i && i <= lp->m)) + xerror("glp_get_row_stat: i = %d; row number out of range\n", + i); + return lp->row[i]->stat; +} + +/*********************************************************************** +* NAME +* +* glp_get_row_prim - retrieve row primal value (basic solution) +* +* SYNOPSIS +* +* double glp_get_row_prim(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_row_prim returns primal value of the auxiliary +* variable associated with i-th row. */ + +double glp_get_row_prim(glp_prob *lp, int i) +{ /*struct LPXCPS *cps = lp->cps;*/ + double prim; + if (!(1 <= i && i <= lp->m)) + xerror("glp_get_row_prim: i = %d; row number out of range\n", + i); + prim = lp->row[i]->prim; + /*if (cps->round && fabs(prim) < 1e-9) prim = 0.0;*/ + return prim; +} + +/*********************************************************************** +* NAME +* +* glp_get_row_dual - retrieve row dual value (basic solution) +* +* SYNOPSIS +* +* double glp_get_row_dual(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_row_dual returns dual value (i.e. reduced cost) +* of the auxiliary variable associated with i-th row. */ + +double glp_get_row_dual(glp_prob *lp, int i) +{ /*struct LPXCPS *cps = lp->cps;*/ + double dual; + if (!(1 <= i && i <= lp->m)) + xerror("glp_get_row_dual: i = %d; row number out of range\n", + i); + dual = lp->row[i]->dual; + /*if (cps->round && fabs(dual) < 1e-9) dual = 0.0;*/ + return dual; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_stat - retrieve column status +* +* SYNOPSIS +* +* int glp_get_col_stat(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_col_stat returns current status assigned to the +* structural variable associated with j-th column as follows: +* +* GLP_BS - basic variable; +* GLP_NL - non-basic variable on its lower bound; +* GLP_NU - non-basic variable on its upper bound; +* GLP_NF - non-basic free (unbounded) variable; +* GLP_NS - non-basic fixed variable. */ + +int glp_get_col_stat(glp_prob *lp, int j) +{ if (!(1 <= j && j <= lp->n)) + xerror("glp_get_col_stat: j = %d; column number out of range\n" + , j); + return lp->col[j]->stat; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_prim - retrieve column primal value (basic solution) +* +* SYNOPSIS +* +* double glp_get_col_prim(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_col_prim returns primal value of the structural +* variable associated with j-th column. */ + +double glp_get_col_prim(glp_prob *lp, int j) +{ /*struct LPXCPS *cps = lp->cps;*/ + double prim; + if (!(1 <= j && j <= lp->n)) + xerror("glp_get_col_prim: j = %d; column number out of range\n" + , j); + prim = lp->col[j]->prim; + /*if (cps->round && fabs(prim) < 1e-9) prim = 0.0;*/ + return prim; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_dual - retrieve column dual value (basic solution) +* +* SYNOPSIS +* +* double glp_get_col_dual(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_col_dual returns dual value (i.e. reduced cost) +* of the structural variable associated with j-th column. */ + +double glp_get_col_dual(glp_prob *lp, int j) +{ /*struct LPXCPS *cps = lp->cps;*/ + double dual; + if (!(1 <= j && j <= lp->n)) + xerror("glp_get_col_dual: j = %d; column number out of range\n" + , j); + dual = lp->col[j]->dual; + /*if (cps->round && fabs(dual) < 1e-9) dual = 0.0;*/ + return dual; +} + +/*********************************************************************** +* NAME +* +* glp_get_unbnd_ray - determine variable causing unboundedness +* +* SYNOPSIS +* +* int glp_get_unbnd_ray(glp_prob *lp); +* +* RETURNS +* +* The routine glp_get_unbnd_ray returns the number k of a variable, +* which causes primal or dual unboundedness. If 1 <= k <= m, it is +* k-th auxiliary variable, and if m+1 <= k <= m+n, it is (k-m)-th +* structural variable, where m is the number of rows, n is the number +* of columns in the problem object. If such variable is not defined, +* the routine returns 0. +* +* COMMENTS +* +* If it is not exactly known which version of the simplex solver +* detected unboundedness, i.e. whether the unboundedness is primal or +* dual, it is sufficient to check the status of the variable reported +* with the routine glp_get_row_stat or glp_get_col_stat. If the +* variable is non-basic, the unboundedness is primal, otherwise, if +* the variable is basic, the unboundedness is dual (the latter case +* means that the problem has no primal feasible dolution). */ + +int glp_get_unbnd_ray(glp_prob *lp) +{ int k; + k = lp->some; + xassert(k >= 0); + if (k > lp->m + lp->n) k = 0; + return k; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi07.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi07.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,451 @@ +/* glpapi07.c (exact simplex solver) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" +#include "glpssx.h" + +/*********************************************************************** +* NAME +* +* glp_exact - solve LP problem in exact arithmetic +* +* SYNOPSIS +* +* int glp_exact(glp_prob *lp, const glp_smcp *parm); +* +* DESCRIPTION +* +* The routine glp_exact is a tentative implementation of the primal +* two-phase simplex method based on exact (rational) arithmetic. It is +* similar to the routine glp_simplex, however, for all internal +* computations it uses arithmetic of rational numbers, which is exact +* in mathematical sense, i.e. free of round-off errors unlike floating +* point arithmetic. +* +* Note that the routine glp_exact uses inly two control parameters +* passed in the structure glp_smcp, namely, it_lim and tm_lim. +* +* RETURNS +* +* 0 The LP problem instance has been successfully solved. This code +* does not necessarily mean that the solver has found optimal +* solution. It only means that the solution process was successful. +* +* GLP_EBADB +* Unable to start the search, because the initial basis specified +* in the problem object is invalid--the number of basic (auxiliary +* and structural) variables is not the same as the number of rows in +* the problem object. +* +* GLP_ESING +* Unable to start the search, because the basis matrix correspodning +* to the initial basis is exactly singular. +* +* GLP_EBOUND +* Unable to start the search, because some double-bounded variables +* have incorrect bounds. +* +* GLP_EFAIL +* The problem has no rows/columns. +* +* GLP_EITLIM +* The search was prematurely terminated, because the simplex +* iteration limit has been exceeded. +* +* GLP_ETMLIM +* The search was prematurely terminated, because the time limit has +* been exceeded. */ + +static void set_d_eps(mpq_t x, double val) +{ /* convert double val to rational x obtaining a more adequate + fraction than provided by mpq_set_d due to allowing a small + approximation error specified by a given relative tolerance; + for example, mpq_set_d would give the following + 1/3 ~= 0.333333333333333314829616256247391... -> + -> 6004799503160661/18014398509481984 + while this routine gives exactly 1/3 */ + int s, n, j; + double f, p, q, eps = 1e-9; + mpq_t temp; + xassert(-DBL_MAX <= val && val <= +DBL_MAX); +#if 1 /* 30/VII-2008 */ + if (val == floor(val)) + { /* if val is integral, do not approximate */ + mpq_set_d(x, val); + goto done; + } +#endif + if (val > 0.0) + s = +1; + else if (val < 0.0) + s = -1; + else + { mpq_set_si(x, 0, 1); + goto done; + } + f = frexp(fabs(val), &n); + /* |val| = f * 2^n, where 0.5 <= f < 1.0 */ + fp2rat(f, 0.1 * eps, &p, &q); + /* f ~= p / q, where p and q are integers */ + mpq_init(temp); + mpq_set_d(x, p); + mpq_set_d(temp, q); + mpq_div(x, x, temp); + mpq_set_si(temp, 1, 1); + for (j = 1; j <= abs(n); j++) + mpq_add(temp, temp, temp); + if (n > 0) + mpq_mul(x, x, temp); + else if (n < 0) + mpq_div(x, x, temp); + mpq_clear(temp); + if (s < 0) mpq_neg(x, x); + /* check that the desired tolerance has been attained */ + xassert(fabs(val - mpq_get_d(x)) <= eps * (1.0 + fabs(val))); +done: return; +} + +static void load_data(SSX *ssx, LPX *lp) +{ /* load LP problem data into simplex solver workspace */ + int m = ssx->m; + int n = ssx->n; + int nnz = ssx->A_ptr[n+1]-1; + int j, k, type, loc, len, *ind; + double lb, ub, coef, *val; + xassert(lpx_get_num_rows(lp) == m); + xassert(lpx_get_num_cols(lp) == n); + xassert(lpx_get_num_nz(lp) == nnz); + /* types and bounds of rows and columns */ + for (k = 1; k <= m+n; k++) + { if (k <= m) + { type = lpx_get_row_type(lp, k); + lb = lpx_get_row_lb(lp, k); + ub = lpx_get_row_ub(lp, k); + } + else + { type = lpx_get_col_type(lp, k-m); + lb = lpx_get_col_lb(lp, k-m); + ub = lpx_get_col_ub(lp, k-m); + } + switch (type) + { case LPX_FR: type = SSX_FR; break; + case LPX_LO: type = SSX_LO; break; + case LPX_UP: type = SSX_UP; break; + case LPX_DB: type = SSX_DB; break; + case LPX_FX: type = SSX_FX; break; + default: xassert(type != type); + } + ssx->type[k] = type; + set_d_eps(ssx->lb[k], lb); + set_d_eps(ssx->ub[k], ub); + } + /* optimization direction */ + switch (lpx_get_obj_dir(lp)) + { case LPX_MIN: ssx->dir = SSX_MIN; break; + case LPX_MAX: ssx->dir = SSX_MAX; break; + default: xassert(lp != lp); + } + /* objective coefficients */ + for (k = 0; k <= m+n; k++) + { if (k == 0) + coef = lpx_get_obj_coef(lp, 0); + else if (k <= m) + coef = 0.0; + else + coef = lpx_get_obj_coef(lp, k-m); + set_d_eps(ssx->coef[k], coef); + } + /* constraint coefficients */ + ind = xcalloc(1+m, sizeof(int)); + val = xcalloc(1+m, sizeof(double)); + loc = 0; + for (j = 1; j <= n; j++) + { ssx->A_ptr[j] = loc+1; + len = lpx_get_mat_col(lp, j, ind, val); + for (k = 1; k <= len; k++) + { loc++; + ssx->A_ind[loc] = ind[k]; + set_d_eps(ssx->A_val[loc], val[k]); + } + } + xassert(loc == nnz); + xfree(ind); + xfree(val); + return; +} + +static int load_basis(SSX *ssx, LPX *lp) +{ /* load current LP basis into simplex solver workspace */ + int m = ssx->m; + int n = ssx->n; + int *type = ssx->type; + int *stat = ssx->stat; + int *Q_row = ssx->Q_row; + int *Q_col = ssx->Q_col; + int i, j, k; + xassert(lpx_get_num_rows(lp) == m); + xassert(lpx_get_num_cols(lp) == n); + /* statuses of rows and columns */ + for (k = 1; k <= m+n; k++) + { if (k <= m) + stat[k] = lpx_get_row_stat(lp, k); + else + stat[k] = lpx_get_col_stat(lp, k-m); + switch (stat[k]) + { case LPX_BS: + stat[k] = SSX_BS; + break; + case LPX_NL: + stat[k] = SSX_NL; + xassert(type[k] == SSX_LO || type[k] == SSX_DB); + break; + case LPX_NU: + stat[k] = SSX_NU; + xassert(type[k] == SSX_UP || type[k] == SSX_DB); + break; + case LPX_NF: + stat[k] = SSX_NF; + xassert(type[k] == SSX_FR); + break; + case LPX_NS: + stat[k] = SSX_NS; + xassert(type[k] == SSX_FX); + break; + default: + xassert(stat != stat); + } + } + /* build permutation matix Q */ + i = j = 0; + for (k = 1; k <= m+n; k++) + { if (stat[k] == SSX_BS) + { i++; + if (i > m) return 1; + Q_row[k] = i, Q_col[i] = k; + } + else + { j++; + if (j > n) return 1; + Q_row[k] = m+j, Q_col[m+j] = k; + } + } + xassert(i == m && j == n); + return 0; +} + +int glp_exact(glp_prob *lp, const glp_smcp *parm) +{ glp_smcp _parm; + SSX *ssx; + int m = lpx_get_num_rows(lp); + int n = lpx_get_num_cols(lp); + int nnz = lpx_get_num_nz(lp); + int i, j, k, type, pst, dst, ret, *stat; + double lb, ub, *prim, *dual, sum; + if (parm == NULL) + parm = &_parm, glp_init_smcp((glp_smcp *)parm); + /* check control parameters */ + if (parm->it_lim < 0) + xerror("glp_exact: it_lim = %d; invalid parameter\n", + parm->it_lim); + if (parm->tm_lim < 0) + xerror("glp_exact: tm_lim = %d; invalid parameter\n", + parm->tm_lim); + /* the problem must have at least one row and one column */ + if (!(m > 0 && n > 0)) + { xprintf("glp_exact: problem has no rows/columns\n"); + return GLP_EFAIL; + } +#if 1 + /* basic solution is currently undefined */ + lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; + lp->obj_val = 0.0; + lp->some = 0; +#endif + /* check that all double-bounded variables have correct bounds */ + for (k = 1; k <= m+n; k++) + { if (k <= m) + { type = lpx_get_row_type(lp, k); + lb = lpx_get_row_lb(lp, k); + ub = lpx_get_row_ub(lp, k); + } + else + { type = lpx_get_col_type(lp, k-m); + lb = lpx_get_col_lb(lp, k-m); + ub = lpx_get_col_ub(lp, k-m); + } + if (type == LPX_DB && lb >= ub) + { xprintf("glp_exact: %s %d has invalid bounds\n", + k <= m ? "row" : "column", k <= m ? k : k-m); + return GLP_EBOUND; + } + } + /* create the simplex solver workspace */ + xprintf("glp_exact: %d rows, %d columns, %d non-zeros\n", + m, n, nnz); +#ifdef HAVE_GMP + xprintf("GNU MP bignum library is being used\n"); +#else + xprintf("GLPK bignum module is being used\n"); + xprintf("(Consider installing GNU MP to attain a much better perf" + "ormance.)\n"); +#endif + ssx = ssx_create(m, n, nnz); + /* load LP problem data into the workspace */ + load_data(ssx, lp); + /* load current LP basis into the workspace */ + if (load_basis(ssx, lp)) + { xprintf("glp_exact: initial LP basis is invalid\n"); + ret = GLP_EBADB; + goto done; + } + /* inherit some control parameters from the LP object */ +#if 0 + ssx->it_lim = lpx_get_int_parm(lp, LPX_K_ITLIM); + ssx->it_cnt = lpx_get_int_parm(lp, LPX_K_ITCNT); + ssx->tm_lim = lpx_get_real_parm(lp, LPX_K_TMLIM); +#else + ssx->it_lim = parm->it_lim; + ssx->it_cnt = lp->it_cnt; + ssx->tm_lim = (double)parm->tm_lim / 1000.0; +#endif + ssx->out_frq = 5.0; + ssx->tm_beg = xtime(); + ssx->tm_lag = xlset(0); + /* solve LP */ + ret = ssx_driver(ssx); + /* copy back some statistics to the LP object */ +#if 0 + lpx_set_int_parm(lp, LPX_K_ITLIM, ssx->it_lim); + lpx_set_int_parm(lp, LPX_K_ITCNT, ssx->it_cnt); + lpx_set_real_parm(lp, LPX_K_TMLIM, ssx->tm_lim); +#else + lp->it_cnt = ssx->it_cnt; +#endif + /* analyze the return code */ + switch (ret) + { case 0: + /* optimal solution found */ + ret = 0; + pst = LPX_P_FEAS, dst = LPX_D_FEAS; + break; + case 1: + /* problem has no feasible solution */ + ret = 0; + pst = LPX_P_NOFEAS, dst = LPX_D_INFEAS; + break; + case 2: + /* problem has unbounded solution */ + ret = 0; + pst = LPX_P_FEAS, dst = LPX_D_NOFEAS; +#if 1 + xassert(1 <= ssx->q && ssx->q <= n); + lp->some = ssx->Q_col[m + ssx->q]; + xassert(1 <= lp->some && lp->some <= m+n); +#endif + break; + case 3: + /* iteration limit exceeded (phase I) */ + ret = GLP_EITLIM; + pst = LPX_P_INFEAS, dst = LPX_D_INFEAS; + break; + case 4: + /* iteration limit exceeded (phase II) */ + ret = GLP_EITLIM; + pst = LPX_P_FEAS, dst = LPX_D_INFEAS; + break; + case 5: + /* time limit exceeded (phase I) */ + ret = GLP_ETMLIM; + pst = LPX_P_INFEAS, dst = LPX_D_INFEAS; + break; + case 6: + /* time limit exceeded (phase II) */ + ret = GLP_ETMLIM; + pst = LPX_P_FEAS, dst = LPX_D_INFEAS; + break; + case 7: + /* initial basis matrix is singular */ + ret = GLP_ESING; + goto done; + default: + xassert(ret != ret); + } + /* obtain final basic solution components */ + stat = xcalloc(1+m+n, sizeof(int)); + prim = xcalloc(1+m+n, sizeof(double)); + dual = xcalloc(1+m+n, sizeof(double)); + for (k = 1; k <= m+n; k++) + { if (ssx->stat[k] == SSX_BS) + { i = ssx->Q_row[k]; /* x[k] = xB[i] */ + xassert(1 <= i && i <= m); + stat[k] = LPX_BS; + prim[k] = mpq_get_d(ssx->bbar[i]); + dual[k] = 0.0; + } + else + { j = ssx->Q_row[k] - m; /* x[k] = xN[j] */ + xassert(1 <= j && j <= n); + switch (ssx->stat[k]) + { case SSX_NF: + stat[k] = LPX_NF; + prim[k] = 0.0; + break; + case SSX_NL: + stat[k] = LPX_NL; + prim[k] = mpq_get_d(ssx->lb[k]); + break; + case SSX_NU: + stat[k] = LPX_NU; + prim[k] = mpq_get_d(ssx->ub[k]); + break; + case SSX_NS: + stat[k] = LPX_NS; + prim[k] = mpq_get_d(ssx->lb[k]); + break; + default: + xassert(ssx != ssx); + } + dual[k] = mpq_get_d(ssx->cbar[j]); + } + } + /* and store them into the LP object */ + pst = pst - LPX_P_UNDEF + GLP_UNDEF; + dst = dst - LPX_D_UNDEF + GLP_UNDEF; + for (k = 1; k <= m+n; k++) + stat[k] = stat[k] - LPX_BS + GLP_BS; + sum = lpx_get_obj_coef(lp, 0); + for (j = 1; j <= n; j++) + sum += lpx_get_obj_coef(lp, j) * prim[m+j]; + lpx_put_solution(lp, 1, &pst, &dst, &sum, + &stat[0], &prim[0], &dual[0], &stat[m], &prim[m], &dual[m]); + xfree(stat); + xfree(prim); + xfree(dual); +done: /* delete the simplex solver workspace */ + ssx_delete(ssx); + /* return to the application program */ + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi08.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi08.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,389 @@ +/* glpapi08.c (interior-point method routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" +#include "glpipm.h" +#include "glpnpp.h" + +/*********************************************************************** +* NAME +* +* glp_interior - solve LP problem with the interior-point method +* +* SYNOPSIS +* +* int glp_interior(glp_prob *P, const glp_iptcp *parm); +* +* The routine glp_interior is a driver to the LP solver based on the +* interior-point method. +* +* The interior-point solver has a set of control parameters. Values of +* the control parameters can be passed in a structure glp_iptcp, which +* the parameter parm points to. +* +* Currently this routine implements an easy variant of the primal-dual +* interior-point method based on Mehrotra's technique. +* +* This routine transforms the original LP problem to an equivalent LP +* problem in the standard formulation (all constraints are equalities, +* all variables are non-negative), calls the routine ipm_main to solve +* the transformed problem, and then transforms an obtained solution to +* the solution of the original problem. +* +* RETURNS +* +* 0 The LP problem instance has been successfully solved. This code +* does not necessarily mean that the solver has found optimal +* solution. It only means that the solution process was successful. +* +* GLP_EFAIL +* The problem has no rows/columns. +* +* GLP_ENOCVG +* Very slow convergence or divergence. +* +* GLP_EITLIM +* Iteration limit exceeded. +* +* GLP_EINSTAB +* Numerical instability on solving Newtonian system. */ + +static void transform(NPP *npp) +{ /* transform LP to the standard formulation */ + NPPROW *row, *prev_row; + NPPCOL *col, *prev_col; + for (row = npp->r_tail; row != NULL; row = prev_row) + { prev_row = row->prev; + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) + npp_free_row(npp, row); + else if (row->lb == -DBL_MAX) + npp_leq_row(npp, row); + else if (row->ub == +DBL_MAX) + npp_geq_row(npp, row); + else if (row->lb != row->ub) + { if (fabs(row->lb) < fabs(row->ub)) + npp_geq_row(npp, row); + else + npp_leq_row(npp, row); + } + } + for (col = npp->c_tail; col != NULL; col = prev_col) + { prev_col = col->prev; + if (col->lb == -DBL_MAX && col->ub == +DBL_MAX) + npp_free_col(npp, col); + else if (col->lb == -DBL_MAX) + npp_ubnd_col(npp, col); + else if (col->ub == +DBL_MAX) + { if (col->lb != 0.0) + npp_lbnd_col(npp, col); + } + else if (col->lb != col->ub) + { if (fabs(col->lb) < fabs(col->ub)) + { if (col->lb != 0.0) + npp_lbnd_col(npp, col); + } + else + npp_ubnd_col(npp, col); + npp_dbnd_col(npp, col); + } + else + npp_fixed_col(npp, col); + } + for (row = npp->r_head; row != NULL; row = row->next) + xassert(row->lb == row->ub); + for (col = npp->c_head; col != NULL; col = col->next) + xassert(col->lb == 0.0 && col->ub == +DBL_MAX); + return; +} + +int glp_interior(glp_prob *P, const glp_iptcp *parm) +{ glp_iptcp _parm; + GLPROW *row; + GLPCOL *col; + NPP *npp = NULL; + glp_prob *prob = NULL; + int i, j, ret; + /* check control parameters */ + if (parm == NULL) + glp_init_iptcp(&_parm), parm = &_parm; + if (!(parm->msg_lev == GLP_MSG_OFF || + parm->msg_lev == GLP_MSG_ERR || + parm->msg_lev == GLP_MSG_ON || + parm->msg_lev == GLP_MSG_ALL)) + xerror("glp_interior: msg_lev = %d; invalid parameter\n", + parm->msg_lev); + if (!(parm->ord_alg == GLP_ORD_NONE || + parm->ord_alg == GLP_ORD_QMD || + parm->ord_alg == GLP_ORD_AMD || + parm->ord_alg == GLP_ORD_SYMAMD)) + xerror("glp_interior: ord_alg = %d; invalid parameter\n", + parm->ord_alg); + /* interior-point solution is currently undefined */ + P->ipt_stat = GLP_UNDEF; + P->ipt_obj = 0.0; + /* check bounds of double-bounded variables */ + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + if (row->type == GLP_DB && row->lb >= row->ub) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_interior: row %d: lb = %g, ub = %g; incorre" + "ct bounds\n", i, row->lb, row->ub); + ret = GLP_EBOUND; + goto done; + } + } + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->type == GLP_DB && col->lb >= col->ub) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_interior: column %d: lb = %g, ub = %g; inco" + "rrect bounds\n", j, col->lb, col->ub); + ret = GLP_EBOUND; + goto done; + } + } + /* transform LP to the standard formulation */ + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("Original LP has %d row(s), %d column(s), and %d non-z" + "ero(s)\n", P->m, P->n, P->nnz); + npp = npp_create_wksp(); + npp_load_prob(npp, P, GLP_OFF, GLP_IPT, GLP_ON); + transform(npp); + prob = glp_create_prob(); + npp_build_prob(npp, prob); + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("Working LP has %d row(s), %d column(s), and %d non-ze" + "ro(s)\n", prob->m, prob->n, prob->nnz); +#if 1 + /* currently empty problem cannot be solved */ + if (!(prob->m > 0 && prob->n > 0)) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_interior: unable to solve empty problem\n"); + ret = GLP_EFAIL; + goto done; + } +#endif + /* scale the resultant LP */ + { ENV *env = get_env_ptr(); + int term_out = env->term_out; + env->term_out = GLP_OFF; + glp_scale_prob(prob, GLP_SF_EQ); + env->term_out = term_out; + } + /* warn about dense columns */ + if (parm->msg_lev >= GLP_MSG_ON && prob->m >= 200) + { int len, cnt = 0; + for (j = 1; j <= prob->n; j++) + { len = glp_get_mat_col(prob, j, NULL, NULL); + if ((double)len >= 0.20 * (double)prob->m) cnt++; + } + if (cnt == 1) + xprintf("WARNING: PROBLEM HAS ONE DENSE COLUMN\n"); + else if (cnt > 0) + xprintf("WARNING: PROBLEM HAS %d DENSE COLUMNS\n", cnt); + } + /* solve the transformed LP */ + ret = ipm_solve(prob, parm); + /* postprocess solution from the transformed LP */ + npp_postprocess(npp, prob); + /* and store solution to the original LP */ + npp_unload_sol(npp, P); +done: /* free working program objects */ + if (npp != NULL) npp_delete_wksp(npp); + if (prob != NULL) glp_delete_prob(prob); + /* return to the application program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_init_iptcp - initialize interior-point solver control parameters +* +* SYNOPSIS +* +* void glp_init_iptcp(glp_iptcp *parm); +* +* DESCRIPTION +* +* The routine glp_init_iptcp initializes control parameters, which are +* used by the interior-point solver, with default values. +* +* Default values of the control parameters are stored in the glp_iptcp +* structure, which the parameter parm points to. */ + +void glp_init_iptcp(glp_iptcp *parm) +{ parm->msg_lev = GLP_MSG_ALL; + parm->ord_alg = GLP_ORD_AMD; + return; +} + +/*********************************************************************** +* NAME +* +* glp_ipt_status - retrieve status of interior-point solution +* +* SYNOPSIS +* +* int glp_ipt_status(glp_prob *lp); +* +* RETURNS +* +* The routine glp_ipt_status reports the status of solution found by +* the interior-point solver as follows: +* +* GLP_UNDEF - interior-point solution is undefined; +* GLP_OPT - interior-point solution is optimal; +* GLP_INFEAS - interior-point solution is infeasible; +* GLP_NOFEAS - no feasible solution exists. */ + +int glp_ipt_status(glp_prob *lp) +{ int ipt_stat = lp->ipt_stat; + return ipt_stat; +} + +/*********************************************************************** +* NAME +* +* glp_ipt_obj_val - retrieve objective value (interior point) +* +* SYNOPSIS +* +* double glp_ipt_obj_val(glp_prob *lp); +* +* RETURNS +* +* The routine glp_ipt_obj_val returns value of the objective function +* for interior-point solution. */ + +double glp_ipt_obj_val(glp_prob *lp) +{ /*struct LPXCPS *cps = lp->cps;*/ + double z; + z = lp->ipt_obj; + /*if (cps->round && fabs(z) < 1e-9) z = 0.0;*/ + return z; +} + +/*********************************************************************** +* NAME +* +* glp_ipt_row_prim - retrieve row primal value (interior point) +* +* SYNOPSIS +* +* double glp_ipt_row_prim(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_ipt_row_prim returns primal value of the auxiliary +* variable associated with i-th row. */ + +double glp_ipt_row_prim(glp_prob *lp, int i) +{ /*struct LPXCPS *cps = lp->cps;*/ + double pval; + if (!(1 <= i && i <= lp->m)) + xerror("glp_ipt_row_prim: i = %d; row number out of range\n", + i); + pval = lp->row[i]->pval; + /*if (cps->round && fabs(pval) < 1e-9) pval = 0.0;*/ + return pval; +} + +/*********************************************************************** +* NAME +* +* glp_ipt_row_dual - retrieve row dual value (interior point) +* +* SYNOPSIS +* +* double glp_ipt_row_dual(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_ipt_row_dual returns dual value (i.e. reduced cost) +* of the auxiliary variable associated with i-th row. */ + +double glp_ipt_row_dual(glp_prob *lp, int i) +{ /*struct LPXCPS *cps = lp->cps;*/ + double dval; + if (!(1 <= i && i <= lp->m)) + xerror("glp_ipt_row_dual: i = %d; row number out of range\n", + i); + dval = lp->row[i]->dval; + /*if (cps->round && fabs(dval) < 1e-9) dval = 0.0;*/ + return dval; +} + +/*********************************************************************** +* NAME +* +* glp_ipt_col_prim - retrieve column primal value (interior point) +* +* SYNOPSIS +* +* double glp_ipt_col_prim(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_ipt_col_prim returns primal value of the structural +* variable associated with j-th column. */ + +double glp_ipt_col_prim(glp_prob *lp, int j) +{ /*struct LPXCPS *cps = lp->cps;*/ + double pval; + if (!(1 <= j && j <= lp->n)) + xerror("glp_ipt_col_prim: j = %d; column number out of range\n" + , j); + pval = lp->col[j]->pval; + /*if (cps->round && fabs(pval) < 1e-9) pval = 0.0;*/ + return pval; +} + +/*********************************************************************** +* NAME +* +* glp_ipt_col_dual - retrieve column dual value (interior point) +* +* SYNOPSIS +* +* #include "glplpx.h" +* double glp_ipt_col_dual(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_ipt_col_dual returns dual value (i.e. reduced cost) +* of the structural variable associated with j-th column. */ + +double glp_ipt_col_dual(glp_prob *lp, int j) +{ /*struct LPXCPS *cps = lp->cps;*/ + double dval; + if (!(1 <= j && j <= lp->n)) + xerror("glp_ipt_col_dual: j = %d; column number out of range\n" + , j); + dval = lp->col[j]->dval; + /*if (cps->round && fabs(dval) < 1e-9) dval = 0.0;*/ + return dval; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi09.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi09.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,741 @@ +/* glpapi09.c (mixed integer programming routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" +#include "glpnpp.h" + +/*********************************************************************** +* NAME +* +* glp_set_col_kind - set (change) column kind +* +* SYNOPSIS +* +* void glp_set_col_kind(glp_prob *mip, int j, int kind); +* +* DESCRIPTION +* +* The routine glp_set_col_kind sets (changes) the kind of j-th column +* (structural variable) as specified by the parameter kind: +* +* GLP_CV - continuous variable; +* GLP_IV - integer variable; +* GLP_BV - binary variable. */ + +void glp_set_col_kind(glp_prob *mip, int j, int kind) +{ GLPCOL *col; + if (!(1 <= j && j <= mip->n)) + xerror("glp_set_col_kind: j = %d; column number out of range\n" + , j); + col = mip->col[j]; + switch (kind) + { case GLP_CV: + col->kind = GLP_CV; + break; + case GLP_IV: + col->kind = GLP_IV; + break; + case GLP_BV: + col->kind = GLP_IV; + if (!(col->type == GLP_DB && col->lb == 0.0 && col->ub == + 1.0)) glp_set_col_bnds(mip, j, GLP_DB, 0.0, 1.0); + break; + default: + xerror("glp_set_col_kind: j = %d; kind = %d; invalid column" + " kind\n", j, kind); + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_kind - retrieve column kind +* +* SYNOPSIS +* +* int glp_get_col_kind(glp_prob *mip, int j); +* +* RETURNS +* +* The routine glp_get_col_kind returns the kind of j-th column, i.e. +* the kind of corresponding structural variable, as follows: +* +* GLP_CV - continuous variable; +* GLP_IV - integer variable; +* GLP_BV - binary variable */ + +int glp_get_col_kind(glp_prob *mip, int j) +{ GLPCOL *col; + int kind; + if (!(1 <= j && j <= mip->n)) + xerror("glp_get_col_kind: j = %d; column number out of range\n" + , j); + col = mip->col[j]; + kind = col->kind; + switch (kind) + { case GLP_CV: + break; + case GLP_IV: + if (col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0) + kind = GLP_BV; + break; + default: + xassert(kind != kind); + } + return kind; +} + +/*********************************************************************** +* NAME +* +* glp_get_num_int - retrieve number of integer columns +* +* SYNOPSIS +* +* int glp_get_num_int(glp_prob *mip); +* +* RETURNS +* +* The routine glp_get_num_int returns the current number of columns, +* which are marked as integer. */ + +int glp_get_num_int(glp_prob *mip) +{ GLPCOL *col; + int j, count = 0; + for (j = 1; j <= mip->n; j++) + { col = mip->col[j]; + if (col->kind == GLP_IV) count++; + } + return count; +} + +/*********************************************************************** +* NAME +* +* glp_get_num_bin - retrieve number of binary columns +* +* SYNOPSIS +* +* int glp_get_num_bin(glp_prob *mip); +* +* RETURNS +* +* The routine glp_get_num_bin returns the current number of columns, +* which are marked as binary. */ + +int glp_get_num_bin(glp_prob *mip) +{ GLPCOL *col; + int j, count = 0; + for (j = 1; j <= mip->n; j++) + { col = mip->col[j]; + if (col->kind == GLP_IV && col->type == GLP_DB && col->lb == + 0.0 && col->ub == 1.0) count++; + } + return count; +} + +/*********************************************************************** +* NAME +* +* glp_intopt - solve MIP problem with the branch-and-bound method +* +* SYNOPSIS +* +* int glp_intopt(glp_prob *P, const glp_iocp *parm); +* +* DESCRIPTION +* +* The routine glp_intopt is a driver to the MIP solver based on the +* branch-and-bound method. +* +* On entry the problem object should contain optimal solution to LP +* relaxation (which can be obtained with the routine glp_simplex). +* +* The MIP solver has a set of control parameters. Values of the control +* parameters can be passed in a structure glp_iocp, which the parameter +* parm points to. +* +* The parameter parm can be specified as NULL, in which case the MIP +* solver uses default settings. +* +* RETURNS +* +* 0 The MIP problem instance has been successfully solved. This code +* does not necessarily mean that the solver has found optimal +* solution. It only means that the solution process was successful. +* +* GLP_EBOUND +* Unable to start the search, because some double-bounded variables +* have incorrect bounds or some integer variables have non-integer +* (fractional) bounds. +* +* GLP_EROOT +* Unable to start the search, because optimal basis for initial LP +* relaxation is not provided. +* +* GLP_EFAIL +* The search was prematurely terminated due to the solver failure. +* +* GLP_EMIPGAP +* The search was prematurely terminated, because the relative mip +* gap tolerance has been reached. +* +* GLP_ETMLIM +* The search was prematurely terminated, because the time limit has +* been exceeded. +* +* GLP_ENOPFS +* The MIP problem instance has no primal feasible solution (only if +* the MIP presolver is used). +* +* GLP_ENODFS +* LP relaxation of the MIP problem instance has no dual feasible +* solution (only if the MIP presolver is used). +* +* GLP_ESTOP +* The search was prematurely terminated by application. */ + +static int solve_mip(glp_prob *P, const glp_iocp *parm) +{ /* solve MIP directly without using the preprocessor */ + glp_tree *T; + int ret; + /* optimal basis to LP relaxation must be provided */ + if (glp_get_status(P) != GLP_OPT) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_intopt: optimal basis to initial LP relaxation" + " not provided\n"); + ret = GLP_EROOT; + goto done; + } + /* it seems all is ok */ + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("Integer optimization begins...\n"); + /* create the branch-and-bound tree */ + T = ios_create_tree(P, parm); + /* solve the problem instance */ + ret = ios_driver(T); + /* delete the branch-and-bound tree */ + ios_delete_tree(T); + /* analyze exit code reported by the mip driver */ + if (ret == 0) + { if (P->mip_stat == GLP_FEAS) + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("INTEGER OPTIMAL SOLUTION FOUND\n"); + P->mip_stat = GLP_OPT; + } + else + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS NO INTEGER FEASIBLE SOLUTION\n"); + P->mip_stat = GLP_NOFEAS; + } + } + else if (ret == GLP_EMIPGAP) + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("RELATIVE MIP GAP TOLERANCE REACHED; SEARCH TERMINA" + "TED\n"); + } + else if (ret == GLP_ETMLIM) + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); + } + else if (ret == GLP_EFAIL) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_intopt: cannot solve current LP relaxation\n"); + } + else if (ret == GLP_ESTOP) + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("SEARCH TERMINATED BY APPLICATION\n"); + } + else + xassert(ret != ret); +done: return ret; +} + +static int preprocess_and_solve_mip(glp_prob *P, const glp_iocp *parm) +{ /* solve MIP using the preprocessor */ + ENV *env = get_env_ptr(); + int term_out = env->term_out; + NPP *npp; + glp_prob *mip = NULL; + glp_bfcp bfcp; + glp_smcp smcp; + int ret; + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("Preprocessing...\n"); + /* create preprocessor workspace */ + npp = npp_create_wksp(); + /* load original problem into the preprocessor workspace */ + npp_load_prob(npp, P, GLP_OFF, GLP_MIP, GLP_OFF); + /* process MIP prior to applying the branch-and-bound method */ + if (!term_out || parm->msg_lev < GLP_MSG_ALL) + env->term_out = GLP_OFF; + else + env->term_out = GLP_ON; + ret = npp_integer(npp, parm); + env->term_out = term_out; + if (ret == 0) + ; + else if (ret == GLP_ENOPFS) + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION\n"); + } + else if (ret == GLP_ENODFS) + { if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("LP RELAXATION HAS NO DUAL FEASIBLE SOLUTION\n"); + } + else + xassert(ret != ret); + if (ret != 0) goto done; + /* build transformed MIP */ + mip = glp_create_prob(); + npp_build_prob(npp, mip); + /* if the transformed MIP is empty, it has empty solution, which + is optimal */ + if (mip->m == 0 && mip->n == 0) + { mip->mip_stat = GLP_OPT; + mip->mip_obj = mip->c0; + if (parm->msg_lev >= GLP_MSG_ALL) + { xprintf("Objective value = %17.9e\n", mip->mip_obj); + xprintf("INTEGER OPTIMAL SOLUTION FOUND BY MIP PREPROCESSOR" + "\n"); + } + goto post; + } + /* display some statistics */ + if (parm->msg_lev >= GLP_MSG_ALL) + { int ni = glp_get_num_int(mip); + int nb = glp_get_num_bin(mip); + char s[50]; + xprintf("%d row%s, %d column%s, %d non-zero%s\n", + mip->m, mip->m == 1 ? "" : "s", mip->n, mip->n == 1 ? "" : + "s", mip->nnz, mip->nnz == 1 ? "" : "s"); + if (nb == 0) + strcpy(s, "none of"); + else if (ni == 1 && nb == 1) + strcpy(s, ""); + else if (nb == 1) + strcpy(s, "one of"); + else if (nb == ni) + strcpy(s, "all of"); + else + sprintf(s, "%d of", nb); + xprintf("%d integer variable%s, %s which %s binary\n", + ni, ni == 1 ? "" : "s", s, nb == 1 ? "is" : "are"); + } + /* inherit basis factorization control parameters */ + glp_get_bfcp(P, &bfcp); + glp_set_bfcp(mip, &bfcp); + /* scale the transformed problem */ + if (!term_out || parm->msg_lev < GLP_MSG_ALL) + env->term_out = GLP_OFF; + else + env->term_out = GLP_ON; + glp_scale_prob(mip, + GLP_SF_GM | GLP_SF_EQ | GLP_SF_2N | GLP_SF_SKIP); + env->term_out = term_out; + /* build advanced initial basis */ + if (!term_out || parm->msg_lev < GLP_MSG_ALL) + env->term_out = GLP_OFF; + else + env->term_out = GLP_ON; + glp_adv_basis(mip, 0); + env->term_out = term_out; + /* solve initial LP relaxation */ + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("Solving LP relaxation...\n"); + glp_init_smcp(&smcp); + smcp.msg_lev = parm->msg_lev; + mip->it_cnt = P->it_cnt; + ret = glp_simplex(mip, &smcp); + P->it_cnt = mip->it_cnt; + if (ret != 0) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_intopt: cannot solve LP relaxation\n"); + ret = GLP_EFAIL; + goto done; + } + /* check status of the basic solution */ + ret = glp_get_status(mip); + if (ret == GLP_OPT) + ret = 0; + else if (ret == GLP_NOFEAS) + ret = GLP_ENOPFS; + else if (ret == GLP_UNBND) + ret = GLP_ENODFS; + else + xassert(ret != ret); + if (ret != 0) goto done; + /* solve the transformed MIP */ + mip->it_cnt = P->it_cnt; + ret = solve_mip(mip, parm); + P->it_cnt = mip->it_cnt; + /* only integer feasible solution can be postprocessed */ + if (!(mip->mip_stat == GLP_OPT || mip->mip_stat == GLP_FEAS)) + { P->mip_stat = mip->mip_stat; + goto done; + } + /* postprocess solution from the transformed MIP */ +post: npp_postprocess(npp, mip); + /* the transformed MIP is no longer needed */ + glp_delete_prob(mip), mip = NULL; + /* store solution to the original problem */ + npp_unload_sol(npp, P); +done: /* delete the transformed MIP, if it exists */ + if (mip != NULL) glp_delete_prob(mip); + /* delete preprocessor workspace */ + npp_delete_wksp(npp); + return ret; +} + +#ifndef HAVE_ALIEN_SOLVER /* 28/V-2010 */ +int _glp_intopt1(glp_prob *P, const glp_iocp *parm) +{ xassert(P == P); + xassert(parm == parm); + xprintf("glp_intopt: no alien solver is available\n"); + return GLP_EFAIL; +} +#endif + +int glp_intopt(glp_prob *P, const glp_iocp *parm) +{ /* solve MIP problem with the branch-and-bound method */ + glp_iocp _parm; + int i, j, ret; + /* check problem object */ + if (P == NULL || P->magic != GLP_PROB_MAGIC) + xerror("glp_intopt: P = %p; invalid problem object\n", P); + if (P->tree != NULL) + xerror("glp_intopt: operation not allowed\n"); + /* check control parameters */ + if (parm == NULL) + parm = &_parm, glp_init_iocp((glp_iocp *)parm); + if (!(parm->msg_lev == GLP_MSG_OFF || + parm->msg_lev == GLP_MSG_ERR || + parm->msg_lev == GLP_MSG_ON || + parm->msg_lev == GLP_MSG_ALL || + parm->msg_lev == GLP_MSG_DBG)) + xerror("glp_intopt: msg_lev = %d; invalid parameter\n", + parm->msg_lev); + if (!(parm->br_tech == GLP_BR_FFV || + parm->br_tech == GLP_BR_LFV || + parm->br_tech == GLP_BR_MFV || + parm->br_tech == GLP_BR_DTH || + parm->br_tech == GLP_BR_PCH)) + xerror("glp_intopt: br_tech = %d; invalid parameter\n", + parm->br_tech); + if (!(parm->bt_tech == GLP_BT_DFS || + parm->bt_tech == GLP_BT_BFS || + parm->bt_tech == GLP_BT_BLB || + parm->bt_tech == GLP_BT_BPH)) + xerror("glp_intopt: bt_tech = %d; invalid parameter\n", + parm->bt_tech); + if (!(0.0 < parm->tol_int && parm->tol_int < 1.0)) + xerror("glp_intopt: tol_int = %g; invalid parameter\n", + parm->tol_int); + if (!(0.0 < parm->tol_obj && parm->tol_obj < 1.0)) + xerror("glp_intopt: tol_obj = %g; invalid parameter\n", + parm->tol_obj); + if (parm->tm_lim < 0) + xerror("glp_intopt: tm_lim = %d; invalid parameter\n", + parm->tm_lim); + if (parm->out_frq < 0) + xerror("glp_intopt: out_frq = %d; invalid parameter\n", + parm->out_frq); + if (parm->out_dly < 0) + xerror("glp_intopt: out_dly = %d; invalid parameter\n", + parm->out_dly); + if (!(0 <= parm->cb_size && parm->cb_size <= 256)) + xerror("glp_intopt: cb_size = %d; invalid parameter\n", + parm->cb_size); + if (!(parm->pp_tech == GLP_PP_NONE || + parm->pp_tech == GLP_PP_ROOT || + parm->pp_tech == GLP_PP_ALL)) + xerror("glp_intopt: pp_tech = %d; invalid parameter\n", + parm->pp_tech); + if (parm->mip_gap < 0.0) + xerror("glp_intopt: mip_gap = %g; invalid parameter\n", + parm->mip_gap); + if (!(parm->mir_cuts == GLP_ON || parm->mir_cuts == GLP_OFF)) + xerror("glp_intopt: mir_cuts = %d; invalid parameter\n", + parm->mir_cuts); + if (!(parm->gmi_cuts == GLP_ON || parm->gmi_cuts == GLP_OFF)) + xerror("glp_intopt: gmi_cuts = %d; invalid parameter\n", + parm->gmi_cuts); + if (!(parm->cov_cuts == GLP_ON || parm->cov_cuts == GLP_OFF)) + xerror("glp_intopt: cov_cuts = %d; invalid parameter\n", + parm->cov_cuts); + if (!(parm->clq_cuts == GLP_ON || parm->clq_cuts == GLP_OFF)) + xerror("glp_intopt: clq_cuts = %d; invalid parameter\n", + parm->clq_cuts); + if (!(parm->presolve == GLP_ON || parm->presolve == GLP_OFF)) + xerror("glp_intopt: presolve = %d; invalid parameter\n", + parm->presolve); + if (!(parm->binarize == GLP_ON || parm->binarize == GLP_OFF)) + xerror("glp_intopt: binarize = %d; invalid parameter\n", + parm->binarize); + if (!(parm->fp_heur == GLP_ON || parm->fp_heur == GLP_OFF)) + xerror("glp_intopt: fp_heur = %d; invalid parameter\n", + parm->fp_heur); +#if 1 /* 28/V-2010 */ + if (!(parm->alien == GLP_ON || parm->alien == GLP_OFF)) + xerror("glp_intopt: alien = %d; invalid parameter\n", + parm->alien); +#endif + /* integer solution is currently undefined */ + P->mip_stat = GLP_UNDEF; + P->mip_obj = 0.0; + /* check bounds of double-bounded variables */ + for (i = 1; i <= P->m; i++) + { GLPROW *row = P->row[i]; + if (row->type == GLP_DB && row->lb >= row->ub) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_intopt: row %d: lb = %g, ub = %g; incorrect" + " bounds\n", i, row->lb, row->ub); + ret = GLP_EBOUND; + goto done; + } + } + for (j = 1; j <= P->n; j++) + { GLPCOL *col = P->col[j]; + if (col->type == GLP_DB && col->lb >= col->ub) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_intopt: column %d: lb = %g, ub = %g; incorr" + "ect bounds\n", j, col->lb, col->ub); + ret = GLP_EBOUND; + goto done; + } + } + /* bounds of all integer variables must be integral */ + for (j = 1; j <= P->n; j++) + { GLPCOL *col = P->col[j]; + if (col->kind != GLP_IV) continue; + if (col->type == GLP_LO || col->type == GLP_DB) + { if (col->lb != floor(col->lb)) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_intopt: integer column %d has non-intege" + "r lower bound %g\n", j, col->lb); + ret = GLP_EBOUND; + goto done; + } + } + if (col->type == GLP_UP || col->type == GLP_DB) + { if (col->ub != floor(col->ub)) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_intopt: integer column %d has non-intege" + "r upper bound %g\n", j, col->ub); + ret = GLP_EBOUND; + goto done; + } + } + if (col->type == GLP_FX) + { if (col->lb != floor(col->lb)) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("glp_intopt: integer column %d has non-intege" + "r fixed value %g\n", j, col->lb); + ret = GLP_EBOUND; + goto done; + } + } + } + /* solve MIP problem */ + if (parm->msg_lev >= GLP_MSG_ALL) + { int ni = glp_get_num_int(P); + int nb = glp_get_num_bin(P); + char s[50]; + xprintf("GLPK Integer Optimizer, v%s\n", glp_version()); + xprintf("%d row%s, %d column%s, %d non-zero%s\n", + P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", + P->nnz, P->nnz == 1 ? "" : "s"); + if (nb == 0) + strcpy(s, "none of"); + else if (ni == 1 && nb == 1) + strcpy(s, ""); + else if (nb == 1) + strcpy(s, "one of"); + else if (nb == ni) + strcpy(s, "all of"); + else + sprintf(s, "%d of", nb); + xprintf("%d integer variable%s, %s which %s binary\n", + ni, ni == 1 ? "" : "s", s, nb == 1 ? "is" : "are"); + } +#if 1 /* 28/V-2010 */ + if (parm->alien) + { /* use alien integer optimizer */ + ret = _glp_intopt1(P, parm); + goto done; + } +#endif + if (!parm->presolve) + ret = solve_mip(P, parm); + else + ret = preprocess_and_solve_mip(P, parm); +done: /* return to the application program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_init_iocp - initialize integer optimizer control parameters +* +* SYNOPSIS +* +* void glp_init_iocp(glp_iocp *parm); +* +* DESCRIPTION +* +* The routine glp_init_iocp initializes control parameters, which are +* used by the integer optimizer, with default values. +* +* Default values of the control parameters are stored in a glp_iocp +* structure, which the parameter parm points to. */ + +void glp_init_iocp(glp_iocp *parm) +{ parm->msg_lev = GLP_MSG_ALL; + parm->br_tech = GLP_BR_DTH; + parm->bt_tech = GLP_BT_BLB; + parm->tol_int = 1e-5; + parm->tol_obj = 1e-7; + parm->tm_lim = INT_MAX; + parm->out_frq = 5000; + parm->out_dly = 10000; + parm->cb_func = NULL; + parm->cb_info = NULL; + parm->cb_size = 0; + parm->pp_tech = GLP_PP_ALL; + parm->mip_gap = 0.0; + parm->mir_cuts = GLP_OFF; + parm->gmi_cuts = GLP_OFF; + parm->cov_cuts = GLP_OFF; + parm->clq_cuts = GLP_OFF; + parm->presolve = GLP_OFF; + parm->binarize = GLP_OFF; + parm->fp_heur = GLP_OFF; +#if 1 /* 28/V-2010 */ + parm->alien = GLP_OFF; +#endif + return; +} + +/*********************************************************************** +* NAME +* +* glp_mip_status - retrieve status of MIP solution +* +* SYNOPSIS +* +* int glp_mip_status(glp_prob *mip); +* +* RETURNS +* +* The routine lpx_mip_status reports the status of MIP solution found +* by the branch-and-bound solver as follows: +* +* GLP_UNDEF - MIP solution is undefined; +* GLP_OPT - MIP solution is integer optimal; +* GLP_FEAS - MIP solution is integer feasible but its optimality +* (or non-optimality) has not been proven, perhaps due to +* premature termination of the search; +* GLP_NOFEAS - problem has no integer feasible solution (proven by the +* solver). */ + +int glp_mip_status(glp_prob *mip) +{ int mip_stat = mip->mip_stat; + return mip_stat; +} + +/*********************************************************************** +* NAME +* +* glp_mip_obj_val - retrieve objective value (MIP solution) +* +* SYNOPSIS +* +* double glp_mip_obj_val(glp_prob *mip); +* +* RETURNS +* +* The routine glp_mip_obj_val returns value of the objective function +* for MIP solution. */ + +double glp_mip_obj_val(glp_prob *mip) +{ /*struct LPXCPS *cps = mip->cps;*/ + double z; + z = mip->mip_obj; + /*if (cps->round && fabs(z) < 1e-9) z = 0.0;*/ + return z; +} + +/*********************************************************************** +* NAME +* +* glp_mip_row_val - retrieve row value (MIP solution) +* +* SYNOPSIS +* +* double glp_mip_row_val(glp_prob *mip, int i); +* +* RETURNS +* +* The routine glp_mip_row_val returns value of the auxiliary variable +* associated with i-th row. */ + +double glp_mip_row_val(glp_prob *mip, int i) +{ /*struct LPXCPS *cps = mip->cps;*/ + double mipx; + if (!(1 <= i && i <= mip->m)) + xerror("glp_mip_row_val: i = %d; row number out of range\n", i) + ; + mipx = mip->row[i]->mipx; + /*if (cps->round && fabs(mipx) < 1e-9) mipx = 0.0;*/ + return mipx; +} + +/*********************************************************************** +* NAME +* +* glp_mip_col_val - retrieve column value (MIP solution) +* +* SYNOPSIS +* +* double glp_mip_col_val(glp_prob *mip, int j); +* +* RETURNS +* +* The routine glp_mip_col_val returns value of the structural variable +* associated with j-th column. */ + +double glp_mip_col_val(glp_prob *mip, int j) +{ /*struct LPXCPS *cps = mip->cps;*/ + double mipx; + if (!(1 <= j && j <= mip->n)) + xerror("glp_mip_col_val: j = %d; column number out of range\n", + j); + mipx = mip->col[j]->mipx; + /*if (cps->round && fabs(mipx) < 1e-9) mipx = 0.0;*/ + return mipx; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi10.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi10.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,282 @@ +/* glpapi10.c (solution checking routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +void _glp_check_kkt(glp_prob *P, int sol, int cond, double *_ae_max, + int *_ae_ind, double *_re_max, int *_re_ind) +{ /* check feasibility and optimality conditions */ + int m = P->m; + int n = P->n; + GLPROW *row; + GLPCOL *col; + GLPAIJ *aij; + int i, j, ae_ind, re_ind; + double e, sp, sn, t, ae_max, re_max; + if (!(sol == GLP_SOL || sol == GLP_IPT || sol == GLP_MIP)) + xerror("glp_check_kkt: sol = %d; invalid solution indicator\n", + sol); + if (!(cond == GLP_KKT_PE || cond == GLP_KKT_PB || + cond == GLP_KKT_DE || cond == GLP_KKT_DB || + cond == GLP_KKT_CS)) + xerror("glp_check_kkt: cond = %d; invalid condition indicator " + "\n", cond); + ae_max = re_max = 0.0; + ae_ind = re_ind = 0; + if (cond == GLP_KKT_PE) + { /* xR - A * xS = 0 */ + for (i = 1; i <= m; i++) + { row = P->row[i]; + sp = sn = 0.0; + /* t := xR[i] */ + if (sol == GLP_SOL) + t = row->prim; + else if (sol == GLP_IPT) + t = row->pval; + else if (sol == GLP_MIP) + t = row->mipx; + else + xassert(sol != sol); + if (t >= 0.0) sp += t; else sn -= t; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col = aij->col; + /* t := - a[i,j] * xS[j] */ + if (sol == GLP_SOL) + t = - aij->val * col->prim; + else if (sol == GLP_IPT) + t = - aij->val * col->pval; + else if (sol == GLP_MIP) + t = - aij->val * col->mipx; + else + xassert(sol != sol); + if (t >= 0.0) sp += t; else sn -= t; + } + /* absolute error */ + e = fabs(sp - sn); + if (ae_max < e) + ae_max = e, ae_ind = i; + /* relative error */ + e /= (1.0 + sp + sn); + if (re_max < e) + re_max = e, re_ind = i; + } + } + else if (cond == GLP_KKT_PB) + { /* lR <= xR <= uR */ + for (i = 1; i <= m; i++) + { row = P->row[i]; + /* t := xR[i] */ + if (sol == GLP_SOL) + t = row->prim; + else if (sol == GLP_IPT) + t = row->pval; + else if (sol == GLP_MIP) + t = row->mipx; + else + xassert(sol != sol); + /* check lower bound */ + if (row->type == GLP_LO || row->type == GLP_DB || + row->type == GLP_FX) + { if (t < row->lb) + { /* absolute error */ + e = row->lb - t; + if (ae_max < e) + ae_max = e, ae_ind = i; + /* relative error */ + e /= (1.0 + fabs(row->lb)); + if (re_max < e) + re_max = e, re_ind = i; + } + } + /* check upper bound */ + if (row->type == GLP_UP || row->type == GLP_DB || + row->type == GLP_FX) + { if (t > row->ub) + { /* absolute error */ + e = t - row->ub; + if (ae_max < e) + ae_max = e, ae_ind = i; + /* relative error */ + e /= (1.0 + fabs(row->ub)); + if (re_max < e) + re_max = e, re_ind = i; + } + } + } + /* lS <= xS <= uS */ + for (j = 1; j <= n; j++) + { col = P->col[j]; + /* t := xS[j] */ + if (sol == GLP_SOL) + t = col->prim; + else if (sol == GLP_IPT) + t = col->pval; + else if (sol == GLP_MIP) + t = col->mipx; + else + xassert(sol != sol); + /* check lower bound */ + if (col->type == GLP_LO || col->type == GLP_DB || + col->type == GLP_FX) + { if (t < col->lb) + { /* absolute error */ + e = col->lb - t; + if (ae_max < e) + ae_max = e, ae_ind = m+j; + /* relative error */ + e /= (1.0 + fabs(col->lb)); + if (re_max < e) + re_max = e, re_ind = m+j; + } + } + /* check upper bound */ + if (col->type == GLP_UP || col->type == GLP_DB || + col->type == GLP_FX) + { if (t > col->ub) + { /* absolute error */ + e = t - col->ub; + if (ae_max < e) + ae_max = e, ae_ind = m+j; + /* relative error */ + e /= (1.0 + fabs(col->ub)); + if (re_max < e) + re_max = e, re_ind = m+j; + } + } + } + } + else if (cond == GLP_KKT_DE) + { /* A' * (lambdaR - cR) + (lambdaS - cS) = 0 */ + for (j = 1; j <= n; j++) + { col = P->col[j]; + sp = sn = 0.0; + /* t := lambdaS[j] - cS[j] */ + if (sol == GLP_SOL) + t = col->dual - col->coef; + else if (sol == GLP_IPT) + t = col->dval - col->coef; + else + xassert(sol != sol); + if (t >= 0.0) sp += t; else sn -= t; + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + { row = aij->row; + /* t := a[i,j] * (lambdaR[i] - cR[i]) */ + if (sol == GLP_SOL) + t = aij->val * row->dual; + else if (sol == GLP_IPT) + t = aij->val * row->dval; + else + xassert(sol != sol); + if (t >= 0.0) sp += t; else sn -= t; + } + /* absolute error */ + e = fabs(sp - sn); + if (ae_max < e) + ae_max = e, ae_ind = m+j; + /* relative error */ + e /= (1.0 + sp + sn); + if (re_max < e) + re_max = e, re_ind = m+j; + } + } + else if (cond == GLP_KKT_DB) + { /* check lambdaR */ + for (i = 1; i <= m; i++) + { row = P->row[i]; + /* t := lambdaR[i] */ + if (sol == GLP_SOL) + t = row->dual; + else if (sol == GLP_IPT) + t = row->dval; + else + xassert(sol != sol); + /* correct sign */ + if (P->dir == GLP_MIN) + t = + t; + else if (P->dir == GLP_MAX) + t = - t; + else + xassert(P != P); + /* check for positivity */ + if (row->type == GLP_FR || row->type == GLP_LO) + { if (t < 0.0) + { e = - t; + if (ae_max < e) + ae_max = re_max = e, ae_ind = re_ind = i; + } + } + /* check for negativity */ + if (row->type == GLP_FR || row->type == GLP_UP) + { if (t > 0.0) + { e = + t; + if (ae_max < e) + ae_max = re_max = e, ae_ind = re_ind = i; + } + } + } + /* check lambdaS */ + for (j = 1; j <= n; j++) + { col = P->col[j]; + /* t := lambdaS[j] */ + if (sol == GLP_SOL) + t = col->dual; + else if (sol == GLP_IPT) + t = col->dval; + else + xassert(sol != sol); + /* correct sign */ + if (P->dir == GLP_MIN) + t = + t; + else if (P->dir == GLP_MAX) + t = - t; + else + xassert(P != P); + /* check for positivity */ + if (col->type == GLP_FR || col->type == GLP_LO) + { if (t < 0.0) + { e = - t; + if (ae_max < e) + ae_max = re_max = e, ae_ind = re_ind = m+j; + } + } + /* check for negativity */ + if (col->type == GLP_FR || col->type == GLP_UP) + { if (t > 0.0) + { e = + t; + if (ae_max < e) + ae_max = re_max = e, ae_ind = re_ind = m+j; + } + } + } + } + else + xassert(cond != cond); + if (_ae_max != NULL) *_ae_max = ae_max; + if (_ae_ind != NULL) *_ae_ind = ae_ind; + if (_re_max != NULL) *_re_max = re_max; + if (_re_ind != NULL) *_re_ind = re_ind; + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi11.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi11.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1217 @@ +/* glpapi11.c (utility routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +int glp_print_sol(glp_prob *P, const char *fname) +{ /* write basic solution in printable format */ + XFILE *fp; + GLPROW *row; + GLPCOL *col; + int i, j, t, ae_ind, re_ind, ret; + double ae_max, re_max; + xprintf("Writing basic solution to `%s'...\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xfprintf(fp, "%-12s%s\n", "Problem:", + P->name == NULL ? "" : P->name); + xfprintf(fp, "%-12s%d\n", "Rows:", P->m); + xfprintf(fp, "%-12s%d\n", "Columns:", P->n); + xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz); + t = glp_get_status(P); + xfprintf(fp, "%-12s%s\n", "Status:", + t == GLP_OPT ? "OPTIMAL" : + t == GLP_FEAS ? "FEASIBLE" : + t == GLP_INFEAS ? "INFEASIBLE (INTERMEDIATE)" : + t == GLP_NOFEAS ? "INFEASIBLE (FINAL)" : + t == GLP_UNBND ? "UNBOUNDED" : + t == GLP_UNDEF ? "UNDEFINED" : "???"); + xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:", + P->obj == NULL ? "" : P->obj, + P->obj == NULL ? "" : " = ", P->obj_val, + P->dir == GLP_MIN ? "MINimum" : + P->dir == GLP_MAX ? "MAXimum" : "???"); + xfprintf(fp, "\n"); + xfprintf(fp, " No. Row name St Activity Lower bound " + " Upper bound Marginal\n"); + xfprintf(fp, "------ ------------ -- ------------- ------------- " + "------------- -------------\n"); + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + xfprintf(fp, "%6d ", i); + if (row->name == NULL || strlen(row->name) <= 12) + xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name); + else + xfprintf(fp, "%s\n%20s", row->name, ""); + xfprintf(fp, "%s ", + row->stat == GLP_BS ? "B " : + row->stat == GLP_NL ? "NL" : + row->stat == GLP_NU ? "NU" : + row->stat == GLP_NF ? "NF" : + row->stat == GLP_NS ? "NS" : "??"); + xfprintf(fp, "%13.6g ", + fabs(row->prim) <= 1e-9 ? 0.0 : row->prim); + if (row->type == GLP_LO || row->type == GLP_DB || + row->type == GLP_FX) + xfprintf(fp, "%13.6g ", row->lb); + else + xfprintf(fp, "%13s ", ""); + if (row->type == GLP_UP || row->type == GLP_DB) + xfprintf(fp, "%13.6g ", row->ub); + else + xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : ""); + if (row->stat != GLP_BS) + { if (fabs(row->dual) <= 1e-9) + xfprintf(fp, "%13s", "< eps"); + else + xfprintf(fp, "%13.6g ", row->dual); + } + xfprintf(fp, "\n"); + } + xfprintf(fp, "\n"); + xfprintf(fp, " No. Column name St Activity Lower bound " + " Upper bound Marginal\n"); + xfprintf(fp, "------ ------------ -- ------------- ------------- " + "------------- -------------\n"); + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + xfprintf(fp, "%6d ", j); + if (col->name == NULL || strlen(col->name) <= 12) + xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name); + else + xfprintf(fp, "%s\n%20s", col->name, ""); + xfprintf(fp, "%s ", + col->stat == GLP_BS ? "B " : + col->stat == GLP_NL ? "NL" : + col->stat == GLP_NU ? "NU" : + col->stat == GLP_NF ? "NF" : + col->stat == GLP_NS ? "NS" : "??"); + xfprintf(fp, "%13.6g ", + fabs(col->prim) <= 1e-9 ? 0.0 : col->prim); + if (col->type == GLP_LO || col->type == GLP_DB || + col->type == GLP_FX) + xfprintf(fp, "%13.6g ", col->lb); + else + xfprintf(fp, "%13s ", ""); + if (col->type == GLP_UP || col->type == GLP_DB) + xfprintf(fp, "%13.6g ", col->ub); + else + xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : ""); + if (col->stat != GLP_BS) + { if (fabs(col->dual) <= 1e-9) + xfprintf(fp, "%13s", "< eps"); + else + xfprintf(fp, "%13.6g ", col->dual); + } + xfprintf(fp, "\n"); + } + xfprintf(fp, "\n"); + xfprintf(fp, "Karush-Kuhn-Tucker optimality conditions:\n"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_SOL, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n", + ae_max, ae_ind); + xfprintf(fp, " max.rel.err = %.2e on row %d\n", + re_max, re_ind); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS WRONG"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_SOL, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n", + ae_max, ae_ind <= P->m ? "row" : "column", + ae_ind <= P->m ? ae_ind : ae_ind - P->m); + xfprintf(fp, " max.rel.err = %.2e on %s %d\n", + re_max, re_ind <= P->m ? "row" : "column", + re_ind <= P->m ? re_ind : re_ind - P->m); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS INFEASIBL" + "E"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_SOL, GLP_KKT_DE, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.DE: max.abs.err = %.2e on column %d\n", + ae_max, ae_ind == 0 ? 0 : ae_ind - P->m); + xfprintf(fp, " max.rel.err = %.2e on column %d\n", + re_max, re_ind == 0 ? 0 : re_ind - P->m); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS WRONG"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_SOL, GLP_KKT_DB, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.DB: max.abs.err = %.2e on %s %d\n", + ae_max, ae_ind <= P->m ? "row" : "column", + ae_ind <= P->m ? ae_ind : ae_ind - P->m); + xfprintf(fp, " max.rel.err = %.2e on %s %d\n", + re_max, re_ind <= P->m ? "row" : "column", + re_ind <= P->m ? re_ind : re_ind - P->m); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS INFEASIBLE") + ; + xfprintf(fp, "\n"); + xfprintf(fp, "End of output\n"); + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_read_sol - read basic solution from text file +* +* SYNOPSIS +* +* int glp_read_sol(glp_prob *lp, const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_sol reads basic solution from a text file whose +* name is specified by the parameter fname into the problem object. +* +* For the file format see description of the routine glp_write_sol. +* +* RETURNS +* +* On success the routine returns zero, otherwise non-zero. */ + +int glp_read_sol(glp_prob *lp, const char *fname) +{ glp_data *data; + jmp_buf jump; + int i, j, k, ret = 0; + xprintf("Reading basic solution from `%s'...\n", fname); + data = glp_sdf_open_file(fname); + if (data == NULL) + { ret = 1; + goto done; + } + if (setjmp(jump)) + { ret = 1; + goto done; + } + glp_sdf_set_jump(data, jump); + /* number of rows, number of columns */ + k = glp_sdf_read_int(data); + if (k != lp->m) + glp_sdf_error(data, "wrong number of rows\n"); + k = glp_sdf_read_int(data); + if (k != lp->n) + glp_sdf_error(data, "wrong number of columns\n"); + /* primal status, dual status, objective value */ + k = glp_sdf_read_int(data); + if (!(k == GLP_UNDEF || k == GLP_FEAS || k == GLP_INFEAS || + k == GLP_NOFEAS)) + glp_sdf_error(data, "invalid primal status\n"); + lp->pbs_stat = k; + k = glp_sdf_read_int(data); + if (!(k == GLP_UNDEF || k == GLP_FEAS || k == GLP_INFEAS || + k == GLP_NOFEAS)) + glp_sdf_error(data, "invalid dual status\n"); + lp->dbs_stat = k; + lp->obj_val = glp_sdf_read_num(data); + /* rows (auxiliary variables) */ + for (i = 1; i <= lp->m; i++) + { GLPROW *row = lp->row[i]; + /* status, primal value, dual value */ + k = glp_sdf_read_int(data); + if (!(k == GLP_BS || k == GLP_NL || k == GLP_NU || + k == GLP_NF || k == GLP_NS)) + glp_sdf_error(data, "invalid row status\n"); + glp_set_row_stat(lp, i, k); + row->prim = glp_sdf_read_num(data); + row->dual = glp_sdf_read_num(data); + } + /* columns (structural variables) */ + for (j = 1; j <= lp->n; j++) + { GLPCOL *col = lp->col[j]; + /* status, primal value, dual value */ + k = glp_sdf_read_int(data); + if (!(k == GLP_BS || k == GLP_NL || k == GLP_NU || + k == GLP_NF || k == GLP_NS)) + glp_sdf_error(data, "invalid column status\n"); + glp_set_col_stat(lp, j, k); + col->prim = glp_sdf_read_num(data); + col->dual = glp_sdf_read_num(data); + } + xprintf("%d lines were read\n", glp_sdf_line(data)); +done: if (ret) lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; + if (data != NULL) glp_sdf_close_file(data); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_sol - write basic solution to text file +* +* SYNOPSIS +* +* int glp_write_sol(glp_prob *lp, const char *fname); +* +* DESCRIPTION +* +* The routine glp_write_sol writes the current basic solution to a +* text file whose name is specified by the parameter fname. This file +* can be read back with the routine glp_read_sol. +* +* RETURNS +* +* On success the routine returns zero, otherwise non-zero. +* +* FILE FORMAT +* +* The file created by the routine glp_write_sol is a plain text file, +* which contains the following information: +* +* m n +* p_stat d_stat obj_val +* r_stat[1] r_prim[1] r_dual[1] +* . . . +* r_stat[m] r_prim[m] r_dual[m] +* c_stat[1] c_prim[1] c_dual[1] +* . . . +* c_stat[n] c_prim[n] c_dual[n] +* +* where: +* m is the number of rows (auxiliary variables); +* n is the number of columns (structural variables); +* p_stat is the primal status of the basic solution (GLP_UNDEF = 1, +* GLP_FEAS = 2, GLP_INFEAS = 3, or GLP_NOFEAS = 4); +* d_stat is the dual status of the basic solution (GLP_UNDEF = 1, +* GLP_FEAS = 2, GLP_INFEAS = 3, or GLP_NOFEAS = 4); +* obj_val is the objective value; +* r_stat[i], i = 1,...,m, is the status of i-th row (GLP_BS = 1, +* GLP_NL = 2, GLP_NU = 3, GLP_NF = 4, or GLP_NS = 5); +* r_prim[i], i = 1,...,m, is the primal value of i-th row; +* r_dual[i], i = 1,...,m, is the dual value of i-th row; +* c_stat[j], j = 1,...,n, is the status of j-th column (GLP_BS = 1, +* GLP_NL = 2, GLP_NU = 3, GLP_NF = 4, or GLP_NS = 5); +* c_prim[j], j = 1,...,n, is the primal value of j-th column; +* c_dual[j], j = 1,...,n, is the dual value of j-th column. */ + +int glp_write_sol(glp_prob *lp, const char *fname) +{ XFILE *fp; + int i, j, ret = 0; + xprintf("Writing basic solution to `%s'...\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* number of rows, number of columns */ + xfprintf(fp, "%d %d\n", lp->m, lp->n); + /* primal status, dual status, objective value */ + xfprintf(fp, "%d %d %.*g\n", lp->pbs_stat, lp->dbs_stat, DBL_DIG, + lp->obj_val); + /* rows (auxiliary variables) */ + for (i = 1; i <= lp->m; i++) + { GLPROW *row = lp->row[i]; + /* status, primal value, dual value */ + xfprintf(fp, "%d %.*g %.*g\n", row->stat, DBL_DIG, row->prim, + DBL_DIG, row->dual); + } + /* columns (structural variables) */ + for (j = 1; j <= lp->n; j++) + { GLPCOL *col = lp->col[j]; + /* status, primal value, dual value */ + xfprintf(fp, "%d %.*g %.*g\n", col->stat, DBL_DIG, col->prim, + DBL_DIG, col->dual); + } + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", 2 + lp->m + lp->n); +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/**********************************************************************/ + +static char *format(char buf[13+1], double x) +{ /* format floating-point number in MPS/360-like style */ + if (x == -DBL_MAX) + strcpy(buf, " -Inf"); + else if (x == +DBL_MAX) + strcpy(buf, " +Inf"); + else if (fabs(x) <= 999999.99998) + { sprintf(buf, "%13.5f", x); +#if 1 + if (strcmp(buf, " 0.00000") == 0 || + strcmp(buf, " -0.00000") == 0) + strcpy(buf, " . "); + else if (memcmp(buf, " 0.", 8) == 0) + memcpy(buf, " .", 8); + else if (memcmp(buf, " -0.", 8) == 0) + memcpy(buf, " -.", 8); +#endif + } + else + sprintf(buf, "%13.6g", x); + return buf; +} + +int glp_print_ranges(glp_prob *P, int len, const int list[], + int flags, const char *fname) +{ /* print sensitivity analysis report */ + XFILE *fp = NULL; + GLPROW *row; + GLPCOL *col; + int m, n, pass, k, t, numb, type, stat, var1, var2, count, page, + ret; + double lb, ub, slack, coef, prim, dual, value1, value2, coef1, + coef2, obj1, obj2; + const char *name, *limit; + char buf[13+1]; + /* sanity checks */ + if (P == NULL || P->magic != GLP_PROB_MAGIC) + xerror("glp_print_ranges: P = %p; invalid problem object\n", + P); + m = P->m, n = P->n; + if (len < 0) + xerror("glp_print_ranges: len = %d; invalid list length\n", + len); + if (len > 0) + { if (list == NULL) + xerror("glp_print_ranges: list = %p: invalid parameter\n", + list); + for (t = 1; t <= len; t++) + { k = list[t]; + if (!(1 <= k && k <= m+n)) + xerror("glp_print_ranges: list[%d] = %d; row/column numb" + "er out of range\n", t, k); + } + } + if (flags != 0) + xerror("glp_print_ranges: flags = %d; invalid parameter\n", + flags); + if (fname == NULL) + xerror("glp_print_ranges: fname = %p; invalid parameter\n", + fname); + if (glp_get_status(P) != GLP_OPT) + { xprintf("glp_print_ranges: optimal basic solution required\n"); + ret = 1; + goto done; + } + if (!glp_bf_exists(P)) + { xprintf("glp_print_ranges: basis factorization required\n"); + ret = 2; + goto done; + } + /* start reporting */ + xprintf("Write sensitivity analysis report to `%s'...\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 3; + goto done; + } + page = count = 0; + for (pass = 1; pass <= 2; pass++) + for (t = 1; t <= (len == 0 ? m+n : len); t++) + { if (t == 1) count = 0; + k = (len == 0 ? t : list[t]); + if (pass == 1 && k > m || pass == 2 && k <= m) + continue; + if (count == 0) + { xfprintf(fp, "GLPK %-4s - SENSITIVITY ANALYSIS REPORT%73sPa" + "ge%4d\n", glp_version(), "", ++page); + xfprintf(fp, "\n"); + xfprintf(fp, "%-12s%s\n", "Problem:", + P->name == NULL ? "" : P->name); + xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:", + P->obj == NULL ? "" : P->obj, + P->obj == NULL ? "" : " = ", P->obj_val, + P->dir == GLP_MIN ? "MINimum" : + P->dir == GLP_MAX ? "MAXimum" : "???"); + xfprintf(fp, "\n"); + xfprintf(fp, "%6s %-12s %2s %13s %13s %13s %13s %13s %13s " + "%s\n", "No.", pass == 1 ? "Row name" : "Column name", + "St", "Activity", pass == 1 ? "Slack" : "Obj coef", + "Lower bound", "Activity", "Obj coef", "Obj value at", + "Limiting"); + xfprintf(fp, "%6s %-12s %2s %13s %13s %13s %13s %13s %13s " + "%s\n", "", "", "", "", "Marginal", "Upper bound", + "range", "range", "break point", "variable"); + xfprintf(fp, "------ ------------ -- ------------- --------" + "----- ------------- ------------- ------------- ------" + "------- ------------\n"); + } + if (pass == 1) + { numb = k; + xassert(1 <= numb && numb <= m); + row = P->row[numb]; + name = row->name; + type = row->type; + lb = glp_get_row_lb(P, numb); + ub = glp_get_row_ub(P, numb); + coef = 0.0; + stat = row->stat; + prim = row->prim; + if (type == GLP_FR) + slack = - prim; + else if (type == GLP_LO) + slack = lb - prim; + else if (type == GLP_UP || type == GLP_DB || type == GLP_FX) + slack = ub - prim; + dual = row->dual; + } + else + { numb = k - m; + xassert(1 <= numb && numb <= n); + col = P->col[numb]; + name = col->name; + lb = glp_get_col_lb(P, numb); + ub = glp_get_col_ub(P, numb); + coef = col->coef; + stat = col->stat; + prim = col->prim; + slack = 0.0; + dual = col->dual; + } + if (stat != GLP_BS) + { glp_analyze_bound(P, k, &value1, &var1, &value2, &var2); + if (stat == GLP_NF) + coef1 = coef2 = coef; + else if (stat == GLP_NS) + coef1 = -DBL_MAX, coef2 = +DBL_MAX; + else if (stat == GLP_NL && P->dir == GLP_MIN || + stat == GLP_NU && P->dir == GLP_MAX) + coef1 = coef - dual, coef2 = +DBL_MAX; + else + coef1 = -DBL_MAX, coef2 = coef - dual; + if (value1 == -DBL_MAX) + { if (dual < -1e-9) + obj1 = +DBL_MAX; + else if (dual > +1e-9) + obj1 = -DBL_MAX; + else + obj1 = P->obj_val; + } + else + obj1 = P->obj_val + dual * (value1 - prim); + if (value2 == +DBL_MAX) + { if (dual < -1e-9) + obj2 = -DBL_MAX; + else if (dual > +1e-9) + obj2 = +DBL_MAX; + else + obj2 = P->obj_val; + } + else + obj2 = P->obj_val + dual * (value2 - prim); + } + else + { glp_analyze_coef(P, k, &coef1, &var1, &value1, &coef2, + &var2, &value2); + if (coef1 == -DBL_MAX) + { if (prim < -1e-9) + obj1 = +DBL_MAX; + else if (prim > +1e-9) + obj1 = -DBL_MAX; + else + obj1 = P->obj_val; + } + else + obj1 = P->obj_val + (coef1 - coef) * prim; + if (coef2 == +DBL_MAX) + { if (prim < -1e-9) + obj2 = -DBL_MAX; + else if (prim > +1e-9) + obj2 = +DBL_MAX; + else + obj2 = P->obj_val; + } + else + obj2 = P->obj_val + (coef2 - coef) * prim; + } + /*** first line ***/ + /* row/column number */ + xfprintf(fp, "%6d", numb); + /* row/column name */ + xfprintf(fp, " %-12.12s", name == NULL ? "" : name); + if (name != NULL && strlen(name) > 12) + xfprintf(fp, "%s\n%6s %12s", name+12, "", ""); + /* row/column status */ + xfprintf(fp, " %2s", + stat == GLP_BS ? "BS" : stat == GLP_NL ? "NL" : + stat == GLP_NU ? "NU" : stat == GLP_NF ? "NF" : + stat == GLP_NS ? "NS" : "??"); + /* row/column activity */ + xfprintf(fp, " %s", format(buf, prim)); + /* row slack, column objective coefficient */ + xfprintf(fp, " %s", format(buf, k <= m ? slack : coef)); + /* row/column lower bound */ + xfprintf(fp, " %s", format(buf, lb)); + /* row/column activity range */ + xfprintf(fp, " %s", format(buf, value1)); + /* row/column objective coefficient range */ + xfprintf(fp, " %s", format(buf, coef1)); + /* objective value at break point */ + xfprintf(fp, " %s", format(buf, obj1)); + /* limiting variable name */ + if (var1 != 0) + { if (var1 <= m) + limit = glp_get_row_name(P, var1); + else + limit = glp_get_col_name(P, var1 - m); + if (limit != NULL) + xfprintf(fp, " %s", limit); + } + xfprintf(fp, "\n"); + /*** second line ***/ + xfprintf(fp, "%6s %-12s %2s %13s", "", "", "", ""); + /* row/column reduced cost */ + xfprintf(fp, " %s", format(buf, dual)); + /* row/column upper bound */ + xfprintf(fp, " %s", format(buf, ub)); + /* row/column activity range */ + xfprintf(fp, " %s", format(buf, value2)); + /* row/column objective coefficient range */ + xfprintf(fp, " %s", format(buf, coef2)); + /* objective value at break point */ + xfprintf(fp, " %s", format(buf, obj2)); + /* limiting variable name */ + if (var2 != 0) + { if (var2 <= m) + limit = glp_get_row_name(P, var2); + else + limit = glp_get_col_name(P, var2 - m); + if (limit != NULL) + xfprintf(fp, " %s", limit); + } + xfprintf(fp, "\n"); + xfprintf(fp, "\n"); + /* print 10 items per page */ + count = (count + 1) % 10; + } + xfprintf(fp, "End of report\n"); + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 4; + goto done; + } + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/**********************************************************************/ + +int glp_print_ipt(glp_prob *P, const char *fname) +{ /* write interior-point solution in printable format */ + XFILE *fp; + GLPROW *row; + GLPCOL *col; + int i, j, t, ae_ind, re_ind, ret; + double ae_max, re_max; + xprintf("Writing interior-point solution to `%s'...\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xfprintf(fp, "%-12s%s\n", "Problem:", + P->name == NULL ? "" : P->name); + xfprintf(fp, "%-12s%d\n", "Rows:", P->m); + xfprintf(fp, "%-12s%d\n", "Columns:", P->n); + xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz); + t = glp_ipt_status(P); + xfprintf(fp, "%-12s%s\n", "Status:", + t == GLP_OPT ? "OPTIMAL" : + t == GLP_UNDEF ? "UNDEFINED" : + t == GLP_INFEAS ? "INFEASIBLE (INTERMEDIATE)" : + t == GLP_NOFEAS ? "INFEASIBLE (FINAL)" : "???"); + xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:", + P->obj == NULL ? "" : P->obj, + P->obj == NULL ? "" : " = ", P->ipt_obj, + P->dir == GLP_MIN ? "MINimum" : + P->dir == GLP_MAX ? "MAXimum" : "???"); + xfprintf(fp, "\n"); + xfprintf(fp, " No. Row name Activity Lower bound " + " Upper bound Marginal\n"); + xfprintf(fp, "------ ------------ ------------- ------------- " + "------------- -------------\n"); + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + xfprintf(fp, "%6d ", i); + if (row->name == NULL || strlen(row->name) <= 12) + xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name); + else + xfprintf(fp, "%s\n%20s", row->name, ""); + xfprintf(fp, "%3s", ""); + xfprintf(fp, "%13.6g ", + fabs(row->pval) <= 1e-9 ? 0.0 : row->pval); + if (row->type == GLP_LO || row->type == GLP_DB || + row->type == GLP_FX) + xfprintf(fp, "%13.6g ", row->lb); + else + xfprintf(fp, "%13s ", ""); + if (row->type == GLP_UP || row->type == GLP_DB) + xfprintf(fp, "%13.6g ", row->ub); + else + xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : ""); + if (fabs(row->dval) <= 1e-9) + xfprintf(fp, "%13s", "< eps"); + else + xfprintf(fp, "%13.6g ", row->dval); + xfprintf(fp, "\n"); + } + xfprintf(fp, "\n"); + xfprintf(fp, " No. Column name Activity Lower bound " + " Upper bound Marginal\n"); + xfprintf(fp, "------ ------------ ------------- ------------- " + "------------- -------------\n"); + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + xfprintf(fp, "%6d ", j); + if (col->name == NULL || strlen(col->name) <= 12) + xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name); + else + xfprintf(fp, "%s\n%20s", col->name, ""); + xfprintf(fp, "%3s", ""); + xfprintf(fp, "%13.6g ", + fabs(col->pval) <= 1e-9 ? 0.0 : col->pval); + if (col->type == GLP_LO || col->type == GLP_DB || + col->type == GLP_FX) + xfprintf(fp, "%13.6g ", col->lb); + else + xfprintf(fp, "%13s ", ""); + if (col->type == GLP_UP || col->type == GLP_DB) + xfprintf(fp, "%13.6g ", col->ub); + else + xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : ""); + if (fabs(col->dval) <= 1e-9) + xfprintf(fp, "%13s", "< eps"); + else + xfprintf(fp, "%13.6g ", col->dval); + xfprintf(fp, "\n"); + } + xfprintf(fp, "\n"); + xfprintf(fp, "Karush-Kuhn-Tucker optimality conditions:\n"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_IPT, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n", + ae_max, ae_ind); + xfprintf(fp, " max.rel.err = %.2e on row %d\n", + re_max, re_ind); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS WRONG"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_IPT, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n", + ae_max, ae_ind <= P->m ? "row" : "column", + ae_ind <= P->m ? ae_ind : ae_ind - P->m); + xfprintf(fp, " max.rel.err = %.2e on %s %d\n", + re_max, re_ind <= P->m ? "row" : "column", + re_ind <= P->m ? re_ind : re_ind - P->m); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS INFEASIBL" + "E"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_IPT, GLP_KKT_DE, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.DE: max.abs.err = %.2e on column %d\n", + ae_max, ae_ind == 0 ? 0 : ae_ind - P->m); + xfprintf(fp, " max.rel.err = %.2e on column %d\n", + re_max, re_ind == 0 ? 0 : re_ind - P->m); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS WRONG"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_IPT, GLP_KKT_DB, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.DB: max.abs.err = %.2e on %s %d\n", + ae_max, ae_ind <= P->m ? "row" : "column", + ae_ind <= P->m ? ae_ind : ae_ind - P->m); + xfprintf(fp, " max.rel.err = %.2e on %s %d\n", + re_max, re_ind <= P->m ? "row" : "column", + re_ind <= P->m ? re_ind : re_ind - P->m); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS INFEASIBLE") + ; + xfprintf(fp, "\n"); + xfprintf(fp, "End of output\n"); + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_read_ipt - read interior-point solution from text file +* +* SYNOPSIS +* +* int glp_read_ipt(glp_prob *lp, const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_ipt reads interior-point solution from a text +* file whose name is specified by the parameter fname into the problem +* object. +* +* For the file format see description of the routine glp_write_ipt. +* +* RETURNS +* +* On success the routine returns zero, otherwise non-zero. */ + +int glp_read_ipt(glp_prob *lp, const char *fname) +{ glp_data *data; + jmp_buf jump; + int i, j, k, ret = 0; + xprintf("Reading interior-point solution from `%s'...\n", fname); + data = glp_sdf_open_file(fname); + if (data == NULL) + { ret = 1; + goto done; + } + if (setjmp(jump)) + { ret = 1; + goto done; + } + glp_sdf_set_jump(data, jump); + /* number of rows, number of columns */ + k = glp_sdf_read_int(data); + if (k != lp->m) + glp_sdf_error(data, "wrong number of rows\n"); + k = glp_sdf_read_int(data); + if (k != lp->n) + glp_sdf_error(data, "wrong number of columns\n"); + /* solution status, objective value */ + k = glp_sdf_read_int(data); + if (!(k == GLP_UNDEF || k == GLP_OPT)) + glp_sdf_error(data, "invalid solution status\n"); + lp->ipt_stat = k; + lp->ipt_obj = glp_sdf_read_num(data); + /* rows (auxiliary variables) */ + for (i = 1; i <= lp->m; i++) + { GLPROW *row = lp->row[i]; + /* primal value, dual value */ + row->pval = glp_sdf_read_num(data); + row->dval = glp_sdf_read_num(data); + } + /* columns (structural variables) */ + for (j = 1; j <= lp->n; j++) + { GLPCOL *col = lp->col[j]; + /* primal value, dual value */ + col->pval = glp_sdf_read_num(data); + col->dval = glp_sdf_read_num(data); + } + xprintf("%d lines were read\n", glp_sdf_line(data)); +done: if (ret) lp->ipt_stat = GLP_UNDEF; + if (data != NULL) glp_sdf_close_file(data); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_ipt - write interior-point solution to text file +* +* SYNOPSIS +* +* int glp_write_ipt(glp_prob *lp, const char *fname); +* +* DESCRIPTION +* +* The routine glp_write_ipt writes the current interior-point solution +* to a text file whose name is specified by the parameter fname. This +* file can be read back with the routine glp_read_ipt. +* +* RETURNS +* +* On success the routine returns zero, otherwise non-zero. +* +* FILE FORMAT +* +* The file created by the routine glp_write_ipt is a plain text file, +* which contains the following information: +* +* m n +* stat obj_val +* r_prim[1] r_dual[1] +* . . . +* r_prim[m] r_dual[m] +* c_prim[1] c_dual[1] +* . . . +* c_prim[n] c_dual[n] +* +* where: +* m is the number of rows (auxiliary variables); +* n is the number of columns (structural variables); +* stat is the solution status (GLP_UNDEF = 1 or GLP_OPT = 5); +* obj_val is the objective value; +* r_prim[i], i = 1,...,m, is the primal value of i-th row; +* r_dual[i], i = 1,...,m, is the dual value of i-th row; +* c_prim[j], j = 1,...,n, is the primal value of j-th column; +* c_dual[j], j = 1,...,n, is the dual value of j-th column. */ + +int glp_write_ipt(glp_prob *lp, const char *fname) +{ XFILE *fp; + int i, j, ret = 0; + xprintf("Writing interior-point solution to `%s'...\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* number of rows, number of columns */ + xfprintf(fp, "%d %d\n", lp->m, lp->n); + /* solution status, objective value */ + xfprintf(fp, "%d %.*g\n", lp->ipt_stat, DBL_DIG, lp->ipt_obj); + /* rows (auxiliary variables) */ + for (i = 1; i <= lp->m; i++) + { GLPROW *row = lp->row[i]; + /* primal value, dual value */ + xfprintf(fp, "%.*g %.*g\n", DBL_DIG, row->pval, DBL_DIG, + row->dval); + } + /* columns (structural variables) */ + for (j = 1; j <= lp->n; j++) + { GLPCOL *col = lp->col[j]; + /* primal value, dual value */ + xfprintf(fp, "%.*g %.*g\n", DBL_DIG, col->pval, DBL_DIG, + col->dval); + } + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", 2 + lp->m + lp->n); +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/**********************************************************************/ + +int glp_print_mip(glp_prob *P, const char *fname) +{ /* write MIP solution in printable format */ + XFILE *fp; + GLPROW *row; + GLPCOL *col; + int i, j, t, ae_ind, re_ind, ret; + double ae_max, re_max; + xprintf("Writing MIP solution to `%s'...\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xfprintf(fp, "%-12s%s\n", "Problem:", + P->name == NULL ? "" : P->name); + xfprintf(fp, "%-12s%d\n", "Rows:", P->m); + xfprintf(fp, "%-12s%d (%d integer, %d binary)\n", "Columns:", + P->n, glp_get_num_int(P), glp_get_num_bin(P)); + xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz); + t = glp_mip_status(P); + xfprintf(fp, "%-12s%s\n", "Status:", + t == GLP_OPT ? "INTEGER OPTIMAL" : + t == GLP_FEAS ? "INTEGER NON-OPTIMAL" : + t == GLP_NOFEAS ? "INTEGER EMPTY" : + t == GLP_UNDEF ? "INTEGER UNDEFINED" : "???"); + xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:", + P->obj == NULL ? "" : P->obj, + P->obj == NULL ? "" : " = ", P->mip_obj, + P->dir == GLP_MIN ? "MINimum" : + P->dir == GLP_MAX ? "MAXimum" : "???"); + xfprintf(fp, "\n"); + xfprintf(fp, " No. Row name Activity Lower bound " + " Upper bound\n"); + xfprintf(fp, "------ ------------ ------------- ------------- " + "-------------\n"); + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + xfprintf(fp, "%6d ", i); + if (row->name == NULL || strlen(row->name) <= 12) + xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name); + else + xfprintf(fp, "%s\n%20s", row->name, ""); + xfprintf(fp, "%3s", ""); + xfprintf(fp, "%13.6g ", + fabs(row->mipx) <= 1e-9 ? 0.0 : row->mipx); + if (row->type == GLP_LO || row->type == GLP_DB || + row->type == GLP_FX) + xfprintf(fp, "%13.6g ", row->lb); + else + xfprintf(fp, "%13s ", ""); + if (row->type == GLP_UP || row->type == GLP_DB) + xfprintf(fp, "%13.6g ", row->ub); + else + xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : ""); + xfprintf(fp, "\n"); + } + xfprintf(fp, "\n"); + xfprintf(fp, " No. Column name Activity Lower bound " + " Upper bound\n"); + xfprintf(fp, "------ ------------ ------------- ------------- " + "-------------\n"); + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + xfprintf(fp, "%6d ", j); + if (col->name == NULL || strlen(col->name) <= 12) + xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name); + else + xfprintf(fp, "%s\n%20s", col->name, ""); + xfprintf(fp, "%s ", + col->kind == GLP_CV ? " " : + col->kind == GLP_IV ? "*" : "?"); + xfprintf(fp, "%13.6g ", + fabs(col->mipx) <= 1e-9 ? 0.0 : col->mipx); + if (col->type == GLP_LO || col->type == GLP_DB || + col->type == GLP_FX) + xfprintf(fp, "%13.6g ", col->lb); + else + xfprintf(fp, "%13s ", ""); + if (col->type == GLP_UP || col->type == GLP_DB) + xfprintf(fp, "%13.6g ", col->ub); + else + xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : ""); + xfprintf(fp, "\n"); + } + xfprintf(fp, "\n"); + xfprintf(fp, "Integer feasibility conditions:\n"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_MIP, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n", + ae_max, ae_ind); + xfprintf(fp, " max.rel.err = %.2e on row %d\n", + re_max, re_ind); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "SOLUTION IS WRONG"); + xfprintf(fp, "\n"); + _glp_check_kkt(P, GLP_MIP, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, + &re_ind); + xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n", + ae_max, ae_ind <= P->m ? "row" : "column", + ae_ind <= P->m ? ae_ind : ae_ind - P->m); + xfprintf(fp, " max.rel.err = %.2e on %s %d\n", + re_max, re_ind <= P->m ? "row" : "column", + re_ind <= P->m ? re_ind : re_ind - P->m); + xfprintf(fp, "%8s%s\n", "", + re_max <= 1e-9 ? "High quality" : + re_max <= 1e-6 ? "Medium quality" : + re_max <= 1e-3 ? "Low quality" : "SOLUTION IS INFEASIBLE"); + xfprintf(fp, "\n"); + xfprintf(fp, "End of output\n"); + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_read_mip - read MIP solution from text file +* +* SYNOPSIS +* +* int glp_read_mip(glp_prob *mip, const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_mip reads MIP solution from a text file whose +* name is specified by the parameter fname into the problem object. +* +* For the file format see description of the routine glp_write_mip. +* +* RETURNS +* +* On success the routine returns zero, otherwise non-zero. */ + +int glp_read_mip(glp_prob *mip, const char *fname) +{ glp_data *data; + jmp_buf jump; + int i, j, k, ret = 0; + xprintf("Reading MIP solution from `%s'...\n", fname); + data = glp_sdf_open_file(fname); + if (data == NULL) + { ret = 1; + goto done; + } + if (setjmp(jump)) + { ret = 1; + goto done; + } + glp_sdf_set_jump(data, jump); + /* number of rows, number of columns */ + k = glp_sdf_read_int(data); + if (k != mip->m) + glp_sdf_error(data, "wrong number of rows\n"); + k = glp_sdf_read_int(data); + if (k != mip->n) + glp_sdf_error(data, "wrong number of columns\n"); + /* solution status, objective value */ + k = glp_sdf_read_int(data); + if (!(k == GLP_UNDEF || k == GLP_OPT || k == GLP_FEAS || + k == GLP_NOFEAS)) + glp_sdf_error(data, "invalid solution status\n"); + mip->mip_stat = k; + mip->mip_obj = glp_sdf_read_num(data); + /* rows (auxiliary variables) */ + for (i = 1; i <= mip->m; i++) + { GLPROW *row = mip->row[i]; + row->mipx = glp_sdf_read_num(data); + } + /* columns (structural variables) */ + for (j = 1; j <= mip->n; j++) + { GLPCOL *col = mip->col[j]; + col->mipx = glp_sdf_read_num(data); + if (col->kind == GLP_IV && col->mipx != floor(col->mipx)) + glp_sdf_error(data, "non-integer column value"); + } + xprintf("%d lines were read\n", glp_sdf_line(data)); +done: if (ret) mip->mip_stat = GLP_UNDEF; + if (data != NULL) glp_sdf_close_file(data); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_mip - write MIP solution to text file +* +* SYNOPSIS +* +* int glp_write_mip(glp_prob *mip, const char *fname); +* +* DESCRIPTION +* +* The routine glp_write_mip writes the current MIP solution to a text +* file whose name is specified by the parameter fname. This file can +* be read back with the routine glp_read_mip. +* +* RETURNS +* +* On success the routine returns zero, otherwise non-zero. +* +* FILE FORMAT +* +* The file created by the routine glp_write_sol is a plain text file, +* which contains the following information: +* +* m n +* stat obj_val +* r_val[1] +* . . . +* r_val[m] +* c_val[1] +* . . . +* c_val[n] +* +* where: +* m is the number of rows (auxiliary variables); +* n is the number of columns (structural variables); +* stat is the solution status (GLP_UNDEF = 1, GLP_FEAS = 2, +* GLP_NOFEAS = 4, or GLP_OPT = 5); +* obj_val is the objective value; +* r_val[i], i = 1,...,m, is the value of i-th row; +* c_val[j], j = 1,...,n, is the value of j-th column. */ + +int glp_write_mip(glp_prob *mip, const char *fname) +{ XFILE *fp; + int i, j, ret = 0; + xprintf("Writing MIP solution to `%s'...\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* number of rows, number of columns */ + xfprintf(fp, "%d %d\n", mip->m, mip->n); + /* solution status, objective value */ + xfprintf(fp, "%d %.*g\n", mip->mip_stat, DBL_DIG, mip->mip_obj); + /* rows (auxiliary variables) */ + for (i = 1; i <= mip->m; i++) + xfprintf(fp, "%.*g\n", DBL_DIG, mip->row[i]->mipx); + /* columns (structural variables) */ + for (j = 1; j <= mip->n; j++) + xfprintf(fp, "%.*g\n", DBL_DIG, mip->col[j]->mipx); + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", 2 + mip->m + mip->n); +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi12.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi12.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,2219 @@ +/* glpapi12.c (basis factorization and simplex tableau routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_bf_exists - check if the basis factorization exists +* +* SYNOPSIS +* +* int glp_bf_exists(glp_prob *lp); +* +* RETURNS +* +* If the basis factorization for the current basis associated with +* the specified problem object exists and therefore is available for +* computations, the routine glp_bf_exists returns non-zero. Otherwise +* the routine returns zero. */ + +int glp_bf_exists(glp_prob *lp) +{ int ret; + ret = (lp->m == 0 || lp->valid); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_factorize - compute the basis factorization +* +* SYNOPSIS +* +* int glp_factorize(glp_prob *lp); +* +* DESCRIPTION +* +* The routine glp_factorize computes the basis factorization for the +* current basis associated with the specified problem object. +* +* RETURNS +* +* 0 The basis factorization has been successfully computed. +* +* GLP_EBADB +* The basis matrix is invalid, i.e. the number of basic (auxiliary +* and structural) variables differs from the number of rows in the +* problem object. +* +* GLP_ESING +* The basis matrix is singular within the working precision. +* +* GLP_ECOND +* The basis matrix is ill-conditioned. */ + +static int b_col(void *info, int j, int ind[], double val[]) +{ glp_prob *lp = info; + int m = lp->m; + GLPAIJ *aij; + int k, len; + xassert(1 <= j && j <= m); + /* determine the ordinal number of basic auxiliary or structural + variable x[k] corresponding to basic variable xB[j] */ + k = lp->head[j]; + /* build j-th column of the basic matrix, which is k-th column of + the scaled augmented matrix (I | -R*A*S) */ + if (k <= m) + { /* x[k] is auxiliary variable */ + len = 1; + ind[1] = k; + val[1] = 1.0; + } + else + { /* x[k] is structural variable */ + len = 0; + for (aij = lp->col[k-m]->ptr; aij != NULL; aij = aij->c_next) + { len++; + ind[len] = aij->row->i; + val[len] = - aij->row->rii * aij->val * aij->col->sjj; + } + } + return len; +} + +static void copy_bfcp(glp_prob *lp); + +int glp_factorize(glp_prob *lp) +{ int m = lp->m; + int n = lp->n; + GLPROW **row = lp->row; + GLPCOL **col = lp->col; + int *head = lp->head; + int j, k, stat, ret; + /* invalidate the basis factorization */ + lp->valid = 0; + /* build the basis header */ + j = 0; + for (k = 1; k <= m+n; k++) + { if (k <= m) + { stat = row[k]->stat; + row[k]->bind = 0; + } + else + { stat = col[k-m]->stat; + col[k-m]->bind = 0; + } + if (stat == GLP_BS) + { j++; + if (j > m) + { /* too many basic variables */ + ret = GLP_EBADB; + goto fini; + } + head[j] = k; + if (k <= m) + row[k]->bind = j; + else + col[k-m]->bind = j; + } + } + if (j < m) + { /* too few basic variables */ + ret = GLP_EBADB; + goto fini; + } + /* try to factorize the basis matrix */ + if (m > 0) + { if (lp->bfd == NULL) + { lp->bfd = bfd_create_it(); + copy_bfcp(lp); + } + switch (bfd_factorize(lp->bfd, m, lp->head, b_col, lp)) + { case 0: + /* ok */ + break; + case BFD_ESING: + /* singular matrix */ + ret = GLP_ESING; + goto fini; + case BFD_ECOND: + /* ill-conditioned matrix */ + ret = GLP_ECOND; + goto fini; + default: + xassert(lp != lp); + } + lp->valid = 1; + } + /* factorization successful */ + ret = 0; +fini: /* bring the return code to the calling program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_bf_updated - check if the basis factorization has been updated +* +* SYNOPSIS +* +* int glp_bf_updated(glp_prob *lp); +* +* RETURNS +* +* If the basis factorization has been just computed from scratch, the +* routine glp_bf_updated returns zero. Otherwise, if the factorization +* has been updated one or more times, the routine returns non-zero. */ + +int glp_bf_updated(glp_prob *lp) +{ int cnt; + if (!(lp->m == 0 || lp->valid)) + xerror("glp_bf_update: basis factorization does not exist\n"); +#if 0 /* 15/XI-2009 */ + cnt = (lp->m == 0 ? 0 : lp->bfd->upd_cnt); +#else + cnt = (lp->m == 0 ? 0 : bfd_get_count(lp->bfd)); +#endif + return cnt; +} + +/*********************************************************************** +* NAME +* +* glp_get_bfcp - retrieve basis factorization control parameters +* +* SYNOPSIS +* +* void glp_get_bfcp(glp_prob *lp, glp_bfcp *parm); +* +* DESCRIPTION +* +* The routine glp_get_bfcp retrieves control parameters, which are +* used on computing and updating the basis factorization associated +* with the specified problem object. +* +* Current values of control parameters are stored by the routine in +* a glp_bfcp structure, which the parameter parm points to. */ + +void glp_get_bfcp(glp_prob *lp, glp_bfcp *parm) +{ glp_bfcp *bfcp = lp->bfcp; + if (bfcp == NULL) + { parm->type = GLP_BF_FT; + parm->lu_size = 0; + parm->piv_tol = 0.10; + parm->piv_lim = 4; + parm->suhl = GLP_ON; + parm->eps_tol = 1e-15; + parm->max_gro = 1e+10; + parm->nfs_max = 100; + parm->upd_tol = 1e-6; + parm->nrs_max = 100; + parm->rs_size = 0; + } + else + memcpy(parm, bfcp, sizeof(glp_bfcp)); + return; +} + +/*********************************************************************** +* NAME +* +* glp_set_bfcp - change basis factorization control parameters +* +* SYNOPSIS +* +* void glp_set_bfcp(glp_prob *lp, const glp_bfcp *parm); +* +* DESCRIPTION +* +* The routine glp_set_bfcp changes control parameters, which are used +* by internal GLPK routines in computing and updating the basis +* factorization associated with the specified problem object. +* +* New values of the control parameters should be passed in a structure +* glp_bfcp, which the parameter parm points to. +* +* The parameter parm can be specified as NULL, in which case all +* control parameters are reset to their default values. */ + +#if 0 /* 15/XI-2009 */ +static void copy_bfcp(glp_prob *lp) +{ glp_bfcp _parm, *parm = &_parm; + BFD *bfd = lp->bfd; + glp_get_bfcp(lp, parm); + xassert(bfd != NULL); + bfd->type = parm->type; + bfd->lu_size = parm->lu_size; + bfd->piv_tol = parm->piv_tol; + bfd->piv_lim = parm->piv_lim; + bfd->suhl = parm->suhl; + bfd->eps_tol = parm->eps_tol; + bfd->max_gro = parm->max_gro; + bfd->nfs_max = parm->nfs_max; + bfd->upd_tol = parm->upd_tol; + bfd->nrs_max = parm->nrs_max; + bfd->rs_size = parm->rs_size; + return; +} +#else +static void copy_bfcp(glp_prob *lp) +{ glp_bfcp _parm, *parm = &_parm; + glp_get_bfcp(lp, parm); + bfd_set_parm(lp->bfd, parm); + return; +} +#endif + +void glp_set_bfcp(glp_prob *lp, const glp_bfcp *parm) +{ glp_bfcp *bfcp = lp->bfcp; + if (parm == NULL) + { /* reset to default values */ + if (bfcp != NULL) + xfree(bfcp), lp->bfcp = NULL; + } + else + { /* set to specified values */ + if (bfcp == NULL) + bfcp = lp->bfcp = xmalloc(sizeof(glp_bfcp)); + memcpy(bfcp, parm, sizeof(glp_bfcp)); + if (!(bfcp->type == GLP_BF_FT || bfcp->type == GLP_BF_BG || + bfcp->type == GLP_BF_GR)) + xerror("glp_set_bfcp: type = %d; invalid parameter\n", + bfcp->type); + if (bfcp->lu_size < 0) + xerror("glp_set_bfcp: lu_size = %d; invalid parameter\n", + bfcp->lu_size); + if (!(0.0 < bfcp->piv_tol && bfcp->piv_tol < 1.0)) + xerror("glp_set_bfcp: piv_tol = %g; invalid parameter\n", + bfcp->piv_tol); + if (bfcp->piv_lim < 1) + xerror("glp_set_bfcp: piv_lim = %d; invalid parameter\n", + bfcp->piv_lim); + if (!(bfcp->suhl == GLP_ON || bfcp->suhl == GLP_OFF)) + xerror("glp_set_bfcp: suhl = %d; invalid parameter\n", + bfcp->suhl); + if (!(0.0 <= bfcp->eps_tol && bfcp->eps_tol <= 1e-6)) + xerror("glp_set_bfcp: eps_tol = %g; invalid parameter\n", + bfcp->eps_tol); + if (bfcp->max_gro < 1.0) + xerror("glp_set_bfcp: max_gro = %g; invalid parameter\n", + bfcp->max_gro); + if (!(1 <= bfcp->nfs_max && bfcp->nfs_max <= 32767)) + xerror("glp_set_bfcp: nfs_max = %d; invalid parameter\n", + bfcp->nfs_max); + if (!(0.0 < bfcp->upd_tol && bfcp->upd_tol < 1.0)) + xerror("glp_set_bfcp: upd_tol = %g; invalid parameter\n", + bfcp->upd_tol); + if (!(1 <= bfcp->nrs_max && bfcp->nrs_max <= 32767)) + xerror("glp_set_bfcp: nrs_max = %d; invalid parameter\n", + bfcp->nrs_max); + if (bfcp->rs_size < 0) + xerror("glp_set_bfcp: rs_size = %d; invalid parameter\n", + bfcp->nrs_max); + if (bfcp->rs_size == 0) + bfcp->rs_size = 20 * bfcp->nrs_max; + } + if (lp->bfd != NULL) copy_bfcp(lp); + return; +} + +/*********************************************************************** +* NAME +* +* glp_get_bhead - retrieve the basis header information +* +* SYNOPSIS +* +* int glp_get_bhead(glp_prob *lp, int k); +* +* DESCRIPTION +* +* The routine glp_get_bhead returns the basis header information for +* the current basis associated with the specified problem object. +* +* RETURNS +* +* If xB[k], 1 <= k <= m, is i-th auxiliary variable (1 <= i <= m), the +* routine returns i. Otherwise, if xB[k] is j-th structural variable +* (1 <= j <= n), the routine returns m+j. Here m is the number of rows +* and n is the number of columns in the problem object. */ + +int glp_get_bhead(glp_prob *lp, int k) +{ if (!(lp->m == 0 || lp->valid)) + xerror("glp_get_bhead: basis factorization does not exist\n"); + if (!(1 <= k && k <= lp->m)) + xerror("glp_get_bhead: k = %d; index out of range\n", k); + return lp->head[k]; +} + +/*********************************************************************** +* NAME +* +* glp_get_row_bind - retrieve row index in the basis header +* +* SYNOPSIS +* +* int glp_get_row_bind(glp_prob *lp, int i); +* +* RETURNS +* +* The routine glp_get_row_bind returns the index k of basic variable +* xB[k], 1 <= k <= m, which is i-th auxiliary variable, 1 <= i <= m, +* in the current basis associated with the specified problem object, +* where m is the number of rows. However, if i-th auxiliary variable +* is non-basic, the routine returns zero. */ + +int glp_get_row_bind(glp_prob *lp, int i) +{ if (!(lp->m == 0 || lp->valid)) + xerror("glp_get_row_bind: basis factorization does not exist\n" + ); + if (!(1 <= i && i <= lp->m)) + xerror("glp_get_row_bind: i = %d; row number out of range\n", + i); + return lp->row[i]->bind; +} + +/*********************************************************************** +* NAME +* +* glp_get_col_bind - retrieve column index in the basis header +* +* SYNOPSIS +* +* int glp_get_col_bind(glp_prob *lp, int j); +* +* RETURNS +* +* The routine glp_get_col_bind returns the index k of basic variable +* xB[k], 1 <= k <= m, which is j-th structural variable, 1 <= j <= n, +* in the current basis associated with the specified problem object, +* where m is the number of rows, n is the number of columns. However, +* if j-th structural variable is non-basic, the routine returns zero.*/ + +int glp_get_col_bind(glp_prob *lp, int j) +{ if (!(lp->m == 0 || lp->valid)) + xerror("glp_get_col_bind: basis factorization does not exist\n" + ); + if (!(1 <= j && j <= lp->n)) + xerror("glp_get_col_bind: j = %d; column number out of range\n" + , j); + return lp->col[j]->bind; +} + +/*********************************************************************** +* NAME +* +* glp_ftran - perform forward transformation (solve system B*x = b) +* +* SYNOPSIS +* +* void glp_ftran(glp_prob *lp, double x[]); +* +* DESCRIPTION +* +* The routine glp_ftran performs forward transformation, i.e. solves +* the system B*x = b, where B is the basis matrix corresponding to the +* current basis for the specified problem object, x is the vector of +* unknowns to be computed, b is the vector of right-hand sides. +* +* On entry elements of the vector b should be stored in dense format +* in locations x[1], ..., x[m], where m is the number of rows. On exit +* the routine stores elements of the vector x in the same locations. +* +* SCALING/UNSCALING +* +* Let A~ = (I | -A) is the augmented constraint matrix of the original +* (unscaled) problem. In the scaled LP problem instead the matrix A the +* scaled matrix A" = R*A*S is actually used, so +* +* A~" = (I | A") = (I | R*A*S) = (R*I*inv(R) | R*A*S) = +* (1) +* = R*(I | A)*S~ = R*A~*S~, +* +* is the scaled augmented constraint matrix, where R and S are diagonal +* scaling matrices used to scale rows and columns of the matrix A, and +* +* S~ = diag(inv(R) | S) (2) +* +* is an augmented diagonal scaling matrix. +* +* By definition: +* +* A~ = (B | N), (3) +* +* where B is the basic matrix, which consists of basic columns of the +* augmented constraint matrix A~, and N is a matrix, which consists of +* non-basic columns of A~. From (1) it follows that: +* +* A~" = (B" | N") = (R*B*SB | R*N*SN), (4) +* +* where SB and SN are parts of the augmented scaling matrix S~, which +* correspond to basic and non-basic variables, respectively. Therefore +* +* B" = R*B*SB, (5) +* +* which is the scaled basis matrix. */ + +void glp_ftran(glp_prob *lp, double x[]) +{ int m = lp->m; + GLPROW **row = lp->row; + GLPCOL **col = lp->col; + int i, k; + /* B*x = b ===> (R*B*SB)*(inv(SB)*x) = R*b ===> + B"*x" = b", where b" = R*b, x = SB*x" */ + if (!(m == 0 || lp->valid)) + xerror("glp_ftran: basis factorization does not exist\n"); + /* b" := R*b */ + for (i = 1; i <= m; i++) + x[i] *= row[i]->rii; + /* x" := inv(B")*b" */ + if (m > 0) bfd_ftran(lp->bfd, x); + /* x := SB*x" */ + for (i = 1; i <= m; i++) + { k = lp->head[i]; + if (k <= m) + x[i] /= row[k]->rii; + else + x[i] *= col[k-m]->sjj; + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_btran - perform backward transformation (solve system B'*x = b) +* +* SYNOPSIS +* +* void glp_btran(glp_prob *lp, double x[]); +* +* DESCRIPTION +* +* The routine glp_btran performs backward transformation, i.e. solves +* the system B'*x = b, where B' is a matrix transposed to the basis +* matrix corresponding to the current basis for the specified problem +* problem object, x is the vector of unknowns to be computed, b is the +* vector of right-hand sides. +* +* On entry elements of the vector b should be stored in dense format +* in locations x[1], ..., x[m], where m is the number of rows. On exit +* the routine stores elements of the vector x in the same locations. +* +* SCALING/UNSCALING +* +* See comments to the routine glp_ftran. */ + +void glp_btran(glp_prob *lp, double x[]) +{ int m = lp->m; + GLPROW **row = lp->row; + GLPCOL **col = lp->col; + int i, k; + /* B'*x = b ===> (SB*B'*R)*(inv(R)*x) = SB*b ===> + (B")'*x" = b", where b" = SB*b, x = R*x" */ + if (!(m == 0 || lp->valid)) + xerror("glp_btran: basis factorization does not exist\n"); + /* b" := SB*b */ + for (i = 1; i <= m; i++) + { k = lp->head[i]; + if (k <= m) + x[i] /= row[k]->rii; + else + x[i] *= col[k-m]->sjj; + } + /* x" := inv[(B")']*b" */ + if (m > 0) bfd_btran(lp->bfd, x); + /* x := R*x" */ + for (i = 1; i <= m; i++) + x[i] *= row[i]->rii; + return; +} + +/*********************************************************************** +* NAME +* +* glp_warm_up - "warm up" LP basis +* +* SYNOPSIS +* +* int glp_warm_up(glp_prob *P); +* +* DESCRIPTION +* +* The routine glp_warm_up "warms up" the LP basis for the specified +* problem object using current statuses assigned to rows and columns +* (that is, to auxiliary and structural variables). +* +* This operation includes computing factorization of the basis matrix +* (if it does not exist), computing primal and dual components of basic +* solution, and determining the solution status. +* +* RETURNS +* +* 0 The operation has been successfully performed. +* +* GLP_EBADB +* The basis matrix is invalid, i.e. the number of basic (auxiliary +* and structural) variables differs from the number of rows in the +* problem object. +* +* GLP_ESING +* The basis matrix is singular within the working precision. +* +* GLP_ECOND +* The basis matrix is ill-conditioned. */ + +int glp_warm_up(glp_prob *P) +{ GLPROW *row; + GLPCOL *col; + GLPAIJ *aij; + int i, j, type, ret; + double eps, temp, *work; + /* invalidate basic solution */ + P->pbs_stat = P->dbs_stat = GLP_UNDEF; + P->obj_val = 0.0; + P->some = 0; + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + row->prim = row->dual = 0.0; + } + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + col->prim = col->dual = 0.0; + } + /* compute the basis factorization, if necessary */ + if (!glp_bf_exists(P)) + { ret = glp_factorize(P); + if (ret != 0) goto done; + } + /* allocate working array */ + work = xcalloc(1+P->m, sizeof(double)); + /* determine and store values of non-basic variables, compute + vector (- N * xN) */ + for (i = 1; i <= P->m; i++) + work[i] = 0.0; + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + if (row->stat == GLP_BS) + continue; + else if (row->stat == GLP_NL) + row->prim = row->lb; + else if (row->stat == GLP_NU) + row->prim = row->ub; + else if (row->stat == GLP_NF) + row->prim = 0.0; + else if (row->stat == GLP_NS) + row->prim = row->lb; + else + xassert(row != row); + /* N[j] is i-th column of matrix (I|-A) */ + work[i] -= row->prim; + } + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->stat == GLP_BS) + continue; + else if (col->stat == GLP_NL) + col->prim = col->lb; + else if (col->stat == GLP_NU) + col->prim = col->ub; + else if (col->stat == GLP_NF) + col->prim = 0.0; + else if (col->stat == GLP_NS) + col->prim = col->lb; + else + xassert(col != col); + /* N[j] is (m+j)-th column of matrix (I|-A) */ + if (col->prim != 0.0) + { for (aij = col->ptr; aij != NULL; aij = aij->c_next) + work[aij->row->i] += aij->val * col->prim; + } + } + /* compute vector of basic variables xB = - inv(B) * N * xN */ + glp_ftran(P, work); + /* store values of basic variables, check primal feasibility */ + P->pbs_stat = GLP_FEAS; + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + if (row->stat != GLP_BS) + continue; + row->prim = work[row->bind]; + type = row->type; + if (type == GLP_LO || type == GLP_DB || type == GLP_FX) + { eps = 1e-6 + 1e-9 * fabs(row->lb); + if (row->prim < row->lb - eps) + P->pbs_stat = GLP_INFEAS; + } + if (type == GLP_UP || type == GLP_DB || type == GLP_FX) + { eps = 1e-6 + 1e-9 * fabs(row->ub); + if (row->prim > row->ub + eps) + P->pbs_stat = GLP_INFEAS; + } + } + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->stat != GLP_BS) + continue; + col->prim = work[col->bind]; + type = col->type; + if (type == GLP_LO || type == GLP_DB || type == GLP_FX) + { eps = 1e-6 + 1e-9 * fabs(col->lb); + if (col->prim < col->lb - eps) + P->pbs_stat = GLP_INFEAS; + } + if (type == GLP_UP || type == GLP_DB || type == GLP_FX) + { eps = 1e-6 + 1e-9 * fabs(col->ub); + if (col->prim > col->ub + eps) + P->pbs_stat = GLP_INFEAS; + } + } + /* compute value of the objective function */ + P->obj_val = P->c0; + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + P->obj_val += col->coef * col->prim; + } + /* build vector cB of objective coefficients at basic variables */ + for (i = 1; i <= P->m; i++) + work[i] = 0.0; + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->stat == GLP_BS) + work[col->bind] = col->coef; + } + /* compute vector of simplex multipliers pi = inv(B') * cB */ + glp_btran(P, work); + /* compute and store reduced costs of non-basic variables d[j] = + c[j] - N'[j] * pi, check dual feasibility */ + P->dbs_stat = GLP_FEAS; + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + if (row->stat == GLP_BS) + { row->dual = 0.0; + continue; + } + /* N[j] is i-th column of matrix (I|-A) */ + row->dual = - work[i]; + type = row->type; + temp = (P->dir == GLP_MIN ? + row->dual : - row->dual); + if ((type == GLP_FR || type == GLP_LO) && temp < -1e-5 || + (type == GLP_FR || type == GLP_UP) && temp > +1e-5) + P->dbs_stat = GLP_INFEAS; + } + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->stat == GLP_BS) + { col->dual = 0.0; + continue; + } + /* N[j] is (m+j)-th column of matrix (I|-A) */ + col->dual = col->coef; + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + col->dual += aij->val * work[aij->row->i]; + type = col->type; + temp = (P->dir == GLP_MIN ? + col->dual : - col->dual); + if ((type == GLP_FR || type == GLP_LO) && temp < -1e-5 || + (type == GLP_FR || type == GLP_UP) && temp > +1e-5) + P->dbs_stat = GLP_INFEAS; + } + /* free working array */ + xfree(work); + ret = 0; +done: return ret; +} + +/*********************************************************************** +* NAME +* +* glp_eval_tab_row - compute row of the simplex tableau +* +* SYNOPSIS +* +* int glp_eval_tab_row(glp_prob *lp, int k, int ind[], double val[]); +* +* DESCRIPTION +* +* The routine glp_eval_tab_row computes a row of the current simplex +* tableau for the basic variable, which is specified by the number k: +* if 1 <= k <= m, x[k] is k-th auxiliary variable; if m+1 <= k <= m+n, +* x[k] is (k-m)-th structural variable, where m is number of rows, and +* n is number of columns. The current basis must be available. +* +* The routine stores column indices and numerical values of non-zero +* elements of the computed row using sparse format to the locations +* ind[1], ..., ind[len] and val[1], ..., val[len], respectively, where +* 0 <= len <= n is number of non-zeros returned on exit. +* +* Element indices stored in the array ind have the same sense as the +* index k, i.e. indices 1 to m denote auxiliary variables and indices +* m+1 to m+n denote structural ones (all these variables are obviously +* non-basic by definition). +* +* The computed row shows how the specified basic variable x[k] = xB[i] +* depends on non-basic variables: +* +* xB[i] = alfa[i,1]*xN[1] + alfa[i,2]*xN[2] + ... + alfa[i,n]*xN[n], +* +* where alfa[i,j] are elements of the simplex table row, xN[j] are +* non-basic (auxiliary and structural) variables. +* +* RETURNS +* +* The routine returns number of non-zero elements in the simplex table +* row stored in the arrays ind and val. +* +* BACKGROUND +* +* The system of equality constraints of the LP problem is: +* +* xR = A * xS, (1) +* +* where xR is the vector of auxliary variables, xS is the vector of +* structural variables, A is the matrix of constraint coefficients. +* +* The system (1) can be written in homogenous form as follows: +* +* A~ * x = 0, (2) +* +* where A~ = (I | -A) is the augmented constraint matrix (has m rows +* and m+n columns), x = (xR | xS) is the vector of all (auxiliary and +* structural) variables. +* +* By definition for the current basis we have: +* +* A~ = (B | N), (3) +* +* where B is the basis matrix. Thus, the system (2) can be written as: +* +* B * xB + N * xN = 0. (4) +* +* From (4) it follows that: +* +* xB = A^ * xN, (5) +* +* where the matrix +* +* A^ = - inv(B) * N (6) +* +* is called the simplex table. +* +* It is understood that i-th row of the simplex table is: +* +* e * A^ = - e * inv(B) * N, (7) +* +* where e is a unity vector with e[i] = 1. +* +* To compute i-th row of the simplex table the routine first computes +* i-th row of the inverse: +* +* rho = inv(B') * e, (8) +* +* where B' is a matrix transposed to B, and then computes elements of +* i-th row of the simplex table as scalar products: +* +* alfa[i,j] = - rho * N[j] for all j, (9) +* +* where N[j] is a column of the augmented constraint matrix A~, which +* corresponds to some non-basic auxiliary or structural variable. */ + +int glp_eval_tab_row(glp_prob *lp, int k, int ind[], double val[]) +{ int m = lp->m; + int n = lp->n; + int i, t, len, lll, *iii; + double alfa, *rho, *vvv; + if (!(m == 0 || lp->valid)) + xerror("glp_eval_tab_row: basis factorization does not exist\n" + ); + if (!(1 <= k && k <= m+n)) + xerror("glp_eval_tab_row: k = %d; variable number out of range" + , k); + /* determine xB[i] which corresponds to x[k] */ + if (k <= m) + i = glp_get_row_bind(lp, k); + else + i = glp_get_col_bind(lp, k-m); + if (i == 0) + xerror("glp_eval_tab_row: k = %d; variable must be basic", k); + xassert(1 <= i && i <= m); + /* allocate working arrays */ + rho = xcalloc(1+m, sizeof(double)); + iii = xcalloc(1+m, sizeof(int)); + vvv = xcalloc(1+m, sizeof(double)); + /* compute i-th row of the inverse; see (8) */ + for (t = 1; t <= m; t++) rho[t] = 0.0; + rho[i] = 1.0; + glp_btran(lp, rho); + /* compute i-th row of the simplex table */ + len = 0; + for (k = 1; k <= m+n; k++) + { if (k <= m) + { /* x[k] is auxiliary variable, so N[k] is a unity column */ + if (glp_get_row_stat(lp, k) == GLP_BS) continue; + /* compute alfa[i,j]; see (9) */ + alfa = - rho[k]; + } + else + { /* x[k] is structural variable, so N[k] is a column of the + original constraint matrix A with negative sign */ + if (glp_get_col_stat(lp, k-m) == GLP_BS) continue; + /* compute alfa[i,j]; see (9) */ + lll = glp_get_mat_col(lp, k-m, iii, vvv); + alfa = 0.0; + for (t = 1; t <= lll; t++) alfa += rho[iii[t]] * vvv[t]; + } + /* store alfa[i,j] */ + if (alfa != 0.0) len++, ind[len] = k, val[len] = alfa; + } + xassert(len <= n); + /* free working arrays */ + xfree(rho); + xfree(iii); + xfree(vvv); + /* return to the calling program */ + return len; +} + +/*********************************************************************** +* NAME +* +* glp_eval_tab_col - compute column of the simplex tableau +* +* SYNOPSIS +* +* int glp_eval_tab_col(glp_prob *lp, int k, int ind[], double val[]); +* +* DESCRIPTION +* +* The routine glp_eval_tab_col computes a column of the current simplex +* table for the non-basic variable, which is specified by the number k: +* if 1 <= k <= m, x[k] is k-th auxiliary variable; if m+1 <= k <= m+n, +* x[k] is (k-m)-th structural variable, where m is number of rows, and +* n is number of columns. The current basis must be available. +* +* The routine stores row indices and numerical values of non-zero +* elements of the computed column using sparse format to the locations +* ind[1], ..., ind[len] and val[1], ..., val[len] respectively, where +* 0 <= len <= m is number of non-zeros returned on exit. +* +* Element indices stored in the array ind have the same sense as the +* index k, i.e. indices 1 to m denote auxiliary variables and indices +* m+1 to m+n denote structural ones (all these variables are obviously +* basic by the definition). +* +* The computed column shows how basic variables depend on the specified +* non-basic variable x[k] = xN[j]: +* +* xB[1] = ... + alfa[1,j]*xN[j] + ... +* xB[2] = ... + alfa[2,j]*xN[j] + ... +* . . . . . . +* xB[m] = ... + alfa[m,j]*xN[j] + ... +* +* where alfa[i,j] are elements of the simplex table column, xB[i] are +* basic (auxiliary and structural) variables. +* +* RETURNS +* +* The routine returns number of non-zero elements in the simplex table +* column stored in the arrays ind and val. +* +* BACKGROUND +* +* As it was explained in comments to the routine glp_eval_tab_row (see +* above) the simplex table is the following matrix: +* +* A^ = - inv(B) * N. (1) +* +* Therefore j-th column of the simplex table is: +* +* A^ * e = - inv(B) * N * e = - inv(B) * N[j], (2) +* +* where e is a unity vector with e[j] = 1, B is the basis matrix, N[j] +* is a column of the augmented constraint matrix A~, which corresponds +* to the given non-basic auxiliary or structural variable. */ + +int glp_eval_tab_col(glp_prob *lp, int k, int ind[], double val[]) +{ int m = lp->m; + int n = lp->n; + int t, len, stat; + double *col; + if (!(m == 0 || lp->valid)) + xerror("glp_eval_tab_col: basis factorization does not exist\n" + ); + if (!(1 <= k && k <= m+n)) + xerror("glp_eval_tab_col: k = %d; variable number out of range" + , k); + if (k <= m) + stat = glp_get_row_stat(lp, k); + else + stat = glp_get_col_stat(lp, k-m); + if (stat == GLP_BS) + xerror("glp_eval_tab_col: k = %d; variable must be non-basic", + k); + /* obtain column N[k] with negative sign */ + col = xcalloc(1+m, sizeof(double)); + for (t = 1; t <= m; t++) col[t] = 0.0; + if (k <= m) + { /* x[k] is auxiliary variable, so N[k] is a unity column */ + col[k] = -1.0; + } + else + { /* x[k] is structural variable, so N[k] is a column of the + original constraint matrix A with negative sign */ + len = glp_get_mat_col(lp, k-m, ind, val); + for (t = 1; t <= len; t++) col[ind[t]] = val[t]; + } + /* compute column of the simplex table, which corresponds to the + specified non-basic variable x[k] */ + glp_ftran(lp, col); + len = 0; + for (t = 1; t <= m; t++) + { if (col[t] != 0.0) + { len++; + ind[len] = glp_get_bhead(lp, t); + val[len] = col[t]; + } + } + xfree(col); + /* return to the calling program */ + return len; +} + +/*********************************************************************** +* NAME +* +* glp_transform_row - transform explicitly specified row +* +* SYNOPSIS +* +* int glp_transform_row(glp_prob *P, int len, int ind[], double val[]); +* +* DESCRIPTION +* +* The routine glp_transform_row performs the same operation as the +* routine glp_eval_tab_row with exception that the row to be +* transformed is specified explicitly as a sparse vector. +* +* The explicitly specified row may be thought as a linear form: +* +* x = a[1]*x[m+1] + a[2]*x[m+2] + ... + a[n]*x[m+n], (1) +* +* where x is an auxiliary variable for this row, a[j] are coefficients +* of the linear form, x[m+j] are structural variables. +* +* On entry column indices and numerical values of non-zero elements of +* the row should be stored in locations ind[1], ..., ind[len] and +* val[1], ..., val[len], where len is the number of non-zero elements. +* +* This routine uses the system of equality constraints and the current +* basis in order to express the auxiliary variable x in (1) through the +* current non-basic variables (as if the transformed row were added to +* the problem object and its auxiliary variable were basic), i.e. the +* resultant row has the form: +* +* x = alfa[1]*xN[1] + alfa[2]*xN[2] + ... + alfa[n]*xN[n], (2) +* +* where xN[j] are non-basic (auxiliary or structural) variables, n is +* the number of columns in the LP problem object. +* +* On exit the routine stores indices and numerical values of non-zero +* elements of the resultant row (2) in locations ind[1], ..., ind[len'] +* and val[1], ..., val[len'], where 0 <= len' <= n is the number of +* non-zero elements in the resultant row returned by the routine. Note +* that indices (numbers) of non-basic variables stored in the array ind +* correspond to original ordinal numbers of variables: indices 1 to m +* mean auxiliary variables and indices m+1 to m+n mean structural ones. +* +* RETURNS +* +* The routine returns len', which is the number of non-zero elements in +* the resultant row stored in the arrays ind and val. +* +* BACKGROUND +* +* The explicitly specified row (1) is transformed in the same way as it +* were the objective function row. +* +* From (1) it follows that: +* +* x = aB * xB + aN * xN, (3) +* +* where xB is the vector of basic variables, xN is the vector of +* non-basic variables. +* +* The simplex table, which corresponds to the current basis, is: +* +* xB = [-inv(B) * N] * xN. (4) +* +* Therefore substituting xB from (4) to (3) we have: +* +* x = aB * [-inv(B) * N] * xN + aN * xN = +* (5) +* = rho * (-N) * xN + aN * xN = alfa * xN, +* +* where: +* +* rho = inv(B') * aB, (6) +* +* and +* +* alfa = aN + rho * (-N) (7) +* +* is the resultant row computed by the routine. */ + +int glp_transform_row(glp_prob *P, int len, int ind[], double val[]) +{ int i, j, k, m, n, t, lll, *iii; + double alfa, *a, *aB, *rho, *vvv; + if (!glp_bf_exists(P)) + xerror("glp_transform_row: basis factorization does not exist " + "\n"); + m = glp_get_num_rows(P); + n = glp_get_num_cols(P); + /* unpack the row to be transformed to the array a */ + a = xcalloc(1+n, sizeof(double)); + for (j = 1; j <= n; j++) a[j] = 0.0; + if (!(0 <= len && len <= n)) + xerror("glp_transform_row: len = %d; invalid row length\n", + len); + for (t = 1; t <= len; t++) + { j = ind[t]; + if (!(1 <= j && j <= n)) + xerror("glp_transform_row: ind[%d] = %d; column index out o" + "f range\n", t, j); + if (val[t] == 0.0) + xerror("glp_transform_row: val[%d] = 0; zero coefficient no" + "t allowed\n", t); + if (a[j] != 0.0) + xerror("glp_transform_row: ind[%d] = %d; duplicate column i" + "ndices not allowed\n", t, j); + a[j] = val[t]; + } + /* construct the vector aB */ + aB = xcalloc(1+m, sizeof(double)); + for (i = 1; i <= m; i++) + { k = glp_get_bhead(P, i); + /* xB[i] is k-th original variable */ + xassert(1 <= k && k <= m+n); + aB[i] = (k <= m ? 0.0 : a[k-m]); + } + /* solve the system B'*rho = aB to compute the vector rho */ + rho = aB, glp_btran(P, rho); + /* compute coefficients at non-basic auxiliary variables */ + len = 0; + for (i = 1; i <= m; i++) + { if (glp_get_row_stat(P, i) != GLP_BS) + { alfa = - rho[i]; + if (alfa != 0.0) + { len++; + ind[len] = i; + val[len] = alfa; + } + } + } + /* compute coefficients at non-basic structural variables */ + iii = xcalloc(1+m, sizeof(int)); + vvv = xcalloc(1+m, sizeof(double)); + for (j = 1; j <= n; j++) + { if (glp_get_col_stat(P, j) != GLP_BS) + { alfa = a[j]; + lll = glp_get_mat_col(P, j, iii, vvv); + for (t = 1; t <= lll; t++) alfa += vvv[t] * rho[iii[t]]; + if (alfa != 0.0) + { len++; + ind[len] = m+j; + val[len] = alfa; + } + } + } + xassert(len <= n); + xfree(iii); + xfree(vvv); + xfree(aB); + xfree(a); + return len; +} + +/*********************************************************************** +* NAME +* +* glp_transform_col - transform explicitly specified column +* +* SYNOPSIS +* +* int glp_transform_col(glp_prob *P, int len, int ind[], double val[]); +* +* DESCRIPTION +* +* The routine glp_transform_col performs the same operation as the +* routine glp_eval_tab_col with exception that the column to be +* transformed is specified explicitly as a sparse vector. +* +* The explicitly specified column may be thought as if it were added +* to the original system of equality constraints: +* +* x[1] = a[1,1]*x[m+1] + ... + a[1,n]*x[m+n] + a[1]*x +* x[2] = a[2,1]*x[m+1] + ... + a[2,n]*x[m+n] + a[2]*x (1) +* . . . . . . . . . . . . . . . +* x[m] = a[m,1]*x[m+1] + ... + a[m,n]*x[m+n] + a[m]*x +* +* where x[i] are auxiliary variables, x[m+j] are structural variables, +* x is a structural variable for the explicitly specified column, a[i] +* are constraint coefficients for x. +* +* On entry row indices and numerical values of non-zero elements of +* the column should be stored in locations ind[1], ..., ind[len] and +* val[1], ..., val[len], where len is the number of non-zero elements. +* +* This routine uses the system of equality constraints and the current +* basis in order to express the current basic variables through the +* structural variable x in (1) (as if the transformed column were added +* to the problem object and the variable x were non-basic), i.e. the +* resultant column has the form: +* +* xB[1] = ... + alfa[1]*x +* xB[2] = ... + alfa[2]*x (2) +* . . . . . . +* xB[m] = ... + alfa[m]*x +* +* where xB are basic (auxiliary and structural) variables, m is the +* number of rows in the problem object. +* +* On exit the routine stores indices and numerical values of non-zero +* elements of the resultant column (2) in locations ind[1], ..., +* ind[len'] and val[1], ..., val[len'], where 0 <= len' <= m is the +* number of non-zero element in the resultant column returned by the +* routine. Note that indices (numbers) of basic variables stored in +* the array ind correspond to original ordinal numbers of variables: +* indices 1 to m mean auxiliary variables and indices m+1 to m+n mean +* structural ones. +* +* RETURNS +* +* The routine returns len', which is the number of non-zero elements +* in the resultant column stored in the arrays ind and val. +* +* BACKGROUND +* +* The explicitly specified column (1) is transformed in the same way +* as any other column of the constraint matrix using the formula: +* +* alfa = inv(B) * a, (3) +* +* where alfa is the resultant column computed by the routine. */ + +int glp_transform_col(glp_prob *P, int len, int ind[], double val[]) +{ int i, m, t; + double *a, *alfa; + if (!glp_bf_exists(P)) + xerror("glp_transform_col: basis factorization does not exist " + "\n"); + m = glp_get_num_rows(P); + /* unpack the column to be transformed to the array a */ + a = xcalloc(1+m, sizeof(double)); + for (i = 1; i <= m; i++) a[i] = 0.0; + if (!(0 <= len && len <= m)) + xerror("glp_transform_col: len = %d; invalid column length\n", + len); + for (t = 1; t <= len; t++) + { i = ind[t]; + if (!(1 <= i && i <= m)) + xerror("glp_transform_col: ind[%d] = %d; row index out of r" + "ange\n", t, i); + if (val[t] == 0.0) + xerror("glp_transform_col: val[%d] = 0; zero coefficient no" + "t allowed\n", t); + if (a[i] != 0.0) + xerror("glp_transform_col: ind[%d] = %d; duplicate row indi" + "ces not allowed\n", t, i); + a[i] = val[t]; + } + /* solve the system B*a = alfa to compute the vector alfa */ + alfa = a, glp_ftran(P, alfa); + /* store resultant coefficients */ + len = 0; + for (i = 1; i <= m; i++) + { if (alfa[i] != 0.0) + { len++; + ind[len] = glp_get_bhead(P, i); + val[len] = alfa[i]; + } + } + xfree(a); + return len; +} + +/*********************************************************************** +* NAME +* +* glp_prim_rtest - perform primal ratio test +* +* SYNOPSIS +* +* int glp_prim_rtest(glp_prob *P, int len, const int ind[], +* const double val[], int dir, double eps); +* +* DESCRIPTION +* +* The routine glp_prim_rtest performs the primal ratio test using an +* explicitly specified column of the simplex table. +* +* The current basic solution associated with the LP problem object +* must be primal feasible. +* +* The explicitly specified column of the simplex table shows how the +* basic variables xB depend on some non-basic variable x (which is not +* necessarily presented in the problem object): +* +* xB[1] = ... + alfa[1] * x + ... +* xB[2] = ... + alfa[2] * x + ... (*) +* . . . . . . . . +* xB[m] = ... + alfa[m] * x + ... +* +* The column (*) is specifed on entry to the routine using the sparse +* format. Ordinal numbers of basic variables xB[i] should be placed in +* locations ind[1], ..., ind[len], where ordinal number 1 to m denote +* auxiliary variables, and ordinal numbers m+1 to m+n denote structural +* variables. The corresponding non-zero coefficients alfa[i] should be +* placed in locations val[1], ..., val[len]. The arrays ind and val are +* not changed on exit. +* +* The parameter dir specifies direction in which the variable x changes +* on entering the basis: +1 means increasing, -1 means decreasing. +* +* The parameter eps is an absolute tolerance (small positive number) +* used by the routine to skip small alfa[j] of the row (*). +* +* The routine determines which basic variable (among specified in +* ind[1], ..., ind[len]) should leave the basis in order to keep primal +* feasibility. +* +* RETURNS +* +* The routine glp_prim_rtest returns the index piv in the arrays ind +* and val corresponding to the pivot element chosen, 1 <= piv <= len. +* If the adjacent basic solution is primal unbounded and therefore the +* choice cannot be made, the routine returns zero. +* +* COMMENTS +* +* If the non-basic variable x is presented in the LP problem object, +* the column (*) can be computed with the routine glp_eval_tab_col; +* otherwise it can be computed with the routine glp_transform_col. */ + +int glp_prim_rtest(glp_prob *P, int len, const int ind[], + const double val[], int dir, double eps) +{ int k, m, n, piv, t, type, stat; + double alfa, big, beta, lb, ub, temp, teta; + if (glp_get_prim_stat(P) != GLP_FEAS) + xerror("glp_prim_rtest: basic solution is not primal feasible " + "\n"); + if (!(dir == +1 || dir == -1)) + xerror("glp_prim_rtest: dir = %d; invalid parameter\n", dir); + if (!(0.0 < eps && eps < 1.0)) + xerror("glp_prim_rtest: eps = %g; invalid parameter\n", eps); + m = glp_get_num_rows(P); + n = glp_get_num_cols(P); + /* initial settings */ + piv = 0, teta = DBL_MAX, big = 0.0; + /* walk through the entries of the specified column */ + for (t = 1; t <= len; t++) + { /* get the ordinal number of basic variable */ + k = ind[t]; + if (!(1 <= k && k <= m+n)) + xerror("glp_prim_rtest: ind[%d] = %d; variable number out o" + "f range\n", t, k); + /* determine type, bounds, status and primal value of basic + variable xB[i] = x[k] in the current basic solution */ + if (k <= m) + { type = glp_get_row_type(P, k); + lb = glp_get_row_lb(P, k); + ub = glp_get_row_ub(P, k); + stat = glp_get_row_stat(P, k); + beta = glp_get_row_prim(P, k); + } + else + { type = glp_get_col_type(P, k-m); + lb = glp_get_col_lb(P, k-m); + ub = glp_get_col_ub(P, k-m); + stat = glp_get_col_stat(P, k-m); + beta = glp_get_col_prim(P, k-m); + } + if (stat != GLP_BS) + xerror("glp_prim_rtest: ind[%d] = %d; non-basic variable no" + "t allowed\n", t, k); + /* determine influence coefficient at basic variable xB[i] + in the explicitly specified column and turn to the case of + increasing the variable x in order to simplify the program + logic */ + alfa = (dir > 0 ? + val[t] : - val[t]); + /* analyze main cases */ + if (type == GLP_FR) + { /* xB[i] is free variable */ + continue; + } + else if (type == GLP_LO) +lo: { /* xB[i] has an lower bound */ + if (alfa > - eps) continue; + temp = (lb - beta) / alfa; + } + else if (type == GLP_UP) +up: { /* xB[i] has an upper bound */ + if (alfa < + eps) continue; + temp = (ub - beta) / alfa; + } + else if (type == GLP_DB) + { /* xB[i] has both lower and upper bounds */ + if (alfa < 0.0) goto lo; else goto up; + } + else if (type == GLP_FX) + { /* xB[i] is fixed variable */ + if (- eps < alfa && alfa < + eps) continue; + temp = 0.0; + } + else + xassert(type != type); + /* if the value of the variable xB[i] violates its lower or + upper bound (slightly, because the current basis is assumed + to be primal feasible), temp is negative; we can think this + happens due to round-off errors and the value is exactly on + the bound; this allows replacing temp by zero */ + if (temp < 0.0) temp = 0.0; + /* apply the minimal ratio test */ + if (teta > temp || teta == temp && big < fabs(alfa)) + piv = t, teta = temp, big = fabs(alfa); + } + /* return index of the pivot element chosen */ + return piv; +} + +/*********************************************************************** +* NAME +* +* glp_dual_rtest - perform dual ratio test +* +* SYNOPSIS +* +* int glp_dual_rtest(glp_prob *P, int len, const int ind[], +* const double val[], int dir, double eps); +* +* DESCRIPTION +* +* The routine glp_dual_rtest performs the dual ratio test using an +* explicitly specified row of the simplex table. +* +* The current basic solution associated with the LP problem object +* must be dual feasible. +* +* The explicitly specified row of the simplex table is a linear form +* that shows how some basic variable x (which is not necessarily +* presented in the problem object) depends on non-basic variables xN: +* +* x = alfa[1] * xN[1] + alfa[2] * xN[2] + ... + alfa[n] * xN[n]. (*) +* +* The row (*) is specified on entry to the routine using the sparse +* format. Ordinal numbers of non-basic variables xN[j] should be placed +* in locations ind[1], ..., ind[len], where ordinal numbers 1 to m +* denote auxiliary variables, and ordinal numbers m+1 to m+n denote +* structural variables. The corresponding non-zero coefficients alfa[j] +* should be placed in locations val[1], ..., val[len]. The arrays ind +* and val are not changed on exit. +* +* The parameter dir specifies direction in which the variable x changes +* on leaving the basis: +1 means that x goes to its lower bound, and -1 +* means that x goes to its upper bound. +* +* The parameter eps is an absolute tolerance (small positive number) +* used by the routine to skip small alfa[j] of the row (*). +* +* The routine determines which non-basic variable (among specified in +* ind[1], ..., ind[len]) should enter the basis in order to keep dual +* feasibility. +* +* RETURNS +* +* The routine glp_dual_rtest returns the index piv in the arrays ind +* and val corresponding to the pivot element chosen, 1 <= piv <= len. +* If the adjacent basic solution is dual unbounded and therefore the +* choice cannot be made, the routine returns zero. +* +* COMMENTS +* +* If the basic variable x is presented in the LP problem object, the +* row (*) can be computed with the routine glp_eval_tab_row; otherwise +* it can be computed with the routine glp_transform_row. */ + +int glp_dual_rtest(glp_prob *P, int len, const int ind[], + const double val[], int dir, double eps) +{ int k, m, n, piv, t, stat; + double alfa, big, cost, obj, temp, teta; + if (glp_get_dual_stat(P) != GLP_FEAS) + xerror("glp_dual_rtest: basic solution is not dual feasible\n") + ; + if (!(dir == +1 || dir == -1)) + xerror("glp_dual_rtest: dir = %d; invalid parameter\n", dir); + if (!(0.0 < eps && eps < 1.0)) + xerror("glp_dual_rtest: eps = %g; invalid parameter\n", eps); + m = glp_get_num_rows(P); + n = glp_get_num_cols(P); + /* take into account optimization direction */ + obj = (glp_get_obj_dir(P) == GLP_MIN ? +1.0 : -1.0); + /* initial settings */ + piv = 0, teta = DBL_MAX, big = 0.0; + /* walk through the entries of the specified row */ + for (t = 1; t <= len; t++) + { /* get ordinal number of non-basic variable */ + k = ind[t]; + if (!(1 <= k && k <= m+n)) + xerror("glp_dual_rtest: ind[%d] = %d; variable number out o" + "f range\n", t, k); + /* determine status and reduced cost of non-basic variable + x[k] = xN[j] in the current basic solution */ + if (k <= m) + { stat = glp_get_row_stat(P, k); + cost = glp_get_row_dual(P, k); + } + else + { stat = glp_get_col_stat(P, k-m); + cost = glp_get_col_dual(P, k-m); + } + if (stat == GLP_BS) + xerror("glp_dual_rtest: ind[%d] = %d; basic variable not al" + "lowed\n", t, k); + /* determine influence coefficient at non-basic variable xN[j] + in the explicitly specified row and turn to the case of + increasing the variable x in order to simplify the program + logic */ + alfa = (dir > 0 ? + val[t] : - val[t]); + /* analyze main cases */ + if (stat == GLP_NL) + { /* xN[j] is on its lower bound */ + if (alfa < + eps) continue; + temp = (obj * cost) / alfa; + } + else if (stat == GLP_NU) + { /* xN[j] is on its upper bound */ + if (alfa > - eps) continue; + temp = (obj * cost) / alfa; + } + else if (stat == GLP_NF) + { /* xN[j] is non-basic free variable */ + if (- eps < alfa && alfa < + eps) continue; + temp = 0.0; + } + else if (stat == GLP_NS) + { /* xN[j] is non-basic fixed variable */ + continue; + } + else + xassert(stat != stat); + /* if the reduced cost of the variable xN[j] violates its zero + bound (slightly, because the current basis is assumed to be + dual feasible), temp is negative; we can think this happens + due to round-off errors and the reduced cost is exact zero; + this allows replacing temp by zero */ + if (temp < 0.0) temp = 0.0; + /* apply the minimal ratio test */ + if (teta > temp || teta == temp && big < fabs(alfa)) + piv = t, teta = temp, big = fabs(alfa); + } + /* return index of the pivot element chosen */ + return piv; +} + +/*********************************************************************** +* NAME +* +* glp_analyze_row - simulate one iteration of dual simplex method +* +* SYNOPSIS +* +* int glp_analyze_row(glp_prob *P, int len, const int ind[], +* const double val[], int type, double rhs, double eps, int *piv, +* double *x, double *dx, double *y, double *dy, double *dz); +* +* DESCRIPTION +* +* Let the current basis be optimal or dual feasible, and there be +* specified a row (constraint), which is violated by the current basic +* solution. The routine glp_analyze_row simulates one iteration of the +* dual simplex method to determine some information on the adjacent +* basis (see below), where the specified row becomes active constraint +* (i.e. its auxiliary variable becomes non-basic). +* +* The current basic solution associated with the problem object passed +* to the routine must be dual feasible, and its primal components must +* be defined. +* +* The row to be analyzed must be previously transformed either with +* the routine glp_eval_tab_row (if the row is in the problem object) +* or with the routine glp_transform_row (if the row is external, i.e. +* not in the problem object). This is needed to express the row only +* through (auxiliary and structural) variables, which are non-basic in +* the current basis: +* +* y = alfa[1] * xN[1] + alfa[2] * xN[2] + ... + alfa[n] * xN[n], +* +* where y is an auxiliary variable of the row, alfa[j] is an influence +* coefficient, xN[j] is a non-basic variable. +* +* The row is passed to the routine in sparse format. Ordinal numbers +* of non-basic variables are stored in locations ind[1], ..., ind[len], +* where numbers 1 to m denote auxiliary variables while numbers m+1 to +* m+n denote structural variables. Corresponding non-zero coefficients +* alfa[j] are stored in locations val[1], ..., val[len]. The arrays +* ind and val are ot changed on exit. +* +* The parameters type and rhs specify the row type and its right-hand +* side as follows: +* +* type = GLP_LO: y = sum alfa[j] * xN[j] >= rhs +* +* type = GLP_UP: y = sum alfa[j] * xN[j] <= rhs +* +* The parameter eps is an absolute tolerance (small positive number) +* used by the routine to skip small coefficients alfa[j] on performing +* the dual ratio test. +* +* If the operation was successful, the routine stores the following +* information to corresponding location (if some parameter is NULL, +* its value is not stored): +* +* piv index in the array ind and val, 1 <= piv <= len, determining +* the non-basic variable, which would enter the adjacent basis; +* +* x value of the non-basic variable in the current basis; +* +* dx difference between values of the non-basic variable in the +* adjacent and current bases, dx = x.new - x.old; +* +* y value of the row (i.e. of its auxiliary variable) in the +* current basis; +* +* dy difference between values of the row in the adjacent and +* current bases, dy = y.new - y.old; +* +* dz difference between values of the objective function in the +* adjacent and current bases, dz = z.new - z.old. Note that in +* case of minimization dz >= 0, and in case of maximization +* dz <= 0, i.e. in the adjacent basis the objective function +* always gets worse (degrades). */ + +int _glp_analyze_row(glp_prob *P, int len, const int ind[], + const double val[], int type, double rhs, double eps, int *_piv, + double *_x, double *_dx, double *_y, double *_dy, double *_dz) +{ int t, k, dir, piv, ret = 0; + double x, dx, y, dy, dz; + if (P->pbs_stat == GLP_UNDEF) + xerror("glp_analyze_row: primal basic solution components are " + "undefined\n"); + if (P->dbs_stat != GLP_FEAS) + xerror("glp_analyze_row: basic solution is not dual feasible\n" + ); + /* compute the row value y = sum alfa[j] * xN[j] in the current + basis */ + if (!(0 <= len && len <= P->n)) + xerror("glp_analyze_row: len = %d; invalid row length\n", len); + y = 0.0; + for (t = 1; t <= len; t++) + { /* determine value of x[k] = xN[j] in the current basis */ + k = ind[t]; + if (!(1 <= k && k <= P->m+P->n)) + xerror("glp_analyze_row: ind[%d] = %d; row/column index out" + " of range\n", t, k); + if (k <= P->m) + { /* x[k] is auxiliary variable */ + if (P->row[k]->stat == GLP_BS) + xerror("glp_analyze_row: ind[%d] = %d; basic auxiliary v" + "ariable is not allowed\n", t, k); + x = P->row[k]->prim; + } + else + { /* x[k] is structural variable */ + if (P->col[k-P->m]->stat == GLP_BS) + xerror("glp_analyze_row: ind[%d] = %d; basic structural " + "variable is not allowed\n", t, k); + x = P->col[k-P->m]->prim; + } + y += val[t] * x; + } + /* check if the row is primal infeasible in the current basis, + i.e. the constraint is violated at the current point */ + if (type == GLP_LO) + { if (y >= rhs) + { /* the constraint is not violated */ + ret = 1; + goto done; + } + /* in the adjacent basis y goes to its lower bound */ + dir = +1; + } + else if (type == GLP_UP) + { if (y <= rhs) + { /* the constraint is not violated */ + ret = 1; + goto done; + } + /* in the adjacent basis y goes to its upper bound */ + dir = -1; + } + else + xerror("glp_analyze_row: type = %d; invalid parameter\n", + type); + /* compute dy = y.new - y.old */ + dy = rhs - y; + /* perform dual ratio test to determine which non-basic variable + should enter the adjacent basis to keep it dual feasible */ + piv = glp_dual_rtest(P, len, ind, val, dir, eps); + if (piv == 0) + { /* no dual feasible adjacent basis exists */ + ret = 2; + goto done; + } + /* non-basic variable x[k] = xN[j] should enter the basis */ + k = ind[piv]; + xassert(1 <= k && k <= P->m+P->n); + /* determine its value in the current basis */ + if (k <= P->m) + x = P->row[k]->prim; + else + x = P->col[k-P->m]->prim; + /* compute dx = x.new - x.old = dy / alfa[j] */ + xassert(val[piv] != 0.0); + dx = dy / val[piv]; + /* compute dz = z.new - z.old = d[j] * dx, where d[j] is reduced + cost of xN[j] in the current basis */ + if (k <= P->m) + dz = P->row[k]->dual * dx; + else + dz = P->col[k-P->m]->dual * dx; + /* store the analysis results */ + if (_piv != NULL) *_piv = piv; + if (_x != NULL) *_x = x; + if (_dx != NULL) *_dx = dx; + if (_y != NULL) *_y = y; + if (_dy != NULL) *_dy = dy; + if (_dz != NULL) *_dz = dz; +done: return ret; +} + +#if 0 +int main(void) +{ /* example program for the routine glp_analyze_row */ + glp_prob *P; + glp_smcp parm; + int i, k, len, piv, ret, ind[1+100]; + double rhs, x, dx, y, dy, dz, val[1+100]; + P = glp_create_prob(); + /* read plan.mps (see glpk/examples) */ + ret = glp_read_mps(P, GLP_MPS_DECK, NULL, "plan.mps"); + glp_assert(ret == 0); + /* and solve it to optimality */ + ret = glp_simplex(P, NULL); + glp_assert(ret == 0); + glp_assert(glp_get_status(P) == GLP_OPT); + /* the optimal objective value is 296.217 */ + /* we would like to know what happens if we would add a new row + (constraint) to plan.mps: + .01 * bin1 + .01 * bin2 + .02 * bin4 + .02 * bin5 <= 12 */ + /* first, we specify this new row */ + glp_create_index(P); + len = 0; + ind[++len] = glp_find_col(P, "BIN1"), val[len] = .01; + ind[++len] = glp_find_col(P, "BIN2"), val[len] = .01; + ind[++len] = glp_find_col(P, "BIN4"), val[len] = .02; + ind[++len] = glp_find_col(P, "BIN5"), val[len] = .02; + rhs = 12; + /* then we can compute value of the row (i.e. of its auxiliary + variable) in the current basis to see if the constraint is + violated */ + y = 0.0; + for (k = 1; k <= len; k++) + y += val[k] * glp_get_col_prim(P, ind[k]); + glp_printf("y = %g\n", y); + /* this prints y = 15.1372, so the constraint is violated, since + we require that y <= rhs = 12 */ + /* now we transform the row to express it only through non-basic + (auxiliary and artificial) variables */ + len = glp_transform_row(P, len, ind, val); + /* finally, we simulate one step of the dual simplex method to + obtain necessary information for the adjacent basis */ + ret = _glp_analyze_row(P, len, ind, val, GLP_UP, rhs, 1e-9, &piv, + &x, &dx, &y, &dy, &dz); + glp_assert(ret == 0); + glp_printf("k = %d, x = %g; dx = %g; y = %g; dy = %g; dz = %g\n", + ind[piv], x, dx, y, dy, dz); + /* this prints dz = 5.64418 and means that in the adjacent basis + the objective function would be 296.217 + 5.64418 = 301.861 */ + /* now we actually include the row into the problem object; note + that the arrays ind and val are clobbered, so we need to build + them once again */ + len = 0; + ind[++len] = glp_find_col(P, "BIN1"), val[len] = .01; + ind[++len] = glp_find_col(P, "BIN2"), val[len] = .01; + ind[++len] = glp_find_col(P, "BIN4"), val[len] = .02; + ind[++len] = glp_find_col(P, "BIN5"), val[len] = .02; + rhs = 12; + i = glp_add_rows(P, 1); + glp_set_row_bnds(P, i, GLP_UP, 0, rhs); + glp_set_mat_row(P, i, len, ind, val); + /* and perform one dual simplex iteration */ + glp_init_smcp(&parm); + parm.meth = GLP_DUAL; + parm.it_lim = 1; + glp_simplex(P, &parm); + /* the current objective value is 301.861 */ + return 0; +} +#endif + +/*********************************************************************** +* NAME +* +* glp_analyze_bound - analyze active bound of non-basic variable +* +* SYNOPSIS +* +* void glp_analyze_bound(glp_prob *P, int k, double *limit1, int *var1, +* double *limit2, int *var2); +* +* DESCRIPTION +* +* The routine glp_analyze_bound analyzes the effect of varying the +* active bound of specified non-basic variable. +* +* The non-basic variable is specified by the parameter k, where +* 1 <= k <= m means auxiliary variable of corresponding row while +* m+1 <= k <= m+n means structural variable (column). +* +* Note that the current basic solution must be optimal, and the basis +* factorization must exist. +* +* Results of the analysis have the following meaning. +* +* value1 is the minimal value of the active bound, at which the basis +* still remains primal feasible and thus optimal. -DBL_MAX means that +* the active bound has no lower limit. +* +* var1 is the ordinal number of an auxiliary (1 to m) or structural +* (m+1 to n) basic variable, which reaches its bound first and thereby +* limits further decreasing the active bound being analyzed. +* if value1 = -DBL_MAX, var1 is set to 0. +* +* value2 is the maximal value of the active bound, at which the basis +* still remains primal feasible and thus optimal. +DBL_MAX means that +* the active bound has no upper limit. +* +* var2 is the ordinal number of an auxiliary (1 to m) or structural +* (m+1 to n) basic variable, which reaches its bound first and thereby +* limits further increasing the active bound being analyzed. +* if value2 = +DBL_MAX, var2 is set to 0. */ + +void glp_analyze_bound(glp_prob *P, int k, double *value1, int *var1, + double *value2, int *var2) +{ GLPROW *row; + GLPCOL *col; + int m, n, stat, kase, p, len, piv, *ind; + double x, new_x, ll, uu, xx, delta, *val; + /* sanity checks */ + if (P == NULL || P->magic != GLP_PROB_MAGIC) + xerror("glp_analyze_bound: P = %p; invalid problem object\n", + P); + m = P->m, n = P->n; + if (!(P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS)) + xerror("glp_analyze_bound: optimal basic solution required\n"); + if (!(m == 0 || P->valid)) + xerror("glp_analyze_bound: basis factorization required\n"); + if (!(1 <= k && k <= m+n)) + xerror("glp_analyze_bound: k = %d; variable number out of rang" + "e\n", k); + /* retrieve information about the specified non-basic variable + x[k] whose active bound is to be analyzed */ + if (k <= m) + { row = P->row[k]; + stat = row->stat; + x = row->prim; + } + else + { col = P->col[k-m]; + stat = col->stat; + x = col->prim; + } + if (stat == GLP_BS) + xerror("glp_analyze_bound: k = %d; basic variable not allowed " + "\n", k); + /* allocate working arrays */ + ind = xcalloc(1+m, sizeof(int)); + val = xcalloc(1+m, sizeof(double)); + /* compute column of the simplex table corresponding to the + non-basic variable x[k] */ + len = glp_eval_tab_col(P, k, ind, val); + xassert(0 <= len && len <= m); + /* perform analysis */ + for (kase = -1; kase <= +1; kase += 2) + { /* kase < 0 means active bound of x[k] is decreasing; + kase > 0 means active bound of x[k] is increasing */ + /* use the primal ratio test to determine some basic variable + x[p] which reaches its bound first */ + piv = glp_prim_rtest(P, len, ind, val, kase, 1e-9); + if (piv == 0) + { /* nothing limits changing the active bound of x[k] */ + p = 0; + new_x = (kase < 0 ? -DBL_MAX : +DBL_MAX); + goto store; + } + /* basic variable x[p] limits changing the active bound of + x[k]; determine its value in the current basis */ + xassert(1 <= piv && piv <= len); + p = ind[piv]; + if (p <= m) + { row = P->row[p]; + ll = glp_get_row_lb(P, row->i); + uu = glp_get_row_ub(P, row->i); + stat = row->stat; + xx = row->prim; + } + else + { col = P->col[p-m]; + ll = glp_get_col_lb(P, col->j); + uu = glp_get_col_ub(P, col->j); + stat = col->stat; + xx = col->prim; + } + xassert(stat == GLP_BS); + /* determine delta x[p] = bound of x[p] - value of x[p] */ + if (kase < 0 && val[piv] > 0.0 || + kase > 0 && val[piv] < 0.0) + { /* delta x[p] < 0, so x[p] goes toward its lower bound */ + xassert(ll != -DBL_MAX); + delta = ll - xx; + } + else + { /* delta x[p] > 0, so x[p] goes toward its upper bound */ + xassert(uu != +DBL_MAX); + delta = uu - xx; + } + /* delta x[p] = alfa[p,k] * delta x[k], so new x[k] = x[k] + + delta x[k] = x[k] + delta x[p] / alfa[p,k] is the value of + x[k] in the adjacent basis */ + xassert(val[piv] != 0.0); + new_x = x + delta / val[piv]; +store: /* store analysis results */ + if (kase < 0) + { if (value1 != NULL) *value1 = new_x; + if (var1 != NULL) *var1 = p; + } + else + { if (value2 != NULL) *value2 = new_x; + if (var2 != NULL) *var2 = p; + } + } + /* free working arrays */ + xfree(ind); + xfree(val); + return; +} + +/*********************************************************************** +* NAME +* +* glp_analyze_coef - analyze objective coefficient at basic variable +* +* SYNOPSIS +* +* void glp_analyze_coef(glp_prob *P, int k, double *coef1, int *var1, +* double *value1, double *coef2, int *var2, double *value2); +* +* DESCRIPTION +* +* The routine glp_analyze_coef analyzes the effect of varying the +* objective coefficient at specified basic variable. +* +* The basic variable is specified by the parameter k, where +* 1 <= k <= m means auxiliary variable of corresponding row while +* m+1 <= k <= m+n means structural variable (column). +* +* Note that the current basic solution must be optimal, and the basis +* factorization must exist. +* +* Results of the analysis have the following meaning. +* +* coef1 is the minimal value of the objective coefficient, at which +* the basis still remains dual feasible and thus optimal. -DBL_MAX +* means that the objective coefficient has no lower limit. +* +* var1 is the ordinal number of an auxiliary (1 to m) or structural +* (m+1 to n) non-basic variable, whose reduced cost reaches its zero +* bound first and thereby limits further decreasing the objective +* coefficient being analyzed. If coef1 = -DBL_MAX, var1 is set to 0. +* +* value1 is value of the basic variable being analyzed in an adjacent +* basis, which is defined as follows. Let the objective coefficient +* reaches its minimal value (coef1) and continues decreasing. Then the +* reduced cost of the limiting non-basic variable (var1) becomes dual +* infeasible and the current basis becomes non-optimal that forces the +* limiting non-basic variable to enter the basis replacing there some +* basic variable that leaves the basis to keep primal feasibility. +* Should note that on determining the adjacent basis current bounds +* of the basic variable being analyzed are ignored as if it were free +* (unbounded) variable, so it cannot leave the basis. It may happen +* that no dual feasible adjacent basis exists, in which case value1 is +* set to -DBL_MAX or +DBL_MAX. +* +* coef2 is the maximal value of the objective coefficient, at which +* the basis still remains dual feasible and thus optimal. +DBL_MAX +* means that the objective coefficient has no upper limit. +* +* var2 is the ordinal number of an auxiliary (1 to m) or structural +* (m+1 to n) non-basic variable, whose reduced cost reaches its zero +* bound first and thereby limits further increasing the objective +* coefficient being analyzed. If coef2 = +DBL_MAX, var2 is set to 0. +* +* value2 is value of the basic variable being analyzed in an adjacent +* basis, which is defined exactly in the same way as value1 above with +* exception that now the objective coefficient is increasing. */ + +void glp_analyze_coef(glp_prob *P, int k, double *coef1, int *var1, + double *value1, double *coef2, int *var2, double *value2) +{ GLPROW *row; GLPCOL *col; + int m, n, type, stat, kase, p, q, dir, clen, cpiv, rlen, rpiv, + *cind, *rind; + double lb, ub, coef, x, lim_coef, new_x, d, delta, ll, uu, xx, + *rval, *cval; + /* sanity checks */ + if (P == NULL || P->magic != GLP_PROB_MAGIC) + xerror("glp_analyze_coef: P = %p; invalid problem object\n", + P); + m = P->m, n = P->n; + if (!(P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS)) + xerror("glp_analyze_coef: optimal basic solution required\n"); + if (!(m == 0 || P->valid)) + xerror("glp_analyze_coef: basis factorization required\n"); + if (!(1 <= k && k <= m+n)) + xerror("glp_analyze_coef: k = %d; variable number out of range" + "\n", k); + /* retrieve information about the specified basic variable x[k] + whose objective coefficient c[k] is to be analyzed */ + if (k <= m) + { row = P->row[k]; + type = row->type; + lb = row->lb; + ub = row->ub; + coef = 0.0; + stat = row->stat; + x = row->prim; + } + else + { col = P->col[k-m]; + type = col->type; + lb = col->lb; + ub = col->ub; + coef = col->coef; + stat = col->stat; + x = col->prim; + } + if (stat != GLP_BS) + xerror("glp_analyze_coef: k = %d; non-basic variable not allow" + "ed\n", k); + /* allocate working arrays */ + cind = xcalloc(1+m, sizeof(int)); + cval = xcalloc(1+m, sizeof(double)); + rind = xcalloc(1+n, sizeof(int)); + rval = xcalloc(1+n, sizeof(double)); + /* compute row of the simplex table corresponding to the basic + variable x[k] */ + rlen = glp_eval_tab_row(P, k, rind, rval); + xassert(0 <= rlen && rlen <= n); + /* perform analysis */ + for (kase = -1; kase <= +1; kase += 2) + { /* kase < 0 means objective coefficient c[k] is decreasing; + kase > 0 means objective coefficient c[k] is increasing */ + /* note that decreasing c[k] is equivalent to increasing dual + variable lambda[k] and vice versa; we need to correctly set + the dir flag as required by the routine glp_dual_rtest */ + if (P->dir == GLP_MIN) + dir = - kase; + else if (P->dir == GLP_MAX) + dir = + kase; + else + xassert(P != P); + /* use the dual ratio test to determine non-basic variable + x[q] whose reduced cost d[q] reaches zero bound first */ + rpiv = glp_dual_rtest(P, rlen, rind, rval, dir, 1e-9); + if (rpiv == 0) + { /* nothing limits changing c[k] */ + lim_coef = (kase < 0 ? -DBL_MAX : +DBL_MAX); + q = 0; + /* x[k] keeps its current value */ + new_x = x; + goto store; + } + /* non-basic variable x[q] limits changing coefficient c[k]; + determine its status and reduced cost d[k] in the current + basis */ + xassert(1 <= rpiv && rpiv <= rlen); + q = rind[rpiv]; + xassert(1 <= q && q <= m+n); + if (q <= m) + { row = P->row[q]; + stat = row->stat; + d = row->dual; + } + else + { col = P->col[q-m]; + stat = col->stat; + d = col->dual; + } + /* note that delta d[q] = new d[q] - d[q] = - d[q], because + new d[q] = 0; delta d[q] = alfa[k,q] * delta c[k], so + delta c[k] = delta d[q] / alfa[k,q] = - d[q] / alfa[k,q] */ + xassert(rval[rpiv] != 0.0); + delta = - d / rval[rpiv]; + /* compute new c[k] = c[k] + delta c[k], which is the limiting + value of the objective coefficient c[k] */ + lim_coef = coef + delta; + /* let c[k] continue decreasing/increasing that makes d[q] + dual infeasible and forces x[q] to enter the basis; + to perform the primal ratio test we need to know in which + direction x[q] changes on entering the basis; we determine + that analyzing the sign of delta d[q] (see above), since + d[q] may be close to zero having wrong sign */ + /* let, for simplicity, the problem is minimization */ + if (kase < 0 && rval[rpiv] > 0.0 || + kase > 0 && rval[rpiv] < 0.0) + { /* delta d[q] < 0, so d[q] being non-negative will become + negative, so x[q] will increase */ + dir = +1; + } + else + { /* delta d[q] > 0, so d[q] being non-positive will become + positive, so x[q] will decrease */ + dir = -1; + } + /* if the problem is maximization, correct the direction */ + if (P->dir == GLP_MAX) dir = - dir; + /* check that we didn't make a silly mistake */ + if (dir > 0) + xassert(stat == GLP_NL || stat == GLP_NF); + else + xassert(stat == GLP_NU || stat == GLP_NF); + /* compute column of the simplex table corresponding to the + non-basic variable x[q] */ + clen = glp_eval_tab_col(P, q, cind, cval); + /* make x[k] temporarily free (unbounded) */ + if (k <= m) + { row = P->row[k]; + row->type = GLP_FR; + row->lb = row->ub = 0.0; + } + else + { col = P->col[k-m]; + col->type = GLP_FR; + col->lb = col->ub = 0.0; + } + /* use the primal ratio test to determine some basic variable + which leaves the basis */ + cpiv = glp_prim_rtest(P, clen, cind, cval, dir, 1e-9); + /* restore original bounds of the basic variable x[k] */ + if (k <= m) + { row = P->row[k]; + row->type = type; + row->lb = lb, row->ub = ub; + } + else + { col = P->col[k-m]; + col->type = type; + col->lb = lb, col->ub = ub; + } + if (cpiv == 0) + { /* non-basic variable x[q] can change unlimitedly */ + if (dir < 0 && rval[rpiv] > 0.0 || + dir > 0 && rval[rpiv] < 0.0) + { /* delta x[k] = alfa[k,q] * delta x[q] < 0 */ + new_x = -DBL_MAX; + } + else + { /* delta x[k] = alfa[k,q] * delta x[q] > 0 */ + new_x = +DBL_MAX; + } + goto store; + } + /* some basic variable x[p] limits changing non-basic variable + x[q] in the adjacent basis */ + xassert(1 <= cpiv && cpiv <= clen); + p = cind[cpiv]; + xassert(1 <= p && p <= m+n); + xassert(p != k); + if (p <= m) + { row = P->row[p]; + xassert(row->stat == GLP_BS); + ll = glp_get_row_lb(P, row->i); + uu = glp_get_row_ub(P, row->i); + xx = row->prim; + } + else + { col = P->col[p-m]; + xassert(col->stat == GLP_BS); + ll = glp_get_col_lb(P, col->j); + uu = glp_get_col_ub(P, col->j); + xx = col->prim; + } + /* determine delta x[p] = new x[p] - x[p] */ + if (dir < 0 && cval[cpiv] > 0.0 || + dir > 0 && cval[cpiv] < 0.0) + { /* delta x[p] < 0, so x[p] goes toward its lower bound */ + xassert(ll != -DBL_MAX); + delta = ll - xx; + } + else + { /* delta x[p] > 0, so x[p] goes toward its upper bound */ + xassert(uu != +DBL_MAX); + delta = uu - xx; + } + /* compute new x[k] = x[k] + alfa[k,q] * delta x[q], where + delta x[q] = delta x[p] / alfa[p,q] */ + xassert(cval[cpiv] != 0.0); + new_x = x + (rval[rpiv] / cval[cpiv]) * delta; +store: /* store analysis results */ + if (kase < 0) + { if (coef1 != NULL) *coef1 = lim_coef; + if (var1 != NULL) *var1 = q; + if (value1 != NULL) *value1 = new_x; + } + else + { if (coef2 != NULL) *coef2 = lim_coef; + if (var2 != NULL) *var2 = q; + if (value2 != NULL) *value2 = new_x; + } + } + /* free working arrays */ + xfree(cind); + xfree(cval); + xfree(rind); + xfree(rval); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi13.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi13.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,702 @@ +/* glpapi13.c (branch-and-bound interface routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* NAME +* +* glp_ios_reason - determine reason for calling the callback routine +* +* SYNOPSIS +* +* glp_ios_reason(glp_tree *tree); +* +* RETURNS +* +* The routine glp_ios_reason returns a code, which indicates why the +* user-defined callback routine is being called. */ + +int glp_ios_reason(glp_tree *tree) +{ return + tree->reason; +} + +/*********************************************************************** +* NAME +* +* glp_ios_get_prob - access the problem object +* +* SYNOPSIS +* +* glp_prob *glp_ios_get_prob(glp_tree *tree); +* +* DESCRIPTION +* +* The routine glp_ios_get_prob can be called from the user-defined +* callback routine to access the problem object, which is used by the +* MIP solver. It is the original problem object passed to the routine +* glp_intopt if the MIP presolver is not used; otherwise it is an +* internal problem object built by the presolver. If the current +* subproblem exists, LP segment of the problem object corresponds to +* its LP relaxation. +* +* RETURNS +* +* The routine glp_ios_get_prob returns a pointer to the problem object +* used by the MIP solver. */ + +glp_prob *glp_ios_get_prob(glp_tree *tree) +{ return + tree->mip; +} + +/*********************************************************************** +* NAME +* +* glp_ios_tree_size - determine size of the branch-and-bound tree +* +* SYNOPSIS +* +* void glp_ios_tree_size(glp_tree *tree, int *a_cnt, int *n_cnt, +* int *t_cnt); +* +* DESCRIPTION +* +* The routine glp_ios_tree_size stores the following three counts which +* characterize the current size of the branch-and-bound tree: +* +* a_cnt is the current number of active nodes, i.e. the current size of +* the active list; +* +* n_cnt is the current number of all (active and inactive) nodes; +* +* t_cnt is the total number of nodes including those which have been +* already removed from the tree. This count is increased whenever +* a new node appears in the tree and never decreased. +* +* If some of the parameters a_cnt, n_cnt, t_cnt is a null pointer, the +* corresponding count is not stored. */ + +void glp_ios_tree_size(glp_tree *tree, int *a_cnt, int *n_cnt, + int *t_cnt) +{ if (a_cnt != NULL) *a_cnt = tree->a_cnt; + if (n_cnt != NULL) *n_cnt = tree->n_cnt; + if (t_cnt != NULL) *t_cnt = tree->t_cnt; + return; +} + +/*********************************************************************** +* NAME +* +* glp_ios_curr_node - determine current active subproblem +* +* SYNOPSIS +* +* int glp_ios_curr_node(glp_tree *tree); +* +* RETURNS +* +* The routine glp_ios_curr_node returns the reference number of the +* current active subproblem. However, if the current subproblem does +* not exist, the routine returns zero. */ + +int glp_ios_curr_node(glp_tree *tree) +{ IOSNPD *node; + /* obtain pointer to the current subproblem */ + node = tree->curr; + /* return its reference number */ + return node == NULL ? 0 : node->p; +} + +/*********************************************************************** +* NAME +* +* glp_ios_next_node - determine next active subproblem +* +* SYNOPSIS +* +* int glp_ios_next_node(glp_tree *tree, int p); +* +* RETURNS +* +* If the parameter p is zero, the routine glp_ios_next_node returns +* the reference number of the first active subproblem. However, if the +* tree is empty, zero is returned. +* +* If the parameter p is not zero, it must specify the reference number +* of some active subproblem, in which case the routine returns the +* reference number of the next active subproblem. However, if there is +* no next active subproblem in the list, zero is returned. +* +* All subproblems in the active list are ordered chronologically, i.e. +* subproblem A precedes subproblem B if A was created before B. */ + +int glp_ios_next_node(glp_tree *tree, int p) +{ IOSNPD *node; + if (p == 0) + { /* obtain pointer to the first active subproblem */ + node = tree->head; + } + else + { /* obtain pointer to the specified subproblem */ + if (!(1 <= p && p <= tree->nslots)) +err: xerror("glp_ios_next_node: p = %d; invalid subproblem refer" + "ence number\n", p); + node = tree->slot[p].node; + if (node == NULL) goto err; + /* the specified subproblem must be active */ + if (node->count != 0) + xerror("glp_ios_next_node: p = %d; subproblem not in the ac" + "tive list\n", p); + /* obtain pointer to the next active subproblem */ + node = node->next; + } + /* return the reference number */ + return node == NULL ? 0 : node->p; +} + +/*********************************************************************** +* NAME +* +* glp_ios_prev_node - determine previous active subproblem +* +* SYNOPSIS +* +* int glp_ios_prev_node(glp_tree *tree, int p); +* +* RETURNS +* +* If the parameter p is zero, the routine glp_ios_prev_node returns +* the reference number of the last active subproblem. However, if the +* tree is empty, zero is returned. +* +* If the parameter p is not zero, it must specify the reference number +* of some active subproblem, in which case the routine returns the +* reference number of the previous active subproblem. However, if there +* is no previous active subproblem in the list, zero is returned. +* +* All subproblems in the active list are ordered chronologically, i.e. +* subproblem A precedes subproblem B if A was created before B. */ + +int glp_ios_prev_node(glp_tree *tree, int p) +{ IOSNPD *node; + if (p == 0) + { /* obtain pointer to the last active subproblem */ + node = tree->tail; + } + else + { /* obtain pointer to the specified subproblem */ + if (!(1 <= p && p <= tree->nslots)) +err: xerror("glp_ios_prev_node: p = %d; invalid subproblem refer" + "ence number\n", p); + node = tree->slot[p].node; + if (node == NULL) goto err; + /* the specified subproblem must be active */ + if (node->count != 0) + xerror("glp_ios_prev_node: p = %d; subproblem not in the ac" + "tive list\n", p); + /* obtain pointer to the previous active subproblem */ + node = node->prev; + } + /* return the reference number */ + return node == NULL ? 0 : node->p; +} + +/*********************************************************************** +* NAME +* +* glp_ios_up_node - determine parent subproblem +* +* SYNOPSIS +* +* int glp_ios_up_node(glp_tree *tree, int p); +* +* RETURNS +* +* The parameter p must specify the reference number of some (active or +* inactive) subproblem, in which case the routine iet_get_up_node +* returns the reference number of its parent subproblem. However, if +* the specified subproblem is the root of the tree and, therefore, has +* no parent, the routine returns zero. */ + +int glp_ios_up_node(glp_tree *tree, int p) +{ IOSNPD *node; + /* obtain pointer to the specified subproblem */ + if (!(1 <= p && p <= tree->nslots)) +err: xerror("glp_ios_up_node: p = %d; invalid subproblem reference " + "number\n", p); + node = tree->slot[p].node; + if (node == NULL) goto err; + /* obtain pointer to the parent subproblem */ + node = node->up; + /* return the reference number */ + return node == NULL ? 0 : node->p; +} + +/*********************************************************************** +* NAME +* +* glp_ios_node_level - determine subproblem level +* +* SYNOPSIS +* +* int glp_ios_node_level(glp_tree *tree, int p); +* +* RETURNS +* +* The routine glp_ios_node_level returns the level of the subproblem, +* whose reference number is p, in the branch-and-bound tree. (The root +* subproblem has level 0, and the level of any other subproblem is the +* level of its parent plus one.) */ + +int glp_ios_node_level(glp_tree *tree, int p) +{ IOSNPD *node; + /* obtain pointer to the specified subproblem */ + if (!(1 <= p && p <= tree->nslots)) +err: xerror("glp_ios_node_level: p = %d; invalid subproblem referen" + "ce number\n", p); + node = tree->slot[p].node; + if (node == NULL) goto err; + /* return the node level */ + return node->level; +} + +/*********************************************************************** +* NAME +* +* glp_ios_node_bound - determine subproblem local bound +* +* SYNOPSIS +* +* double glp_ios_node_bound(glp_tree *tree, int p); +* +* RETURNS +* +* The routine glp_ios_node_bound returns the local bound for (active or +* inactive) subproblem, whose reference number is p. +* +* COMMENTS +* +* The local bound for subproblem p is an lower (minimization) or upper +* (maximization) bound for integer optimal solution to this subproblem +* (not to the original problem). This bound is local in the sense that +* only subproblems in the subtree rooted at node p cannot have better +* integer feasible solutions. +* +* On creating a subproblem (due to the branching step) its local bound +* is inherited from its parent and then may get only stronger (never +* weaker). For the root subproblem its local bound is initially set to +* -DBL_MAX (minimization) or +DBL_MAX (maximization) and then improved +* as the root LP relaxation has been solved. +* +* Note that the local bound is not necessarily the optimal objective +* value to corresponding LP relaxation; it may be stronger. */ + +double glp_ios_node_bound(glp_tree *tree, int p) +{ IOSNPD *node; + /* obtain pointer to the specified subproblem */ + if (!(1 <= p && p <= tree->nslots)) +err: xerror("glp_ios_node_bound: p = %d; invalid subproblem referen" + "ce number\n", p); + node = tree->slot[p].node; + if (node == NULL) goto err; + /* return the node local bound */ + return node->bound; +} + +/*********************************************************************** +* NAME +* +* glp_ios_best_node - find active subproblem with best local bound +* +* SYNOPSIS +* +* int glp_ios_best_node(glp_tree *tree); +* +* RETURNS +* +* The routine glp_ios_best_node returns the reference number of the +* active subproblem, whose local bound is best (i.e. smallest in case +* of minimization or largest in case of maximization). However, if the +* tree is empty, the routine returns zero. +* +* COMMENTS +* +* The best local bound is an lower (minimization) or upper +* (maximization) bound for integer optimal solution to the original +* MIP problem. */ + +int glp_ios_best_node(glp_tree *tree) +{ return + ios_best_node(tree); +} + +/*********************************************************************** +* NAME +* +* glp_ios_mip_gap - compute relative MIP gap +* +* SYNOPSIS +* +* double glp_ios_mip_gap(glp_tree *tree); +* +* DESCRIPTION +* +* The routine glp_ios_mip_gap computes the relative MIP gap with the +* following formula: +* +* gap = |best_mip - best_bnd| / (|best_mip| + DBL_EPSILON), +* +* where best_mip is the best integer feasible solution found so far, +* best_bnd is the best (global) bound. If no integer feasible solution +* has been found yet, gap is set to DBL_MAX. +* +* RETURNS +* +* The routine glp_ios_mip_gap returns the relative MIP gap. */ + +double glp_ios_mip_gap(glp_tree *tree) +{ return + ios_relative_gap(tree); +} + +/*********************************************************************** +* NAME +* +* glp_ios_node_data - access subproblem application-specific data +* +* SYNOPSIS +* +* void *glp_ios_node_data(glp_tree *tree, int p); +* +* DESCRIPTION +* +* The routine glp_ios_node_data allows the application accessing a +* memory block allocated for the subproblem (which may be active or +* inactive), whose reference number is p. +* +* The size of the block is defined by the control parameter cb_size +* passed to the routine glp_intopt. The block is initialized by binary +* zeros on creating corresponding subproblem, and its contents is kept +* until the subproblem will be removed from the tree. +* +* The application may use these memory blocks to store specific data +* for each subproblem. +* +* RETURNS +* +* The routine glp_ios_node_data returns a pointer to the memory block +* for the specified subproblem. Note that if cb_size = 0, the routine +* returns a null pointer. */ + +void *glp_ios_node_data(glp_tree *tree, int p) +{ IOSNPD *node; + /* obtain pointer to the specified subproblem */ + if (!(1 <= p && p <= tree->nslots)) +err: xerror("glp_ios_node_level: p = %d; invalid subproblem referen" + "ce number\n", p); + node = tree->slot[p].node; + if (node == NULL) goto err; + /* return pointer to the application-specific data */ + return node->data; +} + +/*********************************************************************** +* NAME +* +* glp_ios_row_attr - retrieve additional row attributes +* +* SYNOPSIS +* +* void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr); +* +* DESCRIPTION +* +* The routine glp_ios_row_attr retrieves additional attributes of row +* i and stores them in the structure glp_attr. */ + +void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr) +{ GLPROW *row; + if (!(1 <= i && i <= tree->mip->m)) + xerror("glp_ios_row_attr: i = %d; row number out of range\n", + i); + row = tree->mip->row[i]; + attr->level = row->level; + attr->origin = row->origin; + attr->klass = row->klass; + return; +} + +/**********************************************************************/ + +int glp_ios_pool_size(glp_tree *tree) +{ /* determine current size of the cut pool */ + if (tree->reason != GLP_ICUTGEN) + xerror("glp_ios_pool_size: operation not allowed\n"); + xassert(tree->local != NULL); + return tree->local->size; +} + +/**********************************************************************/ + +int glp_ios_add_row(glp_tree *tree, + const char *name, int klass, int flags, int len, const int ind[], + const double val[], int type, double rhs) +{ /* add row (constraint) to the cut pool */ + int num; + if (tree->reason != GLP_ICUTGEN) + xerror("glp_ios_add_row: operation not allowed\n"); + xassert(tree->local != NULL); + num = ios_add_row(tree, tree->local, name, klass, flags, len, + ind, val, type, rhs); + return num; +} + +/**********************************************************************/ + +void glp_ios_del_row(glp_tree *tree, int i) +{ /* remove row (constraint) from the cut pool */ + if (tree->reason != GLP_ICUTGEN) + xerror("glp_ios_del_row: operation not allowed\n"); + ios_del_row(tree, tree->local, i); + return; +} + +/**********************************************************************/ + +void glp_ios_clear_pool(glp_tree *tree) +{ /* remove all rows (constraints) from the cut pool */ + if (tree->reason != GLP_ICUTGEN) + xerror("glp_ios_clear_pool: operation not allowed\n"); + ios_clear_pool(tree, tree->local); + return; +} + +/*********************************************************************** +* NAME +* +* glp_ios_can_branch - check if can branch upon specified variable +* +* SYNOPSIS +* +* int glp_ios_can_branch(glp_tree *tree, int j); +* +* RETURNS +* +* If j-th variable (column) can be used to branch upon, the routine +* glp_ios_can_branch returns non-zero, otherwise zero. */ + +int glp_ios_can_branch(glp_tree *tree, int j) +{ if (!(1 <= j && j <= tree->mip->n)) + xerror("glp_ios_can_branch: j = %d; column number out of range" + "\n", j); + return tree->non_int[j]; +} + +/*********************************************************************** +* NAME +* +* glp_ios_branch_upon - choose variable to branch upon +* +* SYNOPSIS +* +* void glp_ios_branch_upon(glp_tree *tree, int j, int sel); +* +* DESCRIPTION +* +* The routine glp_ios_branch_upon can be called from the user-defined +* callback routine in response to the reason GLP_IBRANCH to choose a +* branching variable, whose ordinal number is j. Should note that only +* variables, for which the routine glp_ios_can_branch returns non-zero, +* can be used to branch upon. +* +* The parameter sel is a flag that indicates which branch (subproblem) +* should be selected next to continue the search: +* +* GLP_DN_BRNCH - select down-branch; +* GLP_UP_BRNCH - select up-branch; +* GLP_NO_BRNCH - use general selection technique. */ + +void glp_ios_branch_upon(glp_tree *tree, int j, int sel) +{ if (!(1 <= j && j <= tree->mip->n)) + xerror("glp_ios_branch_upon: j = %d; column number out of rang" + "e\n", j); + if (!(sel == GLP_DN_BRNCH || sel == GLP_UP_BRNCH || + sel == GLP_NO_BRNCH)) + xerror("glp_ios_branch_upon: sel = %d: invalid branch selectio" + "n flag\n", sel); + if (!(tree->non_int[j])) + xerror("glp_ios_branch_upon: j = %d; variable cannot be used t" + "o branch upon\n", j); + if (tree->br_var != 0) + xerror("glp_ios_branch_upon: branching variable already chosen" + "\n"); + tree->br_var = j; + tree->br_sel = sel; + return; +} + +/*********************************************************************** +* NAME +* +* glp_ios_select_node - select subproblem to continue the search +* +* SYNOPSIS +* +* void glp_ios_select_node(glp_tree *tree, int p); +* +* DESCRIPTION +* +* The routine glp_ios_select_node can be called from the user-defined +* callback routine in response to the reason GLP_ISELECT to select an +* active subproblem, whose reference number is p. The search will be +* continued from the subproblem selected. */ + +void glp_ios_select_node(glp_tree *tree, int p) +{ IOSNPD *node; + /* obtain pointer to the specified subproblem */ + if (!(1 <= p && p <= tree->nslots)) +err: xerror("glp_ios_select_node: p = %d; invalid subproblem refere" + "nce number\n", p); + node = tree->slot[p].node; + if (node == NULL) goto err; + /* the specified subproblem must be active */ + if (node->count != 0) + xerror("glp_ios_select_node: p = %d; subproblem not in the act" + "ive list\n", p); + /* no subproblem must be selected yet */ + if (tree->next_p != 0) + xerror("glp_ios_select_node: subproblem already selected\n"); + /* select the specified subproblem to continue the search */ + tree->next_p = p; + return; +} + +/*********************************************************************** +* NAME +* +* glp_ios_heur_sol - provide solution found by heuristic +* +* SYNOPSIS +* +* int glp_ios_heur_sol(glp_tree *tree, const double x[]); +* +* DESCRIPTION +* +* The routine glp_ios_heur_sol can be called from the user-defined +* callback routine in response to the reason GLP_IHEUR to provide an +* integer feasible solution found by a primal heuristic. +* +* Primal values of *all* variables (columns) found by the heuristic +* should be placed in locations x[1], ..., x[n], where n is the number +* of columns in the original problem object. Note that the routine +* glp_ios_heur_sol *does not* check primal feasibility of the solution +* provided. +* +* Using the solution passed in the array x the routine computes value +* of the objective function. If the objective value is better than the +* best known integer feasible solution, the routine computes values of +* auxiliary variables (rows) and stores all solution components in the +* problem object. +* +* RETURNS +* +* If the provided solution is accepted, the routine glp_ios_heur_sol +* returns zero. Otherwise, if the provided solution is rejected, the +* routine returns non-zero. */ + +int glp_ios_heur_sol(glp_tree *tree, const double x[]) +{ glp_prob *mip = tree->mip; + int m = tree->orig_m; + int n = tree->n; + int i, j; + double obj; + xassert(mip->m >= m); + xassert(mip->n == n); + /* check values of integer variables and compute value of the + objective function */ + obj = mip->c0; + for (j = 1; j <= n; j++) + { GLPCOL *col = mip->col[j]; + if (col->kind == GLP_IV) + { /* provided value must be integral */ + if (x[j] != floor(x[j])) return 1; + } + obj += col->coef * x[j]; + } + /* check if the provided solution is better than the best known + integer feasible solution */ + if (mip->mip_stat == GLP_FEAS) + { switch (mip->dir) + { case GLP_MIN: + if (obj >= tree->mip->mip_obj) return 1; + break; + case GLP_MAX: + if (obj <= tree->mip->mip_obj) return 1; + break; + default: + xassert(mip != mip); + } + } + /* it is better; store it in the problem object */ + if (tree->parm->msg_lev >= GLP_MSG_ON) + xprintf("Solution found by heuristic: %.12g\n", obj); + mip->mip_stat = GLP_FEAS; + mip->mip_obj = obj; + for (j = 1; j <= n; j++) + mip->col[j]->mipx = x[j]; + for (i = 1; i <= m; i++) + { GLPROW *row = mip->row[i]; + GLPAIJ *aij; + row->mipx = 0.0; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + row->mipx += aij->val * aij->col->mipx; + } + return 0; +} + +/*********************************************************************** +* NAME +* +* glp_ios_terminate - terminate the solution process. +* +* SYNOPSIS +* +* void glp_ios_terminate(glp_tree *tree); +* +* DESCRIPTION +* +* The routine glp_ios_terminate sets a flag indicating that the MIP +* solver should prematurely terminate the search. */ + +void glp_ios_terminate(glp_tree *tree) +{ if (tree->parm->msg_lev >= GLP_MSG_DBG) + xprintf("The search is prematurely terminated due to applicati" + "on request\n"); + tree->stop = 1; + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi14.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi14.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,274 @@ +/* glpapi14.c (processing models in GNU MathProg language) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define GLP_TRAN_DEFINED +typedef struct MPL glp_tran; + +#include "glpmpl.h" +#include "glpapi.h" + +glp_tran *glp_mpl_alloc_wksp(void) +{ /* allocate the MathProg translator workspace */ + glp_tran *tran; + tran = mpl_initialize(); + return tran; +} + +#if 1 /* 08/XII-2009 */ +void _glp_mpl_init_rand(glp_tran *tran, int seed) +{ if (tran->phase != 0) + xerror("glp_mpl_init_rand: invalid call sequence\n"); + rng_init_rand(tran->rand, seed); + return; +} +#endif + +int glp_mpl_read_model(glp_tran *tran, const char *fname, int skip) +{ /* read and translate model section */ + int ret; + if (tran->phase != 0) + xerror("glp_mpl_read_model: invalid call sequence\n"); + ret = mpl_read_model(tran, (char *)fname, skip); + if (ret == 1 || ret == 2) + ret = 0; + else if (ret == 4) + ret = 1; + else + xassert(ret != ret); + return ret; +} + +int glp_mpl_read_data(glp_tran *tran, const char *fname) +{ /* read and translate data section */ + int ret; + if (!(tran->phase == 1 || tran->phase == 2)) + xerror("glp_mpl_read_data: invalid call sequence\n"); + ret = mpl_read_data(tran, (char *)fname); + if (ret == 2) + ret = 0; + else if (ret == 4) + ret = 1; + else + xassert(ret != ret); + return ret; +} + +int glp_mpl_generate(glp_tran *tran, const char *fname) +{ /* generate the model */ + int ret; + if (!(tran->phase == 1 || tran->phase == 2)) + xerror("glp_mpl_generate: invalid call sequence\n"); + ret = mpl_generate(tran, (char *)fname); + if (ret == 3) + ret = 0; + else if (ret == 4) + ret = 1; + return ret; +} + +void glp_mpl_build_prob(glp_tran *tran, glp_prob *prob) +{ /* build LP/MIP problem instance from the model */ + int m, n, i, j, t, kind, type, len, *ind; + double lb, ub, *val; + if (tran->phase != 3) + xerror("glp_mpl_build_prob: invalid call sequence\n"); + /* erase the problem object */ + glp_erase_prob(prob); + /* set problem name */ + glp_set_prob_name(prob, mpl_get_prob_name(tran)); + /* build rows (constraints) */ + m = mpl_get_num_rows(tran); + if (m > 0) + glp_add_rows(prob, m); + for (i = 1; i <= m; i++) + { /* set row name */ + glp_set_row_name(prob, i, mpl_get_row_name(tran, i)); + /* set row bounds */ + type = mpl_get_row_bnds(tran, i, &lb, &ub); + switch (type) + { case MPL_FR: type = GLP_FR; break; + case MPL_LO: type = GLP_LO; break; + case MPL_UP: type = GLP_UP; break; + case MPL_DB: type = GLP_DB; break; + case MPL_FX: type = GLP_FX; break; + default: xassert(type != type); + } + if (type == GLP_DB && fabs(lb - ub) < 1e-9 * (1.0 + fabs(lb))) + { type = GLP_FX; + if (fabs(lb) <= fabs(ub)) ub = lb; else lb = ub; + } + glp_set_row_bnds(prob, i, type, lb, ub); + /* warn about non-zero constant term */ + if (mpl_get_row_c0(tran, i) != 0.0) + xprintf("glp_mpl_build_prob: row %s; constant term %.12g ig" + "nored\n", + mpl_get_row_name(tran, i), mpl_get_row_c0(tran, i)); + } + /* build columns (variables) */ + n = mpl_get_num_cols(tran); + if (n > 0) + glp_add_cols(prob, n); + for (j = 1; j <= n; j++) + { /* set column name */ + glp_set_col_name(prob, j, mpl_get_col_name(tran, j)); + /* set column kind */ + kind = mpl_get_col_kind(tran, j); + switch (kind) + { case MPL_NUM: + break; + case MPL_INT: + case MPL_BIN: + glp_set_col_kind(prob, j, GLP_IV); + break; + default: + xassert(kind != kind); + } + /* set column bounds */ + type = mpl_get_col_bnds(tran, j, &lb, &ub); + switch (type) + { case MPL_FR: type = GLP_FR; break; + case MPL_LO: type = GLP_LO; break; + case MPL_UP: type = GLP_UP; break; + case MPL_DB: type = GLP_DB; break; + case MPL_FX: type = GLP_FX; break; + default: xassert(type != type); + } + if (kind == MPL_BIN) + { if (type == GLP_FR || type == GLP_UP || lb < 0.0) lb = 0.0; + if (type == GLP_FR || type == GLP_LO || ub > 1.0) ub = 1.0; + type = GLP_DB; + } + if (type == GLP_DB && fabs(lb - ub) < 1e-9 * (1.0 + fabs(lb))) + { type = GLP_FX; + if (fabs(lb) <= fabs(ub)) ub = lb; else lb = ub; + } + glp_set_col_bnds(prob, j, type, lb, ub); + } + /* load the constraint matrix */ + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + for (i = 1; i <= m; i++) + { len = mpl_get_mat_row(tran, i, ind, val); + glp_set_mat_row(prob, i, len, ind, val); + } + /* build objective function (the first objective is used) */ + for (i = 1; i <= m; i++) + { kind = mpl_get_row_kind(tran, i); + if (kind == MPL_MIN || kind == MPL_MAX) + { /* set objective name */ + glp_set_obj_name(prob, mpl_get_row_name(tran, i)); + /* set optimization direction */ + glp_set_obj_dir(prob, kind == MPL_MIN ? GLP_MIN : GLP_MAX); + /* set constant term */ + glp_set_obj_coef(prob, 0, mpl_get_row_c0(tran, i)); + /* set objective coefficients */ + len = mpl_get_mat_row(tran, i, ind, val); + for (t = 1; t <= len; t++) + glp_set_obj_coef(prob, ind[t], val[t]); + break; + } + } + /* free working arrays */ + xfree(ind); + xfree(val); + return; +} + +int glp_mpl_postsolve(glp_tran *tran, glp_prob *prob, int sol) +{ /* postsolve the model */ + int i, j, m, n, stat, ret; + double prim, dual; + if (!(tran->phase == 3 && !tran->flag_p)) + xerror("glp_mpl_postsolve: invalid call sequence\n"); + if (!(sol == GLP_SOL || sol == GLP_IPT || sol == GLP_MIP)) + xerror("glp_mpl_postsolve: sol = %d; invalid parameter\n", + sol); + m = mpl_get_num_rows(tran); + n = mpl_get_num_cols(tran); + if (!(m == glp_get_num_rows(prob) && + n == glp_get_num_cols(prob))) + xerror("glp_mpl_postsolve: wrong problem object\n"); + if (!mpl_has_solve_stmt(tran)) + { ret = 0; + goto done; + } + for (i = 1; i <= m; i++) + { if (sol == GLP_SOL) + { stat = glp_get_row_stat(prob, i); + prim = glp_get_row_prim(prob, i); + dual = glp_get_row_dual(prob, i); + } + else if (sol == GLP_IPT) + { stat = 0; + prim = glp_ipt_row_prim(prob, i); + dual = glp_ipt_row_dual(prob, i); + } + else if (sol == GLP_MIP) + { stat = 0; + prim = glp_mip_row_val(prob, i); + dual = 0.0; + } + else + xassert(sol != sol); + if (fabs(prim) < 1e-9) prim = 0.0; + if (fabs(dual) < 1e-9) dual = 0.0; + mpl_put_row_soln(tran, i, stat, prim, dual); + } + for (j = 1; j <= n; j++) + { if (sol == GLP_SOL) + { stat = glp_get_col_stat(prob, j); + prim = glp_get_col_prim(prob, j); + dual = glp_get_col_dual(prob, j); + } + else if (sol == GLP_IPT) + { stat = 0; + prim = glp_ipt_col_prim(prob, j); + dual = glp_ipt_col_dual(prob, j); + } + else if (sol == GLP_MIP) + { stat = 0; + prim = glp_mip_col_val(prob, j); + dual = 0.0; + } + else + xassert(sol != sol); + if (fabs(prim) < 1e-9) prim = 0.0; + if (fabs(dual) < 1e-9) dual = 0.0; + mpl_put_col_soln(tran, j, stat, prim, dual); + } + ret = mpl_postsolve(tran); + if (ret == 3) + ret = 0; + else if (ret == 4) + ret = 1; +done: return ret; +} + +void glp_mpl_free_wksp(glp_tran *tran) +{ /* free the MathProg translator workspace */ + mpl_terminate(tran); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi15.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi15.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,609 @@ +/* glpapi15.c (basic graph and network routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/* CAUTION: DO NOT CHANGE THE LIMITS BELOW */ + +#define NV_MAX 100000000 /* = 100*10^6 */ +/* maximal number of vertices in the graph */ + +#define NA_MAX 500000000 /* = 500*10^6 */ +/* maximal number of arcs in the graph */ + +/*********************************************************************** +* NAME +* +* glp_create_graph - create graph +* +* SYNOPSIS +* +* glp_graph *glp_create_graph(int v_size, int a_size); +* +* DESCRIPTION +* +* The routine creates a new graph, which initially is empty, i.e. has +* no vertices and arcs. +* +* The parameter v_size specifies the size of data associated with each +* vertex of the graph (0 to 256 bytes). +* +* The parameter a_size specifies the size of data associated with each +* arc of the graph (0 to 256 bytes). +* +* RETURNS +* +* The routine returns a pointer to the graph created. */ + +static void create_graph(glp_graph *G, int v_size, int a_size) +{ G->pool = dmp_create_pool(); + G->name = NULL; + G->nv_max = 50; + G->nv = G->na = 0; + G->v = xcalloc(1+G->nv_max, sizeof(glp_vertex *)); + G->index = NULL; + G->v_size = v_size; + G->a_size = a_size; + return; +} + +glp_graph *glp_create_graph(int v_size, int a_size) +{ glp_graph *G; + if (!(0 <= v_size && v_size <= 256)) + xerror("glp_create_graph: v_size = %d; invalid size of vertex " + "data\n", v_size); + if (!(0 <= a_size && a_size <= 256)) + xerror("glp_create_graph: a_size = %d; invalid size of arc dat" + "a\n", a_size); + G = xmalloc(sizeof(glp_graph)); + create_graph(G, v_size, a_size); + return G; +} + +/*********************************************************************** +* NAME +* +* glp_set_graph_name - assign (change) graph name +* +* SYNOPSIS +* +* void glp_set_graph_name(glp_graph *G, const char *name); +* +* DESCRIPTION +* +* The routine glp_set_graph_name assigns a symbolic name specified by +* the character string name (1 to 255 chars) to the graph. +* +* If the parameter name is NULL or an empty string, the routine erases +* the existing symbolic name of the graph. */ + +void glp_set_graph_name(glp_graph *G, const char *name) +{ if (G->name != NULL) + { dmp_free_atom(G->pool, G->name, strlen(G->name)+1); + G->name = NULL; + } + if (!(name == NULL || name[0] == '\0')) + { int j; + for (j = 0; name[j] != '\0'; j++) + { if (j == 256) + xerror("glp_set_graph_name: graph name too long\n"); + if (iscntrl((unsigned char)name[j])) + xerror("glp_set_graph_name: graph name contains invalid " + "character(s)\n"); + } + G->name = dmp_get_atom(G->pool, strlen(name)+1); + strcpy(G->name, name); + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_add_vertices - add new vertices to graph +* +* SYNOPSIS +* +* int glp_add_vertices(glp_graph *G, int nadd); +* +* DESCRIPTION +* +* The routine glp_add_vertices adds nadd vertices to the specified +* graph. New vertices are always added to the end of the vertex list, +* so ordinal numbers of existing vertices remain unchanged. +* +* Being added each new vertex is isolated (has no incident arcs). +* +* RETURNS +* +* The routine glp_add_vertices returns an ordinal number of the first +* new vertex added to the graph. */ + +int glp_add_vertices(glp_graph *G, int nadd) +{ int i, nv_new; + if (nadd < 1) + xerror("glp_add_vertices: nadd = %d; invalid number of vertice" + "s\n", nadd); + if (nadd > NV_MAX - G->nv) + xerror("glp_add_vertices: nadd = %d; too many vertices\n", + nadd); + /* determine new number of vertices */ + nv_new = G->nv + nadd; + /* increase the room, if necessary */ + if (G->nv_max < nv_new) + { glp_vertex **save = G->v; + while (G->nv_max < nv_new) + { G->nv_max += G->nv_max; + xassert(G->nv_max > 0); + } + G->v = xcalloc(1+G->nv_max, sizeof(glp_vertex *)); + memcpy(&G->v[1], &save[1], G->nv * sizeof(glp_vertex *)); + xfree(save); + } + /* add new vertices to the end of the vertex list */ + for (i = G->nv+1; i <= nv_new; i++) + { glp_vertex *v; + G->v[i] = v = dmp_get_atom(G->pool, sizeof(glp_vertex)); + v->i = i; + v->name = NULL; + v->entry = NULL; + if (G->v_size == 0) + v->data = NULL; + else + { v->data = dmp_get_atom(G->pool, G->v_size); + memset(v->data, 0, G->v_size); + } + v->temp = NULL; + v->in = v->out = NULL; + } + /* set new number of vertices */ + G->nv = nv_new; + /* return the ordinal number of the first vertex added */ + return nv_new - nadd + 1; +} + +/**********************************************************************/ + +void glp_set_vertex_name(glp_graph *G, int i, const char *name) +{ /* assign (change) vertex name */ + glp_vertex *v; + if (!(1 <= i && i <= G->nv)) + xerror("glp_set_vertex_name: i = %d; vertex number out of rang" + "e\n", i); + v = G->v[i]; + if (v->name != NULL) + { if (v->entry != NULL) + { xassert(G->index != NULL); + avl_delete_node(G->index, v->entry); + v->entry = NULL; + } + dmp_free_atom(G->pool, v->name, strlen(v->name)+1); + v->name = NULL; + } + if (!(name == NULL || name[0] == '\0')) + { int k; + for (k = 0; name[k] != '\0'; k++) + { if (k == 256) + xerror("glp_set_vertex_name: i = %d; vertex name too lon" + "g\n", i); + if (iscntrl((unsigned char)name[k])) + xerror("glp_set_vertex_name: i = %d; vertex name contain" + "s invalid character(s)\n", i); + } + v->name = dmp_get_atom(G->pool, strlen(name)+1); + strcpy(v->name, name); + if (G->index != NULL) + { xassert(v->entry == NULL); + v->entry = avl_insert_node(G->index, v->name); + avl_set_node_link(v->entry, v); + } + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_add_arc - add new arc to graph +* +* SYNOPSIS +* +* glp_arc *glp_add_arc(glp_graph *G, int i, int j); +* +* DESCRIPTION +* +* The routine glp_add_arc adds a new arc to the specified graph. +* +* The parameters i and j specify the ordinal numbers of, resp., tail +* and head vertices of the arc. Note that self-loops and multiple arcs +* are allowed. +* +* RETURNS +* +* The routine glp_add_arc returns a pointer to the arc added. */ + +glp_arc *glp_add_arc(glp_graph *G, int i, int j) +{ glp_arc *a; + if (!(1 <= i && i <= G->nv)) + xerror("glp_add_arc: i = %d; tail vertex number out of range\n" + , i); + if (!(1 <= j && j <= G->nv)) + xerror("glp_add_arc: j = %d; head vertex number out of range\n" + , j); + if (G->na == NA_MAX) + xerror("glp_add_arc: too many arcs\n"); + a = dmp_get_atom(G->pool, sizeof(glp_arc)); + a->tail = G->v[i]; + a->head = G->v[j]; + if (G->a_size == 0) + a->data = NULL; + else + { a->data = dmp_get_atom(G->pool, G->a_size); + memset(a->data, 0, G->a_size); + } + a->temp = NULL; + a->t_prev = NULL; + a->t_next = G->v[i]->out; + if (a->t_next != NULL) a->t_next->t_prev = a; + a->h_prev = NULL; + a->h_next = G->v[j]->in; + if (a->h_next != NULL) a->h_next->h_prev = a; + G->v[i]->out = G->v[j]->in = a; + G->na++; + return a; +} + +/*********************************************************************** +* NAME +* +* glp_del_vertices - delete vertices from graph +* +* SYNOPSIS +* +* void glp_del_vertices(glp_graph *G, int ndel, const int num[]); +* +* DESCRIPTION +* +* The routine glp_del_vertices deletes vertices along with all +* incident arcs from the specified graph. Ordinal numbers of vertices +* to be deleted should be placed in locations num[1], ..., num[ndel], +* ndel > 0. +* +* Note that deleting vertices involves changing ordinal numbers of +* other vertices remaining in the graph. New ordinal numbers of the +* remaining vertices are assigned under the assumption that the +* original order of vertices is not changed. */ + +void glp_del_vertices(glp_graph *G, int ndel, const int num[]) +{ glp_vertex *v; + int i, k, nv_new; + /* scan the list of vertices to be deleted */ + if (!(1 <= ndel && ndel <= G->nv)) + xerror("glp_del_vertices: ndel = %d; invalid number of vertice" + "s\n", ndel); + for (k = 1; k <= ndel; k++) + { /* take the number of vertex to be deleted */ + i = num[k]; + /* obtain pointer to i-th vertex */ + if (!(1 <= i && i <= G->nv)) + xerror("glp_del_vertices: num[%d] = %d; vertex number out o" + "f range\n", k, i); + v = G->v[i]; + /* check that the vertex is not marked yet */ + if (v->i == 0) + xerror("glp_del_vertices: num[%d] = %d; duplicate vertex nu" + "mbers not allowed\n", k, i); + /* erase symbolic name assigned to the vertex */ + glp_set_vertex_name(G, i, NULL); + xassert(v->name == NULL); + xassert(v->entry == NULL); + /* free vertex data, if allocated */ + if (v->data != NULL) + dmp_free_atom(G->pool, v->data, G->v_size); + /* delete all incoming arcs */ + while (v->in != NULL) + glp_del_arc(G, v->in); + /* delete all outgoing arcs */ + while (v->out != NULL) + glp_del_arc(G, v->out); + /* mark the vertex to be deleted */ + v->i = 0; + } + /* delete all marked vertices from the vertex list */ + nv_new = 0; + for (i = 1; i <= G->nv; i++) + { /* obtain pointer to i-th vertex */ + v = G->v[i]; + /* check if the vertex is marked */ + if (v->i == 0) + { /* it is marked, delete it */ + dmp_free_atom(G->pool, v, sizeof(glp_vertex)); + } + else + { /* it is not marked, keep it */ + v->i = ++nv_new; + G->v[v->i] = v; + } + } + /* set new number of vertices in the graph */ + G->nv = nv_new; + return; +} + +/*********************************************************************** +* NAME +* +* glp_del_arc - delete arc from graph +* +* SYNOPSIS +* +* void glp_del_arc(glp_graph *G, glp_arc *a); +* +* DESCRIPTION +* +* The routine glp_del_arc deletes an arc from the specified graph. +* The arc to be deleted must exist. */ + +void glp_del_arc(glp_graph *G, glp_arc *a) +{ /* some sanity checks */ + xassert(G->na > 0); + xassert(1 <= a->tail->i && a->tail->i <= G->nv); + xassert(a->tail == G->v[a->tail->i]); + xassert(1 <= a->head->i && a->head->i <= G->nv); + xassert(a->head == G->v[a->head->i]); + /* remove the arc from the list of incoming arcs */ + if (a->h_prev == NULL) + a->head->in = a->h_next; + else + a->h_prev->h_next = a->h_next; + if (a->h_next == NULL) + ; + else + a->h_next->h_prev = a->h_prev; + /* remove the arc from the list of outgoing arcs */ + if (a->t_prev == NULL) + a->tail->out = a->t_next; + else + a->t_prev->t_next = a->t_next; + if (a->t_next == NULL) + ; + else + a->t_next->t_prev = a->t_prev; + /* free arc data, if allocated */ + if (a->data != NULL) + dmp_free_atom(G->pool, a->data, G->a_size); + /* delete the arc from the graph */ + dmp_free_atom(G->pool, a, sizeof(glp_arc)); + G->na--; + return; +} + +/*********************************************************************** +* NAME +* +* glp_erase_graph - erase graph content +* +* SYNOPSIS +* +* void glp_erase_graph(glp_graph *G, int v_size, int a_size); +* +* DESCRIPTION +* +* The routine glp_erase_graph erases the content of the specified +* graph. The effect of this operation is the same as if the graph +* would be deleted with the routine glp_delete_graph and then created +* anew with the routine glp_create_graph, with exception that the +* handle (pointer) to the graph remains valid. */ + +static void delete_graph(glp_graph *G) +{ dmp_delete_pool(G->pool); + xfree(G->v); + if (G->index != NULL) avl_delete_tree(G->index); + return; +} + +void glp_erase_graph(glp_graph *G, int v_size, int a_size) +{ if (!(0 <= v_size && v_size <= 256)) + xerror("glp_erase_graph: v_size = %d; invalid size of vertex d" + "ata\n", v_size); + if (!(0 <= a_size && a_size <= 256)) + xerror("glp_erase_graph: a_size = %d; invalid size of arc data" + "\n", a_size); + delete_graph(G); + create_graph(G, v_size, a_size); + return; +} + +/*********************************************************************** +* NAME +* +* glp_delete_graph - delete graph +* +* SYNOPSIS +* +* void glp_delete_graph(glp_graph *G); +* +* DESCRIPTION +* +* The routine glp_delete_graph deletes the specified graph and frees +* all the memory allocated to this program object. */ + +void glp_delete_graph(glp_graph *G) +{ delete_graph(G); + xfree(G); + return; +} + +/**********************************************************************/ + +void glp_create_v_index(glp_graph *G) +{ /* create vertex name index */ + glp_vertex *v; + int i; + if (G->index == NULL) + { G->index = avl_create_tree(avl_strcmp, NULL); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + xassert(v->entry == NULL); + if (v->name != NULL) + { v->entry = avl_insert_node(G->index, v->name); + avl_set_node_link(v->entry, v); + } + } + } + return; +} + +int glp_find_vertex(glp_graph *G, const char *name) +{ /* find vertex by its name */ + AVLNODE *node; + int i = 0; + if (G->index == NULL) + xerror("glp_find_vertex: vertex name index does not exist\n"); + if (!(name == NULL || name[0] == '\0' || strlen(name) > 255)) + { node = avl_find_node(G->index, name); + if (node != NULL) + i = ((glp_vertex *)avl_get_node_link(node))->i; + } + return i; +} + +void glp_delete_v_index(glp_graph *G) +{ /* delete vertex name index */ + int i; + if (G->index != NULL) + { avl_delete_tree(G->index), G->index = NULL; + for (i = 1; i <= G->nv; i++) G->v[i]->entry = NULL; + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_read_graph - read graph from plain text file +* +* SYNOPSIS +* +* int glp_read_graph(glp_graph *G, const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_graph reads a graph from a plain text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_read_graph(glp_graph *G, const char *fname) +{ glp_data *data; + jmp_buf jump; + int nv, na, i, j, k, ret; + glp_erase_graph(G, G->v_size, G->a_size); + xprintf("Reading graph from `%s'...\n", fname); + data = glp_sdf_open_file(fname); + if (data == NULL) + { ret = 1; + goto done; + } + if (setjmp(jump)) + { ret = 1; + goto done; + } + glp_sdf_set_jump(data, jump); + nv = glp_sdf_read_int(data); + if (nv < 0) + glp_sdf_error(data, "invalid number of vertices\n"); + na = glp_sdf_read_int(data); + if (na < 0) + glp_sdf_error(data, "invalid number of arcs\n"); + xprintf("Graph has %d vert%s and %d arc%s\n", + nv, nv == 1 ? "ex" : "ices", na, na == 1 ? "" : "s"); + if (nv > 0) glp_add_vertices(G, nv); + for (k = 1; k <= na; k++) + { i = glp_sdf_read_int(data); + if (!(1 <= i && i <= nv)) + glp_sdf_error(data, "tail vertex number out of range\n"); + j = glp_sdf_read_int(data); + if (!(1 <= j && j <= nv)) + glp_sdf_error(data, "head vertex number out of range\n"); + glp_add_arc(G, i, j); + } + xprintf("%d lines were read\n", glp_sdf_line(data)); + ret = 0; +done: if (data != NULL) glp_sdf_close_file(data); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_graph - write graph to plain text file +* +* SYNOPSIS +* +* int glp_write_graph(glp_graph *G, const char *fname). +* +* DESCRIPTION +* +* The routine glp_write_graph writes the specified graph to a plain +* text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_write_graph(glp_graph *G, const char *fname) +{ XFILE *fp; + glp_vertex *v; + glp_arc *a; + int i, count, ret; + xprintf("Writing graph to `%s'...\n", fname); + fp = xfopen(fname, "w"), count = 0; + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xfprintf(fp, "%d %d\n", G->nv, G->na), count++; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + xfprintf(fp, "%d %d\n", a->tail->i, a->head->i), count++; + } + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", count); + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi16.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi16.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,329 @@ +/* glpapi16.c (graph and network analysis routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" +#include "glpnet.h" + +/*********************************************************************** +* NAME +* +* glp_weak_comp - find all weakly connected components of graph +* +* SYNOPSIS +* +* int glp_weak_comp(glp_graph *G, int v_num); +* +* DESCRIPTION +* +* The routine glp_weak_comp finds all weakly connected components of +* the specified graph. +* +* The parameter v_num specifies an offset of the field of type int +* in the vertex data block, to which the routine stores the number of +* a (weakly) connected component containing that vertex. If v_num < 0, +* no component numbers are stored. +* +* The components are numbered in arbitrary order from 1 to nc, where +* nc is the total number of components found, 0 <= nc <= |V|. +* +* RETURNS +* +* The routine returns nc, the total number of components found. */ + +int glp_weak_comp(glp_graph *G, int v_num) +{ glp_vertex *v; + glp_arc *a; + int f, i, j, nc, nv, pos1, pos2, *prev, *next, *list; + if (v_num >= 0 && v_num > G->v_size - (int)sizeof(int)) + xerror("glp_weak_comp: v_num = %d; invalid offset\n", v_num); + nv = G->nv; + if (nv == 0) + { nc = 0; + goto done; + } + /* allocate working arrays */ + prev = xcalloc(1+nv, sizeof(int)); + next = xcalloc(1+nv, sizeof(int)); + list = xcalloc(1+nv, sizeof(int)); + /* if vertex i is unlabelled, prev[i] is the index of previous + unlabelled vertex, and next[i] is the index of next unlabelled + vertex; if vertex i is labelled, then prev[i] < 0, and next[i] + is the connected component number */ + /* initially all vertices are unlabelled */ + f = 1; + for (i = 1; i <= nv; i++) + prev[i] = i - 1, next[i] = i + 1; + next[nv] = 0; + /* main loop (until all vertices have been labelled) */ + nc = 0; + while (f != 0) + { /* take an unlabelled vertex */ + i = f; + /* and remove it from the list of unlabelled vertices */ + f = next[i]; + if (f != 0) prev[f] = 0; + /* label the vertex; it begins a new component */ + prev[i] = -1, next[i] = ++nc; + /* breadth first search */ + list[1] = i, pos1 = pos2 = 1; + while (pos1 <= pos2) + { /* dequeue vertex i */ + i = list[pos1++]; + /* consider all arcs incoming to vertex i */ + for (a = G->v[i]->in; a != NULL; a = a->h_next) + { /* vertex j is adjacent to vertex i */ + j = a->tail->i; + if (prev[j] >= 0) + { /* vertex j is unlabelled */ + /* remove it from the list of unlabelled vertices */ + if (prev[j] == 0) + f = next[j]; + else + next[prev[j]] = next[j]; + if (next[j] == 0) + ; + else + prev[next[j]] = prev[j]; + /* label the vertex */ + prev[j] = -1, next[j] = nc; + /* and enqueue it for further consideration */ + list[++pos2] = j; + } + } + /* consider all arcs outgoing from vertex i */ + for (a = G->v[i]->out; a != NULL; a = a->t_next) + { /* vertex j is adjacent to vertex i */ + j = a->head->i; + if (prev[j] >= 0) + { /* vertex j is unlabelled */ + /* remove it from the list of unlabelled vertices */ + if (prev[j] == 0) + f = next[j]; + else + next[prev[j]] = next[j]; + if (next[j] == 0) + ; + else + prev[next[j]] = prev[j]; + /* label the vertex */ + prev[j] = -1, next[j] = nc; + /* and enqueue it for further consideration */ + list[++pos2] = j; + } + } + } + } + /* store component numbers */ + if (v_num >= 0) + { for (i = 1; i <= nv; i++) + { v = G->v[i]; + memcpy((char *)v->data + v_num, &next[i], sizeof(int)); + } + } + /* free working arrays */ + xfree(prev); + xfree(next); + xfree(list); +done: return nc; +} + +/*********************************************************************** +* NAME +* +* glp_strong_comp - find all strongly connected components of graph +* +* SYNOPSIS +* +* int glp_strong_comp(glp_graph *G, int v_num); +* +* DESCRIPTION +* +* The routine glp_strong_comp finds all strongly connected components +* of the specified graph. +* +* The parameter v_num specifies an offset of the field of type int +* in the vertex data block, to which the routine stores the number of +* a strongly connected component containing that vertex. If v_num < 0, +* no component numbers are stored. +* +* The components are numbered in arbitrary order from 1 to nc, where +* nc is the total number of components found, 0 <= nc <= |V|. However, +* the component numbering has the property that for every arc (i->j) +* in the graph the condition num(i) >= num(j) holds. +* +* RETURNS +* +* The routine returns nc, the total number of components found. */ + +int glp_strong_comp(glp_graph *G, int v_num) +{ glp_vertex *v; + glp_arc *a; + int i, k, last, n, na, nc, *icn, *ip, *lenr, *ior, *ib, *lowl, + *numb, *prev; + if (v_num >= 0 && v_num > G->v_size - (int)sizeof(int)) + xerror("glp_strong_comp: v_num = %d; invalid offset\n", + v_num); + n = G->nv; + if (n == 0) + { nc = 0; + goto done; + } + na = G->na; + icn = xcalloc(1+na, sizeof(int)); + ip = xcalloc(1+n, sizeof(int)); + lenr = xcalloc(1+n, sizeof(int)); + ior = xcalloc(1+n, sizeof(int)); + ib = xcalloc(1+n, sizeof(int)); + lowl = xcalloc(1+n, sizeof(int)); + numb = xcalloc(1+n, sizeof(int)); + prev = xcalloc(1+n, sizeof(int)); + k = 1; + for (i = 1; i <= n; i++) + { v = G->v[i]; + ip[i] = k; + for (a = v->out; a != NULL; a = a->t_next) + icn[k++] = a->head->i; + lenr[i] = k - ip[i]; + } + xassert(na == k-1); + nc = mc13d(n, icn, ip, lenr, ior, ib, lowl, numb, prev); + if (v_num >= 0) + { xassert(ib[1] == 1); + for (k = 1; k <= nc; k++) + { last = (k < nc ? ib[k+1] : n+1); + xassert(ib[k] < last); + for (i = ib[k]; i < last; i++) + { v = G->v[ior[i]]; + memcpy((char *)v->data + v_num, &k, sizeof(int)); + } + } + } + xfree(icn); + xfree(ip); + xfree(lenr); + xfree(ior); + xfree(ib); + xfree(lowl); + xfree(numb); + xfree(prev); +done: return nc; +} + +/*********************************************************************** +* NAME +* +* glp_top_sort - topological sorting of acyclic digraph +* +* SYNOPSIS +* +* int glp_top_sort(glp_graph *G, int v_num); +* +* DESCRIPTION +* +* The routine glp_top_sort performs topological sorting of vertices of +* the specified acyclic digraph. +* +* The parameter v_num specifies an offset of the field of type int in +* the vertex data block, to which the routine stores the vertex number +* assigned. If v_num < 0, vertex numbers are not stored. +* +* The vertices are numbered from 1 to n, where n is the total number +* of vertices in the graph. The vertex numbering has the property that +* for every arc (i->j) in the graph the condition num(i) < num(j) +* holds. Special case num(i) = 0 means that vertex i is not assigned a +* number, because the graph is *not* acyclic. +* +* RETURNS +* +* If the graph is acyclic and therefore all the vertices have been +* assigned numbers, the routine glp_top_sort returns zero. Otherwise, +* if the graph is not acyclic, the routine returns the number of +* vertices which have not been numbered, i.e. for which num(i) = 0. */ + +static int top_sort(glp_graph *G, int num[]) +{ glp_arc *a; + int i, j, cnt, top, *stack, *indeg; + /* allocate working arrays */ + indeg = xcalloc(1+G->nv, sizeof(int)); + stack = xcalloc(1+G->nv, sizeof(int)); + /* determine initial indegree of each vertex; push into the stack + the vertices having zero indegree */ + top = 0; + for (i = 1; i <= G->nv; i++) + { num[i] = indeg[i] = 0; + for (a = G->v[i]->in; a != NULL; a = a->h_next) + indeg[i]++; + if (indeg[i] == 0) + stack[++top] = i; + } + /* assign numbers to vertices in the sorted order */ + cnt = 0; + while (top > 0) + { /* pull vertex i from the stack */ + i = stack[top--]; + /* it has zero indegree in the current graph */ + xassert(indeg[i] == 0); + /* so assign it a next number */ + xassert(num[i] == 0); + num[i] = ++cnt; + /* remove vertex i from the current graph, update indegree of + its adjacent vertices, and push into the stack new vertices + whose indegree becomes zero */ + for (a = G->v[i]->out; a != NULL; a = a->t_next) + { j = a->head->i; + /* there exists arc (i->j) in the graph */ + xassert(indeg[j] > 0); + indeg[j]--; + if (indeg[j] == 0) + stack[++top] = j; + } + } + /* free working arrays */ + xfree(indeg); + xfree(stack); + return G->nv - cnt; +} + +int glp_top_sort(glp_graph *G, int v_num) +{ glp_vertex *v; + int i, cnt, *num; + if (v_num >= 0 && v_num > G->v_size - (int)sizeof(int)) + xerror("glp_top_sort: v_num = %d; invalid offset\n", v_num); + if (G->nv == 0) + { cnt = 0; + goto done; + } + num = xcalloc(1+G->nv, sizeof(int)); + cnt = top_sort(G, num); + if (v_num >= 0) + { for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + memcpy((char *)v->data + v_num, &num[i], sizeof(int)); + } + } + xfree(num); +done: return cnt; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi17.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi17.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1048 @@ +/* glpapi17.c (flow network problems) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" +#include "glpnet.h" + +/*********************************************************************** +* NAME +* +* glp_mincost_lp - convert minimum cost flow problem to LP +* +* SYNOPSIS +* +* void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, +* int v_rhs, int a_low, int a_cap, int a_cost); +* +* DESCRIPTION +* +* The routine glp_mincost_lp builds an LP problem, which corresponds +* to the minimum cost flow problem on the specified network G. */ + +void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, int v_rhs, + int a_low, int a_cap, int a_cost) +{ glp_vertex *v; + glp_arc *a; + int i, j, type, ind[1+2]; + double rhs, low, cap, cost, val[1+2]; + if (!(names == GLP_ON || names == GLP_OFF)) + xerror("glp_mincost_lp: names = %d; invalid parameter\n", + names); + if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) + xerror("glp_mincost_lp: v_rhs = %d; invalid offset\n", v_rhs); + if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) + xerror("glp_mincost_lp: a_low = %d; invalid offset\n", a_low); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_mincost_lp: a_cap = %d; invalid offset\n", a_cap); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_mincost_lp: a_cost = %d; invalid offset\n", a_cost) + ; + glp_erase_prob(lp); + if (names) glp_set_prob_name(lp, G->name); + if (G->nv > 0) glp_add_rows(lp, G->nv); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + if (names) glp_set_row_name(lp, i, v->name); + if (v_rhs >= 0) + memcpy(&rhs, (char *)v->data + v_rhs, sizeof(double)); + else + rhs = 0.0; + glp_set_row_bnds(lp, i, GLP_FX, rhs, rhs); + } + if (G->na > 0) glp_add_cols(lp, G->na); + for (i = 1, j = 0; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { j++; + if (names) + { char name[50+1]; + sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); + xassert(strlen(name) < sizeof(name)); + glp_set_col_name(lp, j, name); + } + if (a->tail->i != a->head->i) + { ind[1] = a->tail->i, val[1] = +1.0; + ind[2] = a->head->i, val[2] = -1.0; + glp_set_mat_col(lp, j, 2, ind, val); + } + if (a_low >= 0) + memcpy(&low, (char *)a->data + a_low, sizeof(double)); + else + low = 0.0; + if (a_cap >= 0) + memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); + else + cap = 1.0; + if (cap == DBL_MAX) + type = GLP_LO; + else if (low != cap) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(lp, j, type, low, cap); + if (a_cost >= 0) + memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); + else + cost = 0.0; + glp_set_obj_coef(lp, j, cost); + } + } + xassert(j == G->na); + return; +} + +/**********************************************************************/ + +int glp_mincost_okalg(glp_graph *G, int v_rhs, int a_low, int a_cap, + int a_cost, double *sol, int a_x, int v_pi) +{ /* find minimum-cost flow with out-of-kilter algorithm */ + glp_vertex *v; + glp_arc *a; + int nv, na, i, k, s, t, *tail, *head, *low, *cap, *cost, *x, *pi, + ret; + double sum, temp; + if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) + xerror("glp_mincost_okalg: v_rhs = %d; invalid offset\n", + v_rhs); + if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) + xerror("glp_mincost_okalg: a_low = %d; invalid offset\n", + a_low); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_mincost_okalg: a_cap = %d; invalid offset\n", + a_cap); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_mincost_okalg: a_cost = %d; invalid offset\n", + a_cost); + if (a_x >= 0 && a_x > G->a_size - (int)sizeof(double)) + xerror("glp_mincost_okalg: a_x = %d; invalid offset\n", a_x); + if (v_pi >= 0 && v_pi > G->v_size - (int)sizeof(double)) + xerror("glp_mincost_okalg: v_pi = %d; invalid offset\n", v_pi); + /* s is artificial source node */ + s = G->nv + 1; + /* t is artificial sink node */ + t = s + 1; + /* nv is the total number of nodes in the resulting network */ + nv = t; + /* na is the total number of arcs in the resulting network */ + na = G->na + 1; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + if (v_rhs >= 0) + memcpy(&temp, (char *)v->data + v_rhs, sizeof(double)); + else + temp = 0.0; + if (temp != 0.0) na++; + } + /* allocate working arrays */ + tail = xcalloc(1+na, sizeof(int)); + head = xcalloc(1+na, sizeof(int)); + low = xcalloc(1+na, sizeof(int)); + cap = xcalloc(1+na, sizeof(int)); + cost = xcalloc(1+na, sizeof(int)); + x = xcalloc(1+na, sizeof(int)); + pi = xcalloc(1+nv, sizeof(int)); + /* construct the resulting network */ + k = 0; + /* (original arcs) */ + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { k++; + tail[k] = a->tail->i; + head[k] = a->head->i; + if (tail[k] == head[k]) + { ret = GLP_EDATA; + goto done; + } + if (a_low >= 0) + memcpy(&temp, (char *)a->data + a_low, sizeof(double)); + else + temp = 0.0; + if (!(0.0 <= temp && temp <= (double)INT_MAX && + temp == floor(temp))) + { ret = GLP_EDATA; + goto done; + } + low[k] = (int)temp; + if (a_cap >= 0) + memcpy(&temp, (char *)a->data + a_cap, sizeof(double)); + else + temp = 1.0; + if (!((double)low[k] <= temp && temp <= (double)INT_MAX && + temp == floor(temp))) + { ret = GLP_EDATA; + goto done; + } + cap[k] = (int)temp; + if (a_cost >= 0) + memcpy(&temp, (char *)a->data + a_cost, sizeof(double)); + else + temp = 0.0; + if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) + { ret = GLP_EDATA; + goto done; + } + cost[k] = (int)temp; + } + } + /* (artificial arcs) */ + sum = 0.0; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + if (v_rhs >= 0) + memcpy(&temp, (char *)v->data + v_rhs, sizeof(double)); + else + temp = 0.0; + if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) + { ret = GLP_EDATA; + goto done; + } + if (temp > 0.0) + { /* artificial arc from s to original source i */ + k++; + tail[k] = s; + head[k] = i; + low[k] = cap[k] = (int)(+temp); /* supply */ + cost[k] = 0; + sum += (double)temp; + } + else if (temp < 0.0) + { /* artificial arc from original sink i to t */ + k++; + tail[k] = i; + head[k] = t; + low[k] = cap[k] = (int)(-temp); /* demand */ + cost[k] = 0; + } + } + /* (feedback arc from t to s) */ + k++; + xassert(k == na); + tail[k] = t; + head[k] = s; + if (sum > (double)INT_MAX) + { ret = GLP_EDATA; + goto done; + } + low[k] = cap[k] = (int)sum; /* total supply/demand */ + cost[k] = 0; + /* find minimal-cost circulation in the resulting network */ + ret = okalg(nv, na, tail, head, low, cap, cost, x, pi); + switch (ret) + { case 0: + /* optimal circulation found */ + ret = 0; + break; + case 1: + /* no feasible circulation exists */ + ret = GLP_ENOPFS; + break; + case 2: + /* integer overflow occured */ + ret = GLP_ERANGE; + goto done; + case 3: + /* optimality test failed (logic error) */ + ret = GLP_EFAIL; + goto done; + default: + xassert(ret != ret); + } + /* store solution components */ + /* (objective function = the total cost) */ + if (sol != NULL) + { temp = 0.0; + for (k = 1; k <= na; k++) + temp += (double)cost[k] * (double)x[k]; + *sol = temp; + } + /* (arc flows) */ + if (a_x >= 0) + { k = 0; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { temp = (double)x[++k]; + memcpy((char *)a->data + a_x, &temp, sizeof(double)); + } + } + } + /* (node potentials = Lagrange multipliers) */ + if (v_pi >= 0) + { for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + temp = - (double)pi[i]; + memcpy((char *)v->data + v_pi, &temp, sizeof(double)); + } + } +done: /* free working arrays */ + xfree(tail); + xfree(head); + xfree(low); + xfree(cap); + xfree(cost); + xfree(x); + xfree(pi); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_maxflow_lp - convert maximum flow problem to LP +* +* SYNOPSIS +* +* void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s, +* int t, int a_cap); +* +* DESCRIPTION +* +* The routine glp_maxflow_lp builds an LP problem, which corresponds +* to the maximum flow problem on the specified network G. */ + +void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s, + int t, int a_cap) +{ glp_vertex *v; + glp_arc *a; + int i, j, type, ind[1+2]; + double cap, val[1+2]; + if (!(names == GLP_ON || names == GLP_OFF)) + xerror("glp_maxflow_lp: names = %d; invalid parameter\n", + names); + if (!(1 <= s && s <= G->nv)) + xerror("glp_maxflow_lp: s = %d; source node number out of rang" + "e\n", s); + if (!(1 <= t && t <= G->nv)) + xerror("glp_maxflow_lp: t = %d: sink node number out of range " + "\n", t); + if (s == t) + xerror("glp_maxflow_lp: s = t = %d; source and sink nodes must" + " be distinct\n", s); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_maxflow_lp: a_cap = %d; invalid offset\n", a_cap); + glp_erase_prob(lp); + if (names) glp_set_prob_name(lp, G->name); + glp_set_obj_dir(lp, GLP_MAX); + glp_add_rows(lp, G->nv); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + if (names) glp_set_row_name(lp, i, v->name); + if (i == s) + type = GLP_LO; + else if (i == t) + type = GLP_UP; + else + type = GLP_FX; + glp_set_row_bnds(lp, i, type, 0.0, 0.0); + } + if (G->na > 0) glp_add_cols(lp, G->na); + for (i = 1, j = 0; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { j++; + if (names) + { char name[50+1]; + sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); + xassert(strlen(name) < sizeof(name)); + glp_set_col_name(lp, j, name); + } + if (a->tail->i != a->head->i) + { ind[1] = a->tail->i, val[1] = +1.0; + ind[2] = a->head->i, val[2] = -1.0; + glp_set_mat_col(lp, j, 2, ind, val); + } + if (a_cap >= 0) + memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); + else + cap = 1.0; + if (cap == DBL_MAX) + type = GLP_LO; + else if (cap != 0.0) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(lp, j, type, 0.0, cap); + if (a->tail->i == s) + glp_set_obj_coef(lp, j, +1.0); + else if (a->head->i == s) + glp_set_obj_coef(lp, j, -1.0); + } + } + xassert(j == G->na); + return; +} + +int glp_maxflow_ffalg(glp_graph *G, int s, int t, int a_cap, + double *sol, int a_x, int v_cut) +{ /* find maximal flow with Ford-Fulkerson algorithm */ + glp_vertex *v; + glp_arc *a; + int nv, na, i, k, flag, *tail, *head, *cap, *x, ret; + char *cut; + double temp; + if (!(1 <= s && s <= G->nv)) + xerror("glp_maxflow_ffalg: s = %d; source node number out of r" + "ange\n", s); + if (!(1 <= t && t <= G->nv)) + xerror("glp_maxflow_ffalg: t = %d: sink node number out of ran" + "ge\n", t); + if (s == t) + xerror("glp_maxflow_ffalg: s = t = %d; source and sink nodes m" + "ust be distinct\n", s); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_maxflow_ffalg: a_cap = %d; invalid offset\n", + a_cap); + if (v_cut >= 0 && v_cut > G->v_size - (int)sizeof(int)) + xerror("glp_maxflow_ffalg: v_cut = %d; invalid offset\n", + v_cut); + /* allocate working arrays */ + nv = G->nv; + na = G->na; + tail = xcalloc(1+na, sizeof(int)); + head = xcalloc(1+na, sizeof(int)); + cap = xcalloc(1+na, sizeof(int)); + x = xcalloc(1+na, sizeof(int)); + if (v_cut < 0) + cut = NULL; + else + cut = xcalloc(1+nv, sizeof(char)); + /* copy the flow network */ + k = 0; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { k++; + tail[k] = a->tail->i; + head[k] = a->head->i; + if (tail[k] == head[k]) + { ret = GLP_EDATA; + goto done; + } + if (a_cap >= 0) + memcpy(&temp, (char *)a->data + a_cap, sizeof(double)); + else + temp = 1.0; + if (!(0.0 <= temp && temp <= (double)INT_MAX && + temp == floor(temp))) + { ret = GLP_EDATA; + goto done; + } + cap[k] = (int)temp; + } + } + xassert(k == na); + /* find maximal flow in the flow network */ + ffalg(nv, na, tail, head, s, t, cap, x, cut); + ret = 0; + /* store solution components */ + /* (objective function = total flow through the network) */ + if (sol != NULL) + { temp = 0.0; + for (k = 1; k <= na; k++) + { if (tail[k] == s) + temp += (double)x[k]; + else if (head[k] == s) + temp -= (double)x[k]; + } + *sol = temp; + } + /* (arc flows) */ + if (a_x >= 0) + { k = 0; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { temp = (double)x[++k]; + memcpy((char *)a->data + a_x, &temp, sizeof(double)); + } + } + } + /* (node flags) */ + if (v_cut >= 0) + { for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + flag = cut[i]; + memcpy((char *)v->data + v_cut, &flag, sizeof(int)); + } + } +done: /* free working arrays */ + xfree(tail); + xfree(head); + xfree(cap); + xfree(x); + if (cut != NULL) xfree(cut); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_check_asnprob - check correctness of assignment problem data +* +* SYNOPSIS +* +* int glp_check_asnprob(glp_graph *G, int v_set); +* +* RETURNS +* +* If the specified assignment problem data are correct, the routine +* glp_check_asnprob returns zero, otherwise, non-zero. */ + +int glp_check_asnprob(glp_graph *G, int v_set) +{ glp_vertex *v; + int i, k, ret = 0; + if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) + xerror("glp_check_asnprob: v_set = %d; invalid offset\n", + v_set); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + if (v_set >= 0) + { memcpy(&k, (char *)v->data + v_set, sizeof(int)); + if (k == 0) + { if (v->in != NULL) + { ret = 1; + break; + } + } + else if (k == 1) + { if (v->out != NULL) + { ret = 2; + break; + } + } + else + { ret = 3; + break; + } + } + else + { if (v->in != NULL && v->out != NULL) + { ret = 4; + break; + } + } + } + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_asnprob_lp - convert assignment problem to LP +* +* SYNOPSIS +* +* int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names, +* int v_set, int a_cost); +* +* DESCRIPTION +* +* The routine glp_asnprob_lp builds an LP problem, which corresponds +* to the assignment problem on the specified graph G. +* +* RETURNS +* +* If the LP problem has been successfully built, the routine returns +* zero, otherwise, non-zero. */ + +int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names, + int v_set, int a_cost) +{ glp_vertex *v; + glp_arc *a; + int i, j, ret, ind[1+2]; + double cost, val[1+2]; + if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX || + form == GLP_ASN_MMP)) + xerror("glp_asnprob_lp: form = %d; invalid parameter\n", + form); + if (!(names == GLP_ON || names == GLP_OFF)) + xerror("glp_asnprob_lp: names = %d; invalid parameter\n", + names); + if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) + xerror("glp_asnprob_lp: v_set = %d; invalid offset\n", + v_set); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_asnprob_lp: a_cost = %d; invalid offset\n", + a_cost); + ret = glp_check_asnprob(G, v_set); + if (ret != 0) goto done; + glp_erase_prob(P); + if (names) glp_set_prob_name(P, G->name); + glp_set_obj_dir(P, form == GLP_ASN_MIN ? GLP_MIN : GLP_MAX); + if (G->nv > 0) glp_add_rows(P, G->nv); + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + if (names) glp_set_row_name(P, i, v->name); + glp_set_row_bnds(P, i, form == GLP_ASN_MMP ? GLP_UP : GLP_FX, + 1.0, 1.0); + } + if (G->na > 0) glp_add_cols(P, G->na); + for (i = 1, j = 0; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { j++; + if (names) + { char name[50+1]; + sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); + xassert(strlen(name) < sizeof(name)); + glp_set_col_name(P, j, name); + } + ind[1] = a->tail->i, val[1] = +1.0; + ind[2] = a->head->i, val[2] = +1.0; + glp_set_mat_col(P, j, 2, ind, val); + glp_set_col_bnds(P, j, GLP_DB, 0.0, 1.0); + if (a_cost >= 0) + memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); + else + cost = 1.0; + glp_set_obj_coef(P, j, cost); + } + } + xassert(j == G->na); +done: return ret; +} + +/**********************************************************************/ + +int glp_asnprob_okalg(int form, glp_graph *G, int v_set, int a_cost, + double *sol, int a_x) +{ /* solve assignment problem with out-of-kilter algorithm */ + glp_vertex *v; + glp_arc *a; + int nv, na, i, k, *tail, *head, *low, *cap, *cost, *x, *pi, ret; + double temp; + if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX || + form == GLP_ASN_MMP)) + xerror("glp_asnprob_okalg: form = %d; invalid parameter\n", + form); + if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) + xerror("glp_asnprob_okalg: v_set = %d; invalid offset\n", + v_set); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_asnprob_okalg: a_cost = %d; invalid offset\n", + a_cost); + if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int)) + xerror("glp_asnprob_okalg: a_x = %d; invalid offset\n", a_x); + if (glp_check_asnprob(G, v_set)) + return GLP_EDATA; + /* nv is the total number of nodes in the resulting network */ + nv = G->nv + 1; + /* na is the total number of arcs in the resulting network */ + na = G->na + G->nv; + /* allocate working arrays */ + tail = xcalloc(1+na, sizeof(int)); + head = xcalloc(1+na, sizeof(int)); + low = xcalloc(1+na, sizeof(int)); + cap = xcalloc(1+na, sizeof(int)); + cost = xcalloc(1+na, sizeof(int)); + x = xcalloc(1+na, sizeof(int)); + pi = xcalloc(1+nv, sizeof(int)); + /* construct the resulting network */ + k = 0; + /* (original arcs) */ + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { k++; + tail[k] = a->tail->i; + head[k] = a->head->i; + low[k] = 0; + cap[k] = 1; + if (a_cost >= 0) + memcpy(&temp, (char *)a->data + a_cost, sizeof(double)); + else + temp = 1.0; + if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) + { ret = GLP_EDATA; + goto done; + } + cost[k] = (int)temp; + if (form != GLP_ASN_MIN) cost[k] = - cost[k]; + } + } + /* (artificial arcs) */ + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + k++; + if (v->out == NULL) + tail[k] = i, head[k] = nv; + else if (v->in == NULL) + tail[k] = nv, head[k] = i; + else + xassert(v != v); + low[k] = (form == GLP_ASN_MMP ? 0 : 1); + cap[k] = 1; + cost[k] = 0; + } + xassert(k == na); + /* find minimal-cost circulation in the resulting network */ + ret = okalg(nv, na, tail, head, low, cap, cost, x, pi); + switch (ret) + { case 0: + /* optimal circulation found */ + ret = 0; + break; + case 1: + /* no feasible circulation exists */ + ret = GLP_ENOPFS; + break; + case 2: + /* integer overflow occured */ + ret = GLP_ERANGE; + goto done; + case 3: + /* optimality test failed (logic error) */ + ret = GLP_EFAIL; + goto done; + default: + xassert(ret != ret); + } + /* store solution components */ + /* (objective function = the total cost) */ + if (sol != NULL) + { temp = 0.0; + for (k = 1; k <= na; k++) + temp += (double)cost[k] * (double)x[k]; + if (form != GLP_ASN_MIN) temp = - temp; + *sol = temp; + } + /* (arc flows) */ + if (a_x >= 0) + { k = 0; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { k++; + if (ret == 0) + xassert(x[k] == 0 || x[k] == 1); + memcpy((char *)a->data + a_x, &x[k], sizeof(int)); + } + } + } +done: /* free working arrays */ + xfree(tail); + xfree(head); + xfree(low); + xfree(cap); + xfree(cost); + xfree(x); + xfree(pi); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_asnprob_hall - find bipartite matching of maximum cardinality +* +* SYNOPSIS +* +* int glp_asnprob_hall(glp_graph *G, int v_set, int a_x); +* +* DESCRIPTION +* +* The routine glp_asnprob_hall finds a matching of maximal cardinality +* in the specified bipartite graph G. It uses a version of the Fortran +* routine MC21A developed by I.S.Duff [1], which implements Hall's +* algorithm [2]. +* +* RETURNS +* +* The routine glp_asnprob_hall returns the cardinality of the matching +* found. However, if the specified graph is incorrect (as detected by +* the routine glp_check_asnprob), the routine returns negative value. +* +* REFERENCES +* +* 1. I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM +* Trans. on Math. Softw. 7 (1981), 387-390. +* +* 2. M.Hall, "An Algorithm for distinct representatives," Amer. Math. +* Monthly 63 (1956), 716-717. */ + +int glp_asnprob_hall(glp_graph *G, int v_set, int a_x) +{ glp_vertex *v; + glp_arc *a; + int card, i, k, loc, n, n1, n2, xij; + int *num, *icn, *ip, *lenr, *iperm, *pr, *arp, *cv, *out; + if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) + xerror("glp_asnprob_hall: v_set = %d; invalid offset\n", + v_set); + if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int)) + xerror("glp_asnprob_hall: a_x = %d; invalid offset\n", a_x); + if (glp_check_asnprob(G, v_set)) + return -1; + /* determine the number of vertices in sets R and S and renumber + vertices in S which correspond to columns of the matrix; skip + all isolated vertices */ + num = xcalloc(1+G->nv, sizeof(int)); + n1 = n2 = 0; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + if (v->in == NULL && v->out != NULL) + n1++, num[i] = 0; /* vertex in R */ + else if (v->in != NULL && v->out == NULL) + n2++, num[i] = n2; /* vertex in S */ + else + { xassert(v->in == NULL && v->out == NULL); + num[i] = -1; /* isolated vertex */ + } + } + /* the matrix must be square, thus, if it has more columns than + rows, extra rows will be just empty, and vice versa */ + n = (n1 >= n2 ? n1 : n2); + /* allocate working arrays */ + icn = xcalloc(1+G->na, sizeof(int)); + ip = xcalloc(1+n, sizeof(int)); + lenr = xcalloc(1+n, sizeof(int)); + iperm = xcalloc(1+n, sizeof(int)); + pr = xcalloc(1+n, sizeof(int)); + arp = xcalloc(1+n, sizeof(int)); + cv = xcalloc(1+n, sizeof(int)); + out = xcalloc(1+n, sizeof(int)); + /* build the adjacency matrix of the bipartite graph in row-wise + format (rows are vertices in R, columns are vertices in S) */ + k = 0, loc = 1; + for (i = 1; i <= G->nv; i++) + { if (num[i] != 0) continue; + /* vertex i in R */ + ip[++k] = loc; + v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { xassert(num[a->head->i] != 0); + icn[loc++] = num[a->head->i]; + } + lenr[k] = loc - ip[k]; + } + xassert(loc-1 == G->na); + /* make all extra rows empty (all extra columns are empty due to + the row-wise format used) */ + for (k++; k <= n; k++) + ip[k] = loc, lenr[k] = 0; + /* find a row permutation that maximizes the number of non-zeros + on the main diagonal */ + card = mc21a(n, icn, ip, lenr, iperm, pr, arp, cv, out); +#if 1 /* 18/II-2010 */ + /* FIXED: if card = n, arp remains clobbered on exit */ + for (i = 1; i <= n; i++) + arp[i] = 0; + for (i = 1; i <= card; i++) + { k = iperm[i]; + xassert(1 <= k && k <= n); + xassert(arp[k] == 0); + arp[k] = i; + } +#endif + /* store solution, if necessary */ + if (a_x < 0) goto skip; + k = 0; + for (i = 1; i <= G->nv; i++) + { if (num[i] != 0) continue; + /* vertex i in R */ + k++; + v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { /* arp[k] is the number of matched column or zero */ + if (arp[k] == num[a->head->i]) + { xassert(arp[k] != 0); + xij = 1; + } + else + xij = 0; + memcpy((char *)a->data + a_x, &xij, sizeof(int)); + } + } +skip: /* free working arrays */ + xfree(num); + xfree(icn); + xfree(ip); + xfree(lenr); + xfree(iperm); + xfree(pr); + xfree(arp); + xfree(cv); + xfree(out); + return card; +} + +/*********************************************************************** +* NAME +* +* glp_cpp - solve critical path problem +* +* SYNOPSIS +* +* double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls); +* +* DESCRIPTION +* +* The routine glp_cpp solves the critical path problem represented in +* the form of the project network. +* +* The parameter G is a pointer to the graph object, which specifies +* the project network. This graph must be acyclic. Multiple arcs are +* allowed being considered as single arcs. +* +* The parameter v_t specifies an offset of the field of type double +* in the vertex data block, which contains time t[i] >= 0 needed to +* perform corresponding job j. If v_t < 0, it is assumed that t[i] = 1 +* for all jobs. +* +* The parameter v_es specifies an offset of the field of type double +* in the vertex data block, to which the routine stores earliest start +* time for corresponding job. If v_es < 0, this time is not stored. +* +* The parameter v_ls specifies an offset of the field of type double +* in the vertex data block, to which the routine stores latest start +* time for corresponding job. If v_ls < 0, this time is not stored. +* +* RETURNS +* +* The routine glp_cpp returns the minimal project duration, that is, +* minimal time needed to perform all jobs in the project. */ + +static void sorting(glp_graph *G, int list[]); + +double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls) +{ glp_vertex *v; + glp_arc *a; + int i, j, k, nv, *list; + double temp, total, *t, *es, *ls; + if (v_t >= 0 && v_t > G->v_size - (int)sizeof(double)) + xerror("glp_cpp: v_t = %d; invalid offset\n", v_t); + if (v_es >= 0 && v_es > G->v_size - (int)sizeof(double)) + xerror("glp_cpp: v_es = %d; invalid offset\n", v_es); + if (v_ls >= 0 && v_ls > G->v_size - (int)sizeof(double)) + xerror("glp_cpp: v_ls = %d; invalid offset\n", v_ls); + nv = G->nv; + if (nv == 0) + { total = 0.0; + goto done; + } + /* allocate working arrays */ + t = xcalloc(1+nv, sizeof(double)); + es = xcalloc(1+nv, sizeof(double)); + ls = xcalloc(1+nv, sizeof(double)); + list = xcalloc(1+nv, sizeof(int)); + /* retrieve job times */ + for (i = 1; i <= nv; i++) + { v = G->v[i]; + if (v_t >= 0) + { memcpy(&t[i], (char *)v->data + v_t, sizeof(double)); + if (t[i] < 0.0) + xerror("glp_cpp: t[%d] = %g; invalid time\n", i, t[i]); + } + else + t[i] = 1.0; + } + /* perform topological sorting to determine the list of nodes + (jobs) such that if list[k] = i and list[kk] = j and there + exists arc (i->j), then k < kk */ + sorting(G, list); + /* FORWARD PASS */ + /* determine earliest start times */ + for (k = 1; k <= nv; k++) + { j = list[k]; + es[j] = 0.0; + for (a = G->v[j]->in; a != NULL; a = a->h_next) + { i = a->tail->i; + /* there exists arc (i->j) in the project network */ + temp = es[i] + t[i]; + if (es[j] < temp) es[j] = temp; + } + } + /* determine the minimal project duration */ + total = 0.0; + for (i = 1; i <= nv; i++) + { temp = es[i] + t[i]; + if (total < temp) total = temp; + } + /* BACKWARD PASS */ + /* determine latest start times */ + for (k = nv; k >= 1; k--) + { i = list[k]; + ls[i] = total - t[i]; + for (a = G->v[i]->out; a != NULL; a = a->t_next) + { j = a->head->i; + /* there exists arc (i->j) in the project network */ + temp = ls[j] - t[i]; + if (ls[i] > temp) ls[i] = temp; + } + /* avoid possible round-off errors */ + if (ls[i] < es[i]) ls[i] = es[i]; + } + /* store results, if necessary */ + if (v_es >= 0) + { for (i = 1; i <= nv; i++) + { v = G->v[i]; + memcpy((char *)v->data + v_es, &es[i], sizeof(double)); + } + } + if (v_ls >= 0) + { for (i = 1; i <= nv; i++) + { v = G->v[i]; + memcpy((char *)v->data + v_ls, &ls[i], sizeof(double)); + } + } + /* free working arrays */ + xfree(t); + xfree(es); + xfree(ls); + xfree(list); +done: return total; +} + +static void sorting(glp_graph *G, int list[]) +{ /* perform topological sorting to determine the list of nodes + (jobs) such that if list[k] = i and list[kk] = j and there + exists arc (i->j), then k < kk */ + int i, k, nv, v_size, *num; + void **save; + nv = G->nv; + v_size = G->v_size; + save = xcalloc(1+nv, sizeof(void *)); + num = xcalloc(1+nv, sizeof(int)); + G->v_size = sizeof(int); + for (i = 1; i <= nv; i++) + { save[i] = G->v[i]->data; + G->v[i]->data = &num[i]; + list[i] = 0; + } + if (glp_top_sort(G, 0) != 0) + xerror("glp_cpp: project network is not acyclic\n"); + G->v_size = v_size; + for (i = 1; i <= nv; i++) + { G->v[i]->data = save[i]; + k = num[i]; + xassert(1 <= k && k <= nv); + xassert(list[k] == 0); + list[k] = i; + } + xfree(save); + xfree(num); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi18.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi18.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,122 @@ +/* glpapi18.c (maximum clique problem) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" +#include "glpnet.h" + +static void set_edge(int nv, unsigned char a[], int i, int j) +{ int k; + xassert(1 <= j && j < i && i <= nv); + k = ((i - 1) * (i - 2)) / 2 + (j - 1); + a[k / CHAR_BIT] |= + (unsigned char)(1 << ((CHAR_BIT - 1) - k % CHAR_BIT)); + return; +} + +int glp_wclique_exact(glp_graph *G, int v_wgt, double *sol, int v_set) +{ /* find maximum weight clique with exact algorithm */ + glp_arc *e; + int i, j, k, len, x, *w, *ind, ret = 0; + unsigned char *a; + double s, t; + if (v_wgt >= 0 && v_wgt > G->v_size - (int)sizeof(double)) + xerror("glp_wclique_exact: v_wgt = %d; invalid parameter\n", + v_wgt); + if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) + xerror("glp_wclique_exact: v_set = %d; invalid parameter\n", + v_set); + if (G->nv == 0) + { /* empty graph has only empty clique */ + if (sol != NULL) *sol = 0.0; + return 0; + } + /* allocate working arrays */ + w = xcalloc(1+G->nv, sizeof(int)); + ind = xcalloc(1+G->nv, sizeof(int)); + len = G->nv; /* # vertices */ + len = len * (len - 1) / 2; /* # entries in lower triangle */ + len = (len + (CHAR_BIT - 1)) / CHAR_BIT; /* # bytes needed */ + a = xcalloc(len, sizeof(char)); + memset(a, 0, len * sizeof(char)); + /* determine vertex weights */ + s = 0.0; + for (i = 1; i <= G->nv; i++) + { if (v_wgt >= 0) + { memcpy(&t, (char *)G->v[i]->data + v_wgt, sizeof(double)); + if (!(0.0 <= t && t <= (double)INT_MAX && t == floor(t))) + { ret = GLP_EDATA; + goto done; + } + w[i] = (int)t; + } + else + w[i] = 1; + s += (double)w[i]; + } + if (s > (double)INT_MAX) + { ret = GLP_EDATA; + goto done; + } + /* build the adjacency matrix */ + for (i = 1; i <= G->nv; i++) + { for (e = G->v[i]->in; e != NULL; e = e->h_next) + { j = e->tail->i; + /* there exists edge (j,i) in the graph */ + if (i > j) set_edge(G->nv, a, i, j); + } + for (e = G->v[i]->out; e != NULL; e = e->t_next) + { j = e->head->i; + /* there exists edge (i,j) in the graph */ + if (i > j) set_edge(G->nv, a, i, j); + } + } + /* find maximum weight clique in the graph */ + len = wclique(G->nv, w, a, ind); + /* compute the clique weight */ + s = 0.0; + for (k = 1; k <= len; k++) + { i = ind[k]; + xassert(1 <= i && i <= G->nv); + s += (double)w[i]; + } + if (sol != NULL) *sol = s; + /* mark vertices included in the clique */ + if (v_set >= 0) + { x = 0; + for (i = 1; i <= G->nv; i++) + memcpy((char *)G->v[i]->data + v_set, &x, sizeof(int)); + x = 1; + for (k = 1; k <= len; k++) + { i = ind[k]; + memcpy((char *)G->v[i]->data + v_set, &x, sizeof(int)); + } + } +done: /* free working arrays */ + xfree(w); + xfree(ind); + xfree(a); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpapi19.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpapi19.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1196 @@ +/* glpapi19.c (stand-alone LP/MIP solver) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" +#include "glpgmp.h" + +struct csa +{ /* common storage area */ + glp_prob *prob; + /* LP/MIP problem object */ + glp_bfcp bfcp; + /* basis factorization control parameters */ + glp_smcp smcp; + /* simplex method control parameters */ + glp_iptcp iptcp; + /* interior-point method control parameters */ + glp_iocp iocp; + /* integer optimizer control parameters */ + glp_tran *tran; + /* model translator workspace */ + glp_graph *graph; + /* network problem object */ + int format; + /* problem file format: */ +#define FMT_MPS_DECK 1 /* fixed MPS */ +#define FMT_MPS_FILE 2 /* free MPS */ +#define FMT_LP 3 /* CPLEX LP */ +#define FMT_GLP 4 /* GLPK LP/MIP */ +#define FMT_MATHPROG 5 /* MathProg */ +#define FMT_MIN_COST 6 /* DIMACS min-cost flow */ +#define FMT_MAX_FLOW 7 /* DIMACS maximum flow */ + const char *in_file; + /* name of input problem file */ +#define DATA_MAX 10 + /* maximal number of input data files */ + int ndf; + /* number of input data files specified */ + const char *in_data[1+DATA_MAX]; + /* name(s) of input data file(s) */ + const char *out_dpy; + /* name of output file to send display output; NULL means the + display output is sent to the terminal */ + int seed; + /* seed value to be passed to the MathProg translator; initially + set to 1; 0x80000000 means the value is omitted */ + int solution; + /* solution type flag: */ +#define SOL_BASIC 1 /* basic */ +#define SOL_INTERIOR 2 /* interior-point */ +#define SOL_INTEGER 3 /* mixed integer */ + const char *in_res; + /* name of input solution file in raw format */ + int dir; + /* optimization direction flag: + 0 - not specified + GLP_MIN - minimization + GLP_MAX - maximization */ + int scale; + /* automatic problem scaling flag */ + const char *out_sol; + /* name of output solution file in printable format */ + const char *out_res; + /* name of output solution file in raw format */ + const char *out_ranges; + /* name of output file to write sensitivity analysis report */ + int check; + /* input data checking flag; no solution is performed */ + const char *new_name; + /* new name to be assigned to the problem */ + const char *out_mps; + /* name of output problem file in fixed MPS format */ + const char *out_freemps; + /* name of output problem file in free MPS format */ + const char *out_cpxlp; + /* name of output problem file in CPLEX LP format */ + const char *out_glp; + /* name of output problem file in GLPK format */ + const char *out_pb; + /* name of output problem file in OPB format */ + const char *out_npb; + /* name of output problem file in normalized OPB format */ + const char *log_file; + /* name of output file to hardcopy terminal output */ + int crash; + /* initial basis option: */ +#define USE_STD_BASIS 1 /* use standard basis */ +#define USE_ADV_BASIS 2 /* use advanced basis */ +#define USE_CPX_BASIS 3 /* use Bixby's basis */ +#define USE_INI_BASIS 4 /* use initial basis from ini_file */ + const char *ini_file; + /* name of input file containing initial basis */ + int exact; + /* flag to use glp_exact rather than glp_simplex */ + int xcheck; + /* flag to check final basis with glp_exact */ + int nomip; + /* flag to consider MIP as pure LP */ +}; + +static void print_help(const char *my_name) +{ /* print help information */ + xprintf("Usage: %s [options...] filename\n", my_name); + xprintf("\n"); + xprintf("General options:\n"); + xprintf(" --mps read LP/MIP problem in fixed MPS fo" + "rmat\n"); + xprintf(" --freemps read LP/MIP problem in free MPS for" + "mat (default)\n"); + xprintf(" --lp read LP/MIP problem in CPLEX LP for" + "mat\n"); + xprintf(" --glp read LP/MIP problem in GLPK format " + "\n"); + xprintf(" --math read LP/MIP model written in GNU Ma" + "thProg modeling\n"); + xprintf(" language\n"); + xprintf(" -m filename, --model filename\n"); + xprintf(" read model section and optional dat" + "a section from\n"); + xprintf(" filename (same as --math)\n"); + xprintf(" -d filename, --data filename\n"); + xprintf(" read data section from filename (fo" + "r --math only);\n"); + xprintf(" if model file also has data section" + ", it is ignored\n"); + xprintf(" -y filename, --display filename\n"); + xprintf(" send display output to filename (fo" + "r --math only);\n"); + xprintf(" by default the output is sent to te" + "rminal\n"); + xprintf(" --seed value initialize pseudo-random number gen" + "erator used in\n"); + xprintf(" MathProg model with specified seed " + "(any integer);\n"); + xprintf(" if seed value is ?, some random see" + "d will be used\n"); + xprintf(" --mincost read min-cost flow problem in DIMAC" + "S format\n"); + xprintf(" --maxflow read maximum flow problem in DIMACS" + " format\n"); + xprintf(" --simplex use simplex method (default)\n"); + xprintf(" --interior use interior point method (LP only)" + "\n"); + xprintf(" -r filename, --read filename\n"); + xprintf(" read solution from filename rather " + "to find it with\n"); + xprintf(" the solver\n"); + xprintf(" --min minimization\n"); + xprintf(" --max maximization\n"); + xprintf(" --scale scale problem (default)\n"); + xprintf(" --noscale do not scale problem\n"); + xprintf(" -o filename, --output filename\n"); + xprintf(" write solution to filename in print" + "able format\n"); + xprintf(" -w filename, --write filename\n"); + xprintf(" write solution to filename in plain" + " text format\n"); + xprintf(" --ranges filename\n"); + xprintf(" write sensitivity analysis report t" + "o filename in\n"); + xprintf(" printable format (simplex only)\n"); + xprintf(" --tmlim nnn limit solution time to nnn seconds " + "\n"); + xprintf(" --memlim nnn limit available memory to nnn megab" + "ytes\n"); + xprintf(" --check do not solve problem, check input d" + "ata only\n"); + xprintf(" --name probname change problem name to probname\n"); + xprintf(" --wmps filename write problem to filename in fixed " + "MPS format\n"); + xprintf(" --wfreemps filename\n"); + xprintf(" write problem to filename in free M" + "PS format\n"); + xprintf(" --wlp filename write problem to filename in CPLEX " + "LP format\n"); + xprintf(" --wglp filename write problem to filename in GLPK f" + "ormat\n"); +#if 0 + xprintf(" --wpb filename write problem to filename in OPB fo" + "rmat\n"); + xprintf(" --wnpb filename write problem to filename in normal" + "ized OPB format\n"); +#endif + xprintf(" --log filename write copy of terminal output to fi" + "lename\n"); + xprintf(" -h, --help display this help information and e" + "xit\n"); + xprintf(" -v, --version display program version and exit\n") + ; + xprintf("\n"); + xprintf("LP basis factorization options:\n"); + xprintf(" --luf LU + Forrest-Tomlin update\n"); + xprintf(" (faster, less stable; default)\n"); + xprintf(" --cbg LU + Schur complement + Bartels-Gol" + "ub update\n"); + xprintf(" (slower, more stable)\n"); + xprintf(" --cgr LU + Schur complement + Givens rota" + "tion update\n"); + xprintf(" (slower, more stable)\n"); + xprintf("\n"); + xprintf("Options specific to simplex solver:\n"); + xprintf(" --primal use primal simplex (default)\n"); + xprintf(" --dual use dual simplex\n"); + xprintf(" --std use standard initial basis of all s" + "lacks\n"); + xprintf(" --adv use advanced initial basis (default" + ")\n"); + xprintf(" --bib use Bixby's initial basis\n"); + xprintf(" --ini filename use as initial basis previously sav" + "ed with -w\n"); + xprintf(" (disables LP presolver)\n"); + xprintf(" --steep use steepest edge technique (defaul" + "t)\n"); + xprintf(" --nosteep use standard \"textbook\" pricing\n" + ); + xprintf(" --relax use Harris' two-pass ratio test (de" + "fault)\n"); + xprintf(" --norelax use standard \"textbook\" ratio tes" + "t\n"); + xprintf(" --presol use presolver (default; assumes --s" + "cale and --adv)\n"); + xprintf(" --nopresol do not use presolver\n"); + xprintf(" --exact use simplex method based on exact a" + "rithmetic\n"); + xprintf(" --xcheck check final basis using exact arith" + "metic\n"); + xprintf("\n"); + xprintf("Options specific to interior-point solver:\n"); + xprintf(" --nord use natural (original) ordering\n"); + xprintf(" --qmd use quotient minimum degree orderin" + "g\n"); + xprintf(" --amd use approximate minimum degree orde" + "ring (default)\n"); + xprintf(" --symamd use approximate minimum degree orde" + "ring\n"); + xprintf("\n"); + xprintf("Options specific to MIP solver:\n"); + xprintf(" --nomip consider all integer variables as c" + "ontinuous\n"); + xprintf(" (allows solving MIP as pure LP)\n"); + xprintf(" --first branch on first integer variable\n") + ; + xprintf(" --last branch on last integer variable\n"); + xprintf(" --mostf branch on most fractional variable " + "\n"); + xprintf(" --drtom branch using heuristic by Driebeck " + "and Tomlin\n"); + xprintf(" (default)\n"); + xprintf(" --pcost branch using hybrid pseudocost heur" + "istic (may be\n"); + xprintf(" useful for hard instances)\n"); + xprintf(" --dfs backtrack using depth first search " + "\n"); + xprintf(" --bfs backtrack using breadth first searc" + "h\n"); + xprintf(" --bestp backtrack using the best projection" + " heuristic\n"); + xprintf(" --bestb backtrack using node with best loca" + "l bound\n"); + xprintf(" (default)\n"); + xprintf(" --intopt use MIP presolver (default)\n"); + xprintf(" --nointopt do not use MIP presolver\n"); + xprintf(" --binarize replace general integer variables b" + "y binary ones\n"); + xprintf(" (assumes --intopt)\n"); + xprintf(" --fpump apply feasibility pump heuristic\n") + ; + xprintf(" --gomory generate Gomory's mixed integer cut" + "s\n"); + xprintf(" --mir generate MIR (mixed integer roundin" + "g) cuts\n"); + xprintf(" --cover generate mixed cover cuts\n"); + xprintf(" --clique generate clique cuts\n"); + xprintf(" --cuts generate all cuts above\n"); + xprintf(" --mipgap tol set relative mip gap tolerance to t" + "ol\n"); + xprintf("\n"); + xprintf("For description of the MPS and CPLEX LP formats see Refe" + "rence Manual.\n"); + xprintf("For description of the modeling language see \"GLPK: Mod" + "eling Language\n"); + xprintf("GNU MathProg\". Both documents are included in the GLPK " + "distribution.\n"); + xprintf("\n"); + xprintf("See GLPK web page at .\n"); + xprintf("\n"); + xprintf("Please report bugs to .\n"); + return; +} + +static void print_version(int briefly) +{ /* print version information */ + xprintf("GLPSOL: GLPK LP/MIP Solver, v%s\n", glp_version()); + if (briefly) goto done; + xprintf("\n"); + xprintf("Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, " + "2007, 2008,\n"); + xprintf("2009, 2010 Andrew Makhorin, Department for Applied Infor" + "matics, Moscow\n"); + xprintf("Aviation Institute, Moscow, Russia. All rights reserved." + "\n"); + xprintf("\n"); + xprintf("This program has ABSOLUTELY NO WARRANTY.\n"); + xprintf("\n"); + xprintf("This program is free software; you may re-distribute it " + "under the terms\n"); + xprintf("of the GNU General Public License version 3 or later.\n") + ; +done: return; +} + +static int parse_cmdline(struct csa *csa, int argc, const char *argv[]) +{ /* parse command-line parameters */ + int k; +#define p(str) (strcmp(argv[k], str) == 0) + for (k = 1; k < argc; k++) + { if (p("--mps")) + csa->format = FMT_MPS_DECK; + else if (p("--freemps")) + csa->format = FMT_MPS_FILE; + else if (p("--lp") || p("--cpxlp")) + csa->format = FMT_LP; + else if (p("--glp")) + csa->format = FMT_GLP; + else if (p("--math") || p("-m") || p("--model")) + csa->format = FMT_MATHPROG; + else if (p("-d") || p("--data")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No input data file specified\n"); + return 1; + } + if (csa->ndf == DATA_MAX) + { xprintf("Too many input data files\n"); + return 1; + } + csa->in_data[++(csa->ndf)] = argv[k]; + } + else if (p("-y") || p("--display")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No display output file specified\n"); + return 1; + } + if (csa->out_dpy != NULL) + { xprintf("Only one display output file allowed\n"); + return 1; + } + csa->out_dpy = argv[k]; + } + else if (p("--seed")) + { k++; + if (k == argc || argv[k][0] == '\0' || + argv[k][0] == '-' && !isdigit((unsigned char)argv[k][1])) + { xprintf("No seed value specified\n"); + return 1; + } + if (strcmp(argv[k], "?") == 0) + csa->seed = 0x80000000; + else if (str2int(argv[k], &csa->seed)) + { xprintf("Invalid seed value `%s'\n", argv[k]); + return 1; + } + } + else if (p("--mincost")) + csa->format = FMT_MIN_COST; + else if (p("--maxflow")) + csa->format = FMT_MAX_FLOW; + else if (p("--simplex")) + csa->solution = SOL_BASIC; + else if (p("--interior")) + csa->solution = SOL_INTERIOR; +#if 1 /* 28/V-2010 */ + else if (p("--alien")) + csa->iocp.alien = GLP_ON; +#endif + else if (p("-r") || p("--read")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No input solution file specified\n"); + return 1; + } + if (csa->in_res != NULL) + { xprintf("Only one input solution file allowed\n"); + return 1; + } + csa->in_res = argv[k]; + } + else if (p("--min")) + csa->dir = GLP_MIN; + else if (p("--max")) + csa->dir = GLP_MAX; + else if (p("--scale")) + csa->scale = 1; + else if (p("--noscale")) + csa->scale = 0; + else if (p("-o") || p("--output")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No output solution file specified\n"); + return 1; + } + if (csa->out_sol != NULL) + { xprintf("Only one output solution file allowed\n"); + return 1; + } + csa->out_sol = argv[k]; + } + else if (p("-w") || p("--write")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No output solution file specified\n"); + return 1; + } + if (csa->out_res != NULL) + { xprintf("Only one output solution file allowed\n"); + return 1; + } + csa->out_res = argv[k]; + } + else if (p("--ranges") || p("--bounds")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No output file specified to write sensitivity a" + "nalysis report\n"); + return 1; + } + if (csa->out_ranges != NULL) + { xprintf("Only one output file allowed to write sensitivi" + "ty analysis report\n"); + return 1; + } + csa->out_ranges = argv[k]; + } + else if (p("--tmlim")) + { int tm_lim; + k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No time limit specified\n"); + return 1; + } + if (str2int(argv[k], &tm_lim) || tm_lim < 0) + { xprintf("Invalid time limit `%s'\n", argv[k]); + return 1; + } + if (tm_lim <= INT_MAX / 1000) + csa->smcp.tm_lim = csa->iocp.tm_lim = 1000 * tm_lim; + else + csa->smcp.tm_lim = csa->iocp.tm_lim = INT_MAX; + } + else if (p("--memlim")) + { int mem_lim; + k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No memory limit specified\n"); + return 1; + } + if (str2int(argv[k], &mem_lim) || mem_lim < 1) + { xprintf("Invalid memory limit `%s'\n", argv[k]); + return 1; + } + glp_mem_limit(mem_lim); + } + else if (p("--check")) + csa->check = 1; + else if (p("--name")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No problem name specified\n"); + return 1; + } + if (csa->new_name != NULL) + { xprintf("Only one problem name allowed\n"); + return 1; + } + csa->new_name = argv[k]; + } + else if (p("--wmps")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No fixed MPS output file specified\n"); + return 1; + } + if (csa->out_mps != NULL) + { xprintf("Only one fixed MPS output file allowed\n"); + return 1; + } + csa->out_mps = argv[k]; + } + else if (p("--wfreemps")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No free MPS output file specified\n"); + return 1; + } + if (csa->out_freemps != NULL) + { xprintf("Only one free MPS output file allowed\n"); + return 1; + } + csa->out_freemps = argv[k]; + } + else if (p("--wlp") || p("--wcpxlp") || p("--wlpt")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No CPLEX LP output file specified\n"); + return 1; + } + if (csa->out_cpxlp != NULL) + { xprintf("Only one CPLEX LP output file allowed\n"); + return 1; + } + csa->out_cpxlp = argv[k]; + } + else if (p("--wglp")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No GLPK LP/MIP output file specified\n"); + return 1; + } + if (csa->out_glp != NULL) + { xprintf("Only one GLPK LP/MIP output file allowed\n"); + return 1; + } + csa->out_glp = argv[k]; + } + else if (p("--wpb")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No problem output file specified\n"); + return 1; + } + if (csa->out_pb != NULL) + { xprintf("Only one OPB output file allowed\n"); + return 1; + } + csa->out_pb = argv[k]; + } + else if (p("--wnpb")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No problem output file specified\n"); + return 1; + } + if (csa->out_npb != NULL) + { xprintf("Only one normalized OPB output file allowed\n"); + return 1; + } + csa->out_npb = argv[k]; + } + else if (p("--log")) + { k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No log file specified\n"); + return 1; + } + if (csa->log_file != NULL) + { xprintf("Only one log file allowed\n"); + return 1; + } + csa->log_file = argv[k]; + } + else if (p("-h") || p("--help")) + { print_help(argv[0]); + return -1; + } + else if (p("-v") || p("--version")) + { print_version(0); + return -1; + } + else if (p("--luf")) + csa->bfcp.type = GLP_BF_FT; + else if (p("--cbg")) + csa->bfcp.type = GLP_BF_BG; + else if (p("--cgr")) + csa->bfcp.type = GLP_BF_GR; + else if (p("--primal")) + csa->smcp.meth = GLP_PRIMAL; + else if (p("--dual")) + csa->smcp.meth = GLP_DUAL; + else if (p("--std")) + csa->crash = USE_STD_BASIS; + else if (p("--adv")) + csa->crash = USE_ADV_BASIS; + else if (p("--bib")) + csa->crash = USE_CPX_BASIS; + else if (p("--ini")) + { csa->crash = USE_INI_BASIS; + csa->smcp.presolve = GLP_OFF; + k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No initial basis file specified\n"); + return 1; + } + if (csa->ini_file != NULL) + { xprintf("Only one initial basis file allowed\n"); + return 1; + } + csa->ini_file = argv[k]; + } + else if (p("--steep")) + csa->smcp.pricing = GLP_PT_PSE; + else if (p("--nosteep")) + csa->smcp.pricing = GLP_PT_STD; + else if (p("--relax")) + csa->smcp.r_test = GLP_RT_HAR; + else if (p("--norelax")) + csa->smcp.r_test = GLP_RT_STD; + else if (p("--presol")) + csa->smcp.presolve = GLP_ON; + else if (p("--nopresol")) + csa->smcp.presolve = GLP_OFF; + else if (p("--exact")) + csa->exact = 1; + else if (p("--xcheck")) + csa->xcheck = 1; + else if (p("--nord")) + csa->iptcp.ord_alg = GLP_ORD_NONE; + else if (p("--qmd")) + csa->iptcp.ord_alg = GLP_ORD_QMD; + else if (p("--amd")) + csa->iptcp.ord_alg = GLP_ORD_AMD; + else if (p("--symamd")) + csa->iptcp.ord_alg = GLP_ORD_SYMAMD; + else if (p("--nomip")) + csa->nomip = 1; + else if (p("--first")) + csa->iocp.br_tech = GLP_BR_FFV; + else if (p("--last")) + csa->iocp.br_tech = GLP_BR_LFV; + else if (p("--drtom")) + csa->iocp.br_tech = GLP_BR_DTH; + else if (p("--mostf")) + csa->iocp.br_tech = GLP_BR_MFV; + else if (p("--pcost")) + csa->iocp.br_tech = GLP_BR_PCH; + else if (p("--dfs")) + csa->iocp.bt_tech = GLP_BT_DFS; + else if (p("--bfs")) + csa->iocp.bt_tech = GLP_BT_BFS; + else if (p("--bestp")) + csa->iocp.bt_tech = GLP_BT_BPH; + else if (p("--bestb")) + csa->iocp.bt_tech = GLP_BT_BLB; + else if (p("--intopt")) + csa->iocp.presolve = GLP_ON; + else if (p("--nointopt")) + csa->iocp.presolve = GLP_OFF; + else if (p("--binarize")) + csa->iocp.presolve = csa->iocp.binarize = GLP_ON; + else if (p("--fpump")) + csa->iocp.fp_heur = GLP_ON; + else if (p("--gomory")) + csa->iocp.gmi_cuts = GLP_ON; + else if (p("--mir")) + csa->iocp.mir_cuts = GLP_ON; + else if (p("--cover")) + csa->iocp.cov_cuts = GLP_ON; + else if (p("--clique")) + csa->iocp.clq_cuts = GLP_ON; + else if (p("--cuts")) + csa->iocp.gmi_cuts = csa->iocp.mir_cuts = + csa->iocp.cov_cuts = csa->iocp.clq_cuts = GLP_ON; + else if (p("--mipgap")) + { double mip_gap; + k++; + if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') + { xprintf("No relative gap tolerance specified\n"); + return 1; + } + if (str2num(argv[k], &mip_gap) || mip_gap < 0.0) + { xprintf("Invalid relative mip gap tolerance `%s'\n", + argv[k]); + return 1; + } + csa->iocp.mip_gap = mip_gap; + } + else if (argv[k][0] == '-' || + (argv[k][0] == '-' && argv[k][1] == '-')) + { xprintf("Invalid option `%s'; try %s --help\n", + argv[k], argv[0]); + return 1; + } + else + { if (csa->in_file != NULL) + { xprintf("Only one input problem file allowed\n"); + return 1; + } + csa->in_file = argv[k]; + } + } +#undef p + return 0; +} + +typedef struct { double rhs, pi; } v_data; +typedef struct { double low, cap, cost, x; } a_data; + +int glp_main(int argc, const char *argv[]) +{ /* stand-alone LP/MIP solver */ + struct csa _csa, *csa = &_csa; + int ret; + glp_long start; + /* perform initialization */ + csa->prob = glp_create_prob(); + glp_get_bfcp(csa->prob, &csa->bfcp); + glp_init_smcp(&csa->smcp); + csa->smcp.presolve = GLP_ON; + glp_init_iptcp(&csa->iptcp); + glp_init_iocp(&csa->iocp); + csa->iocp.presolve = GLP_ON; + csa->tran = NULL; + csa->graph = NULL; + csa->format = FMT_MPS_FILE; + csa->in_file = NULL; + csa->ndf = 0; + csa->out_dpy = NULL; + csa->seed = 1; + csa->solution = SOL_BASIC; + csa->in_res = NULL; + csa->dir = 0; + csa->scale = 1; + csa->out_sol = NULL; + csa->out_res = NULL; + csa->out_ranges = NULL; + csa->check = 0; + csa->new_name = NULL; + csa->out_mps = NULL; + csa->out_freemps = NULL; + csa->out_cpxlp = NULL; + csa->out_glp = NULL; + csa->out_pb = NULL; + csa->out_npb = NULL; + csa->log_file = NULL; + csa->crash = USE_ADV_BASIS; + csa->ini_file = NULL; + csa->exact = 0; + csa->xcheck = 0; + csa->nomip = 0; + /* parse command-line parameters */ + ret = parse_cmdline(csa, argc, argv); + if (ret < 0) + { ret = EXIT_SUCCESS; + goto done; + } + if (ret > 0) + { ret = EXIT_FAILURE; + goto done; + } + /*--------------------------------------------------------------*/ + /* remove all output files specified in the command line */ + if (csa->out_dpy != NULL) remove(csa->out_dpy); + if (csa->out_sol != NULL) remove(csa->out_sol); + if (csa->out_res != NULL) remove(csa->out_res); + if (csa->out_ranges != NULL) remove(csa->out_ranges); + if (csa->out_mps != NULL) remove(csa->out_mps); + if (csa->out_freemps != NULL) remove(csa->out_freemps); + if (csa->out_cpxlp != NULL) remove(csa->out_cpxlp); + if (csa->out_glp != NULL) remove(csa->out_glp); + if (csa->out_pb != NULL) remove(csa->out_pb); + if (csa->out_npb != NULL) remove(csa->out_npb); + if (csa->log_file != NULL) remove(csa->log_file); + /*--------------------------------------------------------------*/ + /* open log file, if required */ + if (csa->log_file != NULL) + { if (glp_open_tee(csa->log_file)) + { xprintf("Unable to create log file\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /*--------------------------------------------------------------*/ + /* print version information */ + print_version(1); + /*--------------------------------------------------------------*/ + /* print parameters specified in the command line */ + if (argc > 1) + { int k, len = INT_MAX; + xprintf("Parameter(s) specified in the command line:"); + for (k = 1; k < argc; k++) + { if (len > 72) + xprintf("\n"), len = 0; + xprintf(" %s", argv[k]); + len += 1 + strlen(argv[k]); + } + xprintf("\n"); + } + /*--------------------------------------------------------------*/ + /* read problem data from the input file */ + if (csa->in_file == NULL) + { xprintf("No input problem file specified; try %s --help\n", + argv[0]); + ret = EXIT_FAILURE; + goto done; + } + if (csa->format == FMT_MPS_DECK) + { ret = glp_read_mps(csa->prob, GLP_MPS_DECK, NULL, + csa->in_file); + if (ret != 0) +err1: { xprintf("MPS file processing error\n"); + ret = EXIT_FAILURE; + goto done; + } + } + else if (csa->format == FMT_MPS_FILE) + { ret = glp_read_mps(csa->prob, GLP_MPS_FILE, NULL, + csa->in_file); + if (ret != 0) goto err1; + } + else if (csa->format == FMT_LP) + { ret = glp_read_lp(csa->prob, NULL, csa->in_file); + if (ret != 0) + { xprintf("CPLEX LP file processing error\n"); + ret = EXIT_FAILURE; + goto done; + } + } + else if (csa->format == FMT_GLP) + { ret = glp_read_prob(csa->prob, 0, csa->in_file); + if (ret != 0) + { xprintf("GLPK LP/MIP file processing error\n"); + ret = EXIT_FAILURE; + goto done; + } + } + else if (csa->format == FMT_MATHPROG) + { int k; + /* allocate the translator workspace */ + csa->tran = glp_mpl_alloc_wksp(); + /* set seed value */ + if (csa->seed == 0x80000000) + { csa->seed = glp_time().lo; + xprintf("Seed value %d will be used\n", csa->seed); + } + _glp_mpl_init_rand(csa->tran, csa->seed); + /* read model section and optional data section */ + if (glp_mpl_read_model(csa->tran, csa->in_file, csa->ndf > 0)) +err2: { xprintf("MathProg model processing error\n"); + ret = EXIT_FAILURE; + goto done; + } + /* read optional data section(s), if necessary */ + for (k = 1; k <= csa->ndf; k++) + { if (glp_mpl_read_data(csa->tran, csa->in_data[k])) + goto err2; + } + /* generate the model */ + if (glp_mpl_generate(csa->tran, csa->out_dpy)) goto err2; + /* build the problem instance from the model */ + glp_mpl_build_prob(csa->tran, csa->prob); + } + else if (csa->format == FMT_MIN_COST) + { csa->graph = glp_create_graph(sizeof(v_data), sizeof(a_data)); + ret = glp_read_mincost(csa->graph, offsetof(v_data, rhs), + offsetof(a_data, low), offsetof(a_data, cap), + offsetof(a_data, cost), csa->in_file); + if (ret != 0) + { xprintf("DIMACS file processing error\n"); + ret = EXIT_FAILURE; + goto done; + } + glp_mincost_lp(csa->prob, csa->graph, GLP_ON, + offsetof(v_data, rhs), offsetof(a_data, low), + offsetof(a_data, cap), offsetof(a_data, cost)); + glp_set_prob_name(csa->prob, csa->in_file); + } + else if (csa->format == FMT_MAX_FLOW) + { int s, t; + csa->graph = glp_create_graph(sizeof(v_data), sizeof(a_data)); + ret = glp_read_maxflow(csa->graph, &s, &t, + offsetof(a_data, cap), csa->in_file); + if (ret != 0) + { xprintf("DIMACS file processing error\n"); + ret = EXIT_FAILURE; + goto done; + } + glp_maxflow_lp(csa->prob, csa->graph, GLP_ON, s, t, + offsetof(a_data, cap)); + glp_set_prob_name(csa->prob, csa->in_file); + } + else + xassert(csa != csa); + /*--------------------------------------------------------------*/ + /* change problem name, if required */ + if (csa->new_name != NULL) + glp_set_prob_name(csa->prob, csa->new_name); + /* change optimization direction, if required */ + if (csa->dir != 0) + glp_set_obj_dir(csa->prob, csa->dir); + /* sort elements of the constraint matrix */ + glp_sort_matrix(csa->prob); + /*--------------------------------------------------------------*/ + /* write problem data in fixed MPS format, if required */ + if (csa->out_mps != NULL) + { ret = glp_write_mps(csa->prob, GLP_MPS_DECK, NULL, + csa->out_mps); + if (ret != 0) + { xprintf("Unable to write problem in fixed MPS format\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /* write problem data in free MPS format, if required */ + if (csa->out_freemps != NULL) + { ret = glp_write_mps(csa->prob, GLP_MPS_FILE, NULL, + csa->out_freemps); + if (ret != 0) + { xprintf("Unable to write problem in free MPS format\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /* write problem data in CPLEX LP format, if required */ + if (csa->out_cpxlp != NULL) + { ret = glp_write_lp(csa->prob, NULL, csa->out_cpxlp); + if (ret != 0) + { xprintf("Unable to write problem in CPLEX LP format\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /* write problem data in GLPK format, if required */ + if (csa->out_glp != NULL) + { ret = glp_write_prob(csa->prob, 0, csa->out_glp); + if (ret != 0) + { xprintf("Unable to write problem in GLPK format\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /* write problem data in OPB format, if required */ + if (csa->out_pb != NULL) + { ret = lpx_write_pb(csa->prob, csa->out_pb, 0, 0); + if (ret != 0) + { xprintf("Unable to write problem in OPB format\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /* write problem data in normalized OPB format, if required */ + if (csa->out_npb != NULL) + { ret = lpx_write_pb(csa->prob, csa->out_npb, 1, 1); + if (ret != 0) + { xprintf( + "Unable to write problem in normalized OPB format\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /*--------------------------------------------------------------*/ + /* if only problem data check is required, skip computations */ + if (csa->check) + { ret = EXIT_SUCCESS; + goto done; + } + /*--------------------------------------------------------------*/ + /* determine the solution type */ + if (!csa->nomip && + glp_get_num_int(csa->prob) + glp_get_num_bin(csa->prob) > 0) + { if (csa->solution == SOL_INTERIOR) + { xprintf("Interior-point method is not able to solve MIP pro" + "blem; use --simplex\n"); + ret = EXIT_FAILURE; + goto done; + } + csa->solution = SOL_INTEGER; + } + /*--------------------------------------------------------------*/ + /* if solution is provided, read it and skip computations */ + if (csa->in_res != NULL) + { if (csa->solution == SOL_BASIC) + ret = glp_read_sol(csa->prob, csa->in_res); + else if (csa->solution == SOL_INTERIOR) + ret = glp_read_ipt(csa->prob, csa->in_res); + else if (csa->solution == SOL_INTEGER) + ret = glp_read_mip(csa->prob, csa->in_res); + else + xassert(csa != csa); + if (ret != 0) + { xprintf("Unable to read problem solution\n"); + ret = EXIT_FAILURE; + goto done; + } + goto skip; + } + /*--------------------------------------------------------------*/ + /* scale the problem data, if required */ + if (csa->scale) + { if (csa->solution == SOL_BASIC && !csa->smcp.presolve || + csa->solution == SOL_INTERIOR || + csa->solution == SOL_INTEGER && !csa->iocp.presolve) + glp_scale_prob(csa->prob, GLP_SF_AUTO); + } + /*--------------------------------------------------------------*/ + /* construct starting LP basis */ + if (csa->solution == SOL_BASIC && !csa->smcp.presolve || + csa->solution == SOL_INTEGER && !csa->iocp.presolve) + { if (csa->crash == USE_STD_BASIS) + glp_std_basis(csa->prob); + else if (csa->crash == USE_ADV_BASIS) + glp_adv_basis(csa->prob, 0); + else if (csa->crash == USE_CPX_BASIS) + glp_cpx_basis(csa->prob); + else if (csa->crash == USE_INI_BASIS) + { ret = glp_read_sol(csa->prob, csa->ini_file); + if (ret != 0) + { xprintf("Unable to read initial basis\n"); + ret = EXIT_FAILURE; + goto done; + } + } + else + xassert(csa != csa); + } + /*--------------------------------------------------------------*/ + /* solve the problem */ + start = xtime(); + if (csa->solution == SOL_BASIC) + { if (!csa->exact) + { glp_set_bfcp(csa->prob, &csa->bfcp); + glp_simplex(csa->prob, &csa->smcp); + if (csa->xcheck) + { if (csa->smcp.presolve && + glp_get_status(csa->prob) != GLP_OPT) + xprintf("If you need to check final basis for non-opt" + "imal solution, use --nopresol\n"); + else + glp_exact(csa->prob, &csa->smcp); + } + if (csa->out_sol != NULL || csa->out_res != NULL) + { if (csa->smcp.presolve && + glp_get_status(csa->prob) != GLP_OPT) + xprintf("If you need actual output for non-optimal solut" + "ion, use --nopresol\n"); + } + } + else + glp_exact(csa->prob, &csa->smcp); + } + else if (csa->solution == SOL_INTERIOR) + glp_interior(csa->prob, &csa->iptcp); + else if (csa->solution == SOL_INTEGER) + { if (!csa->iocp.presolve) + { glp_set_bfcp(csa->prob, &csa->bfcp); + glp_simplex(csa->prob, &csa->smcp); + } +#if 0 + csa->iocp.msg_lev = GLP_MSG_DBG; + csa->iocp.pp_tech = GLP_PP_NONE; +#endif + glp_intopt(csa->prob, &csa->iocp); + } + else + xassert(csa != csa); + /*--------------------------------------------------------------*/ + /* display statistics */ + xprintf("Time used: %.1f secs\n", xdifftime(xtime(), start)); + { glp_long tpeak; + char buf[50]; + glp_mem_usage(NULL, NULL, NULL, &tpeak); + xprintf("Memory used: %.1f Mb (%s bytes)\n", + xltod(tpeak) / 1048576.0, xltoa(tpeak, buf)); + } + /*--------------------------------------------------------------*/ +skip: /* postsolve the model, if necessary */ + if (csa->tran != NULL) + { if (csa->solution == SOL_BASIC) + ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_SOL); + else if (csa->solution == SOL_INTERIOR) + ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_IPT); + else if (csa->solution == SOL_INTEGER) + ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_MIP); + else + xassert(csa != csa); + if (ret != 0) + { xprintf("Model postsolving error\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /*--------------------------------------------------------------*/ + /* write problem solution in printable format, if required */ + if (csa->out_sol != NULL) + { if (csa->solution == SOL_BASIC) + ret = lpx_print_sol(csa->prob, csa->out_sol); + else if (csa->solution == SOL_INTERIOR) + ret = lpx_print_ips(csa->prob, csa->out_sol); + else if (csa->solution == SOL_INTEGER) + ret = lpx_print_mip(csa->prob, csa->out_sol); + else + xassert(csa != csa); + if (ret != 0) + { xprintf("Unable to write problem solution\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /* write problem solution in printable format, if required */ + if (csa->out_res != NULL) + { if (csa->solution == SOL_BASIC) + ret = glp_write_sol(csa->prob, csa->out_res); + else if (csa->solution == SOL_INTERIOR) + ret = glp_write_ipt(csa->prob, csa->out_res); + else if (csa->solution == SOL_INTEGER) + ret = glp_write_mip(csa->prob, csa->out_res); + else + xassert(csa != csa); + if (ret != 0) + { xprintf("Unable to write problem solution\n"); + ret = EXIT_FAILURE; + goto done; + } + } + /* write sensitivity analysis report, if required */ + if (csa->out_ranges != NULL) + { if (csa->solution == SOL_BASIC) + { if (glp_get_status(csa->prob) == GLP_OPT) + { if (glp_bf_exists(csa->prob)) +ranges: { ret = glp_print_ranges(csa->prob, 0, NULL, 0, + csa->out_ranges); + if (ret != 0) + { xprintf("Unable to write sensitivity analysis repo" + "rt\n"); + ret = EXIT_FAILURE; + goto done; + } + } + else + { ret = glp_factorize(csa->prob); + if (ret == 0) goto ranges; + xprintf("Cannot produce sensitivity analysis report d" + "ue to error in basis factorization (glp_factorize" + " returned %d); try --nopresol\n", ret); + } + } + else + xprintf("Cannot produce sensitivity analysis report for " + "non-optimal basic solution\n"); + } + else + xprintf("Cannot produce sensitivity analysis report for int" + "erior-point or MIP solution\n"); + } + /*--------------------------------------------------------------*/ + /* all seems to be ok */ + ret = EXIT_SUCCESS; + /*--------------------------------------------------------------*/ +done: /* delete the LP/MIP problem object */ + if (csa->prob != NULL) + glp_delete_prob(csa->prob); + /* free the translator workspace, if necessary */ + if (csa->tran != NULL) + glp_mpl_free_wksp(csa->tran); + /* delete the network problem object, if necessary */ + if (csa->graph != NULL) + glp_delete_graph(csa->graph); + xassert(gmp_pool_count() == 0); + gmp_free_mem(); + /* close log file, if necessary */ + if (csa->log_file != NULL) glp_close_tee(); + /* check that no memory blocks are still allocated */ + { int count; + glp_long total; + glp_mem_usage(&count, NULL, &total, NULL); + if (count != 0) + xerror("Error: %d memory block(s) were lost\n", count); + xassert(count == 0); + xassert(total.lo == 0 && total.hi == 0); + } + /* free the GLPK environment */ + glp_free_env(); + /* return to the control program */ + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpavl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpavl.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,357 @@ +/* glpavl.c (binary search tree) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpavl.h" + +AVL *avl_create_tree(int (*fcmp)(void *info, const void *key1, + const void *key2), void *info) +{ /* create AVL tree */ + AVL *tree; + tree = xmalloc(sizeof(AVL)); + tree->pool = dmp_create_pool(); + tree->root = NULL; + tree->fcmp = fcmp; + tree->info = info; + tree->size = 0; + tree->height = 0; + return tree; +} + +int avl_strcmp(void *info, const void *key1, const void *key2) +{ /* compare character string keys */ + xassert(info == info); + return strcmp(key1, key2); +} + +static AVLNODE *rotate_subtree(AVL *tree, AVLNODE *node); + +AVLNODE *avl_insert_node(AVL *tree, const void *key) +{ /* insert new node into AVL tree */ + AVLNODE *p, *q, *r; + short int flag; + /* find an appropriate point for insertion */ + p = NULL; q = tree->root; + while (q != NULL) + { p = q; + if (tree->fcmp(tree->info, key, p->key) <= 0) + { flag = 0; + q = p->left; + p->rank++; + } + else + { flag = 1; + q = p->right; + } + } + /* create new node and insert it into the tree */ + r = dmp_get_atom(tree->pool, sizeof(AVLNODE)); + r->key = key; r->type = 0; r->link = NULL; + r->rank = 1; r->up = p; + r->flag = (short int)(p == NULL ? 0 : flag); + r->bal = 0; r->left = NULL; r->right = NULL; + tree->size++; + if (p == NULL) + tree->root = r; + else + if (flag == 0) p->left = r; else p->right = r; + /* go upstairs to the root and correct all subtrees affected by + insertion */ + while (p != NULL) + { if (flag == 0) + { /* the height of the left subtree of [p] is increased */ + if (p->bal > 0) + { p->bal = 0; + break; + } + if (p->bal < 0) + { rotate_subtree(tree, p); + break; + } + p->bal = -1; flag = p->flag; p = p->up; + } + else + { /* the height of the right subtree of [p] is increased */ + if (p->bal < 0) + { p->bal = 0; + break; + } + if (p->bal > 0) + { rotate_subtree(tree, p); + break; + } + p->bal = +1; flag = p->flag; p = p->up; + } + } + /* if the root has been reached, the height of the entire tree is + increased */ + if (p == NULL) tree->height++; + return r; +} + +void avl_set_node_type(AVLNODE *node, int type) +{ /* assign the type field of specified node */ + node->type = type; + return; +} + +void avl_set_node_link(AVLNODE *node, void *link) +{ /* assign the link field of specified node */ + node->link = link; + return; +} + +AVLNODE *avl_find_node(AVL *tree, const void *key) +{ /* find node in AVL tree */ + AVLNODE *p; + int c; + p = tree->root; + while (p != NULL) + { c = tree->fcmp(tree->info, key, p->key); + if (c == 0) break; + p = (c < 0 ? p->left : p->right); + } + return p; +} + +int avl_get_node_type(AVLNODE *node) +{ /* retrieve the type field of specified node */ + return node->type; +} + +void *avl_get_node_link(AVLNODE *node) +{ /* retrieve the link field of specified node */ + return node->link; +} + +static AVLNODE *find_next_node(AVL *tree, AVLNODE *node) +{ /* find next node in AVL tree */ + AVLNODE *p, *q; + if (tree->root == NULL) return NULL; + p = node; + q = (p == NULL ? tree->root : p->right); + if (q == NULL) + { /* go upstairs from the left subtree */ + for (;;) + { q = p->up; + if (q == NULL) break; + if (p->flag == 0) break; + p = q; + } + } + else + { /* go downstairs into the right subtree */ + for (;;) + { p = q->left; + if (p == NULL) break; + q = p; + } + } + return q; +} + +void avl_delete_node(AVL *tree, AVLNODE *node) +{ /* delete specified node from AVL tree */ + AVLNODE *f, *p, *q, *r, *s, *x, *y; + short int flag; + p = node; + /* if both subtrees of the specified node are non-empty, the node + should be interchanged with the next one, at least one subtree + of which is always empty */ + if (p->left == NULL || p->right == NULL) goto skip; + f = p->up; q = p->left; + r = find_next_node(tree, p); s = r->right; + if (p->right == r) + { if (f == NULL) + tree->root = r; + else + if (p->flag == 0) f->left = r; else f->right = r; + r->rank = p->rank; r->up = f; + r->flag = p->flag; r->bal = p->bal; + r->left = q; r->right = p; + q->up = r; + p->rank = 1; p->up = r; p->flag = 1; + p->bal = (short int)(s == NULL ? 0 : +1); + p->left = NULL; p->right = s; + if (s != NULL) s->up = p; + } + else + { x = p->right; y = r->up; + if (f == NULL) + tree->root = r; + else + if (p->flag == 0) f->left = r; else f->right = r; + r->rank = p->rank; r->up = f; + r->flag = p->flag; r->bal = p->bal; + r->left = q; r->right = x; + q->up = r; x->up = r; y->left = p; + p->rank = 1; p->up = y; p->flag = 0; + p->bal = (short int)(s == NULL ? 0 : +1); + p->left = NULL; p->right = s; + if (s != NULL) s->up = p; + } +skip: /* now the specified node [p] has at least one empty subtree; + go upstairs to the root and adjust the rank field of all nodes + affected by deletion */ + q = p; f = q->up; + while (f != NULL) + { if (q->flag == 0) f->rank--; + q = f; f = q->up; + } + /* delete the specified node from the tree */ + f = p->up; flag = p->flag; + q = p->left != NULL ? p->left : p->right; + if (f == NULL) + tree->root = q; + else + if (flag == 0) f->left = q; else f->right = q; + if (q != NULL) q->up = f, q->flag = flag; + tree->size--; + /* go upstairs to the root and correct all subtrees affected by + deletion */ + while (f != NULL) + { if (flag == 0) + { /* the height of the left subtree of [f] is decreased */ + if (f->bal == 0) + { f->bal = +1; + break; + } + if (f->bal < 0) + f->bal = 0; + else + { f = rotate_subtree(tree, f); + if (f->bal < 0) break; + } + flag = f->flag; f = f->up; + } + else + { /* the height of the right subtree of [f] is decreased */ + if (f->bal == 0) + { f->bal = -1; + break; + } + if (f->bal > 0) + f->bal = 0; + else + { f = rotate_subtree(tree, f); + if (f->bal > 0) break; + } + flag = f->flag; f = f->up; + } + } + /* if the root has been reached, the height of the entire tree is + decreased */ + if (f == NULL) tree->height--; + /* returns the deleted node to the memory pool */ + dmp_free_atom(tree->pool, p, sizeof(AVLNODE)); + return; +} + +static AVLNODE *rotate_subtree(AVL *tree, AVLNODE *node) +{ /* restore balance of AVL subtree */ + AVLNODE *f, *p, *q, *r, *x, *y; + xassert(node != NULL); + p = node; + if (p->bal < 0) + { /* perform negative (left) rotation */ + f = p->up; q = p->left; r = q->right; + if (q->bal <= 0) + { /* perform single negative rotation */ + if (f == NULL) + tree->root = q; + else + if (p->flag == 0) f->left = q; else f->right = q; + p->rank -= q->rank; + q->up = f; q->flag = p->flag; q->bal++; q->right = p; + p->up = q; p->flag = 1; + p->bal = (short int)(-q->bal); p->left = r; + if (r != NULL) r->up = p, r->flag = 0; + node = q; + } + else + { /* perform double negative rotation */ + x = r->left; y = r->right; + if (f == NULL) + tree->root = r; + else + if (p->flag == 0) f->left = r; else f->right = r; + p->rank -= (q->rank + r->rank); + r->rank += q->rank; + p->bal = (short int)(r->bal >= 0 ? 0 : +1); + q->bal = (short int)(r->bal <= 0 ? 0 : -1); + r->up = f; r->flag = p->flag; r->bal = 0; + r->left = q; r->right = p; + p->up = r; p->flag = 1; p->left = y; + q->up = r; q->flag = 0; q->right = x; + if (x != NULL) x->up = q, x->flag = 1; + if (y != NULL) y->up = p, y->flag = 0; + node = r; + } + } + else + { /* perform positive (right) rotation */ + f = p->up; q = p->right; r = q->left; + if (q->bal >= 0) + { /* perform single positive rotation */ + if (f == NULL) + tree->root = q; + else + if (p->flag == 0) f->left = q; else f->right = q; + q->rank += p->rank; + q->up = f; q->flag = p->flag; q->bal--; q->left = p; + p->up = q; p->flag = 0; + p->bal = (short int)(-q->bal); p->right = r; + if (r != NULL) r->up = p, r->flag = 1; + node = q; + } + else + { /* perform double positive rotation */ + x = r->left; y = r->right; + if (f == NULL) + tree->root = r; + else + if (p->flag == 0) f->left = r; else f->right = r; + q->rank -= r->rank; + r->rank += p->rank; + p->bal = (short int)(r->bal <= 0 ? 0 : -1); + q->bal = (short int)(r->bal >= 0 ? 0 : +1); + r->up = f; r->flag = p->flag; r->bal = 0; + r->left = p; r->right = q; + p->up = r; p->flag = 0; p->right = x; + q->up = r; q->flag = 1; q->left = y; + if (x != NULL) x->up = p, x->flag = 1; + if (y != NULL) y->up = q, y->flag = 0; + node = r; + } + } + return node; +} + +void avl_delete_tree(AVL *tree) +{ /* delete AVL tree */ + dmp_delete_pool(tree->pool); + xfree(tree); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpavl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpavl.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,123 @@ +/* glpavl.h (binary search tree) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPAVL_H +#define GLPAVL_H + +#include "glpdmp.h" + +typedef struct AVL AVL; +typedef struct AVLNODE AVLNODE; + +struct AVL +{ /* AVL tree (Adelson-Velsky & Landis binary search tree) */ + DMP *pool; + /* memory pool for allocating nodes */ + AVLNODE *root; + /* pointer to the root node */ + int (*fcmp)(void *info, const void *key1, const void *key2); + /* application-defined key comparison routine */ + void *info; + /* transit pointer passed to the routine fcmp */ + int size; + /* the tree size (the total number of nodes) */ + int height; + /* the tree height */ +}; + +struct AVLNODE +{ /* node of AVL tree */ + const void *key; + /* pointer to the node key (data structure for representing keys + is supplied by the application) */ + int rank; + /* node rank = relative position of the node in its own subtree = + the number of nodes in the left subtree plus one */ + int type; + /* reserved for the application specific information */ + void *link; + /* reserved for the application specific information */ + AVLNODE *up; + /* pointer to the parent node */ + short int flag; + /* node flag: + 0 - this node is the left child of its parent (or this node is + the root of the tree and has no parent) + 1 - this node is the right child of its parent */ + short int bal; + /* node balance = the difference between heights of the right and + left subtrees: + -1 - the left subtree is higher than the right one; + 0 - the left and right subtrees have the same height; + +1 - the left subtree is lower than the right one */ + AVLNODE *left; + /* pointer to the root of the left subtree */ + AVLNODE *right; + /* pointer to the root of the right subtree */ +}; + +#define avl_create_tree _glp_avl_create_tree +AVL *avl_create_tree(int (*fcmp)(void *info, const void *key1, + const void *key2), void *info); +/* create AVL tree */ + +#define avl_strcmp _glp_avl_strcmp +int avl_strcmp(void *info, const void *key1, const void *key2); +/* compare character string keys */ + +#define avl_insert_node _glp_avl_insert_node +AVLNODE *avl_insert_node(AVL *tree, const void *key); +/* insert new node into AVL tree */ + +#define avl_set_node_type _glp_avl_set_node_type +void avl_set_node_type(AVLNODE *node, int type); +/* assign the type field of specified node */ + +#define avl_set_node_link _glp_avl_set_node_link +void avl_set_node_link(AVLNODE *node, void *link); +/* assign the link field of specified node */ + +#define avl_find_node _glp_avl_find_node +AVLNODE *avl_find_node(AVL *tree, const void *key); +/* find node in AVL tree */ + +#define avl_get_node_type _glp_avl_get_node_type +int avl_get_node_type(AVLNODE *node); +/* retrieve the type field of specified node */ + +#define avl_get_node_link _glp_avl_get_node_link +void *avl_get_node_link(AVLNODE *node); +/* retrieve the link field of specified node */ + +#define avl_delete_node _glp_avl_delete_node +void avl_delete_node(AVL *tree, AVLNODE *node); +/* delete specified node from AVL tree */ + +#define avl_delete_tree _glp_avl_delete_tree +void avl_delete_tree(AVL *tree); +/* delete AVL tree */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpbfd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpbfd.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,481 @@ +/* glpbfd.c (LP basis factorization driver) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +typedef struct BFD BFD; + +#define GLPBFD_PRIVATE +#include "glpapi.h" +#include "glpfhv.h" +#include "glplpf.h" + +/* CAUTION: DO NOT CHANGE THE LIMIT BELOW */ + +#define M_MAX 100000000 /* = 100*10^6 */ +/* maximal order of the basis matrix */ + +struct BFD +{ /* LP basis factorization */ + int valid; + /* factorization is valid only if this flag is set */ + int type; + /* factorization type: + GLP_BF_FT - LUF + Forrest-Tomlin + GLP_BF_BG - LUF + Schur compl. + Bartels-Golub + GLP_BF_GR - LUF + Schur compl. + Givens rotation */ + FHV *fhv; + /* LP basis factorization (GLP_BF_FT) */ + LPF *lpf; + /* LP basis factorization (GLP_BF_BG, GLP_BF_GR) */ + int lu_size; /* luf.sv_size */ + double piv_tol; /* luf.piv_tol */ + int piv_lim; /* luf.piv_lim */ + int suhl; /* luf.suhl */ + double eps_tol; /* luf.eps_tol */ + double max_gro; /* luf.max_gro */ + int nfs_max; /* fhv.hh_max */ + double upd_tol; /* fhv.upd_tol */ + int nrs_max; /* lpf.n_max */ + int rs_size; /* lpf.v_size */ + /* internal control parameters */ + int upd_lim; + /* the factorization update limit */ + int upd_cnt; + /* the factorization update count */ +}; + +/*********************************************************************** +* NAME +* +* bfd_create_it - create LP basis factorization +* +* SYNOPSIS +* +* #include "glpbfd.h" +* BFD *bfd_create_it(void); +* +* DESCRIPTION +* +* The routine bfd_create_it creates a program object, which represents +* a factorization of LP basis. +* +* RETURNS +* +* The routine bfd_create_it returns a pointer to the object created. */ + +BFD *bfd_create_it(void) +{ BFD *bfd; + bfd = xmalloc(sizeof(BFD)); + bfd->valid = 0; + bfd->type = GLP_BF_FT; + bfd->fhv = NULL; + bfd->lpf = NULL; + bfd->lu_size = 0; + bfd->piv_tol = 0.10; + bfd->piv_lim = 4; + bfd->suhl = 1; + bfd->eps_tol = 1e-15; + bfd->max_gro = 1e+10; + bfd->nfs_max = 100; + bfd->upd_tol = 1e-6; + bfd->nrs_max = 100; + bfd->rs_size = 1000; + bfd->upd_lim = -1; + bfd->upd_cnt = 0; + return bfd; +} + +/**********************************************************************/ + +void bfd_set_parm(BFD *bfd, const void *_parm) +{ /* change LP basis factorization control parameters */ + const glp_bfcp *parm = _parm; + xassert(bfd != NULL); + bfd->type = parm->type; + bfd->lu_size = parm->lu_size; + bfd->piv_tol = parm->piv_tol; + bfd->piv_lim = parm->piv_lim; + bfd->suhl = parm->suhl; + bfd->eps_tol = parm->eps_tol; + bfd->max_gro = parm->max_gro; + bfd->nfs_max = parm->nfs_max; + bfd->upd_tol = parm->upd_tol; + bfd->nrs_max = parm->nrs_max; + bfd->rs_size = parm->rs_size; + return; +} + +/*********************************************************************** +* NAME +* +* bfd_factorize - compute LP basis factorization +* +* SYNOPSIS +* +* #include "glpbfd.h" +* int bfd_factorize(BFD *bfd, int m, int bh[], int (*col)(void *info, +* int j, int ind[], double val[]), void *info); +* +* DESCRIPTION +* +* The routine bfd_factorize computes the factorization of the basis +* matrix B specified by the routine col. +* +* The parameter bfd specified the basis factorization data structure +* created with the routine bfd_create_it. +* +* The parameter m specifies the order of B, m > 0. +* +* The array bh specifies the basis header: bh[j], 1 <= j <= m, is the +* number of j-th column of B in some original matrix. The array bh is +* optional and can be specified as NULL. +* +* The formal routine col specifies the matrix B to be factorized. To +* obtain j-th column of A the routine bfd_factorize calls the routine +* col with the parameter j (1 <= j <= n). In response the routine col +* should store row indices and numerical values of non-zero elements +* of j-th column of B to locations ind[1,...,len] and val[1,...,len], +* respectively, where len is the number of non-zeros in j-th column +* returned on exit. Neither zero nor duplicate elements are allowed. +* +* The parameter info is a transit pointer passed to the routine col. +* +* RETURNS +* +* 0 The factorization has been successfully computed. +* +* BFD_ESING +* The specified matrix is singular within the working precision. +* +* BFD_ECOND +* The specified matrix is ill-conditioned. +* +* For more details see comments to the routine luf_factorize. */ + +int bfd_factorize(BFD *bfd, int m, const int bh[], int (*col) + (void *info, int j, int ind[], double val[]), void *info) +{ LUF *luf; + int nov, ret; + xassert(bfd != NULL); + xassert(1 <= m && m <= M_MAX); + /* invalidate the factorization */ + bfd->valid = 0; + /* create the factorization, if necessary */ + nov = 0; + switch (bfd->type) + { case GLP_BF_FT: + if (bfd->lpf != NULL) + lpf_delete_it(bfd->lpf), bfd->lpf = NULL; + if (bfd->fhv == NULL) + bfd->fhv = fhv_create_it(), nov = 1; + break; + case GLP_BF_BG: + case GLP_BF_GR: + if (bfd->fhv != NULL) + fhv_delete_it(bfd->fhv), bfd->fhv = NULL; + if (bfd->lpf == NULL) + bfd->lpf = lpf_create_it(), nov = 1; + break; + default: + xassert(bfd != bfd); + } + /* set control parameters specific to LUF */ + if (bfd->fhv != NULL) + luf = bfd->fhv->luf; + else if (bfd->lpf != NULL) + luf = bfd->lpf->luf; + else + xassert(bfd != bfd); + if (nov) luf->new_sva = bfd->lu_size; + luf->piv_tol = bfd->piv_tol; + luf->piv_lim = bfd->piv_lim; + luf->suhl = bfd->suhl; + luf->eps_tol = bfd->eps_tol; + luf->max_gro = bfd->max_gro; + /* set control parameters specific to FHV */ + if (bfd->fhv != NULL) + { if (nov) bfd->fhv->hh_max = bfd->nfs_max; + bfd->fhv->upd_tol = bfd->upd_tol; + } + /* set control parameters specific to LPF */ + if (bfd->lpf != NULL) + { if (nov) bfd->lpf->n_max = bfd->nrs_max; + if (nov) bfd->lpf->v_size = bfd->rs_size; + } + /* try to factorize the basis matrix */ + if (bfd->fhv != NULL) + { switch (fhv_factorize(bfd->fhv, m, col, info)) + { case 0: + break; + case FHV_ESING: + ret = BFD_ESING; + goto done; + case FHV_ECOND: + ret = BFD_ECOND; + goto done; + default: + xassert(bfd != bfd); + } + } + else if (bfd->lpf != NULL) + { switch (lpf_factorize(bfd->lpf, m, bh, col, info)) + { case 0: + /* set the Schur complement update type */ + switch (bfd->type) + { case GLP_BF_BG: + /* Bartels-Golub update */ + bfd->lpf->scf->t_opt = SCF_TBG; + break; + case GLP_BF_GR: + /* Givens rotation update */ + bfd->lpf->scf->t_opt = SCF_TGR; + break; + default: + xassert(bfd != bfd); + } + break; + case LPF_ESING: + ret = BFD_ESING; + goto done; + case LPF_ECOND: + ret = BFD_ECOND; + goto done; + default: + xassert(bfd != bfd); + } + } + else + xassert(bfd != bfd); + /* the basis matrix has been successfully factorized */ + bfd->valid = 1; + bfd->upd_cnt = 0; + ret = 0; +done: /* return to the calling program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* bfd_ftran - perform forward transformation (solve system B*x = b) +* +* SYNOPSIS +* +* #include "glpbfd.h" +* void bfd_ftran(BFD *bfd, double x[]); +* +* DESCRIPTION +* +* The routine bfd_ftran performs forward transformation, i.e. solves +* the system B*x = b, where B is the basis matrix, x is the vector of +* unknowns to be computed, b is the vector of right-hand sides. +* +* On entry elements of the vector b should be stored in dense format +* in locations x[1], ..., x[m], where m is the number of rows. On exit +* the routine stores elements of the vector x in the same locations. */ + +void bfd_ftran(BFD *bfd, double x[]) +{ xassert(bfd != NULL); + xassert(bfd->valid); + if (bfd->fhv != NULL) + fhv_ftran(bfd->fhv, x); + else if (bfd->lpf != NULL) + lpf_ftran(bfd->lpf, x); + else + xassert(bfd != bfd); + return; +} + +/*********************************************************************** +* NAME +* +* bfd_btran - perform backward transformation (solve system B'*x = b) +* +* SYNOPSIS +* +* #include "glpbfd.h" +* void bfd_btran(BFD *bfd, double x[]); +* +* DESCRIPTION +* +* The routine bfd_btran performs backward transformation, i.e. solves +* the system B'*x = b, where B' is a matrix transposed to the basis +* matrix B, x is the vector of unknowns to be computed, b is the vector +* of right-hand sides. +* +* On entry elements of the vector b should be stored in dense format +* in locations x[1], ..., x[m], where m is the number of rows. On exit +* the routine stores elements of the vector x in the same locations. */ + +void bfd_btran(BFD *bfd, double x[]) +{ xassert(bfd != NULL); + xassert(bfd->valid); + if (bfd->fhv != NULL) + fhv_btran(bfd->fhv, x); + else if (bfd->lpf != NULL) + lpf_btran(bfd->lpf, x); + else + xassert(bfd != bfd); + return; +} + +/*********************************************************************** +* NAME +* +* bfd_update_it - update LP basis factorization +* +* SYNOPSIS +* +* #include "glpbfd.h" +* int bfd_update_it(BFD *bfd, int j, int bh, int len, const int ind[], +* const double val[]); +* +* DESCRIPTION +* +* The routine bfd_update_it updates the factorization of the basis +* matrix B after replacing its j-th column by a new vector. +* +* The parameter j specifies the number of column of B, which has been +* replaced, 1 <= j <= m, where m is the order of B. +* +* The parameter bh specifies the basis header entry for the new column +* of B, which is the number of the new column in some original matrix. +* This parameter is optional and can be specified as 0. +* +* Row indices and numerical values of non-zero elements of the new +* column of B should be placed in locations ind[1], ..., ind[len] and +* val[1], ..., val[len], resp., where len is the number of non-zeros +* in the column. Neither zero nor duplicate elements are allowed. +* +* RETURNS +* +* 0 The factorization has been successfully updated. +* +* BFD_ESING +* New basis matrix is singular within the working precision. +* +* BFD_ECHECK +* The factorization is inaccurate. +* +* BFD_ELIMIT +* Factorization update limit has been reached. +* +* BFD_EROOM +* Overflow of the sparse vector area. +* +* In case of non-zero return code the factorization becomes invalid. +* It should not be used until it has been recomputed with the routine +* bfd_factorize. */ + +int bfd_update_it(BFD *bfd, int j, int bh, int len, const int ind[], + const double val[]) +{ int ret; + xassert(bfd != NULL); + xassert(bfd->valid); + /* try to update the factorization */ + if (bfd->fhv != NULL) + { switch (fhv_update_it(bfd->fhv, j, len, ind, val)) + { case 0: + break; + case FHV_ESING: + bfd->valid = 0; + ret = BFD_ESING; + goto done; + case FHV_ECHECK: + bfd->valid = 0; + ret = BFD_ECHECK; + goto done; + case FHV_ELIMIT: + bfd->valid = 0; + ret = BFD_ELIMIT; + goto done; + case FHV_EROOM: + bfd->valid = 0; + ret = BFD_EROOM; + goto done; + default: + xassert(bfd != bfd); + } + } + else if (bfd->lpf != NULL) + { switch (lpf_update_it(bfd->lpf, j, bh, len, ind, val)) + { case 0: + break; + case LPF_ESING: + bfd->valid = 0; + ret = BFD_ESING; + goto done; + case LPF_ELIMIT: + bfd->valid = 0; + ret = BFD_ELIMIT; + goto done; + default: + xassert(bfd != bfd); + } + } + else + xassert(bfd != bfd); + /* the factorization has been successfully updated */ + /* increase the update count */ + bfd->upd_cnt++; + ret = 0; +done: /* return to the calling program */ + return ret; +} + +/**********************************************************************/ + +int bfd_get_count(BFD *bfd) +{ /* determine factorization update count */ + xassert(bfd != NULL); + xassert(bfd->valid); + return bfd->upd_cnt; +} + +/*********************************************************************** +* NAME +* +* bfd_delete_it - delete LP basis factorization +* +* SYNOPSIS +* +* #include "glpbfd.h" +* void bfd_delete_it(BFD *bfd); +* +* DESCRIPTION +* +* The routine bfd_delete_it deletes LP basis factorization specified +* by the parameter fhv and frees all memory allocated to this program +* object. */ + +void bfd_delete_it(BFD *bfd) +{ xassert(bfd != NULL); + if (bfd->fhv != NULL) + fhv_delete_it(bfd->fhv); + if (bfd->lpf != NULL) + lpf_delete_it(bfd->lpf); + xfree(bfd); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpbfd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpbfd.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,75 @@ +/* glpbfd.h (LP basis factorization driver) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPBFD_H +#define GLPBFD_H + +#ifndef GLPBFD_PRIVATE +typedef struct { double _opaque_bfd[100]; } BFD; +#endif + +/* return codes: */ +#define BFD_ESING 1 /* singular matrix */ +#define BFD_ECOND 2 /* ill-conditioned matrix */ +#define BFD_ECHECK 3 /* insufficient accuracy */ +#define BFD_ELIMIT 4 /* update limit reached */ +#define BFD_EROOM 5 /* SVA overflow */ + +#define bfd_create_it _glp_bfd_create_it +BFD *bfd_create_it(void); +/* create LP basis factorization */ + +#define bfd_set_parm _glp_bfd_set_parm +void bfd_set_parm(BFD *bfd, const void *parm); +/* change LP basis factorization control parameters */ + +#define bfd_factorize _glp_bfd_factorize +int bfd_factorize(BFD *bfd, int m, const int bh[], int (*col) + (void *info, int j, int ind[], double val[]), void *info); +/* compute LP basis factorization */ + +#define bfd_ftran _glp_bfd_ftran +void bfd_ftran(BFD *bfd, double x[]); +/* perform forward transformation (solve system B*x = b) */ + +#define bfd_btran _glp_bfd_btran +void bfd_btran(BFD *bfd, double x[]); +/* perform backward transformation (solve system B'*x = b) */ + +#define bfd_update_it _glp_bfd_update_it +int bfd_update_it(BFD *bfd, int j, int bh, int len, const int ind[], + const double val[]); +/* update LP basis factorization */ + +#define bfd_get_count _glp_bfd_get_count +int bfd_get_count(BFD *bfd); +/* determine factorization update count */ + +#define bfd_delete_it _glp_bfd_delete_it +void bfd_delete_it(BFD *bfd); +/* delete LP basis factorization */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpbfx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpbfx.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,91 @@ +/* glpbfx.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +typedef struct BFX BFX; +#define GLPBFX_DEFINED +#include "glpbfx.h" +#include "glpenv.h" +#include "glplux.h" + +struct BFX +{ int valid; + LUX *lux; +}; + +BFX *bfx_create_binv(void) +{ /* create factorization of the basis matrix */ + BFX *bfx; + bfx = xmalloc(sizeof(BFX)); + bfx->valid = 0; + bfx->lux = NULL; + return bfx; +} + +int bfx_factorize(BFX *binv, int m, int (*col)(void *info, int j, + int ind[], mpq_t val[]), void *info) +{ /* compute factorization of the basis matrix */ + int ret; + xassert(m > 0); + if (binv->lux != NULL && binv->lux->n != m) + { lux_delete(binv->lux); + binv->lux = NULL; + } + if (binv->lux == NULL) + binv->lux = lux_create(m); + ret = lux_decomp(binv->lux, col, info); + binv->valid = (ret == 0); + return ret; +} + +void bfx_ftran(BFX *binv, mpq_t x[], int save) +{ /* perform forward transformation (FTRAN) */ + xassert(binv->valid); + lux_solve(binv->lux, 0, x); + xassert(save == save); + return; +} + +void bfx_btran(BFX *binv, mpq_t x[]) +{ /* perform backward transformation (BTRAN) */ + xassert(binv->valid); + lux_solve(binv->lux, 1, x); + return; +} + +int bfx_update(BFX *binv, int j) +{ /* update factorization of the basis matrix */ + xassert(binv->valid); + xassert(1 <= j && j <= binv->lux->n); + return 1; +} + +void bfx_delete_binv(BFX *binv) +{ /* delete factorization of the basis matrix */ + if (binv->lux != NULL) + lux_delete(binv->lux); + xfree(binv); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpbfx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpbfx.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,71 @@ +/* glpbfx.h (basis factorization interface, bignum arithmetic) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPBFX_H +#define GLPBFX_H + +#include "glpgmp.h" + +#ifndef GLPBFX_DEFINED +#define GLPBFX_DEFINED +typedef struct { double _opaque_bfx; } BFX; +#endif + +#define bfx_create_binv _glp_bfx_create_binv +#define bfx_is_valid _glp_bfx_is_valid +#define bfx_invalidate _glp_bfx_invalidate +#define bfx_factorize _glp_bfx_factorize +#define bfx_ftran _glp_bfx_ftran +#define bfx_btran _glp_bfx_btran +#define bfx_update _glp_bfx_update +#define bfx_delete_binv _glp_bfx_delete_binv + +BFX *bfx_create_binv(void); +/* create factorization of the basis matrix */ + +int bfx_is_valid(BFX *binv); +/* check if factorization is valid */ + +void bfx_invalidate(BFX *binv); +/* invalidate factorization of the basis matrix */ + +int bfx_factorize(BFX *binv, int m, int (*col)(void *info, int j, + int ind[], mpq_t val[]), void *info); +/* compute factorization of the basis matrix */ + +void bfx_ftran(BFX *binv, mpq_t x[], int save); +/* perform forward transformation (FTRAN) */ + +void bfx_btran(BFX *binv, mpq_t x[]); +/* perform backward transformation (BTRAN) */ + +int bfx_update(BFX *binv, int j); +/* update factorization of the basis matrix */ + +void bfx_delete_binv(BFX *binv); +/* delete factorization of the basis matrix */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpcpx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpcpx.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1239 @@ +/* glpcpx.c (CPLEX LP format routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_init_cpxcp - initialize CPLEX LP format control parameters +* +* SYNOPSIS +* +* void glp_init_cpxcp(glp_cpxcp *parm): +* +* The routine glp_init_cpxcp initializes control parameters used by +* the CPLEX LP input/output routines glp_read_lp and glp_write_lp with +* default values. +* +* Default values of the control parameters are stored in the glp_cpxcp +* structure, which the parameter parm points to. */ + +void glp_init_cpxcp(glp_cpxcp *parm) +{ xassert(parm != NULL); + return; +} + +static void check_parm(const char *func, const glp_cpxcp *parm) +{ /* check control parameters */ + xassert(func != NULL); + xassert(parm != NULL); + return; +} + +/*********************************************************************** +* NAME +* +* glp_read_lp - read problem data in CPLEX LP format +* +* SYNOPSIS +* +* int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char +* *fname); +* +* DESCRIPTION +* +* The routine glp_read_lp reads problem data in CPLEX LP format from +* a text file. +* +* The parameter parm is a pointer to the structure glp_cpxcp, which +* specifies control parameters used by the routine. If parm is NULL, +* the routine uses default settings. +* +* The character string fname specifies a name of the text file to be +* read. +* +* Note that before reading data the current content of the problem +* object is completely erased with the routine glp_erase_prob. +* +* RETURNS +* +* If the operation was successful, the routine glp_read_lp returns +* zero. Otherwise, it prints an error message and returns non-zero. */ + +struct csa +{ /* common storage area */ + glp_prob *P; + /* LP/MIP problem object */ + const glp_cpxcp *parm; + /* pointer to control parameters */ + const char *fname; + /* name of input CPLEX LP file */ + XFILE *fp; + /* stream assigned to input CPLEX LP file */ + jmp_buf jump; + /* label for go to in case of error */ + int count; + /* line count */ + int c; + /* current character or XEOF */ + int token; + /* current token: */ +#define T_EOF 0x00 /* end of file */ +#define T_MINIMIZE 0x01 /* keyword 'minimize' */ +#define T_MAXIMIZE 0x02 /* keyword 'maximize' */ +#define T_SUBJECT_TO 0x03 /* keyword 'subject to' */ +#define T_BOUNDS 0x04 /* keyword 'bounds' */ +#define T_GENERAL 0x05 /* keyword 'general' */ +#define T_INTEGER 0x06 /* keyword 'integer' */ +#define T_BINARY 0x07 /* keyword 'binary' */ +#define T_END 0x08 /* keyword 'end' */ +#define T_NAME 0x09 /* symbolic name */ +#define T_NUMBER 0x0A /* numeric constant */ +#define T_PLUS 0x0B /* delimiter '+' */ +#define T_MINUS 0x0C /* delimiter '-' */ +#define T_COLON 0x0D /* delimiter ':' */ +#define T_LE 0x0E /* delimiter '<=' */ +#define T_GE 0x0F /* delimiter '>=' */ +#define T_EQ 0x10 /* delimiter '=' */ + char image[255+1]; + /* image of current token */ + int imlen; + /* length of token image */ + double value; + /* value of numeric constant */ + int n_max; + /* length of the following five arrays (enlarged automatically, + if necessary) */ + int *ind; /* int ind[1+n_max]; */ + double *val; /* double val[1+n_max]; */ + char *flag; /* char flag[1+n_max]; */ + /* working arrays used to construct linear forms */ + double *lb; /* double lb[1+n_max]; */ + double *ub; /* double ub[1+n_max]; */ + /* lower and upper bounds of variables (columns) */ +}; + +#define CHAR_SET "!\"#$%&()/,.;?@_`'{}|~" +/* characters, which may appear in symbolic names */ + +static void error(struct csa *csa, const char *fmt, ...) +{ /* print error message and terminate processing */ + va_list arg; + xprintf("%s:%d: ", csa->fname, csa->count); + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + longjmp(csa->jump, 1); + /* no return */ +} + +static void warning(struct csa *csa, const char *fmt, ...) +{ /* print warning message and continue processing */ + va_list arg; + xprintf("%s:%d: warning: ", csa->fname, csa->count); + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + return; +} + +static void read_char(struct csa *csa) +{ /* read next character from input file */ + int c; + xassert(csa->c != XEOF); + if (csa->c == '\n') csa->count++; + c = xfgetc(csa->fp); + if (c < 0) + { if (xferror(csa->fp)) + error(csa, "read error - %s\n", xerrmsg()); + else if (csa->c == '\n') + { csa->count--; + c = XEOF; + } + else + { warning(csa, "missing final end of line\n"); + c = '\n'; + } + } + else if (c == '\n') + ; + else if (isspace(c)) + c = ' '; + else if (iscntrl(c)) + error(csa, "invalid control character 0x%02X\n", c); + csa->c = c; + return; +} + +static void add_char(struct csa *csa) +{ /* append current character to current token */ + if (csa->imlen == sizeof(csa->image)-1) + error(csa, "token `%.15s...' too long\n", csa->image); + csa->image[csa->imlen++] = (char)csa->c; + csa->image[csa->imlen] = '\0'; + read_char(csa); + return; +} + +static int the_same(char *s1, char *s2) +{ /* compare two character strings ignoring case sensitivity */ + for (; *s1 != '\0'; s1++, s2++) + { if (tolower((unsigned char)*s1) != tolower((unsigned char)*s2)) + return 0; + } + return 1; +} + +static void scan_token(struct csa *csa) +{ /* scan next token */ + int flag; + csa->token = -1; + csa->image[0] = '\0'; + csa->imlen = 0; + csa->value = 0.0; +loop: flag = 0; + /* skip non-significant characters */ + while (csa->c == ' ') read_char(csa); + /* recognize and scan current token */ + if (csa->c == XEOF) + csa->token = T_EOF; + else if (csa->c == '\n') + { read_char(csa); + /* if the next character is letter, it may begin a keyword */ + if (isalpha(csa->c)) + { flag = 1; + goto name; + } + goto loop; + } + else if (csa->c == '\\') + { /* comment; ignore everything until end-of-line */ + while (csa->c != '\n') read_char(csa); + goto loop; + } + else if (isalpha(csa->c) || csa->c != '.' && strchr(CHAR_SET, + csa->c) != NULL) +name: { /* symbolic name */ + csa->token = T_NAME; + while (isalnum(csa->c) || strchr(CHAR_SET, csa->c) != NULL) + add_char(csa); + if (flag) + { /* check for keyword */ + if (the_same(csa->image, "minimize")) + csa->token = T_MINIMIZE; + else if (the_same(csa->image, "minimum")) + csa->token = T_MINIMIZE; + else if (the_same(csa->image, "min")) + csa->token = T_MINIMIZE; + else if (the_same(csa->image, "maximize")) + csa->token = T_MAXIMIZE; + else if (the_same(csa->image, "maximum")) + csa->token = T_MAXIMIZE; + else if (the_same(csa->image, "max")) + csa->token = T_MAXIMIZE; + else if (the_same(csa->image, "subject")) + { if (csa->c == ' ') + { read_char(csa); + if (tolower(csa->c) == 't') + { csa->token = T_SUBJECT_TO; + csa->image[csa->imlen++] = ' '; + csa->image[csa->imlen] = '\0'; + add_char(csa); + if (tolower(csa->c) != 'o') + error(csa, "keyword `subject to' incomplete\n"); + add_char(csa); + if (isalpha(csa->c)) + error(csa, "keyword `%s%c...' not recognized\n", + csa->image, csa->c); + } + } + } + else if (the_same(csa->image, "such")) + { if (csa->c == ' ') + { read_char(csa); + if (tolower(csa->c) == 't') + { csa->token = T_SUBJECT_TO; + csa->image[csa->imlen++] = ' '; + csa->image[csa->imlen] = '\0'; + add_char(csa); + if (tolower(csa->c) != 'h') +err: error(csa, "keyword `such that' incomplete\n"); + add_char(csa); + if (tolower(csa->c) != 'a') goto err; + add_char(csa); + if (tolower(csa->c) != 't') goto err; + add_char(csa); + if (isalpha(csa->c)) + error(csa, "keyword `%s%c...' not recognized\n", + csa->image, csa->c); + } + } + } + else if (the_same(csa->image, "st")) + csa->token = T_SUBJECT_TO; + else if (the_same(csa->image, "s.t.")) + csa->token = T_SUBJECT_TO; + else if (the_same(csa->image, "st.")) + csa->token = T_SUBJECT_TO; + else if (the_same(csa->image, "bounds")) + csa->token = T_BOUNDS; + else if (the_same(csa->image, "bound")) + csa->token = T_BOUNDS; + else if (the_same(csa->image, "general")) + csa->token = T_GENERAL; + else if (the_same(csa->image, "generals")) + csa->token = T_GENERAL; + else if (the_same(csa->image, "gen")) + csa->token = T_GENERAL; + else if (the_same(csa->image, "integer")) + csa->token = T_INTEGER; + else if (the_same(csa->image, "integers")) + csa->token = T_INTEGER; + else if (the_same(csa->image, "int")) + csa->token = T_INTEGER; + else if (the_same(csa->image, "binary")) + csa->token = T_BINARY; + else if (the_same(csa->image, "binaries")) + csa->token = T_BINARY; + else if (the_same(csa->image, "bin")) + csa->token = T_BINARY; + else if (the_same(csa->image, "end")) + csa->token = T_END; + } + } + else if (isdigit(csa->c) || csa->c == '.') + { /* numeric constant */ + csa->token = T_NUMBER; + /* scan integer part */ + while (isdigit(csa->c)) add_char(csa); + /* scan optional fractional part (it is mandatory, if there is + no integer part) */ + if (csa->c == '.') + { add_char(csa); + if (csa->imlen == 1 && !isdigit(csa->c)) + error(csa, "invalid use of decimal point\n"); + while (isdigit(csa->c)) add_char(csa); + } + /* scan optional decimal exponent */ + if (csa->c == 'e' || csa->c == 'E') + { add_char(csa); + if (csa->c == '+' || csa->c == '-') add_char(csa); + if (!isdigit(csa->c)) + error(csa, "numeric constant `%s' incomplete\n", + csa->image); + while (isdigit(csa->c)) add_char(csa); + } + /* convert the numeric constant to floating-point */ + if (str2num(csa->image, &csa->value)) + error(csa, "numeric constant `%s' out of range\n", + csa->image); + } + else if (csa->c == '+') + csa->token = T_PLUS, add_char(csa); + else if (csa->c == '-') + csa->token = T_MINUS, add_char(csa); + else if (csa->c == ':') + csa->token = T_COLON, add_char(csa); + else if (csa->c == '<') + { csa->token = T_LE, add_char(csa); + if (csa->c == '=') add_char(csa); + } + else if (csa->c == '>') + { csa->token = T_GE, add_char(csa); + if (csa->c == '=') add_char(csa); + } + else if (csa->c == '=') + { csa->token = T_EQ, add_char(csa); + if (csa->c == '<') + csa->token = T_LE, add_char(csa); + else if (csa->c == '>') + csa->token = T_GE, add_char(csa); + } + else + error(csa, "character `%c' not recognized\n", csa->c); + /* skip non-significant characters */ + while (csa->c == ' ') read_char(csa); + return; +} + +static int find_col(struct csa *csa, char *name) +{ /* find column by its symbolic name */ + int j; + j = glp_find_col(csa->P, name); + if (j == 0) + { /* not found; create new column */ + j = glp_add_cols(csa->P, 1); + glp_set_col_name(csa->P, j, name); + /* enlarge working arrays, if necessary */ + if (csa->n_max < j) + { int n_max = csa->n_max; + int *ind = csa->ind; + double *val = csa->val; + char *flag = csa->flag; + double *lb = csa->lb; + double *ub = csa->ub; + csa->n_max += csa->n_max; + csa->ind = xcalloc(1+csa->n_max, sizeof(int)); + memcpy(&csa->ind[1], &ind[1], n_max * sizeof(int)); + xfree(ind); + csa->val = xcalloc(1+csa->n_max, sizeof(double)); + memcpy(&csa->val[1], &val[1], n_max * sizeof(double)); + xfree(val); + csa->flag = xcalloc(1+csa->n_max, sizeof(char)); + memset(&csa->flag[1], 0, csa->n_max * sizeof(char)); + memcpy(&csa->flag[1], &flag[1], n_max * sizeof(char)); + xfree(flag); + csa->lb = xcalloc(1+csa->n_max, sizeof(double)); + memcpy(&csa->lb[1], &lb[1], n_max * sizeof(double)); + xfree(lb); + csa->ub = xcalloc(1+csa->n_max, sizeof(double)); + memcpy(&csa->ub[1], &ub[1], n_max * sizeof(double)); + xfree(ub); + } + csa->lb[j] = +DBL_MAX, csa->ub[j] = -DBL_MAX; + } + return j; +} + +/*********************************************************************** +* parse_linear_form - parse linear form +* +* This routine parses the linear form using the following syntax: +* +* ::= +* ::= +* ::= | +* ::= | + | - | +* + | - +* +* The routine returns the number of terms in the linear form. */ + +static int parse_linear_form(struct csa *csa) +{ int j, k, len = 0, newlen; + double s, coef; +loop: /* parse an optional sign */ + if (csa->token == T_PLUS) + s = +1.0, scan_token(csa); + else if (csa->token == T_MINUS) + s = -1.0, scan_token(csa); + else + s = +1.0; + /* parse an optional coefficient */ + if (csa->token == T_NUMBER) + coef = csa->value, scan_token(csa); + else + coef = 1.0; + /* parse a variable name */ + if (csa->token != T_NAME) + error(csa, "missing variable name\n"); + /* find the corresponding column */ + j = find_col(csa, csa->image); + /* check if the variable is already used in the linear form */ + if (csa->flag[j]) + error(csa, "multiple use of variable `%s' not allowed\n", + csa->image); + /* add new term to the linear form */ + len++, csa->ind[len] = j, csa->val[len] = s * coef; + /* and mark that the variable is used in the linear form */ + csa->flag[j] = 1; + scan_token(csa); + /* if the next token is a sign, there is another term */ + if (csa->token == T_PLUS || csa->token == T_MINUS) goto loop; + /* clear marks of the variables used in the linear form */ + for (k = 1; k <= len; k++) csa->flag[csa->ind[k]] = 0; + /* remove zero coefficients */ + newlen = 0; + for (k = 1; k <= len; k++) + { if (csa->val[k] != 0.0) + { newlen++; + csa->ind[newlen] = csa->ind[k]; + csa->val[newlen] = csa->val[k]; + } + } + return newlen; +} + +/*********************************************************************** +* parse_objective - parse objective function +* +* This routine parses definition of the objective function using the +* following syntax: +* +* ::= minimize | minimum | min | maximize | maximum | max +* ::= | : +* ::= */ + +static void parse_objective(struct csa *csa) +{ /* parse objective sense */ + int k, len; + /* parse the keyword 'minimize' or 'maximize' */ + if (csa->token == T_MINIMIZE) + glp_set_obj_dir(csa->P, GLP_MIN); + else if (csa->token == T_MAXIMIZE) + glp_set_obj_dir(csa->P, GLP_MAX); + else + xassert(csa != csa); + scan_token(csa); + /* parse objective name */ + if (csa->token == T_NAME && csa->c == ':') + { /* objective name is followed by a colon */ + glp_set_obj_name(csa->P, csa->image); + scan_token(csa); + xassert(csa->token == T_COLON); + scan_token(csa); + } + else + { /* objective name is not specified; use default */ + glp_set_obj_name(csa->P, "obj"); + } + /* parse linear form */ + len = parse_linear_form(csa); + for (k = 1; k <= len; k++) + glp_set_obj_coef(csa->P, csa->ind[k], csa->val[k]); + return; +} + +/*********************************************************************** +* parse_constraints - parse constraints section +* +* This routine parses the constraints section using the following +* syntax: +* +* ::= | : +* ::= < | <= | =< | > | >= | => | = +* ::= | + | +* - +* ::= +* +* ::= subject to | such that | st | s.t. | st. +* ::= | +* */ + +static void parse_constraints(struct csa *csa) +{ int i, len, type; + double s; + /* parse the keyword 'subject to' */ + xassert(csa->token == T_SUBJECT_TO); + scan_token(csa); +loop: /* create new row (constraint) */ + i = glp_add_rows(csa->P, 1); + /* parse row name */ + if (csa->token == T_NAME && csa->c == ':') + { /* row name is followed by a colon */ + if (glp_find_row(csa->P, csa->image) != 0) + error(csa, "constraint `%s' multiply defined\n", + csa->image); + glp_set_row_name(csa->P, i, csa->image); + scan_token(csa); + xassert(csa->token == T_COLON); + scan_token(csa); + } + else + { /* row name is not specified; use default */ + char name[50]; + sprintf(name, "r.%d", csa->count); + glp_set_row_name(csa->P, i, name); + } + /* parse linear form */ + len = parse_linear_form(csa); + glp_set_mat_row(csa->P, i, len, csa->ind, csa->val); + /* parse constraint sense */ + if (csa->token == T_LE) + type = GLP_UP, scan_token(csa); + else if (csa->token == T_GE) + type = GLP_LO, scan_token(csa); + else if (csa->token == T_EQ) + type = GLP_FX, scan_token(csa); + else + error(csa, "missing constraint sense\n"); + /* parse right-hand side */ + if (csa->token == T_PLUS) + s = +1.0, scan_token(csa); + else if (csa->token == T_MINUS) + s = -1.0, scan_token(csa); + else + s = +1.0; + if (csa->token != T_NUMBER) + error(csa, "missing right-hand side\n"); + glp_set_row_bnds(csa->P, i, type, s * csa->value, s * csa->value); + /* the rest of the current line must be empty */ + if (!(csa->c == '\n' || csa->c == XEOF)) + error(csa, "invalid symbol(s) beyond right-hand side\n"); + scan_token(csa); + /* if the next token is a sign, numeric constant, or a symbolic + name, here is another constraint */ + if (csa->token == T_PLUS || csa->token == T_MINUS || + csa->token == T_NUMBER || csa->token == T_NAME) goto loop; + return; +} + +static void set_lower_bound(struct csa *csa, int j, double lb) +{ /* set lower bound of j-th variable */ + if (csa->lb[j] != +DBL_MAX) + { warning(csa, "lower bound of variable `%s' redefined\n", + glp_get_col_name(csa->P, j)); + } + csa->lb[j] = lb; + return; +} + +static void set_upper_bound(struct csa *csa, int j, double ub) +{ /* set upper bound of j-th variable */ + if (csa->ub[j] != -DBL_MAX) + { warning(csa, "upper bound of variable `%s' redefined\n", + glp_get_col_name(csa->P, j)); + } + csa->ub[j] = ub; + return; +} + +/*********************************************************************** +* parse_bounds - parse bounds section +* +* This routine parses the bounds section using the following syntax: +* +* ::= +* ::= infinity | inf +* ::= | + | +* - | + | - +* ::= < | <= | =< +* ::= > | >= | => +* ::= | +* | | +* | = | free +* ::= bounds | bound +* ::= | +* */ + +static void parse_bounds(struct csa *csa) +{ int j, lb_flag; + double lb, s; + /* parse the keyword 'bounds' */ + xassert(csa->token == T_BOUNDS); + scan_token(csa); +loop: /* bound definition can start with a sign, numeric constant, or + a symbolic name */ + if (!(csa->token == T_PLUS || csa->token == T_MINUS || + csa->token == T_NUMBER || csa->token == T_NAME)) goto done; + /* parse bound definition */ + if (csa->token == T_PLUS || csa->token == T_MINUS) + { /* parse signed lower bound */ + lb_flag = 1; + s = (csa->token == T_PLUS ? +1.0 : -1.0); + scan_token(csa); + if (csa->token == T_NUMBER) + lb = s * csa->value, scan_token(csa); + else if (the_same(csa->image, "infinity") || + the_same(csa->image, "inf")) + { if (s > 0.0) + error(csa, "invalid use of `+inf' as lower bound\n"); + lb = -DBL_MAX, scan_token(csa); + } + else + error(csa, "missing lower bound\n"); + } + else if (csa->token == T_NUMBER) + { /* parse unsigned lower bound */ + lb_flag = 1; + lb = csa->value, scan_token(csa); + } + else + { /* lower bound is not specified */ + lb_flag = 0; + } + /* parse the token that should follow the lower bound */ + if (lb_flag) + { if (csa->token != T_LE) + error(csa, "missing `<', `<=', or `=<' after lower bound\n") + ; + scan_token(csa); + } + /* parse variable name */ + if (csa->token != T_NAME) + error(csa, "missing variable name\n"); + j = find_col(csa, csa->image); + /* set lower bound */ + if (lb_flag) set_lower_bound(csa, j, lb); + scan_token(csa); + /* parse the context that follows the variable name */ + if (csa->token == T_LE) + { /* parse upper bound */ + scan_token(csa); + if (csa->token == T_PLUS || csa->token == T_MINUS) + { /* parse signed upper bound */ + s = (csa->token == T_PLUS ? +1.0 : -1.0); + scan_token(csa); + if (csa->token == T_NUMBER) + { set_upper_bound(csa, j, s * csa->value); + scan_token(csa); + } + else if (the_same(csa->image, "infinity") || + the_same(csa->image, "inf")) + { if (s < 0.0) + error(csa, "invalid use of `-inf' as upper bound\n"); + set_upper_bound(csa, j, +DBL_MAX); + scan_token(csa); + } + else + error(csa, "missing upper bound\n"); + } + else if (csa->token == T_NUMBER) + { /* parse unsigned upper bound */ + set_upper_bound(csa, j, csa->value); + scan_token(csa); + } + else + error(csa, "missing upper bound\n"); + } + else if (csa->token == T_GE) + { /* parse lower bound */ + if (lb_flag) + { /* the context '... <= x >= ...' is invalid */ + error(csa, "invalid bound definition\n"); + } + scan_token(csa); + if (csa->token == T_PLUS || csa->token == T_MINUS) + { /* parse signed lower bound */ + s = (csa->token == T_PLUS ? +1.0 : -1.0); + scan_token(csa); + if (csa->token == T_NUMBER) + { set_lower_bound(csa, j, s * csa->value); + scan_token(csa); + } + else if (the_same(csa->image, "infinity") || + the_same(csa->image, "inf") == 0) + { if (s > 0.0) + error(csa, "invalid use of `+inf' as lower bound\n"); + set_lower_bound(csa, j, -DBL_MAX); + scan_token(csa); + } + else + error(csa, "missing lower bound\n"); + } + else if (csa->token == T_NUMBER) + { /* parse unsigned lower bound */ + set_lower_bound(csa, j, csa->value); + scan_token(csa); + } + else + error(csa, "missing lower bound\n"); + } + else if (csa->token == T_EQ) + { /* parse fixed value */ + if (lb_flag) + { /* the context '... <= x = ...' is invalid */ + error(csa, "invalid bound definition\n"); + } + scan_token(csa); + if (csa->token == T_PLUS || csa->token == T_MINUS) + { /* parse signed fixed value */ + s = (csa->token == T_PLUS ? +1.0 : -1.0); + scan_token(csa); + if (csa->token == T_NUMBER) + { set_lower_bound(csa, j, s * csa->value); + set_upper_bound(csa, j, s * csa->value); + scan_token(csa); + } + else + error(csa, "missing fixed value\n"); + } + else if (csa->token == T_NUMBER) + { /* parse unsigned fixed value */ + set_lower_bound(csa, j, csa->value); + set_upper_bound(csa, j, csa->value); + scan_token(csa); + } + else + error(csa, "missing fixed value\n"); + } + else if (the_same(csa->image, "free")) + { /* parse the keyword 'free' */ + if (lb_flag) + { /* the context '... <= x free ...' is invalid */ + error(csa, "invalid bound definition\n"); + } + set_lower_bound(csa, j, -DBL_MAX); + set_upper_bound(csa, j, +DBL_MAX); + scan_token(csa); + } + else if (!lb_flag) + { /* neither lower nor upper bounds are specified */ + error(csa, "invalid bound definition\n"); + } + goto loop; +done: return; +} + +/*********************************************************************** +* parse_integer - parse general, integer, or binary section +* +* ::= +* ::= general | generals | gen +* ::= integer | integers | int +* ::= binary | binaries | bin +*
::= +* ::=
| +* */ + +static void parse_integer(struct csa *csa) +{ int j, binary; + /* parse the keyword 'general', 'integer', or 'binary' */ + if (csa->token == T_GENERAL) + binary = 0, scan_token(csa); + else if (csa->token == T_INTEGER) + binary = 0, scan_token(csa); + else if (csa->token == T_BINARY) + binary = 1, scan_token(csa); + else + xassert(csa != csa); + /* parse list of variables (may be empty) */ + while (csa->token == T_NAME) + { /* find the corresponding column */ + j = find_col(csa, csa->image); + /* change kind of the variable */ + glp_set_col_kind(csa->P, j, GLP_IV); + /* set 0-1 bounds for the binary variable */ + if (binary) + { set_lower_bound(csa, j, 0.0); + set_upper_bound(csa, j, 1.0); + } + scan_token(csa); + } + return; +} + +int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname) +{ /* read problem data in CPLEX LP format */ + glp_cpxcp _parm; + struct csa _csa, *csa = &_csa; + int ret; + xprintf("Reading problem data from `%s'...\n", fname); + if (parm == NULL) + glp_init_cpxcp(&_parm), parm = &_parm; + /* check control parameters */ + check_parm("glp_read_lp", parm); + /* initialize common storage area */ + csa->P = P; + csa->parm = parm; + csa->fname = fname; + csa->fp = NULL; + if (setjmp(csa->jump)) + { ret = 1; + goto done; + } + csa->count = 0; + csa->c = '\n'; + csa->token = T_EOF; + csa->image[0] = '\0'; + csa->imlen = 0; + csa->value = 0.0; + csa->n_max = 100; + csa->ind = xcalloc(1+csa->n_max, sizeof(int)); + csa->val = xcalloc(1+csa->n_max, sizeof(double)); + csa->flag = xcalloc(1+csa->n_max, sizeof(char)); + memset(&csa->flag[1], 0, csa->n_max * sizeof(char)); + csa->lb = xcalloc(1+csa->n_max, sizeof(double)); + csa->ub = xcalloc(1+csa->n_max, sizeof(double)); + /* erase problem object */ + glp_erase_prob(P); + glp_create_index(P); + /* open input CPLEX LP file */ + csa->fp = xfopen(fname, "r"); + if (csa->fp == NULL) + { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* scan very first token */ + scan_token(csa); + /* parse definition of the objective function */ + if (!(csa->token == T_MINIMIZE || csa->token == T_MAXIMIZE)) + error(csa, "`minimize' or `maximize' keyword missing\n"); + parse_objective(csa); + /* parse constraints section */ + if (csa->token != T_SUBJECT_TO) + error(csa, "constraints section missing\n"); + parse_constraints(csa); + /* parse optional bounds section */ + if (csa->token == T_BOUNDS) parse_bounds(csa); + /* parse optional general, integer, and binary sections */ + while (csa->token == T_GENERAL || + csa->token == T_INTEGER || + csa->token == T_BINARY) parse_integer(csa); + /* check for the keyword 'end' */ + if (csa->token == T_END) + scan_token(csa); + else if (csa->token == T_EOF) + warning(csa, "keyword `end' missing\n"); + else + error(csa, "symbol `%s' in wrong position\n", csa->image); + /* nothing must follow the keyword 'end' (except comments) */ + if (csa->token != T_EOF) + error(csa, "extra symbol(s) detected beyond `end'\n"); + /* set bounds of variables */ + { int j, type; + double lb, ub; + for (j = 1; j <= P->n; j++) + { lb = csa->lb[j]; + ub = csa->ub[j]; + if (lb == +DBL_MAX) lb = 0.0; /* default lb */ + if (ub == -DBL_MAX) ub = +DBL_MAX; /* default ub */ + if (lb == -DBL_MAX && ub == +DBL_MAX) + type = GLP_FR; + else if (ub == +DBL_MAX) + type = GLP_LO; + else if (lb == -DBL_MAX) + type = GLP_UP; + else if (lb != ub) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(csa->P, j, type, lb, ub); + } + } + /* print some statistics */ + xprintf("%d row%s, %d column%s, %d non-zero%s\n", + P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", + P->nnz, P->nnz == 1 ? "" : "s"); + if (glp_get_num_int(P) > 0) + { int ni = glp_get_num_int(P); + int nb = glp_get_num_bin(P); + if (ni == 1) + { if (nb == 0) + xprintf("One variable is integer\n"); + else + xprintf("One variable is binary\n"); + } + else + { xprintf("%d integer variables, ", ni); + if (nb == 0) + xprintf("none"); + else if (nb == 1) + xprintf("one"); + else if (nb == ni) + xprintf("all"); + else + xprintf("%d", nb); + xprintf(" of which %s binary\n", nb == 1 ? "is" : "are"); + } + } + xprintf("%d lines were read\n", csa->count); + /* problem data has been successfully read */ + glp_delete_index(P); + glp_sort_matrix(P); + ret = 0; +done: if (csa->fp != NULL) xfclose(csa->fp); + xfree(csa->ind); + xfree(csa->val); + xfree(csa->flag); + xfree(csa->lb); + xfree(csa->ub); + if (ret != 0) glp_erase_prob(P); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_lp - write problem data in CPLEX LP format +* +* SYNOPSIS +* +* int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char +* *fname); +* +* DESCRIPTION +* +* The routine glp_write_lp writes problem data in CPLEX LP format to +* a text file. +* +* The parameter parm is a pointer to the structure glp_cpxcp, which +* specifies control parameters used by the routine. If parm is NULL, +* the routine uses default settings. +* +* The character string fname specifies a name of the text file to be +* written. +* +* RETURNS +* +* If the operation was successful, the routine glp_write_lp returns +* zero. Otherwise, it prints an error message and returns non-zero. */ + +#define csa csa1 + +struct csa +{ /* common storage area */ + glp_prob *P; + /* pointer to problem object */ + const glp_cpxcp *parm; + /* pointer to control parameters */ +}; + +static int check_name(char *name) +{ /* check if specified name is valid for CPLEX LP format */ + if (*name == '.') return 1; + if (isdigit((unsigned char)*name)) return 1; + for (; *name; name++) + { if (!isalnum((unsigned char)*name) && + strchr(CHAR_SET, (unsigned char)*name) == NULL) return 1; + } + return 0; /* name is ok */ +} + +static void adjust_name(char *name) +{ /* attempt to adjust specified name to make it valid for CPLEX LP + format */ + for (; *name; name++) + { if (*name == ' ') + *name = '_'; + else if (*name == '-') + *name = '~'; + else if (*name == '[') + *name = '('; + else if (*name == ']') + *name = ')'; + } + return; +} + +static char *row_name(struct csa *csa, int i, char rname[255+1]) +{ /* construct symbolic name of i-th row (constraint) */ + const char *name; + if (i == 0) + name = glp_get_obj_name(csa->P); + else + name = glp_get_row_name(csa->P, i); + if (name == NULL) goto fake; + strcpy(rname, name); + adjust_name(rname); + if (check_name(rname)) goto fake; + return rname; +fake: if (i == 0) + strcpy(rname, "obj"); + else + sprintf(rname, "r_%d", i); + return rname; +} + +static char *col_name(struct csa *csa, int j, char cname[255+1]) +{ /* construct symbolic name of j-th column (variable) */ + const char *name; + name = glp_get_col_name(csa->P, j); + if (name == NULL) goto fake; + strcpy(cname, name); + adjust_name(cname); + if (check_name(cname)) goto fake; + return cname; +fake: sprintf(cname, "x_%d", j); + return cname; +} + +int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname) +{ /* write problem data in CPLEX LP format */ + glp_cpxcp _parm; + struct csa _csa, *csa = &_csa; + XFILE *fp; + GLPROW *row; + GLPCOL *col; + GLPAIJ *aij; + int i, j, len, flag, count, ret; + char line[1000+1], term[500+1], name[255+1]; + xprintf("Writing problem data to `%s'...\n", fname); + if (parm == NULL) + glp_init_cpxcp(&_parm), parm = &_parm; + /* check control parameters */ + check_parm("glp_write_lp", parm); + /* initialize common storage area */ + csa->P = P; + csa->parm = parm; + /* create output CPLEX LP file */ + fp = xfopen(fname, "w"), count = 0; + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* write problem name */ + xfprintf(fp, "\\* Problem: %s *\\\n", + P->name == NULL ? "Unknown" : P->name), count++; + xfprintf(fp, "\n"), count++; + /* the problem should contain at least one row and one column */ + if (!(P->m > 0 && P->n > 0)) + { xprintf("Warning: problem has no rows/columns\n"); + xfprintf(fp, "\\* WARNING: PROBLEM HAS NO ROWS/COLUMNS *\\\n"), + count++; + xfprintf(fp, "\n"), count++; + goto skip; + } + /* write the objective function definition */ + if (P->dir == GLP_MIN) + xfprintf(fp, "Minimize\n"), count++; + else if (P->dir == GLP_MAX) + xfprintf(fp, "Maximize\n"), count++; + else + xassert(P != P); + row_name(csa, 0, name); + sprintf(line, " %s:", name); + len = 0; + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->coef != 0.0 || col->ptr == NULL) + { len++; + col_name(csa, j, name); + if (col->coef == 0.0) + sprintf(term, " + 0 %s", name); /* empty column */ + else if (col->coef == +1.0) + sprintf(term, " + %s", name); + else if (col->coef == -1.0) + sprintf(term, " - %s", name); + else if (col->coef > 0.0) + sprintf(term, " + %.*g %s", DBL_DIG, +col->coef, name); + else + sprintf(term, " - %.*g %s", DBL_DIG, -col->coef, name); + if (strlen(line) + strlen(term) > 72) + xfprintf(fp, "%s\n", line), line[0] = '\0', count++; + strcat(line, term); + } + } + if (len == 0) + { /* empty objective */ + sprintf(term, " 0 %s", col_name(csa, 1, name)); + strcat(line, term); + } + xfprintf(fp, "%s\n", line), count++; + if (P->c0 != 0.0) + xfprintf(fp, "\\* constant term = %.*g *\\\n", DBL_DIG, P->c0), + count++; + xfprintf(fp, "\n"), count++; + /* write the constraints section */ + xfprintf(fp, "Subject To\n"), count++; + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + if (row->type == GLP_FR) continue; /* skip free row */ + row_name(csa, i, name); + sprintf(line, " %s:", name); + /* linear form */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col_name(csa, aij->col->j, name); + if (aij->val == +1.0) + sprintf(term, " + %s", name); + else if (aij->val == -1.0) + sprintf(term, " - %s", name); + else if (aij->val > 0.0) + sprintf(term, " + %.*g %s", DBL_DIG, +aij->val, name); + else + sprintf(term, " - %.*g %s", DBL_DIG, -aij->val, name); + if (strlen(line) + strlen(term) > 72) + xfprintf(fp, "%s\n", line), line[0] = '\0', count++; + strcat(line, term); + } + if (row->type == GLP_DB) + { /* double-bounded (ranged) constraint */ + sprintf(term, " - ~r_%d", i); + if (strlen(line) + strlen(term) > 72) + xfprintf(fp, "%s\n", line), line[0] = '\0', count++; + strcat(line, term); + } + else if (row->ptr == NULL) + { /* empty constraint */ + sprintf(term, " 0 %s", col_name(csa, 1, name)); + strcat(line, term); + } + /* right hand-side */ + if (row->type == GLP_LO) + sprintf(term, " >= %.*g", DBL_DIG, row->lb); + else if (row->type == GLP_UP) + sprintf(term, " <= %.*g", DBL_DIG, row->ub); + else if (row->type == GLP_DB || row->type == GLP_FX) + sprintf(term, " = %.*g", DBL_DIG, row->lb); + else + xassert(row != row); + if (strlen(line) + strlen(term) > 72) + xfprintf(fp, "%s\n", line), line[0] = '\0', count++; + strcat(line, term); + xfprintf(fp, "%s\n", line), count++; + } + xfprintf(fp, "\n"), count++; + /* write the bounds section */ + flag = 0; + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + if (row->type != GLP_DB) continue; + if (!flag) + xfprintf(fp, "Bounds\n"), flag = 1, count++; + xfprintf(fp, " 0 <= ~r_%d <= %.*g\n", + i, DBL_DIG, row->ub - row->lb), count++; + } + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->type == GLP_LO && col->lb == 0.0) continue; + if (!flag) + xfprintf(fp, "Bounds\n"), flag = 1, count++; + col_name(csa, j, name); + if (col->type == GLP_FR) + xfprintf(fp, " %s free\n", name), count++; + else if (col->type == GLP_LO) + xfprintf(fp, " %s >= %.*g\n", + name, DBL_DIG, col->lb), count++; + else if (col->type == GLP_UP) + xfprintf(fp, " -Inf <= %s <= %.*g\n", + name, DBL_DIG, col->ub), count++; + else if (col->type == GLP_DB) + xfprintf(fp, " %.*g <= %s <= %.*g\n", + DBL_DIG, col->lb, name, DBL_DIG, col->ub), count++; + else if (col->type == GLP_FX) + xfprintf(fp, " %s = %.*g\n", + name, DBL_DIG, col->lb), count++; + else + xassert(col != col); + } + if (flag) xfprintf(fp, "\n"), count++; + /* write the integer section */ + flag = 0; + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->kind == GLP_CV) continue; + xassert(col->kind == GLP_IV); + if (!flag) + xfprintf(fp, "Generals\n"), flag = 1, count++; + xfprintf(fp, " %s\n", col_name(csa, j, name)), count++; + } + if (flag) xfprintf(fp, "\n"), count++; +skip: /* write the end keyword */ + xfprintf(fp, "End\n"), count++; + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* problem data has been successfully written */ + xprintf("%d lines were written\n", count); + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpdmp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpdmp.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,259 @@ +/* glpdmp.c (dynamic memory pool) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpdmp.h" + +#if 1 /* 29/VIII-2008 */ +/* some processors need data to be properly aligned; the macro + align_datasize enlarges the specified size of a data item to provide + a proper alignment of immediately following data */ + +#define align_datasize(size) ((((size) + 7) / 8) * 8) +/* 8 bytes is sufficient in both 32- and 64-bit environments */ +#endif + +#ifdef GLP_DEBUG +struct info +{ DMP *pool; + int size; +}; +#endif + +/*********************************************************************** +* NAME +* +* dmp_create_pool - create dynamic memory pool +* +* SYNOPSIS +* +* #include "glpdmp.h" +* DMP *dmp_create_pool(void); +* +* DESCRIPTION +* +* The routine dmp_create_pool creates a dynamic memory pool. +* +* RETURNS +* +* The routine returns a pointer to the memory pool created. */ + +DMP *dmp_create_pool(void) +{ DMP *pool; + int k; +#ifdef GLP_DEBUG + xprintf("dmp_create_pool: warning: debug mode enabled\n"); +#endif + pool = xmalloc(sizeof(DMP)); +#if 0 + pool->size = 0; +#endif + for (k = 0; k <= 31; k++) pool->avail[k] = NULL; + pool->block = NULL; + pool->used = DMP_BLK_SIZE; + pool->count.lo = pool->count.hi = 0; + return pool; +} + +/*********************************************************************** +* NAME +* +* dmp_get_atom - get free atom from dynamic memory pool +* +* SYNOPSIS +* +* #include "glpdmp.h" +* void *dmp_get_atom(DMP *pool, int size); +* +* DESCRIPTION +* +* The routine dmp_get_atom obtains a free atom (memory block) from the +* specified memory pool. +* +* The parameter size is the atom size, in bytes, 1 <= size <= 256. +* +* Note that the free atom contains arbitrary data, not binary zeros. +* +* RETURNS +* +* The routine returns a pointer to the free atom obtained. */ + +void *dmp_get_atom(DMP *pool, int size) +{ void *atom; + int k; +#ifdef GLP_DEBUG + int orig_size = size; +#endif + if (!(1 <= size && size <= 256)) + xerror("dmp_get_atom: size = %d; invalid atom size\n", size); +#if 0 + if (!(pool->size == 0 || pool->size == size)) + xerror("dmp_get_atom: size = %d; wrong atom size\n", size); +#endif + /* adjust the size to provide the proper data alignment */ + size = align_datasize(size); +#ifdef GLP_DEBUG + size += align_datasize(sizeof(struct info)); +#endif + /* adjust the size to make it multiple of 8 bytes, if needed */ + size = ((size + 7) / 8) * 8; + /* determine the corresponding list of free cells */ + k = size / 8 - 1; + xassert(0 <= k && k <= 31); + /* obtain a free atom */ + if (pool->avail[k] == NULL) + { /* the list of free cells is empty */ + if (pool->used + size > DMP_BLK_SIZE) + { /* allocate a new memory block */ + void *block = xmalloc(DMP_BLK_SIZE); + *(void **)block = pool->block; + pool->block = block; + pool->used = align_datasize(sizeof(void *)); + } + /* place the atom in the current memory block */ + atom = (char *)pool->block + pool->used; + pool->used += size; + } + else + { /* obtain the atom from the list of free cells */ + atom = pool->avail[k]; + pool->avail[k] = *(void **)atom; + } + memset(atom, '?', size); + /* increase the number of atoms which are currently in use */ + pool->count.lo++; + if (pool->count.lo == 0) pool->count.hi++; +#ifdef GLP_DEBUG + ((struct info *)atom)->pool = pool; + ((struct info *)atom)->size = orig_size; + atom = (char *)atom + align_datasize(sizeof(struct info)); +#endif + return atom; +} + +/*********************************************************************** +* NAME +* +* dmp_free_atom - return atom to dynamic memory pool +* +* SYNOPSIS +* +* #include "glpdmp.h" +* void dmp_free_atom(DMP *pool, void *atom, int size); +* +* DESCRIPTION +* +* The routine dmp_free_atom returns the specified atom (memory block) +* to the specified memory pool, making it free. +* +* The parameter size is the atom size, in bytes, 1 <= size <= 256. +* +* Note that the atom can be returned only to the pool, from which it +* was obtained, and its size must be exactly the same as on obtaining +* it from the pool. */ + +void dmp_free_atom(DMP *pool, void *atom, int size) +{ int k; + if (!(1 <= size && size <= 256)) + xerror("dmp_free_atom: size = %d; invalid atom size\n", size); +#if 0 + if (!(pool->size == 0 || pool->size == size)) + xerror("dmp_free_atom: size = %d; wrong atom size\n", size); +#endif + if (pool->count.lo == 0 && pool->count.hi == 0) + xerror("dmp_free_atom: pool allocation error\n"); +#ifdef GLP_DEBUG + atom = (char *)atom - align_datasize(sizeof(struct info)); + xassert(((struct info *)atom)->pool == pool); + xassert(((struct info *)atom)->size == size); +#endif + /* adjust the size to provide the proper data alignment */ + size = align_datasize(size); +#ifdef GLP_DEBUG + size += align_datasize(sizeof(struct info)); +#endif + /* adjust the size to make it multiple of 8 bytes, if needed */ + size = ((size + 7) / 8) * 8; + /* determine the corresponding list of free cells */ + k = size / 8 - 1; + xassert(0 <= k && k <= 31); + /* return the atom to the list of free cells */ + *(void **)atom = pool->avail[k]; + pool->avail[k] = atom; + /* decrease the number of atoms which are currently in use */ + pool->count.lo--; + if (pool->count.lo == 0xFFFFFFFF) pool->count.hi--; + return; +} + +/*********************************************************************** +* NAME +* +* dmp_in_use - determine how many atoms are still in use +* +* SYNOPSIS +* +* #include "glpdmp.h" +* glp_long dmp_in_use(DMP *pool); +* +* DESCRIPTION +* +* The routine dmp_in_use determines how many atoms allocated from the +* specified memory pool with the routine dmp_get_atom are still in use, +* i.e. not returned to the pool with the routine dmp_free_atom. +* +* RETURNS +* +* The routine returns the number of atoms which are still in use. */ + +glp_long dmp_in_use(DMP *pool) +{ return + pool->count; +} + +/*********************************************************************** +* NAME +* +* dmp_delete_pool - delete dynamic memory pool +* +* SYNOPSIS +* +* #include "glpdmp.h" +* void dmp_delete_pool(DMP *pool); +* +* DESCRIPTION +* +* The routine dmp_delete_pool deletes the specified dynamic memory +* pool and frees all the memory allocated to this object. */ + +void dmp_delete_pool(DMP *pool) +{ while (pool->block != NULL) + { void *block = pool->block; + pool->block = *(void **)block; + xfree(block); + } + xfree(pool); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpdmp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpdmp.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,80 @@ +/* glpdmp.h (dynamic memory pool) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPDMP_H +#define GLPDMP_H + +#include "glpenv.h" + +typedef struct DMP DMP; + +#define DMP_BLK_SIZE 8000 +/* size of memory blocks, in bytes, allocated for memory pools */ + +struct DMP +{ /* dynamic memory pool */ +#if 0 + int size; + /* size of atoms, in bytes, 1 <= size <= 256; if size = 0, atoms + may have different sizes */ +#endif + void *avail[32]; + /* avail[k], 0 <= k <= 31, is a pointer to the first available + (free) cell of (k+1)*8 bytes long; in the beginning of each + free cell there is a pointer to another free cell of the same + length */ + void *block; + /* pointer to the most recently allocated memory block; in the + beginning of each allocated memory block there is a pointer to + the previously allocated memory block */ + int used; + /* number of bytes used in the most recently allocated memory + block */ + glp_long count; + /* number of atoms which are currently in use */ +}; + +#define dmp_create_pool _glp_dmp_create_pool +DMP *dmp_create_pool(void); +/* create dynamic memory pool */ + +#define dmp_get_atom _glp_dmp_get_atom +void *dmp_get_atom(DMP *pool, int size); +/* get free atom from dynamic memory pool */ + +#define dmp_free_atom _glp_dmp_free_atom +void dmp_free_atom(DMP *pool, void *atom, int size); +/* return atom to dynamic memory pool */ + +#define dmp_in_use _glp_dmp_in_use +glp_long dmp_in_use(DMP *pool); +/* determine how many atoms are still in use */ + +#define dmp_delete_pool _glp_dmp_delete_pool +void dmp_delete_pool(DMP *pool); +/* delete dynamic memory pool */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpdmx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpdmx.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1468 @@ +/* glpdmx.c (reading/writing data in DIMACS format) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_STDIO +#include "glpapi.h" + +struct csa +{ /* common storage area */ + jmp_buf jump; + /* label for go to in case of error */ + const char *fname; + /* name of input text file */ + XFILE *fp; + /* stream assigned to input text file */ + int count; + /* line count */ + int c; + /* current character */ + char field[255+1]; + /* data field */ + int empty; + /* warning 'empty line ignored' was printed */ + int nonint; + /* warning 'non-integer data detected' was printed */ +}; + +static void error(struct csa *csa, const char *fmt, ...) +{ /* print error message and terminate processing */ + va_list arg; + xprintf("%s:%d: error: ", csa->fname, csa->count); + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + xprintf("\n"); + longjmp(csa->jump, 1); + /* no return */ +} + +static void warning(struct csa *csa, const char *fmt, ...) +{ /* print warning message and continue processing */ + va_list arg; + xprintf("%s:%d: warning: ", csa->fname, csa->count); + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + xprintf("\n"); + return; +} + +static void read_char(struct csa *csa) +{ /* read character from input text file */ + int c; + if (csa->c == '\n') csa->count++; + c = xfgetc(csa->fp); + if (c < 0) + { if (xferror(csa->fp)) + error(csa, "read error - %s", xerrmsg()); + else if (csa->c == '\n') + error(csa, "unexpected end of file"); + else + { warning(csa, "missing final end of line"); + c = '\n'; + } + } + else if (c == '\n') + ; + else if (isspace(c)) + c = ' '; + else if (iscntrl(c)) + error(csa, "invalid control character 0x%02X", c); + csa->c = c; + return; +} + +static void read_designator(struct csa *csa) +{ /* read one-character line designator */ + xassert(csa->c == '\n'); + read_char(csa); + for (;;) + { /* skip preceding white-space characters */ + while (csa->c == ' ') + read_char(csa); + if (csa->c == '\n') + { /* ignore empty line */ + if (!csa->empty) + { warning(csa, "empty line ignored"); + csa->empty = 1; + } + read_char(csa); + } + else if (csa->c == 'c') + { /* skip comment line */ + while (csa->c != '\n') + read_char(csa); + read_char(csa); + } + else + { /* hmm... looks like a line designator */ + csa->field[0] = (char)csa->c, csa->field[1] = '\0'; + /* check that it is followed by a white-space character */ + read_char(csa); + if (!(csa->c == ' ' || csa->c == '\n')) + error(csa, "line designator missing or invalid"); + break; + } + } + return; +} + +static void read_field(struct csa *csa) +{ /* read data field */ + int len = 0; + /* skip preceding white-space characters */ + while (csa->c == ' ') + read_char(csa); + /* scan data field */ + if (csa->c == '\n') + error(csa, "unexpected end of line"); + while (!(csa->c == ' ' || csa->c == '\n')) + { if (len == sizeof(csa->field)-1) + error(csa, "data field `%.15s...' too long", csa->field); + csa->field[len++] = (char)csa->c; + read_char(csa); + } + csa->field[len] = '\0'; + return; +} + +static void end_of_line(struct csa *csa) +{ /* skip white-space characters until end of line */ + while (csa->c == ' ') + read_char(csa); + if (csa->c != '\n') + error(csa, "too many data fields specified"); + return; +} + +static void check_int(struct csa *csa, double num) +{ /* print a warning if non-integer data are detected */ + if (!csa->nonint && num != floor(num)) + { warning(csa, "non-integer data detected"); + csa->nonint = 1; + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_read_mincost - read min-cost flow problem data in DIMACS format +* +* SYNOPSIS +* +* int glp_read_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, +* int a_cost, const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_mincost reads minimum cost flow problem data in +* DIMACS format from a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_read_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, + int a_cost, const char *fname) +{ struct csa _csa, *csa = &_csa; + glp_vertex *v; + glp_arc *a; + int i, j, k, nv, na, ret = 0; + double rhs, low, cap, cost; + char *flag = NULL; + if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) + xerror("glp_read_mincost: v_rhs = %d; invalid offset\n", + v_rhs); + if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) + xerror("glp_read_mincost: a_low = %d; invalid offset\n", + a_low); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_read_mincost: a_cap = %d; invalid offset\n", + a_cap); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_read_mincost: a_cost = %d; invalid offset\n", + a_cost); + glp_erase_graph(G, G->v_size, G->a_size); + if (setjmp(csa->jump)) + { ret = 1; + goto done; + } + csa->fname = fname; + csa->fp = NULL; + csa->count = 0; + csa->c = '\n'; + csa->field[0] = '\0'; + csa->empty = csa->nonint = 0; + xprintf("Reading min-cost flow problem data from `%s'...\n", + fname); + csa->fp = xfopen(fname, "r"); + if (csa->fp == NULL) + { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); + longjmp(csa->jump, 1); + } + /* read problem line */ + read_designator(csa); + if (strcmp(csa->field, "p") != 0) + error(csa, "problem line missing or invalid"); + read_field(csa); + if (strcmp(csa->field, "min") != 0) + error(csa, "wrong problem designator; `min' expected"); + read_field(csa); + if (!(str2int(csa->field, &nv) == 0 && nv >= 0)) + error(csa, "number of nodes missing or invalid"); + read_field(csa); + if (!(str2int(csa->field, &na) == 0 && na >= 0)) + error(csa, "number of arcs missing or invalid"); + xprintf("Flow network has %d node%s and %d arc%s\n", + nv, nv == 1 ? "" : "s", na, na == 1 ? "" : "s"); + if (nv > 0) glp_add_vertices(G, nv); + end_of_line(csa); + /* read node descriptor lines */ + flag = xcalloc(1+nv, sizeof(char)); + memset(&flag[1], 0, nv * sizeof(char)); + if (v_rhs >= 0) + { rhs = 0.0; + for (i = 1; i <= nv; i++) + { v = G->v[i]; + memcpy((char *)v->data + v_rhs, &rhs, sizeof(double)); + } + } + for (;;) + { read_designator(csa); + if (strcmp(csa->field, "n") != 0) break; + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "node number missing or invalid"); + if (!(1 <= i && i <= nv)) + error(csa, "node number %d out of range", i); + if (flag[i]) + error(csa, "duplicate descriptor of node %d", i); + read_field(csa); + if (str2num(csa->field, &rhs) != 0) + error(csa, "node supply/demand missing or invalid"); + check_int(csa, rhs); + if (v_rhs >= 0) + { v = G->v[i]; + memcpy((char *)v->data + v_rhs, &rhs, sizeof(double)); + } + flag[i] = 1; + end_of_line(csa); + } + xfree(flag), flag = NULL; + /* read arc descriptor lines */ + for (k = 1; k <= na; k++) + { if (k > 1) read_designator(csa); + if (strcmp(csa->field, "a") != 0) + error(csa, "wrong line designator; `a' expected"); + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "starting node number missing or invalid"); + if (!(1 <= i && i <= nv)) + error(csa, "starting node number %d out of range", i); + read_field(csa); + if (str2int(csa->field, &j) != 0) + error(csa, "ending node number missing or invalid"); + if (!(1 <= j && j <= nv)) + error(csa, "ending node number %d out of range", j); + read_field(csa); + if (!(str2num(csa->field, &low) == 0 && low >= 0.0)) + error(csa, "lower bound of arc flow missing or invalid"); + check_int(csa, low); + read_field(csa); + if (!(str2num(csa->field, &cap) == 0 && cap >= low)) + error(csa, "upper bound of arc flow missing or invalid"); + check_int(csa, cap); + read_field(csa); + if (str2num(csa->field, &cost) != 0) + error(csa, "per-unit cost of arc flow missing or invalid"); + check_int(csa, cost); + a = glp_add_arc(G, i, j); + if (a_low >= 0) + memcpy((char *)a->data + a_low, &low, sizeof(double)); + if (a_cap >= 0) + memcpy((char *)a->data + a_cap, &cap, sizeof(double)); + if (a_cost >= 0) + memcpy((char *)a->data + a_cost, &cost, sizeof(double)); + end_of_line(csa); + } + xprintf("%d lines were read\n", csa->count); +done: if (ret) glp_erase_graph(G, G->v_size, G->a_size); + if (csa->fp != NULL) xfclose(csa->fp); + if (flag != NULL) xfree(flag); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_mincost - write min-cost flow problem data in DIMACS format +* +* SYNOPSIS +* +* int glp_write_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, +* int a_cost, const char *fname); +* +* DESCRIPTION +* +* The routine glp_write_mincost writes minimum cost flow problem data +* in DIMACS format to a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_write_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, + int a_cost, const char *fname) +{ XFILE *fp; + glp_vertex *v; + glp_arc *a; + int i, count = 0, ret; + double rhs, low, cap, cost; + if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) + xerror("glp_write_mincost: v_rhs = %d; invalid offset\n", + v_rhs); + if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) + xerror("glp_write_mincost: a_low = %d; invalid offset\n", + a_low); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_write_mincost: a_cap = %d; invalid offset\n", + a_cap); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_write_mincost: a_cost = %d; invalid offset\n", + a_cost); + xprintf("Writing min-cost flow problem data to `%s'...\n", + fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xfprintf(fp, "c %s\n", + G->name == NULL ? "unknown" : G->name), count++; + xfprintf(fp, "p min %d %d\n", G->nv, G->na), count++; + if (v_rhs >= 0) + { for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + memcpy(&rhs, (char *)v->data + v_rhs, sizeof(double)); + if (rhs != 0.0) + xfprintf(fp, "n %d %.*g\n", i, DBL_DIG, rhs), count++; + } + } + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { if (a_low >= 0) + memcpy(&low, (char *)a->data + a_low, sizeof(double)); + else + low = 0.0; + if (a_cap >= 0) + memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); + else + cap = 1.0; + if (a_cost >= 0) + memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); + else + cost = 0.0; + xfprintf(fp, "a %d %d %.*g %.*g %.*g\n", + a->tail->i, a->head->i, DBL_DIG, low, DBL_DIG, cap, + DBL_DIG, cost), count++; + } + } + xfprintf(fp, "c eof\n"), count++; + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", count); + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_read_maxflow - read maximum flow problem data in DIMACS format +* +* SYNOPSIS +* +* int glp_read_maxflow(glp_graph *G, int *s, int *t, int a_cap, +* const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_maxflow reads maximum flow problem data in +* DIMACS format from a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_read_maxflow(glp_graph *G, int *_s, int *_t, int a_cap, + const char *fname) +{ struct csa _csa, *csa = &_csa; + glp_arc *a; + int i, j, k, s, t, nv, na, ret = 0; + double cap; + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_read_maxflow: a_cap = %d; invalid offset\n", + a_cap); + glp_erase_graph(G, G->v_size, G->a_size); + if (setjmp(csa->jump)) + { ret = 1; + goto done; + } + csa->fname = fname; + csa->fp = NULL; + csa->count = 0; + csa->c = '\n'; + csa->field[0] = '\0'; + csa->empty = csa->nonint = 0; + xprintf("Reading maximum flow problem data from `%s'...\n", + fname); + csa->fp = xfopen(fname, "r"); + if (csa->fp == NULL) + { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); + longjmp(csa->jump, 1); + } + /* read problem line */ + read_designator(csa); + if (strcmp(csa->field, "p") != 0) + error(csa, "problem line missing or invalid"); + read_field(csa); + if (strcmp(csa->field, "max") != 0) + error(csa, "wrong problem designator; `max' expected"); + read_field(csa); + if (!(str2int(csa->field, &nv) == 0 && nv >= 2)) + error(csa, "number of nodes missing or invalid"); + read_field(csa); + if (!(str2int(csa->field, &na) == 0 && na >= 0)) + error(csa, "number of arcs missing or invalid"); + xprintf("Flow network has %d node%s and %d arc%s\n", + nv, nv == 1 ? "" : "s", na, na == 1 ? "" : "s"); + if (nv > 0) glp_add_vertices(G, nv); + end_of_line(csa); + /* read node descriptor lines */ + s = t = 0; + for (;;) + { read_designator(csa); + if (strcmp(csa->field, "n") != 0) break; + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "node number missing or invalid"); + if (!(1 <= i && i <= nv)) + error(csa, "node number %d out of range", i); + read_field(csa); + if (strcmp(csa->field, "s") == 0) + { if (s > 0) + error(csa, "only one source node allowed"); + s = i; + } + else if (strcmp(csa->field, "t") == 0) + { if (t > 0) + error(csa, "only one sink node allowed"); + t = i; + } + else + error(csa, "wrong node designator; `s' or `t' expected"); + if (s > 0 && s == t) + error(csa, "source and sink nodes must be distinct"); + end_of_line(csa); + } + if (s == 0) + error(csa, "source node descriptor missing\n"); + if (t == 0) + error(csa, "sink node descriptor missing\n"); + if (_s != NULL) *_s = s; + if (_t != NULL) *_t = t; + /* read arc descriptor lines */ + for (k = 1; k <= na; k++) + { if (k > 1) read_designator(csa); + if (strcmp(csa->field, "a") != 0) + error(csa, "wrong line designator; `a' expected"); + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "starting node number missing or invalid"); + if (!(1 <= i && i <= nv)) + error(csa, "starting node number %d out of range", i); + read_field(csa); + if (str2int(csa->field, &j) != 0) + error(csa, "ending node number missing or invalid"); + if (!(1 <= j && j <= nv)) + error(csa, "ending node number %d out of range", j); + read_field(csa); + if (!(str2num(csa->field, &cap) == 0 && cap >= 0.0)) + error(csa, "arc capacity missing or invalid"); + check_int(csa, cap); + a = glp_add_arc(G, i, j); + if (a_cap >= 0) + memcpy((char *)a->data + a_cap, &cap, sizeof(double)); + end_of_line(csa); + } + xprintf("%d lines were read\n", csa->count); +done: if (ret) glp_erase_graph(G, G->v_size, G->a_size); + if (csa->fp != NULL) xfclose(csa->fp); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_maxflow - write maximum flow problem data in DIMACS format +* +* SYNOPSIS +* +* int glp_write_maxflow(glp_graph *G, int s, int t, int a_cap, +* const char *fname); +* +* DESCRIPTION +* +* The routine glp_write_maxflow writes maximum flow problem data in +* DIMACS format to a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_write_maxflow(glp_graph *G, int s, int t, int a_cap, + const char *fname) +{ XFILE *fp; + glp_vertex *v; + glp_arc *a; + int i, count = 0, ret; + double cap; + if (!(1 <= s && s <= G->nv)) + xerror("glp_write_maxflow: s = %d; source node number out of r" + "ange\n", s); + if (!(1 <= t && t <= G->nv)) + xerror("glp_write_maxflow: t = %d: sink node number out of ran" + "ge\n", t); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_write_mincost: a_cap = %d; invalid offset\n", + a_cap); + xprintf("Writing maximum flow problem data to `%s'...\n", + fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xfprintf(fp, "c %s\n", + G->name == NULL ? "unknown" : G->name), count++; + xfprintf(fp, "p max %d %d\n", G->nv, G->na), count++; + xfprintf(fp, "n %d s\n", s), count++; + xfprintf(fp, "n %d t\n", t), count++; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { if (a_cap >= 0) + memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); + else + cap = 1.0; + xfprintf(fp, "a %d %d %.*g\n", + a->tail->i, a->head->i, DBL_DIG, cap), count++; + } + } + xfprintf(fp, "c eof\n"), count++; + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", count); + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_read_asnprob - read assignment problem data in DIMACS format +* +* SYNOPSIS +* +* int glp_read_asnprob(glp_graph *G, int v_set, int a_cost, +* const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_asnprob reads assignment problem data in DIMACS +* format from a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_read_asnprob(glp_graph *G, int v_set, int a_cost, const char + *fname) +{ struct csa _csa, *csa = &_csa; + glp_vertex *v; + glp_arc *a; + int nv, na, n1, i, j, k, ret = 0; + double cost; + char *flag = NULL; + if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) + xerror("glp_read_asnprob: v_set = %d; invalid offset\n", + v_set); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_read_asnprob: a_cost = %d; invalid offset\n", + a_cost); + glp_erase_graph(G, G->v_size, G->a_size); + if (setjmp(csa->jump)) + { ret = 1; + goto done; + } + csa->fname = fname; + csa->fp = NULL; + csa->count = 0; + csa->c = '\n'; + csa->field[0] = '\0'; + csa->empty = csa->nonint = 0; + xprintf("Reading assignment problem data from `%s'...\n", fname); + csa->fp = xfopen(fname, "r"); + if (csa->fp == NULL) + { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); + longjmp(csa->jump, 1); + } + /* read problem line */ + read_designator(csa); + if (strcmp(csa->field, "p") != 0) + error(csa, "problem line missing or invalid"); + read_field(csa); + if (strcmp(csa->field, "asn") != 0) + error(csa, "wrong problem designator; `asn' expected"); + read_field(csa); + if (!(str2int(csa->field, &nv) == 0 && nv >= 0)) + error(csa, "number of nodes missing or invalid"); + read_field(csa); + if (!(str2int(csa->field, &na) == 0 && na >= 0)) + error(csa, "number of arcs missing or invalid"); + if (nv > 0) glp_add_vertices(G, nv); + end_of_line(csa); + /* read node descriptor lines */ + flag = xcalloc(1+nv, sizeof(char)); + memset(&flag[1], 0, nv * sizeof(char)); + n1 = 0; + for (;;) + { read_designator(csa); + if (strcmp(csa->field, "n") != 0) break; + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "node number missing or invalid"); + if (!(1 <= i && i <= nv)) + error(csa, "node number %d out of range", i); + if (flag[i]) + error(csa, "duplicate descriptor of node %d", i); + flag[i] = 1, n1++; + end_of_line(csa); + } + xprintf( + "Assignment problem has %d + %d = %d node%s and %d arc%s\n", + n1, nv - n1, nv, nv == 1 ? "" : "s", na, na == 1 ? "" : "s"); + if (v_set >= 0) + { for (i = 1; i <= nv; i++) + { v = G->v[i]; + k = (flag[i] ? 0 : 1); + memcpy((char *)v->data + v_set, &k, sizeof(int)); + } + } + /* read arc descriptor lines */ + for (k = 1; k <= na; k++) + { if (k > 1) read_designator(csa); + if (strcmp(csa->field, "a") != 0) + error(csa, "wrong line designator; `a' expected"); + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "starting node number missing or invalid"); + if (!(1 <= i && i <= nv)) + error(csa, "starting node number %d out of range", i); + if (!flag[i]) + error(csa, "node %d cannot be a starting node", i); + read_field(csa); + if (str2int(csa->field, &j) != 0) + error(csa, "ending node number missing or invalid"); + if (!(1 <= j && j <= nv)) + error(csa, "ending node number %d out of range", j); + if (flag[j]) + error(csa, "node %d cannot be an ending node", j); + read_field(csa); + if (str2num(csa->field, &cost) != 0) + error(csa, "arc cost missing or invalid"); + check_int(csa, cost); + a = glp_add_arc(G, i, j); + if (a_cost >= 0) + memcpy((char *)a->data + a_cost, &cost, sizeof(double)); + end_of_line(csa); + } + xprintf("%d lines were read\n", csa->count); +done: if (ret) glp_erase_graph(G, G->v_size, G->a_size); + if (csa->fp != NULL) xfclose(csa->fp); + if (flag != NULL) xfree(flag); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_asnprob - write assignment problem data in DIMACS format +* +* SYNOPSIS +* +* int glp_write_asnprob(glp_graph *G, int v_set, int a_cost, +* const char *fname); +* +* DESCRIPTION +* +* The routine glp_write_asnprob writes assignment problem data in +* DIMACS format to a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_write_asnprob(glp_graph *G, int v_set, int a_cost, const char + *fname) +{ XFILE *fp; + glp_vertex *v; + glp_arc *a; + int i, k, count = 0, ret; + double cost; + if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) + xerror("glp_write_asnprob: v_set = %d; invalid offset\n", + v_set); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_write_asnprob: a_cost = %d; invalid offset\n", + a_cost); + xprintf("Writing assignment problem data to `%s'...\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xfprintf(fp, "c %s\n", + G->name == NULL ? "unknown" : G->name), count++; + xfprintf(fp, "p asn %d %d\n", G->nv, G->na), count++; + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + if (v_set >= 0) + memcpy(&k, (char *)v->data + v_set, sizeof(int)); + else + k = (v->out != NULL ? 0 : 1); + if (k == 0) + xfprintf(fp, "n %d\n", i), count++; + } + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (a = v->out; a != NULL; a = a->t_next) + { if (a_cost >= 0) + memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); + else + cost = 1.0; + xfprintf(fp, "a %d %d %.*g\n", + a->tail->i, a->head->i, DBL_DIG, cost), count++; + } + } + xfprintf(fp, "c eof\n"), count++; + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", count); + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_read_ccdata - read graph in DIMACS clique/coloring format +* +* SYNOPSIS +* +* int glp_read_ccdata(glp_graph *G, int v_wgt, const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_ccdata reads an (undirected) graph in DIMACS +* clique/coloring format from a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_read_ccdata(glp_graph *G, int v_wgt, const char *fname) +{ struct csa _csa, *csa = &_csa; + glp_vertex *v; + int i, j, k, nv, ne, ret = 0; + double w; + char *flag = NULL; + if (v_wgt >= 0 && v_wgt > G->v_size - (int)sizeof(double)) + xerror("glp_read_ccdata: v_wgt = %d; invalid offset\n", + v_wgt); + glp_erase_graph(G, G->v_size, G->a_size); + if (setjmp(csa->jump)) + { ret = 1; + goto done; + } + csa->fname = fname; + csa->fp = NULL; + csa->count = 0; + csa->c = '\n'; + csa->field[0] = '\0'; + csa->empty = csa->nonint = 0; + xprintf("Reading graph from `%s'...\n", fname); + csa->fp = xfopen(fname, "r"); + if (csa->fp == NULL) + { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); + longjmp(csa->jump, 1); + } + /* read problem line */ + read_designator(csa); + if (strcmp(csa->field, "p") != 0) + error(csa, "problem line missing or invalid"); + read_field(csa); + if (strcmp(csa->field, "edge") != 0) + error(csa, "wrong problem designator; `edge' expected"); + read_field(csa); + if (!(str2int(csa->field, &nv) == 0 && nv >= 0)) + error(csa, "number of vertices missing or invalid"); + read_field(csa); + if (!(str2int(csa->field, &ne) == 0 && ne >= 0)) + error(csa, "number of edges missing or invalid"); + xprintf("Graph has %d vert%s and %d edge%s\n", + nv, nv == 1 ? "ex" : "ices", ne, ne == 1 ? "" : "s"); + if (nv > 0) glp_add_vertices(G, nv); + end_of_line(csa); + /* read node descriptor lines */ + flag = xcalloc(1+nv, sizeof(char)); + memset(&flag[1], 0, nv * sizeof(char)); + if (v_wgt >= 0) + { w = 1.0; + for (i = 1; i <= nv; i++) + { v = G->v[i]; + memcpy((char *)v->data + v_wgt, &w, sizeof(double)); + } + } + for (;;) + { read_designator(csa); + if (strcmp(csa->field, "n") != 0) break; + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "vertex number missing or invalid"); + if (!(1 <= i && i <= nv)) + error(csa, "vertex number %d out of range", i); + if (flag[i]) + error(csa, "duplicate descriptor of vertex %d", i); + read_field(csa); + if (str2num(csa->field, &w) != 0) + error(csa, "vertex weight missing or invalid"); + check_int(csa, w); + if (v_wgt >= 0) + { v = G->v[i]; + memcpy((char *)v->data + v_wgt, &w, sizeof(double)); + } + flag[i] = 1; + end_of_line(csa); + } + xfree(flag), flag = NULL; + /* read edge descriptor lines */ + for (k = 1; k <= ne; k++) + { if (k > 1) read_designator(csa); + if (strcmp(csa->field, "e") != 0) + error(csa, "wrong line designator; `e' expected"); + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "first vertex number missing or invalid"); + if (!(1 <= i && i <= nv)) + error(csa, "first vertex number %d out of range", i); + read_field(csa); + if (str2int(csa->field, &j) != 0) + error(csa, "second vertex number missing or invalid"); + if (!(1 <= j && j <= nv)) + error(csa, "second vertex number %d out of range", j); + glp_add_arc(G, i, j); + end_of_line(csa); + } + xprintf("%d lines were read\n", csa->count); +done: if (ret) glp_erase_graph(G, G->v_size, G->a_size); + if (csa->fp != NULL) xfclose(csa->fp); + if (flag != NULL) xfree(flag); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_ccdata - write graph in DIMACS clique/coloring format +* +* SYNOPSIS +* +* int glp_write_ccdata(glp_graph *G, int v_wgt, const char *fname); +* +* DESCRIPTION +* +* The routine glp_write_ccdata writes the specified graph in DIMACS +* clique/coloring format to a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_write_ccdata(glp_graph *G, int v_wgt, const char *fname) +{ XFILE *fp; + glp_vertex *v; + glp_arc *e; + int i, count = 0, ret; + double w; + if (v_wgt >= 0 && v_wgt > G->v_size - (int)sizeof(double)) + xerror("glp_write_ccdata: v_wgt = %d; invalid offset\n", + v_wgt); + xprintf("Writing graph to `%s'\n", fname); + fp = xfopen(fname, "w"); + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xfprintf(fp, "c %s\n", + G->name == NULL ? "unknown" : G->name), count++; + xfprintf(fp, "p edge %d %d\n", G->nv, G->na), count++; + if (v_wgt >= 0) + { for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + memcpy(&w, (char *)v->data + v_wgt, sizeof(double)); + if (w != 1.0) + xfprintf(fp, "n %d %.*g\n", i, DBL_DIG, w), count++; + } + } + for (i = 1; i <= G->nv; i++) + { v = G->v[i]; + for (e = v->out; e != NULL; e = e->t_next) + xfprintf(fp, "e %d %d\n", e->tail->i, e->head->i), count++; + } + xfprintf(fp, "c eof\n"), count++; + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", count); + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_read_prob - read problem data in GLPK format +* +* SYNOPSIS +* +* int glp_read_prob(glp_prob *P, int flags, const char *fname); +* +* The routine glp_read_prob reads problem data in GLPK LP/MIP format +* from a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_read_prob(glp_prob *P, int flags, const char *fname) +{ struct csa _csa, *csa = &_csa; + int mip, m, n, nnz, ne, i, j, k, type, kind, ret, *ln = NULL, + *ia = NULL, *ja = NULL; + double lb, ub, temp, *ar = NULL; + char *rf = NULL, *cf = NULL; + if (P == NULL || P->magic != GLP_PROB_MAGIC) + xerror("glp_read_prob: P = %p; invalid problem object\n", + P); + if (flags != 0) + xerror("glp_read_prob: flags = %d; invalid parameter\n", + flags); + if (fname == NULL) + xerror("glp_read_prob: fname = %d; invalid parameter\n", + fname); + glp_erase_prob(P); + if (setjmp(csa->jump)) + { ret = 1; + goto done; + } + csa->fname = fname; + csa->fp = NULL; + csa->count = 0; + csa->c = '\n'; + csa->field[0] = '\0'; + csa->empty = csa->nonint = 0; + xprintf("Reading problem data from `%s'...\n", fname); + csa->fp = xfopen(fname, "r"); + if (csa->fp == NULL) + { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); + longjmp(csa->jump, 1); + } + /* read problem line */ + read_designator(csa); + if (strcmp(csa->field, "p") != 0) + error(csa, "problem line missing or invalid"); + read_field(csa); + if (strcmp(csa->field, "lp") == 0) + mip = 0; + else if (strcmp(csa->field, "mip") == 0) + mip = 1; + else + error(csa, "wrong problem designator; `lp' or `mip' expected\n" + ); + read_field(csa); + if (strcmp(csa->field, "min") == 0) + glp_set_obj_dir(P, GLP_MIN); + else if (strcmp(csa->field, "max") == 0) + glp_set_obj_dir(P, GLP_MAX); + else + error(csa, "objective sense missing or invalid"); + read_field(csa); + if (!(str2int(csa->field, &m) == 0 && m >= 0)) + error(csa, "number of rows missing or invalid"); + read_field(csa); + if (!(str2int(csa->field, &n) == 0 && n >= 0)) + error(csa, "number of columns missing or invalid"); + read_field(csa); + if (!(str2int(csa->field, &nnz) == 0 && nnz >= 0)) + error(csa, "number of constraint coefficients missing or inval" + "id"); + if (m > 0) + { glp_add_rows(P, m); + for (i = 1; i <= m; i++) + glp_set_row_bnds(P, i, GLP_FX, 0.0, 0.0); + } + if (n > 0) + { glp_add_cols(P, n); + for (j = 1; j <= n; j++) + { if (!mip) + glp_set_col_bnds(P, j, GLP_LO, 0.0, 0.0); + else + glp_set_col_kind(P, j, GLP_BV); + } + } + end_of_line(csa); + /* allocate working arrays */ + rf = xcalloc(1+m, sizeof(char)); + memset(rf, 0, 1+m); + cf = xcalloc(1+n, sizeof(char)); + memset(cf, 0, 1+n); + ln = xcalloc(1+nnz, sizeof(int)); + ia = xcalloc(1+nnz, sizeof(int)); + ja = xcalloc(1+nnz, sizeof(int)); + ar = xcalloc(1+nnz, sizeof(double)); + /* read descriptor lines */ + ne = 0; + for (;;) + { read_designator(csa); + if (strcmp(csa->field, "i") == 0) + { /* row descriptor */ + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "row number missing or invalid"); + if (!(1 <= i && i <= m)) + error(csa, "row number out of range"); + read_field(csa); + if (strcmp(csa->field, "f") == 0) + type = GLP_FR; + else if (strcmp(csa->field, "l") == 0) + type = GLP_LO; + else if (strcmp(csa->field, "u") == 0) + type = GLP_UP; + else if (strcmp(csa->field, "d") == 0) + type = GLP_DB; + else if (strcmp(csa->field, "s") == 0) + type = GLP_FX; + else + error(csa, "row type missing or invalid"); + if (type == GLP_LO || type == GLP_DB || type == GLP_FX) + { read_field(csa); + if (str2num(csa->field, &lb) != 0) + error(csa, "row lower bound/fixed value missing or in" + "valid"); + } + else + lb = 0.0; + if (type == GLP_UP || type == GLP_DB) + { read_field(csa); + if (str2num(csa->field, &ub) != 0) + error(csa, "row upper bound missing or invalid"); + } + else + ub = 0.0; + if (rf[i] & 0x01) + error(csa, "duplicate row descriptor"); + glp_set_row_bnds(P, i, type, lb, ub), rf[i] |= 0x01; + } + else if (strcmp(csa->field, "j") == 0) + { /* column descriptor */ + read_field(csa); + if (str2int(csa->field, &j) != 0) + error(csa, "column number missing or invalid"); + if (!(1 <= j && j <= n)) + error(csa, "column number out of range"); + if (!mip) + kind = GLP_CV; + else + { read_field(csa); + if (strcmp(csa->field, "c") == 0) + kind = GLP_CV; + else if (strcmp(csa->field, "i") == 0) + kind = GLP_IV; + else if (strcmp(csa->field, "b") == 0) + { kind = GLP_IV; + type = GLP_DB, lb = 0.0, ub = 1.0; + goto skip; + } + else + error(csa, "column kind missing or invalid"); + } + read_field(csa); + if (strcmp(csa->field, "f") == 0) + type = GLP_FR; + else if (strcmp(csa->field, "l") == 0) + type = GLP_LO; + else if (strcmp(csa->field, "u") == 0) + type = GLP_UP; + else if (strcmp(csa->field, "d") == 0) + type = GLP_DB; + else if (strcmp(csa->field, "s") == 0) + type = GLP_FX; + else + error(csa, "column type missing or invalid"); + if (type == GLP_LO || type == GLP_DB || type == GLP_FX) + { read_field(csa); + if (str2num(csa->field, &lb) != 0) + error(csa, "column lower bound/fixed value missing or" + " invalid"); + } + else + lb = 0.0; + if (type == GLP_UP || type == GLP_DB) + { read_field(csa); + if (str2num(csa->field, &ub) != 0) + error(csa, "column upper bound missing or invalid"); + } + else + ub = 0.0; +skip: if (cf[j] & 0x01) + error(csa, "duplicate column descriptor"); + glp_set_col_kind(P, j, kind); + glp_set_col_bnds(P, j, type, lb, ub), cf[j] |= 0x01; + } + else if (strcmp(csa->field, "a") == 0) + { /* coefficient descriptor */ + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "row number missing or invalid"); + if (!(0 <= i && i <= m)) + error(csa, "row number out of range"); + read_field(csa); + if (str2int(csa->field, &j) != 0) + error(csa, "column number missing or invalid"); + if (!((i == 0 ? 0 : 1) <= j && j <= n)) + error(csa, "column number out of range"); + read_field(csa); + if (i == 0) + { if (str2num(csa->field, &temp) != 0) + error(csa, "objective %s missing or invalid", + j == 0 ? "constant term" : "coefficient"); + if (cf[j] & 0x10) + error(csa, "duplicate objective %s", + j == 0 ? "constant term" : "coefficient"); + glp_set_obj_coef(P, j, temp), cf[j] |= 0x10; + } + else + { if (str2num(csa->field, &temp) != 0) + error(csa, "constraint coefficient missing or invalid" + ); + if (ne == nnz) + error(csa, "too many constraint coefficient descripto" + "rs"); + ln[++ne] = csa->count; + ia[ne] = i, ja[ne] = j, ar[ne] = temp; + } + } + else if (strcmp(csa->field, "n") == 0) + { /* symbolic name descriptor */ + read_field(csa); + if (strcmp(csa->field, "p") == 0) + { /* problem name */ + read_field(csa); + if (P->name != NULL) + error(csa, "duplicate problem name"); + glp_set_prob_name(P, csa->field); + } + else if (strcmp(csa->field, "z") == 0) + { /* objective name */ + read_field(csa); + if (P->obj != NULL) + error(csa, "duplicate objective name"); + glp_set_obj_name(P, csa->field); + } + else if (strcmp(csa->field, "i") == 0) + { /* row name */ + read_field(csa); + if (str2int(csa->field, &i) != 0) + error(csa, "row number missing or invalid"); + if (!(1 <= i && i <= m)) + error(csa, "row number out of range"); + read_field(csa); + if (P->row[i]->name != NULL) + error(csa, "duplicate row name"); + glp_set_row_name(P, i, csa->field); + } + else if (strcmp(csa->field, "j") == 0) + { /* column name */ + read_field(csa); + if (str2int(csa->field, &j) != 0) + error(csa, "column number missing or invalid"); + if (!(1 <= j && j <= n)) + error(csa, "column number out of range"); + read_field(csa); + if (P->col[j]->name != NULL) + error(csa, "duplicate column name"); + glp_set_col_name(P, j, csa->field); + } + else + error(csa, "object designator missing or invalid"); + } + else if (strcmp(csa->field, "e") == 0) + break; + else + error(csa, "line designator missing or invalid"); + end_of_line(csa); + } + if (ne < nnz) + error(csa, "too few constraint coefficient descriptors"); + xassert(ne == nnz); + k = glp_check_dup(m, n, ne, ia, ja); + xassert(0 <= k && k <= nnz); + if (k > 0) + { csa->count = ln[k]; + error(csa, "duplicate constraint coefficient"); + } + glp_load_matrix(P, ne, ia, ja, ar); + /* print some statistics */ + if (P->name != NULL) + xprintf("Problem: %s\n", P->name); + if (P->obj != NULL) + xprintf("Objective: %s\n", P->obj); + xprintf("%d row%s, %d column%s, %d non-zero%s\n", + m, m == 1 ? "" : "s", n, n == 1 ? "" : "s", nnz, nnz == 1 ? + "" : "s"); + if (glp_get_num_int(P) > 0) + { int ni = glp_get_num_int(P); + int nb = glp_get_num_bin(P); + if (ni == 1) + { if (nb == 0) + xprintf("One variable is integer\n"); + else + xprintf("One variable is binary\n"); + } + else + { xprintf("%d integer variables, ", ni); + if (nb == 0) + xprintf("none"); + else if (nb == 1) + xprintf("one"); + else if (nb == ni) + xprintf("all"); + else + xprintf("%d", nb); + xprintf(" of which %s binary\n", nb == 1 ? "is" : "are"); + } + } + xprintf("%d lines were read\n", csa->count); + /* problem data has been successfully read */ + glp_sort_matrix(P); + ret = 0; +done: if (csa->fp != NULL) xfclose(csa->fp); + if (rf != NULL) xfree(rf); + if (cf != NULL) xfree(cf); + if (ln != NULL) xfree(ln); + if (ia != NULL) xfree(ia); + if (ja != NULL) xfree(ja); + if (ar != NULL) xfree(ar); + if (ret) glp_erase_prob(P); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_prob - write problem data in GLPK format +* +* SYNOPSIS +* +* int glp_write_prob(glp_prob *P, int flags, const char *fname); +* +* The routine glp_write_prob writes problem data in GLPK LP/MIP format +* to a text file. +* +* RETURNS +* +* If the operation was successful, the routine returns zero. Otherwise +* it prints an error message and returns non-zero. */ + +int glp_write_prob(glp_prob *P, int flags, const char *fname) +{ XFILE *fp; + GLPROW *row; + GLPCOL *col; + GLPAIJ *aij; + int mip, i, j, count, ret; + if (P == NULL || P->magic != GLP_PROB_MAGIC) + xerror("glp_write_prob: P = %p; invalid problem object\n", + P); + if (flags != 0) + xerror("glp_write_prob: flags = %d; invalid parameter\n", + flags); + if (fname == NULL) + xerror("glp_write_prob: fname = %d; invalid parameter\n", + fname); + xprintf("Writing problem data to `%s'...\n", fname); + fp = xfopen(fname, "w"), count = 0; + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* write problem line */ + mip = (glp_get_num_int(P) > 0); + xfprintf(fp, "p %s %s %d %d %d\n", !mip ? "lp" : "mip", + P->dir == GLP_MIN ? "min" : P->dir == GLP_MAX ? "max" : "???", + P->m, P->n, P->nnz), count++; + if (P->name != NULL) + xfprintf(fp, "n p %s\n", P->name), count++; + if (P->obj != NULL) + xfprintf(fp, "n z %s\n", P->obj), count++; + /* write row descriptors */ + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + if (row->type == GLP_FX && row->lb == 0.0) + goto skip1; + xfprintf(fp, "i %d ", i), count++; + if (row->type == GLP_FR) + xfprintf(fp, "f\n"); + else if (row->type == GLP_LO) + xfprintf(fp, "l %.*g\n", DBL_DIG, row->lb); + else if (row->type == GLP_UP) + xfprintf(fp, "u %.*g\n", DBL_DIG, row->ub); + else if (row->type == GLP_DB) + xfprintf(fp, "d %.*g %.*g\n", DBL_DIG, row->lb, DBL_DIG, + row->ub); + else if (row->type == GLP_FX) + xfprintf(fp, "s %.*g\n", DBL_DIG, row->lb); + else + xassert(row != row); +skip1: if (row->name != NULL) + xfprintf(fp, "n i %d %s\n", i, row->name), count++; + } + /* write column descriptors */ + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (!mip && col->type == GLP_LO && col->lb == 0.0) + goto skip2; + if (mip && col->kind == GLP_IV && col->type == GLP_DB && + col->lb == 0.0 && col->ub == 1.0) + goto skip2; + xfprintf(fp, "j %d ", j), count++; + if (mip) + { if (col->kind == GLP_CV) + xfprintf(fp, "c "); + else if (col->kind == GLP_IV) + xfprintf(fp, "i "); + else + xassert(col != col); + } + if (col->type == GLP_FR) + xfprintf(fp, "f\n"); + else if (col->type == GLP_LO) + xfprintf(fp, "l %.*g\n", DBL_DIG, col->lb); + else if (col->type == GLP_UP) + xfprintf(fp, "u %.*g\n", DBL_DIG, col->ub); + else if (col->type == GLP_DB) + xfprintf(fp, "d %.*g %.*g\n", DBL_DIG, col->lb, DBL_DIG, + col->ub); + else if (col->type == GLP_FX) + xfprintf(fp, "s %.*g\n", DBL_DIG, col->lb); + else + xassert(col != col); +skip2: if (col->name != NULL) + xfprintf(fp, "n j %d %s\n", j, col->name), count++; + } + /* write objective coefficient descriptors */ + if (P->c0 != 0.0) + xfprintf(fp, "a 0 0 %.*g\n", DBL_DIG, P->c0), count++; + for (j = 1; j <= P->n; j++) + { col = P->col[j]; + if (col->coef != 0.0) + xfprintf(fp, "a 0 %d %.*g\n", j, DBL_DIG, col->coef), + count++; + } + /* write constraint coefficient descriptors */ + for (i = 1; i <= P->m; i++) + { row = P->row[i]; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + xfprintf(fp, "a %d %d %.*g\n", i, aij->col->j, DBL_DIG, + aij->val), count++; + } + /* write end line */ + xfprintf(fp, "e o f\n"), count++; + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + xprintf("%d lines were written\n", count); + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,228 @@ +/* glpenv.h (GLPK environment) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPENV_H +#define GLPENV_H + +#include "glpstd.h" +#include "glplib.h" + +typedef struct ENV ENV; +typedef struct MEM MEM; +typedef struct XFILE XFILE; + +#define ENV_MAGIC 0x454E5631 +/* environment block magic value */ + +#define TERM_BUF_SIZE 4096 +/* terminal output buffer size, in bytes */ + +#define IOERR_MSG_SIZE 1024 +/* i/o error message buffer size, in bytes */ + +#define MEM_MAGIC 0x4D454D31 +/* memory block descriptor magic value */ + +struct ENV +{ /* environment block */ + int magic; + /* magic value used for debugging */ + char version[7+1]; + /* version string returned by the routine glp_version */ + /*--------------------------------------------------------------*/ + /* terminal output */ + char *term_buf; /* char term_buf[TERM_BUF_SIZE]; */ + /* terminal output buffer */ + int term_out; + /* flag to enable/disable terminal output */ + int (*term_hook)(void *info, const char *s); + /* user-defined routine to intercept terminal output */ + void *term_info; + /* transit pointer (cookie) passed to the routine term_hook */ + FILE *tee_file; + /* output stream used to copy terminal output */ + /*--------------------------------------------------------------*/ + /* error handling */ + const char *err_file; + /* value of the __FILE__ macro passed to glp_error */ + int err_line; + /* value of the __LINE__ macro passed to glp_error */ + void (*err_hook)(void *info); + /* user-defined routine to intercept abnormal termination */ + void *err_info; + /* transit pointer (cookie) passed to the routine err_hook */ + /*--------------------------------------------------------------*/ + /* memory allocation */ + glp_long mem_limit; + /* maximal amount of memory (in bytes) available for dynamic + allocation */ + MEM *mem_ptr; + /* pointer to the linked list of allocated memory blocks */ + int mem_count; + /* total number of currently allocated memory blocks */ + int mem_cpeak; + /* peak value of mem_count */ + glp_long mem_total; + /* total amount of currently allocated memory (in bytes; is the + sum of the size field over all memory block descriptors) */ + glp_long mem_tpeak; + /* peak value of mem_total */ + /*--------------------------------------------------------------*/ + /* stream input/output */ + XFILE *file_ptr; + /* pointer to the linked list of active stream descriptors */ + char *ioerr_msg; /* char ioerr_msg[IOERR_MSG_SIZE]; */ + /* input/output error message buffer */ + /*--------------------------------------------------------------*/ + /* shared libraries support */ + void *h_odbc; + /* handle to ODBC shared library */ + void *h_mysql; + /* handle to MySQL shared library */ +}; + +struct MEM +{ /* memory block descriptor */ + int flag; + /* descriptor flag */ + int size; + /* size of block (in bytes, including descriptor) */ + MEM *prev; + /* pointer to previous memory block descriptor */ + MEM *next; + /* pointer to next memory block descriptor */ +}; + +struct XFILE +{ /* input/output stream descriptor */ + int type; + /* stream handle type: */ +#define FH_FILE 0x11 /* FILE */ +#define FH_ZLIB 0x22 /* gzFile */ + void *fh; + /* pointer to stream handle */ + XFILE *prev; + /* pointer to previous stream descriptor */ + XFILE *next; + /* pointer to next stream descriptor */ +}; + +#define XEOF (-1) + +#define get_env_ptr _glp_get_env_ptr +ENV *get_env_ptr(void); +/* retrieve pointer to environment block */ + +#define tls_set_ptr _glp_tls_set_ptr +void tls_set_ptr(void *ptr); +/* store global pointer in TLS */ + +#define tls_get_ptr _glp_tls_get_ptr +void *tls_get_ptr(void); +/* retrieve global pointer from TLS */ + +#define xprintf glp_printf +void glp_printf(const char *fmt, ...); +/* write formatted output to the terminal */ + +#define xvprintf glp_vprintf +void glp_vprintf(const char *fmt, va_list arg); +/* write formatted output to the terminal */ + +#ifndef GLP_ERROR_DEFINED +#define GLP_ERROR_DEFINED +typedef void (*_glp_error)(const char *fmt, ...); +#endif + +#define xerror glp_error_(__FILE__, __LINE__) +_glp_error glp_error_(const char *file, int line); +/* display error message and terminate execution */ + +#define xassert(expr) \ + ((void)((expr) || (glp_assert_(#expr, __FILE__, __LINE__), 1))) +void glp_assert_(const char *expr, const char *file, int line); +/* check for logical condition */ + +#define xmalloc glp_malloc +void *glp_malloc(int size); +/* allocate memory block */ + +#define xcalloc glp_calloc +void *glp_calloc(int n, int size); +/* allocate memory block */ + +#define xfree glp_free +void glp_free(void *ptr); +/* free memory block */ + +#define xtime glp_time +glp_long glp_time(void); +/* determine current universal time */ + +#define xdifftime glp_difftime +double glp_difftime(glp_long t1, glp_long t0); +/* compute difference between two time values, in seconds */ + +#define lib_err_msg _glp_lib_err_msg +void lib_err_msg(const char *msg); + +#define xerrmsg _glp_lib_xerrmsg +const char *xerrmsg(void); + +#define xfopen _glp_lib_xfopen +XFILE *xfopen(const char *fname, const char *mode); + +#define xferror _glp_lib_xferror +int xferror(XFILE *file); + +#define xfeof _glp_lib_xfeof +int xfeof(XFILE *file); + +#define xfgetc _glp_lib_xfgetc +int xfgetc(XFILE *file); + +#define xfputc _glp_lib_xfputc +int xfputc(int c, XFILE *file); + +#define xfflush _glp_lib_xfflush +int xfflush(XFILE *fp); + +#define xfclose _glp_lib_xfclose +int xfclose(XFILE *file); + +#define xfprintf _glp_lib_xfprintf +int xfprintf(XFILE *file, const char *fmt, ...); + +#define xdlopen _glp_xdlopen +void *xdlopen(const char *module); + +#define xdlsym _glp_xdlsym +void *xdlsym(void *h, const char *symbol); + +#define xdlclose _glp_xdlclose +void xdlclose(void *h); + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,233 @@ +/* glpenv01.c (environment initialization/termination) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_init_env - initialize GLPK environment +* +* SYNOPSIS +* +* int glp_init_env(void); +* +* DESCRIPTION +* +* The routine glp_init_env initializes the GLPK environment. Normally +* the application program does not need to call this routine, because +* it is called automatically on the first call to any API routine. +* +* RETURNS +* +* The routine glp_init_env returns one of the following codes: +* +* 0 - initialization successful; +* 1 - environment has been already initialized; +* 2 - initialization failed (insufficient memory); +* 3 - initialization failed (unsupported programming model). */ + +int glp_init_env(void) +{ ENV *env; + int ok; + /* check if the programming model is supported */ + ok = (CHAR_BIT == 8 && sizeof(char) == 1 && + sizeof(short) == 2 && sizeof(int) == 4 && + (sizeof(void *) == 4 || sizeof(void *) == 8)); + if (!ok) return 3; + /* check if the environment is already initialized */ + if (tls_get_ptr() != NULL) return 1; + /* allocate and initialize the environment block */ + env = malloc(sizeof(ENV)); + if (env == NULL) return 2; + env->magic = ENV_MAGIC; + sprintf(env->version, "%d.%d", + GLP_MAJOR_VERSION, GLP_MINOR_VERSION); + env->term_buf = malloc(TERM_BUF_SIZE); + if (env->term_buf == NULL) + { free(env); + return 2; + } + env->term_out = GLP_ON; + env->term_hook = NULL; + env->term_info = NULL; + env->tee_file = NULL; + env->err_file = ""; + env->err_line = 0; + env->err_hook = NULL; + env->err_info = NULL; + env->mem_limit.hi = 0x7FFFFFFF, env->mem_limit.lo = 0xFFFFFFFF; + env->mem_ptr = NULL; + env->mem_count = env->mem_cpeak = 0; + env->mem_total = env->mem_tpeak = xlset(0); + env->file_ptr = NULL; + env->ioerr_msg = malloc(IOERR_MSG_SIZE); + if (env->ioerr_msg == NULL) + { free(env->term_buf); + free(env); + return 2; + } + strcpy(env->ioerr_msg, "No error"); + env->h_odbc = env->h_mysql = NULL; + /* save pointer to the environment block */ + tls_set_ptr(env); + /* initialization successful */ + return 0; +} + +/*********************************************************************** +* NAME +* +* get_env_ptr - retrieve pointer to environment block +* +* SYNOPSIS +* +* #include "glpenv.h" +* ENV *get_env_ptr(void); +* +* DESCRIPTION +* +* The routine get_env_ptr retrieves and returns a pointer to the GLPK +* environment block. +* +* If the GLPK environment has not been initialized yet, the routine +* performs initialization. If initialization fails, the routine prints +* an error message to stderr and terminates the program. +* +* RETURNS +* +* The routine returns a pointer to the environment block. */ + +ENV *get_env_ptr(void) +{ ENV *env = tls_get_ptr(); + /* check if the environment has been initialized */ + if (env == NULL) + { /* not initialized yet; perform initialization */ + if (glp_init_env() != 0) + { /* initialization failed; display an error message */ + fprintf(stderr, "GLPK initialization failed\n"); + fflush(stderr); + /* and abnormally terminate the program */ + abort(); + } + /* initialization successful; retrieve the pointer */ + env = tls_get_ptr(); + } + /* check if the environment block is valid */ + if (env->magic != ENV_MAGIC) + { fprintf(stderr, "Invalid GLPK environment\n"); + fflush(stderr); + abort(); + } + return env; +} + +/*********************************************************************** +* NAME +* +* glp_version - determine library version +* +* SYNOPSIS +* +* const char *glp_version(void); +* +* RETURNS +* +* The routine glp_version returns a pointer to a null-terminated +* character string, which specifies the version of the GLPK library in +* the form "X.Y", where X is the major version number, and Y is the +* minor version number, for example, "4.16". */ + +const char *glp_version(void) +{ ENV *env = get_env_ptr(); + return env->version; +} + +/*********************************************************************** +* NAME +* +* glp_free_env - free GLPK environment +* +* SYNOPSIS +* +* int glp_free_env(void); +* +* DESCRIPTION +* +* The routine glp_free_env frees all resources used by GLPK routines +* (memory blocks, etc.) which are currently still in use. +* +* Normally the application program does not need to call this routine, +* because GLPK routines always free all unused resources. However, if +* the application program even has deleted all problem objects, there +* will be several memory blocks still allocated for the library needs. +* For some reasons the application program may want GLPK to free this +* memory, in which case it should call glp_free_env. +* +* Note that a call to glp_free_env invalidates all problem objects as +* if no GLPK routine were called. +* +* RETURNS +* +* 0 - termination successful; +* 1 - environment is inactive (was not initialized). */ + +int glp_free_env(void) +{ ENV *env = tls_get_ptr(); + MEM *desc; + /* check if the environment is active */ + if (env == NULL) return 1; + /* check if the environment block is valid */ + if (env->magic != ENV_MAGIC) + { fprintf(stderr, "Invalid GLPK environment\n"); + fflush(stderr); + abort(); + } + /* close handles to shared libraries */ + if (env->h_odbc != NULL) + xdlclose(env->h_odbc); + if (env->h_mysql != NULL) + xdlclose(env->h_mysql); + /* close streams which are still open */ + while (env->file_ptr != NULL) + xfclose(env->file_ptr); + /* free memory blocks which are still allocated */ + while (env->mem_ptr != NULL) + { desc = env->mem_ptr; + env->mem_ptr = desc->next; + free(desc); + } + /* invalidate the environment block */ + env->magic = -1; + /* free memory allocated to the environment block */ + free(env->term_buf); + free(env->ioerr_msg); + free(env); + /* reset a pointer to the environment block */ + tls_set_ptr(NULL); + /* termination successful */ + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,73 @@ +/* glpenv02.c (thread local storage) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" + +static void *tls = NULL; +/* in a re-entrant version of the package this variable must be placed + in the Thread Local Storage (TLS) */ + +/*********************************************************************** +* NAME +* +* tls_set_ptr - store global pointer in TLS +* +* SYNOPSIS +* +* #include "glpenv.h" +* void tls_set_ptr(void *ptr); +* +* DESCRIPTION +* +* The routine tls_set_ptr stores a pointer specified by the parameter +* ptr in the Thread Local Storage (TLS). */ + +void tls_set_ptr(void *ptr) +{ tls = ptr; + return; +} + +/*********************************************************************** +* NAME +* +* tls_get_ptr - retrieve global pointer from TLS +* +* SYNOPSIS +* +* #include "glpenv.h" +* void *tls_get_ptr(void); +* +* RETURNS +* +* The routine tls_get_ptr returns a pointer previously stored by the +* routine tls_set_ptr. If the latter has not been called yet, NULL is +* returned. */ + +void *tls_get_ptr(void) +{ void *ptr; + ptr = tls; + return ptr; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv03.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv03.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,228 @@ +/* glpenv03.c (terminal output) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_printf - write formatted output to terminal +* +* SYNOPSIS +* +* void glp_printf(const char *fmt, ...); +* +* DESCRIPTION +* +* The routine glp_printf uses the format control string fmt to format +* its parameters and writes the formatted output to the terminal. */ + +void glp_printf(const char *fmt, ...) +{ va_list arg; + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + return; +} + +/*********************************************************************** +* NAME +* +* glp_vprintf - write formatted output to terminal +* +* SYNOPSIS +* +* void glp_vprintf(const char *fmt, va_list arg); +* +* DESCRIPTION +* +* The routine glp_vprintf uses the format control string fmt to format +* its parameters specified by the list arg and writes the formatted +* output to the terminal. */ + +void glp_vprintf(const char *fmt, va_list arg) +{ ENV *env = get_env_ptr(); + /* if terminal output is disabled, do nothing */ + if (!env->term_out) goto skip; + /* format the output */ + vsprintf(env->term_buf, fmt, arg); + /* pass the output to the user-defined routine */ + if (env->term_hook != NULL) + { if (env->term_hook(env->term_info, env->term_buf) != 0) + goto skip; + } + /* send the output to the terminal */ + fputs(env->term_buf, stdout); + fflush(stdout); + /* copy the output to the text file */ + if (env->tee_file != NULL) + { fputs(env->term_buf, env->tee_file); + fflush(env->tee_file); + } +skip: return; +} + +/*********************************************************************** +* NAME +* +* glp_term_out - enable/disable terminal output +* +* SYNOPSIS +* +* int glp_term_out(int flag); +* +* DESCRIPTION +* +* Depending on the parameter flag the routine glp_term_out enables or +* disables terminal output performed by glpk routines: +* +* GLP_ON - enable terminal output; +* GLP_OFF - disable terminal output. +* +* RETURNS +* +* The routine glp_term_out returns the previous value of the terminal +* output flag. */ + +int glp_term_out(int flag) +{ ENV *env = get_env_ptr(); + int old = env->term_out; + if (!(flag == GLP_ON || flag == GLP_OFF)) + xerror("glp_term_out: flag = %d; invalid value\n", flag); + env->term_out = flag; + return old; +} + +/*********************************************************************** +* NAME +* +* glp_term_hook - install hook to intercept terminal output +* +* SYNOPSIS +* +* void glp_term_hook(int (*func)(void *info, const char *s), +* void *info); +* +* DESCRIPTION +* +* The routine glp_term_hook installs a user-defined hook routine to +* intercept all terminal output performed by glpk routines. +* +* This feature can be used to redirect the terminal output to other +* destination, for example to a file or a text window. +* +* The parameter func specifies the user-defined hook routine. It is +* called from an internal printing routine, which passes to it two +* parameters: info and s. The parameter info is a transit pointer, +* specified in the corresponding call to the routine glp_term_hook; +* it may be used to pass some information to the hook routine. The +* parameter s is a pointer to the null terminated character string, +* which is intended to be written to the terminal. If the hook routine +* returns zero, the printing routine writes the string s to the +* terminal in a usual way; otherwise, if the hook routine returns +* non-zero, no terminal output is performed. +* +* To uninstall the hook routine the parameters func and info should be +* specified as NULL. */ + +void glp_term_hook(int (*func)(void *info, const char *s), void *info) +{ ENV *env = get_env_ptr(); + if (func == NULL) + { env->term_hook = NULL; + env->term_info = NULL; + } + else + { env->term_hook = func; + env->term_info = info; + } + return; +} + +/*********************************************************************** +* NAME +* +* glp_open_tee - start copying terminal output to text file +* +* SYNOPSIS +* +* int glp_open_tee(const char *fname); +* +* DESCRIPTION +* +* The routine glp_open_tee starts copying all the terminal output to +* an output text file, whose name is specified by the character string +* fname. +* +* RETURNS +* +* 0 - operation successful +* 1 - copying terminal output is already active +* 2 - unable to create output file */ + +int glp_open_tee(const char *fname) +{ ENV *env = get_env_ptr(); + if (env->tee_file != NULL) + { /* copying terminal output is already active */ + return 1; + } + env->tee_file = fopen(fname, "w"); + if (env->tee_file == NULL) + { /* unable to create output file */ + return 2; + } + return 0; +} + +/*********************************************************************** +* NAME +* +* glp_close_tee - stop copying terminal output to text file +* +* SYNOPSIS +* +* int glp_close_tee(void); +* +* DESCRIPTION +* +* The routine glp_close_tee stops copying the terminal output to the +* output text file previously open by the routine glp_open_tee closing +* that file. +* +* RETURNS +* +* 0 - operation successful +* 1 - copying terminal output was not started */ + +int glp_close_tee(void) +{ ENV *env = get_env_ptr(); + if (env->tee_file == NULL) + { /* copying terminal output was not started */ + return 1; + } + fclose(env->tee_file); + env->tee_file = NULL; + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv04.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv04.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,124 @@ +/* glpenv04.c (error handling) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_error - display error message and terminate execution +* +* SYNOPSIS +* +* void glp_error(const char *fmt, ...); +* +* DESCRIPTION +* +* The routine glp_error (implemented as a macro) formats its +* parameters using the format control string fmt, writes the formatted +* message to the terminal, and abnormally terminates the program. */ + +static void error(const char *fmt, ...) +{ ENV *env = get_env_ptr(); + va_list arg; + env->term_out = GLP_ON; + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + xprintf("Error detected in file %s at line %d\n", env->err_file, + env->err_line); + if (env->err_hook != NULL) + env->err_hook(env->err_info); + abort(); + exit(EXIT_FAILURE); + /* no return */ +} + +_glp_error glp_error_(const char *file, int line) +{ ENV *env = get_env_ptr(); + env->err_file = file; + env->err_line = line; + return error; +} + +/*********************************************************************** +* NAME +* +* glp_assert - check for logical condition +* +* SYNOPSIS +* +* #include "glplib.h" +* void glp_assert(int expr); +* +* DESCRIPTION +* +* The routine glp_assert (implemented as a macro) checks for a logical +* condition specified by the parameter expr. If the condition is false +* (i.e. the value of expr is zero), the routine writes a message to +* the terminal and abnormally terminates the program. */ + +void glp_assert_(const char *expr, const char *file, int line) +{ glp_error_(file, line)("Assertion failed: %s\n", expr); + /* no return */ +} + +/*********************************************************************** +* NAME +* +* glp_error_hook - install hook to intercept abnormal termination +* +* SYNOPSIS +* +* void glp_error_hook(void (*func)(void *info), void *info); +* +* DESCRIPTION +* +* The routine glp_error_hook installs a user-defined hook routine to +* intercept abnormal termination. +* +* The parameter func specifies the user-defined hook routine. It is +* called from the routine glp_error before the latter calls the abort +* function to abnormally terminate the application program because of +* fatal error. The parameter info is a transit pointer, specified in +* the corresponding call to the routine glp_error_hook; it may be used +* to pass some information to the hook routine. +* +* To uninstall the hook routine the parameters func and info should be +* specified as NULL. */ + +void glp_error_hook(void (*func)(void *info), void *info) +{ ENV *env = get_env_ptr(); + if (func == NULL) + { env->err_hook = NULL; + env->err_info = NULL; + } + else + { env->err_hook = func; + env->err_info = info; + } + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv05.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv05.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,225 @@ +/* glpenv05.c (memory allocation) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/* some processors need data to be properly aligned; the macro + align_datasize enlarges the specified size of a data item to provide + a proper alignment of immediately following data */ + +#define align_datasize(size) ((((size) + 15) / 16) * 16) +/* 16 bytes is sufficient in both 32- and 64-bit environments + (8 bytes is not sufficient in 64-bit environment due to jmp_buf) */ + +/*********************************************************************** +* NAME +* +* glp_malloc - allocate memory block +* +* SYNOPSIS +* +* void *glp_malloc(int size); +* +* DESCRIPTION +* +* The routine glp_malloc allocates a memory block of size bytes long. +* +* Note that being allocated the memory block contains arbitrary data +* (not binary zeros). +* +* RETURNS +* +* The routine glp_malloc returns a pointer to the allocated block. +* To free this block the routine glp_free (not free!) must be used. */ + +void *glp_malloc(int size) +{ ENV *env = get_env_ptr(); + MEM *desc; + int size_of_desc = align_datasize(sizeof(MEM)); + if (size < 1 || size > INT_MAX - size_of_desc) + xerror("glp_malloc: size = %d; invalid parameter\n", size); + size += size_of_desc; + if (xlcmp(xlset(size), + xlsub(env->mem_limit, env->mem_total)) > 0) + xerror("glp_malloc: memory limit exceeded\n"); + if (env->mem_count == INT_MAX) + xerror("glp_malloc: too many memory blocks allocated\n"); + desc = malloc(size); + if (desc == NULL) + xerror("glp_malloc: no memory available\n"); + memset(desc, '?', size); + desc->flag = MEM_MAGIC; + desc->size = size; + desc->prev = NULL; + desc->next = env->mem_ptr; + if (desc->next != NULL) desc->next->prev = desc; + env->mem_ptr = desc; + env->mem_count++; + if (env->mem_cpeak < env->mem_count) + env->mem_cpeak = env->mem_count; + env->mem_total = xladd(env->mem_total, xlset(size)); + if (xlcmp(env->mem_tpeak, env->mem_total) < 0) + env->mem_tpeak = env->mem_total; + return (void *)((char *)desc + size_of_desc); +} + +/*********************************************************************** +* NAME +* +* glp_calloc - allocate memory block +* +* SYNOPSIS +* +* void *glp_calloc(int n, int size); +* +* DESCRIPTION +* +* The routine glp_calloc allocates a memory block of (n*size) bytes +* long. +* +* Note that being allocated the memory block contains arbitrary data +* (not binary zeros). +* +* RETURNS +* +* The routine glp_calloc returns a pointer to the allocated block. +* To free this block the routine glp_free (not free!) must be used. */ + +void *glp_calloc(int n, int size) +{ if (n < 1) + xerror("glp_calloc: n = %d; invalid parameter\n", n); + if (size < 1) + xerror("glp_calloc: size = %d; invalid parameter\n", size); + if (n > INT_MAX / size) + xerror("glp_calloc: n = %d; size = %d; array too big\n", n, + size); + return xmalloc(n * size); +} + +/*********************************************************************** +* NAME +* +* glp_free - free memory block +* +* SYNOPSIS +* +* void glp_free(void *ptr); +* +* DESCRIPTION +* +* The routine glp_free frees a memory block pointed to by ptr, which +* was previuosly allocated by the routine glp_malloc or glp_calloc. */ + +void glp_free(void *ptr) +{ ENV *env = get_env_ptr(); + MEM *desc; + int size_of_desc = align_datasize(sizeof(MEM)); + if (ptr == NULL) + xerror("glp_free: ptr = %p; null pointer\n", ptr); + desc = (void *)((char *)ptr - size_of_desc); + if (desc->flag != MEM_MAGIC) + xerror("glp_free: ptr = %p; invalid pointer\n", ptr); + if (env->mem_count == 0 || + xlcmp(env->mem_total, xlset(desc->size)) < 0) + xerror("glp_free: memory allocation error\n"); + if (desc->prev == NULL) + env->mem_ptr = desc->next; + else + desc->prev->next = desc->next; + if (desc->next == NULL) + ; + else + desc->next->prev = desc->prev; + env->mem_count--; + env->mem_total = xlsub(env->mem_total, xlset(desc->size)); + memset(desc, '?', size_of_desc); + free(desc); + return; +} + +/*********************************************************************** +* NAME +* +* glp_mem_limit - set memory usage limit +* +* SYNOPSIS +* +* void glp_mem_limit(int limit); +* +* DESCRIPTION +* +* The routine glp_mem_limit limits the amount of memory available for +* dynamic allocation (in GLPK routines) to limit megabytes. */ + +void glp_mem_limit(int limit) +{ ENV *env = get_env_ptr(); + if (limit < 0) + xerror("glp_mem_limit: limit = %d; invalid parameter\n", + limit); + env->mem_limit = xlmul(xlset(limit), xlset(1 << 20)); + return; +} + +/*********************************************************************** +* NAME +* +* glp_mem_usage - get memory usage information +* +* SYNOPSIS +* +* void glp_mem_usage(int *count, int *cpeak, glp_long *total, +* glp_long *tpeak); +* +* DESCRIPTION +* +* The routine glp_mem_usage reports some information about utilization +* of the memory by GLPK routines. Information is stored to locations +* specified by corresponding parameters (see below). Any parameter can +* be specified as NULL, in which case corresponding information is not +* stored. +* +* *count is the number of the memory blocks currently allocated by the +* routines xmalloc and xcalloc (one call to xmalloc or xcalloc results +* in allocating one memory block). +* +* *cpeak is the peak value of *count reached since the initialization +* of the GLPK library environment. +* +* *total is the total amount, in bytes, of the memory blocks currently +* allocated by the routines xmalloc and xcalloc. +* +* *tpeak is the peak value of *total reached since the initialization +* of the GLPK library envirionment. */ + +void glp_mem_usage(int *count, int *cpeak, glp_long *total, + glp_long *tpeak) +{ ENV *env = get_env_ptr(); + if (count != NULL) *count = env->mem_count; + if (cpeak != NULL) *cpeak = env->mem_cpeak; + if (total != NULL) *total = env->mem_total; + if (tpeak != NULL) *tpeak = env->mem_tpeak; + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv06.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv06.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,172 @@ +/* glpenv06.c (standard time) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_time - determine current universal time +* +* SYNOPSIS +* +* glp_long glp_time(void); +* +* RETURNS +* +* The routine glp_time returns the current universal time (UTC), in +* milliseconds, elapsed since 00:00:00 GMT January 1, 1970. */ + +static const int epoch = 2440588; /* = jday(1, 1, 1970) */ + +/* POSIX version ******************************************************/ + +#if defined(HAVE_SYS_TIME_H) && defined(HAVE_GETTIMEOFDAY) + +#include +#include + +glp_long glp_time(void) +{ struct timeval tv; + struct tm *tm; + glp_long t; + int j; + gettimeofday(&tv, NULL); + tm = gmtime(&tv.tv_sec); + j = jday(tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year); + xassert(j >= 0); + t = xlset(j - epoch); + t = xlmul(t, xlset(24)); + t = xladd(t, xlset(tm->tm_hour)); + t = xlmul(t, xlset(60)); + t = xladd(t, xlset(tm->tm_min)); + t = xlmul(t, xlset(60)); + t = xladd(t, xlset(tm->tm_sec)); + t = xlmul(t, xlset(1000)); + t = xladd(t, xlset(tv.tv_usec / 1000)); + return t; +} + +/* Windows version ****************************************************/ + +#elif defined(__WOE__) + +#include + +glp_long glp_time(void) +{ SYSTEMTIME st; + glp_long t; + int j; + GetSystemTime(&st); + j = jday(st.wDay, st.wMonth, st.wYear); + xassert(j >= 0); + t = xlset(j - epoch); + t = xlmul(t, xlset(24)); + t = xladd(t, xlset(st.wHour)); + t = xlmul(t, xlset(60)); + t = xladd(t, xlset(st.wMinute)); + t = xlmul(t, xlset(60)); + t = xladd(t, xlset(st.wSecond)); + t = xlmul(t, xlset(1000)); + t = xladd(t, xlset(st.wMilliseconds)); + return t; +} + +/* portable ISO C version *********************************************/ + +#else + +#include + +glp_long glp_time(void) +{ time_t timer; + struct tm *tm; + glp_long t; + int j; + timer = time(NULL); + tm = gmtime(&timer); + j = jday(tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year); + xassert(j >= 0); + t = xlset(j - epoch); + t = xlmul(t, xlset(24)); + t = xladd(t, xlset(tm->tm_hour)); + t = xlmul(t, xlset(60)); + t = xladd(t, xlset(tm->tm_min)); + t = xlmul(t, xlset(60)); + t = xladd(t, xlset(tm->tm_sec)); + t = xlmul(t, xlset(1000)); + return t; +} + +#endif + +/*********************************************************************** +* NAME +* +* glp_difftime - compute difference between two time values +* +* SYNOPSIS +* +* double glp_difftime(glp_long t1, glp_long t0); +* +* RETURNS +* +* The routine glp_difftime returns the difference between two time +* values t1 and t0, expressed in seconds. */ + +double glp_difftime(glp_long t1, glp_long t0) +{ return + xltod(xlsub(t1, t0)) / 1000.0; +} + +/**********************************************************************/ + +#if 0 +int main(void) +{ glp_long t; + glp_ldiv d; + int ttt, ss, mm, hh, day, month, year; + char s[50]; + t = glp_time(); + xprintf("t = %s\n", xltoa(t, s)); + d = xldiv(t, xlset(1000)); + ttt = d.rem.lo, t = d.quot; + d = xldiv(t, xlset(60)); + ss = d.rem.lo, t = d.quot; + d = xldiv(t, xlset(60)); + mm = d.rem.lo, t = d.quot; + d = xldiv(t, xlset(24)); + hh = d.rem.lo, t = d.quot; + xassert(jdate(t.lo + epoch, &day, &month, &year) == 0); + xprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d\n", year, month, day, + hh, mm, ss, ttt); + return 0; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv07.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv07.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,671 @@ +/* glpenv07.c (stream input/output) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glpenv.h" + +/*********************************************************************** +* NAME +* +* lib_err_msg - save error message string +* +* SYNOPSIS +* +* #include "glpenv.h" +* void lib_err_msg(const char *msg); +* +* DESCRIPTION +* +* The routine lib_err_msg saves an error message string specified by +* the parameter msg. The message is obtained by some library routines +* with a call to strerror(errno). */ + +void lib_err_msg(const char *msg) +{ ENV *env = get_env_ptr(); + int len = strlen(msg); + if (len >= IOERR_MSG_SIZE) + len = IOERR_MSG_SIZE - 1; + memcpy(env->ioerr_msg, msg, len); + if (len > 0 && env->ioerr_msg[len-1] == '\n') len--; + env->ioerr_msg[len] = '\0'; + return; +} + +/*********************************************************************** +* NAME +* +* xerrmsg - retrieve error message string +* +* SYNOPSIS +* +* #include "glpenv.h" +* const char *xerrmsg(void); +* +* RETURNS +* +* The routine xerrmsg returns a pointer to an error message string +* previously set by some library routine to indicate an error. */ + +const char *xerrmsg(void) +{ ENV *env = get_env_ptr(); + return env->ioerr_msg; +} + +/*********************************************************************** +* NAME +* +* xfopen - open a stream +* +* SYNOPSIS +* +* #include "glpenv.h" +* XFILE *xfopen(const char *fname, const char *mode); +* +* DESCRIPTION +* +* The routine xfopen opens the file whose name is a string pointed to +* by fname and associates a stream with it. +* +* The parameter mode points to a string, which indicates the open mode +* and should be one of the following: +* +* "r" open text file for reading; +* "w" truncate to zero length or create text file for writing; +* "rb" open binary file for reading; +* "wb" truncate to zero length or create binary file for writing. +* +* RETURNS +* +* The routine xfopen returns a pointer to the object controlling the +* stream. If the open operation fails, xfopen returns NULL. */ + +static void *c_fopen(const char *fname, const char *mode); +static void *z_fopen(const char *fname, const char *mode); + +static int is_gz_file(const char *fname) +{ char *ext = strrchr(fname, '.'); + return ext != NULL && strcmp(ext, ".gz") == 0; +} + +XFILE *xfopen(const char *fname, const char *mode) +{ ENV *env = get_env_ptr(); + XFILE *fp; + int type; + void *fh; + if (!is_gz_file(fname)) + { type = FH_FILE; + fh = c_fopen(fname, mode); + } + else + { type = FH_ZLIB; + fh = z_fopen(fname, mode); + } + if (fh == NULL) + { fp = NULL; + goto done; + } + fp = xmalloc(sizeof(XFILE)); + fp->type = type; + fp->fh = fh; + fp->prev = NULL; + fp->next = env->file_ptr; + if (fp->next != NULL) fp->next->prev = fp; + env->file_ptr = fp; +done: return fp; +} + +/*********************************************************************** +* NAME +* +* xfgetc - read character from the stream +* +* SYNOPSIS +* +* #include "glpenv.h" +* int xfgetc(XFILE *fp); +* +* DESCRIPTION +* +* If the end-of-file indicator for the input stream pointed to by fp +* is not set and a next character is present, the routine xfgetc +* obtains that character as an unsigned char converted to an int and +* advances the associated file position indicator for the stream (if +* defined). +* +* RETURNS +* +* If the end-of-file indicator for the stream is set, or if the +* stream is at end-of-file, the end-of-file indicator for the stream +* is set and the routine xfgetc returns XEOF. Otherwise, the routine +* xfgetc returns the next character from the input stream pointed to +* by fp. If a read error occurs, the error indicator for the stream is +* set and the xfgetc routine returns XEOF. +* +* Note: An end-of-file and a read error can be distinguished by use of +* the routines xfeof and xferror. */ + +static int c_fgetc(void *fh); +static int z_fgetc(void *fh); + +int xfgetc(XFILE *fp) +{ int c; + switch (fp->type) + { case FH_FILE: + c = c_fgetc(fp->fh); + break; + case FH_ZLIB: + c = z_fgetc(fp->fh); + break; + default: + xassert(fp != fp); + } + return c; +} + +/*********************************************************************** +* NAME +* +* xfputc - write character to the stream +* +* SYNOPSIS +* +* #include "glpenv.h" +* int xfputc(int c, XFILE *fp); +* +* DESCRIPTION +* +* The routine xfputc writes the character specified by c (converted +* to an unsigned char) to the output stream pointed to by fp, at the +* position indicated by the associated file position indicator (if +* defined), and advances the indicator appropriately. +* +* RETURNS +* +* The routine xfputc returns the character written. If a write error +* occurs, the error indicator for the stream is set and xfputc returns +* XEOF. */ + +static int c_fputc(int c, void *fh); +static int z_fputc(int c, void *fh); + +int xfputc(int c, XFILE *fp) +{ switch (fp->type) + { case FH_FILE: + c = c_fputc(c, fp->fh); + break; + case FH_ZLIB: + c = z_fputc(c, fp->fh); + break; + default: + xassert(fp != fp); + } + return c; +} + +/*********************************************************************** +* NAME +* +* xferror - test error indicator for the stream +* +* SYNOPSIS +* +* #include "glpenv.h" +* int xferror(XFILE *fp); +* +* DESCRIPTION +* +* The routine xferror tests the error indicator for the stream +* pointed to by fp. +* +* RETURNS +* +* The routine xferror returns non-zero if and only if the error +* indicator is set for the stream. */ + +static int c_ferror(void *fh); +static int z_ferror(void *fh); + +int xferror(XFILE *fp) +{ int ret; + switch (fp->type) + { case FH_FILE: + ret = c_ferror(fp->fh); + break; + case FH_ZLIB: + ret = z_ferror(fp->fh); + break; + default: + xassert(fp != fp); + } + return ret; +} + +/*********************************************************************** +* NAME +* +* xfeof - test end-of-file indicator for the stream +* +* SYNOPSIS +* +* #include "glpenv.h" +* int xfeof(XFILE *fp); +* +* DESCRIPTION +* +* The routine xfeof tests the end-of-file indicator for the stream +* pointed to by fp. +* +* RETURNS +* +* The routine xfeof returns non-zero if and only if the end-of-file +* indicator is set for the stream. */ + +static int c_feof(void *fh); +static int z_feof(void *fh); + +int xfeof(XFILE *fp) +{ int ret; + switch (fp->type) + { case FH_FILE: + ret = c_feof(fp->fh); + break; + case FH_ZLIB: + ret = z_feof(fp->fh); + break; + default: + xassert(fp != fp); + } + return ret; +} + +int xfprintf(XFILE *file, const char *fmt, ...) +{ ENV *env = get_env_ptr(); + int cnt, j; + va_list arg; + va_start(arg, fmt); + cnt = vsprintf(env->term_buf, fmt, arg); + va_end(arg); + for (j = 0; j < cnt; j++) + { if (xfputc(env->term_buf[j], file) < 0) + { cnt = -1; + break; + } + } + return cnt; +} + +/*********************************************************************** +* NAME +* +* xfflush - flush the stream +* +* SYNOPSIS +* +* #include "glpenv.h" +* int xfflush(XFILE *fp); +* +* DESCRIPTION +* +* The routine xfflush causes any unwritten data for the output stream +* pointed to by fp to be written to the associated file. +* +* RETURNS +* +* The routine xfflush returns zero if the stream was successfully +* flushed. Otherwise, xfflush sets the error indicator for the stream +* and returns XEOF. */ + +static int c_fflush(void *fh); +static int z_fflush(void *fh); + +int xfflush(XFILE *fp) +{ int ret; + switch (fp->type) + { case FH_FILE: + ret = c_fflush(fp->fh); + break; + case FH_ZLIB: + ret = z_fflush(fp->fh); + break; + default: + xassert(fp != fp); + } + return ret; +} + +/*********************************************************************** +* NAME +* +* xfclose - close the stream +* +* SYNOPSIS +* +* #include "glpenv.h" +* int xfclose(XFILE *fp); +* +* DESCRIPTION +* +* A successful call to the routine xfclose causes the stream pointed +* to by fp to be flushed and the associated file to be closed. Whether +* or not the call succeeds, the stream is disassociated from the file. +* +* RETURNS +* +* The routine xfclose returns zero if the stream was successfully +* closed, or XEOF if any errors were detected. */ + +static int c_fclose(void *fh); +static int z_fclose(void *fh); + +int xfclose(XFILE *fp) +{ ENV *env = get_env_ptr(); + int ret; + switch (fp->type) + { case FH_FILE: + ret = c_fclose(fp->fh); + break; + case FH_ZLIB: + ret = z_fclose(fp->fh); + break; + default: + xassert(fp != fp); + } + fp->type = 0xF00BAD; + if (fp->prev == NULL) + env->file_ptr = fp->next; + else + fp->prev->next = fp->next; + if (fp->next == NULL) + ; + else + fp->next->prev = fp->prev; + xfree(fp); + return ret; +} + +/*********************************************************************** +* The following routines implement stream input/output based on the +* standard C streams. */ + +static void *c_fopen(const char *fname, const char *mode) +{ FILE *fh; + if (strcmp(fname, "/dev/stdin") == 0) + fh = stdin; + else if (strcmp(fname, "/dev/stdout") == 0) + fh = stdout; + else if (strcmp(fname, "/dev/stderr") == 0) + fh = stderr; + else + fh = fopen(fname, mode); + if (fh == NULL) + lib_err_msg(strerror(errno)); + return fh; +} + +static int c_fgetc(void *_fh) +{ FILE *fh = _fh; + int c; + if (ferror(fh) || feof(fh)) + { c = XEOF; + goto done; + } + c = fgetc(fh); + if (ferror(fh)) + { lib_err_msg(strerror(errno)); + c = XEOF; + } + else if (feof(fh)) + c = XEOF; + else + xassert(0x00 <= c && c <= 0xFF); +done: return c; +} + +static int c_fputc(int c, void *_fh) +{ FILE *fh = _fh; + if (ferror(fh)) + { c = XEOF; + goto done; + } + c = (unsigned char)c; + fputc(c, fh); + if (ferror(fh)) + { lib_err_msg(strerror(errno)); + c = XEOF; + } +done: return c; +} + +static int c_ferror(void *_fh) +{ FILE *fh = _fh; + return ferror(fh); +} + +static int c_feof(void *_fh) +{ FILE *fh = _fh; + return feof(fh); +} + +static int c_fflush(void *_fh) +{ FILE *fh = _fh; + int ret; + ret = fflush(fh); + if (ret != 0) + { lib_err_msg(strerror(errno)); + ret = XEOF; + } + return ret; +} + +static int c_fclose(void *_fh) +{ FILE *fh = _fh; + int ret; + if (fh == stdin) + ret = 0; + else if (fh == stdout || fh == stderr) + fflush(fh), ret = 0; + else + ret = fclose(fh); + if (ret != 0) + { lib_err_msg(strerror(errno)); + ret = XEOF; + } + return ret; +} + +/*********************************************************************** +* The following routines implement stream input/output based on the +* zlib library, which provides processing .gz files "on the fly". */ + +#ifndef HAVE_ZLIB + +static void *z_fopen(const char *fname, const char *mode) +{ xassert(fname == fname); + xassert(mode == mode); + lib_err_msg("Compressed files not supported"); + return NULL; +} + +static int z_fgetc(void *fh) +{ xassert(fh != fh); + return 0; +} + +static int z_fputc(int c, void *fh) +{ xassert(c != c); + xassert(fh != fh); + return 0; +} + +static int z_ferror(void *fh) +{ xassert(fh != fh); + return 0; +} + +static int z_feof(void *fh) +{ xassert(fh != fh); + return 0; +} + +static int z_fflush(void *fh) +{ xassert(fh != fh); + return 0; +} + +static int z_fclose(void *fh) +{ xassert(fh != fh); + return 0; +} + +#else + +#include + +struct z_file +{ /* .gz file handle */ + gzFile file; + /* pointer to .gz stream */ + int err; + /* i/o error indicator */ + int eof; + /* end-of-file indicator */ +}; + +static void *z_fopen(const char *fname, const char *mode) +{ struct z_file *fh; + gzFile file; + if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) + mode = "rb"; + else if (strcmp(mode, "w") == 0 || strcmp(mode, "wb") == 0) + mode = "wb"; + else + { lib_err_msg("Invalid open mode"); + fh = NULL; + goto done; + } + file = gzopen(fname, mode); + if (file == NULL) + { lib_err_msg(strerror(errno)); + fh = NULL; + goto done; + } + fh = xmalloc(sizeof(struct z_file)); + fh->file = file; + fh->err = fh->eof = 0; +done: return fh; +} + +static int z_fgetc(void *_fh) +{ struct z_file *fh = _fh; + int c; + if (fh->err || fh->eof) + { c = XEOF; + goto done; + } + c = gzgetc(fh->file); + if (c < 0) + { int errnum; + const char *msg; + msg = gzerror(fh->file, &errnum); + if (errnum == Z_STREAM_END) + fh->eof = 1; + else if (errnum == Z_ERRNO) + { fh->err = 1; + lib_err_msg(strerror(errno)); + } + else + { fh->err = 1; + lib_err_msg(msg); + } + c = XEOF; + } + else + xassert(0x00 <= c && c <= 0xFF); +done: return c; +} + +static int z_fputc(int c, void *_fh) +{ struct z_file *fh = _fh; + if (fh->err) + { c = XEOF; + goto done; + } + c = (unsigned char)c; + if (gzputc(fh->file, c) < 0) + { int errnum; + const char *msg; + fh->err = 1; + msg = gzerror(fh->file, &errnum); + if (errnum == Z_ERRNO) + lib_err_msg(strerror(errno)); + else + lib_err_msg(msg); + c = XEOF; + } +done: return c; +} + +static int z_ferror(void *_fh) +{ struct z_file *fh = _fh; + return fh->err; +} + +static int z_feof(void *_fh) +{ struct z_file *fh = _fh; + return fh->eof; +} + +static int z_fflush(void *_fh) +{ struct z_file *fh = _fh; + int ret; + ret = gzflush(fh->file, Z_FINISH); + if (ret == Z_OK) + ret = 0; + else + { int errnum; + const char *msg; + fh->err = 1; + msg = gzerror(fh->file, &errnum); + if (errnum == Z_ERRNO) + lib_err_msg(strerror(errno)); + else + lib_err_msg(msg); + ret = XEOF; + } + return ret; +} + +static int z_fclose(void *_fh) +{ struct z_file *fh = _fh; + gzclose(fh->file); + xfree(fh); + return 0; +} + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpenv08.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpenv08.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,156 @@ +/* glpenv08.c (shared library support) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glpenv.h" + +/* GNU version ********************************************************/ + +#if defined(HAVE_LTDL) + +#include + +void *xdlopen(const char *module) +{ void *h = NULL; + if (lt_dlinit() != 0) + { lib_err_msg(lt_dlerror()); + goto done; + } + h = lt_dlopen(module); + if (h == NULL) + { lib_err_msg(lt_dlerror()); + if (lt_dlexit() != 0) + xerror("xdlopen: %s\n", lt_dlerror()); + } +done: return h; +} + +void *xdlsym(void *h, const char *symbol) +{ void *ptr; + xassert(h != NULL); + ptr = lt_dlsym(h, symbol); + if (ptr == NULL) + xerror("xdlsym: %s: %s\n", symbol, lt_dlerror()); + return ptr; +} + +void xdlclose(void *h) +{ xassert(h != NULL); + if (lt_dlclose(h) != 0) + xerror("xdlclose: %s\n", lt_dlerror()); + if (lt_dlexit() != 0) + xerror("xdlclose: %s\n", lt_dlerror()); + return; +} + +/* POSIX version ******************************************************/ + +#elif defined(HAVE_DLFCN) + +#include + +void *xdlopen(const char *module) +{ void *h; + h = dlopen(module, RTLD_NOW); + if (h == NULL) + lib_err_msg(dlerror()); + return h; +} + +void *xdlsym(void *h, const char *symbol) +{ void *ptr; + xassert(h != NULL); + ptr = dlsym(h, symbol); + if (ptr == NULL) + xerror("xdlsym: %s: %s\n", symbol, dlerror()); + return ptr; +} + +void xdlclose(void *h) +{ xassert(h != NULL); + if (dlclose(h) != 0) + xerror("xdlclose: %s\n", dlerror()); + return; +} + +/* Windows version ****************************************************/ + +#elif defined(__WOE__) + +#include + +void *xdlopen(const char *module) +{ void *h; + h = LoadLibrary(module); + if (h == NULL) + { char msg[20]; + sprintf(msg, "Error %d", GetLastError()); + lib_err_msg(msg); + } + return h; +} + +void *xdlsym(void *h, const char *symbol) +{ void *ptr; + xassert(h != NULL); + ptr = GetProcAddress(h, symbol); + if (ptr == NULL) + xerror("xdlsym: %s: Error %d\n", symbol, GetLastError()); + return ptr; +} + +void xdlclose(void *h) +{ xassert(h != NULL); + if (!FreeLibrary(h)) + xerror("xdlclose: Error %d\n", GetLastError()); + return; +} + +/* NULL version *******************************************************/ + +#else + +void *xdlopen(const char *module) +{ xassert(module == module); + lib_err_msg("Shared libraries not supported"); + return NULL; +} + +void *xdlsym(void *h, const char *symbol) +{ xassert(h != h); + xassert(symbol != symbol); + return NULL; +} + +void xdlclose(void *h) +{ xassert(h != h); + return; +} + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpfhv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpfhv.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,774 @@ +/* glpfhv.c (LP basis factorization, FHV eta file version) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpfhv.h" +#include "glpenv.h" +#define xfault xerror + +/* CAUTION: DO NOT CHANGE THE LIMIT BELOW */ + +#define M_MAX 100000000 /* = 100*10^6 */ +/* maximal order of the basis matrix */ + +/*********************************************************************** +* NAME +* +* fhv_create_it - create LP basis factorization +* +* SYNOPSIS +* +* #include "glpfhv.h" +* FHV *fhv_create_it(void); +* +* DESCRIPTION +* +* The routine fhv_create_it creates a program object, which represents +* a factorization of LP basis. +* +* RETURNS +* +* The routine fhv_create_it returns a pointer to the object created. */ + +FHV *fhv_create_it(void) +{ FHV *fhv; + fhv = xmalloc(sizeof(FHV)); + fhv->m_max = fhv->m = 0; + fhv->valid = 0; + fhv->luf = luf_create_it(); + fhv->hh_max = 50; + fhv->hh_nfs = 0; + fhv->hh_ind = fhv->hh_ptr = fhv->hh_len = NULL; + fhv->p0_row = fhv->p0_col = NULL; + fhv->cc_ind = NULL; + fhv->cc_val = NULL; + fhv->upd_tol = 1e-6; + fhv->nnz_h = 0; + return fhv; +} + +/*********************************************************************** +* NAME +* +* fhv_factorize - compute LP basis factorization +* +* SYNOPSIS +* +* #include "glpfhv.h" +* int fhv_factorize(FHV *fhv, int m, int (*col)(void *info, int j, +* int ind[], double val[]), void *info); +* +* DESCRIPTION +* +* The routine fhv_factorize computes the factorization of the basis +* matrix B specified by the routine col. +* +* The parameter fhv specified the basis factorization data structure +* created by the routine fhv_create_it. +* +* The parameter m specifies the order of B, m > 0. +* +* The formal routine col specifies the matrix B to be factorized. To +* obtain j-th column of A the routine fhv_factorize calls the routine +* col with the parameter j (1 <= j <= n). In response the routine col +* should store row indices and numerical values of non-zero elements +* of j-th column of B to locations ind[1,...,len] and val[1,...,len], +* respectively, where len is the number of non-zeros in j-th column +* returned on exit. Neither zero nor duplicate elements are allowed. +* +* The parameter info is a transit pointer passed to the routine col. +* +* RETURNS +* +* 0 The factorization has been successfully computed. +* +* FHV_ESING +* The specified matrix is singular within the working precision. +* +* FHV_ECOND +* The specified matrix is ill-conditioned. +* +* For more details see comments to the routine luf_factorize. +* +* ALGORITHM +* +* The routine fhv_factorize calls the routine luf_factorize (see the +* module GLPLUF), which actually computes LU-factorization of the basis +* matrix B in the form +* +* [B] = (F, V, P, Q), +* +* where F and V are such matrices that +* +* B = F * V, +* +* and P and Q are such permutation matrices that the matrix +* +* L = P * F * inv(P) +* +* is lower triangular with unity diagonal, and the matrix +* +* U = P * V * Q +* +* is upper triangular. +* +* In order to build the complete representation of the factorization +* (see formula (1) in the file glpfhv.h) the routine fhv_factorize just +* additionally sets H = I and P0 = P. */ + +int fhv_factorize(FHV *fhv, int m, int (*col)(void *info, int j, + int ind[], double val[]), void *info) +{ int ret; + if (m < 1) + xfault("fhv_factorize: m = %d; invalid parameter\n", m); + if (m > M_MAX) + xfault("fhv_factorize: m = %d; matrix too big\n", m); + fhv->m = m; + /* invalidate the factorization */ + fhv->valid = 0; + /* allocate/reallocate arrays, if necessary */ + if (fhv->hh_ind == NULL) + fhv->hh_ind = xcalloc(1+fhv->hh_max, sizeof(int)); + if (fhv->hh_ptr == NULL) + fhv->hh_ptr = xcalloc(1+fhv->hh_max, sizeof(int)); + if (fhv->hh_len == NULL) + fhv->hh_len = xcalloc(1+fhv->hh_max, sizeof(int)); + if (fhv->m_max < m) + { if (fhv->p0_row != NULL) xfree(fhv->p0_row); + if (fhv->p0_col != NULL) xfree(fhv->p0_col); + if (fhv->cc_ind != NULL) xfree(fhv->cc_ind); + if (fhv->cc_val != NULL) xfree(fhv->cc_val); + fhv->m_max = m + 100; + fhv->p0_row = xcalloc(1+fhv->m_max, sizeof(int)); + fhv->p0_col = xcalloc(1+fhv->m_max, sizeof(int)); + fhv->cc_ind = xcalloc(1+fhv->m_max, sizeof(int)); + fhv->cc_val = xcalloc(1+fhv->m_max, sizeof(double)); + } + /* try to factorize the basis matrix */ + switch (luf_factorize(fhv->luf, m, col, info)) + { case 0: + break; + case LUF_ESING: + ret = FHV_ESING; + goto done; + case LUF_ECOND: + ret = FHV_ECOND; + goto done; + default: + xassert(fhv != fhv); + } + /* the basis matrix has been successfully factorized */ + fhv->valid = 1; + /* H := I */ + fhv->hh_nfs = 0; + /* P0 := P */ + memcpy(&fhv->p0_row[1], &fhv->luf->pp_row[1], sizeof(int) * m); + memcpy(&fhv->p0_col[1], &fhv->luf->pp_col[1], sizeof(int) * m); + /* currently H has no factors */ + fhv->nnz_h = 0; + ret = 0; +done: /* return to the calling program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* fhv_h_solve - solve system H*x = b or H'*x = b +* +* SYNOPSIS +* +* #include "glpfhv.h" +* void fhv_h_solve(FHV *fhv, int tr, double x[]); +* +* DESCRIPTION +* +* The routine fhv_h_solve solves either the system H*x = b (if the +* flag tr is zero) or the system H'*x = b (if the flag tr is non-zero), +* where the matrix H is a component of the factorization specified by +* the parameter fhv, H' is a matrix transposed to H. +* +* On entry the array x should contain elements of the right-hand side +* vector b in locations x[1], ..., x[m], where m is the order of the +* matrix H. On exit this array will contain elements of the solution +* vector x in the same locations. */ + +void fhv_h_solve(FHV *fhv, int tr, double x[]) +{ int nfs = fhv->hh_nfs; + int *hh_ind = fhv->hh_ind; + int *hh_ptr = fhv->hh_ptr; + int *hh_len = fhv->hh_len; + int *sv_ind = fhv->luf->sv_ind; + double *sv_val = fhv->luf->sv_val; + int i, k, beg, end, ptr; + double temp; + if (!fhv->valid) + xfault("fhv_h_solve: the factorization is not valid\n"); + if (!tr) + { /* solve the system H*x = b */ + for (k = 1; k <= nfs; k++) + { i = hh_ind[k]; + temp = x[i]; + beg = hh_ptr[k]; + end = beg + hh_len[k] - 1; + for (ptr = beg; ptr <= end; ptr++) + temp -= sv_val[ptr] * x[sv_ind[ptr]]; + x[i] = temp; + } + } + else + { /* solve the system H'*x = b */ + for (k = nfs; k >= 1; k--) + { i = hh_ind[k]; + temp = x[i]; + if (temp == 0.0) continue; + beg = hh_ptr[k]; + end = beg + hh_len[k] - 1; + for (ptr = beg; ptr <= end; ptr++) + x[sv_ind[ptr]] -= sv_val[ptr] * temp; + } + } + return; +} + +/*********************************************************************** +* NAME +* +* fhv_ftran - perform forward transformation (solve system B*x = b) +* +* SYNOPSIS +* +* #include "glpfhv.h" +* void fhv_ftran(FHV *fhv, double x[]); +* +* DESCRIPTION +* +* The routine fhv_ftran performs forward transformation, i.e. solves +* the system B*x = b, where B is the basis matrix, x is the vector of +* unknowns to be computed, b is the vector of right-hand sides. +* +* On entry elements of the vector b should be stored in dense format +* in locations x[1], ..., x[m], where m is the number of rows. On exit +* the routine stores elements of the vector x in the same locations. */ + +void fhv_ftran(FHV *fhv, double x[]) +{ int *pp_row = fhv->luf->pp_row; + int *pp_col = fhv->luf->pp_col; + int *p0_row = fhv->p0_row; + int *p0_col = fhv->p0_col; + if (!fhv->valid) + xfault("fhv_ftran: the factorization is not valid\n"); + /* B = F*H*V, therefore inv(B) = inv(V)*inv(H)*inv(F) */ + fhv->luf->pp_row = p0_row; + fhv->luf->pp_col = p0_col; + luf_f_solve(fhv->luf, 0, x); + fhv->luf->pp_row = pp_row; + fhv->luf->pp_col = pp_col; + fhv_h_solve(fhv, 0, x); + luf_v_solve(fhv->luf, 0, x); + return; +} + +/*********************************************************************** +* NAME +* +* fhv_btran - perform backward transformation (solve system B'*x = b) +* +* SYNOPSIS +* +* #include "glpfhv.h" +* void fhv_btran(FHV *fhv, double x[]); +* +* DESCRIPTION +* +* The routine fhv_btran performs backward transformation, i.e. solves +* the system B'*x = b, where B' is a matrix transposed to the basis +* matrix B, x is the vector of unknowns to be computed, b is the vector +* of right-hand sides. +* +* On entry elements of the vector b should be stored in dense format +* in locations x[1], ..., x[m], where m is the number of rows. On exit +* the routine stores elements of the vector x in the same locations. */ + +void fhv_btran(FHV *fhv, double x[]) +{ int *pp_row = fhv->luf->pp_row; + int *pp_col = fhv->luf->pp_col; + int *p0_row = fhv->p0_row; + int *p0_col = fhv->p0_col; + if (!fhv->valid) + xfault("fhv_btran: the factorization is not valid\n"); + /* B = F*H*V, therefore inv(B') = inv(F')*inv(H')*inv(V') */ + luf_v_solve(fhv->luf, 1, x); + fhv_h_solve(fhv, 1, x); + fhv->luf->pp_row = p0_row; + fhv->luf->pp_col = p0_col; + luf_f_solve(fhv->luf, 1, x); + fhv->luf->pp_row = pp_row; + fhv->luf->pp_col = pp_col; + return; +} + +/*********************************************************************** +* NAME +* +* fhv_update_it - update LP basis factorization +* +* SYNOPSIS +* +* #include "glpfhv.h" +* int fhv_update_it(FHV *fhv, int j, int len, const int ind[], +* const double val[]); +* +* DESCRIPTION +* +* The routine fhv_update_it updates the factorization of the basis +* matrix B after replacing its j-th column by a new vector. +* +* The parameter j specifies the number of column of B, which has been +* replaced, 1 <= j <= m, where m is the order of B. +* +* Row indices and numerical values of non-zero elements of the new +* column of B should be placed in locations ind[1], ..., ind[len] and +* val[1], ..., val[len], resp., where len is the number of non-zeros +* in the column. Neither zero nor duplicate elements are allowed. +* +* RETURNS +* +* 0 The factorization has been successfully updated. +* +* FHV_ESING +* The adjacent basis matrix is structurally singular, since after +* changing j-th column of matrix V by the new column (see algorithm +* below) the case k1 > k2 occured. +* +* FHV_ECHECK +* The factorization is inaccurate, since after transforming k2-th +* row of matrix U = P*V*Q, its diagonal element u[k2,k2] is zero or +* close to zero, +* +* FHV_ELIMIT +* Maximal number of H factors has been reached. +* +* FHV_EROOM +* Overflow of the sparse vector area. +* +* In case of non-zero return code the factorization becomes invalid. +* It should not be used until it has been recomputed with the routine +* fhv_factorize. +* +* ALGORITHM +* +* The routine fhv_update_it is based on the transformation proposed by +* Forrest and Tomlin. +* +* Let j-th column of the basis matrix B have been replaced by new +* column B[j]. In order to keep the equality B = F*H*V j-th column of +* matrix V should be replaced by the column inv(F*H)*B[j]. +* +* From the standpoint of matrix U = P*V*Q, replacement of j-th column +* of matrix V is equivalent to replacement of k1-th column of matrix U, +* where k1 is determined by permutation matrix Q. Thus, matrix U loses +* its upper triangular form and becomes the following: +* +* 1 k1 k2 m +* 1 x x * x x x x x x x +* . x * x x x x x x x +* k1 . . * x x x x x x x +* . . * x x x x x x x +* . . * . x x x x x x +* . . * . . x x x x x +* . . * . . . x x x x +* k2 . . * . . . . x x x +* . . . . . . . . x x +* m . . . . . . . . . x +* +* where row index k2 corresponds to the lowest non-zero element of +* k1-th column. +* +* The routine moves rows and columns k1+1, k1+2, ..., k2 of matrix U +* by one position to the left and upwards and moves k1-th row and k1-th +* column to position k2. As the result of such symmetric permutations +* matrix U becomes the following: +* +* 1 k1 k2 m +* 1 x x x x x x x * x x +* . x x x x x x * x x +* k1 . . x x x x x * x x +* . . . x x x x * x x +* . . . . x x x * x x +* . . . . . x x * x x +* . . . . . . x * x x +* k2 . . x x x x x * x x +* . . . . . . . . x x +* m . . . . . . . . . x +* +* Then the routine performs gaussian elimination to eliminate elements +* u[k2,k1], u[k2,k1+1], ..., u[k2,k2-1] using diagonal elements +* u[k1,k1], u[k1+1,k1+1], ..., u[k2-1,k2-1] as pivots in the same way +* as described in comments to the routine luf_factorize (see the module +* GLPLUF). Note that actually all operations are performed on matrix V, +* not on matrix U. During the elimination process the routine permutes +* neither rows nor columns, so only k2-th row of matrix U is changed. +* +* To keep the main equality B = F*H*V, each time when the routine +* applies elementary gaussian transformation to the transformed row of +* matrix V (which corresponds to k2-th row of matrix U), it also adds +* a new element (gaussian multiplier) to the current row-like factor +* of matrix H, which corresponds to the transformed row of matrix V. */ + +int fhv_update_it(FHV *fhv, int j, int len, const int ind[], + const double val[]) +{ int m = fhv->m; + LUF *luf = fhv->luf; + int *vr_ptr = luf->vr_ptr; + int *vr_len = luf->vr_len; + int *vr_cap = luf->vr_cap; + double *vr_piv = luf->vr_piv; + int *vc_ptr = luf->vc_ptr; + int *vc_len = luf->vc_len; + int *vc_cap = luf->vc_cap; + int *pp_row = luf->pp_row; + int *pp_col = luf->pp_col; + int *qq_row = luf->qq_row; + int *qq_col = luf->qq_col; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + double *work = luf->work; + double eps_tol = luf->eps_tol; + int *hh_ind = fhv->hh_ind; + int *hh_ptr = fhv->hh_ptr; + int *hh_len = fhv->hh_len; + int *p0_row = fhv->p0_row; + int *p0_col = fhv->p0_col; + int *cc_ind = fhv->cc_ind; + double *cc_val = fhv->cc_val; + double upd_tol = fhv->upd_tol; + int i, i_beg, i_end, i_ptr, j_beg, j_end, j_ptr, k, k1, k2, p, q, + p_beg, p_end, p_ptr, ptr, ret; + double f, temp; + if (!fhv->valid) + xfault("fhv_update_it: the factorization is not valid\n"); + if (!(1 <= j && j <= m)) + xfault("fhv_update_it: j = %d; column number out of range\n", + j); + /* check if the new factor of matrix H can be created */ + if (fhv->hh_nfs == fhv->hh_max) + { /* maximal number of updates has been reached */ + fhv->valid = 0; + ret = FHV_ELIMIT; + goto done; + } + /* convert new j-th column of B to dense format */ + for (i = 1; i <= m; i++) + cc_val[i] = 0.0; + for (k = 1; k <= len; k++) + { i = ind[k]; + if (!(1 <= i && i <= m)) + xfault("fhv_update_it: ind[%d] = %d; row number out of rang" + "e\n", k, i); + if (cc_val[i] != 0.0) + xfault("fhv_update_it: ind[%d] = %d; duplicate row index no" + "t allowed\n", k, i); + if (val[k] == 0.0) + xfault("fhv_update_it: val[%d] = %g; zero element not allow" + "ed\n", k, val[k]); + cc_val[i] = val[k]; + } + /* new j-th column of V := inv(F * H) * (new B[j]) */ + fhv->luf->pp_row = p0_row; + fhv->luf->pp_col = p0_col; + luf_f_solve(fhv->luf, 0, cc_val); + fhv->luf->pp_row = pp_row; + fhv->luf->pp_col = pp_col; + fhv_h_solve(fhv, 0, cc_val); + /* convert new j-th column of V to sparse format */ + len = 0; + for (i = 1; i <= m; i++) + { temp = cc_val[i]; + if (temp == 0.0 || fabs(temp) < eps_tol) continue; + len++, cc_ind[len] = i, cc_val[len] = temp; + } + /* clear old content of j-th column of matrix V */ + j_beg = vc_ptr[j]; + j_end = j_beg + vc_len[j] - 1; + for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) + { /* get row index of v[i,j] */ + i = sv_ind[j_ptr]; + /* find v[i,j] in the i-th row */ + i_beg = vr_ptr[i]; + i_end = i_beg + vr_len[i] - 1; + for (i_ptr = i_beg; sv_ind[i_ptr] != j; i_ptr++) /* nop */; + xassert(i_ptr <= i_end); + /* remove v[i,j] from the i-th row */ + sv_ind[i_ptr] = sv_ind[i_end]; + sv_val[i_ptr] = sv_val[i_end]; + vr_len[i]--; + } + /* now j-th column of matrix V is empty */ + luf->nnz_v -= vc_len[j]; + vc_len[j] = 0; + /* add new elements of j-th column of matrix V to corresponding + row lists; determine indices k1 and k2 */ + k1 = qq_row[j], k2 = 0; + for (ptr = 1; ptr <= len; ptr++) + { /* get row index of v[i,j] */ + i = cc_ind[ptr]; + /* at least one unused location is needed in i-th row */ + if (vr_len[i] + 1 > vr_cap[i]) + { if (luf_enlarge_row(luf, i, vr_len[i] + 10)) + { /* overflow of the sparse vector area */ + fhv->valid = 0; + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + ret = FHV_EROOM; + goto done; + } + } + /* add v[i,j] to i-th row */ + i_ptr = vr_ptr[i] + vr_len[i]; + sv_ind[i_ptr] = j; + sv_val[i_ptr] = cc_val[ptr]; + vr_len[i]++; + /* adjust index k2 */ + if (k2 < pp_col[i]) k2 = pp_col[i]; + } + /* capacity of j-th column (which is currently empty) should be + not less than len locations */ + if (vc_cap[j] < len) + { if (luf_enlarge_col(luf, j, len)) + { /* overflow of the sparse vector area */ + fhv->valid = 0; + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + ret = FHV_EROOM; + goto done; + } + } + /* add new elements of matrix V to j-th column list */ + j_ptr = vc_ptr[j]; + memmove(&sv_ind[j_ptr], &cc_ind[1], len * sizeof(int)); + memmove(&sv_val[j_ptr], &cc_val[1], len * sizeof(double)); + vc_len[j] = len; + luf->nnz_v += len; + /* if k1 > k2, diagonal element u[k2,k2] of matrix U is zero and + therefore the adjacent basis matrix is structurally singular */ + if (k1 > k2) + { fhv->valid = 0; + ret = FHV_ESING; + goto done; + } + /* perform implicit symmetric permutations of rows and columns of + matrix U */ + i = pp_row[k1], j = qq_col[k1]; + for (k = k1; k < k2; k++) + { pp_row[k] = pp_row[k+1], pp_col[pp_row[k]] = k; + qq_col[k] = qq_col[k+1], qq_row[qq_col[k]] = k; + } + pp_row[k2] = i, pp_col[i] = k2; + qq_col[k2] = j, qq_row[j] = k2; + /* now i-th row of the matrix V is k2-th row of matrix U; since + no pivoting is used, only this row will be transformed */ + /* copy elements of i-th row of matrix V to the working array and + remove these elements from matrix V */ + for (j = 1; j <= m; j++) work[j] = 0.0; + i_beg = vr_ptr[i]; + i_end = i_beg + vr_len[i] - 1; + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + { /* get column index of v[i,j] */ + j = sv_ind[i_ptr]; + /* store v[i,j] to the working array */ + work[j] = sv_val[i_ptr]; + /* find v[i,j] in the j-th column */ + j_beg = vc_ptr[j]; + j_end = j_beg + vc_len[j] - 1; + for (j_ptr = j_beg; sv_ind[j_ptr] != i; j_ptr++) /* nop */; + xassert(j_ptr <= j_end); + /* remove v[i,j] from the j-th column */ + sv_ind[j_ptr] = sv_ind[j_end]; + sv_val[j_ptr] = sv_val[j_end]; + vc_len[j]--; + } + /* now i-th row of matrix V is empty */ + luf->nnz_v -= vr_len[i]; + vr_len[i] = 0; + /* create the next row-like factor of the matrix H; this factor + corresponds to i-th (transformed) row */ + fhv->hh_nfs++; + hh_ind[fhv->hh_nfs] = i; + /* hh_ptr[] will be set later */ + hh_len[fhv->hh_nfs] = 0; + /* up to (k2 - k1) free locations are needed to add new elements + to the non-trivial row of the row-like factor */ + if (luf->sv_end - luf->sv_beg < k2 - k1) + { luf_defrag_sva(luf); + if (luf->sv_end - luf->sv_beg < k2 - k1) + { /* overflow of the sparse vector area */ + fhv->valid = luf->valid = 0; + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + ret = FHV_EROOM; + goto done; + } + } + /* eliminate subdiagonal elements of matrix U */ + for (k = k1; k < k2; k++) + { /* v[p,q] = u[k,k] */ + p = pp_row[k], q = qq_col[k]; + /* this is the crucial point, where even tiny non-zeros should + not be dropped */ + if (work[q] == 0.0) continue; + /* compute gaussian multiplier f = v[i,q] / v[p,q] */ + f = work[q] / vr_piv[p]; + /* perform gaussian transformation: + (i-th row) := (i-th row) - f * (p-th row) + in order to eliminate v[i,q] = u[k2,k] */ + p_beg = vr_ptr[p]; + p_end = p_beg + vr_len[p] - 1; + for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++) + work[sv_ind[p_ptr]] -= f * sv_val[p_ptr]; + /* store new element (gaussian multiplier that corresponds to + p-th row) in the current row-like factor */ + luf->sv_end--; + sv_ind[luf->sv_end] = p; + sv_val[luf->sv_end] = f; + hh_len[fhv->hh_nfs]++; + } + /* set pointer to the current row-like factor of the matrix H + (if no elements were added to this factor, it is unity matrix + and therefore can be discarded) */ + if (hh_len[fhv->hh_nfs] == 0) + fhv->hh_nfs--; + else + { hh_ptr[fhv->hh_nfs] = luf->sv_end; + fhv->nnz_h += hh_len[fhv->hh_nfs]; + } + /* store new pivot which corresponds to u[k2,k2] */ + vr_piv[i] = work[qq_col[k2]]; + /* new elements of i-th row of matrix V (which are non-diagonal + elements u[k2,k2+1], ..., u[k2,m] of matrix U = P*V*Q) now are + contained in the working array; add them to matrix V */ + len = 0; + for (k = k2+1; k <= m; k++) + { /* get column index and value of v[i,j] = u[k2,k] */ + j = qq_col[k]; + temp = work[j]; + /* if v[i,j] is close to zero, skip it */ + if (fabs(temp) < eps_tol) continue; + /* at least one unused location is needed in j-th column */ + if (vc_len[j] + 1 > vc_cap[j]) + { if (luf_enlarge_col(luf, j, vc_len[j] + 10)) + { /* overflow of the sparse vector area */ + fhv->valid = 0; + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + ret = FHV_EROOM; + goto done; + } + } + /* add v[i,j] to j-th column */ + j_ptr = vc_ptr[j] + vc_len[j]; + sv_ind[j_ptr] = i; + sv_val[j_ptr] = temp; + vc_len[j]++; + /* also store v[i,j] to the auxiliary array */ + len++, cc_ind[len] = j, cc_val[len] = temp; + } + /* capacity of i-th row (which is currently empty) should be not + less than len locations */ + if (vr_cap[i] < len) + { if (luf_enlarge_row(luf, i, len)) + { /* overflow of the sparse vector area */ + fhv->valid = 0; + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + ret = FHV_EROOM; + goto done; + } + } + /* add new elements to i-th row list */ + i_ptr = vr_ptr[i]; + memmove(&sv_ind[i_ptr], &cc_ind[1], len * sizeof(int)); + memmove(&sv_val[i_ptr], &cc_val[1], len * sizeof(double)); + vr_len[i] = len; + luf->nnz_v += len; + /* updating is finished; check that diagonal element u[k2,k2] is + not very small in absolute value among other elements in k2-th + row and k2-th column of matrix U = P*V*Q */ + /* temp = max(|u[k2,*]|, |u[*,k2]|) */ + temp = 0.0; + /* walk through k2-th row of U which is i-th row of V */ + i = pp_row[k2]; + i_beg = vr_ptr[i]; + i_end = i_beg + vr_len[i] - 1; + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + if (temp < fabs(sv_val[i_ptr])) temp = fabs(sv_val[i_ptr]); + /* walk through k2-th column of U which is j-th column of V */ + j = qq_col[k2]; + j_beg = vc_ptr[j]; + j_end = j_beg + vc_len[j] - 1; + for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) + if (temp < fabs(sv_val[j_ptr])) temp = fabs(sv_val[j_ptr]); + /* check that u[k2,k2] is not very small */ + if (fabs(vr_piv[i]) < upd_tol * temp) + { /* the factorization seems to be inaccurate and therefore must + be recomputed */ + fhv->valid = 0; + ret = FHV_ECHECK; + goto done; + } + /* the factorization has been successfully updated */ + ret = 0; +done: /* return to the calling program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* fhv_delete_it - delete LP basis factorization +* +* SYNOPSIS +* +* #include "glpfhv.h" +* void fhv_delete_it(FHV *fhv); +* +* DESCRIPTION +* +* The routine fhv_delete_it deletes LP basis factorization specified +* by the parameter fhv and frees all memory allocated to this program +* object. */ + +void fhv_delete_it(FHV *fhv) +{ luf_delete_it(fhv->luf); + if (fhv->hh_ind != NULL) xfree(fhv->hh_ind); + if (fhv->hh_ptr != NULL) xfree(fhv->hh_ptr); + if (fhv->hh_len != NULL) xfree(fhv->hh_len); + if (fhv->p0_row != NULL) xfree(fhv->p0_row); + if (fhv->p0_col != NULL) xfree(fhv->p0_col); + if (fhv->cc_ind != NULL) xfree(fhv->cc_ind); + if (fhv->cc_val != NULL) xfree(fhv->cc_val); + xfree(fhv); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpfhv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpfhv.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,170 @@ +/* glpfhv.h (LP basis factorization, FHV eta file version) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPFHV_H +#define GLPFHV_H + +#include "glpluf.h" + +/*********************************************************************** +* The structure FHV defines the factorization of the basis mxm-matrix +* B, where m is the number of rows in corresponding problem instance. +* +* This factorization is the following sextet: +* +* [B] = (F, H, V, P0, P, Q), (1) +* +* where F, H, and V are such matrices that +* +* B = F * H * V, (2) +* +* and P0, P, and Q are such permutation matrices that the matrix +* +* L = P0 * F * inv(P0) (3) +* +* is lower triangular with unity diagonal, and the matrix +* +* U = P * V * Q (4) +* +* is upper triangular. All the matrices have the same order m, which +* is the order of the basis matrix B. +* +* The matrices F, V, P, and Q are stored in the structure LUF (see the +* module GLPLUF), which is a member of the structure FHV. +* +* The matrix H is stored in the form of eta file using row-like format +* as follows: +* +* H = H[1] * H[2] * ... * H[nfs], (5) +* +* where H[k], k = 1, 2, ..., nfs, is a row-like factor, which differs +* from the unity matrix only by one row, nfs is current number of row- +* like factors. After the factorization has been built for some given +* basis matrix B the matrix H has no factors and thus it is the unity +* matrix. Then each time when the factorization is recomputed for an +* adjacent basis matrix, the next factor H[k], k = 1, 2, ... is built +* and added to the end of the eta file H. +* +* Being sparse vectors non-trivial rows of the factors H[k] are stored +* in the right part of the sparse vector area (SVA) in the same manner +* as rows and columns of the matrix F. +* +* For more details see the program documentation. */ + +typedef struct FHV FHV; + +struct FHV +{ /* LP basis factorization */ + int m_max; + /* maximal value of m (increased automatically, if necessary) */ + int m; + /* the order of matrices B, F, H, V, P0, P, Q */ + int valid; + /* the factorization is valid only if this flag is set */ + LUF *luf; + /* LU-factorization (contains the matrices F, V, P, Q) */ + /*--------------------------------------------------------------*/ + /* matrix H in the form of eta file */ + int hh_max; + /* maximal number of row-like factors (which limits the number of + updates of the factorization) */ + int hh_nfs; + /* current number of row-like factors (0 <= hh_nfs <= hh_max) */ + int *hh_ind; /* int hh_ind[1+hh_max]; */ + /* hh_ind[k], k = 1, ..., nfs, is the number of a non-trivial row + of factor H[k] */ + int *hh_ptr; /* int hh_ptr[1+hh_max]; */ + /* hh_ptr[k], k = 1, ..., nfs, is a pointer to the first element + of the non-trivial row of factor H[k] in the SVA */ + int *hh_len; /* int hh_len[1+hh_max]; */ + /* hh_len[k], k = 1, ..., nfs, is the number of non-zero elements + in the non-trivial row of factor H[k] */ + /*--------------------------------------------------------------*/ + /* matrix P0 */ + int *p0_row; /* int p0_row[1+m_max]; */ + /* p0_row[i] = j means that p0[i,j] = 1 */ + int *p0_col; /* int p0_col[1+m_max]; */ + /* p0_col[j] = i means that p0[i,j] = 1 */ + /* if i-th row or column of the matrix F corresponds to i'-th row + or column of the matrix L = P0*F*inv(P0), then p0_row[i'] = i + and p0_col[i] = i' */ + /*--------------------------------------------------------------*/ + /* working arrays */ + int *cc_ind; /* int cc_ind[1+m_max]; */ + /* integer working array */ + double *cc_val; /* double cc_val[1+m_max]; */ + /* floating-point working array */ + /*--------------------------------------------------------------*/ + /* control parameters */ + double upd_tol; + /* update tolerance; if after updating the factorization absolute + value of some diagonal element u[k,k] of matrix U = P*V*Q is + less than upd_tol * max(|u[k,*]|, |u[*,k]|), the factorization + is considered as inaccurate */ + /*--------------------------------------------------------------*/ + /* some statistics */ + int nnz_h; + /* current number of non-zeros in all factors of matrix H */ +}; + +/* return codes: */ +#define FHV_ESING 1 /* singular matrix */ +#define FHV_ECOND 2 /* ill-conditioned matrix */ +#define FHV_ECHECK 3 /* insufficient accuracy */ +#define FHV_ELIMIT 4 /* update limit reached */ +#define FHV_EROOM 5 /* SVA overflow */ + +#define fhv_create_it _glp_fhv_create_it +FHV *fhv_create_it(void); +/* create LP basis factorization */ + +#define fhv_factorize _glp_fhv_factorize +int fhv_factorize(FHV *fhv, int m, int (*col)(void *info, int j, + int ind[], double val[]), void *info); +/* compute LP basis factorization */ + +#define fhv_h_solve _glp_fhv_h_solve +void fhv_h_solve(FHV *fhv, int tr, double x[]); +/* solve system H*x = b or H'*x = b */ + +#define fhv_ftran _glp_fhv_ftran +void fhv_ftran(FHV *fhv, double x[]); +/* perform forward transformation (solve system B*x = b) */ + +#define fhv_btran _glp_fhv_btran +void fhv_btran(FHV *fhv, double x[]); +/* perform backward transformation (solve system B'*x = b) */ + +#define fhv_update_it _glp_fhv_update_it +int fhv_update_it(FHV *fhv, int j, int len, const int ind[], + const double val[]); +/* update LP basis factorization */ + +#define fhv_delete_it _glp_fhv_delete_it +void fhv_delete_it(FHV *fhv); +/* delete LP basis factorization */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpgmp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpgmp.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1108 @@ +/* glpgmp.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_STDIO +#include "glpdmp.h" +#include "glpgmp.h" +#define xfault xerror + +#ifdef HAVE_GMP /* use GNU MP bignum library */ + +int gmp_pool_count(void) { return 0; } + +void gmp_free_mem(void) { return; } + +#else /* use GLPK bignum module */ + +static DMP *gmp_pool = NULL; +static int gmp_size = 0; +static unsigned short *gmp_work = NULL; + +void *gmp_get_atom(int size) +{ if (gmp_pool == NULL) + gmp_pool = dmp_create_pool(); + return dmp_get_atom(gmp_pool, size); +} + +void gmp_free_atom(void *ptr, int size) +{ xassert(gmp_pool != NULL); + dmp_free_atom(gmp_pool, ptr, size); + return; +} + +int gmp_pool_count(void) +{ if (gmp_pool == NULL) + return 0; + else + return dmp_in_use(gmp_pool).lo; +} + +unsigned short *gmp_get_work(int size) +{ xassert(size > 0); + if (gmp_size < size) + { if (gmp_size == 0) + { xassert(gmp_work == NULL); + gmp_size = 100; + } + else + { xassert(gmp_work != NULL); + xfree(gmp_work); + } + while (gmp_size < size) gmp_size += gmp_size; + gmp_work = xcalloc(gmp_size, sizeof(unsigned short)); + } + return gmp_work; +} + +void gmp_free_mem(void) +{ if (gmp_pool != NULL) dmp_delete_pool(gmp_pool); + if (gmp_work != NULL) xfree(gmp_work); + gmp_pool = NULL; + gmp_size = 0; + gmp_work = NULL; + return; +} + +/*====================================================================*/ + +mpz_t _mpz_init(void) +{ /* initialize x, and set its value to 0 */ + mpz_t x; + x = gmp_get_atom(sizeof(struct mpz)); + x->val = 0; + x->ptr = NULL; + return x; +} + +void mpz_clear(mpz_t x) +{ /* free the space occupied by x */ + mpz_set_si(x, 0); + xassert(x->ptr == NULL); + /* free the number descriptor */ + gmp_free_atom(x, sizeof(struct mpz)); + return; +} + +void mpz_set(mpz_t z, mpz_t x) +{ /* set the value of z from x */ + struct mpz_seg *e, *ee, *es; + if (z != x) + { mpz_set_si(z, 0); + z->val = x->val; + xassert(z->ptr == NULL); + for (e = x->ptr, es = NULL; e != NULL; e = e->next) + { ee = gmp_get_atom(sizeof(struct mpz_seg)); + memcpy(ee->d, e->d, 12); + ee->next = NULL; + if (z->ptr == NULL) + z->ptr = ee; + else + es->next = ee; + es = ee; + } + } + return; +} + +void mpz_set_si(mpz_t x, int val) +{ /* set the value of x to val */ + struct mpz_seg *e; + /* free existing segments, if any */ + while (x->ptr != NULL) + { e = x->ptr; + x->ptr = e->next; + gmp_free_atom(e, sizeof(struct mpz_seg)); + } + /* assign new value */ + if (val == 0x80000000) + { /* long format is needed */ + x->val = -1; + x->ptr = e = gmp_get_atom(sizeof(struct mpz_seg)); + memset(e->d, 0, 12); + e->d[1] = 0x8000; + e->next = NULL; + } + else + { /* short format is enough */ + x->val = val; + } + return; +} + +double mpz_get_d(mpz_t x) +{ /* convert x to a double, truncating if necessary */ + struct mpz_seg *e; + int j; + double val, deg; + if (x->ptr == NULL) + val = (double)x->val; + else + { xassert(x->val != 0); + val = 0.0; + deg = 1.0; + for (e = x->ptr; e != NULL; e = e->next) + { for (j = 0; j <= 5; j++) + { val += deg * (double)((int)e->d[j]); + deg *= 65536.0; + } + } + if (x->val < 0) val = - val; + } + return val; +} + +double mpz_get_d_2exp(int *exp, mpz_t x) +{ /* convert x to a double, truncating if necessary (i.e. rounding + towards zero), and returning the exponent separately; + the return value is in the range 0.5 <= |d| < 1 and the + exponent is stored to *exp; d*2^exp is the (truncated) x value; + if x is zero, the return is 0.0 and 0 is stored to *exp; + this is similar to the standard C frexp function */ + struct mpz_seg *e; + int j, n, n1; + double val; + if (x->ptr == NULL) + val = (double)x->val, n = 0; + else + { xassert(x->val != 0); + val = 0.0, n = 0; + for (e = x->ptr; e != NULL; e = e->next) + { for (j = 0; j <= 5; j++) + { val += (double)((int)e->d[j]); + val /= 65536.0, n += 16; + } + } + if (x->val < 0) val = - val; + } + val = frexp(val, &n1); + *exp = n + n1; + return val; +} + +void mpz_swap(mpz_t x, mpz_t y) +{ /* swap the values x and y efficiently */ + int val; + void *ptr; + val = x->val, ptr = x->ptr; + x->val = y->val, x->ptr = y->ptr; + y->val = val, y->ptr = ptr; + return; +} + +static void normalize(mpz_t x) +{ /* normalize integer x that includes removing non-significant + (leading) zeros and converting to short format, if possible */ + struct mpz_seg *es, *e; + /* if the integer is in short format, it remains unchanged */ + if (x->ptr == NULL) + { xassert(x->val != 0x80000000); + goto done; + } + xassert(x->val == +1 || x->val == -1); + /* find the last (most significant) non-zero segment */ + es = NULL; + for (e = x->ptr; e != NULL; e = e->next) + { if (e->d[0] || e->d[1] || e->d[2] || + e->d[3] || e->d[4] || e->d[5]) es = e; + } + /* if all segments contain zeros, the integer is zero */ + if (es == NULL) + { mpz_set_si(x, 0); + goto done; + } + /* remove non-significant (leading) zero segments */ + while (es->next != NULL) + { e = es->next; + es->next = e->next; + gmp_free_atom(e, sizeof(struct mpz_seg)); + } + /* convert the integer to short format, if possible */ + e = x->ptr; + if (e->next == NULL && e->d[1] <= 0x7FFF && + !e->d[2] && !e->d[3] && !e->d[4] && !e->d[5]) + { int val; + val = (int)e->d[0] + ((int)e->d[1] << 16); + if (x->val < 0) val = - val; + mpz_set_si(x, val); + } +done: return; +} + +void mpz_add(mpz_t z, mpz_t x, mpz_t y) +{ /* set z to x + y */ + static struct mpz_seg zero = { { 0, 0, 0, 0, 0, 0 }, NULL }; + struct mpz_seg dumx, dumy, *ex, *ey, *ez, *es, *ee; + int k, sx, sy, sz; + unsigned int t; + /* if [x] = 0 then [z] = [y] */ + if (x->val == 0) + { xassert(x->ptr == NULL); + mpz_set(z, y); + goto done; + } + /* if [y] = 0 then [z] = [x] */ + if (y->val == 0) + { xassert(y->ptr == NULL); + mpz_set(z, x); + goto done; + } + /* special case when both [x] and [y] are in short format */ + if (x->ptr == NULL && y->ptr == NULL) + { int xval = x->val, yval = y->val, zval = x->val + y->val; + xassert(xval != 0x80000000 && yval != 0x80000000); + if (!(xval > 0 && yval > 0 && zval <= 0 || + xval < 0 && yval < 0 && zval >= 0)) + { mpz_set_si(z, zval); + goto done; + } + } + /* convert [x] to long format, if necessary */ + if (x->ptr == NULL) + { xassert(x->val != 0x80000000); + if (x->val >= 0) + { sx = +1; + t = (unsigned int)(+ x->val); + } + else + { sx = -1; + t = (unsigned int)(- x->val); + } + ex = &dumx; + ex->d[0] = (unsigned short)t; + ex->d[1] = (unsigned short)(t >> 16); + ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0; + ex->next = NULL; + } + else + { sx = x->val; + xassert(sx == +1 || sx == -1); + ex = x->ptr; + } + /* convert [y] to long format, if necessary */ + if (y->ptr == NULL) + { xassert(y->val != 0x80000000); + if (y->val >= 0) + { sy = +1; + t = (unsigned int)(+ y->val); + } + else + { sy = -1; + t = (unsigned int)(- y->val); + } + ey = &dumy; + ey->d[0] = (unsigned short)t; + ey->d[1] = (unsigned short)(t >> 16); + ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0; + ey->next = NULL; + } + else + { sy = y->val; + xassert(sy == +1 || sy == -1); + ey = y->ptr; + } + /* main fragment */ + sz = sx; + ez = es = NULL; + if (sx > 0 && sy > 0 || sx < 0 && sy < 0) + { /* [x] and [y] have identical signs -- addition */ + t = 0; + for (; ex || ey; ex = ex->next, ey = ey->next) + { if (ex == NULL) ex = &zero; + if (ey == NULL) ey = &zero; + ee = gmp_get_atom(sizeof(struct mpz_seg)); + for (k = 0; k <= 5; k++) + { t += (unsigned int)ex->d[k]; + t += (unsigned int)ey->d[k]; + ee->d[k] = (unsigned short)t; + t >>= 16; + } + ee->next = NULL; + if (ez == NULL) + ez = ee; + else + es->next = ee; + es = ee; + } + if (t) + { /* overflow -- one extra digit is needed */ + ee = gmp_get_atom(sizeof(struct mpz_seg)); + ee->d[0] = 1; + ee->d[1] = ee->d[2] = ee->d[3] = ee->d[4] = ee->d[5] = 0; + ee->next = NULL; + xassert(es != NULL); + es->next = ee; + } + } + else + { /* [x] and [y] have different signs -- subtraction */ + t = 1; + for (; ex || ey; ex = ex->next, ey = ey->next) + { if (ex == NULL) ex = &zero; + if (ey == NULL) ey = &zero; + ee = gmp_get_atom(sizeof(struct mpz_seg)); + for (k = 0; k <= 5; k++) + { t += (unsigned int)ex->d[k]; + t += (0xFFFF - (unsigned int)ey->d[k]); + ee->d[k] = (unsigned short)t; + t >>= 16; + } + ee->next = NULL; + if (ez == NULL) + ez = ee; + else + es->next = ee; + es = ee; + } + if (!t) + { /* |[x]| < |[y]| -- result in complement coding */ + sz = - sz; + t = 1; + for (ee = ez; ee != NULL; ee = ee->next) + for (k = 0; k <= 5; k++) + { t += (0xFFFF - (unsigned int)ee->d[k]); + ee->d[k] = (unsigned short)t; + t >>= 16; + } + } + } + /* contruct and normalize result */ + mpz_set_si(z, 0); + z->val = sz; + z->ptr = ez; + normalize(z); +done: return; +} + +void mpz_sub(mpz_t z, mpz_t x, mpz_t y) +{ /* set z to x - y */ + if (x == y) + mpz_set_si(z, 0); + else + { y->val = - y->val; + mpz_add(z, x, y); + if (y != z) y->val = - y->val; + } + return; +} + +void mpz_mul(mpz_t z, mpz_t x, mpz_t y) +{ /* set z to x * y */ + struct mpz_seg dumx, dumy, *ex, *ey, *es, *e; + int sx, sy, k, nx, ny, n; + unsigned int t; + unsigned short *work, *wx, *wy; + /* if [x] = 0 then [z] = 0 */ + if (x->val == 0) + { xassert(x->ptr == NULL); + mpz_set_si(z, 0); + goto done; + } + /* if [y] = 0 then [z] = 0 */ + if (y->val == 0) + { xassert(y->ptr == NULL); + mpz_set_si(z, 0); + goto done; + } + /* special case when both [x] and [y] are in short format */ + if (x->ptr == NULL && y->ptr == NULL) + { int xval = x->val, yval = y->val, sz = +1; + xassert(xval != 0x80000000 && yval != 0x80000000); + if (xval < 0) xval = - xval, sz = - sz; + if (yval < 0) yval = - yval, sz = - sz; + if (xval <= 0x7FFFFFFF / yval) + { mpz_set_si(z, sz * (xval * yval)); + goto done; + } + } + /* convert [x] to long format, if necessary */ + if (x->ptr == NULL) + { xassert(x->val != 0x80000000); + if (x->val >= 0) + { sx = +1; + t = (unsigned int)(+ x->val); + } + else + { sx = -1; + t = (unsigned int)(- x->val); + } + ex = &dumx; + ex->d[0] = (unsigned short)t; + ex->d[1] = (unsigned short)(t >> 16); + ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0; + ex->next = NULL; + } + else + { sx = x->val; + xassert(sx == +1 || sx == -1); + ex = x->ptr; + } + /* convert [y] to long format, if necessary */ + if (y->ptr == NULL) + { xassert(y->val != 0x80000000); + if (y->val >= 0) + { sy = +1; + t = (unsigned int)(+ y->val); + } + else + { sy = -1; + t = (unsigned int)(- y->val); + } + ey = &dumy; + ey->d[0] = (unsigned short)t; + ey->d[1] = (unsigned short)(t >> 16); + ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0; + ey->next = NULL; + } + else + { sy = y->val; + xassert(sy == +1 || sy == -1); + ey = y->ptr; + } + /* determine the number of digits of [x] */ + nx = n = 0; + for (e = ex; e != NULL; e = e->next) + for (k = 0; k <= 5; k++) + { n++; + if (e->d[k]) nx = n; + } + xassert(nx > 0); + /* determine the number of digits of [y] */ + ny = n = 0; + for (e = ey; e != NULL; e = e->next) + for (k = 0; k <= 5; k++) + { n++; + if (e->d[k]) ny = n; + } + xassert(ny > 0); + /* we need working array containing at least nx+ny+ny places */ + work = gmp_get_work(nx+ny+ny); + /* load digits of [x] */ + wx = &work[0]; + for (n = 0; n < nx; n++) wx[ny+n] = 0; + for (n = 0, e = ex; e != NULL; e = e->next) + for (k = 0; k <= 5; k++, n++) + if (e->d[k]) wx[ny+n] = e->d[k]; + /* load digits of [y] */ + wy = &work[nx+ny]; + for (n = 0; n < ny; n++) wy[n] = 0; + for (n = 0, e = ey; e != NULL; e = e->next) + for (k = 0; k <= 5; k++, n++) + if (e->d[k]) wy[n] = e->d[k]; + /* compute [x] * [y] */ + bigmul(nx, ny, wx, wy); + /* construct and normalize result */ + mpz_set_si(z, 0); + z->val = sx * sy; + es = NULL; + k = 6; + for (n = 0; n < nx+ny; n++) + { if (k > 5) + { e = gmp_get_atom(sizeof(struct mpz_seg)); + e->d[0] = e->d[1] = e->d[2] = 0; + e->d[3] = e->d[4] = e->d[5] = 0; + e->next = NULL; + if (z->ptr == NULL) + z->ptr = e; + else + es->next = e; + es = e; + k = 0; + } + es->d[k++] = wx[n]; + } + normalize(z); +done: return; +} + +void mpz_neg(mpz_t z, mpz_t x) +{ /* set z to 0 - x */ + mpz_set(z, x); + z->val = - z->val; + return; +} + +void mpz_abs(mpz_t z, mpz_t x) +{ /* set z to the absolute value of x */ + mpz_set(z, x); + if (z->val < 0) z->val = - z->val; + return; +} + +void mpz_div(mpz_t q, mpz_t r, mpz_t x, mpz_t y) +{ /* divide x by y, forming quotient q and/or remainder r + if q = NULL then quotient is not stored; if r = NULL then + remainder is not stored + the sign of quotient is determined as in algebra while the + sign of remainder is the same as the sign of dividend: + +26 : +7 = +3, remainder is +5 + -26 : +7 = -3, remainder is -5 + +26 : -7 = -3, remainder is +5 + -26 : -7 = +3, remainder is -5 */ + struct mpz_seg dumx, dumy, *ex, *ey, *es, *e; + int sx, sy, k, nx, ny, n; + unsigned int t; + unsigned short *work, *wx, *wy; + /* divide by zero is not allowed */ + if (y->val == 0) + { xassert(y->ptr == NULL); + xfault("mpz_div: divide by zero not allowed\n"); + } + /* if [x] = 0 then [q] = [r] = 0 */ + if (x->val == 0) + { xassert(x->ptr == NULL); + if (q != NULL) mpz_set_si(q, 0); + if (r != NULL) mpz_set_si(r, 0); + goto done; + } + /* special case when both [x] and [y] are in short format */ + if (x->ptr == NULL && y->ptr == NULL) + { int xval = x->val, yval = y->val; + xassert(xval != 0x80000000 && yval != 0x80000000); + if (q != NULL) mpz_set_si(q, xval / yval); + if (r != NULL) mpz_set_si(r, xval % yval); + goto done; + } + /* convert [x] to long format, if necessary */ + if (x->ptr == NULL) + { xassert(x->val != 0x80000000); + if (x->val >= 0) + { sx = +1; + t = (unsigned int)(+ x->val); + } + else + { sx = -1; + t = (unsigned int)(- x->val); + } + ex = &dumx; + ex->d[0] = (unsigned short)t; + ex->d[1] = (unsigned short)(t >> 16); + ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0; + ex->next = NULL; + } + else + { sx = x->val; + xassert(sx == +1 || sx == -1); + ex = x->ptr; + } + /* convert [y] to long format, if necessary */ + if (y->ptr == NULL) + { xassert(y->val != 0x80000000); + if (y->val >= 0) + { sy = +1; + t = (unsigned int)(+ y->val); + } + else + { sy = -1; + t = (unsigned int)(- y->val); + } + ey = &dumy; + ey->d[0] = (unsigned short)t; + ey->d[1] = (unsigned short)(t >> 16); + ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0; + ey->next = NULL; + } + else + { sy = y->val; + xassert(sy == +1 || sy == -1); + ey = y->ptr; + } + /* determine the number of digits of [x] */ + nx = n = 0; + for (e = ex; e != NULL; e = e->next) + for (k = 0; k <= 5; k++) + { n++; + if (e->d[k]) nx = n; + } + xassert(nx > 0); + /* determine the number of digits of [y] */ + ny = n = 0; + for (e = ey; e != NULL; e = e->next) + for (k = 0; k <= 5; k++) + { n++; + if (e->d[k]) ny = n; + } + xassert(ny > 0); + /* if nx < ny then [q] = 0 and [r] = [x] */ + if (nx < ny) + { if (r != NULL) mpz_set(r, x); + if (q != NULL) mpz_set_si(q, 0); + goto done; + } + /* we need working array containing at least nx+ny+1 places */ + work = gmp_get_work(nx+ny+1); + /* load digits of [x] */ + wx = &work[0]; + for (n = 0; n < nx; n++) wx[n] = 0; + for (n = 0, e = ex; e != NULL; e = e->next) + for (k = 0; k <= 5; k++, n++) + if (e->d[k]) wx[n] = e->d[k]; + /* load digits of [y] */ + wy = &work[nx+1]; + for (n = 0; n < ny; n++) wy[n] = 0; + for (n = 0, e = ey; e != NULL; e = e->next) + for (k = 0; k <= 5; k++, n++) + if (e->d[k]) wy[n] = e->d[k]; + /* compute quotient and remainder */ + xassert(wy[ny-1] != 0); + bigdiv(nx-ny, ny, wx, wy); + /* construct and normalize quotient */ + if (q != NULL) + { mpz_set_si(q, 0); + q->val = sx * sy; + es = NULL; + k = 6; + for (n = ny; n <= nx; n++) + { if (k > 5) + { e = gmp_get_atom(sizeof(struct mpz_seg)); + e->d[0] = e->d[1] = e->d[2] = 0; + e->d[3] = e->d[4] = e->d[5] = 0; + e->next = NULL; + if (q->ptr == NULL) + q->ptr = e; + else + es->next = e; + es = e; + k = 0; + } + es->d[k++] = wx[n]; + } + normalize(q); + } + /* construct and normalize remainder */ + if (r != NULL) + { mpz_set_si(r, 0); + r->val = sx; + es = NULL; + k = 6; + for (n = 0; n < ny; n++) + { if (k > 5) + { e = gmp_get_atom(sizeof(struct mpz_seg)); + e->d[0] = e->d[1] = e->d[2] = 0; + e->d[3] = e->d[4] = e->d[5] = 0; + e->next = NULL; + if (r->ptr == NULL) + r->ptr = e; + else + es->next = e; + es = e; + k = 0; + } + es->d[k++] = wx[n]; + } + normalize(r); + } +done: return; +} + +void mpz_gcd(mpz_t z, mpz_t x, mpz_t y) +{ /* set z to the greatest common divisor of x and y */ + /* in case of arbitrary integers GCD(x, y) = GCD(|x|, |y|), and, + in particular, GCD(0, 0) = 0 */ + mpz_t u, v, r; + mpz_init(u); + mpz_init(v); + mpz_init(r); + mpz_abs(u, x); + mpz_abs(v, y); + while (mpz_sgn(v)) + { mpz_div(NULL, r, u, v); + mpz_set(u, v); + mpz_set(v, r); + } + mpz_set(z, u); + mpz_clear(u); + mpz_clear(v); + mpz_clear(r); + return; +} + +int mpz_cmp(mpz_t x, mpz_t y) +{ /* compare x and y; return a positive value if x > y, zero if + x = y, or a nefative value if x < y */ + static struct mpz_seg zero = { { 0, 0, 0, 0, 0, 0 }, NULL }; + struct mpz_seg dumx, dumy, *ex, *ey; + int cc, sx, sy, k; + unsigned int t; + if (x == y) + { cc = 0; + goto done; + } + /* special case when both [x] and [y] are in short format */ + if (x->ptr == NULL && y->ptr == NULL) + { int xval = x->val, yval = y->val; + xassert(xval != 0x80000000 && yval != 0x80000000); + cc = (xval > yval ? +1 : xval < yval ? -1 : 0); + goto done; + } + /* special case when [x] and [y] have different signs */ + if (x->val > 0 && y->val <= 0 || x->val == 0 && y->val < 0) + { cc = +1; + goto done; + } + if (x->val < 0 && y->val >= 0 || x->val == 0 && y->val > 0) + { cc = -1; + goto done; + } + /* convert [x] to long format, if necessary */ + if (x->ptr == NULL) + { xassert(x->val != 0x80000000); + if (x->val >= 0) + { sx = +1; + t = (unsigned int)(+ x->val); + } + else + { sx = -1; + t = (unsigned int)(- x->val); + } + ex = &dumx; + ex->d[0] = (unsigned short)t; + ex->d[1] = (unsigned short)(t >> 16); + ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0; + ex->next = NULL; + } + else + { sx = x->val; + xassert(sx == +1 || sx == -1); + ex = x->ptr; + } + /* convert [y] to long format, if necessary */ + if (y->ptr == NULL) + { xassert(y->val != 0x80000000); + if (y->val >= 0) + { sy = +1; + t = (unsigned int)(+ y->val); + } + else + { sy = -1; + t = (unsigned int)(- y->val); + } + ey = &dumy; + ey->d[0] = (unsigned short)t; + ey->d[1] = (unsigned short)(t >> 16); + ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0; + ey->next = NULL; + } + else + { sy = y->val; + xassert(sy == +1 || sy == -1); + ey = y->ptr; + } + /* main fragment */ + xassert(sx > 0 && sy > 0 || sx < 0 && sy < 0); + cc = 0; + for (; ex || ey; ex = ex->next, ey = ey->next) + { if (ex == NULL) ex = &zero; + if (ey == NULL) ey = &zero; + for (k = 0; k <= 5; k++) + { if (ex->d[k] > ey->d[k]) cc = +1; + if (ex->d[k] < ey->d[k]) cc = -1; + } + } + if (sx < 0) cc = - cc; +done: return cc; +} + +int mpz_sgn(mpz_t x) +{ /* return +1 if x > 0, 0 if x = 0, and -1 if x < 0 */ + int s; + s = (x->val > 0 ? +1 : x->val < 0 ? -1 : 0); + return s; +} + +int mpz_out_str(void *_fp, int base, mpz_t x) +{ /* output x on stream fp, as a string in given base; the base + may vary from 2 to 36; + return the number of bytes written, or if an error occurred, + return 0 */ + FILE *fp = _fp; + mpz_t b, y, r; + int n, j, nwr = 0; + unsigned char *d; + static char *set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (!(2 <= base && base <= 36)) + xfault("mpz_out_str: base = %d; invalid base\n", base); + mpz_init(b); + mpz_set_si(b, base); + mpz_init(y); + mpz_init(r); + /* determine the number of digits */ + mpz_abs(y, x); + for (n = 0; mpz_sgn(y) != 0; n++) + mpz_div(y, NULL, y, b); + if (n == 0) n = 1; + /* compute the digits */ + d = xmalloc(n); + mpz_abs(y, x); + for (j = 0; j < n; j++) + { mpz_div(y, r, y, b); + xassert(0 <= r->val && r->val < base && r->ptr == NULL); + d[j] = (unsigned char)r->val; + } + /* output the integer to the stream */ + if (fp == NULL) fp = stdout; + if (mpz_sgn(x) < 0) + fputc('-', fp), nwr++; + for (j = n-1; j >= 0; j--) + fputc(set[d[j]], fp), nwr++; + if (ferror(fp)) nwr = 0; + mpz_clear(b); + mpz_clear(y); + mpz_clear(r); + xfree(d); + return nwr; +} + +/*====================================================================*/ + +mpq_t _mpq_init(void) +{ /* initialize x, and set its value to 0/1 */ + mpq_t x; + x = gmp_get_atom(sizeof(struct mpq)); + x->p.val = 0; + x->p.ptr = NULL; + x->q.val = 1; + x->q.ptr = NULL; + return x; +} + +void mpq_clear(mpq_t x) +{ /* free the space occupied by x */ + mpz_set_si(&x->p, 0); + xassert(x->p.ptr == NULL); + mpz_set_si(&x->q, 0); + xassert(x->q.ptr == NULL); + /* free the number descriptor */ + gmp_free_atom(x, sizeof(struct mpq)); + return; +} + +void mpq_canonicalize(mpq_t x) +{ /* remove any factors that are common to the numerator and + denominator of x, and make the denominator positive */ + mpz_t f; + xassert(x->q.val != 0); + if (x->q.val < 0) + { mpz_neg(&x->p, &x->p); + mpz_neg(&x->q, &x->q); + } + mpz_init(f); + mpz_gcd(f, &x->p, &x->q); + if (!(f->val == 1 && f->ptr == NULL)) + { mpz_div(&x->p, NULL, &x->p, f); + mpz_div(&x->q, NULL, &x->q, f); + } + mpz_clear(f); + return; +} + +void mpq_set(mpq_t z, mpq_t x) +{ /* set the value of z from x */ + if (z != x) + { mpz_set(&z->p, &x->p); + mpz_set(&z->q, &x->q); + } + return; +} + +void mpq_set_si(mpq_t x, int p, unsigned int q) +{ /* set the value of x to p/q */ + if (q == 0) + xfault("mpq_set_si: zero denominator not allowed\n"); + mpz_set_si(&x->p, p); + xassert(q <= 0x7FFFFFFF); + mpz_set_si(&x->q, q); + return; +} + +double mpq_get_d(mpq_t x) +{ /* convert x to a double, truncating if necessary */ + int np, nq; + double p, q; + p = mpz_get_d_2exp(&np, &x->p); + q = mpz_get_d_2exp(&nq, &x->q); + return ldexp(p / q, np - nq); +} + +void mpq_set_d(mpq_t x, double val) +{ /* set x to val; there is no rounding, the conversion is exact */ + int s, n, d, j; + double f; + mpz_t temp; + xassert(-DBL_MAX <= val && val <= +DBL_MAX); + mpq_set_si(x, 0, 1); + if (val > 0.0) + s = +1; + else if (val < 0.0) + s = -1; + else + goto done; + f = frexp(fabs(val), &n); + /* |val| = f * 2^n, where 0.5 <= f < 1.0 */ + mpz_init(temp); + while (f != 0.0) + { f *= 16.0, n -= 4; + d = (int)f; + xassert(0 <= d && d <= 15); + f -= (double)d; + /* x := 16 * x + d */ + mpz_set_si(temp, 16); + mpz_mul(&x->p, &x->p, temp); + mpz_set_si(temp, d); + mpz_add(&x->p, &x->p, temp); + } + mpz_clear(temp); + /* x := x * 2^n */ + if (n > 0) + { for (j = 1; j <= n; j++) + mpz_add(&x->p, &x->p, &x->p); + } + else if (n < 0) + { for (j = 1; j <= -n; j++) + mpz_add(&x->q, &x->q, &x->q); + mpq_canonicalize(x); + } + if (s < 0) mpq_neg(x, x); +done: return; +} + +void mpq_add(mpq_t z, mpq_t x, mpq_t y) +{ /* set z to x + y */ + mpz_t p, q; + mpz_init(p); + mpz_init(q); + mpz_mul(p, &x->p, &y->q); + mpz_mul(q, &x->q, &y->p); + mpz_add(p, p, q); + mpz_mul(q, &x->q, &y->q); + mpz_set(&z->p, p); + mpz_set(&z->q, q); + mpz_clear(p); + mpz_clear(q); + mpq_canonicalize(z); + return; +} + +void mpq_sub(mpq_t z, mpq_t x, mpq_t y) +{ /* set z to x - y */ + mpz_t p, q; + mpz_init(p); + mpz_init(q); + mpz_mul(p, &x->p, &y->q); + mpz_mul(q, &x->q, &y->p); + mpz_sub(p, p, q); + mpz_mul(q, &x->q, &y->q); + mpz_set(&z->p, p); + mpz_set(&z->q, q); + mpz_clear(p); + mpz_clear(q); + mpq_canonicalize(z); + return; +} + +void mpq_mul(mpq_t z, mpq_t x, mpq_t y) +{ /* set z to x * y */ + mpz_mul(&z->p, &x->p, &y->p); + mpz_mul(&z->q, &x->q, &y->q); + mpq_canonicalize(z); + return; +} + +void mpq_div(mpq_t z, mpq_t x, mpq_t y) +{ /* set z to x / y */ + mpz_t p, q; + if (mpq_sgn(y) == 0) + xfault("mpq_div: zero divisor not allowed\n"); + mpz_init(p); + mpz_init(q); + mpz_mul(p, &x->p, &y->q); + mpz_mul(q, &x->q, &y->p); + mpz_set(&z->p, p); + mpz_set(&z->q, q); + mpz_clear(p); + mpz_clear(q); + mpq_canonicalize(z); + return; +} + +void mpq_neg(mpq_t z, mpq_t x) +{ /* set z to 0 - x */ + mpq_set(z, x); + mpz_neg(&z->p, &z->p); + return; +} + +void mpq_abs(mpq_t z, mpq_t x) +{ /* set z to the absolute value of x */ + mpq_set(z, x); + mpz_abs(&z->p, &z->p); + xassert(mpz_sgn(&x->q) > 0); + return; +} + +int mpq_cmp(mpq_t x, mpq_t y) +{ /* compare x and y; return a positive value if x > y, zero if + x = y, or a nefative value if x < y */ + mpq_t temp; + int s; + mpq_init(temp); + mpq_sub(temp, x, y); + s = mpq_sgn(temp); + mpq_clear(temp); + return s; +} + +int mpq_sgn(mpq_t x) +{ /* return +1 if x > 0, 0 if x = 0, and -1 if x < 0 */ + int s; + s = mpz_sgn(&x->p); + xassert(mpz_sgn(&x->q) > 0); + return s; +} + +int mpq_out_str(void *_fp, int base, mpq_t x) +{ /* output x on stream fp, as a string in given base; the base + may vary from 2 to 36; output is in the form 'num/den' or if + the denominator is 1 then just 'num'; + if the parameter fp is a null pointer, stdout is assumed; + return the number of bytes written, or if an error occurred, + return 0 */ + FILE *fp = _fp; + int nwr; + if (!(2 <= base && base <= 36)) + xfault("mpq_out_str: base = %d; invalid base\n", base); + if (fp == NULL) fp = stdout; + nwr = mpz_out_str(fp, base, &x->p); + if (x->q.val == 1 && x->q.ptr == NULL) + ; + else + { fputc('/', fp), nwr++; + nwr += mpz_out_str(fp, base, &x->q); + } + if (ferror(fp)) nwr = 0; + return nwr; +} + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpgmp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpgmp.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,190 @@ +/* glpgmp.h (bignum arithmetic) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPGMP_H +#define GLPGMP_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_GMP /* use GNU MP bignum library */ + +#include + +#define gmp_pool_count _glp_gmp_pool_count +#define gmp_free_mem _glp_gmp_free_mem + +int gmp_pool_count(void); +void gmp_free_mem(void); + +#else /* use GLPK bignum module */ + +/*---------------------------------------------------------------------- +// INTEGER NUMBERS +// +// Depending on its magnitude an integer number of arbitrary precision +// is represented either in short format or in long format. +// +// Short format corresponds to the int type and allows representing +// integer numbers in the range [-(2^31-1), +(2^31-1)]. Note that for +// the most negative number of int type the short format is not used. +// +// In long format integer numbers are represented using the positional +// system with the base (radix) 2^16 = 65536: +// +// x = (-1)^s sum{j in 0..n-1} d[j] * 65536^j, +// +// where x is the integer to be represented, s is its sign (+1 or -1), +// d[j] are its digits (0 <= d[j] <= 65535). +// +// RATIONAL NUMBERS +// +// A rational number is represented as an irreducible fraction: +// +// p / q, +// +// where p (numerator) and q (denominator) are integer numbers (q > 0) +// having no common divisors. */ + +struct mpz +{ /* integer number */ + int val; + /* if ptr is a null pointer, the number is in short format, and + val is its value; otherwise, the number is in long format, and + val is its sign (+1 or -1) */ + struct mpz_seg *ptr; + /* pointer to the linked list of the number segments ordered in + ascending of powers of the base */ +}; + +struct mpz_seg +{ /* integer number segment */ + unsigned short d[6]; + /* six digits of the number ordered in ascending of powers of the + base */ + struct mpz_seg *next; + /* pointer to the next number segment */ +}; + +struct mpq +{ /* rational number (p / q) */ + struct mpz p; + /* numerator */ + struct mpz q; + /* denominator */ +}; + +typedef struct mpz *mpz_t; +typedef struct mpq *mpq_t; + +#define gmp_get_atom _glp_gmp_get_atom +#define gmp_free_atom _glp_gmp_free_atom +#define gmp_pool_count _glp_gmp_pool_count +#define gmp_get_work _glp_gmp_get_work +#define gmp_free_mem _glp_gmp_free_mem + +#define _mpz_init _glp_mpz_init +#define mpz_clear _glp_mpz_clear +#define mpz_set _glp_mpz_set +#define mpz_set_si _glp_mpz_set_si +#define mpz_get_d _glp_mpz_get_d +#define mpz_get_d_2exp _glp_mpz_get_d_2exp +#define mpz_swap _glp_mpz_swap +#define mpz_add _glp_mpz_add +#define mpz_sub _glp_mpz_sub +#define mpz_mul _glp_mpz_mul +#define mpz_neg _glp_mpz_neg +#define mpz_abs _glp_mpz_abs +#define mpz_div _glp_mpz_div +#define mpz_gcd _glp_mpz_gcd +#define mpz_cmp _glp_mpz_cmp +#define mpz_sgn _glp_mpz_sgn +#define mpz_out_str _glp_mpz_out_str + +#define _mpq_init _glp_mpq_init +#define mpq_clear _glp_mpq_clear +#define mpq_canonicalize _glp_mpq_canonicalize +#define mpq_set _glp_mpq_set +#define mpq_set_si _glp_mpq_set_si +#define mpq_get_d _glp_mpq_get_d +#define mpq_set_d _glp_mpq_set_d +#define mpq_add _glp_mpq_add +#define mpq_sub _glp_mpq_sub +#define mpq_mul _glp_mpq_mul +#define mpq_div _glp_mpq_div +#define mpq_neg _glp_mpq_neg +#define mpq_abs _glp_mpq_abs +#define mpq_cmp _glp_mpq_cmp +#define mpq_sgn _glp_mpq_sgn +#define mpq_out_str _glp_mpq_out_str + +void *gmp_get_atom(int size); +void gmp_free_atom(void *ptr, int size); +int gmp_pool_count(void); +unsigned short *gmp_get_work(int size); +void gmp_free_mem(void); + +mpz_t _mpz_init(void); +#define mpz_init(x) (void)((x) = _mpz_init()) +void mpz_clear(mpz_t x); +void mpz_set(mpz_t z, mpz_t x); +void mpz_set_si(mpz_t x, int val); +double mpz_get_d(mpz_t x); +double mpz_get_d_2exp(int *exp, mpz_t x); +void mpz_swap(mpz_t x, mpz_t y); +void mpz_add(mpz_t, mpz_t, mpz_t); +void mpz_sub(mpz_t, mpz_t, mpz_t); +void mpz_mul(mpz_t, mpz_t, mpz_t); +void mpz_neg(mpz_t z, mpz_t x); +void mpz_abs(mpz_t z, mpz_t x); +void mpz_div(mpz_t q, mpz_t r, mpz_t x, mpz_t y); +void mpz_gcd(mpz_t z, mpz_t x, mpz_t y); +int mpz_cmp(mpz_t x, mpz_t y); +int mpz_sgn(mpz_t x); +int mpz_out_str(void *fp, int base, mpz_t x); + +mpq_t _mpq_init(void); +#define mpq_init(x) (void)((x) = _mpq_init()) +void mpq_clear(mpq_t x); +void mpq_canonicalize(mpq_t x); +void mpq_set(mpq_t z, mpq_t x); +void mpq_set_si(mpq_t x, int p, unsigned int q); +double mpq_get_d(mpq_t x); +void mpq_set_d(mpq_t x, double val); +void mpq_add(mpq_t z, mpq_t x, mpq_t y); +void mpq_sub(mpq_t z, mpq_t x, mpq_t y); +void mpq_mul(mpq_t z, mpq_t x, mpq_t y); +void mpq_div(mpq_t z, mpq_t x, mpq_t y); +void mpq_neg(mpq_t z, mpq_t x); +void mpq_abs(mpq_t z, mpq_t x); +int mpq_cmp(mpq_t x, mpq_t y); +int mpq_sgn(mpq_t x); +int mpq_out_str(void *fp, int base, mpq_t x); + +#endif + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glphbm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glphbm.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,519 @@ +/* glphbm.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_ERRNO +#define _GLPSTD_STDIO +#include "glphbm.h" +#include "glpenv.h" + +/*********************************************************************** +* NAME +* +* hbm_read_mat - read sparse matrix in Harwell-Boeing format +* +* SYNOPSIS +* +* #include "glphbm.h" +* HBM *hbm_read_mat(const char *fname); +* +* DESCRIPTION +* +* The routine hbm_read_mat reads a sparse matrix in the Harwell-Boeing +* format from a text file whose name is the character string fname. +* +* Detailed description of the Harwell-Boeing format recognised by this +* routine is given in the following report: +* +* I.S.Duff, R.G.Grimes, J.G.Lewis. User's Guide for the Harwell-Boeing +* Sparse Matrix Collection (Release I), TR/PA/92/86, October 1992. +* +* RETURNS +* +* If no error occured, the routine hbm_read_mat returns a pointer to +* a data structure containing the matrix. In case of error the routine +* prints an appropriate error message and returns NULL. */ + +struct dsa +{ /* working area used by routine hbm_read_mat */ + const char *fname; + /* name of input text file */ + FILE *fp; + /* stream assigned to input text file */ + int seqn; + /* card sequential number */ + char card[80+1]; + /* card image buffer */ + int fmt_p; + /* scale factor */ + int fmt_k; + /* iterator */ + int fmt_f; + /* format code */ + int fmt_w; + /* field width */ + int fmt_d; + /* number of decimal places after point */ +}; + +/*********************************************************************** +* read_card - read next data card +* +* This routine reads the next 80-column card from the input text file +* and stores its image into the character string card. If the card was +* read successfully, the routine returns zero, otherwise non-zero. */ + +static int read_card(struct dsa *dsa) +{ int k, c; + dsa->seqn++; + memset(dsa->card, ' ', 80), dsa->card[80] = '\0'; + k = 0; + for (;;) + { c = fgetc(dsa->fp); + if (ferror(dsa->fp)) + { xprintf("%s:%d: read error - %s\n", dsa->fname, dsa->seqn, + strerror(errno)); + return 1; + } + if (feof(dsa->fp)) + { if (k == 0) + xprintf("%s:%d: unexpected EOF\n", dsa->fname, + dsa->seqn); + else + xprintf("%s:%d: missing final LF\n", dsa->fname, + dsa->seqn); + return 1; + } + if (c == '\r') continue; + if (c == '\n') break; + if (iscntrl(c)) + { xprintf("%s:%d: invalid control character 0x%02X\n", + dsa->fname, dsa->seqn, c); + return 1; + } + if (k == 80) + { xprintf("%s:%d: card image too long\n", dsa->fname, + dsa->seqn); + return 1; + } + dsa->card[k++] = (char)c; + } + return 0; +} + +/*********************************************************************** +* scan_int - scan integer value from the current card +* +* This routine scans an integer value from the current card, where fld +* is the name of the field, pos is the position of the field, width is +* the width of the field, val points to a location to which the scanned +* value should be stored. If the value was scanned successfully, the +* routine returns zero, otherwise non-zero. */ + +static int scan_int(struct dsa *dsa, char *fld, int pos, int width, + int *val) +{ char str[80+1]; + xassert(1 <= width && width <= 80); + memcpy(str, dsa->card + pos, width), str[width] = '\0'; + if (str2int(strspx(str), val)) + { xprintf("%s:%d: field `%s' contains invalid value `%s'\n", + dsa->fname, dsa->seqn, fld, str); + return 1; + } + return 0; +} + +/*********************************************************************** +* parse_fmt - parse Fortran format specification +* +* This routine parses the Fortran format specification represented as +* character string which fmt points to and stores format elements into +* appropriate static locations. Should note that not all valid Fortran +* format specifications may be recognised. If the format specification +* was recognised, the routine returns zero, otherwise non-zero. */ + +static int parse_fmt(struct dsa *dsa, char *fmt) +{ int k, s, val; + char str[80+1]; + /* first character should be left parenthesis */ + if (fmt[0] != '(') +fail: { xprintf("hbm_read_mat: format `%s' not recognised\n", fmt); + return 1; + } + k = 1; + /* optional scale factor */ + dsa->fmt_p = 0; + if (isdigit((unsigned char)fmt[k])) + { s = 0; + while (isdigit((unsigned char)fmt[k])) + { if (s == 80) goto fail; + str[s++] = fmt[k++]; + } + str[s] = '\0'; + if (str2int(str, &val)) goto fail; + if (toupper((unsigned char)fmt[k]) != 'P') goto iter; + dsa->fmt_p = val, k++; + if (!(0 <= dsa->fmt_p && dsa->fmt_p <= 255)) goto fail; + /* optional comma may follow scale factor */ + if (fmt[k] == ',') k++; + } + /* optional iterator */ + dsa->fmt_k = 1; + if (isdigit((unsigned char)fmt[k])) + { s = 0; + while (isdigit((unsigned char)fmt[k])) + { if (s == 80) goto fail; + str[s++] = fmt[k++]; + } + str[s] = '\0'; + if (str2int(str, &val)) goto fail; +iter: dsa->fmt_k = val; + if (!(1 <= dsa->fmt_k && dsa->fmt_k <= 255)) goto fail; + } + /* format code */ + dsa->fmt_f = toupper((unsigned char)fmt[k++]); + if (!(dsa->fmt_f == 'D' || dsa->fmt_f == 'E' || + dsa->fmt_f == 'F' || dsa->fmt_f == 'G' || + dsa->fmt_f == 'I')) goto fail; + /* field width */ + if (!isdigit((unsigned char)fmt[k])) goto fail; + s = 0; + while (isdigit((unsigned char)fmt[k])) + { if (s == 80) goto fail; + str[s++] = fmt[k++]; + } + str[s] = '\0'; + if (str2int(str, &dsa->fmt_w)) goto fail; + if (!(1 <= dsa->fmt_w && dsa->fmt_w <= 255)) goto fail; + /* optional number of decimal places after point */ + dsa->fmt_d = 0; + if (fmt[k] == '.') + { k++; + if (!isdigit((unsigned char)fmt[k])) goto fail; + s = 0; + while (isdigit((unsigned char)fmt[k])) + { if (s == 80) goto fail; + str[s++] = fmt[k++]; + } + str[s] = '\0'; + if (str2int(str, &dsa->fmt_d)) goto fail; + if (!(0 <= dsa->fmt_d && dsa->fmt_d <= 255)) goto fail; + } + /* last character should be right parenthesis */ + if (!(fmt[k] == ')' && fmt[k+1] == '\0')) goto fail; + return 0; +} + +/*********************************************************************** +* read_int_array - read array of integer type +* +* This routine reads an integer array from the input text file, where +* name is array name, fmt is Fortran format specification that controls +* reading, n is number of array elements, val is array of integer type. +* If the array was read successful, the routine returns zero, otherwise +* non-zero. */ + +static int read_int_array(struct dsa *dsa, char *name, char *fmt, + int n, int val[]) +{ int k, pos; + char str[80+1]; + if (parse_fmt(dsa, fmt)) return 1; + if (!(dsa->fmt_f == 'I' && dsa->fmt_w <= 80 && + dsa->fmt_k * dsa->fmt_w <= 80)) + { xprintf( + "%s:%d: can't read array `%s' - invalid format `%s'\n", + dsa->fname, dsa->seqn, name, fmt); + return 1; + } + for (k = 1, pos = INT_MAX; k <= n; k++, pos++) + { if (pos >= dsa->fmt_k) + { if (read_card(dsa)) return 1; + pos = 0; + } + memcpy(str, dsa->card + dsa->fmt_w * pos, dsa->fmt_w); + str[dsa->fmt_w] = '\0'; + strspx(str); + if (str2int(str, &val[k])) + { xprintf( + "%s:%d: can't read array `%s' - invalid value `%s'\n", + dsa->fname, dsa->seqn, name, str); + return 1; + } + } + return 0; +} + +/*********************************************************************** +* read_real_array - read array of real type +* +* This routine reads a real array from the input text file, where name +* is array name, fmt is Fortran format specification that controls +* reading, n is number of array elements, val is array of real type. +* If the array was read successful, the routine returns zero, otherwise +* non-zero. */ + +static int read_real_array(struct dsa *dsa, char *name, char *fmt, + int n, double val[]) +{ int k, pos; + char str[80+1], *ptr; + if (parse_fmt(dsa, fmt)) return 1; + if (!(dsa->fmt_f != 'I' && dsa->fmt_w <= 80 && + dsa->fmt_k * dsa->fmt_w <= 80)) + { xprintf( + "%s:%d: can't read array `%s' - invalid format `%s'\n", + dsa->fname, dsa->seqn, name, fmt); + return 1; + } + for (k = 1, pos = INT_MAX; k <= n; k++, pos++) + { if (pos >= dsa->fmt_k) + { if (read_card(dsa)) return 1; + pos = 0; + } + memcpy(str, dsa->card + dsa->fmt_w * pos, dsa->fmt_w); + str[dsa->fmt_w] = '\0'; + strspx(str); + if (strchr(str, '.') == NULL && strcmp(str, "0")) + { xprintf("%s(%d): can't read array `%s' - value `%s' has no " + "decimal point\n", dsa->fname, dsa->seqn, name, str); + return 1; + } + /* sometimes lower case letters appear */ + for (ptr = str; *ptr; ptr++) + *ptr = (char)toupper((unsigned char)*ptr); + ptr = strchr(str, 'D'); + if (ptr != NULL) *ptr = 'E'; + /* value may appear with decimal exponent but without letters + E or D (for example, -123.456-012), so missing letter should + be inserted */ + ptr = strchr(str+1, '+'); + if (ptr == NULL) ptr = strchr(str+1, '-'); + if (ptr != NULL && *(ptr-1) != 'E') + { xassert(strlen(str) < 80); + memmove(ptr+1, ptr, strlen(ptr)+1); + *ptr = 'E'; + } + if (str2num(str, &val[k])) + { xprintf( + "%s:%d: can't read array `%s' - invalid value `%s'\n", + dsa->fname, dsa->seqn, name, str); + return 1; + } + } + return 0; +} + +HBM *hbm_read_mat(const char *fname) +{ struct dsa _dsa, *dsa = &_dsa; + HBM *hbm = NULL; + dsa->fname = fname; + xprintf("hbm_read_mat: reading matrix from `%s'...\n", + dsa->fname); + dsa->fp = fopen(dsa->fname, "r"); + if (dsa->fp == NULL) + { xprintf("hbm_read_mat: unable to open `%s' - %s\n", + dsa->fname, strerror(errno)); + goto fail; + } + dsa->seqn = 0; + hbm = xmalloc(sizeof(HBM)); + memset(hbm, 0, sizeof(HBM)); + /* read the first heading card */ + if (read_card(dsa)) goto fail; + memcpy(hbm->title, dsa->card, 72), hbm->title[72] = '\0'; + strtrim(hbm->title); + xprintf("%s\n", hbm->title); + memcpy(hbm->key, dsa->card+72, 8), hbm->key[8] = '\0'; + strspx(hbm->key); + xprintf("key = %s\n", hbm->key); + /* read the second heading card */ + if (read_card(dsa)) goto fail; + if (scan_int(dsa, "totcrd", 0, 14, &hbm->totcrd)) goto fail; + if (scan_int(dsa, "ptrcrd", 14, 14, &hbm->ptrcrd)) goto fail; + if (scan_int(dsa, "indcrd", 28, 14, &hbm->indcrd)) goto fail; + if (scan_int(dsa, "valcrd", 42, 14, &hbm->valcrd)) goto fail; + if (scan_int(dsa, "rhscrd", 56, 14, &hbm->rhscrd)) goto fail; + xprintf("totcrd = %d; ptrcrd = %d; indcrd = %d; valcrd = %d; rhsc" + "rd = %d\n", hbm->totcrd, hbm->ptrcrd, hbm->indcrd, + hbm->valcrd, hbm->rhscrd); + /* read the third heading card */ + if (read_card(dsa)) goto fail; + memcpy(hbm->mxtype, dsa->card, 3), hbm->mxtype[3] = '\0'; + if (strchr("RCP", hbm->mxtype[0]) == NULL || + strchr("SUHZR", hbm->mxtype[1]) == NULL || + strchr("AE", hbm->mxtype[2]) == NULL) + { xprintf("%s:%d: matrix type `%s' not recognised\n", + dsa->fname, dsa->seqn, hbm->mxtype); + goto fail; + } + if (scan_int(dsa, "nrow", 14, 14, &hbm->nrow)) goto fail; + if (scan_int(dsa, "ncol", 28, 14, &hbm->ncol)) goto fail; + if (scan_int(dsa, "nnzero", 42, 14, &hbm->nnzero)) goto fail; + if (scan_int(dsa, "neltvl", 56, 14, &hbm->neltvl)) goto fail; + xprintf("mxtype = %s; nrow = %d; ncol = %d; nnzero = %d; neltvl =" + " %d\n", hbm->mxtype, hbm->nrow, hbm->ncol, hbm->nnzero, + hbm->neltvl); + /* read the fourth heading card */ + if (read_card(dsa)) goto fail; + memcpy(hbm->ptrfmt, dsa->card, 16), hbm->ptrfmt[16] = '\0'; + strspx(hbm->ptrfmt); + memcpy(hbm->indfmt, dsa->card+16, 16), hbm->indfmt[16] = '\0'; + strspx(hbm->indfmt); + memcpy(hbm->valfmt, dsa->card+32, 20), hbm->valfmt[20] = '\0'; + strspx(hbm->valfmt); + memcpy(hbm->rhsfmt, dsa->card+52, 20), hbm->rhsfmt[20] = '\0'; + strspx(hbm->rhsfmt); + xprintf("ptrfmt = %s; indfmt = %s; valfmt = %s; rhsfmt = %s\n", + hbm->ptrfmt, hbm->indfmt, hbm->valfmt, hbm->rhsfmt); + /* read the fifth heading card (optional) */ + if (hbm->rhscrd <= 0) + { strcpy(hbm->rhstyp, "???"); + hbm->nrhs = 0; + hbm->nrhsix = 0; + } + else + { if (read_card(dsa)) goto fail; + memcpy(hbm->rhstyp, dsa->card, 3), hbm->rhstyp[3] = '\0'; + if (scan_int(dsa, "nrhs", 14, 14, &hbm->nrhs)) goto fail; + if (scan_int(dsa, "nrhsix", 28, 14, &hbm->nrhsix)) goto fail; + xprintf("rhstyp = `%s'; nrhs = %d; nrhsix = %d\n", + hbm->rhstyp, hbm->nrhs, hbm->nrhsix); + } + /* read matrix structure */ + hbm->colptr = xcalloc(1+hbm->ncol+1, sizeof(int)); + if (read_int_array(dsa, "colptr", hbm->ptrfmt, hbm->ncol+1, + hbm->colptr)) goto fail; + hbm->rowind = xcalloc(1+hbm->nnzero, sizeof(int)); + if (read_int_array(dsa, "rowind", hbm->indfmt, hbm->nnzero, + hbm->rowind)) goto fail; + /* read matrix values */ + if (hbm->valcrd <= 0) goto done; + if (hbm->mxtype[2] == 'A') + { /* assembled matrix */ + hbm->values = xcalloc(1+hbm->nnzero, sizeof(double)); + if (read_real_array(dsa, "values", hbm->valfmt, hbm->nnzero, + hbm->values)) goto fail; + } + else + { /* elemental (unassembled) matrix */ + hbm->values = xcalloc(1+hbm->neltvl, sizeof(double)); + if (read_real_array(dsa, "values", hbm->valfmt, hbm->neltvl, + hbm->values)) goto fail; + } + /* read right-hand sides */ + if (hbm->nrhs <= 0) goto done; + if (hbm->rhstyp[0] == 'F') + { /* dense format */ + hbm->nrhsvl = hbm->nrow * hbm->nrhs; + hbm->rhsval = xcalloc(1+hbm->nrhsvl, sizeof(double)); + if (read_real_array(dsa, "rhsval", hbm->rhsfmt, hbm->nrhsvl, + hbm->rhsval)) goto fail; + } + else if (hbm->rhstyp[0] == 'M' && hbm->mxtype[2] == 'A') + { /* sparse format */ + /* read pointers */ + hbm->rhsptr = xcalloc(1+hbm->nrhs+1, sizeof(int)); + if (read_int_array(dsa, "rhsptr", hbm->ptrfmt, hbm->nrhs+1, + hbm->rhsptr)) goto fail; + /* read sparsity pattern */ + hbm->rhsind = xcalloc(1+hbm->nrhsix, sizeof(int)); + if (read_int_array(dsa, "rhsind", hbm->indfmt, hbm->nrhsix, + hbm->rhsind)) goto fail; + /* read values */ + hbm->rhsval = xcalloc(1+hbm->nrhsix, sizeof(double)); + if (read_real_array(dsa, "rhsval", hbm->rhsfmt, hbm->nrhsix, + hbm->rhsval)) goto fail; + } + else if (hbm->rhstyp[0] == 'M' && hbm->mxtype[2] == 'E') + { /* elemental format */ + hbm->rhsval = xcalloc(1+hbm->nrhsvl, sizeof(double)); + if (read_real_array(dsa, "rhsval", hbm->rhsfmt, hbm->nrhsvl, + hbm->rhsval)) goto fail; + } + else + { xprintf("%s:%d: right-hand side type `%c' not recognised\n", + dsa->fname, dsa->seqn, hbm->rhstyp[0]); + goto fail; + } + /* read starting guesses */ + if (hbm->rhstyp[1] == 'G') + { hbm->nguess = hbm->nrow * hbm->nrhs; + hbm->sguess = xcalloc(1+hbm->nguess, sizeof(double)); + if (read_real_array(dsa, "sguess", hbm->rhsfmt, hbm->nguess, + hbm->sguess)) goto fail; + } + /* read solution vectors */ + if (hbm->rhstyp[2] == 'X') + { hbm->nexact = hbm->nrow * hbm->nrhs; + hbm->xexact = xcalloc(1+hbm->nexact, sizeof(double)); + if (read_real_array(dsa, "xexact", hbm->rhsfmt, hbm->nexact, + hbm->xexact)) goto fail; + } +done: /* reading has been completed */ + xprintf("hbm_read_mat: %d cards were read\n", dsa->seqn); + fclose(dsa->fp); + return hbm; +fail: /* something wrong in Danish kingdom */ + if (hbm != NULL) + { if (hbm->colptr != NULL) xfree(hbm->colptr); + if (hbm->rowind != NULL) xfree(hbm->rowind); + if (hbm->rhsptr != NULL) xfree(hbm->rhsptr); + if (hbm->rhsind != NULL) xfree(hbm->rhsind); + if (hbm->values != NULL) xfree(hbm->values); + if (hbm->rhsval != NULL) xfree(hbm->rhsval); + if (hbm->sguess != NULL) xfree(hbm->sguess); + if (hbm->xexact != NULL) xfree(hbm->xexact); + xfree(hbm); + } + if (dsa->fp != NULL) fclose(dsa->fp); + return NULL; +} + +/*********************************************************************** +* NAME +* +* hbm_free_mat - free sparse matrix in Harwell-Boeing format +* +* SYNOPSIS +* +* #include "glphbm.h" +* void hbm_free_mat(HBM *hbm); +* +* DESCRIPTION +* +* The hbm_free_mat routine frees all the memory allocated to the data +* structure containing a sparse matrix in the Harwell-Boeing format. */ + +void hbm_free_mat(HBM *hbm) +{ if (hbm->colptr != NULL) xfree(hbm->colptr); + if (hbm->rowind != NULL) xfree(hbm->rowind); + if (hbm->rhsptr != NULL) xfree(hbm->rhsptr); + if (hbm->rhsind != NULL) xfree(hbm->rhsind); + if (hbm->values != NULL) xfree(hbm->values); + if (hbm->rhsval != NULL) xfree(hbm->rhsval); + if (hbm->sguess != NULL) xfree(hbm->sguess); + if (hbm->xexact != NULL) xfree(hbm->xexact); + xfree(hbm); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glphbm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glphbm.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,127 @@ +/* glphbm.h (Harwell-Boeing sparse matrix format) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPHBM_H +#define GLPHBM_H + +typedef struct HBM HBM; + +struct HBM +{ /* sparse matrix in Harwell-Boeing format; for details see the + report: I.S.Duff, R.G.Grimes, J.G.Lewis. User's Guide for the + Harwell-Boeing Sparse Matrix Collection (Release I), 1992 */ + char title[72+1]; + /* matrix title (informative) */ + char key[8+1]; + /* matrix key (informative) */ + char mxtype[3+1]; + /* matrix type: + R.. real matrix + C.. complex matrix + P.. pattern only (no numerical values supplied) + .S. symmetric (lower triangle + main diagonal) + .U. unsymmetric + .H. hermitian (lower triangle + main diagonal) + .Z. skew symmetric (lower triangle only) + .R. rectangular + ..A assembled + ..E elemental (unassembled) */ + char rhstyp[3+1]; + /* optional types: + F.. right-hand sides in dense format + M.. right-hand sides in same format as matrix + .G. starting vector(s) (guess) is supplied + ..X exact solution vector(s) is supplied */ + char ptrfmt[16+1]; + /* format for pointers */ + char indfmt[16+1]; + /* format for row (or variable) indices */ + char valfmt[20+1]; + /* format for numerical values of coefficient matrix */ + char rhsfmt[20+1]; + /* format for numerical values of right-hand sides */ + int totcrd; + /* total number of cards excluding header */ + int ptrcrd; + /* number of cards for ponters */ + int indcrd; + /* number of cards for row (or variable) indices */ + int valcrd; + /* number of cards for numerical values */ + int rhscrd; + /* number of lines for right-hand sides; + including starting guesses and solution vectors if present; + zero indicates no right-hand side data is present */ + int nrow; + /* number of rows (or variables) */ + int ncol; + /* number of columns (or elements) */ + int nnzero; + /* number of row (or variable) indices; + equal to number of entries for assembled matrix */ + int neltvl; + /* number of elemental matrix entries; + zero in case of assembled matrix */ + int nrhs; + /* number of right-hand sides */ + int nrhsix; + /* number of row indices; + ignored in case of unassembled matrix */ + int nrhsvl; + /* total number of entries in all right-hand sides */ + int nguess; + /* total number of entries in all starting guesses */ + int nexact; + /* total number of entries in all solution vectors */ + int *colptr; /* alias: eltptr */ + /* column pointers (in case of assembled matrix); + elemental matrix pointers (in case of unassembled matrix) */ + int *rowind; /* alias: varind */ + /* row indices (in case of assembled matrix); + variable indices (in case of unassembled matrix) */ + int *rhsptr; + /* right-hand side pointers */ + int *rhsind; + /* right-hand side indices */ + double *values; + /* matrix values */ + double *rhsval; + /* right-hand side values */ + double *sguess; + /* starting guess values */ + double *xexact; + /* solution vector values */ +}; + +#define hbm_read_mat _glp_hbm_read_mat +HBM *hbm_read_mat(const char *fname); +/* read sparse matrix in Harwell-Boeing format */ + +#define hbm_free_mat _glp_hbm_free_mat +void hbm_free_mat(HBM *hbm); +/* free sparse matrix in Harwell-Boeing format */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpini01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpini01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,577 @@ +/* glpini01.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*---------------------------------------------------------------------- +-- triang - find maximal triangular part of a rectangular matrix. +-- +-- *Synopsis* +-- +-- int triang(int m, int n, +-- void *info, int (*mat)(void *info, int k, int ndx[]), +-- int rn[], int cn[]); +-- +-- *Description* +-- +-- For a given rectangular (sparse) matrix A with m rows and n columns +-- the routine triang tries to find such permutation matrices P and Q +-- that the first rows and columns of the matrix B = P*A*Q form a lower +-- triangular submatrix of as greatest size as possible: +-- +-- 1 n +-- 1 * . . . . . . x x x x x x +-- * * . . . . . x x x x x x +-- * * * . . . . x x x x x x +-- * * * * . . . x x x x x x +-- B = P*A*Q = * * * * * . . x x x x x x +-- * * * * * * . x x x x x x +-- * * * * * * * x x x x x x +-- x x x x x x x x x x x x x +-- x x x x x x x x x x x x x +-- m x x x x x x x x x x x x x +-- +-- where: '*' - elements of the lower triangular part, '.' - structural +-- zeros, 'x' - other (either non-zero or zero) elements. +-- +-- The parameter info is a transit pointer passed to the formal routine +-- mat (see below). +-- +-- The formal routine mat specifies the given matrix A in both row- and +-- column-wise formats. In order to obtain an i-th row of the matrix A +-- the routine triang calls the routine mat with the parameter k = +i, +-- 1 <= i <= m. In response the routine mat should store column indices +-- of (non-zero) elements of the i-th row to the locations ndx[1], ..., +-- ndx[len], where len is number of non-zeros in the i-th row returned +-- on exit. Analogously, in order to obtain a j-th column of the matrix +-- A, the routine mat is called with the parameter k = -j, 1 <= j <= n, +-- and should return pattern of the j-th column in the same way as for +-- row patterns. Note that the routine mat may be called more than once +-- for the same rows and columns. +-- +-- On exit the routine computes two resultant arrays rn and cn, which +-- define the permutation matrices P and Q, respectively. The array rn +-- should have at least 1+m locations, where rn[i] = i' (1 <= i <= m) +-- means that i-th row of the original matrix A corresponds to i'-th row +-- of the matrix B = P*A*Q. Similarly, the array cn should have at least +-- 1+n locations, where cn[j] = j' (1 <= j <= n) means that j-th column +-- of the matrix A corresponds to j'-th column of the matrix B. +-- +-- *Returns* +-- +-- The routine triang returns the size of the lower tringular part of +-- the matrix B = P*A*Q (see the figure above). +-- +-- *Complexity* +-- +-- The time complexity of the routine triang is O(nnz), where nnz is +-- number of non-zeros in the given matrix A. +-- +-- *Algorithm* +-- +-- The routine triang starts from the matrix B = P*Q*A, where P and Q +-- are unity matrices, so initially B = A. +-- +-- Before the next iteration B = (B1 | B2 | B3), where B1 is partially +-- built a lower triangular submatrix, B2 is the active submatrix, and +-- B3 is a submatrix that contains rejected columns. Thus, the current +-- matrix B looks like follows (initially k1 = 1 and k2 = n): +-- +-- 1 k1 k2 n +-- 1 x . . . . . . . . . . . . . # # # +-- x x . . . . . . . . . . . . # # # +-- x x x . . . . . . . . . . # # # # +-- x x x x . . . . . . . . . # # # # +-- x x x x x . . . . . . . # # # # # +-- k1 x x x x x * * * * * * * # # # # # +-- x x x x x * * * * * * * # # # # # +-- x x x x x * * * * * * * # # # # # +-- x x x x x * * * * * * * # # # # # +-- m x x x x x * * * * * * * # # # # # +-- <--B1---> <----B2-----> <---B3--> +-- +-- On each iteartion the routine looks for a singleton row, i.e. some +-- row that has the only non-zero in the active submatrix B2. If such +-- row exists and the corresponding non-zero is b[i,j], where (by the +-- definition) k1 <= i <= m and k1 <= j <= k2, the routine permutes +-- k1-th and i-th rows and k1-th and j-th columns of the matrix B (in +-- order to place the element in the position b[k1,k1]), removes the +-- k1-th column from the active submatrix B2, and adds this column to +-- the submatrix B1. If no row singletons exist, but B2 is not empty +-- yet, the routine chooses a j-th column, which has maximal number of +-- non-zeros among other columns of B2, removes this column from B2 and +-- adds it to the submatrix B3 in the hope that new row singletons will +-- appear in the active submatrix. */ + +static int triang(int m, int n, + void *info, int (*mat)(void *info, int k, int ndx[]), + int rn[], int cn[]) +{ int *ndx; /* int ndx[1+max(m,n)]; */ + /* this array is used for querying row and column patterns of the + given matrix A (the third parameter to the routine mat) */ + int *rs_len; /* int rs_len[1+m]; */ + /* rs_len[0] is not used; + rs_len[i], 1 <= i <= m, is number of non-zeros in the i-th row + of the matrix A, which (non-zeros) belong to the current active + submatrix */ + int *rs_head; /* int rs_head[1+n]; */ + /* rs_head[len], 0 <= len <= n, is the number i of the first row + of the matrix A, for which rs_len[i] = len */ + int *rs_prev; /* int rs_prev[1+m]; */ + /* rs_prev[0] is not used; + rs_prev[i], 1 <= i <= m, is a number i' of the previous row of + the matrix A, for which rs_len[i] = rs_len[i'] (zero marks the + end of this linked list) */ + int *rs_next; /* int rs_next[1+m]; */ + /* rs_next[0] is not used; + rs_next[i], 1 <= i <= m, is a number i' of the next row of the + matrix A, for which rs_len[i] = rs_len[i'] (zero marks the end + this linked list) */ + int cs_head; + /* is a number j of the first column of the matrix A, which has + maximal number of non-zeros among other columns */ + int *cs_prev; /* cs_prev[1+n]; */ + /* cs_prev[0] is not used; + cs_prev[j], 1 <= j <= n, is a number of the previous column of + the matrix A with the same or greater number of non-zeros than + in the j-th column (zero marks the end of this linked list) */ + int *cs_next; /* cs_next[1+n]; */ + /* cs_next[0] is not used; + cs_next[j], 1 <= j <= n, is a number of the next column of + the matrix A with the same or lesser number of non-zeros than + in the j-th column (zero marks the end of this linked list) */ + int i, j, ii, jj, k1, k2, len, t, size = 0; + int *head, *rn_inv, *cn_inv; + if (!(m > 0 && n > 0)) + xerror("triang: m = %d; n = %d; invalid dimension\n", m, n); + /* allocate working arrays */ + ndx = xcalloc(1+(m >= n ? m : n), sizeof(int)); + rs_len = xcalloc(1+m, sizeof(int)); + rs_head = xcalloc(1+n, sizeof(int)); + rs_prev = xcalloc(1+m, sizeof(int)); + rs_next = xcalloc(1+m, sizeof(int)); + cs_prev = xcalloc(1+n, sizeof(int)); + cs_next = xcalloc(1+n, sizeof(int)); + /* build linked lists of columns of the matrix A with the same + number of non-zeros */ + head = rs_len; /* currently rs_len is used as working array */ + for (len = 0; len <= m; len ++) head[len] = 0; + for (j = 1; j <= n; j++) + { /* obtain length of the j-th column */ + len = mat(info, -j, ndx); + xassert(0 <= len && len <= m); + /* include the j-th column in the corresponding linked list */ + cs_prev[j] = head[len]; + head[len] = j; + } + /* merge all linked lists of columns in one linked list, where + columns are ordered by descending of their lengths */ + cs_head = 0; + for (len = 0; len <= m; len++) + { for (j = head[len]; j != 0; j = cs_prev[j]) + { cs_next[j] = cs_head; + cs_head = j; + } + } + jj = 0; + for (j = cs_head; j != 0; j = cs_next[j]) + { cs_prev[j] = jj; + jj = j; + } + /* build initial doubly linked lists of rows of the matrix A with + the same number of non-zeros */ + for (len = 0; len <= n; len++) rs_head[len] = 0; + for (i = 1; i <= m; i++) + { /* obtain length of the i-th row */ + rs_len[i] = len = mat(info, +i, ndx); + xassert(0 <= len && len <= n); + /* include the i-th row in the correspondng linked list */ + rs_prev[i] = 0; + rs_next[i] = rs_head[len]; + if (rs_next[i] != 0) rs_prev[rs_next[i]] = i; + rs_head[len] = i; + } + /* initially all rows and columns of the matrix A are active */ + for (i = 1; i <= m; i++) rn[i] = 0; + for (j = 1; j <= n; j++) cn[j] = 0; + /* set initial bounds of the active submatrix */ + k1 = 1, k2 = n; + /* main loop starts here */ + while (k1 <= k2) + { i = rs_head[1]; + if (i != 0) + { /* the i-th row of the matrix A is a row singleton, since + it has the only non-zero in the active submatrix */ + xassert(rs_len[i] == 1); + /* determine the number j of an active column of the matrix + A, in which this non-zero is placed */ + j = 0; + t = mat(info, +i, ndx); + xassert(0 <= t && t <= n); + for (t = t; t >= 1; t--) + { jj = ndx[t]; + xassert(1 <= jj && jj <= n); + if (cn[jj] == 0) + { xassert(j == 0); + j = jj; + } + } + xassert(j != 0); + /* the singleton is a[i,j]; move a[i,j] to the position + b[k1,k1] of the matrix B */ + rn[i] = cn[j] = k1; + /* shift the left bound of the active submatrix */ + k1++; + /* increase the size of the lower triangular part */ + size++; + } + else + { /* the current active submatrix has no row singletons */ + /* remove an active column with maximal number of non-zeros + from the active submatrix */ + j = cs_head; + xassert(j != 0); + cn[j] = k2; + /* shift the right bound of the active submatrix */ + k2--; + } + /* the j-th column of the matrix A has been removed from the + active submatrix */ + /* remove the j-th column from the linked list */ + if (cs_prev[j] == 0) + cs_head = cs_next[j]; + else + cs_next[cs_prev[j]] = cs_next[j]; + if (cs_next[j] == 0) + /* nop */; + else + cs_prev[cs_next[j]] = cs_prev[j]; + /* go through non-zeros of the j-th columns and update active + lengths of the corresponding rows */ + t = mat(info, -j, ndx); + xassert(0 <= t && t <= m); + for (t = t; t >= 1; t--) + { i = ndx[t]; + xassert(1 <= i && i <= m); + /* the non-zero a[i,j] has left the active submatrix */ + len = rs_len[i]; + xassert(len >= 1); + /* remove the i-th row from the linked list of rows with + active length len */ + if (rs_prev[i] == 0) + rs_head[len] = rs_next[i]; + else + rs_next[rs_prev[i]] = rs_next[i]; + if (rs_next[i] == 0) + /* nop */; + else + rs_prev[rs_next[i]] = rs_prev[i]; + /* decrease the active length of the i-th row */ + rs_len[i] = --len; + /* return the i-th row to the corresponding linked list */ + rs_prev[i] = 0; + rs_next[i] = rs_head[len]; + if (rs_next[i] != 0) rs_prev[rs_next[i]] = i; + rs_head[len] = i; + } + } + /* other rows of the matrix A, which are still active, correspond + to rows k1, ..., m of the matrix B (in arbitrary order) */ + for (i = 1; i <= m; i++) if (rn[i] == 0) rn[i] = k1++; + /* but for columns this is not needed, because now the submatrix + B2 has no columns */ + for (j = 1; j <= n; j++) xassert(cn[j] != 0); + /* perform some optional checks */ + /* make sure that rn is a permutation of {1, ..., m} and cn is a + permutation of {1, ..., n} */ + rn_inv = rs_len; /* used as working array */ + for (ii = 1; ii <= m; ii++) rn_inv[ii] = 0; + for (i = 1; i <= m; i++) + { ii = rn[i]; + xassert(1 <= ii && ii <= m); + xassert(rn_inv[ii] == 0); + rn_inv[ii] = i; + } + cn_inv = rs_head; /* used as working array */ + for (jj = 1; jj <= n; jj++) cn_inv[jj] = 0; + for (j = 1; j <= n; j++) + { jj = cn[j]; + xassert(1 <= jj && jj <= n); + xassert(cn_inv[jj] == 0); + cn_inv[jj] = j; + } + /* make sure that the matrix B = P*A*Q really has the form, which + was declared */ + for (ii = 1; ii <= size; ii++) + { int diag = 0; + i = rn_inv[ii]; + t = mat(info, +i, ndx); + xassert(0 <= t && t <= n); + for (t = t; t >= 1; t--) + { j = ndx[t]; + xassert(1 <= j && j <= n); + jj = cn[j]; + if (jj <= size) xassert(jj <= ii); + if (jj == ii) + { xassert(!diag); + diag = 1; + } + } + xassert(diag); + } + /* free working arrays */ + xfree(ndx); + xfree(rs_len); + xfree(rs_head); + xfree(rs_prev); + xfree(rs_next); + xfree(cs_prev); + xfree(cs_next); + /* return to the calling program */ + return size; +} + +/*---------------------------------------------------------------------- +-- adv_basis - construct advanced initial LP basis. +-- +-- *Synopsis* +-- +-- #include "glpini.h" +-- void adv_basis(glp_prob *lp); +-- +-- *Description* +-- +-- The routine adv_basis constructs an advanced initial basis for an LP +-- problem object, which the parameter lp points to. +-- +-- In order to build the initial basis the routine does the following: +-- +-- 1) includes in the basis all non-fixed auxiliary variables; +-- +-- 2) includes in the basis as many as possible non-fixed structural +-- variables preserving triangular form of the basis matrix; +-- +-- 3) includes in the basis appropriate (fixed) auxiliary variables +-- in order to complete the basis. +-- +-- As a result the initial basis has minimum of fixed variables and the +-- corresponding basis matrix is triangular. */ + +static int mat(void *info, int k, int ndx[]) +{ /* this auxiliary routine returns the pattern of a given row or + a given column of the augmented constraint matrix A~ = (I|-A), + in which columns of fixed variables are implicitly cleared */ + LPX *lp = info; + int m = lpx_get_num_rows(lp); + int n = lpx_get_num_cols(lp); + int typx, i, j, lll, len = 0; + if (k > 0) + { /* the pattern of the i-th row is required */ + i = +k; + xassert(1 <= i && i <= m); +#if 0 /* 22/XII-2003 */ + /* if the auxiliary variable x[i] is non-fixed, include its + element (placed in the i-th column) in the pattern */ + lpx_get_row_bnds(lp, i, &typx, NULL, NULL); + if (typx != LPX_FX) ndx[++len] = i; + /* include in the pattern elements placed in columns, which + correspond to non-fixed structural varables */ + i_beg = aa_ptr[i]; + i_end = i_beg + aa_len[i] - 1; + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + { j = m + sv_ndx[i_ptr]; + lpx_get_col_bnds(lp, j-m, &typx, NULL, NULL); + if (typx != LPX_FX) ndx[++len] = j; + } +#else + lll = lpx_get_mat_row(lp, i, ndx, NULL); + for (k = 1; k <= lll; k++) + { lpx_get_col_bnds(lp, ndx[k], &typx, NULL, NULL); + if (typx != LPX_FX) ndx[++len] = m + ndx[k]; + } + lpx_get_row_bnds(lp, i, &typx, NULL, NULL); + if (typx != LPX_FX) ndx[++len] = i; +#endif + } + else + { /* the pattern of the j-th column is required */ + j = -k; + xassert(1 <= j && j <= m+n); + /* if the (auxiliary or structural) variable x[j] is fixed, + the pattern of its column is empty */ + if (j <= m) + lpx_get_row_bnds(lp, j, &typx, NULL, NULL); + else + lpx_get_col_bnds(lp, j-m, &typx, NULL, NULL); + if (typx != LPX_FX) + { if (j <= m) + { /* x[j] is non-fixed auxiliary variable */ + ndx[++len] = j; + } + else + { /* x[j] is non-fixed structural variables */ +#if 0 /* 22/XII-2003 */ + j_beg = aa_ptr[j]; + j_end = j_beg + aa_len[j] - 1; + for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) + ndx[++len] = sv_ndx[j_ptr]; +#else + len = lpx_get_mat_col(lp, j-m, ndx, NULL); +#endif + } + } + } + /* return the length of the row/column pattern */ + return len; +} + +static void adv_basis(glp_prob *lp) +{ int m = lpx_get_num_rows(lp); + int n = lpx_get_num_cols(lp); + int i, j, jj, k, size; + int *rn, *cn, *rn_inv, *cn_inv; + int typx, *tagx = xcalloc(1+m+n, sizeof(int)); + double lb, ub; + xprintf("Constructing initial basis...\n"); +#if 0 /* 13/V-2009 */ + if (m == 0) + xerror("glp_adv_basis: problem has no rows\n"); + if (n == 0) + xerror("glp_adv_basis: problem has no columns\n"); +#else + if (m == 0 || n == 0) + { glp_std_basis(lp); + return; + } +#endif + /* use the routine triang (see above) to find maximal triangular + part of the augmented constraint matrix A~ = (I|-A); in order + to prevent columns of fixed variables to be included in the + triangular part, such columns are implictly removed from the + matrix A~ by the routine adv_mat */ + rn = xcalloc(1+m, sizeof(int)); + cn = xcalloc(1+m+n, sizeof(int)); + size = triang(m, m+n, lp, mat, rn, cn); + if (lpx_get_int_parm(lp, LPX_K_MSGLEV) >= 3) + xprintf("Size of triangular part = %d\n", size); + /* the first size rows and columns of the matrix P*A~*Q (where + P and Q are permutation matrices defined by the arrays rn and + cn) form a lower triangular matrix; build the arrays (rn_inv + and cn_inv), which define the matrices inv(P) and inv(Q) */ + rn_inv = xcalloc(1+m, sizeof(int)); + cn_inv = xcalloc(1+m+n, sizeof(int)); + for (i = 1; i <= m; i++) rn_inv[rn[i]] = i; + for (j = 1; j <= m+n; j++) cn_inv[cn[j]] = j; + /* include the columns of the matrix A~, which correspond to the + first size columns of the matrix P*A~*Q, in the basis */ + for (k = 1; k <= m+n; k++) tagx[k] = -1; + for (jj = 1; jj <= size; jj++) + { j = cn_inv[jj]; + /* the j-th column of A~ is the jj-th column of P*A~*Q */ + tagx[j] = LPX_BS; + } + /* if size < m, we need to add appropriate columns of auxiliary + variables to the basis */ + for (jj = size + 1; jj <= m; jj++) + { /* the jj-th column of P*A~*Q should be replaced by the column + of the auxiliary variable, for which the only unity element + is placed in the position [jj,jj] */ + i = rn_inv[jj]; + /* the jj-th row of P*A~*Q is the i-th row of A~, but in the + i-th row of A~ the unity element belongs to the i-th column + of A~; therefore the disired column corresponds to the i-th + auxiliary variable (note that this column doesn't belong to + the triangular part found by the routine triang) */ + xassert(1 <= i && i <= m); + xassert(cn[i] > size); + tagx[i] = LPX_BS; + } + /* free working arrays */ + xfree(rn); + xfree(cn); + xfree(rn_inv); + xfree(cn_inv); + /* build tags of non-basic variables */ + for (k = 1; k <= m+n; k++) + { if (tagx[k] != LPX_BS) + { if (k <= m) + lpx_get_row_bnds(lp, k, &typx, &lb, &ub); + else + lpx_get_col_bnds(lp, k-m, &typx, &lb, &ub); + switch (typx) + { case LPX_FR: + tagx[k] = LPX_NF; break; + case LPX_LO: + tagx[k] = LPX_NL; break; + case LPX_UP: + tagx[k] = LPX_NU; break; + case LPX_DB: + tagx[k] = + (fabs(lb) <= fabs(ub) ? LPX_NL : LPX_NU); + break; + case LPX_FX: + tagx[k] = LPX_NS; break; + default: + xassert(typx != typx); + } + } + } + for (k = 1; k <= m+n; k++) + { if (k <= m) + lpx_set_row_stat(lp, k, tagx[k]); + else + lpx_set_col_stat(lp, k-m, tagx[k]); + } + xfree(tagx); + return; +} + +/*********************************************************************** +* NAME +* +* glp_adv_basis - construct advanced initial LP basis +* +* SYNOPSIS +* +* void glp_adv_basis(glp_prob *lp, int flags); +* +* DESCRIPTION +* +* The routine glp_adv_basis constructs an advanced initial basis for +* the specified problem object. +* +* The parameter flags is reserved for use in the future and must be +* specified as zero. */ + +void glp_adv_basis(glp_prob *lp, int flags) +{ if (flags != 0) + xerror("glp_adv_basis: flags = %d; invalid flags\n", flags); + if (lp->m == 0 || lp->n == 0) + glp_std_basis(lp); + else + adv_basis(lp); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpini02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpini02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,269 @@ +/* glpini02.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +struct var +{ /* structural variable */ + int j; + /* ordinal number */ + double q; + /* penalty value */ +}; + +static int fcmp(const void *ptr1, const void *ptr2) +{ /* this routine is passed to the qsort() function */ + struct var *col1 = (void *)ptr1, *col2 = (void *)ptr2; + if (col1->q < col2->q) return -1; + if (col1->q > col2->q) return +1; + return 0; +} + +static int get_column(glp_prob *lp, int j, int ind[], double val[]) +{ /* Bixby's algorithm assumes that the constraint matrix is scaled + such that the maximum absolute value in every non-zero row and + column is 1 */ + int k, len; + double big; + len = glp_get_mat_col(lp, j, ind, val); + big = 0.0; + for (k = 1; k <= len; k++) + if (big < fabs(val[k])) big = fabs(val[k]); + if (big == 0.0) big = 1.0; + for (k = 1; k <= len; k++) val[k] /= big; + return len; +} + +static void cpx_basis(glp_prob *lp) +{ /* main routine */ + struct var *C, *C2, *C3, *C4; + int m, n, i, j, jk, k, l, ll, t, n2, n3, n4, type, len, *I, *r, + *ind; + double alpha, gamma, cmax, temp, *v, *val; + xprintf("Constructing initial basis...\n"); + /* determine the number of rows and columns */ + m = glp_get_num_rows(lp); + n = glp_get_num_cols(lp); + /* allocate working arrays */ + C = xcalloc(1+n, sizeof(struct var)); + I = xcalloc(1+m, sizeof(int)); + r = xcalloc(1+m, sizeof(int)); + v = xcalloc(1+m, sizeof(double)); + ind = xcalloc(1+m, sizeof(int)); + val = xcalloc(1+m, sizeof(double)); + /* make all auxiliary variables non-basic */ + for (i = 1; i <= m; i++) + { if (glp_get_row_type(lp, i) != GLP_DB) + glp_set_row_stat(lp, i, GLP_NS); + else if (fabs(glp_get_row_lb(lp, i)) <= + fabs(glp_get_row_ub(lp, i))) + glp_set_row_stat(lp, i, GLP_NL); + else + glp_set_row_stat(lp, i, GLP_NU); + } + /* make all structural variables non-basic */ + for (j = 1; j <= n; j++) + { if (glp_get_col_type(lp, j) != GLP_DB) + glp_set_col_stat(lp, j, GLP_NS); + else if (fabs(glp_get_col_lb(lp, j)) <= + fabs(glp_get_col_ub(lp, j))) + glp_set_col_stat(lp, j, GLP_NL); + else + glp_set_col_stat(lp, j, GLP_NU); + } + /* C2 is a set of free structural variables */ + n2 = 0, C2 = C + 0; + for (j = 1; j <= n; j++) + { type = glp_get_col_type(lp, j); + if (type == GLP_FR) + { n2++; + C2[n2].j = j; + C2[n2].q = 0.0; + } + } + /* C3 is a set of structural variables having excatly one (lower + or upper) bound */ + n3 = 0, C3 = C2 + n2; + for (j = 1; j <= n; j++) + { type = glp_get_col_type(lp, j); + if (type == GLP_LO) + { n3++; + C3[n3].j = j; + C3[n3].q = + glp_get_col_lb(lp, j); + } + else if (type == GLP_UP) + { n3++; + C3[n3].j = j; + C3[n3].q = - glp_get_col_ub(lp, j); + } + } + /* C4 is a set of structural variables having both (lower and + upper) bounds */ + n4 = 0, C4 = C3 + n3; + for (j = 1; j <= n; j++) + { type = glp_get_col_type(lp, j); + if (type == GLP_DB) + { n4++; + C4[n4].j = j; + C4[n4].q = glp_get_col_lb(lp, j) - glp_get_col_ub(lp, j); + } + } + /* compute gamma = max{|c[j]|: 1 <= j <= n} */ + gamma = 0.0; + for (j = 1; j <= n; j++) + { temp = fabs(glp_get_obj_coef(lp, j)); + if (gamma < temp) gamma = temp; + } + /* compute cmax */ + cmax = (gamma == 0.0 ? 1.0 : 1000.0 * gamma); + /* compute final penalty for all structural variables within sets + C2, C3, and C4 */ + switch (glp_get_obj_dir(lp)) + { case GLP_MIN: temp = +1.0; break; + case GLP_MAX: temp = -1.0; break; + default: xassert(lp != lp); + } + for (k = 1; k <= n2+n3+n4; k++) + { j = C[k].j; + C[k].q += (temp * glp_get_obj_coef(lp, j)) / cmax; + } + /* sort structural variables within C2, C3, and C4 in ascending + order of penalty value */ + qsort(C2+1, n2, sizeof(struct var), fcmp); + for (k = 1; k < n2; k++) xassert(C2[k].q <= C2[k+1].q); + qsort(C3+1, n3, sizeof(struct var), fcmp); + for (k = 1; k < n3; k++) xassert(C3[k].q <= C3[k+1].q); + qsort(C4+1, n4, sizeof(struct var), fcmp); + for (k = 1; k < n4; k++) xassert(C4[k].q <= C4[k+1].q); + /*** STEP 1 ***/ + for (i = 1; i <= m; i++) + { type = glp_get_row_type(lp, i); + if (type != GLP_FX) + { /* row i is either free or inequality constraint */ + glp_set_row_stat(lp, i, GLP_BS); + I[i] = 1; + r[i] = 1; + } + else + { /* row i is equality constraint */ + I[i] = 0; + r[i] = 0; + } + v[i] = +DBL_MAX; + } + /*** STEP 2 ***/ + for (k = 1; k <= n2+n3+n4; k++) + { jk = C[k].j; + len = get_column(lp, jk, ind, val); + /* let alpha = max{|A[l,jk]|: r[l] = 0} and let l' be such + that alpha = |A[l',jk]| */ + alpha = 0.0, ll = 0; + for (t = 1; t <= len; t++) + { l = ind[t]; + if (r[l] == 0 && alpha < fabs(val[t])) + alpha = fabs(val[t]), ll = l; + } + if (alpha >= 0.99) + { /* B := B union {jk} */ + glp_set_col_stat(lp, jk, GLP_BS); + I[ll] = 1; + v[ll] = alpha; + /* r[l] := r[l] + 1 for all l such that |A[l,jk]| != 0 */ + for (t = 1; t <= len; t++) + { l = ind[t]; + if (val[t] != 0.0) r[l]++; + } + /* continue to the next k */ + continue; + } + /* if |A[l,jk]| > 0.01 * v[l] for some l, continue to the + next k */ + for (t = 1; t <= len; t++) + { l = ind[t]; + if (fabs(val[t]) > 0.01 * v[l]) break; + } + if (t <= len) continue; + /* otherwise, let alpha = max{|A[l,jk]|: I[l] = 0} and let l' + be such that alpha = |A[l',jk]| */ + alpha = 0.0, ll = 0; + for (t = 1; t <= len; t++) + { l = ind[t]; + if (I[l] == 0 && alpha < fabs(val[t])) + alpha = fabs(val[t]), ll = l; + } + /* if alpha = 0, continue to the next k */ + if (alpha == 0.0) continue; + /* B := B union {jk} */ + glp_set_col_stat(lp, jk, GLP_BS); + I[ll] = 1; + v[ll] = alpha; + /* r[l] := r[l] + 1 for all l such that |A[l,jk]| != 0 */ + for (t = 1; t <= len; t++) + { l = ind[t]; + if (val[t] != 0.0) r[l]++; + } + } + /*** STEP 3 ***/ + /* add an artificial variable (auxiliary variable for equality + constraint) to cover each remaining uncovered row */ + for (i = 1; i <= m; i++) + if (I[i] == 0) glp_set_row_stat(lp, i, GLP_BS); + /* free working arrays */ + xfree(C); + xfree(I); + xfree(r); + xfree(v); + xfree(ind); + xfree(val); + return; +} + +/*********************************************************************** +* NAME +* +* glp_cpx_basis - construct Bixby's initial LP basis +* +* SYNOPSIS +* +* void glp_cpx_basis(glp_prob *lp); +* +* DESCRIPTION +* +* The routine glp_cpx_basis constructs an advanced initial basis for +* the specified problem object. +* +* The routine is based on Bixby's algorithm described in the paper: +* +* Robert E. Bixby. Implementing the Simplex Method: The Initial Basis. +* ORSA Journal on Computing, Vol. 4, No. 3, 1992, pp. 267-84. */ + +void glp_cpx_basis(glp_prob *lp) +{ if (lp->m == 0 || lp->n == 0) + glp_std_basis(lp); + else + cpx_basis(lp); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,593 @@ +/* glpios.h (integer optimization suite) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPIOS_H +#define GLPIOS_H + +#define GLP_TREE_DEFINED +typedef struct glp_tree glp_tree; + +#include "glpapi.h" + +typedef struct IOSLOT IOSLOT; +typedef struct IOSNPD IOSNPD; +typedef struct IOSBND IOSBND; +typedef struct IOSTAT IOSTAT; +typedef struct IOSROW IOSROW; +typedef struct IOSAIJ IOSAIJ; +typedef struct IOSPOOL IOSPOOL; +typedef struct IOSCUT IOSCUT; + +struct glp_tree +{ /* branch-and-bound tree */ + int magic; + /* magic value used for debugging */ + DMP *pool; + /* memory pool to store all IOS components */ + int n; + /* number of columns (variables) */ + /*--------------------------------------------------------------*/ + /* problem components corresponding to the original MIP and its + LP relaxation (used to restore the original problem object on + exit from the solver) */ + int orig_m; + /* number of rows */ + unsigned char *orig_type; /* uchar orig_type[1+orig_m+n]; */ + /* types of all variables */ + double *orig_lb; /* double orig_lb[1+orig_m+n]; */ + /* lower bounds of all variables */ + double *orig_ub; /* double orig_ub[1+orig_m+n]; */ + /* upper bounds of all variables */ + unsigned char *orig_stat; /* uchar orig_stat[1+orig_m+n]; */ + /* statuses of all variables */ + double *orig_prim; /* double orig_prim[1+orig_m+n]; */ + /* primal values of all variables */ + double *orig_dual; /* double orig_dual[1+orig_m+n]; */ + /* dual values of all variables */ + double orig_obj; + /* optimal objective value for LP relaxation */ + /*--------------------------------------------------------------*/ + /* branch-and-bound tree */ + int nslots; + /* length of the array of slots (enlarged automatically) */ + int avail; + /* index of the first free slot; 0 means all slots are in use */ + IOSLOT *slot; /* IOSLOT slot[1+nslots]; */ + /* array of slots: + slot[0] is not used; + slot[p], 1 <= p <= nslots, either contains a pointer to some + node of the branch-and-bound tree, in which case p is used on + API level as the reference number of corresponding subproblem, + or is free; all free slots are linked into single linked list; + slot[1] always contains a pointer to the root node (it is free + only if the tree is empty) */ + IOSNPD *head; + /* pointer to the head of the active list */ + IOSNPD *tail; + /* pointer to the tail of the active list */ + /* the active list is a doubly linked list of active subproblems + which correspond to leaves of the tree; all subproblems in the + active list are ordered chronologically (each a new subproblem + is always added to the tail of the list) */ + int a_cnt; + /* current number of active nodes (including the current one) */ + int n_cnt; + /* current number of all (active and inactive) nodes */ + int t_cnt; + /* total number of nodes including those which have been already + removed from the tree; this count is increased by one whenever + a new node is created and never decreased */ + /*--------------------------------------------------------------*/ + /* problem components corresponding to the root subproblem */ + int root_m; + /* number of rows */ + unsigned char *root_type; /* uchar root_type[1+root_m+n]; */ + /* types of all variables */ + double *root_lb; /* double root_lb[1+root_m+n]; */ + /* lower bounds of all variables */ + double *root_ub; /* double root_ub[1+root_m+n]; */ + /* upper bounds of all variables */ + unsigned char *root_stat; /* uchar root_stat[1+root_m+n]; */ + /* statuses of all variables */ + /*--------------------------------------------------------------*/ + /* current subproblem and its LP relaxation */ + IOSNPD *curr; + /* pointer to the current subproblem (which can be only active); + NULL means the current subproblem does not exist */ + glp_prob *mip; + /* original problem object passed to the solver; if the current + subproblem exists, its LP segment corresponds to LP relaxation + of the current subproblem; if the current subproblem does not + exist, its LP segment corresponds to LP relaxation of the root + subproblem (note that the root subproblem may differ from the + original MIP, because it may be preprocessed and/or may have + additional rows) */ + unsigned char *non_int; /* uchar non_int[1+n]; */ + /* these column flags are set each time when LP relaxation of the + current subproblem has been solved; + non_int[0] is not used; + non_int[j], 1 <= j <= n, is j-th column flag; if this flag is + set, corresponding variable is required to be integer, but its + value in basic solution is fractional */ + /*--------------------------------------------------------------*/ + /* problem components corresponding to the parent (predecessor) + subproblem for the current subproblem; used to inspect changes + on freezing the current subproblem */ + int pred_m; + /* number of rows */ + int pred_max; + /* length of the following four arrays (enlarged automatically), + pred_max >= pred_m + n */ + unsigned char *pred_type; /* uchar pred_type[1+pred_m+n]; */ + /* types of all variables */ + double *pred_lb; /* double pred_lb[1+pred_m+n]; */ + /* lower bounds of all variables */ + double *pred_ub; /* double pred_ub[1+pred_m+n]; */ + /* upper bounds of all variables */ + unsigned char *pred_stat; /* uchar pred_stat[1+pred_m+n]; */ + /* statuses of all variables */ + /****************************************************************/ + /* built-in cut generators segment */ + IOSPOOL *local; + /* local cut pool */ + void *mir_gen; + /* pointer to working area used by the MIR cut generator */ + void *clq_gen; + /* pointer to working area used by the clique cut generator */ + /*--------------------------------------------------------------*/ + void *pcost; + /* pointer to working area used on pseudocost branching */ + int *iwrk; /* int iwrk[1+n]; */ + /* working array */ + double *dwrk; /* double dwrk[1+n]; */ + /* working array */ + /*--------------------------------------------------------------*/ + /* control parameters and statistics */ + const glp_iocp *parm; + /* copy of control parameters passed to the solver */ + glp_long tm_beg; + /* starting time of the search, in seconds; the total time of the + search is the difference between xtime() and tm_beg */ + glp_long tm_lag; + /* the most recent time, in seconds, at which the progress of the + the search was displayed */ + int sol_cnt; + /* number of integer feasible solutions found */ + /*--------------------------------------------------------------*/ + /* advanced solver interface */ + int reason; + /* flag indicating the reason why the callback routine is being + called (see glpk.h) */ + int stop; + /* flag indicating that the callback routine requires premature + termination of the search */ + int next_p; + /* reference number of active subproblem selected to continue + the search; 0 means no subproblem has been selected */ + int reopt; + /* flag indicating that the current LP relaxation needs to be + re-optimized */ + int reinv; + /* flag indicating that some (non-active) rows were removed from + the current LP relaxation, so if there no new rows appear, the + basis must be re-factorized */ + int br_var; + /* the number of variable chosen to branch on */ + int br_sel; + /* flag indicating which branch (subproblem) is suggested to be + selected to continue the search: + GLP_DN_BRNCH - select down-branch + GLP_UP_BRNCH - select up-branch + GLP_NO_BRNCH - use general selection technique */ + int child; + /* subproblem reference number corresponding to br_sel */ +}; + +struct IOSLOT +{ /* node subproblem slot */ + IOSNPD *node; + /* pointer to subproblem descriptor; NULL means free slot */ + int next; + /* index of another free slot (only if this slot is free) */ +}; + +struct IOSNPD +{ /* node subproblem descriptor */ + int p; + /* subproblem reference number (it is the index to corresponding + slot, i.e. slot[p] points to this descriptor) */ + IOSNPD *up; + /* pointer to the parent subproblem; NULL means this node is the + root of the tree, in which case p = 1 */ + int level; + /* node level (the root node has level 0) */ + int count; + /* if count = 0, this subproblem is active; if count > 0, this + subproblem is inactive, in which case count is the number of + its child subproblems */ + /* the following three linked lists are destroyed on reviving and + built anew on freezing the subproblem: */ + IOSBND *b_ptr; + /* linked list of rows and columns of the parent subproblem whose + types and bounds were changed */ + IOSTAT *s_ptr; + /* linked list of rows and columns of the parent subproblem whose + statuses were changed */ + IOSROW *r_ptr; + /* linked list of rows (cuts) added to the parent subproblem */ + int solved; + /* how many times LP relaxation of this subproblem was solved; + for inactive subproblem this count is always non-zero; + for active subproblem, which is not current, this count may be + non-zero, if the subproblem was temporarily suspended */ + double lp_obj; + /* optimal objective value to LP relaxation of this subproblem; + on creating a subproblem this value is inherited from its + parent; for the root subproblem, which has no parent, this + value is initially set to -DBL_MAX (minimization) or +DBL_MAX + (maximization); each time the subproblem is re-optimized, this + value is appropriately changed */ + double bound; + /* local lower (minimization) or upper (maximization) bound for + integer optimal solution to *this* subproblem; this bound is + local in the sense that only subproblems in the subtree rooted + at this node cannot have better integer feasible solutions; + on creating a subproblem its local bound is inherited from its + parent and then can be made stronger (never weaker); for the + root subproblem its local bound is initially set to -DBL_MAX + (minimization) or +DBL_MAX (maximization) and then improved as + the root LP relaxation has been solved */ + /* the following two quantities are defined only if LP relaxation + of this subproblem was solved at least once (solved > 0): */ + int ii_cnt; + /* number of integer variables whose value in optimal solution to + LP relaxation of this subproblem is fractional */ + double ii_sum; + /* sum of integer infeasibilities */ +#if 1 /* 30/XI-2009 */ + int changed; + /* how many times this subproblem was re-formulated (by adding + cutting plane constraints) */ +#endif + int br_var; + /* ordinal number of branching variable, 1 <= br_var <= n, used + to split this subproblem; 0 means that either this subproblem + is active or branching was made on a constraint */ + double br_val; + /* (fractional) value of branching variable in optimal solution + to final LP relaxation of this subproblem */ + void *data; /* char data[tree->cb_size]; */ + /* pointer to the application-specific data */ + IOSNPD *temp; + /* working pointer used by some routines */ + IOSNPD *prev; + /* pointer to previous subproblem in the active list */ + IOSNPD *next; + /* pointer to next subproblem in the active list */ +}; + +struct IOSBND +{ /* bounds change entry */ + int k; + /* ordinal number of corresponding row (1 <= k <= m) or column + (m+1 <= k <= m+n), where m and n are the number of rows and + columns, resp., in the parent subproblem */ + unsigned char type; + /* new type */ + double lb; + /* new lower bound */ + double ub; + /* new upper bound */ + IOSBND *next; + /* pointer to next entry for the same subproblem */ +}; + +struct IOSTAT +{ /* status change entry */ + int k; + /* ordinal number of corresponding row (1 <= k <= m) or column + (m+1 <= k <= m+n), where m and n are the number of rows and + columns, resp., in the parent subproblem */ + unsigned char stat; + /* new status */ + IOSTAT *next; + /* pointer to next entry for the same subproblem */ +}; + +struct IOSROW +{ /* row (constraint) addition entry */ + char *name; + /* row name or NULL */ + unsigned char origin; + /* row origin flag (see glp_attr.origin) */ + unsigned char klass; + /* row class descriptor (see glp_attr.klass) */ + unsigned char type; + /* row type (GLP_LO, GLP_UP, etc.) */ + double lb; + /* row lower bound */ + double ub; + /* row upper bound */ + IOSAIJ *ptr; + /* pointer to the row coefficient list */ + double rii; + /* row scale factor */ + unsigned char stat; + /* row status (GLP_BS, GLP_NL, etc.) */ + IOSROW *next; + /* pointer to next entry for the same subproblem */ +}; + +struct IOSAIJ +{ /* constraint coefficient */ + int j; + /* variable (column) number, 1 <= j <= n */ + double val; + /* non-zero coefficient value */ + IOSAIJ *next; + /* pointer to next coefficient for the same row */ +}; + +struct IOSPOOL +{ /* cut pool */ + int size; + /* pool size = number of cuts in the pool */ + IOSCUT *head; + /* pointer to the first cut */ + IOSCUT *tail; + /* pointer to the last cut */ + int ord; + /* ordinal number of the current cut, 1 <= ord <= size */ + IOSCUT *curr; + /* pointer to the current cut */ +}; + +struct IOSCUT +{ /* cut (cutting plane constraint) */ + char *name; + /* cut name or NULL */ + unsigned char klass; + /* cut class descriptor (see glp_attr.klass) */ + IOSAIJ *ptr; + /* pointer to the cut coefficient list */ + unsigned char type; + /* cut type: + GLP_LO: sum a[j] * x[j] >= b + GLP_UP: sum a[j] * x[j] <= b + GLP_FX: sum a[j] * x[j] = b */ + double rhs; + /* cut right-hand side */ + IOSCUT *prev; + /* pointer to previous cut */ + IOSCUT *next; + /* pointer to next cut */ +}; + +#define ios_create_tree _glp_ios_create_tree +glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm); +/* create branch-and-bound tree */ + +#define ios_revive_node _glp_ios_revive_node +void ios_revive_node(glp_tree *tree, int p); +/* revive specified subproblem */ + +#define ios_freeze_node _glp_ios_freeze_node +void ios_freeze_node(glp_tree *tree); +/* freeze current subproblem */ + +#define ios_clone_node _glp_ios_clone_node +void ios_clone_node(glp_tree *tree, int p, int nnn, int ref[]); +/* clone specified subproblem */ + +#define ios_delete_node _glp_ios_delete_node +void ios_delete_node(glp_tree *tree, int p); +/* delete specified subproblem */ + +#define ios_delete_tree _glp_ios_delete_tree +void ios_delete_tree(glp_tree *tree); +/* delete branch-and-bound tree */ + +#define ios_eval_degrad _glp_ios_eval_degrad +void ios_eval_degrad(glp_tree *tree, int j, double *dn, double *up); +/* estimate obj. degrad. for down- and up-branches */ + +#define ios_round_bound _glp_ios_round_bound +double ios_round_bound(glp_tree *tree, double bound); +/* improve local bound by rounding */ + +#define ios_is_hopeful _glp_ios_is_hopeful +int ios_is_hopeful(glp_tree *tree, double bound); +/* check if subproblem is hopeful */ + +#define ios_best_node _glp_ios_best_node +int ios_best_node(glp_tree *tree); +/* find active node with best local bound */ + +#define ios_relative_gap _glp_ios_relative_gap +double ios_relative_gap(glp_tree *tree); +/* compute relative mip gap */ + +#define ios_solve_node _glp_ios_solve_node +int ios_solve_node(glp_tree *tree); +/* solve LP relaxation of current subproblem */ + +#define ios_create_pool _glp_ios_create_pool +IOSPOOL *ios_create_pool(glp_tree *tree); +/* create cut pool */ + +#define ios_add_row _glp_ios_add_row +int ios_add_row(glp_tree *tree, IOSPOOL *pool, + const char *name, int klass, int flags, int len, const int ind[], + const double val[], int type, double rhs); +/* add row (constraint) to the cut pool */ + +#define ios_find_row _glp_ios_find_row +IOSCUT *ios_find_row(IOSPOOL *pool, int i); +/* find row (constraint) in the cut pool */ + +#define ios_del_row _glp_ios_del_row +void ios_del_row(glp_tree *tree, IOSPOOL *pool, int i); +/* remove row (constraint) from the cut pool */ + +#define ios_clear_pool _glp_ios_clear_pool +void ios_clear_pool(glp_tree *tree, IOSPOOL *pool); +/* remove all rows (constraints) from the cut pool */ + +#define ios_delete_pool _glp_ios_delete_pool +void ios_delete_pool(glp_tree *tree, IOSPOOL *pool); +/* delete cut pool */ + +#define ios_preprocess_node _glp_ios_preprocess_node +int ios_preprocess_node(glp_tree *tree, int max_pass); +/* preprocess current subproblem */ + +#define ios_driver _glp_ios_driver +int ios_driver(glp_tree *tree); +/* branch-and-bound driver */ + +/**********************************************************************/ + +typedef struct IOSVEC IOSVEC; + +struct IOSVEC +{ /* sparse vector v = (v[j]) */ + int n; + /* dimension, n >= 0 */ + int nnz; + /* number of non-zero components, 0 <= nnz <= n */ + int *pos; /* int pos[1+n]; */ + /* pos[j] = k, 1 <= j <= n, is position of (non-zero) v[j] in the + arrays ind and val, where 1 <= k <= nnz; pos[j] = 0 means that + v[j] is structural zero */ + int *ind; /* int ind[1+n]; */ + /* ind[k] = j, 1 <= k <= nnz, is index of v[j] */ + double *val; /* double val[1+n]; */ + /* val[k], 1 <= k <= nnz, is a numeric value of v[j] */ +}; + +#define ios_create_vec _glp_ios_create_vec +IOSVEC *ios_create_vec(int n); +/* create sparse vector */ + +#define ios_check_vec _glp_ios_check_vec +void ios_check_vec(IOSVEC *v); +/* check that sparse vector has correct representation */ + +#define ios_get_vj _glp_ios_get_vj +double ios_get_vj(IOSVEC *v, int j); +/* retrieve component of sparse vector */ + +#define ios_set_vj _glp_ios_set_vj +void ios_set_vj(IOSVEC *v, int j, double val); +/* set/change component of sparse vector */ + +#define ios_clear_vec _glp_ios_clear_vec +void ios_clear_vec(IOSVEC *v); +/* set all components of sparse vector to zero */ + +#define ios_clean_vec _glp_ios_clean_vec +void ios_clean_vec(IOSVEC *v, double eps); +/* remove zero or small components from sparse vector */ + +#define ios_copy_vec _glp_ios_copy_vec +void ios_copy_vec(IOSVEC *x, IOSVEC *y); +/* copy sparse vector (x := y) */ + +#define ios_linear_comb _glp_ios_linear_comb +void ios_linear_comb(IOSVEC *x, double a, IOSVEC *y); +/* compute linear combination (x := x + a * y) */ + +#define ios_delete_vec _glp_ios_delete_vec +void ios_delete_vec(IOSVEC *v); +/* delete sparse vector */ + +/**********************************************************************/ + +#define ios_gmi_gen _glp_ios_gmi_gen +void ios_gmi_gen(glp_tree *tree); +/* generate Gomory's mixed integer cuts */ + +#define ios_mir_init _glp_ios_mir_init +void *ios_mir_init(glp_tree *tree); +/* initialize MIR cut generator */ + +#define ios_mir_gen _glp_ios_mir_gen +void ios_mir_gen(glp_tree *tree, void *gen); +/* generate MIR cuts */ + +#define ios_mir_term _glp_ios_mir_term +void ios_mir_term(void *gen); +/* terminate MIR cut generator */ + +#define ios_cov_gen _glp_ios_cov_gen +void ios_cov_gen(glp_tree *tree); +/* generate mixed cover cuts */ + +#define ios_clq_init _glp_ios_clq_init +void *ios_clq_init(glp_tree *tree); +/* initialize clique cut generator */ + +#define ios_clq_gen _glp_ios_clq_gen +void ios_clq_gen(glp_tree *tree, void *gen); +/* generate clique cuts */ + +#define ios_clq_term _glp_ios_clq_term +void ios_clq_term(void *gen); +/* terminate clique cut generator */ + +#define ios_pcost_init _glp_ios_pcost_init +void *ios_pcost_init(glp_tree *tree); +/* initialize working data used on pseudocost branching */ + +#define ios_pcost_branch _glp_ios_pcost_branch +int ios_pcost_branch(glp_tree *T, int *next); +/* choose branching variable with pseudocost branching */ + +#define ios_pcost_update _glp_ios_pcost_update +void ios_pcost_update(glp_tree *tree); +/* update history information for pseudocost branching */ + +#define ios_pcost_free _glp_ios_pcost_free +void ios_pcost_free(glp_tree *tree); +/* free working area used on pseudocost branching */ + +#define ios_feas_pump _glp_ios_feas_pump +void ios_feas_pump(glp_tree *T); +/* feasibility pump heuristic */ + +#define ios_process_cuts _glp_ios_process_cuts +void ios_process_cuts(glp_tree *T); +/* process cuts stored in the local cut pool */ + +#define ios_choose_node _glp_ios_choose_node +int ios_choose_node(glp_tree *T); +/* select subproblem to continue the search */ + +#define ios_choose_var _glp_ios_choose_var +int ios_choose_var(glp_tree *T, int *next); +/* select variable to branch on */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1611 @@ +/* glpios01.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* NAME +* +* ios_create_tree - create branch-and-bound tree +* +* SYNOPSIS +* +* #include "glpios.h" +* glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm); +* +* DESCRIPTION +* +* The routine ios_create_tree creates the branch-and-bound tree. +* +* Being created the tree consists of the only root subproblem whose +* reference number is 1. Note that initially the root subproblem is in +* frozen state and therefore needs to be revived. +* +* RETURNS +* +* The routine returns a pointer to the tree created. */ + +static IOSNPD *new_node(glp_tree *tree, IOSNPD *parent); + +glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm) +{ int m = mip->m; + int n = mip->n; + glp_tree *tree; + int i, j; + xassert(mip->tree == NULL); + mip->tree = tree = xmalloc(sizeof(glp_tree)); + tree->pool = dmp_create_pool(); + tree->n = n; + /* save original problem components */ + tree->orig_m = m; + tree->orig_type = xcalloc(1+m+n, sizeof(char)); + tree->orig_lb = xcalloc(1+m+n, sizeof(double)); + tree->orig_ub = xcalloc(1+m+n, sizeof(double)); + tree->orig_stat = xcalloc(1+m+n, sizeof(char)); + tree->orig_prim = xcalloc(1+m+n, sizeof(double)); + tree->orig_dual = xcalloc(1+m+n, sizeof(double)); + for (i = 1; i <= m; i++) + { GLPROW *row = mip->row[i]; + tree->orig_type[i] = (char)row->type; + tree->orig_lb[i] = row->lb; + tree->orig_ub[i] = row->ub; + tree->orig_stat[i] = (char)row->stat; + tree->orig_prim[i] = row->prim; + tree->orig_dual[i] = row->dual; + } + for (j = 1; j <= n; j++) + { GLPCOL *col = mip->col[j]; + tree->orig_type[m+j] = (char)col->type; + tree->orig_lb[m+j] = col->lb; + tree->orig_ub[m+j] = col->ub; + tree->orig_stat[m+j] = (char)col->stat; + tree->orig_prim[m+j] = col->prim; + tree->orig_dual[m+j] = col->dual; + } + tree->orig_obj = mip->obj_val; + /* initialize the branch-and-bound tree */ + tree->nslots = 0; + tree->avail = 0; + tree->slot = NULL; + tree->head = tree->tail = NULL; + tree->a_cnt = tree->n_cnt = tree->t_cnt = 0; + /* the root subproblem is not solved yet, so its final components + are unknown so far */ + tree->root_m = 0; + tree->root_type = NULL; + tree->root_lb = tree->root_ub = NULL; + tree->root_stat = NULL; + /* the current subproblem does not exist yet */ + tree->curr = NULL; + tree->mip = mip; + /*tree->solved = 0;*/ + tree->non_int = xcalloc(1+n, sizeof(char)); + memset(&tree->non_int[1], 0, n); + /* arrays to save parent subproblem components will be allocated + later */ + tree->pred_m = tree->pred_max = 0; + tree->pred_type = NULL; + tree->pred_lb = tree->pred_ub = NULL; + tree->pred_stat = NULL; + /* cut generator */ + tree->local = ios_create_pool(tree); + /*tree->first_attempt = 1;*/ + /*tree->max_added_cuts = 0;*/ + /*tree->min_eff = 0.0;*/ + /*tree->miss = 0;*/ + /*tree->just_selected = 0;*/ + tree->mir_gen = NULL; + tree->clq_gen = NULL; + /*tree->round = 0;*/ +#if 0 + /* create the conflict graph */ + tree->n_ref = xcalloc(1+n, sizeof(int)); + memset(&tree->n_ref[1], 0, n * sizeof(int)); + tree->c_ref = xcalloc(1+n, sizeof(int)); + memset(&tree->c_ref[1], 0, n * sizeof(int)); + tree->g = scg_create_graph(0); + tree->j_ref = xcalloc(1+tree->g->n_max, sizeof(int)); +#endif + /* pseudocost branching */ + tree->pcost = NULL; + tree->iwrk = xcalloc(1+n, sizeof(int)); + tree->dwrk = xcalloc(1+n, sizeof(double)); + /* initialize control parameters */ + tree->parm = parm; + tree->tm_beg = xtime(); + tree->tm_lag = xlset(0); + tree->sol_cnt = 0; + /* initialize advanced solver interface */ + tree->reason = 0; + tree->reopt = 0; + tree->reinv = 0; + tree->br_var = 0; + tree->br_sel = 0; + tree->child = 0; + tree->next_p = 0; + /*tree->btrack = NULL;*/ + tree->stop = 0; + /* create the root subproblem, which initially is identical to + the original MIP */ + new_node(tree, NULL); + return tree; +} + +/*********************************************************************** +* NAME +* +* ios_revive_node - revive specified subproblem +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_revive_node(glp_tree *tree, int p); +* +* DESCRIPTION +* +* The routine ios_revive_node revives the specified subproblem, whose +* reference number is p, and thereby makes it the current subproblem. +* Note that the specified subproblem must be active. Besides, if the +* current subproblem already exists, it must be frozen before reviving +* another subproblem. */ + +void ios_revive_node(glp_tree *tree, int p) +{ glp_prob *mip = tree->mip; + IOSNPD *node, *root; + /* obtain pointer to the specified subproblem */ + xassert(1 <= p && p <= tree->nslots); + node = tree->slot[p].node; + xassert(node != NULL); + /* the specified subproblem must be active */ + xassert(node->count == 0); + /* the current subproblem must not exist */ + xassert(tree->curr == NULL); + /* the specified subproblem becomes current */ + tree->curr = node; + /*tree->solved = 0;*/ + /* obtain pointer to the root subproblem */ + root = tree->slot[1].node; + xassert(root != NULL); + /* at this point problem object components correspond to the root + subproblem, so if the root subproblem should be revived, there + is nothing more to do */ + if (node == root) goto done; + xassert(mip->m == tree->root_m); + /* build path from the root to the current node */ + node->temp = NULL; + for (node = node; node != NULL; node = node->up) + { if (node->up == NULL) + xassert(node == root); + else + node->up->temp = node; + } + /* go down from the root to the current node and make necessary + changes to restore components of the current subproblem */ + for (node = root; node != NULL; node = node->temp) + { int m = mip->m; + int n = mip->n; + /* if the current node is reached, the problem object at this + point corresponds to its parent, so save attributes of rows + and columns for the parent subproblem */ + if (node->temp == NULL) + { int i, j; + tree->pred_m = m; + /* allocate/reallocate arrays, if necessary */ + if (tree->pred_max < m + n) + { int new_size = m + n + 100; + if (tree->pred_type != NULL) xfree(tree->pred_type); + if (tree->pred_lb != NULL) xfree(tree->pred_lb); + if (tree->pred_ub != NULL) xfree(tree->pred_ub); + if (tree->pred_stat != NULL) xfree(tree->pred_stat); + tree->pred_max = new_size; + tree->pred_type = xcalloc(1+new_size, sizeof(char)); + tree->pred_lb = xcalloc(1+new_size, sizeof(double)); + tree->pred_ub = xcalloc(1+new_size, sizeof(double)); + tree->pred_stat = xcalloc(1+new_size, sizeof(char)); + } + /* save row attributes */ + for (i = 1; i <= m; i++) + { GLPROW *row = mip->row[i]; + tree->pred_type[i] = (char)row->type; + tree->pred_lb[i] = row->lb; + tree->pred_ub[i] = row->ub; + tree->pred_stat[i] = (char)row->stat; + } + /* save column attributes */ + for (j = 1; j <= n; j++) + { GLPCOL *col = mip->col[j]; + tree->pred_type[mip->m+j] = (char)col->type; + tree->pred_lb[mip->m+j] = col->lb; + tree->pred_ub[mip->m+j] = col->ub; + tree->pred_stat[mip->m+j] = (char)col->stat; + } + } + /* change bounds of rows and columns */ + { IOSBND *b; + for (b = node->b_ptr; b != NULL; b = b->next) + { if (b->k <= m) + glp_set_row_bnds(mip, b->k, b->type, b->lb, b->ub); + else + glp_set_col_bnds(mip, b->k-m, b->type, b->lb, b->ub); + } + } + /* change statuses of rows and columns */ + { IOSTAT *s; + for (s = node->s_ptr; s != NULL; s = s->next) + { if (s->k <= m) + glp_set_row_stat(mip, s->k, s->stat); + else + glp_set_col_stat(mip, s->k-m, s->stat); + } + } + /* add new rows */ + if (node->r_ptr != NULL) + { IOSROW *r; + IOSAIJ *a; + int i, len, *ind; + double *val; + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + for (r = node->r_ptr; r != NULL; r = r->next) + { i = glp_add_rows(mip, 1); + glp_set_row_name(mip, i, r->name); +#if 1 /* 20/IX-2008 */ + xassert(mip->row[i]->level == 0); + mip->row[i]->level = node->level; + mip->row[i]->origin = r->origin; + mip->row[i]->klass = r->klass; +#endif + glp_set_row_bnds(mip, i, r->type, r->lb, r->ub); + len = 0; + for (a = r->ptr; a != NULL; a = a->next) + len++, ind[len] = a->j, val[len] = a->val; + glp_set_mat_row(mip, i, len, ind, val); + glp_set_rii(mip, i, r->rii); + glp_set_row_stat(mip, i, r->stat); + } + xfree(ind); + xfree(val); + } +#if 0 + /* add new edges to the conflict graph */ + /* add new cliques to the conflict graph */ + /* (not implemented yet) */ + xassert(node->own_nn == 0); + xassert(node->own_nc == 0); + xassert(node->e_ptr == NULL); +#endif + } + /* the specified subproblem has been revived */ + node = tree->curr; + /* delete its bound change list */ + while (node->b_ptr != NULL) + { IOSBND *b; + b = node->b_ptr; + node->b_ptr = b->next; + dmp_free_atom(tree->pool, b, sizeof(IOSBND)); + } + /* delete its status change list */ + while (node->s_ptr != NULL) + { IOSTAT *s; + s = node->s_ptr; + node->s_ptr = s->next; + dmp_free_atom(tree->pool, s, sizeof(IOSTAT)); + } +#if 1 /* 20/XI-2009 */ + /* delete its row addition list (additional rows may appear, for + example, due to branching on GUB constraints */ + while (node->r_ptr != NULL) + { IOSROW *r; + r = node->r_ptr; + node->r_ptr = r->next; + xassert(r->name == NULL); + while (r->ptr != NULL) + { IOSAIJ *a; + a = r->ptr; + r->ptr = a->next; + dmp_free_atom(tree->pool, a, sizeof(IOSAIJ)); + } + dmp_free_atom(tree->pool, r, sizeof(IOSROW)); + } +#endif +done: return; +} + +/*********************************************************************** +* NAME +* +* ios_freeze_node - freeze current subproblem +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_freeze_node(glp_tree *tree); +* +* DESCRIPTION +* +* The routine ios_freeze_node freezes the current subproblem. */ + +void ios_freeze_node(glp_tree *tree) +{ glp_prob *mip = tree->mip; + int m = mip->m; + int n = mip->n; + IOSNPD *node; + /* obtain pointer to the current subproblem */ + node = tree->curr; + xassert(node != NULL); + if (node->up == NULL) + { /* freeze the root subproblem */ + int k; + xassert(node->p == 1); + xassert(tree->root_m == 0); + xassert(tree->root_type == NULL); + xassert(tree->root_lb == NULL); + xassert(tree->root_ub == NULL); + xassert(tree->root_stat == NULL); + tree->root_m = m; + tree->root_type = xcalloc(1+m+n, sizeof(char)); + tree->root_lb = xcalloc(1+m+n, sizeof(double)); + tree->root_ub = xcalloc(1+m+n, sizeof(double)); + tree->root_stat = xcalloc(1+m+n, sizeof(char)); + for (k = 1; k <= m+n; k++) + { if (k <= m) + { GLPROW *row = mip->row[k]; + tree->root_type[k] = (char)row->type; + tree->root_lb[k] = row->lb; + tree->root_ub[k] = row->ub; + tree->root_stat[k] = (char)row->stat; + } + else + { GLPCOL *col = mip->col[k-m]; + tree->root_type[k] = (char)col->type; + tree->root_lb[k] = col->lb; + tree->root_ub[k] = col->ub; + tree->root_stat[k] = (char)col->stat; + } + } + } + else + { /* freeze non-root subproblem */ + int root_m = tree->root_m; + int pred_m = tree->pred_m; + int i, j, k; + xassert(pred_m <= m); + /* build change lists for rows and columns which exist in the + parent subproblem */ + xassert(node->b_ptr == NULL); + xassert(node->s_ptr == NULL); + for (k = 1; k <= pred_m + n; k++) + { int pred_type, pred_stat, type, stat; + double pred_lb, pred_ub, lb, ub; + /* determine attributes in the parent subproblem */ + pred_type = tree->pred_type[k]; + pred_lb = tree->pred_lb[k]; + pred_ub = tree->pred_ub[k]; + pred_stat = tree->pred_stat[k]; + /* determine attributes in the current subproblem */ + if (k <= pred_m) + { GLPROW *row = mip->row[k]; + type = row->type; + lb = row->lb; + ub = row->ub; + stat = row->stat; + } + else + { GLPCOL *col = mip->col[k - pred_m]; + type = col->type; + lb = col->lb; + ub = col->ub; + stat = col->stat; + } + /* save type and bounds of a row/column, if changed */ + if (!(pred_type == type && pred_lb == lb && pred_ub == ub)) + { IOSBND *b; + b = dmp_get_atom(tree->pool, sizeof(IOSBND)); + b->k = k; + b->type = (unsigned char)type; + b->lb = lb; + b->ub = ub; + b->next = node->b_ptr; + node->b_ptr = b; + } + /* save status of a row/column, if changed */ + if (pred_stat != stat) + { IOSTAT *s; + s = dmp_get_atom(tree->pool, sizeof(IOSTAT)); + s->k = k; + s->stat = (unsigned char)stat; + s->next = node->s_ptr; + node->s_ptr = s; + } + } + /* save new rows added to the current subproblem */ + xassert(node->r_ptr == NULL); + if (pred_m < m) + { int i, len, *ind; + double *val; + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + for (i = m; i > pred_m; i--) + { GLPROW *row = mip->row[i]; + IOSROW *r; + const char *name; + r = dmp_get_atom(tree->pool, sizeof(IOSROW)); + name = glp_get_row_name(mip, i); + if (name == NULL) + r->name = NULL; + else + { r->name = dmp_get_atom(tree->pool, strlen(name)+1); + strcpy(r->name, name); + } +#if 1 /* 20/IX-2008 */ + r->origin = row->origin; + r->klass = row->klass; +#endif + r->type = (unsigned char)row->type; + r->lb = row->lb; + r->ub = row->ub; + r->ptr = NULL; + len = glp_get_mat_row(mip, i, ind, val); + for (k = 1; k <= len; k++) + { IOSAIJ *a; + a = dmp_get_atom(tree->pool, sizeof(IOSAIJ)); + a->j = ind[k]; + a->val = val[k]; + a->next = r->ptr; + r->ptr = a; + } + r->rii = row->rii; + r->stat = (unsigned char)row->stat; + r->next = node->r_ptr; + node->r_ptr = r; + } + xfree(ind); + xfree(val); + } + /* remove all rows missing in the root subproblem */ + if (m != root_m) + { int nrs, *num; + nrs = m - root_m; + xassert(nrs > 0); + num = xcalloc(1+nrs, sizeof(int)); + for (i = 1; i <= nrs; i++) num[i] = root_m + i; + glp_del_rows(mip, nrs, num); + xfree(num); + } + m = mip->m; + /* and restore attributes of all rows and columns for the root + subproblem */ + xassert(m == root_m); + for (i = 1; i <= m; i++) + { glp_set_row_bnds(mip, i, tree->root_type[i], + tree->root_lb[i], tree->root_ub[i]); + glp_set_row_stat(mip, i, tree->root_stat[i]); + } + for (j = 1; j <= n; j++) + { glp_set_col_bnds(mip, j, tree->root_type[m+j], + tree->root_lb[m+j], tree->root_ub[m+j]); + glp_set_col_stat(mip, j, tree->root_stat[m+j]); + } +#if 1 + /* remove all edges and cliques missing in the conflict graph + for the root subproblem */ + /* (not implemented yet) */ +#endif + } + /* the current subproblem has been frozen */ + tree->curr = NULL; + return; +} + +/*********************************************************************** +* NAME +* +* ios_clone_node - clone specified subproblem +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_clone_node(glp_tree *tree, int p, int nnn, int ref[]); +* +* DESCRIPTION +* +* The routine ios_clone_node clones the specified subproblem, whose +* reference number is p, creating its nnn exact copies. Note that the +* specified subproblem must be active and must be in the frozen state +* (i.e. it must not be the current subproblem). +* +* Each clone, an exact copy of the specified subproblem, becomes a new +* active subproblem added to the end of the active list. After cloning +* the specified subproblem becomes inactive. +* +* The reference numbers of clone subproblems are stored to locations +* ref[1], ..., ref[nnn]. */ + +static int get_slot(glp_tree *tree) +{ int p; + /* if no free slots are available, increase the room */ + if (tree->avail == 0) + { int nslots = tree->nslots; + IOSLOT *save = tree->slot; + if (nslots == 0) + tree->nslots = 20; + else + { tree->nslots = nslots + nslots; + xassert(tree->nslots > nslots); + } + tree->slot = xcalloc(1+tree->nslots, sizeof(IOSLOT)); + if (save != NULL) + { memcpy(&tree->slot[1], &save[1], nslots * sizeof(IOSLOT)); + xfree(save); + } + /* push more free slots into the stack */ + for (p = tree->nslots; p > nslots; p--) + { tree->slot[p].node = NULL; + tree->slot[p].next = tree->avail; + tree->avail = p; + } + } + /* pull a free slot from the stack */ + p = tree->avail; + tree->avail = tree->slot[p].next; + xassert(tree->slot[p].node == NULL); + tree->slot[p].next = 0; + return p; +} + +static IOSNPD *new_node(glp_tree *tree, IOSNPD *parent) +{ IOSNPD *node; + int p; + /* pull a free slot for the new node */ + p = get_slot(tree); + /* create descriptor of the new subproblem */ + node = dmp_get_atom(tree->pool, sizeof(IOSNPD)); + tree->slot[p].node = node; + node->p = p; + node->up = parent; + node->level = (parent == NULL ? 0 : parent->level + 1); + node->count = 0; + node->b_ptr = NULL; + node->s_ptr = NULL; + node->r_ptr = NULL; + node->solved = 0; +#if 0 + node->own_nn = node->own_nc = 0; + node->e_ptr = NULL; +#endif +#if 1 /* 04/X-2008 */ + node->lp_obj = (parent == NULL ? (tree->mip->dir == GLP_MIN ? + -DBL_MAX : +DBL_MAX) : parent->lp_obj); +#endif + node->bound = (parent == NULL ? (tree->mip->dir == GLP_MIN ? + -DBL_MAX : +DBL_MAX) : parent->bound); + node->br_var = 0; + node->br_val = 0.0; + node->ii_cnt = 0; + node->ii_sum = 0.0; +#if 1 /* 30/XI-2009 */ + node->changed = 0; +#endif + if (tree->parm->cb_size == 0) + node->data = NULL; + else + { node->data = dmp_get_atom(tree->pool, tree->parm->cb_size); + memset(node->data, 0, tree->parm->cb_size); + } + node->temp = NULL; + node->prev = tree->tail; + node->next = NULL; + /* add the new subproblem to the end of the active list */ + if (tree->head == NULL) + tree->head = node; + else + tree->tail->next = node; + tree->tail = node; + tree->a_cnt++; + tree->n_cnt++; + tree->t_cnt++; + /* increase the number of child subproblems */ + if (parent == NULL) + xassert(p == 1); + else + parent->count++; + return node; +} + +void ios_clone_node(glp_tree *tree, int p, int nnn, int ref[]) +{ IOSNPD *node; + int k; + /* obtain pointer to the subproblem to be cloned */ + xassert(1 <= p && p <= tree->nslots); + node = tree->slot[p].node; + xassert(node != NULL); + /* the specified subproblem must be active */ + xassert(node->count == 0); + /* and must be in the frozen state */ + xassert(tree->curr != node); + /* remove the specified subproblem from the active list, because + it becomes inactive */ + if (node->prev == NULL) + tree->head = node->next; + else + node->prev->next = node->next; + if (node->next == NULL) + tree->tail = node->prev; + else + node->next->prev = node->prev; + node->prev = node->next = NULL; + tree->a_cnt--; + /* create clone subproblems */ + xassert(nnn > 0); + for (k = 1; k <= nnn; k++) + ref[k] = new_node(tree, node)->p; + return; +} + +/*********************************************************************** +* NAME +* +* ios_delete_node - delete specified subproblem +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_delete_node(glp_tree *tree, int p); +* +* DESCRIPTION +* +* The routine ios_delete_node deletes the specified subproblem, whose +* reference number is p. The subproblem must be active and must be in +* the frozen state (i.e. it must not be the current subproblem). +* +* Note that deletion is performed recursively, i.e. if a subproblem to +* be deleted is the only child of its parent, the parent subproblem is +* also deleted, etc. */ + +void ios_delete_node(glp_tree *tree, int p) +{ IOSNPD *node, *temp; + /* obtain pointer to the subproblem to be deleted */ + xassert(1 <= p && p <= tree->nslots); + node = tree->slot[p].node; + xassert(node != NULL); + /* the specified subproblem must be active */ + xassert(node->count == 0); + /* and must be in the frozen state */ + xassert(tree->curr != node); + /* remove the specified subproblem from the active list, because + it is gone from the tree */ + if (node->prev == NULL) + tree->head = node->next; + else + node->prev->next = node->next; + if (node->next == NULL) + tree->tail = node->prev; + else + node->next->prev = node->prev; + node->prev = node->next = NULL; + tree->a_cnt--; +loop: /* recursive deletion starts here */ + /* delete the bound change list */ + { IOSBND *b; + while (node->b_ptr != NULL) + { b = node->b_ptr; + node->b_ptr = b->next; + dmp_free_atom(tree->pool, b, sizeof(IOSBND)); + } + } + /* delete the status change list */ + { IOSTAT *s; + while (node->s_ptr != NULL) + { s = node->s_ptr; + node->s_ptr = s->next; + dmp_free_atom(tree->pool, s, sizeof(IOSTAT)); + } + } + /* delete the row addition list */ + while (node->r_ptr != NULL) + { IOSROW *r; + r = node->r_ptr; + if (r->name != NULL) + dmp_free_atom(tree->pool, r->name, strlen(r->name)+1); + while (r->ptr != NULL) + { IOSAIJ *a; + a = r->ptr; + r->ptr = a->next; + dmp_free_atom(tree->pool, a, sizeof(IOSAIJ)); + } + node->r_ptr = r->next; + dmp_free_atom(tree->pool, r, sizeof(IOSROW)); + } +#if 0 + /* delete the edge addition list */ + /* delete the clique addition list */ + /* (not implemented yet) */ + xassert(node->own_nn == 0); + xassert(node->own_nc == 0); + xassert(node->e_ptr == NULL); +#endif + /* free application-specific data */ + if (tree->parm->cb_size == 0) + xassert(node->data == NULL); + else + dmp_free_atom(tree->pool, node->data, tree->parm->cb_size); + /* free the corresponding node slot */ + p = node->p; + xassert(tree->slot[p].node == node); + tree->slot[p].node = NULL; + tree->slot[p].next = tree->avail; + tree->avail = p; + /* save pointer to the parent subproblem */ + temp = node->up; + /* delete the subproblem descriptor */ + dmp_free_atom(tree->pool, node, sizeof(IOSNPD)); + tree->n_cnt--; + /* take pointer to the parent subproblem */ + node = temp; + if (node != NULL) + { /* the parent subproblem exists; decrease the number of its + child subproblems */ + xassert(node->count > 0); + node->count--; + /* if now the parent subproblem has no childs, it also must be + deleted */ + if (node->count == 0) goto loop; + } + return; +} + +/*********************************************************************** +* NAME +* +* ios_delete_tree - delete branch-and-bound tree +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_delete_tree(glp_tree *tree); +* +* DESCRIPTION +* +* The routine ios_delete_tree deletes the branch-and-bound tree, which +* the parameter tree points to, and frees all the memory allocated to +* this program object. +* +* On exit components of the problem object are restored to correspond +* to the original MIP passed to the routine ios_create_tree. */ + +void ios_delete_tree(glp_tree *tree) +{ glp_prob *mip = tree->mip; + int i, j; + int m = mip->m; + int n = mip->n; + xassert(mip->tree == tree); + /* remove all additional rows */ + if (m != tree->orig_m) + { int nrs, *num; + nrs = m - tree->orig_m; + xassert(nrs > 0); + num = xcalloc(1+nrs, sizeof(int)); + for (i = 1; i <= nrs; i++) num[i] = tree->orig_m + i; + glp_del_rows(mip, nrs, num); + xfree(num); + } + m = tree->orig_m; + /* restore original attributes of rows and columns */ + xassert(m == tree->orig_m); + xassert(n == tree->n); + for (i = 1; i <= m; i++) + { glp_set_row_bnds(mip, i, tree->orig_type[i], + tree->orig_lb[i], tree->orig_ub[i]); + glp_set_row_stat(mip, i, tree->orig_stat[i]); + mip->row[i]->prim = tree->orig_prim[i]; + mip->row[i]->dual = tree->orig_dual[i]; + } + for (j = 1; j <= n; j++) + { glp_set_col_bnds(mip, j, tree->orig_type[m+j], + tree->orig_lb[m+j], tree->orig_ub[m+j]); + glp_set_col_stat(mip, j, tree->orig_stat[m+j]); + mip->col[j]->prim = tree->orig_prim[m+j]; + mip->col[j]->dual = tree->orig_dual[m+j]; + } + mip->pbs_stat = mip->dbs_stat = GLP_FEAS; + mip->obj_val = tree->orig_obj; + /* delete the branch-and-bound tree */ + xassert(tree->local != NULL); + ios_delete_pool(tree, tree->local); + dmp_delete_pool(tree->pool); + xfree(tree->orig_type); + xfree(tree->orig_lb); + xfree(tree->orig_ub); + xfree(tree->orig_stat); + xfree(tree->orig_prim); + xfree(tree->orig_dual); + xfree(tree->slot); + if (tree->root_type != NULL) xfree(tree->root_type); + if (tree->root_lb != NULL) xfree(tree->root_lb); + if (tree->root_ub != NULL) xfree(tree->root_ub); + if (tree->root_stat != NULL) xfree(tree->root_stat); + xfree(tree->non_int); +#if 0 + xfree(tree->n_ref); + xfree(tree->c_ref); + xfree(tree->j_ref); +#endif + if (tree->pcost != NULL) ios_pcost_free(tree); + xfree(tree->iwrk); + xfree(tree->dwrk); +#if 0 + scg_delete_graph(tree->g); +#endif + if (tree->pred_type != NULL) xfree(tree->pred_type); + if (tree->pred_lb != NULL) xfree(tree->pred_lb); + if (tree->pred_ub != NULL) xfree(tree->pred_ub); + if (tree->pred_stat != NULL) xfree(tree->pred_stat); +#if 0 + xassert(tree->cut_gen == NULL); +#endif + xassert(tree->mir_gen == NULL); + xassert(tree->clq_gen == NULL); + xfree(tree); + mip->tree = NULL; + return; +} + +/*********************************************************************** +* NAME +* +* ios_eval_degrad - estimate obj. degrad. for down- and up-branches +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_eval_degrad(glp_tree *tree, int j, double *dn, double *up); +* +* DESCRIPTION +* +* Given optimal basis to LP relaxation of the current subproblem the +* routine ios_eval_degrad performs the dual ratio test to compute the +* objective values in the adjacent basis for down- and up-branches, +* which are stored in locations *dn and *up, assuming that x[j] is a +* variable chosen to branch upon. */ + +void ios_eval_degrad(glp_tree *tree, int j, double *dn, double *up) +{ glp_prob *mip = tree->mip; + int m = mip->m, n = mip->n; + int len, kase, k, t, stat; + double alfa, beta, gamma, delta, dz; + int *ind = tree->iwrk; + double *val = tree->dwrk; + /* current basis must be optimal */ + xassert(glp_get_status(mip) == GLP_OPT); + /* basis factorization must exist */ + xassert(glp_bf_exists(mip)); + /* obtain (fractional) value of x[j] in optimal basic solution + to LP relaxation of the current subproblem */ + xassert(1 <= j && j <= n); + beta = mip->col[j]->prim; + /* since the value of x[j] is fractional, it is basic; compute + corresponding row of the simplex table */ + len = lpx_eval_tab_row(mip, m+j, ind, val); + /* kase < 0 means down-branch; kase > 0 means up-branch */ + for (kase = -1; kase <= +1; kase += 2) + { /* for down-branch we introduce new upper bound floor(beta) + for x[j]; similarly, for up-branch we introduce new lower + bound ceil(beta) for x[j]; in the current basis this new + upper/lower bound is violated, so in the adjacent basis + x[j] will leave the basis and go to its new upper/lower + bound; we need to know which non-basic variable x[k] should + enter the basis to keep dual feasibility */ +#if 0 /* 23/XI-2009 */ + k = lpx_dual_ratio_test(mip, len, ind, val, kase, 1e-7); +#else + k = lpx_dual_ratio_test(mip, len, ind, val, kase, 1e-9); +#endif + /* if no variable has been chosen, current basis being primal + infeasible due to the new upper/lower bound of x[j] is dual + unbounded, therefore, LP relaxation to corresponding branch + has no primal feasible solution */ + if (k == 0) + { if (mip->dir == GLP_MIN) + { if (kase < 0) + *dn = +DBL_MAX; + else + *up = +DBL_MAX; + } + else if (mip->dir == GLP_MAX) + { if (kase < 0) + *dn = -DBL_MAX; + else + *up = -DBL_MAX; + } + else + xassert(mip != mip); + continue; + } + xassert(1 <= k && k <= m+n); + /* row of the simplex table corresponding to specified basic + variable x[j] is the following: + x[j] = ... + alfa * x[k] + ... ; + we need to know influence coefficient, alfa, at non-basic + variable x[k] chosen with the dual ratio test */ + for (t = 1; t <= len; t++) + if (ind[t] == k) break; + xassert(1 <= t && t <= len); + alfa = val[t]; + /* determine status and reduced cost of variable x[k] */ + if (k <= m) + { stat = mip->row[k]->stat; + gamma = mip->row[k]->dual; + } + else + { stat = mip->col[k-m]->stat; + gamma = mip->col[k-m]->dual; + } + /* x[k] cannot be basic or fixed non-basic */ + xassert(stat == GLP_NL || stat == GLP_NU || stat == GLP_NF); + /* if the current basis is dual degenerative, some reduced + costs, which are close to zero, may have wrong sign due to + round-off errors, so correct the sign of gamma */ + if (mip->dir == GLP_MIN) + { if (stat == GLP_NL && gamma < 0.0 || + stat == GLP_NU && gamma > 0.0 || + stat == GLP_NF) gamma = 0.0; + } + else if (mip->dir == GLP_MAX) + { if (stat == GLP_NL && gamma > 0.0 || + stat == GLP_NU && gamma < 0.0 || + stat == GLP_NF) gamma = 0.0; + } + else + xassert(mip != mip); + /* determine the change of x[j] in the adjacent basis: + delta x[j] = new x[j] - old x[j] */ + delta = (kase < 0 ? floor(beta) : ceil(beta)) - beta; + /* compute the change of x[k] in the adjacent basis: + delta x[k] = new x[k] - old x[k] = delta x[j] / alfa */ + delta /= alfa; + /* compute the change of the objective in the adjacent basis: + delta z = new z - old z = gamma * delta x[k] */ + dz = gamma * delta; + if (mip->dir == GLP_MIN) + xassert(dz >= 0.0); + else if (mip->dir == GLP_MAX) + xassert(dz <= 0.0); + else + xassert(mip != mip); + /* compute the new objective value in the adjacent basis: + new z = old z + delta z */ + if (kase < 0) + *dn = mip->obj_val + dz; + else + *up = mip->obj_val + dz; + } + /*xprintf("obj = %g; dn = %g; up = %g\n", + mip->obj_val, *dn, *up);*/ + return; +} + +/*********************************************************************** +* NAME +* +* ios_round_bound - improve local bound by rounding +* +* SYNOPSIS +* +* #include "glpios.h" +* double ios_round_bound(glp_tree *tree, double bound); +* +* RETURNS +* +* For the given local bound for any integer feasible solution to the +* current subproblem the routine ios_round_bound returns an improved +* local bound for the same integer feasible solution. +* +* BACKGROUND +* +* Let the current subproblem has the following objective function: +* +* z = sum c[j] * x[j] + s >= b, (1) +* j in J +* +* where J = {j: c[j] is non-zero and integer, x[j] is integer}, s is +* the sum of terms corresponding to fixed variables, b is an initial +* local bound (minimization). +* +* From (1) it follows that: +* +* d * sum (c[j] / d) * x[j] + s >= b, (2) +* j in J +* +* or, equivalently, +* +* sum (c[j] / d) * x[j] >= (b - s) / d = h, (3) +* j in J +* +* where d = gcd(c[j]). Since the left-hand side of (3) is integer, +* h = (b - s) / d can be rounded up to the nearest integer: +* +* h' = ceil(h) = (b' - s) / d, (4) +* +* that gives an rounded, improved local bound: +* +* b' = d * h' + s. (5) +* +* In case of maximization '>=' in (1) should be replaced by '<=' that +* leads to the following formula: +* +* h' = floor(h) = (b' - s) / d, (6) +* +* which should used in the same way as (4). +* +* NOTE: If b is a valid local bound for a child of the current +* subproblem, b' is also valid for that child subproblem. */ + +double ios_round_bound(glp_tree *tree, double bound) +{ glp_prob *mip = tree->mip; + int n = mip->n; + int d, j, nn, *c = tree->iwrk; + double s, h; + /* determine c[j] and compute s */ + nn = 0, s = mip->c0, d = 0; + for (j = 1; j <= n; j++) + { GLPCOL *col = mip->col[j]; + if (col->coef == 0.0) continue; + if (col->type == GLP_FX) + { /* fixed variable */ + s += col->coef * col->prim; + } + else + { /* non-fixed variable */ + if (col->kind != GLP_IV) goto skip; + if (col->coef != floor(col->coef)) goto skip; + if (fabs(col->coef) <= (double)INT_MAX) + c[++nn] = (int)fabs(col->coef); + else + d = 1; + } + } + /* compute d = gcd(c[1],...c[nn]) */ + if (d == 0) + { if (nn == 0) goto skip; + d = gcdn(nn, c); + } + xassert(d > 0); + /* compute new local bound */ + if (mip->dir == GLP_MIN) + { if (bound != +DBL_MAX) + { h = (bound - s) / (double)d; + if (h >= floor(h) + 0.001) + { /* round up */ + h = ceil(h); + /*xprintf("d = %d; old = %g; ", d, bound);*/ + bound = (double)d * h + s; + /*xprintf("new = %g\n", bound);*/ + } + } + } + else if (mip->dir == GLP_MAX) + { if (bound != -DBL_MAX) + { h = (bound - s) / (double)d; + if (h <= ceil(h) - 0.001) + { /* round down */ + h = floor(h); + bound = (double)d * h + s; + } + } + } + else + xassert(mip != mip); +skip: return bound; +} + +/*********************************************************************** +* NAME +* +* ios_is_hopeful - check if subproblem is hopeful +* +* SYNOPSIS +* +* #include "glpios.h" +* int ios_is_hopeful(glp_tree *tree, double bound); +* +* DESCRIPTION +* +* Given the local bound of a subproblem the routine ios_is_hopeful +* checks if the subproblem can have an integer optimal solution which +* is better than the best one currently known. +* +* RETURNS +* +* If the subproblem can have a better integer optimal solution, the +* routine returns non-zero; otherwise, if the corresponding branch can +* be pruned, the routine returns zero. */ + +int ios_is_hopeful(glp_tree *tree, double bound) +{ glp_prob *mip = tree->mip; + int ret = 1; + double eps; + if (mip->mip_stat == GLP_FEAS) + { eps = tree->parm->tol_obj * (1.0 + fabs(mip->mip_obj)); + switch (mip->dir) + { case GLP_MIN: + if (bound >= mip->mip_obj - eps) ret = 0; + break; + case GLP_MAX: + if (bound <= mip->mip_obj + eps) ret = 0; + break; + default: + xassert(mip != mip); + } + } + else + { switch (mip->dir) + { case GLP_MIN: + if (bound == +DBL_MAX) ret = 0; + break; + case GLP_MAX: + if (bound == -DBL_MAX) ret = 0; + break; + default: + xassert(mip != mip); + } + } + return ret; +} + +/*********************************************************************** +* NAME +* +* ios_best_node - find active node with best local bound +* +* SYNOPSIS +* +* #include "glpios.h" +* int ios_best_node(glp_tree *tree); +* +* DESCRIPTION +* +* The routine ios_best_node finds an active node whose local bound is +* best among other active nodes. +* +* It is understood that the integer optimal solution of the original +* mip problem cannot be better than the best bound, so the best bound +* is an lower (minimization) or upper (maximization) global bound for +* the original problem. +* +* RETURNS +* +* The routine ios_best_node returns the subproblem reference number +* for the best node. However, if the tree is empty, it returns zero. */ + +int ios_best_node(glp_tree *tree) +{ IOSNPD *node, *best = NULL; + switch (tree->mip->dir) + { case GLP_MIN: + /* minimization */ + for (node = tree->head; node != NULL; node = node->next) + if (best == NULL || best->bound > node->bound) + best = node; + break; + case GLP_MAX: + /* maximization */ + for (node = tree->head; node != NULL; node = node->next) + if (best == NULL || best->bound < node->bound) + best = node; + break; + default: + xassert(tree != tree); + } + return best == NULL ? 0 : best->p; +} + +/*********************************************************************** +* NAME +* +* ios_relative_gap - compute relative mip gap +* +* SYNOPSIS +* +* #include "glpios.h" +* double ios_relative_gap(glp_tree *tree); +* +* DESCRIPTION +* +* The routine ios_relative_gap computes the relative mip gap using the +* formula: +* +* gap = |best_mip - best_bnd| / (|best_mip| + DBL_EPSILON), +* +* where best_mip is the best integer feasible solution found so far, +* best_bnd is the best (global) bound. If no integer feasible solution +* has been found yet, rel_gap is set to DBL_MAX. +* +* RETURNS +* +* The routine ios_relative_gap returns the relative mip gap. */ + +double ios_relative_gap(glp_tree *tree) +{ glp_prob *mip = tree->mip; + int p; + double best_mip, best_bnd, gap; + if (mip->mip_stat == GLP_FEAS) + { best_mip = mip->mip_obj; + p = ios_best_node(tree); + if (p == 0) + { /* the tree is empty */ + gap = 0.0; + } + else + { best_bnd = tree->slot[p].node->bound; + gap = fabs(best_mip - best_bnd) / (fabs(best_mip) + + DBL_EPSILON); + } + } + else + { /* no integer feasible solution has been found yet */ + gap = DBL_MAX; + } + return gap; +} + +/*********************************************************************** +* NAME +* +* ios_solve_node - solve LP relaxation of current subproblem +* +* SYNOPSIS +* +* #include "glpios.h" +* int ios_solve_node(glp_tree *tree); +* +* DESCRIPTION +* +* The routine ios_solve_node re-optimizes LP relaxation of the current +* subproblem using the dual simplex method. +* +* RETURNS +* +* The routine returns the code which is reported by glp_simplex. */ + +int ios_solve_node(glp_tree *tree) +{ glp_prob *mip = tree->mip; + glp_smcp parm; + int ret; + /* the current subproblem must exist */ + xassert(tree->curr != NULL); + /* set some control parameters */ + glp_init_smcp(&parm); + switch (tree->parm->msg_lev) + { case GLP_MSG_OFF: + parm.msg_lev = GLP_MSG_OFF; break; + case GLP_MSG_ERR: + parm.msg_lev = GLP_MSG_ERR; break; + case GLP_MSG_ON: + case GLP_MSG_ALL: + parm.msg_lev = GLP_MSG_ON; break; + case GLP_MSG_DBG: + parm.msg_lev = GLP_MSG_ALL; break; + default: + xassert(tree != tree); + } + parm.meth = GLP_DUALP; + if (tree->parm->msg_lev < GLP_MSG_DBG) + parm.out_dly = tree->parm->out_dly; + else + parm.out_dly = 0; + /* if the incumbent objective value is already known, use it to + prematurely terminate the dual simplex search */ + if (mip->mip_stat == GLP_FEAS) + { switch (tree->mip->dir) + { case GLP_MIN: + parm.obj_ul = mip->mip_obj; + break; + case GLP_MAX: + parm.obj_ll = mip->mip_obj; + break; + default: + xassert(mip != mip); + } + } + /* try to solve/re-optimize the LP relaxation */ + ret = glp_simplex(mip, &parm); + tree->curr->solved++; +#if 0 + xprintf("ret = %d; status = %d; pbs = %d; dbs = %d; some = %d\n", + ret, glp_get_status(mip), mip->pbs_stat, mip->dbs_stat, + mip->some); + lpx_print_sol(mip, "sol"); +#endif + return ret; +} + +/**********************************************************************/ + +IOSPOOL *ios_create_pool(glp_tree *tree) +{ /* create cut pool */ + IOSPOOL *pool; +#if 0 + pool = dmp_get_atom(tree->pool, sizeof(IOSPOOL)); +#else + xassert(tree == tree); + pool = xmalloc(sizeof(IOSPOOL)); +#endif + pool->size = 0; + pool->head = pool->tail = NULL; + pool->ord = 0, pool->curr = NULL; + return pool; +} + +int ios_add_row(glp_tree *tree, IOSPOOL *pool, + const char *name, int klass, int flags, int len, const int ind[], + const double val[], int type, double rhs) +{ /* add row (constraint) to the cut pool */ + IOSCUT *cut; + IOSAIJ *aij; + int k; + xassert(pool != NULL); + cut = dmp_get_atom(tree->pool, sizeof(IOSCUT)); + if (name == NULL || name[0] == '\0') + cut->name = NULL; + else + { for (k = 0; name[k] != '\0'; k++) + { if (k == 256) + xerror("glp_ios_add_row: cut name too long\n"); + if (iscntrl((unsigned char)name[k])) + xerror("glp_ios_add_row: cut name contains invalid chara" + "cter(s)\n"); + } + cut->name = dmp_get_atom(tree->pool, strlen(name)+1); + strcpy(cut->name, name); + } + if (!(0 <= klass && klass <= 255)) + xerror("glp_ios_add_row: klass = %d; invalid cut class\n", + klass); + cut->klass = (unsigned char)klass; + if (flags != 0) + xerror("glp_ios_add_row: flags = %d; invalid cut flags\n", + flags); + cut->ptr = NULL; + if (!(0 <= len && len <= tree->n)) + xerror("glp_ios_add_row: len = %d; invalid cut length\n", + len); + for (k = 1; k <= len; k++) + { aij = dmp_get_atom(tree->pool, sizeof(IOSAIJ)); + if (!(1 <= ind[k] && ind[k] <= tree->n)) + xerror("glp_ios_add_row: ind[%d] = %d; column index out of " + "range\n", k, ind[k]); + aij->j = ind[k]; + aij->val = val[k]; + aij->next = cut->ptr; + cut->ptr = aij; + } + if (!(type == GLP_LO || type == GLP_UP || type == GLP_FX)) + xerror("glp_ios_add_row: type = %d; invalid cut type\n", + type); + cut->type = (unsigned char)type; + cut->rhs = rhs; + cut->prev = pool->tail; + cut->next = NULL; + if (cut->prev == NULL) + pool->head = cut; + else + cut->prev->next = cut; + pool->tail = cut; + pool->size++; + return pool->size; +} + +IOSCUT *ios_find_row(IOSPOOL *pool, int i) +{ /* find row (constraint) in the cut pool */ + /* (smart linear search) */ + xassert(pool != NULL); + xassert(1 <= i && i <= pool->size); + if (pool->ord == 0) + { xassert(pool->curr == NULL); + pool->ord = 1; + pool->curr = pool->head; + } + xassert(pool->curr != NULL); + if (i < pool->ord) + { if (i < pool->ord - i) + { pool->ord = 1; + pool->curr = pool->head; + while (pool->ord != i) + { pool->ord++; + xassert(pool->curr != NULL); + pool->curr = pool->curr->next; + } + } + else + { while (pool->ord != i) + { pool->ord--; + xassert(pool->curr != NULL); + pool->curr = pool->curr->prev; + } + } + } + else if (i > pool->ord) + { if (i - pool->ord < pool->size - i) + { while (pool->ord != i) + { pool->ord++; + xassert(pool->curr != NULL); + pool->curr = pool->curr->next; + } + } + else + { pool->ord = pool->size; + pool->curr = pool->tail; + while (pool->ord != i) + { pool->ord--; + xassert(pool->curr != NULL); + pool->curr = pool->curr->prev; + } + } + } + xassert(pool->ord == i); + xassert(pool->curr != NULL); + return pool->curr; +} + +void ios_del_row(glp_tree *tree, IOSPOOL *pool, int i) +{ /* remove row (constraint) from the cut pool */ + IOSCUT *cut; + IOSAIJ *aij; + xassert(pool != NULL); + if (!(1 <= i && i <= pool->size)) + xerror("glp_ios_del_row: i = %d; cut number out of range\n", + i); + cut = ios_find_row(pool, i); + xassert(pool->curr == cut); + if (cut->next != NULL) + pool->curr = cut->next; + else if (cut->prev != NULL) + pool->ord--, pool->curr = cut->prev; + else + pool->ord = 0, pool->curr = NULL; + if (cut->name != NULL) + dmp_free_atom(tree->pool, cut->name, strlen(cut->name)+1); + if (cut->prev == NULL) + { xassert(pool->head == cut); + pool->head = cut->next; + } + else + { xassert(cut->prev->next == cut); + cut->prev->next = cut->next; + } + if (cut->next == NULL) + { xassert(pool->tail == cut); + pool->tail = cut->prev; + } + else + { xassert(cut->next->prev == cut); + cut->next->prev = cut->prev; + } + while (cut->ptr != NULL) + { aij = cut->ptr; + cut->ptr = aij->next; + dmp_free_atom(tree->pool, aij, sizeof(IOSAIJ)); + } + dmp_free_atom(tree->pool, cut, sizeof(IOSCUT)); + pool->size--; + return; +} + +void ios_clear_pool(glp_tree *tree, IOSPOOL *pool) +{ /* remove all rows (constraints) from the cut pool */ + xassert(pool != NULL); + while (pool->head != NULL) + { IOSCUT *cut = pool->head; + pool->head = cut->next; + if (cut->name != NULL) + dmp_free_atom(tree->pool, cut->name, strlen(cut->name)+1); + while (cut->ptr != NULL) + { IOSAIJ *aij = cut->ptr; + cut->ptr = aij->next; + dmp_free_atom(tree->pool, aij, sizeof(IOSAIJ)); + } + dmp_free_atom(tree->pool, cut, sizeof(IOSCUT)); + } + pool->size = 0; + pool->head = pool->tail = NULL; + pool->ord = 0, pool->curr = NULL; + return; +} + +void ios_delete_pool(glp_tree *tree, IOSPOOL *pool) +{ /* delete cut pool */ + xassert(pool != NULL); + ios_clear_pool(tree, pool); + xfree(pool); + return; +} + +/**********************************************************************/ + +#if 0 +static int refer_to_node(glp_tree *tree, int j) +{ /* determine node number corresponding to binary variable x[j] or + its complement */ + glp_prob *mip = tree->mip; + int n = mip->n; + int *ref; + if (j > 0) + ref = tree->n_ref; + else + ref = tree->c_ref, j = - j; + xassert(1 <= j && j <= n); + if (ref[j] == 0) + { /* new node is needed */ + SCG *g = tree->g; + int n_max = g->n_max; + ref[j] = scg_add_nodes(g, 1); + if (g->n_max > n_max) + { int *save = tree->j_ref; + tree->j_ref = xcalloc(1+g->n_max, sizeof(int)); + memcpy(&tree->j_ref[1], &save[1], g->n * sizeof(int)); + xfree(save); + } + xassert(ref[j] == g->n); + tree->j_ref[ref[j]] = j; + xassert(tree->curr != NULL); + if (tree->curr->level > 0) tree->curr->own_nn++; + } + return ref[j]; +} +#endif + +#if 0 +void ios_add_edge(glp_tree *tree, int j1, int j2) +{ /* add new edge to the conflict graph */ + glp_prob *mip = tree->mip; + int n = mip->n; + SCGRIB *e; + int first, i1, i2; + xassert(-n <= j1 && j1 <= +n && j1 != 0); + xassert(-n <= j2 && j2 <= +n && j2 != 0); + xassert(j1 != j2); + /* determine number of the first node, which was added for the + current subproblem */ + xassert(tree->curr != NULL); + first = tree->g->n - tree->curr->own_nn + 1; + /* determine node numbers for both endpoints */ + i1 = refer_to_node(tree, j1); + i2 = refer_to_node(tree, j2); + /* add edge (i1,i2) to the conflict graph */ + e = scg_add_edge(tree->g, i1, i2); + /* if the current subproblem is not the root and both endpoints + were created on some previous levels, save the edge */ + if (tree->curr->level > 0 && i1 < first && i2 < first) + { IOSRIB *rib; + rib = dmp_get_atom(tree->pool, sizeof(IOSRIB)); + rib->j1 = j1; + rib->j2 = j2; + rib->e = e; + rib->next = tree->curr->e_ptr; + tree->curr->e_ptr = rib; + } + return; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,825 @@ +/* glpios02.c (preprocess current subproblem) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* prepare_row_info - prepare row info to determine implied bounds +* +* Given a row (linear form) +* +* n +* sum a[j] * x[j] (1) +* j=1 +* +* and bounds of columns (variables) +* +* l[j] <= x[j] <= u[j] (2) +* +* this routine computes f_min, j_min, f_max, j_max needed to determine +* implied bounds. +* +* ALGORITHM +* +* Let J+ = {j : a[j] > 0} and J- = {j : a[j] < 0}. +* +* Parameters f_min and j_min are computed as follows: +* +* 1) if there is no x[k] such that k in J+ and l[k] = -inf or k in J- +* and u[k] = +inf, then +* +* f_min := sum a[j] * l[j] + sum a[j] * u[j] +* j in J+ j in J- +* (3) +* j_min := 0 +* +* 2) if there is exactly one x[k] such that k in J+ and l[k] = -inf +* or k in J- and u[k] = +inf, then +* +* f_min := sum a[j] * l[j] + sum a[j] * u[j] +* j in J+\{k} j in J-\{k} +* (4) +* j_min := k +* +* 3) if there are two or more x[k] such that k in J+ and l[k] = -inf +* or k in J- and u[k] = +inf, then +* +* f_min := -inf +* (5) +* j_min := 0 +* +* Parameters f_max and j_max are computed in a similar way as follows: +* +* 1) if there is no x[k] such that k in J+ and u[k] = +inf or k in J- +* and l[k] = -inf, then +* +* f_max := sum a[j] * u[j] + sum a[j] * l[j] +* j in J+ j in J- +* (6) +* j_max := 0 +* +* 2) if there is exactly one x[k] such that k in J+ and u[k] = +inf +* or k in J- and l[k] = -inf, then +* +* f_max := sum a[j] * u[j] + sum a[j] * l[j] +* j in J+\{k} j in J-\{k} +* (7) +* j_max := k +* +* 3) if there are two or more x[k] such that k in J+ and u[k] = +inf +* or k in J- and l[k] = -inf, then +* +* f_max := +inf +* (8) +* j_max := 0 */ + +struct f_info +{ int j_min, j_max; + double f_min, f_max; +}; + +static void prepare_row_info(int n, const double a[], const double l[], + const double u[], struct f_info *f) +{ int j, j_min, j_max; + double f_min, f_max; + xassert(n >= 0); + /* determine f_min and j_min */ + f_min = 0.0, j_min = 0; + for (j = 1; j <= n; j++) + { if (a[j] > 0.0) + { if (l[j] == -DBL_MAX) + { if (j_min == 0) + j_min = j; + else + { f_min = -DBL_MAX, j_min = 0; + break; + } + } + else + f_min += a[j] * l[j]; + } + else if (a[j] < 0.0) + { if (u[j] == +DBL_MAX) + { if (j_min == 0) + j_min = j; + else + { f_min = -DBL_MAX, j_min = 0; + break; + } + } + else + f_min += a[j] * u[j]; + } + else + xassert(a != a); + } + f->f_min = f_min, f->j_min = j_min; + /* determine f_max and j_max */ + f_max = 0.0, j_max = 0; + for (j = 1; j <= n; j++) + { if (a[j] > 0.0) + { if (u[j] == +DBL_MAX) + { if (j_max == 0) + j_max = j; + else + { f_max = +DBL_MAX, j_max = 0; + break; + } + } + else + f_max += a[j] * u[j]; + } + else if (a[j] < 0.0) + { if (l[j] == -DBL_MAX) + { if (j_max == 0) + j_max = j; + else + { f_max = +DBL_MAX, j_max = 0; + break; + } + } + else + f_max += a[j] * l[j]; + } + else + xassert(a != a); + } + f->f_max = f_max, f->j_max = j_max; + return; +} + +/*********************************************************************** +* row_implied_bounds - determine row implied bounds +* +* Given a row (linear form) +* +* n +* sum a[j] * x[j] +* j=1 +* +* and bounds of columns (variables) +* +* l[j] <= x[j] <= u[j] +* +* this routine determines implied bounds of the row. +* +* ALGORITHM +* +* Let J+ = {j : a[j] > 0} and J- = {j : a[j] < 0}. +* +* The implied lower bound of the row is computed as follows: +* +* L' := sum a[j] * l[j] + sum a[j] * u[j] (9) +* j in J+ j in J- +* +* and as it follows from (3), (4), and (5): +* +* L' := if j_min = 0 then f_min else -inf (10) +* +* The implied upper bound of the row is computed as follows: +* +* U' := sum a[j] * u[j] + sum a[j] * l[j] (11) +* j in J+ j in J- +* +* and as it follows from (6), (7), and (8): +* +* U' := if j_max = 0 then f_max else +inf (12) +* +* The implied bounds are stored in locations LL and UU. */ + +static void row_implied_bounds(const struct f_info *f, double *LL, + double *UU) +{ *LL = (f->j_min == 0 ? f->f_min : -DBL_MAX); + *UU = (f->j_max == 0 ? f->f_max : +DBL_MAX); + return; +} + +/*********************************************************************** +* col_implied_bounds - determine column implied bounds +* +* Given a row (constraint) +* +* n +* L <= sum a[j] * x[j] <= U (13) +* j=1 +* +* and bounds of columns (variables) +* +* l[j] <= x[j] <= u[j] +* +* this routine determines implied bounds of variable x[k]. +* +* It is assumed that if L != -inf, the lower bound of the row can be +* active, and if U != +inf, the upper bound of the row can be active. +* +* ALGORITHM +* +* From (13) it follows that +* +* L <= sum a[j] * x[j] + a[k] * x[k] <= U +* j!=k +* or +* +* L - sum a[j] * x[j] <= a[k] * x[k] <= U - sum a[j] * x[j] +* j!=k j!=k +* +* Thus, if the row lower bound L can be active, implied lower bound of +* term a[k] * x[k] can be determined as follows: +* +* ilb(a[k] * x[k]) = min(L - sum a[j] * x[j]) = +* j!=k +* (14) +* = L - max sum a[j] * x[j] +* j!=k +* +* where, as it follows from (6), (7), and (8) +* +* / f_max - a[k] * u[k], j_max = 0, a[k] > 0 +* | +* | f_max - a[k] * l[k], j_max = 0, a[k] < 0 +* max sum a[j] * x[j] = { +* j!=k | f_max, j_max = k +* | +* \ +inf, j_max != 0 +* +* and if the upper bound U can be active, implied upper bound of term +* a[k] * x[k] can be determined as follows: +* +* iub(a[k] * x[k]) = max(U - sum a[j] * x[j]) = +* j!=k +* (15) +* = U - min sum a[j] * x[j] +* j!=k +* +* where, as it follows from (3), (4), and (5) +* +* / f_min - a[k] * l[k], j_min = 0, a[k] > 0 +* | +* | f_min - a[k] * u[k], j_min = 0, a[k] < 0 +* min sum a[j] * x[j] = { +* j!=k | f_min, j_min = k +* | +* \ -inf, j_min != 0 +* +* Since +* +* ilb(a[k] * x[k]) <= a[k] * x[k] <= iub(a[k] * x[k]) +* +* implied lower and upper bounds of x[k] are determined as follows: +* +* l'[k] := if a[k] > 0 then ilb / a[k] else ulb / a[k] (16) +* +* u'[k] := if a[k] > 0 then ulb / a[k] else ilb / a[k] (17) +* +* The implied bounds are stored in locations ll and uu. */ + +static void col_implied_bounds(const struct f_info *f, int n, + const double a[], double L, double U, const double l[], + const double u[], int k, double *ll, double *uu) +{ double ilb, iub; + xassert(n >= 0); + xassert(1 <= k && k <= n); + /* determine implied lower bound of term a[k] * x[k] (14) */ + if (L == -DBL_MAX || f->f_max == +DBL_MAX) + ilb = -DBL_MAX; + else if (f->j_max == 0) + { if (a[k] > 0.0) + { xassert(u[k] != +DBL_MAX); + ilb = L - (f->f_max - a[k] * u[k]); + } + else if (a[k] < 0.0) + { xassert(l[k] != -DBL_MAX); + ilb = L - (f->f_max - a[k] * l[k]); + } + else + xassert(a != a); + } + else if (f->j_max == k) + ilb = L - f->f_max; + else + ilb = -DBL_MAX; + /* determine implied upper bound of term a[k] * x[k] (15) */ + if (U == +DBL_MAX || f->f_min == -DBL_MAX) + iub = +DBL_MAX; + else if (f->j_min == 0) + { if (a[k] > 0.0) + { xassert(l[k] != -DBL_MAX); + iub = U - (f->f_min - a[k] * l[k]); + } + else if (a[k] < 0.0) + { xassert(u[k] != +DBL_MAX); + iub = U - (f->f_min - a[k] * u[k]); + } + else + xassert(a != a); + } + else if (f->j_min == k) + iub = U - f->f_min; + else + iub = +DBL_MAX; + /* determine implied bounds of x[k] (16) and (17) */ +#if 1 + /* do not use a[k] if it has small magnitude to prevent wrong + implied bounds; for example, 1e-15 * x1 >= x2 + x3, where + x1 >= -10, x2, x3 >= 0, would lead to wrong conclusion that + x1 >= 0 */ + if (fabs(a[k]) < 1e-6) + *ll = -DBL_MAX, *uu = +DBL_MAX; else +#endif + if (a[k] > 0.0) + { *ll = (ilb == -DBL_MAX ? -DBL_MAX : ilb / a[k]); + *uu = (iub == +DBL_MAX ? +DBL_MAX : iub / a[k]); + } + else if (a[k] < 0.0) + { *ll = (iub == +DBL_MAX ? -DBL_MAX : iub / a[k]); + *uu = (ilb == -DBL_MAX ? +DBL_MAX : ilb / a[k]); + } + else + xassert(a != a); + return; +} + +/*********************************************************************** +* check_row_bounds - check and relax original row bounds +* +* Given a row (constraint) +* +* n +* L <= sum a[j] * x[j] <= U +* j=1 +* +* and bounds of columns (variables) +* +* l[j] <= x[j] <= u[j] +* +* this routine checks the original row bounds L and U for feasibility +* and redundancy. If the original lower bound L or/and upper bound U +* cannot be active due to bounds of variables, the routine remove them +* replacing by -inf or/and +inf, respectively. +* +* If no primal infeasibility is detected, the routine returns zero, +* otherwise non-zero. */ + +static int check_row_bounds(const struct f_info *f, double *L_, + double *U_) +{ int ret = 0; + double L = *L_, U = *U_, LL, UU; + /* determine implied bounds of the row */ + row_implied_bounds(f, &LL, &UU); + /* check if the original lower bound is infeasible */ + if (L != -DBL_MAX) + { double eps = 1e-3 * (1.0 + fabs(L)); + if (UU < L - eps) + { ret = 1; + goto done; + } + } + /* check if the original upper bound is infeasible */ + if (U != +DBL_MAX) + { double eps = 1e-3 * (1.0 + fabs(U)); + if (LL > U + eps) + { ret = 1; + goto done; + } + } + /* check if the original lower bound is redundant */ + if (L != -DBL_MAX) + { double eps = 1e-12 * (1.0 + fabs(L)); + if (LL > L - eps) + { /* it cannot be active, so remove it */ + *L_ = -DBL_MAX; + } + } + /* check if the original upper bound is redundant */ + if (U != +DBL_MAX) + { double eps = 1e-12 * (1.0 + fabs(U)); + if (UU < U + eps) + { /* it cannot be active, so remove it */ + *U_ = +DBL_MAX; + } + } +done: return ret; +} + +/*********************************************************************** +* check_col_bounds - check and tighten original column bounds +* +* Given a row (constraint) +* +* n +* L <= sum a[j] * x[j] <= U +* j=1 +* +* and bounds of columns (variables) +* +* l[j] <= x[j] <= u[j] +* +* for column (variable) x[j] this routine checks the original column +* bounds l[j] and u[j] for feasibility and redundancy. If the original +* lower bound l[j] or/and upper bound u[j] cannot be active due to +* bounds of the constraint and other variables, the routine tighten +* them replacing by corresponding implied bounds, if possible. +* +* NOTE: It is assumed that if L != -inf, the row lower bound can be +* active, and if U != +inf, the row upper bound can be active. +* +* The flag means that variable x[j] is required to be integer. +* +* New actual bounds for x[j] are stored in locations lj and uj. +* +* If no primal infeasibility is detected, the routine returns zero, +* otherwise non-zero. */ + +static int check_col_bounds(const struct f_info *f, int n, + const double a[], double L, double U, const double l[], + const double u[], int flag, int j, double *_lj, double *_uj) +{ int ret = 0; + double lj, uj, ll, uu; + xassert(n >= 0); + xassert(1 <= j && j <= n); + lj = l[j], uj = u[j]; + /* determine implied bounds of the column */ + col_implied_bounds(f, n, a, L, U, l, u, j, &ll, &uu); + /* if x[j] is integral, round its implied bounds */ + if (flag) + { if (ll != -DBL_MAX) + ll = (ll - floor(ll) < 1e-3 ? floor(ll) : ceil(ll)); + if (uu != +DBL_MAX) + uu = (ceil(uu) - uu < 1e-3 ? ceil(uu) : floor(uu)); + } + /* check if the original lower bound is infeasible */ + if (lj != -DBL_MAX) + { double eps = 1e-3 * (1.0 + fabs(lj)); + if (uu < lj - eps) + { ret = 1; + goto done; + } + } + /* check if the original upper bound is infeasible */ + if (uj != +DBL_MAX) + { double eps = 1e-3 * (1.0 + fabs(uj)); + if (ll > uj + eps) + { ret = 1; + goto done; + } + } + /* check if the original lower bound is redundant */ + if (ll != -DBL_MAX) + { double eps = 1e-3 * (1.0 + fabs(ll)); + if (lj < ll - eps) + { /* it cannot be active, so tighten it */ + lj = ll; + } + } + /* check if the original upper bound is redundant */ + if (uu != +DBL_MAX) + { double eps = 1e-3 * (1.0 + fabs(uu)); + if (uj > uu + eps) + { /* it cannot be active, so tighten it */ + uj = uu; + } + } + /* due to round-off errors it may happen that lj > uj (although + lj < uj + eps, since no primal infeasibility is detected), so + adjuct the new actual bounds to provide lj <= uj */ + if (!(lj == -DBL_MAX || uj == +DBL_MAX)) + { double t1 = fabs(lj), t2 = fabs(uj); + double eps = 1e-10 * (1.0 + (t1 <= t2 ? t1 : t2)); + if (lj > uj - eps) + { if (lj == l[j]) + uj = lj; + else if (uj == u[j]) + lj = uj; + else if (t1 <= t2) + uj = lj; + else + lj = uj; + } + } + *_lj = lj, *_uj = uj; +done: return ret; +} + +/*********************************************************************** +* check_efficiency - check if change in column bounds is efficient +* +* Given the original bounds of a column l and u and its new actual +* bounds l' and u' (possibly tighten by the routine check_col_bounds) +* this routine checks if the change in the column bounds is efficient +* enough. If so, the routine returns non-zero, otherwise zero. +* +* The flag means that the variable is required to be integer. */ + +static int check_efficiency(int flag, double l, double u, double ll, + double uu) +{ int eff = 0; + /* check efficiency for lower bound */ + if (l < ll) + { if (flag || l == -DBL_MAX) + eff++; + else + { double r; + if (u == +DBL_MAX) + r = 1.0 + fabs(l); + else + r = 1.0 + (u - l); + if (ll - l >= 0.25 * r) + eff++; + } + } + /* check efficiency for upper bound */ + if (u > uu) + { if (flag || u == +DBL_MAX) + eff++; + else + { double r; + if (l == -DBL_MAX) + r = 1.0 + fabs(u); + else + r = 1.0 + (u - l); + if (u - uu >= 0.25 * r) + eff++; + } + } + return eff; +} + +/*********************************************************************** +* basic_preprocessing - perform basic preprocessing +* +* This routine performs basic preprocessing of the specified MIP that +* includes relaxing some row bounds and tightening some column bounds. +* +* On entry the arrays L and U contains original row bounds, and the +* arrays l and u contains original column bounds: +* +* L[0] is the lower bound of the objective row; +* L[i], i = 1,...,m, is the lower bound of i-th row; +* U[0] is the upper bound of the objective row; +* U[i], i = 1,...,m, is the upper bound of i-th row; +* l[0] is not used; +* l[j], j = 1,...,n, is the lower bound of j-th column; +* u[0] is not used; +* u[j], j = 1,...,n, is the upper bound of j-th column. +* +* On exit the arrays L, U, l, and u contain new actual bounds of rows +* and column in the same locations. +* +* The parameters nrs and num specify an initial list of rows to be +* processed: +* +* nrs is the number of rows in the initial list, 0 <= nrs <= m+1; +* num[0] is not used; +* num[1,...,nrs] are row numbers (0 means the objective row). +* +* The parameter max_pass specifies the maximal number of times that +* each row can be processed, max_pass > 0. +* +* If no primal infeasibility is detected, the routine returns zero, +* otherwise non-zero. */ + +static int basic_preprocessing(glp_prob *mip, double L[], double U[], + double l[], double u[], int nrs, const int num[], int max_pass) +{ int m = mip->m; + int n = mip->n; + struct f_info f; + int i, j, k, len, size, ret = 0; + int *ind, *list, *mark, *pass; + double *val, *lb, *ub; + xassert(0 <= nrs && nrs <= m+1); + xassert(max_pass > 0); + /* allocate working arrays */ + ind = xcalloc(1+n, sizeof(int)); + list = xcalloc(1+m+1, sizeof(int)); + mark = xcalloc(1+m+1, sizeof(int)); + memset(&mark[0], 0, (m+1) * sizeof(int)); + pass = xcalloc(1+m+1, sizeof(int)); + memset(&pass[0], 0, (m+1) * sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + lb = xcalloc(1+n, sizeof(double)); + ub = xcalloc(1+n, sizeof(double)); + /* initialize the list of rows to be processed */ + size = 0; + for (k = 1; k <= nrs; k++) + { i = num[k]; + xassert(0 <= i && i <= m); + /* duplicate row numbers are not allowed */ + xassert(!mark[i]); + list[++size] = i, mark[i] = 1; + } + xassert(size == nrs); + /* process rows in the list until it becomes empty */ + while (size > 0) + { /* get a next row from the list */ + i = list[size--], mark[i] = 0; + /* increase the row processing count */ + pass[i]++; + /* if the row is free, skip it */ + if (L[i] == -DBL_MAX && U[i] == +DBL_MAX) continue; + /* obtain coefficients of the row */ + len = 0; + if (i == 0) + { for (j = 1; j <= n; j++) + { GLPCOL *col = mip->col[j]; + if (col->coef != 0.0) + len++, ind[len] = j, val[len] = col->coef; + } + } + else + { GLPROW *row = mip->row[i]; + GLPAIJ *aij; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + len++, ind[len] = aij->col->j, val[len] = aij->val; + } + /* determine lower and upper bounds of columns corresponding + to non-zero row coefficients */ + for (k = 1; k <= len; k++) + j = ind[k], lb[k] = l[j], ub[k] = u[j]; + /* prepare the row info to determine implied bounds */ + prepare_row_info(len, val, lb, ub, &f); + /* check and relax bounds of the row */ + if (check_row_bounds(&f, &L[i], &U[i])) + { /* the feasible region is empty */ + ret = 1; + goto done; + } + /* if the row became free, drop it */ + if (L[i] == -DBL_MAX && U[i] == +DBL_MAX) continue; + /* process columns having non-zero coefficients in the row */ + for (k = 1; k <= len; k++) + { GLPCOL *col; + int flag, eff; + double ll, uu; + /* take a next column in the row */ + j = ind[k], col = mip->col[j]; + flag = col->kind != GLP_CV; + /* check and tighten bounds of the column */ + if (check_col_bounds(&f, len, val, L[i], U[i], lb, ub, + flag, k, &ll, &uu)) + { /* the feasible region is empty */ + ret = 1; + goto done; + } + /* check if change in the column bounds is efficient */ + eff = check_efficiency(flag, l[j], u[j], ll, uu); + /* set new actual bounds of the column */ + l[j] = ll, u[j] = uu; + /* if the change is efficient, add all rows affected by the + corresponding column, to the list */ + if (eff > 0) + { GLPAIJ *aij; + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + { int ii = aij->row->i; + /* if the row was processed maximal number of times, + skip it */ + if (pass[ii] >= max_pass) continue; + /* if the row is free, skip it */ + if (L[ii] == -DBL_MAX && U[ii] == +DBL_MAX) continue; + /* put the row into the list */ + if (mark[ii] == 0) + { xassert(size <= m); + list[++size] = ii, mark[ii] = 1; + } + } + } + } + } +done: /* free working arrays */ + xfree(ind); + xfree(list); + xfree(mark); + xfree(pass); + xfree(val); + xfree(lb); + xfree(ub); + return ret; +} + +/*********************************************************************** +* NAME +* +* ios_preprocess_node - preprocess current subproblem +* +* SYNOPSIS +* +* #include "glpios.h" +* int ios_preprocess_node(glp_tree *tree, int max_pass); +* +* DESCRIPTION +* +* The routine ios_preprocess_node performs basic preprocessing of the +* current subproblem. +* +* RETURNS +* +* If no primal infeasibility is detected, the routine returns zero, +* otherwise non-zero. */ + +int ios_preprocess_node(glp_tree *tree, int max_pass) +{ glp_prob *mip = tree->mip; + int m = mip->m; + int n = mip->n; + int i, j, nrs, *num, ret = 0; + double *L, *U, *l, *u; + /* the current subproblem must exist */ + xassert(tree->curr != NULL); + /* determine original row bounds */ + L = xcalloc(1+m, sizeof(double)); + U = xcalloc(1+m, sizeof(double)); + switch (mip->mip_stat) + { case GLP_UNDEF: + L[0] = -DBL_MAX, U[0] = +DBL_MAX; + break; + case GLP_FEAS: + switch (mip->dir) + { case GLP_MIN: + L[0] = -DBL_MAX, U[0] = mip->mip_obj - mip->c0; + break; + case GLP_MAX: + L[0] = mip->mip_obj - mip->c0, U[0] = +DBL_MAX; + break; + default: + xassert(mip != mip); + } + break; + default: + xassert(mip != mip); + } + for (i = 1; i <= m; i++) + { L[i] = glp_get_row_lb(mip, i); + U[i] = glp_get_row_ub(mip, i); + } + /* determine original column bounds */ + l = xcalloc(1+n, sizeof(double)); + u = xcalloc(1+n, sizeof(double)); + for (j = 1; j <= n; j++) + { l[j] = glp_get_col_lb(mip, j); + u[j] = glp_get_col_ub(mip, j); + } + /* build the initial list of rows to be analyzed */ + nrs = m + 1; + num = xcalloc(1+nrs, sizeof(int)); + for (i = 1; i <= nrs; i++) num[i] = i - 1; + /* perform basic preprocessing */ + if (basic_preprocessing(mip , L, U, l, u, nrs, num, max_pass)) + { ret = 1; + goto done; + } + /* set new actual (relaxed) row bounds */ + for (i = 1; i <= m; i++) + { /* consider only non-active rows to keep dual feasibility */ + if (glp_get_row_stat(mip, i) == GLP_BS) + { if (L[i] == -DBL_MAX && U[i] == +DBL_MAX) + glp_set_row_bnds(mip, i, GLP_FR, 0.0, 0.0); + else if (U[i] == +DBL_MAX) + glp_set_row_bnds(mip, i, GLP_LO, L[i], 0.0); + else if (L[i] == -DBL_MAX) + glp_set_row_bnds(mip, i, GLP_UP, 0.0, U[i]); + } + } + /* set new actual (tightened) column bounds */ + for (j = 1; j <= n; j++) + { int type; + if (l[j] == -DBL_MAX && u[j] == +DBL_MAX) + type = GLP_FR; + else if (u[j] == +DBL_MAX) + type = GLP_LO; + else if (l[j] == -DBL_MAX) + type = GLP_UP; + else if (l[j] != u[j]) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(mip, j, type, l[j], u[j]); + } +done: /* free working arrays and return */ + xfree(L); + xfree(U); + xfree(l); + xfree(u); + xfree(num); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios03.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios03.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1155 @@ +/* glpios03.c (branch-and-cut driver) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* show_progress - display current progress of the search +* +* This routine displays some information about current progress of the +* search. +* +* The information includes: +* +* the current number of iterations performed by the simplex solver; +* +* the objective value for the best known integer feasible solution, +* which is upper (minimization) or lower (maximization) global bound +* for optimal solution of the original mip problem; +* +* the best local bound for active nodes, which is lower (minimization) +* or upper (maximization) global bound for optimal solution of the +* original mip problem; +* +* the relative mip gap, in percents; +* +* the number of open (active) subproblems; +* +* the number of completely explored subproblems, i.e. whose nodes have +* been removed from the tree. */ + +static void show_progress(glp_tree *T, int bingo) +{ int p; + double temp; + char best_mip[50], best_bound[50], *rho, rel_gap[50]; + /* format the best known integer feasible solution */ + if (T->mip->mip_stat == GLP_FEAS) + sprintf(best_mip, "%17.9e", T->mip->mip_obj); + else + sprintf(best_mip, "%17s", "not found yet"); + /* determine reference number of an active subproblem whose local + bound is best */ + p = ios_best_node(T); + /* format the best bound */ + if (p == 0) + sprintf(best_bound, "%17s", "tree is empty"); + else + { temp = T->slot[p].node->bound; + if (temp == -DBL_MAX) + sprintf(best_bound, "%17s", "-inf"); + else if (temp == +DBL_MAX) + sprintf(best_bound, "%17s", "+inf"); + else + sprintf(best_bound, "%17.9e", temp); + } + /* choose the relation sign between global bounds */ + if (T->mip->dir == GLP_MIN) + rho = ">="; + else if (T->mip->dir == GLP_MAX) + rho = "<="; + else + xassert(T != T); + /* format the relative mip gap */ + temp = ios_relative_gap(T); + if (temp == 0.0) + sprintf(rel_gap, " 0.0%%"); + else if (temp < 0.001) + sprintf(rel_gap, "< 0.1%%"); + else if (temp <= 9.999) + sprintf(rel_gap, "%5.1f%%", 100.0 * temp); + else + sprintf(rel_gap, "%6s", ""); + /* display progress of the search */ + xprintf("+%6d: %s %s %s %s %s (%d; %d)\n", + T->mip->it_cnt, bingo ? ">>>>>" : "mip =", best_mip, rho, + best_bound, rel_gap, T->a_cnt, T->t_cnt - T->n_cnt); + T->tm_lag = xtime(); + return; +} + +/*********************************************************************** +* is_branch_hopeful - check if specified branch is hopeful +* +* This routine checks if the specified subproblem can have an integer +* optimal solution which is better than the best known one. +* +* The check is based on comparison of the local objective bound stored +* in the subproblem descriptor and the incumbent objective value which +* is the global objective bound. +* +* If there is a chance that the specified subproblem can have a better +* integer optimal solution, the routine returns non-zero. Otherwise, if +* the corresponding branch can pruned, zero is returned. */ + +static int is_branch_hopeful(glp_tree *T, int p) +{ xassert(1 <= p && p <= T->nslots); + xassert(T->slot[p].node != NULL); + return ios_is_hopeful(T, T->slot[p].node->bound); +} + +/*********************************************************************** +* check_integrality - check integrality of basic solution +* +* This routine checks if the basic solution of LP relaxation of the +* current subproblem satisfies to integrality conditions, i.e. that all +* variables of integer kind have integral primal values. (The solution +* is assumed to be optimal.) +* +* For each variable of integer kind the routine computes the following +* quantity: +* +* ii(x[j]) = min(x[j] - floor(x[j]), ceil(x[j]) - x[j]), (1) +* +* which is a measure of the integer infeasibility (non-integrality) of +* x[j] (for example, ii(2.1) = 0.1, ii(3.7) = 0.3, ii(5.0) = 0). It is +* understood that 0 <= ii(x[j]) <= 0.5, and variable x[j] is integer +* feasible if ii(x[j]) = 0. However, due to floating-point arithmetic +* the routine checks less restrictive condition: +* +* ii(x[j]) <= tol_int, (2) +* +* where tol_int is a given tolerance (small positive number) and marks +* each variable which does not satisfy to (2) as integer infeasible by +* setting its fractionality flag. +* +* In order to characterize integer infeasibility of the basic solution +* in the whole the routine computes two parameters: ii_cnt, which is +* the number of variables with the fractionality flag set, and ii_sum, +* which is the sum of integer infeasibilities (1). */ + +static void check_integrality(glp_tree *T) +{ glp_prob *mip = T->mip; + int j, type, ii_cnt = 0; + double lb, ub, x, temp1, temp2, ii_sum = 0.0; + /* walk through the set of columns (structural variables) */ + for (j = 1; j <= mip->n; j++) + { GLPCOL *col = mip->col[j]; + T->non_int[j] = 0; + /* if the column is not integer, skip it */ + if (col->kind != GLP_IV) continue; + /* if the column is non-basic, it is integer feasible */ + if (col->stat != GLP_BS) continue; + /* obtain the type and bounds of the column */ + type = col->type, lb = col->lb, ub = col->ub; + /* obtain value of the column in optimal basic solution */ + x = col->prim; + /* if the column's primal value is close to the lower bound, + the column is integer feasible within given tolerance */ + if (type == GLP_LO || type == GLP_DB || type == GLP_FX) + { temp1 = lb - T->parm->tol_int; + temp2 = lb + T->parm->tol_int; + if (temp1 <= x && x <= temp2) continue; +#if 0 + /* the lower bound must not be violated */ + xassert(x >= lb); +#else + if (x < lb) continue; +#endif + } + /* if the column's primal value is close to the upper bound, + the column is integer feasible within given tolerance */ + if (type == GLP_UP || type == GLP_DB || type == GLP_FX) + { temp1 = ub - T->parm->tol_int; + temp2 = ub + T->parm->tol_int; + if (temp1 <= x && x <= temp2) continue; +#if 0 + /* the upper bound must not be violated */ + xassert(x <= ub); +#else + if (x > ub) continue; +#endif + } + /* if the column's primal value is close to nearest integer, + the column is integer feasible within given tolerance */ + temp1 = floor(x + 0.5) - T->parm->tol_int; + temp2 = floor(x + 0.5) + T->parm->tol_int; + if (temp1 <= x && x <= temp2) continue; + /* otherwise the column is integer infeasible */ + T->non_int[j] = 1; + /* increase the number of fractional-valued columns */ + ii_cnt++; + /* compute the sum of integer infeasibilities */ + temp1 = x - floor(x); + temp2 = ceil(x) - x; + xassert(temp1 > 0.0 && temp2 > 0.0); + ii_sum += (temp1 <= temp2 ? temp1 : temp2); + } + /* store ii_cnt and ii_sum to the current problem descriptor */ + xassert(T->curr != NULL); + T->curr->ii_cnt = ii_cnt; + T->curr->ii_sum = ii_sum; + /* and also display these parameters */ + if (T->parm->msg_lev >= GLP_MSG_DBG) + { if (ii_cnt == 0) + xprintf("There are no fractional columns\n"); + else if (ii_cnt == 1) + xprintf("There is one fractional column, integer infeasibil" + "ity is %.3e\n", ii_sum); + else + xprintf("There are %d fractional columns, integer infeasibi" + "lity is %.3e\n", ii_cnt, ii_sum); + } + return; +} + +/*********************************************************************** +* record_solution - record better integer feasible solution +* +* This routine records optimal basic solution of LP relaxation of the +* current subproblem, which being integer feasible is better than the +* best known integer feasible solution. */ + +static void record_solution(glp_tree *T) +{ glp_prob *mip = T->mip; + int i, j; + mip->mip_stat = GLP_FEAS; + mip->mip_obj = mip->obj_val; + for (i = 1; i <= mip->m; i++) + { GLPROW *row = mip->row[i]; + row->mipx = row->prim; + } + for (j = 1; j <= mip->n; j++) + { GLPCOL *col = mip->col[j]; + if (col->kind == GLP_CV) + col->mipx = col->prim; + else if (col->kind == GLP_IV) + { /* value of the integer column must be integral */ + col->mipx = floor(col->prim + 0.5); + } + else + xassert(col != col); + } + T->sol_cnt++; + return; +} + +/*********************************************************************** +* fix_by_red_cost - fix non-basic integer columns by reduced costs +* +* This routine fixes some non-basic integer columns if their reduced +* costs indicate that increasing (decreasing) the column at least by +* one involves the objective value becoming worse than the incumbent +* objective value. */ + +static void fix_by_red_cost(glp_tree *T) +{ glp_prob *mip = T->mip; + int j, stat, fixed = 0; + double obj, lb, ub, dj; + /* the global bound must exist */ + xassert(T->mip->mip_stat == GLP_FEAS); + /* basic solution of LP relaxation must be optimal */ + xassert(mip->pbs_stat == GLP_FEAS && mip->dbs_stat == GLP_FEAS); + /* determine the objective function value */ + obj = mip->obj_val; + /* walk through the column list */ + for (j = 1; j <= mip->n; j++) + { GLPCOL *col = mip->col[j]; + /* if the column is not integer, skip it */ + if (col->kind != GLP_IV) continue; + /* obtain bounds of j-th column */ + lb = col->lb, ub = col->ub; + /* and determine its status and reduced cost */ + stat = col->stat, dj = col->dual; + /* analyze the reduced cost */ + switch (mip->dir) + { case GLP_MIN: + /* minimization */ + if (stat == GLP_NL) + { /* j-th column is non-basic on its lower bound */ + if (dj < 0.0) dj = 0.0; + if (obj + dj >= mip->mip_obj) + glp_set_col_bnds(mip, j, GLP_FX, lb, lb), fixed++; + } + else if (stat == GLP_NU) + { /* j-th column is non-basic on its upper bound */ + if (dj > 0.0) dj = 0.0; + if (obj - dj >= mip->mip_obj) + glp_set_col_bnds(mip, j, GLP_FX, ub, ub), fixed++; + } + break; + case GLP_MAX: + /* maximization */ + if (stat == GLP_NL) + { /* j-th column is non-basic on its lower bound */ + if (dj > 0.0) dj = 0.0; + if (obj + dj <= mip->mip_obj) + glp_set_col_bnds(mip, j, GLP_FX, lb, lb), fixed++; + } + else if (stat == GLP_NU) + { /* j-th column is non-basic on its upper bound */ + if (dj < 0.0) dj = 0.0; + if (obj - dj <= mip->mip_obj) + glp_set_col_bnds(mip, j, GLP_FX, ub, ub), fixed++; + } + break; + default: + xassert(T != T); + } + } + if (T->parm->msg_lev >= GLP_MSG_DBG) + { if (fixed == 0) + /* nothing to say */; + else if (fixed == 1) + xprintf("One column has been fixed by reduced cost\n"); + else + xprintf("%d columns have been fixed by reduced costs\n", + fixed); + } + /* fixing non-basic columns on their current bounds does not + change the basic solution */ + xassert(mip->pbs_stat == GLP_FEAS && mip->dbs_stat == GLP_FEAS); + return; +} + +/*********************************************************************** +* branch_on - perform branching on specified variable +* +* This routine performs branching on j-th column (structural variable) +* of the current subproblem. The specified column must be of integer +* kind and must have a fractional value in optimal basic solution of +* LP relaxation of the current subproblem (i.e. only columns for which +* the flag non_int[j] is set are valid candidates to branch on). +* +* Let x be j-th structural variable, and beta be its primal fractional +* value in the current basic solution. Branching on j-th variable is +* dividing the current subproblem into two new subproblems, which are +* identical to the current subproblem with the following exception: in +* the first subproblem that begins the down-branch x has a new upper +* bound x <= floor(beta), and in the second subproblem that begins the +* up-branch x has a new lower bound x >= ceil(beta). +* +* Depending on estimation of local bounds for down- and up-branches +* this routine returns the following: +* +* 0 - both branches have been created; +* 1 - one branch is hopeless and has been pruned, so now the current +* subproblem is other branch; +* 2 - both branches are hopeless and have been pruned; new subproblem +* selection is needed to continue the search. */ + +static int branch_on(glp_tree *T, int j, int next) +{ glp_prob *mip = T->mip; + IOSNPD *node; + int m = mip->m; + int n = mip->n; + int type, dn_type, up_type, dn_bad, up_bad, p, ret, clone[1+2]; + double lb, ub, beta, new_ub, new_lb, dn_lp, up_lp, dn_bnd, up_bnd; + /* determine bounds and value of x[j] in optimal solution to LP + relaxation of the current subproblem */ + xassert(1 <= j && j <= n); + type = mip->col[j]->type; + lb = mip->col[j]->lb; + ub = mip->col[j]->ub; + beta = mip->col[j]->prim; + /* determine new bounds of x[j] for down- and up-branches */ + new_ub = floor(beta); + new_lb = ceil(beta); + switch (type) + { case GLP_FR: + dn_type = GLP_UP; + up_type = GLP_LO; + break; + case GLP_LO: + xassert(lb <= new_ub); + dn_type = (lb == new_ub ? GLP_FX : GLP_DB); + xassert(lb + 1.0 <= new_lb); + up_type = GLP_LO; + break; + case GLP_UP: + xassert(new_ub <= ub - 1.0); + dn_type = GLP_UP; + xassert(new_lb <= ub); + up_type = (new_lb == ub ? GLP_FX : GLP_DB); + break; + case GLP_DB: + xassert(lb <= new_ub && new_ub <= ub - 1.0); + dn_type = (lb == new_ub ? GLP_FX : GLP_DB); + xassert(lb + 1.0 <= new_lb && new_lb <= ub); + up_type = (new_lb == ub ? GLP_FX : GLP_DB); + break; + default: + xassert(type != type); + } + /* compute local bounds to LP relaxation for both branches */ + ios_eval_degrad(T, j, &dn_lp, &up_lp); + /* and improve them by rounding */ + dn_bnd = ios_round_bound(T, dn_lp); + up_bnd = ios_round_bound(T, up_lp); + /* check local bounds for down- and up-branches */ + dn_bad = !ios_is_hopeful(T, dn_bnd); + up_bad = !ios_is_hopeful(T, up_bnd); + if (dn_bad && up_bad) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Both down- and up-branches are hopeless\n"); + ret = 2; + goto done; + } + else if (up_bad) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Up-branch is hopeless\n"); + glp_set_col_bnds(mip, j, dn_type, lb, new_ub); + T->curr->lp_obj = dn_lp; + if (mip->dir == GLP_MIN) + { if (T->curr->bound < dn_bnd) + T->curr->bound = dn_bnd; + } + else if (mip->dir == GLP_MAX) + { if (T->curr->bound > dn_bnd) + T->curr->bound = dn_bnd; + } + else + xassert(mip != mip); + ret = 1; + goto done; + } + else if (dn_bad) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Down-branch is hopeless\n"); + glp_set_col_bnds(mip, j, up_type, new_lb, ub); + T->curr->lp_obj = up_lp; + if (mip->dir == GLP_MIN) + { if (T->curr->bound < up_bnd) + T->curr->bound = up_bnd; + } + else if (mip->dir == GLP_MAX) + { if (T->curr->bound > up_bnd) + T->curr->bound = up_bnd; + } + else + xassert(mip != mip); + ret = 1; + goto done; + } + /* both down- and up-branches seem to be hopeful */ + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Branching on column %d, primal value is %.9e\n", + j, beta); + /* determine the reference number of the current subproblem */ + xassert(T->curr != NULL); + p = T->curr->p; + T->curr->br_var = j; + T->curr->br_val = beta; + /* freeze the current subproblem */ + ios_freeze_node(T); + /* create two clones of the current subproblem; the first clone + begins the down-branch, the second one begins the up-branch */ + ios_clone_node(T, p, 2, clone); + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Node %d begins down branch, node %d begins up branch " + "\n", clone[1], clone[2]); + /* set new upper bound of j-th column in the down-branch */ + node = T->slot[clone[1]].node; + xassert(node != NULL); + xassert(node->up != NULL); + xassert(node->b_ptr == NULL); + node->b_ptr = dmp_get_atom(T->pool, sizeof(IOSBND)); + node->b_ptr->k = m + j; + node->b_ptr->type = (unsigned char)dn_type; + node->b_ptr->lb = lb; + node->b_ptr->ub = new_ub; + node->b_ptr->next = NULL; + node->lp_obj = dn_lp; + if (mip->dir == GLP_MIN) + { if (node->bound < dn_bnd) + node->bound = dn_bnd; + } + else if (mip->dir == GLP_MAX) + { if (node->bound > dn_bnd) + node->bound = dn_bnd; + } + else + xassert(mip != mip); + /* set new lower bound of j-th column in the up-branch */ + node = T->slot[clone[2]].node; + xassert(node != NULL); + xassert(node->up != NULL); + xassert(node->b_ptr == NULL); + node->b_ptr = dmp_get_atom(T->pool, sizeof(IOSBND)); + node->b_ptr->k = m + j; + node->b_ptr->type = (unsigned char)up_type; + node->b_ptr->lb = new_lb; + node->b_ptr->ub = ub; + node->b_ptr->next = NULL; + node->lp_obj = up_lp; + if (mip->dir == GLP_MIN) + { if (node->bound < up_bnd) + node->bound = up_bnd; + } + else if (mip->dir == GLP_MAX) + { if (node->bound > up_bnd) + node->bound = up_bnd; + } + else + xassert(mip != mip); + /* suggest the subproblem to be solved next */ + xassert(T->child == 0); + if (next == GLP_NO_BRNCH) + T->child = 0; + else if (next == GLP_DN_BRNCH) + T->child = clone[1]; + else if (next == GLP_UP_BRNCH) + T->child = clone[2]; + else + xassert(next != next); + ret = 0; +done: return ret; +} + +/*********************************************************************** +* cleanup_the_tree - prune hopeless branches from the tree +* +* This routine walks through the active list and checks the local +* bound for every active subproblem. If the local bound indicates that +* the subproblem cannot have integer optimal solution better than the +* incumbent objective value, the routine deletes such subproblem that, +* in turn, involves pruning the corresponding branch of the tree. */ + +static void cleanup_the_tree(glp_tree *T) +{ IOSNPD *node, *next_node; + int count = 0; + /* the global bound must exist */ + xassert(T->mip->mip_stat == GLP_FEAS); + /* walk through the list of active subproblems */ + for (node = T->head; node != NULL; node = next_node) + { /* deleting some active problem node may involve deleting its + parents recursively; however, all its parents being created + *before* it are always *precede* it in the node list, so + the next problem node is never affected by such deletion */ + next_node = node->next; + /* if the branch is hopeless, prune it */ + if (!is_branch_hopeful(T, node->p)) + ios_delete_node(T, node->p), count++; + } + if (T->parm->msg_lev >= GLP_MSG_DBG) + { if (count == 1) + xprintf("One hopeless branch has been pruned\n"); + else if (count > 1) + xprintf("%d hopeless branches have been pruned\n", count); + } + return; +} + +/**********************************************************************/ + +static void generate_cuts(glp_tree *T) +{ /* generate generic cuts with built-in generators */ + if (!(T->parm->mir_cuts == GLP_ON || + T->parm->gmi_cuts == GLP_ON || + T->parm->cov_cuts == GLP_ON || + T->parm->clq_cuts == GLP_ON)) goto done; +#if 1 /* 20/IX-2008 */ + { int i, max_cuts, added_cuts; + max_cuts = T->n; + if (max_cuts < 1000) max_cuts = 1000; + added_cuts = 0; + for (i = T->orig_m+1; i <= T->mip->m; i++) + { if (T->mip->row[i]->origin == GLP_RF_CUT) + added_cuts++; + } + /* xprintf("added_cuts = %d\n", added_cuts); */ + if (added_cuts >= max_cuts) goto done; + } +#endif + /* generate and add to POOL all cuts violated by x* */ + if (T->parm->gmi_cuts == GLP_ON) + { if (T->curr->changed < 5) + ios_gmi_gen(T); + } + if (T->parm->mir_cuts == GLP_ON) + { xassert(T->mir_gen != NULL); + ios_mir_gen(T, T->mir_gen); + } + if (T->parm->cov_cuts == GLP_ON) + { /* cover cuts works well along with mir cuts */ + /*if (T->round <= 5)*/ + ios_cov_gen(T); + } + if (T->parm->clq_cuts == GLP_ON) + { if (T->clq_gen != NULL) + { if (T->curr->level == 0 && T->curr->changed < 50 || + T->curr->level > 0 && T->curr->changed < 5) + ios_clq_gen(T, T->clq_gen); + } + } +done: return; +} + +/**********************************************************************/ + +static void remove_cuts(glp_tree *T) +{ /* remove inactive cuts (some valueable globally valid cut might + be saved in the global cut pool) */ + int i, cnt = 0, *num = NULL; + xassert(T->curr != NULL); + for (i = T->orig_m+1; i <= T->mip->m; i++) + { if (T->mip->row[i]->origin == GLP_RF_CUT && + T->mip->row[i]->level == T->curr->level && + T->mip->row[i]->stat == GLP_BS) + { if (num == NULL) + num = xcalloc(1+T->mip->m, sizeof(int)); + num[++cnt] = i; + } + } + if (cnt > 0) + { glp_del_rows(T->mip, cnt, num); +#if 0 + xprintf("%d inactive cut(s) removed\n", cnt); +#endif + xfree(num); + xassert(glp_factorize(T->mip) == 0); + } + return; +} + +/**********************************************************************/ + +static void display_cut_info(glp_tree *T) +{ glp_prob *mip = T->mip; + int i, gmi = 0, mir = 0, cov = 0, clq = 0, app = 0; + for (i = mip->m; i > 0; i--) + { GLPROW *row; + row = mip->row[i]; + /* if (row->level < T->curr->level) break; */ + if (row->origin == GLP_RF_CUT) + { if (row->klass == GLP_RF_GMI) + gmi++; + else if (row->klass == GLP_RF_MIR) + mir++; + else if (row->klass == GLP_RF_COV) + cov++; + else if (row->klass == GLP_RF_CLQ) + clq++; + else + app++; + } + } + xassert(T->curr != NULL); + if (gmi + mir + cov + clq + app > 0) + { xprintf("Cuts on level %d:", T->curr->level); + if (gmi > 0) xprintf(" gmi = %d;", gmi); + if (mir > 0) xprintf(" mir = %d;", mir); + if (cov > 0) xprintf(" cov = %d;", cov); + if (clq > 0) xprintf(" clq = %d;", clq); + if (app > 0) xprintf(" app = %d;", app); + xprintf("\n"); + } + return; +} + +/*********************************************************************** +* NAME +* +* ios_driver - branch-and-cut driver +* +* SYNOPSIS +* +* #include "glpios.h" +* int ios_driver(glp_tree *T); +* +* DESCRIPTION +* +* The routine ios_driver is a branch-and-cut driver. It controls the +* MIP solution process. +* +* RETURNS +* +* 0 The MIP problem instance has been successfully solved. This code +* does not necessarily mean that the solver has found optimal +* solution. It only means that the solution process was successful. +* +* GLP_EFAIL +* The search was prematurely terminated due to the solver failure. +* +* GLP_EMIPGAP +* The search was prematurely terminated, because the relative mip +* gap tolerance has been reached. +* +* GLP_ETMLIM +* The search was prematurely terminated, because the time limit has +* been exceeded. +* +* GLP_ESTOP +* The search was prematurely terminated by application. */ + +int ios_driver(glp_tree *T) +{ int p, curr_p, p_stat, d_stat, ret; +#if 1 /* carry out to glp_tree */ + int pred_p = 0; + /* if the current subproblem has been just created due to + branching, pred_p is the reference number of its parent + subproblem, otherwise pred_p is zero */ +#endif + glp_long ttt = T->tm_beg; +#if 0 + ((glp_iocp *)T->parm)->msg_lev = GLP_MSG_DBG; +#endif + /* on entry to the B&B driver it is assumed that the active list + contains the only active (i.e. root) subproblem, which is the + original MIP problem to be solved */ +loop: /* main loop starts here */ + /* at this point the current subproblem does not exist */ + xassert(T->curr == NULL); + /* if the active list is empty, the search is finished */ + if (T->head == NULL) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Active list is empty!\n"); + xassert(dmp_in_use(T->pool).lo == 0); + ret = 0; + goto done; + } + /* select some active subproblem to continue the search */ + xassert(T->next_p == 0); + /* let the application program select subproblem */ + if (T->parm->cb_func != NULL) + { xassert(T->reason == 0); + T->reason = GLP_ISELECT; + T->parm->cb_func(T, T->parm->cb_info); + T->reason = 0; + if (T->stop) + { ret = GLP_ESTOP; + goto done; + } + } + if (T->next_p != 0) + { /* the application program has selected something */ + ; + } + else if (T->a_cnt == 1) + { /* the only active subproblem exists, so select it */ + xassert(T->head->next == NULL); + T->next_p = T->head->p; + } + else if (T->child != 0) + { /* select one of branching childs suggested by the branching + heuristic */ + T->next_p = T->child; + } + else + { /* select active subproblem as specified by the backtracking + technique option */ + T->next_p = ios_choose_node(T); + } + /* the active subproblem just selected becomes current */ + ios_revive_node(T, T->next_p); + T->next_p = T->child = 0; + /* invalidate pred_p, if it is not the reference number of the + parent of the current subproblem */ + if (T->curr->up != NULL && T->curr->up->p != pred_p) pred_p = 0; + /* determine the reference number of the current subproblem */ + p = T->curr->p; + if (T->parm->msg_lev >= GLP_MSG_DBG) + { xprintf("-----------------------------------------------------" + "-------------------\n"); + xprintf("Processing node %d at level %d\n", p, T->curr->level); + } + /* if it is the root subproblem, initialize cut generators */ + if (p == 1) + { if (T->parm->gmi_cuts == GLP_ON) + { if (T->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Gomory's cuts enabled\n"); + } + if (T->parm->mir_cuts == GLP_ON) + { if (T->parm->msg_lev >= GLP_MSG_ALL) + xprintf("MIR cuts enabled\n"); + xassert(T->mir_gen == NULL); + T->mir_gen = ios_mir_init(T); + } + if (T->parm->cov_cuts == GLP_ON) + { if (T->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Cover cuts enabled\n"); + } + if (T->parm->clq_cuts == GLP_ON) + { xassert(T->clq_gen == NULL); + if (T->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Clique cuts enabled\n"); + T->clq_gen = ios_clq_init(T); + } + } +more: /* minor loop starts here */ + /* at this point the current subproblem needs either to be solved + for the first time or re-optimized due to reformulation */ + /* display current progress of the search */ + if (T->parm->msg_lev >= GLP_MSG_DBG || + T->parm->msg_lev >= GLP_MSG_ON && + (double)(T->parm->out_frq - 1) <= + 1000.0 * xdifftime(xtime(), T->tm_lag)) + show_progress(T, 0); + if (T->parm->msg_lev >= GLP_MSG_ALL && + xdifftime(xtime(), ttt) >= 60.0) + { glp_long total; + glp_mem_usage(NULL, NULL, &total, NULL); + xprintf("Time used: %.1f secs. Memory used: %.1f Mb.\n", + xdifftime(xtime(), T->tm_beg), xltod(total) / 1048576.0); + ttt = xtime(); + } + /* check the mip gap */ + if (T->parm->mip_gap > 0.0 && + ios_relative_gap(T) <= T->parm->mip_gap) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Relative gap tolerance reached; search terminated " + "\n"); + ret = GLP_EMIPGAP; + goto done; + } + /* check if the time limit has been exhausted */ + if (T->parm->tm_lim < INT_MAX && + (double)(T->parm->tm_lim - 1) <= + 1000.0 * xdifftime(xtime(), T->tm_beg)) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Time limit exhausted; search terminated\n"); + ret = GLP_ETMLIM; + goto done; + } + /* let the application program preprocess the subproblem */ + if (T->parm->cb_func != NULL) + { xassert(T->reason == 0); + T->reason = GLP_IPREPRO; + T->parm->cb_func(T, T->parm->cb_info); + T->reason = 0; + if (T->stop) + { ret = GLP_ESTOP; + goto done; + } + } + /* perform basic preprocessing */ + if (T->parm->pp_tech == GLP_PP_NONE) + ; + else if (T->parm->pp_tech == GLP_PP_ROOT) + { if (T->curr->level == 0) + { if (ios_preprocess_node(T, 100)) + goto fath; + } + } + else if (T->parm->pp_tech == GLP_PP_ALL) + { if (ios_preprocess_node(T, T->curr->level == 0 ? 100 : 10)) + goto fath; + } + else + xassert(T != T); + /* preprocessing may improve the global bound */ + if (!is_branch_hopeful(T, p)) + { xprintf("*** not tested yet ***\n"); + goto fath; + } + /* solve LP relaxation of the current subproblem */ + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Solving LP relaxation...\n"); + ret = ios_solve_node(T); + if (!(ret == 0 || ret == GLP_EOBJLL || ret == GLP_EOBJUL)) + { if (T->parm->msg_lev >= GLP_MSG_ERR) + xprintf("ios_driver: unable to solve current LP relaxation;" + " glp_simplex returned %d\n", ret); + ret = GLP_EFAIL; + goto done; + } + /* analyze status of the basic solution to LP relaxation found */ + p_stat = T->mip->pbs_stat; + d_stat = T->mip->dbs_stat; + if (p_stat == GLP_FEAS && d_stat == GLP_FEAS) + { /* LP relaxation has optimal solution */ + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Found optimal solution to LP relaxation\n"); + } + else if (d_stat == GLP_NOFEAS) + { /* LP relaxation has no dual feasible solution */ + /* since the current subproblem cannot have a larger feasible + region than its parent, there is something wrong */ + if (T->parm->msg_lev >= GLP_MSG_ERR) + xprintf("ios_driver: current LP relaxation has no dual feas" + "ible solution\n"); + ret = GLP_EFAIL; + goto done; + } + else if (p_stat == GLP_INFEAS && d_stat == GLP_FEAS) + { /* LP relaxation has no primal solution which is better than + the incumbent objective value */ + xassert(T->mip->mip_stat == GLP_FEAS); + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("LP relaxation has no solution better than incumben" + "t objective value\n"); + /* prune the branch */ + goto fath; + } + else if (p_stat == GLP_NOFEAS) + { /* LP relaxation has no primal feasible solution */ + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("LP relaxation has no feasible solution\n"); + /* prune the branch */ + goto fath; + } + else + { /* other cases cannot appear */ + xassert(T->mip != T->mip); + } + /* at this point basic solution to LP relaxation of the current + subproblem is optimal */ + xassert(p_stat == GLP_FEAS && d_stat == GLP_FEAS); + xassert(T->curr != NULL); + T->curr->lp_obj = T->mip->obj_val; + /* thus, it defines a local bound to integer optimal solution of + the current subproblem */ + { double bound = T->mip->obj_val; + /* some local bound to the current subproblem could be already + set before, so we should only improve it */ + bound = ios_round_bound(T, bound); + if (T->mip->dir == GLP_MIN) + { if (T->curr->bound < bound) + T->curr->bound = bound; + } + else if (T->mip->dir == GLP_MAX) + { if (T->curr->bound > bound) + T->curr->bound = bound; + } + else + xassert(T->mip != T->mip); + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Local bound is %.9e\n", bound); + } + /* if the local bound indicates that integer optimal solution of + the current subproblem cannot be better than the global bound, + prune the branch */ + if (!is_branch_hopeful(T, p)) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Current branch is hopeless and can be pruned\n"); + goto fath; + } + /* let the application program generate additional rows ("lazy" + constraints) */ + xassert(T->reopt == 0); + xassert(T->reinv == 0); + if (T->parm->cb_func != NULL) + { xassert(T->reason == 0); + T->reason = GLP_IROWGEN; + T->parm->cb_func(T, T->parm->cb_info); + T->reason = 0; + if (T->stop) + { ret = GLP_ESTOP; + goto done; + } + if (T->reopt) + { /* some rows were added; re-optimization is needed */ + T->reopt = T->reinv = 0; + goto more; + } + if (T->reinv) + { /* no rows were added, however, some inactive rows were + removed */ + T->reinv = 0; + xassert(glp_factorize(T->mip) == 0); + } + } + /* check if the basic solution is integer feasible */ + check_integrality(T); + /* if the basic solution satisfies to all integrality conditions, + it is a new, better integer feasible solution */ + if (T->curr->ii_cnt == 0) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("New integer feasible solution found\n"); + if (T->parm->msg_lev >= GLP_MSG_ALL) + display_cut_info(T); + record_solution(T); + if (T->parm->msg_lev >= GLP_MSG_ON) + show_progress(T, 1); + /* make the application program happy */ + if (T->parm->cb_func != NULL) + { xassert(T->reason == 0); + T->reason = GLP_IBINGO; + T->parm->cb_func(T, T->parm->cb_info); + T->reason = 0; + if (T->stop) + { ret = GLP_ESTOP; + goto done; + } + } + /* since the current subproblem has been fathomed, prune its + branch */ + goto fath; + } + /* at this point basic solution to LP relaxation of the current + subproblem is optimal, but integer infeasible */ + /* try to fix some non-basic structural variables of integer kind + on their current bounds due to reduced costs */ + if (T->mip->mip_stat == GLP_FEAS) + fix_by_red_cost(T); + /* let the application program try to find some solution to the + original MIP with a primal heuristic */ + if (T->parm->cb_func != NULL) + { xassert(T->reason == 0); + T->reason = GLP_IHEUR; + T->parm->cb_func(T, T->parm->cb_info); + T->reason = 0; + if (T->stop) + { ret = GLP_ESTOP; + goto done; + } + /* check if the current branch became hopeless */ + if (!is_branch_hopeful(T, p)) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Current branch became hopeless and can be prune" + "d\n"); + goto fath; + } + } + /* try to find solution with the feasibility pump heuristic */ + if (T->parm->fp_heur) + { xassert(T->reason == 0); + T->reason = GLP_IHEUR; + ios_feas_pump(T); + T->reason = 0; + /* check if the current branch became hopeless */ + if (!is_branch_hopeful(T, p)) + { if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Current branch became hopeless and can be prune" + "d\n"); + goto fath; + } + } + /* it's time to generate cutting planes */ + xassert(T->local != NULL); + xassert(T->local->size == 0); + /* let the application program generate some cuts; note that it + can add cuts either to the local cut pool or directly to the + current subproblem */ + if (T->parm->cb_func != NULL) + { xassert(T->reason == 0); + T->reason = GLP_ICUTGEN; + T->parm->cb_func(T, T->parm->cb_info); + T->reason = 0; + if (T->stop) + { ret = GLP_ESTOP; + goto done; + } + } + /* try to generate generic cuts with built-in generators + (as suggested by Matteo Fischetti et al. the built-in cuts + are not generated at each branching node; an intense attempt + of generating new cuts is only made at the root node, and then + a moderate effort is spent after each backtracking step) */ + if (T->curr->level == 0 || pred_p == 0) + { xassert(T->reason == 0); + T->reason = GLP_ICUTGEN; + generate_cuts(T); + T->reason = 0; + } + /* if the local cut pool is not empty, select useful cuts and add + them to the current subproblem */ + if (T->local->size > 0) + { xassert(T->reason == 0); + T->reason = GLP_ICUTGEN; + ios_process_cuts(T); + T->reason = 0; + } + /* clear the local cut pool */ + ios_clear_pool(T, T->local); + /* perform re-optimization, if necessary */ + if (T->reopt) + { T->reopt = 0; + T->curr->changed++; + goto more; + } + /* no cuts were generated; remove inactive cuts */ + remove_cuts(T); + if (T->parm->msg_lev >= GLP_MSG_ALL && T->curr->level == 0) + display_cut_info(T); + /* update history information used on pseudocost branching */ + if (T->pcost != NULL) ios_pcost_update(T); + /* it's time to perform branching */ + xassert(T->br_var == 0); + xassert(T->br_sel == 0); + /* let the application program choose variable to branch on */ + if (T->parm->cb_func != NULL) + { xassert(T->reason == 0); + xassert(T->br_var == 0); + xassert(T->br_sel == 0); + T->reason = GLP_IBRANCH; + T->parm->cb_func(T, T->parm->cb_info); + T->reason = 0; + if (T->stop) + { ret = GLP_ESTOP; + goto done; + } + } + /* if nothing has been chosen, choose some variable as specified + by the branching technique option */ + if (T->br_var == 0) + T->br_var = ios_choose_var(T, &T->br_sel); + /* perform actual branching */ + curr_p = T->curr->p; + ret = branch_on(T, T->br_var, T->br_sel); + T->br_var = T->br_sel = 0; + if (ret == 0) + { /* both branches have been created */ + pred_p = curr_p; + goto loop; + } + else if (ret == 1) + { /* one branch is hopeless and has been pruned, so now the + current subproblem is other branch */ + /* the current subproblem should be considered as a new one, + since one bound of the branching variable was changed */ + T->curr->solved = T->curr->changed = 0; + goto more; + } + else if (ret == 2) + { /* both branches are hopeless and have been pruned; new + subproblem selection is needed to continue the search */ + goto fath; + } + else + xassert(ret != ret); +fath: /* the current subproblem has been fathomed */ + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("Node %d fathomed\n", p); + /* freeze the current subproblem */ + ios_freeze_node(T); + /* and prune the corresponding branch of the tree */ + ios_delete_node(T, p); + /* if a new integer feasible solution has just been found, other + branches may become hopeless and therefore must be pruned */ + if (T->mip->mip_stat == GLP_FEAS) cleanup_the_tree(T); + /* new subproblem selection is needed due to backtracking */ + pred_p = 0; + goto loop; +done: /* display progress of the search on exit from the solver */ + if (T->parm->msg_lev >= GLP_MSG_ON) + show_progress(T, 0); + if (T->mir_gen != NULL) + ios_mir_term(T->mir_gen), T->mir_gen = NULL; + if (T->clq_gen != NULL) + ios_clq_term(T->clq_gen), T->clq_gen = NULL; + /* return to the calling program */ + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios04.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios04.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,303 @@ +/* glpios04.c (operations on sparse vectors) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* NAME +* +* ios_create_vec - create sparse vector +* +* SYNOPSIS +* +* #include "glpios.h" +* IOSVEC *ios_create_vec(int n); +* +* DESCRIPTION +* +* The routine ios_create_vec creates a sparse vector of dimension n, +* which initially is a null vector. +* +* RETURNS +* +* The routine returns a pointer to the vector created. */ + +IOSVEC *ios_create_vec(int n) +{ IOSVEC *v; + xassert(n >= 0); + v = xmalloc(sizeof(IOSVEC)); + v->n = n; + v->nnz = 0; + v->pos = xcalloc(1+n, sizeof(int)); + memset(&v->pos[1], 0, n * sizeof(int)); + v->ind = xcalloc(1+n, sizeof(int)); + v->val = xcalloc(1+n, sizeof(double)); + return v; +} + +/*********************************************************************** +* NAME +* +* ios_check_vec - check that sparse vector has correct representation +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_check_vec(IOSVEC *v); +* +* DESCRIPTION +* +* The routine ios_check_vec checks that a sparse vector specified by +* the parameter v has correct representation. +* +* NOTE +* +* Complexity of this operation is O(n). */ + +void ios_check_vec(IOSVEC *v) +{ int j, k, nnz; + xassert(v->n >= 0); + nnz = 0; + for (j = v->n; j >= 1; j--) + { k = v->pos[j]; + xassert(0 <= k && k <= v->nnz); + if (k != 0) + { xassert(v->ind[k] == j); + nnz++; + } + } + xassert(v->nnz == nnz); + return; +} + +/*********************************************************************** +* NAME +* +* ios_get_vj - retrieve component of sparse vector +* +* SYNOPSIS +* +* #include "glpios.h" +* double ios_get_vj(IOSVEC *v, int j); +* +* RETURNS +* +* The routine ios_get_vj returns j-th component of a sparse vector +* specified by the parameter v. */ + +double ios_get_vj(IOSVEC *v, int j) +{ int k; + xassert(1 <= j && j <= v->n); + k = v->pos[j]; + xassert(0 <= k && k <= v->nnz); + return (k == 0 ? 0.0 : v->val[k]); +} + +/*********************************************************************** +* NAME +* +* ios_set_vj - set/change component of sparse vector +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_set_vj(IOSVEC *v, int j, double val); +* +* DESCRIPTION +* +* The routine ios_set_vj assigns val to j-th component of a sparse +* vector specified by the parameter v. */ + +void ios_set_vj(IOSVEC *v, int j, double val) +{ int k; + xassert(1 <= j && j <= v->n); + k = v->pos[j]; + if (val == 0.0) + { if (k != 0) + { /* remove j-th component */ + v->pos[j] = 0; + if (k < v->nnz) + { v->pos[v->ind[v->nnz]] = k; + v->ind[k] = v->ind[v->nnz]; + v->val[k] = v->val[v->nnz]; + } + v->nnz--; + } + } + else + { if (k == 0) + { /* create j-th component */ + k = ++(v->nnz); + v->pos[j] = k; + v->ind[k] = j; + } + v->val[k] = val; + } + return; +} + +/*********************************************************************** +* NAME +* +* ios_clear_vec - set all components of sparse vector to zero +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_clear_vec(IOSVEC *v); +* +* DESCRIPTION +* +* The routine ios_clear_vec sets all components of a sparse vector +* specified by the parameter v to zero. */ + +void ios_clear_vec(IOSVEC *v) +{ int k; + for (k = 1; k <= v->nnz; k++) + v->pos[v->ind[k]] = 0; + v->nnz = 0; + return; +} + +/*********************************************************************** +* NAME +* +* ios_clean_vec - remove zero or small components from sparse vector +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_clean_vec(IOSVEC *v, double eps); +* +* DESCRIPTION +* +* The routine ios_clean_vec removes zero components and components +* whose magnitude is less than eps from a sparse vector specified by +* the parameter v. If eps is 0.0, only zero components are removed. */ + +void ios_clean_vec(IOSVEC *v, double eps) +{ int k, nnz; + nnz = 0; + for (k = 1; k <= v->nnz; k++) + { if (fabs(v->val[k]) == 0.0 || fabs(v->val[k]) < eps) + { /* remove component */ + v->pos[v->ind[k]] = 0; + } + else + { /* keep component */ + nnz++; + v->pos[v->ind[k]] = nnz; + v->ind[nnz] = v->ind[k]; + v->val[nnz] = v->val[k]; + } + } + v->nnz = nnz; + return; +} + +/*********************************************************************** +* NAME +* +* ios_copy_vec - copy sparse vector (x := y) +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_copy_vec(IOSVEC *x, IOSVEC *y); +* +* DESCRIPTION +* +* The routine ios_copy_vec copies a sparse vector specified by the +* parameter y to a sparse vector specified by the parameter x. */ + +void ios_copy_vec(IOSVEC *x, IOSVEC *y) +{ int j; + xassert(x != y); + xassert(x->n == y->n); + ios_clear_vec(x); + x->nnz = y->nnz; + memcpy(&x->ind[1], &y->ind[1], x->nnz * sizeof(int)); + memcpy(&x->val[1], &y->val[1], x->nnz * sizeof(double)); + for (j = 1; j <= x->nnz; j++) + x->pos[x->ind[j]] = j; + return; +} + +/*********************************************************************** +* NAME +* +* ios_linear_comb - compute linear combination (x := x + a * y) +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_linear_comb(IOSVEC *x, double a, IOSVEC *y); +* +* DESCRIPTION +* +* The routine ios_linear_comb computes the linear combination +* +* x := x + a * y, +* +* where x and y are sparse vectors, a is a scalar. */ + +void ios_linear_comb(IOSVEC *x, double a, IOSVEC *y) +{ int j, k; + double xj, yj; + xassert(x != y); + xassert(x->n == y->n); + for (k = 1; k <= y->nnz; k++) + { j = y->ind[k]; + xj = ios_get_vj(x, j); + yj = y->val[k]; + ios_set_vj(x, j, xj + a * yj); + } + return; +} + +/*********************************************************************** +* NAME +* +* ios_delete_vec - delete sparse vector +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_delete_vec(IOSVEC *v); +* +* DESCRIPTION +* +* The routine ios_delete_vec deletes a sparse vector specified by the +* parameter v freeing all the memory allocated to this object. */ + +void ios_delete_vec(IOSVEC *v) +{ /* delete sparse vector */ + xfree(v->pos); + xfree(v->ind); + xfree(v->val); + xfree(v); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios05.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios05.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,281 @@ +/* glpios05.c (Gomory's mixed integer cut generator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* NAME +* +* ios_gmi_gen - generate Gomory's mixed integer cuts. +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_gmi_gen(glp_tree *tree, IOSPOOL *pool); +* +* DESCRIPTION +* +* The routine ios_gmi_gen generates Gomory's mixed integer cuts for +* the current point and adds them to the cut pool. */ + +#define MAXCUTS 50 +/* maximal number of cuts to be generated for one round */ + +struct worka +{ /* Gomory's cut generator working area */ + int *ind; /* int ind[1+n]; */ + double *val; /* double val[1+n]; */ + double *phi; /* double phi[1+m+n]; */ +}; + +#define f(x) ((x) - floor(x)) +/* compute fractional part of x */ + +static void gen_cut(glp_tree *tree, struct worka *worka, int j) +{ /* this routine tries to generate Gomory's mixed integer cut for + specified structural variable x[m+j] of integer kind, which is + basic and has fractional value in optimal solution to current + LP relaxation */ + glp_prob *mip = tree->mip; + int m = mip->m; + int n = mip->n; + int *ind = worka->ind; + double *val = worka->val; + double *phi = worka->phi; + int i, k, len, kind, stat; + double lb, ub, alfa, beta, ksi, phi1, rhs; + /* compute row of the simplex tableau, which (row) corresponds + to specified basic variable xB[i] = x[m+j]; see (23) */ + len = glp_eval_tab_row(mip, m+j, ind, val); + /* determine beta[i], which a value of xB[i] in optimal solution + to current LP relaxation; note that this value is the same as + if it would be computed with formula (27); it is assumed that + beta[i] is fractional enough */ + beta = mip->col[j]->prim; + /* compute cut coefficients phi and right-hand side rho, which + correspond to formula (30); dense format is used, because rows + of the simplex tableau is usually dense */ + for (k = 1; k <= m+n; k++) phi[k] = 0.0; + rhs = f(beta); /* initial value of rho; see (28), (32) */ + for (j = 1; j <= len; j++) + { /* determine original number of non-basic variable xN[j] */ + k = ind[j]; + xassert(1 <= k && k <= m+n); + /* determine the kind, bounds and current status of xN[j] in + optimal solution to LP relaxation */ + if (k <= m) + { /* auxiliary variable */ + GLPROW *row = mip->row[k]; + kind = GLP_CV; + lb = row->lb; + ub = row->ub; + stat = row->stat; + } + else + { /* structural variable */ + GLPCOL *col = mip->col[k-m]; + kind = col->kind; + lb = col->lb; + ub = col->ub; + stat = col->stat; + } + /* xN[j] cannot be basic */ + xassert(stat != GLP_BS); + /* determine row coefficient ksi[i,j] at xN[j]; see (23) */ + ksi = val[j]; + /* if ksi[i,j] is too large in the magnitude, do not generate + the cut */ + if (fabs(ksi) > 1e+05) goto fini; + /* if ksi[i,j] is too small in the magnitude, skip it */ + if (fabs(ksi) < 1e-10) goto skip; + /* compute row coefficient alfa[i,j] at y[j]; see (26) */ + switch (stat) + { case GLP_NF: + /* xN[j] is free (unbounded) having non-zero ksi[i,j]; + do not generate the cut */ + goto fini; + case GLP_NL: + /* xN[j] has active lower bound */ + alfa = - ksi; + break; + case GLP_NU: + /* xN[j] has active upper bound */ + alfa = + ksi; + break; + case GLP_NS: + /* xN[j] is fixed; skip it */ + goto skip; + default: + xassert(stat != stat); + } + /* compute cut coefficient phi'[j] at y[j]; see (21), (28) */ + switch (kind) + { case GLP_IV: + /* y[j] is integer */ + if (fabs(alfa - floor(alfa + 0.5)) < 1e-10) + { /* alfa[i,j] is close to nearest integer; skip it */ + goto skip; + } + else if (f(alfa) <= f(beta)) + phi1 = f(alfa); + else + phi1 = (f(beta) / (1.0 - f(beta))) * (1.0 - f(alfa)); + break; + case GLP_CV: + /* y[j] is continuous */ + if (alfa >= 0.0) + phi1 = + alfa; + else + phi1 = (f(beta) / (1.0 - f(beta))) * (- alfa); + break; + default: + xassert(kind != kind); + } + /* compute cut coefficient phi[j] at xN[j] and update right- + hand side rho; see (31), (32) */ + switch (stat) + { case GLP_NL: + /* xN[j] has active lower bound */ + phi[k] = + phi1; + rhs += phi1 * lb; + break; + case GLP_NU: + /* xN[j] has active upper bound */ + phi[k] = - phi1; + rhs -= phi1 * ub; + break; + default: + xassert(stat != stat); + } +skip: ; + } + /* now the cut has the form sum_k phi[k] * x[k] >= rho, where cut + coefficients are stored in the array phi in dense format; + x[1,...,m] are auxiliary variables, x[m+1,...,m+n] are struc- + tural variables; see (30) */ + /* eliminate auxiliary variables in order to express the cut only + through structural variables; see (33) */ + for (i = 1; i <= m; i++) + { GLPROW *row; + GLPAIJ *aij; + if (fabs(phi[i]) < 1e-10) continue; + /* auxiliary variable x[i] has non-zero cut coefficient */ + row = mip->row[i]; + /* x[i] cannot be fixed */ + xassert(row->type != GLP_FX); + /* substitute x[i] = sum_j a[i,j] * x[m+j] */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + phi[m+aij->col->j] += phi[i] * aij->val; + } + /* convert the final cut to sparse format and substitute fixed + (structural) variables */ + len = 0; + for (j = 1; j <= n; j++) + { GLPCOL *col; + if (fabs(phi[m+j]) < 1e-10) continue; + /* structural variable x[m+j] has non-zero cut coefficient */ + col = mip->col[j]; + if (col->type == GLP_FX) + { /* eliminate x[m+j] */ + rhs -= phi[m+j] * col->lb; + } + else + { len++; + ind[len] = j; + val[len] = phi[m+j]; + } + } + if (fabs(rhs) < 1e-12) rhs = 0.0; + /* if the cut inequality seems to be badly scaled, reject it to + avoid numeric difficulties */ + for (k = 1; k <= len; k++) + { if (fabs(val[k]) < 1e-03) goto fini; + if (fabs(val[k]) > 1e+03) goto fini; + } + /* add the cut to the cut pool for further consideration */ +#if 0 + ios_add_cut_row(tree, pool, GLP_RF_GMI, len, ind, val, GLP_LO, + rhs); +#else + glp_ios_add_row(tree, NULL, GLP_RF_GMI, 0, len, ind, val, GLP_LO, + rhs); +#endif +fini: return; +} + +struct var { int j; double f; }; + +static int fcmp(const void *p1, const void *p2) +{ const struct var *v1 = p1, *v2 = p2; + if (v1->f > v2->f) return -1; + if (v1->f < v2->f) return +1; + return 0; +} + +void ios_gmi_gen(glp_tree *tree) +{ /* main routine to generate Gomory's cuts */ + glp_prob *mip = tree->mip; + int m = mip->m; + int n = mip->n; + struct var *var; + int k, nv, j, size; + struct worka _worka, *worka = &_worka; + /* allocate working arrays */ + var = xcalloc(1+n, sizeof(struct var)); + worka->ind = xcalloc(1+n, sizeof(int)); + worka->val = xcalloc(1+n, sizeof(double)); + worka->phi = xcalloc(1+m+n, sizeof(double)); + /* build the list of integer structural variables, which are + basic and have fractional value in optimal solution to current + LP relaxation */ + nv = 0; + for (j = 1; j <= n; j++) + { GLPCOL *col = mip->col[j]; + double frac; + if (col->kind != GLP_IV) continue; + if (col->type == GLP_FX) continue; + if (col->stat != GLP_BS) continue; + frac = f(col->prim); + if (!(0.05 <= frac && frac <= 0.95)) continue; + /* add variable to the list */ + nv++, var[nv].j = j, var[nv].f = frac; + } + /* order the list by descending fractionality */ + qsort(&var[1], nv, sizeof(struct var), fcmp); + /* try to generate cuts by one for each variable in the list, but + not more than MAXCUTS cuts */ + size = glp_ios_pool_size(tree); + for (k = 1; k <= nv; k++) + { if (glp_ios_pool_size(tree) - size >= MAXCUTS) break; + gen_cut(tree, worka, var[k].j); + } + /* free working arrays */ + xfree(var); + xfree(worka->ind); + xfree(worka->val); + xfree(worka->phi); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios06.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios06.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1447 @@ +/* glpios06.c (MIR cut generator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +#define _MIR_DEBUG 0 + +#define MAXAGGR 5 +/* maximal number of rows which can be aggregated */ + +struct MIR +{ /* MIR cut generator working area */ + /*--------------------------------------------------------------*/ + /* global information valid for the root subproblem */ + int m; + /* number of rows (in the root subproblem) */ + int n; + /* number of columns */ + char *skip; /* char skip[1+m]; */ + /* skip[i], 1 <= i <= m, is a flag that means that row i should + not be used because (1) it is not suitable, or (2) because it + has been used in the aggregated constraint */ + char *isint; /* char isint[1+m+n]; */ + /* isint[k], 1 <= k <= m+n, is a flag that means that variable + x[k] is integer (otherwise, continuous) */ + double *lb; /* double lb[1+m+n]; */ + /* lb[k], 1 <= k <= m+n, is lower bound of x[k]; -DBL_MAX means + that x[k] has no lower bound */ + int *vlb; /* int vlb[1+m+n]; */ + /* vlb[k] = k', 1 <= k <= m+n, is the number of integer variable, + which defines variable lower bound x[k] >= lb[k] * x[k']; zero + means that x[k] has simple lower bound */ + double *ub; /* double ub[1+m+n]; */ + /* ub[k], 1 <= k <= m+n, is upper bound of x[k]; +DBL_MAX means + that x[k] has no upper bound */ + int *vub; /* int vub[1+m+n]; */ + /* vub[k] = k', 1 <= k <= m+n, is the number of integer variable, + which defines variable upper bound x[k] <= ub[k] * x[k']; zero + means that x[k] has simple upper bound */ + /*--------------------------------------------------------------*/ + /* current (fractional) point to be separated */ + double *x; /* double x[1+m+n]; */ + /* x[k] is current value of auxiliary (1 <= k <= m) or structural + (m+1 <= k <= m+n) variable */ + /*--------------------------------------------------------------*/ + /* aggregated constraint sum a[k] * x[k] = b, which is a linear + combination of original constraints transformed to equalities + by introducing auxiliary variables */ + int agg_cnt; + /* number of rows (original constraints) used to build aggregated + constraint, 1 <= agg_cnt <= MAXAGGR */ + int *agg_row; /* int agg_row[1+MAXAGGR]; */ + /* agg_row[k], 1 <= k <= agg_cnt, is the row number used to build + aggregated constraint */ + IOSVEC *agg_vec; /* IOSVEC agg_vec[1:m+n]; */ + /* sparse vector of aggregated constraint coefficients, a[k] */ + double agg_rhs; + /* right-hand side of the aggregated constraint, b */ + /*--------------------------------------------------------------*/ + /* bound substitution flags for modified constraint */ + char *subst; /* char subst[1+m+n]; */ + /* subst[k], 1 <= k <= m+n, is a bound substitution flag used for + variable x[k]: + '?' - x[k] is missing in modified constraint + 'L' - x[k] = (lower bound) + x'[k] + 'U' - x[k] = (upper bound) - x'[k] */ + /*--------------------------------------------------------------*/ + /* modified constraint sum a'[k] * x'[k] = b', where x'[k] >= 0, + derived from aggregated constraint by substituting bounds; + note that due to substitution of variable bounds there may be + additional terms in the modified constraint */ + IOSVEC *mod_vec; /* IOSVEC mod_vec[1:m+n]; */ + /* sparse vector of modified constraint coefficients, a'[k] */ + double mod_rhs; + /* right-hand side of the modified constraint, b' */ + /*--------------------------------------------------------------*/ + /* cutting plane sum alpha[k] * x[k] <= beta */ + IOSVEC *cut_vec; /* IOSVEC cut_vec[1:m+n]; */ + /* sparse vector of cutting plane coefficients, alpha[k] */ + double cut_rhs; + /* right-hand size of the cutting plane, beta */ +}; + +/*********************************************************************** +* NAME +* +* ios_mir_init - initialize MIR cut generator +* +* SYNOPSIS +* +* #include "glpios.h" +* void *ios_mir_init(glp_tree *tree); +* +* DESCRIPTION +* +* The routine ios_mir_init initializes the MIR cut generator assuming +* that the current subproblem is the root subproblem. +* +* RETURNS +* +* The routine ios_mir_init returns a pointer to the MIR cut generator +* working area. */ + +static void set_row_attrib(glp_tree *tree, struct MIR *mir) +{ /* set global row attributes */ + glp_prob *mip = tree->mip; + int m = mir->m; + int k; + for (k = 1; k <= m; k++) + { GLPROW *row = mip->row[k]; + mir->skip[k] = 0; + mir->isint[k] = 0; + switch (row->type) + { case GLP_FR: + mir->lb[k] = -DBL_MAX, mir->ub[k] = +DBL_MAX; break; + case GLP_LO: + mir->lb[k] = row->lb, mir->ub[k] = +DBL_MAX; break; + case GLP_UP: + mir->lb[k] = -DBL_MAX, mir->ub[k] = row->ub; break; + case GLP_DB: + mir->lb[k] = row->lb, mir->ub[k] = row->ub; break; + case GLP_FX: + mir->lb[k] = mir->ub[k] = row->lb; break; + default: + xassert(row != row); + } + mir->vlb[k] = mir->vub[k] = 0; + } + return; +} + +static void set_col_attrib(glp_tree *tree, struct MIR *mir) +{ /* set global column attributes */ + glp_prob *mip = tree->mip; + int m = mir->m; + int n = mir->n; + int k; + for (k = m+1; k <= m+n; k++) + { GLPCOL *col = mip->col[k-m]; + switch (col->kind) + { case GLP_CV: + mir->isint[k] = 0; break; + case GLP_IV: + mir->isint[k] = 1; break; + default: + xassert(col != col); + } + switch (col->type) + { case GLP_FR: + mir->lb[k] = -DBL_MAX, mir->ub[k] = +DBL_MAX; break; + case GLP_LO: + mir->lb[k] = col->lb, mir->ub[k] = +DBL_MAX; break; + case GLP_UP: + mir->lb[k] = -DBL_MAX, mir->ub[k] = col->ub; break; + case GLP_DB: + mir->lb[k] = col->lb, mir->ub[k] = col->ub; break; + case GLP_FX: + mir->lb[k] = mir->ub[k] = col->lb; break; + default: + xassert(col != col); + } + mir->vlb[k] = mir->vub[k] = 0; + } + return; +} + +static void set_var_bounds(glp_tree *tree, struct MIR *mir) +{ /* set variable bounds */ + glp_prob *mip = tree->mip; + int m = mir->m; + GLPAIJ *aij; + int i, k1, k2; + double a1, a2; + for (i = 1; i <= m; i++) + { /* we need the row to be '>= 0' or '<= 0' */ + if (!(mir->lb[i] == 0.0 && mir->ub[i] == +DBL_MAX || + mir->lb[i] == -DBL_MAX && mir->ub[i] == 0.0)) continue; + /* take first term */ + aij = mip->row[i]->ptr; + if (aij == NULL) continue; + k1 = m + aij->col->j, a1 = aij->val; + /* take second term */ + aij = aij->r_next; + if (aij == NULL) continue; + k2 = m + aij->col->j, a2 = aij->val; + /* there must be only two terms */ + if (aij->r_next != NULL) continue; + /* interchange terms, if needed */ + if (!mir->isint[k1] && mir->isint[k2]) + ; + else if (mir->isint[k1] && !mir->isint[k2]) + { k2 = k1, a2 = a1; + k1 = m + aij->col->j, a1 = aij->val; + } + else + { /* both terms are either continuous or integer */ + continue; + } + /* x[k2] should be double-bounded */ + if (mir->lb[k2] == -DBL_MAX || mir->ub[k2] == +DBL_MAX || + mir->lb[k2] == mir->ub[k2]) continue; + /* change signs, if necessary */ + if (mir->ub[i] == 0.0) a1 = - a1, a2 = - a2; + /* now the row has the form a1 * x1 + a2 * x2 >= 0, where x1 + is continuous, x2 is integer */ + if (a1 > 0.0) + { /* x1 >= - (a2 / a1) * x2 */ + if (mir->vlb[k1] == 0) + { /* set variable lower bound for x1 */ + mir->lb[k1] = - a2 / a1; + mir->vlb[k1] = k2; + /* the row should not be used */ + mir->skip[i] = 1; + } + } + else /* a1 < 0.0 */ + { /* x1 <= - (a2 / a1) * x2 */ + if (mir->vub[k1] == 0) + { /* set variable upper bound for x1 */ + mir->ub[k1] = - a2 / a1; + mir->vub[k1] = k2; + /* the row should not be used */ + mir->skip[i] = 1; + } + } + } + return; +} + +static void mark_useless_rows(glp_tree *tree, struct MIR *mir) +{ /* mark rows which should not be used */ + glp_prob *mip = tree->mip; + int m = mir->m; + GLPAIJ *aij; + int i, k, nv; + for (i = 1; i <= m; i++) + { /* free rows should not be used */ + if (mir->lb[i] == -DBL_MAX && mir->ub[i] == +DBL_MAX) + { mir->skip[i] = 1; + continue; + } + nv = 0; + for (aij = mip->row[i]->ptr; aij != NULL; aij = aij->r_next) + { k = m + aij->col->j; + /* rows with free variables should not be used */ + if (mir->lb[k] == -DBL_MAX && mir->ub[k] == +DBL_MAX) + { mir->skip[i] = 1; + break; + } + /* rows with integer variables having infinite (lower or + upper) bound should not be used */ + if (mir->isint[k] && mir->lb[k] == -DBL_MAX || + mir->isint[k] && mir->ub[k] == +DBL_MAX) + { mir->skip[i] = 1; + break; + } + /* count non-fixed variables */ + if (!(mir->vlb[k] == 0 && mir->vub[k] == 0 && + mir->lb[k] == mir->ub[k])) nv++; + } + /* rows with all variables fixed should not be used */ + if (nv == 0) + { mir->skip[i] = 1; + continue; + } + } + return; +} + +void *ios_mir_init(glp_tree *tree) +{ /* initialize MIR cut generator */ + glp_prob *mip = tree->mip; + int m = mip->m; + int n = mip->n; + struct MIR *mir; +#if _MIR_DEBUG + xprintf("ios_mir_init: warning: debug mode enabled\n"); +#endif + /* allocate working area */ + mir = xmalloc(sizeof(struct MIR)); + mir->m = m; + mir->n = n; + mir->skip = xcalloc(1+m, sizeof(char)); + mir->isint = xcalloc(1+m+n, sizeof(char)); + mir->lb = xcalloc(1+m+n, sizeof(double)); + mir->vlb = xcalloc(1+m+n, sizeof(int)); + mir->ub = xcalloc(1+m+n, sizeof(double)); + mir->vub = xcalloc(1+m+n, sizeof(int)); + mir->x = xcalloc(1+m+n, sizeof(double)); + mir->agg_row = xcalloc(1+MAXAGGR, sizeof(int)); + mir->agg_vec = ios_create_vec(m+n); + mir->subst = xcalloc(1+m+n, sizeof(char)); + mir->mod_vec = ios_create_vec(m+n); + mir->cut_vec = ios_create_vec(m+n); + /* set global row attributes */ + set_row_attrib(tree, mir); + /* set global column attributes */ + set_col_attrib(tree, mir); + /* set variable bounds */ + set_var_bounds(tree, mir); + /* mark rows which should not be used */ + mark_useless_rows(tree, mir); + return mir; +} + +/*********************************************************************** +* NAME +* +* ios_mir_gen - generate MIR cuts +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_mir_gen(glp_tree *tree, void *gen, IOSPOOL *pool); +* +* DESCRIPTION +* +* The routine ios_mir_gen generates MIR cuts for the current point and +* adds them to the cut pool. */ + +static void get_current_point(glp_tree *tree, struct MIR *mir) +{ /* obtain current point */ + glp_prob *mip = tree->mip; + int m = mir->m; + int n = mir->n; + int k; + for (k = 1; k <= m; k++) + mir->x[k] = mip->row[k]->prim; + for (k = m+1; k <= m+n; k++) + mir->x[k] = mip->col[k-m]->prim; + return; +} + +#if _MIR_DEBUG +static void check_current_point(struct MIR *mir) +{ /* check current point */ + int m = mir->m; + int n = mir->n; + int k, kk; + double lb, ub, eps; + for (k = 1; k <= m+n; k++) + { /* determine lower bound */ + lb = mir->lb[k]; + kk = mir->vlb[k]; + if (kk != 0) + { xassert(lb != -DBL_MAX); + xassert(!mir->isint[k]); + xassert(mir->isint[kk]); + lb *= mir->x[kk]; + } + /* check lower bound */ + if (lb != -DBL_MAX) + { eps = 1e-6 * (1.0 + fabs(lb)); + xassert(mir->x[k] >= lb - eps); + } + /* determine upper bound */ + ub = mir->ub[k]; + kk = mir->vub[k]; + if (kk != 0) + { xassert(ub != +DBL_MAX); + xassert(!mir->isint[k]); + xassert(mir->isint[kk]); + ub *= mir->x[kk]; + } + /* check upper bound */ + if (ub != +DBL_MAX) + { eps = 1e-6 * (1.0 + fabs(ub)); + xassert(mir->x[k] <= ub + eps); + } + } + return; +} +#endif + +static void initial_agg_row(glp_tree *tree, struct MIR *mir, int i) +{ /* use original i-th row as initial aggregated constraint */ + glp_prob *mip = tree->mip; + int m = mir->m; + GLPAIJ *aij; + xassert(1 <= i && i <= m); + xassert(!mir->skip[i]); + /* mark i-th row in order not to use it in the same aggregated + constraint */ + mir->skip[i] = 2; + mir->agg_cnt = 1; + mir->agg_row[1] = i; + /* use x[i] - sum a[i,j] * x[m+j] = 0, where x[i] is auxiliary + variable of row i, x[m+j] are structural variables */ + ios_clear_vec(mir->agg_vec); + ios_set_vj(mir->agg_vec, i, 1.0); + for (aij = mip->row[i]->ptr; aij != NULL; aij = aij->r_next) + ios_set_vj(mir->agg_vec, m + aij->col->j, - aij->val); + mir->agg_rhs = 0.0; +#if _MIR_DEBUG + ios_check_vec(mir->agg_vec); +#endif + return; +} + +#if _MIR_DEBUG +static void check_agg_row(struct MIR *mir) +{ /* check aggregated constraint */ + int m = mir->m; + int n = mir->n; + int j, k; + double r, big; + /* compute the residual r = sum a[k] * x[k] - b and determine + big = max(1, |a[k]|, |b|) */ + r = 0.0, big = 1.0; + for (j = 1; j <= mir->agg_vec->nnz; j++) + { k = mir->agg_vec->ind[j]; + xassert(1 <= k && k <= m+n); + r += mir->agg_vec->val[j] * mir->x[k]; + if (big < fabs(mir->agg_vec->val[j])) + big = fabs(mir->agg_vec->val[j]); + } + r -= mir->agg_rhs; + if (big < fabs(mir->agg_rhs)) + big = fabs(mir->agg_rhs); + /* the residual must be close to zero */ + xassert(fabs(r) <= 1e-6 * big); + return; +} +#endif + +static void subst_fixed_vars(struct MIR *mir) +{ /* substitute fixed variables into aggregated constraint */ + int m = mir->m; + int n = mir->n; + int j, k; + for (j = 1; j <= mir->agg_vec->nnz; j++) + { k = mir->agg_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (mir->vlb[k] == 0 && mir->vub[k] == 0 && + mir->lb[k] == mir->ub[k]) + { /* x[k] is fixed */ + mir->agg_rhs -= mir->agg_vec->val[j] * mir->lb[k]; + mir->agg_vec->val[j] = 0.0; + } + } + /* remove terms corresponding to fixed variables */ + ios_clean_vec(mir->agg_vec, DBL_EPSILON); +#if _MIR_DEBUG + ios_check_vec(mir->agg_vec); +#endif + return; +} + +static void bound_subst_heur(struct MIR *mir) +{ /* bound substitution heuristic */ + int m = mir->m; + int n = mir->n; + int j, k, kk; + double d1, d2; + for (j = 1; j <= mir->agg_vec->nnz; j++) + { k = mir->agg_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (mir->isint[k]) continue; /* skip integer variable */ + /* compute distance from x[k] to its lower bound */ + kk = mir->vlb[k]; + if (kk == 0) + { if (mir->lb[k] == -DBL_MAX) + d1 = DBL_MAX; + else + d1 = mir->x[k] - mir->lb[k]; + } + else + { xassert(1 <= kk && kk <= m+n); + xassert(mir->isint[kk]); + xassert(mir->lb[k] != -DBL_MAX); + d1 = mir->x[k] - mir->lb[k] * mir->x[kk]; + } + /* compute distance from x[k] to its upper bound */ + kk = mir->vub[k]; + if (kk == 0) + { if (mir->vub[k] == +DBL_MAX) + d2 = DBL_MAX; + else + d2 = mir->ub[k] - mir->x[k]; + } + else + { xassert(1 <= kk && kk <= m+n); + xassert(mir->isint[kk]); + xassert(mir->ub[k] != +DBL_MAX); + d2 = mir->ub[k] * mir->x[kk] - mir->x[k]; + } + /* x[k] cannot be free */ + xassert(d1 != DBL_MAX || d2 != DBL_MAX); + /* choose the bound which is closer to x[k] */ + xassert(mir->subst[k] == '?'); + if (d1 <= d2) + mir->subst[k] = 'L'; + else + mir->subst[k] = 'U'; + } + return; +} + +static void build_mod_row(struct MIR *mir) +{ /* substitute bounds and build modified constraint */ + int m = mir->m; + int n = mir->n; + int j, jj, k, kk; + /* initially modified constraint is aggregated constraint */ + ios_copy_vec(mir->mod_vec, mir->agg_vec); + mir->mod_rhs = mir->agg_rhs; +#if _MIR_DEBUG + ios_check_vec(mir->mod_vec); +#endif + /* substitute bounds for continuous variables; note that due to + substitution of variable bounds additional terms may appear in + modified constraint */ + for (j = mir->mod_vec->nnz; j >= 1; j--) + { k = mir->mod_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (mir->isint[k]) continue; /* skip integer variable */ + if (mir->subst[k] == 'L') + { /* x[k] = (lower bound) + x'[k] */ + xassert(mir->lb[k] != -DBL_MAX); + kk = mir->vlb[k]; + if (kk == 0) + { /* x[k] = lb[k] + x'[k] */ + mir->mod_rhs -= mir->mod_vec->val[j] * mir->lb[k]; + } + else + { /* x[k] = lb[k] * x[kk] + x'[k] */ + xassert(mir->isint[kk]); + jj = mir->mod_vec->pos[kk]; + if (jj == 0) + { ios_set_vj(mir->mod_vec, kk, 1.0); + jj = mir->mod_vec->pos[kk]; + mir->mod_vec->val[jj] = 0.0; + } + mir->mod_vec->val[jj] += + mir->mod_vec->val[j] * mir->lb[k]; + } + } + else if (mir->subst[k] == 'U') + { /* x[k] = (upper bound) - x'[k] */ + xassert(mir->ub[k] != +DBL_MAX); + kk = mir->vub[k]; + if (kk == 0) + { /* x[k] = ub[k] - x'[k] */ + mir->mod_rhs -= mir->mod_vec->val[j] * mir->ub[k]; + } + else + { /* x[k] = ub[k] * x[kk] - x'[k] */ + xassert(mir->isint[kk]); + jj = mir->mod_vec->pos[kk]; + if (jj == 0) + { ios_set_vj(mir->mod_vec, kk, 1.0); + jj = mir->mod_vec->pos[kk]; + mir->mod_vec->val[jj] = 0.0; + } + mir->mod_vec->val[jj] += + mir->mod_vec->val[j] * mir->ub[k]; + } + mir->mod_vec->val[j] = - mir->mod_vec->val[j]; + } + else + xassert(k != k); + } +#if _MIR_DEBUG + ios_check_vec(mir->mod_vec); +#endif + /* substitute bounds for integer variables */ + for (j = 1; j <= mir->mod_vec->nnz; j++) + { k = mir->mod_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (!mir->isint[k]) continue; /* skip continuous variable */ + xassert(mir->subst[k] == '?'); + xassert(mir->vlb[k] == 0 && mir->vub[k] == 0); + xassert(mir->lb[k] != -DBL_MAX && mir->ub[k] != +DBL_MAX); + if (fabs(mir->lb[k]) <= fabs(mir->ub[k])) + { /* x[k] = lb[k] + x'[k] */ + mir->subst[k] = 'L'; + mir->mod_rhs -= mir->mod_vec->val[j] * mir->lb[k]; + } + else + { /* x[k] = ub[k] - x'[k] */ + mir->subst[k] = 'U'; + mir->mod_rhs -= mir->mod_vec->val[j] * mir->ub[k]; + mir->mod_vec->val[j] = - mir->mod_vec->val[j]; + } + } +#if _MIR_DEBUG + ios_check_vec(mir->mod_vec); +#endif + return; +} + +#if _MIR_DEBUG +static void check_mod_row(struct MIR *mir) +{ /* check modified constraint */ + int m = mir->m; + int n = mir->n; + int j, k, kk; + double r, big, x; + /* compute the residual r = sum a'[k] * x'[k] - b' and determine + big = max(1, |a[k]|, |b|) */ + r = 0.0, big = 1.0; + for (j = 1; j <= mir->mod_vec->nnz; j++) + { k = mir->mod_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (mir->subst[k] == 'L') + { /* x'[k] = x[k] - (lower bound) */ + xassert(mir->lb[k] != -DBL_MAX); + kk = mir->vlb[k]; + if (kk == 0) + x = mir->x[k] - mir->lb[k]; + else + x = mir->x[k] - mir->lb[k] * mir->x[kk]; + } + else if (mir->subst[k] == 'U') + { /* x'[k] = (upper bound) - x[k] */ + xassert(mir->ub[k] != +DBL_MAX); + kk = mir->vub[k]; + if (kk == 0) + x = mir->ub[k] - mir->x[k]; + else + x = mir->ub[k] * mir->x[kk] - mir->x[k]; + } + else + xassert(k != k); + r += mir->mod_vec->val[j] * x; + if (big < fabs(mir->mod_vec->val[j])) + big = fabs(mir->mod_vec->val[j]); + } + r -= mir->mod_rhs; + if (big < fabs(mir->mod_rhs)) + big = fabs(mir->mod_rhs); + /* the residual must be close to zero */ + xassert(fabs(r) <= 1e-6 * big); + return; +} +#endif + +/*********************************************************************** +* mir_ineq - construct MIR inequality +* +* Given the single constraint mixed integer set +* +* |N| +* X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s}, +* + + j in N +* +* this routine constructs the mixed integer rounding (MIR) inequality +* +* sum alpha[j] * x[j] <= beta + gamma * s, +* j in N +* +* which is valid for X. +* +* If the MIR inequality has been successfully constructed, the routine +* returns zero. Otherwise, if b is close to nearest integer, there may +* be numeric difficulties due to big coefficients; so in this case the +* routine returns non-zero. */ + +static int mir_ineq(const int n, const double a[], const double b, + double alpha[], double *beta, double *gamma) +{ int j; + double f, t; + if (fabs(b - floor(b + .5)) < 0.01) + return 1; + f = b - floor(b); + for (j = 1; j <= n; j++) + { t = (a[j] - floor(a[j])) - f; + if (t <= 0.0) + alpha[j] = floor(a[j]); + else + alpha[j] = floor(a[j]) + t / (1.0 - f); + } + *beta = floor(b); + *gamma = 1.0 / (1.0 - f); + return 0; +} + +/*********************************************************************** +* cmir_ineq - construct c-MIR inequality +* +* Given the mixed knapsack set +* +* MK |N| +* X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s, +* + + j in N +* +* x[j] <= u[j]}, +* +* a subset C of variables to be complemented, and a divisor delta > 0, +* this routine constructs the complemented MIR (c-MIR) inequality +* +* sum alpha[j] * x[j] <= beta + gamma * s, +* j in N +* MK +* which is valid for X . +* +* If the c-MIR inequality has been successfully constructed, the +* routine returns zero. Otherwise, if there is a risk of numerical +* difficulties due to big coefficients (see comments to the routine +* mir_ineq), the routine cmir_ineq returns non-zero. */ + +static int cmir_ineq(const int n, const double a[], const double b, + const double u[], const char cset[], const double delta, + double alpha[], double *beta, double *gamma) +{ int j; + double *aa, bb; + aa = alpha, bb = b; + for (j = 1; j <= n; j++) + { aa[j] = a[j] / delta; + if (cset[j]) + aa[j] = - aa[j], bb -= a[j] * u[j]; + } + bb /= delta; + if (mir_ineq(n, aa, bb, alpha, beta, gamma)) return 1; + for (j = 1; j <= n; j++) + { if (cset[j]) + alpha[j] = - alpha[j], *beta += alpha[j] * u[j]; + } + *gamma /= delta; + return 0; +} + +/*********************************************************************** +* cmir_sep - c-MIR separation heuristic +* +* Given the mixed knapsack set +* +* MK |N| +* X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s, +* + + j in N +* +* x[j] <= u[j]} +* +* * * +* and a fractional point (x , s ), this routine tries to construct +* c-MIR inequality +* +* sum alpha[j] * x[j] <= beta + gamma * s, +* j in N +* MK +* which is valid for X and has (desirably maximal) violation at the +* fractional point given. This is attained by choosing an appropriate +* set C of variables to be complemented and a divisor delta > 0, which +* together define corresponding c-MIR inequality. +* +* If a violated c-MIR inequality has been successfully constructed, +* the routine returns its violation: +* +* * * +* sum alpha[j] * x [j] - beta - gamma * s , +* j in N +* +* which is positive. In case of failure the routine returns zero. */ + +struct vset { int j; double v; }; + +static int cmir_cmp(const void *p1, const void *p2) +{ const struct vset *v1 = p1, *v2 = p2; + if (v1->v < v2->v) return -1; + if (v1->v > v2->v) return +1; + return 0; +} + +static double cmir_sep(const int n, const double a[], const double b, + const double u[], const double x[], const double s, + double alpha[], double *beta, double *gamma) +{ int fail, j, k, nv, v; + double delta, eps, d_try[1+3], r, r_best; + char *cset; + struct vset *vset; + /* allocate working arrays */ + cset = xcalloc(1+n, sizeof(char)); + vset = xcalloc(1+n, sizeof(struct vset)); + /* choose initial C */ + for (j = 1; j <= n; j++) + cset[j] = (char)(x[j] >= 0.5 * u[j]); + /* choose initial delta */ + r_best = delta = 0.0; + for (j = 1; j <= n; j++) + { xassert(a[j] != 0.0); + /* if x[j] is close to its bounds, skip it */ + eps = 1e-9 * (1.0 + fabs(u[j])); + if (x[j] < eps || x[j] > u[j] - eps) continue; + /* try delta = |a[j]| to construct c-MIR inequality */ + fail = cmir_ineq(n, a, b, u, cset, fabs(a[j]), alpha, beta, + gamma); + if (fail) continue; + /* compute violation */ + r = - (*beta) - (*gamma) * s; + for (k = 1; k <= n; k++) r += alpha[k] * x[k]; + if (r_best < r) r_best = r, delta = fabs(a[j]); + } + if (r_best < 0.001) r_best = 0.0; + if (r_best == 0.0) goto done; + xassert(delta > 0.0); + /* try to increase violation by dividing delta by 2, 4, and 8, + respectively */ + d_try[1] = delta / 2.0; + d_try[2] = delta / 4.0; + d_try[3] = delta / 8.0; + for (j = 1; j <= 3; j++) + { /* construct c-MIR inequality */ + fail = cmir_ineq(n, a, b, u, cset, d_try[j], alpha, beta, + gamma); + if (fail) continue; + /* compute violation */ + r = - (*beta) - (*gamma) * s; + for (k = 1; k <= n; k++) r += alpha[k] * x[k]; + if (r_best < r) r_best = r, delta = d_try[j]; + } + /* build subset of variables lying strictly between their bounds + and order it by nondecreasing values of |x[j] - u[j]/2| */ + nv = 0; + for (j = 1; j <= n; j++) + { /* if x[j] is close to its bounds, skip it */ + eps = 1e-9 * (1.0 + fabs(u[j])); + if (x[j] < eps || x[j] > u[j] - eps) continue; + /* add x[j] to the subset */ + nv++; + vset[nv].j = j; + vset[nv].v = fabs(x[j] - 0.5 * u[j]); + } + qsort(&vset[1], nv, sizeof(struct vset), cmir_cmp); + /* try to increase violation by successively complementing each + variable in the subset */ + for (v = 1; v <= nv; v++) + { j = vset[v].j; + /* replace x[j] by its complement or vice versa */ + cset[j] = (char)!cset[j]; + /* construct c-MIR inequality */ + fail = cmir_ineq(n, a, b, u, cset, delta, alpha, beta, gamma); + /* restore the variable */ + cset[j] = (char)!cset[j]; + /* do not replace the variable in case of failure */ + if (fail) continue; + /* compute violation */ + r = - (*beta) - (*gamma) * s; + for (k = 1; k <= n; k++) r += alpha[k] * x[k]; + if (r_best < r) r_best = r, cset[j] = (char)!cset[j]; + } + /* construct the best c-MIR inequality chosen */ + fail = cmir_ineq(n, a, b, u, cset, delta, alpha, beta, gamma); + xassert(!fail); +done: /* free working arrays */ + xfree(cset); + xfree(vset); + /* return to the calling routine */ + return r_best; +} + +static double generate(struct MIR *mir) +{ /* try to generate violated c-MIR cut for modified constraint */ + int m = mir->m; + int n = mir->n; + int j, k, kk, nint; + double s, *u, *x, *alpha, r_best = 0.0, b, beta, gamma; + ios_copy_vec(mir->cut_vec, mir->mod_vec); + mir->cut_rhs = mir->mod_rhs; + /* remove small terms, which can appear due to substitution of + variable bounds */ + ios_clean_vec(mir->cut_vec, DBL_EPSILON); +#if _MIR_DEBUG + ios_check_vec(mir->cut_vec); +#endif + /* remove positive continuous terms to obtain MK relaxation */ + for (j = 1; j <= mir->cut_vec->nnz; j++) + { k = mir->cut_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (!mir->isint[k] && mir->cut_vec->val[j] > 0.0) + mir->cut_vec->val[j] = 0.0; + } + ios_clean_vec(mir->cut_vec, 0.0); +#if _MIR_DEBUG + ios_check_vec(mir->cut_vec); +#endif + /* move integer terms to the beginning of the sparse vector and + determine the number of integer variables */ + nint = 0; + for (j = 1; j <= mir->cut_vec->nnz; j++) + { k = mir->cut_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (mir->isint[k]) + { double temp; + nint++; + /* interchange elements [nint] and [j] */ + kk = mir->cut_vec->ind[nint]; + mir->cut_vec->pos[k] = nint; + mir->cut_vec->pos[kk] = j; + mir->cut_vec->ind[nint] = k; + mir->cut_vec->ind[j] = kk; + temp = mir->cut_vec->val[nint]; + mir->cut_vec->val[nint] = mir->cut_vec->val[j]; + mir->cut_vec->val[j] = temp; + } + } +#if _MIR_DEBUG + ios_check_vec(mir->cut_vec); +#endif + /* if there is no integer variable, nothing to generate */ + if (nint == 0) goto done; + /* allocate working arrays */ + u = xcalloc(1+nint, sizeof(double)); + x = xcalloc(1+nint, sizeof(double)); + alpha = xcalloc(1+nint, sizeof(double)); + /* determine u and x */ + for (j = 1; j <= nint; j++) + { k = mir->cut_vec->ind[j]; + xassert(m+1 <= k && k <= m+n); + xassert(mir->isint[k]); + u[j] = mir->ub[k] - mir->lb[k]; + xassert(u[j] >= 1.0); + if (mir->subst[k] == 'L') + x[j] = mir->x[k] - mir->lb[k]; + else if (mir->subst[k] == 'U') + x[j] = mir->ub[k] - mir->x[k]; + else + xassert(k != k); + xassert(x[j] >= -0.001); + if (x[j] < 0.0) x[j] = 0.0; + } + /* compute s = - sum of continuous terms */ + s = 0.0; + for (j = nint+1; j <= mir->cut_vec->nnz; j++) + { double x; + k = mir->cut_vec->ind[j]; + xassert(1 <= k && k <= m+n); + /* must be continuous */ + xassert(!mir->isint[k]); + if (mir->subst[k] == 'L') + { xassert(mir->lb[k] != -DBL_MAX); + kk = mir->vlb[k]; + if (kk == 0) + x = mir->x[k] - mir->lb[k]; + else + x = mir->x[k] - mir->lb[k] * mir->x[kk]; + } + else if (mir->subst[k] == 'U') + { xassert(mir->ub[k] != +DBL_MAX); + kk = mir->vub[k]; + if (kk == 0) + x = mir->ub[k] - mir->x[k]; + else + x = mir->ub[k] * mir->x[kk] - mir->x[k]; + } + else + xassert(k != k); + xassert(x >= -0.001); + if (x < 0.0) x = 0.0; + s -= mir->cut_vec->val[j] * x; + } + xassert(s >= 0.0); + /* apply heuristic to obtain most violated c-MIR inequality */ + b = mir->cut_rhs; + r_best = cmir_sep(nint, mir->cut_vec->val, b, u, x, s, alpha, + &beta, &gamma); + if (r_best == 0.0) goto skip; + xassert(r_best > 0.0); + /* convert to raw cut */ + /* sum alpha[j] * x[j] <= beta + gamma * s */ + for (j = 1; j <= nint; j++) + mir->cut_vec->val[j] = alpha[j]; + for (j = nint+1; j <= mir->cut_vec->nnz; j++) + { k = mir->cut_vec->ind[j]; + if (k <= m+n) mir->cut_vec->val[j] *= gamma; + } + mir->cut_rhs = beta; +#if _MIR_DEBUG + ios_check_vec(mir->cut_vec); +#endif +skip: /* free working arrays */ + xfree(u); + xfree(x); + xfree(alpha); +done: return r_best; +} + +#if _MIR_DEBUG +static void check_raw_cut(struct MIR *mir, double r_best) +{ /* check raw cut before back bound substitution */ + int m = mir->m; + int n = mir->n; + int j, k, kk; + double r, big, x; + /* compute the residual r = sum a[k] * x[k] - b and determine + big = max(1, |a[k]|, |b|) */ + r = 0.0, big = 1.0; + for (j = 1; j <= mir->cut_vec->nnz; j++) + { k = mir->cut_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (mir->subst[k] == 'L') + { xassert(mir->lb[k] != -DBL_MAX); + kk = mir->vlb[k]; + if (kk == 0) + x = mir->x[k] - mir->lb[k]; + else + x = mir->x[k] - mir->lb[k] * mir->x[kk]; + } + else if (mir->subst[k] == 'U') + { xassert(mir->ub[k] != +DBL_MAX); + kk = mir->vub[k]; + if (kk == 0) + x = mir->ub[k] - mir->x[k]; + else + x = mir->ub[k] * mir->x[kk] - mir->x[k]; + } + else + xassert(k != k); + r += mir->cut_vec->val[j] * x; + if (big < fabs(mir->cut_vec->val[j])) + big = fabs(mir->cut_vec->val[j]); + } + r -= mir->cut_rhs; + if (big < fabs(mir->cut_rhs)) + big = fabs(mir->cut_rhs); + /* the residual must be close to r_best */ + xassert(fabs(r - r_best) <= 1e-6 * big); + return; +} +#endif + +static void back_subst(struct MIR *mir) +{ /* back substitution of original bounds */ + int m = mir->m; + int n = mir->n; + int j, jj, k, kk; + /* at first, restore bounds of integer variables (because on + restoring variable bounds of continuous variables we need + original, not shifted, bounds of integer variables) */ + for (j = 1; j <= mir->cut_vec->nnz; j++) + { k = mir->cut_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (!mir->isint[k]) continue; /* skip continuous */ + if (mir->subst[k] == 'L') + { /* x'[k] = x[k] - lb[k] */ + xassert(mir->lb[k] != -DBL_MAX); + xassert(mir->vlb[k] == 0); + mir->cut_rhs += mir->cut_vec->val[j] * mir->lb[k]; + } + else if (mir->subst[k] == 'U') + { /* x'[k] = ub[k] - x[k] */ + xassert(mir->ub[k] != +DBL_MAX); + xassert(mir->vub[k] == 0); + mir->cut_rhs -= mir->cut_vec->val[j] * mir->ub[k]; + mir->cut_vec->val[j] = - mir->cut_vec->val[j]; + } + else + xassert(k != k); + } + /* now restore bounds of continuous variables */ + for (j = 1; j <= mir->cut_vec->nnz; j++) + { k = mir->cut_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (mir->isint[k]) continue; /* skip integer */ + if (mir->subst[k] == 'L') + { /* x'[k] = x[k] - (lower bound) */ + xassert(mir->lb[k] != -DBL_MAX); + kk = mir->vlb[k]; + if (kk == 0) + { /* x'[k] = x[k] - lb[k] */ + mir->cut_rhs += mir->cut_vec->val[j] * mir->lb[k]; + } + else + { /* x'[k] = x[k] - lb[k] * x[kk] */ + jj = mir->cut_vec->pos[kk]; +#if 0 + xassert(jj != 0); +#else + if (jj == 0) + { ios_set_vj(mir->cut_vec, kk, 1.0); + jj = mir->cut_vec->pos[kk]; + xassert(jj != 0); + mir->cut_vec->val[jj] = 0.0; + } +#endif + mir->cut_vec->val[jj] -= mir->cut_vec->val[j] * + mir->lb[k]; + } + } + else if (mir->subst[k] == 'U') + { /* x'[k] = (upper bound) - x[k] */ + xassert(mir->ub[k] != +DBL_MAX); + kk = mir->vub[k]; + if (kk == 0) + { /* x'[k] = ub[k] - x[k] */ + mir->cut_rhs -= mir->cut_vec->val[j] * mir->ub[k]; + } + else + { /* x'[k] = ub[k] * x[kk] - x[k] */ + jj = mir->cut_vec->pos[kk]; + if (jj == 0) + { ios_set_vj(mir->cut_vec, kk, 1.0); + jj = mir->cut_vec->pos[kk]; + xassert(jj != 0); + mir->cut_vec->val[jj] = 0.0; + } + mir->cut_vec->val[jj] += mir->cut_vec->val[j] * + mir->ub[k]; + } + mir->cut_vec->val[j] = - mir->cut_vec->val[j]; + } + else + xassert(k != k); + } +#if _MIR_DEBUG + ios_check_vec(mir->cut_vec); +#endif + return; +} + +#if _MIR_DEBUG +static void check_cut_row(struct MIR *mir, double r_best) +{ /* check the cut after back bound substitution or elimination of + auxiliary variables */ + int m = mir->m; + int n = mir->n; + int j, k; + double r, big; + /* compute the residual r = sum a[k] * x[k] - b and determine + big = max(1, |a[k]|, |b|) */ + r = 0.0, big = 1.0; + for (j = 1; j <= mir->cut_vec->nnz; j++) + { k = mir->cut_vec->ind[j]; + xassert(1 <= k && k <= m+n); + r += mir->cut_vec->val[j] * mir->x[k]; + if (big < fabs(mir->cut_vec->val[j])) + big = fabs(mir->cut_vec->val[j]); + } + r -= mir->cut_rhs; + if (big < fabs(mir->cut_rhs)) + big = fabs(mir->cut_rhs); + /* the residual must be close to r_best */ + xassert(fabs(r - r_best) <= 1e-6 * big); + return; +} +#endif + +static void subst_aux_vars(glp_tree *tree, struct MIR *mir) +{ /* final substitution to eliminate auxiliary variables */ + glp_prob *mip = tree->mip; + int m = mir->m; + int n = mir->n; + GLPAIJ *aij; + int j, k, kk, jj; + for (j = mir->cut_vec->nnz; j >= 1; j--) + { k = mir->cut_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (k > m) continue; /* skip structurals */ + for (aij = mip->row[k]->ptr; aij != NULL; aij = aij->r_next) + { kk = m + aij->col->j; /* structural */ + jj = mir->cut_vec->pos[kk]; + if (jj == 0) + { ios_set_vj(mir->cut_vec, kk, 1.0); + jj = mir->cut_vec->pos[kk]; + mir->cut_vec->val[jj] = 0.0; + } + mir->cut_vec->val[jj] += mir->cut_vec->val[j] * aij->val; + } + mir->cut_vec->val[j] = 0.0; + } + ios_clean_vec(mir->cut_vec, 0.0); + return; +} + +static void add_cut(glp_tree *tree, struct MIR *mir) +{ /* add constructed cut inequality to the cut pool */ + int m = mir->m; + int n = mir->n; + int j, k, len; + int *ind = xcalloc(1+n, sizeof(int)); + double *val = xcalloc(1+n, sizeof(double)); + len = 0; + for (j = mir->cut_vec->nnz; j >= 1; j--) + { k = mir->cut_vec->ind[j]; + xassert(m+1 <= k && k <= m+n); + len++, ind[len] = k - m, val[len] = mir->cut_vec->val[j]; + } +#if 0 + ios_add_cut_row(tree, pool, GLP_RF_MIR, len, ind, val, GLP_UP, + mir->cut_rhs); +#else + glp_ios_add_row(tree, NULL, GLP_RF_MIR, 0, len, ind, val, GLP_UP, + mir->cut_rhs); +#endif + xfree(ind); + xfree(val); + return; +} + +static int aggregate_row(glp_tree *tree, struct MIR *mir) +{ /* try to aggregate another row */ + glp_prob *mip = tree->mip; + int m = mir->m; + int n = mir->n; + GLPAIJ *aij; + IOSVEC *v; + int ii, j, jj, k, kk, kappa = 0, ret = 0; + double d1, d2, d, d_max = 0.0; + /* choose appropriate structural variable in the aggregated row + to be substituted */ + for (j = 1; j <= mir->agg_vec->nnz; j++) + { k = mir->agg_vec->ind[j]; + xassert(1 <= k && k <= m+n); + if (k <= m) continue; /* skip auxiliary var */ + if (mir->isint[k]) continue; /* skip integer var */ + if (fabs(mir->agg_vec->val[j]) < 0.001) continue; + /* compute distance from x[k] to its lower bound */ + kk = mir->vlb[k]; + if (kk == 0) + { if (mir->lb[k] == -DBL_MAX) + d1 = DBL_MAX; + else + d1 = mir->x[k] - mir->lb[k]; + } + else + { xassert(1 <= kk && kk <= m+n); + xassert(mir->isint[kk]); + xassert(mir->lb[k] != -DBL_MAX); + d1 = mir->x[k] - mir->lb[k] * mir->x[kk]; + } + /* compute distance from x[k] to its upper bound */ + kk = mir->vub[k]; + if (kk == 0) + { if (mir->vub[k] == +DBL_MAX) + d2 = DBL_MAX; + else + d2 = mir->ub[k] - mir->x[k]; + } + else + { xassert(1 <= kk && kk <= m+n); + xassert(mir->isint[kk]); + xassert(mir->ub[k] != +DBL_MAX); + d2 = mir->ub[k] * mir->x[kk] - mir->x[k]; + } + /* x[k] cannot be free */ + xassert(d1 != DBL_MAX || d2 != DBL_MAX); + /* d = min(d1, d2) */ + d = (d1 <= d2 ? d1 : d2); + xassert(d != DBL_MAX); + /* should not be close to corresponding bound */ + if (d < 0.001) continue; + if (d_max < d) d_max = d, kappa = k; + } + if (kappa == 0) + { /* nothing chosen */ + ret = 1; + goto done; + } + /* x[kappa] has been chosen */ + xassert(m+1 <= kappa && kappa <= m+n); + xassert(!mir->isint[kappa]); + /* find another row, which have not been used yet, to eliminate + x[kappa] from the aggregated row */ + for (ii = 1; ii <= m; ii++) + { if (mir->skip[ii]) continue; + for (aij = mip->row[ii]->ptr; aij != NULL; aij = aij->r_next) + if (aij->col->j == kappa - m) break; + if (aij != NULL && fabs(aij->val) >= 0.001) break; + } + if (ii > m) + { /* nothing found */ + ret = 2; + goto done; + } + /* row ii has been found; include it in the aggregated list */ + mir->agg_cnt++; + xassert(mir->agg_cnt <= MAXAGGR); + mir->agg_row[mir->agg_cnt] = ii; + mir->skip[ii] = 2; + /* v := new row */ + v = ios_create_vec(m+n); + ios_set_vj(v, ii, 1.0); + for (aij = mip->row[ii]->ptr; aij != NULL; aij = aij->r_next) + ios_set_vj(v, m + aij->col->j, - aij->val); +#if _MIR_DEBUG + ios_check_vec(v); +#endif + /* perform gaussian elimination to remove x[kappa] */ + j = mir->agg_vec->pos[kappa]; + xassert(j != 0); + jj = v->pos[kappa]; + xassert(jj != 0); + ios_linear_comb(mir->agg_vec, + - mir->agg_vec->val[j] / v->val[jj], v); + ios_delete_vec(v); + ios_set_vj(mir->agg_vec, kappa, 0.0); +#if _MIR_DEBUG + ios_check_vec(mir->agg_vec); +#endif +done: return ret; +} + +void ios_mir_gen(glp_tree *tree, void *gen) +{ /* main routine to generate MIR cuts */ + glp_prob *mip = tree->mip; + struct MIR *mir = gen; + int m = mir->m; + int n = mir->n; + int i; + double r_best; + xassert(mip->m >= m); + xassert(mip->n == n); + /* obtain current point */ + get_current_point(tree, mir); +#if _MIR_DEBUG + /* check current point */ + check_current_point(mir); +#endif + /* reset bound substitution flags */ + memset(&mir->subst[1], '?', m+n); + /* try to generate a set of violated MIR cuts */ + for (i = 1; i <= m; i++) + { if (mir->skip[i]) continue; + /* use original i-th row as initial aggregated constraint */ + initial_agg_row(tree, mir, i); +loop: ; +#if _MIR_DEBUG + /* check aggregated row */ + check_agg_row(mir); +#endif + /* substitute fixed variables into aggregated constraint */ + subst_fixed_vars(mir); +#if _MIR_DEBUG + /* check aggregated row */ + check_agg_row(mir); +#endif +#if _MIR_DEBUG + /* check bound substitution flags */ + { int k; + for (k = 1; k <= m+n; k++) + xassert(mir->subst[k] == '?'); + } +#endif + /* apply bound substitution heuristic */ + bound_subst_heur(mir); + /* substitute bounds and build modified constraint */ + build_mod_row(mir); +#if _MIR_DEBUG + /* check modified row */ + check_mod_row(mir); +#endif + /* try to generate violated c-MIR cut for modified row */ + r_best = generate(mir); + if (r_best > 0.0) + { /* success */ +#if _MIR_DEBUG + /* check raw cut before back bound substitution */ + check_raw_cut(mir, r_best); +#endif + /* back substitution of original bounds */ + back_subst(mir); +#if _MIR_DEBUG + /* check the cut after back bound substitution */ + check_cut_row(mir, r_best); +#endif + /* final substitution to eliminate auxiliary variables */ + subst_aux_vars(tree, mir); +#if _MIR_DEBUG + /* check the cut after elimination of auxiliaries */ + check_cut_row(mir, r_best); +#endif + /* add constructed cut inequality to the cut pool */ + add_cut(tree, mir); + } + /* reset bound substitution flags */ + { int j, k; + for (j = 1; j <= mir->mod_vec->nnz; j++) + { k = mir->mod_vec->ind[j]; + xassert(1 <= k && k <= m+n); + xassert(mir->subst[k] != '?'); + mir->subst[k] = '?'; + } + } + if (r_best == 0.0) + { /* failure */ + if (mir->agg_cnt < MAXAGGR) + { /* try to aggregate another row */ + if (aggregate_row(tree, mir) == 0) goto loop; + } + } + /* unmark rows used in the aggregated constraint */ + { int k, ii; + for (k = 1; k <= mir->agg_cnt; k++) + { ii = mir->agg_row[k]; + xassert(1 <= ii && ii <= m); + xassert(mir->skip[ii] == 2); + mir->skip[ii] = 0; + } + } + } + return; +} + +/*********************************************************************** +* NAME +* +* ios_mir_term - terminate MIR cut generator +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_mir_term(void *gen); +* +* DESCRIPTION +* +* The routine ios_mir_term deletes the MIR cut generator working area +* freeing all the memory allocated to it. */ + +void ios_mir_term(void *gen) +{ struct MIR *mir = gen; + xfree(mir->skip); + xfree(mir->isint); + xfree(mir->lb); + xfree(mir->vlb); + xfree(mir->ub); + xfree(mir->vub); + xfree(mir->x); + xfree(mir->agg_row); + ios_delete_vec(mir->agg_vec); + xfree(mir->subst); + ios_delete_vec(mir->mod_vec); + ios_delete_vec(mir->cut_vec); + xfree(mir); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios07.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios07.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,554 @@ +/* glpios07.c (mixed cover cut generator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*---------------------------------------------------------------------- +-- COVER INEQUALITIES +-- +-- Consider the set of feasible solutions to 0-1 knapsack problem: +-- +-- sum a[j]*x[j] <= b, (1) +-- j in J +-- +-- x[j] is binary, (2) +-- +-- where, wlog, we assume that a[j] > 0 (since 0-1 variables can be +-- complemented) and a[j] <= b (since a[j] > b implies x[j] = 0). +-- +-- A set C within J is called a cover if +-- +-- sum a[j] > b. (3) +-- j in C +-- +-- For any cover C the inequality +-- +-- sum x[j] <= |C| - 1 (4) +-- j in C +-- +-- is called a cover inequality and is valid for (1)-(2). +-- +-- MIXED COVER INEQUALITIES +-- +-- Consider the set of feasible solutions to mixed knapsack problem: +-- +-- sum a[j]*x[j] + y <= b, (5) +-- j in J +-- +-- x[j] is binary, (6) +-- +-- 0 <= y <= u is continuous, (7) +-- +-- where again we assume that a[j] > 0. +-- +-- Let C within J be some set. From (1)-(4) it follows that +-- +-- sum a[j] > b - y (8) +-- j in C +-- +-- implies +-- +-- sum x[j] <= |C| - 1. (9) +-- j in C +-- +-- Thus, we need to modify the inequality (9) in such a way that it be +-- a constraint only if the condition (8) is satisfied. +-- +-- Consider the following inequality: +-- +-- sum x[j] <= |C| - t. (10) +-- j in C +-- +-- If 0 < t <= 1, then (10) is equivalent to (9), because all x[j] are +-- binary variables. On the other hand, if t <= 0, (10) being satisfied +-- for any values of x[j] is not a constraint. +-- +-- Let +-- +-- t' = sum a[j] + y - b. (11) +-- j in C +-- +-- It is understood that the condition t' > 0 is equivalent to (8). +-- Besides, from (6)-(7) it follows that t' has an implied upper bound: +-- +-- t'max = sum a[j] + u - b. (12) +-- j in C +-- +-- This allows to express the parameter t having desired properties: +-- +-- t = t' / t'max. (13) +-- +-- In fact, t <= 1 by definition, and t > 0 being equivalent to t' > 0 +-- is equivalent to (8). +-- +-- Thus, the inequality (10), where t is given by formula (13) is valid +-- for (5)-(7). +-- +-- Note that if u = 0, then y = 0, so t = 1, and the conditions (8) and +-- (10) is transformed to the conditions (3) and (4). +-- +-- GENERATING MIXED COVER CUTS +-- +-- To generate a mixed cover cut in the form (10) we need to find such +-- set C which satisfies to the inequality (8) and for which, in turn, +-- the inequality (10) is violated in the current point. +-- +-- Substituting t from (13) to (10) gives: +-- +-- 1 +-- sum x[j] <= |C| - ----- (sum a[j] + y - b), (14) +-- j in C t'max j in C +-- +-- and finally we have the cut inequality in the standard form: +-- +-- sum x[j] + alfa * y <= beta, (15) +-- j in C +-- +-- where: +-- +-- alfa = 1 / t'max, (16) +-- +-- beta = |C| - alfa * (sum a[j] - b). (17) +-- j in C */ + +#if 1 +#define MAXTRY 1000 +#else +#define MAXTRY 10000 +#endif + +static int cover2(int n, double a[], double b, double u, double x[], + double y, int cov[], double *_alfa, double *_beta) +{ /* try to generate mixed cover cut using two-element cover */ + int i, j, try = 0, ret = 0; + double eps, alfa, beta, temp, rmax = 0.001; + eps = 0.001 * (1.0 + fabs(b)); + for (i = 0+1; i <= n; i++) + for (j = i+1; j <= n; j++) + { /* C = {i, j} */ + try++; + if (try > MAXTRY) goto done; + /* check if condition (8) is satisfied */ + if (a[i] + a[j] + y > b + eps) + { /* compute parameters for inequality (15) */ + temp = a[i] + a[j] - b; + alfa = 1.0 / (temp + u); + beta = 2.0 - alfa * temp; + /* compute violation of inequality (15) */ + temp = x[i] + x[j] + alfa * y - beta; + /* choose C providing maximum violation */ + if (rmax < temp) + { rmax = temp; + cov[1] = i; + cov[2] = j; + *_alfa = alfa; + *_beta = beta; + ret = 1; + } + } + } +done: return ret; +} + +static int cover3(int n, double a[], double b, double u, double x[], + double y, int cov[], double *_alfa, double *_beta) +{ /* try to generate mixed cover cut using three-element cover */ + int i, j, k, try = 0, ret = 0; + double eps, alfa, beta, temp, rmax = 0.001; + eps = 0.001 * (1.0 + fabs(b)); + for (i = 0+1; i <= n; i++) + for (j = i+1; j <= n; j++) + for (k = j+1; k <= n; k++) + { /* C = {i, j, k} */ + try++; + if (try > MAXTRY) goto done; + /* check if condition (8) is satisfied */ + if (a[i] + a[j] + a[k] + y > b + eps) + { /* compute parameters for inequality (15) */ + temp = a[i] + a[j] + a[k] - b; + alfa = 1.0 / (temp + u); + beta = 3.0 - alfa * temp; + /* compute violation of inequality (15) */ + temp = x[i] + x[j] + x[k] + alfa * y - beta; + /* choose C providing maximum violation */ + if (rmax < temp) + { rmax = temp; + cov[1] = i; + cov[2] = j; + cov[3] = k; + *_alfa = alfa; + *_beta = beta; + ret = 1; + } + } + } +done: return ret; +} + +static int cover4(int n, double a[], double b, double u, double x[], + double y, int cov[], double *_alfa, double *_beta) +{ /* try to generate mixed cover cut using four-element cover */ + int i, j, k, l, try = 0, ret = 0; + double eps, alfa, beta, temp, rmax = 0.001; + eps = 0.001 * (1.0 + fabs(b)); + for (i = 0+1; i <= n; i++) + for (j = i+1; j <= n; j++) + for (k = j+1; k <= n; k++) + for (l = k+1; l <= n; l++) + { /* C = {i, j, k, l} */ + try++; + if (try > MAXTRY) goto done; + /* check if condition (8) is satisfied */ + if (a[i] + a[j] + a[k] + a[l] + y > b + eps) + { /* compute parameters for inequality (15) */ + temp = a[i] + a[j] + a[k] + a[l] - b; + alfa = 1.0 / (temp + u); + beta = 4.0 - alfa * temp; + /* compute violation of inequality (15) */ + temp = x[i] + x[j] + x[k] + x[l] + alfa * y - beta; + /* choose C providing maximum violation */ + if (rmax < temp) + { rmax = temp; + cov[1] = i; + cov[2] = j; + cov[3] = k; + cov[4] = l; + *_alfa = alfa; + *_beta = beta; + ret = 1; + } + } + } +done: return ret; +} + +static int cover(int n, double a[], double b, double u, double x[], + double y, int cov[], double *alfa, double *beta) +{ /* try to generate mixed cover cut; + input (see (5)): + n is the number of binary variables; + a[1:n] are coefficients at binary variables; + b is the right-hand side; + u is upper bound of continuous variable; + x[1:n] are values of binary variables at current point; + y is value of continuous variable at current point; + output (see (15), (16), (17)): + cov[1:r] are indices of binary variables included in cover C, + where r is the set cardinality returned on exit; + alfa coefficient at continuous variable; + beta is the right-hand side; */ + int j; + /* perform some sanity checks */ + xassert(n >= 2); + for (j = 1; j <= n; j++) xassert(a[j] > 0.0); +#if 1 /* ??? */ + xassert(b > -1e-5); +#else + xassert(b > 0.0); +#endif + xassert(u >= 0.0); + for (j = 1; j <= n; j++) xassert(0.0 <= x[j] && x[j] <= 1.0); + xassert(0.0 <= y && y <= u); + /* try to generate mixed cover cut */ + if (cover2(n, a, b, u, x, y, cov, alfa, beta)) return 2; + if (cover3(n, a, b, u, x, y, cov, alfa, beta)) return 3; + if (cover4(n, a, b, u, x, y, cov, alfa, beta)) return 4; + return 0; +} + +/*---------------------------------------------------------------------- +-- lpx_cover_cut - generate mixed cover cut. +-- +-- SYNOPSIS +-- +-- #include "glplpx.h" +-- int lpx_cover_cut(LPX *lp, int len, int ind[], double val[], +-- double work[]); +-- +-- DESCRIPTION +-- +-- The routine lpx_cover_cut generates a mixed cover cut for a given +-- row of the MIP problem. +-- +-- The given row of the MIP problem should be explicitly specified in +-- the form: +-- +-- sum{j in J} a[j]*x[j] <= b. (1) +-- +-- On entry indices (ordinal numbers) of structural variables, which +-- have non-zero constraint coefficients, should be placed in locations +-- ind[1], ..., ind[len], and corresponding constraint coefficients +-- should be placed in locations val[1], ..., val[len]. The right-hand +-- side b should be stored in location val[0]. +-- +-- The working array work should have at least nb locations, where nb +-- is the number of binary variables in (1). +-- +-- The routine generates a mixed cover cut in the same form as (1) and +-- stores the cut coefficients and right-hand side in the same way as +-- just described above. +-- +-- RETURNS +-- +-- If the cutting plane has been successfully generated, the routine +-- returns 1 <= len' <= n, which is the number of non-zero coefficients +-- in the inequality constraint. Otherwise, the routine returns zero. */ + +static int lpx_cover_cut(LPX *lp, int len, int ind[], double val[], + double work[]) +{ int cov[1+4], j, k, nb, newlen, r; + double f_min, f_max, alfa, beta, u, *x = work, y; + /* substitute and remove fixed variables */ + newlen = 0; + for (k = 1; k <= len; k++) + { j = ind[k]; + if (lpx_get_col_type(lp, j) == LPX_FX) + val[0] -= val[k] * lpx_get_col_lb(lp, j); + else + { newlen++; + ind[newlen] = ind[k]; + val[newlen] = val[k]; + } + } + len = newlen; + /* move binary variables to the beginning of the list so that + elements 1, 2, ..., nb correspond to binary variables, and + elements nb+1, nb+2, ..., len correspond to rest variables */ + nb = 0; + for (k = 1; k <= len; k++) + { j = ind[k]; + if (lpx_get_col_kind(lp, j) == LPX_IV && + lpx_get_col_type(lp, j) == LPX_DB && + lpx_get_col_lb(lp, j) == 0.0 && + lpx_get_col_ub(lp, j) == 1.0) + { /* binary variable */ + int ind_k; + double val_k; + nb++; + ind_k = ind[nb], val_k = val[nb]; + ind[nb] = ind[k], val[nb] = val[k]; + ind[k] = ind_k, val[k] = val_k; + } + } + /* now the specified row has the form: + sum a[j]*x[j] + sum a[j]*y[j] <= b, + where x[j] are binary variables, y[j] are rest variables */ + /* at least two binary variables are needed */ + if (nb < 2) return 0; + /* compute implied lower and upper bounds for sum a[j]*y[j] */ + f_min = f_max = 0.0; + for (k = nb+1; k <= len; k++) + { j = ind[k]; + /* both bounds must be finite */ + if (lpx_get_col_type(lp, j) != LPX_DB) return 0; + if (val[k] > 0.0) + { f_min += val[k] * lpx_get_col_lb(lp, j); + f_max += val[k] * lpx_get_col_ub(lp, j); + } + else + { f_min += val[k] * lpx_get_col_ub(lp, j); + f_max += val[k] * lpx_get_col_lb(lp, j); + } + } + /* sum a[j]*x[j] + sum a[j]*y[j] <= b ===> + sum a[j]*x[j] + (sum a[j]*y[j] - f_min) <= b - f_min ===> + sum a[j]*x[j] + y <= b - f_min, + where y = sum a[j]*y[j] - f_min; + note that 0 <= y <= u, u = f_max - f_min */ + /* determine upper bound of y */ + u = f_max - f_min; + /* determine value of y at the current point */ + y = 0.0; + for (k = nb+1; k <= len; k++) + { j = ind[k]; + y += val[k] * lpx_get_col_prim(lp, j); + } + y -= f_min; + if (y < 0.0) y = 0.0; + if (y > u) y = u; + /* modify the right-hand side b */ + val[0] -= f_min; + /* now the transformed row has the form: + sum a[j]*x[j] + y <= b, where 0 <= y <= u */ + /* determine values of x[j] at the current point */ + for (k = 1; k <= nb; k++) + { j = ind[k]; + x[k] = lpx_get_col_prim(lp, j); + if (x[k] < 0.0) x[k] = 0.0; + if (x[k] > 1.0) x[k] = 1.0; + } + /* if a[j] < 0, replace x[j] by its complement 1 - x'[j] */ + for (k = 1; k <= nb; k++) + { if (val[k] < 0.0) + { ind[k] = - ind[k]; + val[k] = - val[k]; + val[0] += val[k]; + x[k] = 1.0 - x[k]; + } + } + /* try to generate a mixed cover cut for the transformed row */ + r = cover(nb, val, val[0], u, x, y, cov, &alfa, &beta); + if (r == 0) return 0; + xassert(2 <= r && r <= 4); + /* now the cut is in the form: + sum{j in C} x[j] + alfa * y <= beta */ + /* store the right-hand side beta */ + ind[0] = 0, val[0] = beta; + /* restore the original ordinal numbers of x[j] */ + for (j = 1; j <= r; j++) cov[j] = ind[cov[j]]; + /* store cut coefficients at binary variables complementing back + the variables having negative row coefficients */ + xassert(r <= nb); + for (k = 1; k <= r; k++) + { if (cov[k] > 0) + { ind[k] = +cov[k]; + val[k] = +1.0; + } + else + { ind[k] = -cov[k]; + val[k] = -1.0; + val[0] -= 1.0; + } + } + /* substitute y = sum a[j]*y[j] - f_min */ + for (k = nb+1; k <= len; k++) + { r++; + ind[r] = ind[k]; + val[r] = alfa * val[k]; + } + val[0] += alfa * f_min; + xassert(r <= len); + len = r; + return len; +} + +/*---------------------------------------------------------------------- +-- lpx_eval_row - compute explictily specified row. +-- +-- SYNOPSIS +-- +-- #include "glplpx.h" +-- double lpx_eval_row(LPX *lp, int len, int ind[], double val[]); +-- +-- DESCRIPTION +-- +-- The routine lpx_eval_row computes the primal value of an explicitly +-- specified row using current values of structural variables. +-- +-- The explicitly specified row may be thought as a linear form: +-- +-- y = a[1]*x[m+1] + a[2]*x[m+2] + ... + a[n]*x[m+n], +-- +-- where y is an auxiliary variable for this row, a[j] are coefficients +-- of the linear form, x[m+j] are structural variables. +-- +-- On entry column indices and numerical values of non-zero elements of +-- the row should be stored in locations ind[1], ..., ind[len] and +-- val[1], ..., val[len], where len is the number of non-zero elements. +-- The array ind and val are not changed on exit. +-- +-- RETURNS +-- +-- The routine returns a computed value of y, the auxiliary variable of +-- the specified row. */ + +static double lpx_eval_row(LPX *lp, int len, int ind[], double val[]) +{ int n = lpx_get_num_cols(lp); + int j, k; + double sum = 0.0; + if (len < 0) + xerror("lpx_eval_row: len = %d; invalid row length\n", len); + for (k = 1; k <= len; k++) + { j = ind[k]; + if (!(1 <= j && j <= n)) + xerror("lpx_eval_row: j = %d; column number out of range\n", + j); + sum += val[k] * lpx_get_col_prim(lp, j); + } + return sum; +} + +/*********************************************************************** +* NAME +* +* ios_cov_gen - generate mixed cover cuts +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_cov_gen(glp_tree *tree); +* +* DESCRIPTION +* +* The routine ios_cov_gen generates mixed cover cuts for the current +* point and adds them to the cut pool. */ + +void ios_cov_gen(glp_tree *tree) +{ glp_prob *prob = tree->mip; + int m = lpx_get_num_rows(prob); + int n = lpx_get_num_cols(prob); + int i, k, type, kase, len, *ind; + double r, *val, *work; + xassert(lpx_get_status(prob) == LPX_OPT); + /* allocate working arrays */ + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + work = xcalloc(1+n, sizeof(double)); + /* look through all rows */ + for (i = 1; i <= m; i++) + for (kase = 1; kase <= 2; kase++) + { type = lpx_get_row_type(prob, i); + if (kase == 1) + { /* consider rows of '<=' type */ + if (!(type == LPX_UP || type == LPX_DB)) continue; + len = lpx_get_mat_row(prob, i, ind, val); + val[0] = lpx_get_row_ub(prob, i); + } + else + { /* consider rows of '>=' type */ + if (!(type == LPX_LO || type == LPX_DB)) continue; + len = lpx_get_mat_row(prob, i, ind, val); + for (k = 1; k <= len; k++) val[k] = - val[k]; + val[0] = - lpx_get_row_lb(prob, i); + } + /* generate mixed cover cut: + sum{j in J} a[j] * x[j] <= b */ + len = lpx_cover_cut(prob, len, ind, val, work); + if (len == 0) continue; + /* at the current point the cut inequality is violated, i.e. + sum{j in J} a[j] * x[j] - b > 0 */ + r = lpx_eval_row(prob, len, ind, val) - val[0]; + if (r < 1e-3) continue; + /* add the cut to the cut pool */ + glp_ios_add_row(tree, NULL, GLP_RF_COV, 0, len, ind, val, + GLP_UP, val[0]); + } + /* free working arrays */ + xfree(ind); + xfree(val); + xfree(work); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios08.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios08.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,907 @@ +/* glpios08.c (clique cut generator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +static double get_row_lb(LPX *lp, int i) +{ /* this routine returns lower bound of row i or -DBL_MAX if the + row has no lower bound */ + double lb; + switch (lpx_get_row_type(lp, i)) + { case LPX_FR: + case LPX_UP: + lb = -DBL_MAX; + break; + case LPX_LO: + case LPX_DB: + case LPX_FX: + lb = lpx_get_row_lb(lp, i); + break; + default: + xassert(lp != lp); + } + return lb; +} + +static double get_row_ub(LPX *lp, int i) +{ /* this routine returns upper bound of row i or +DBL_MAX if the + row has no upper bound */ + double ub; + switch (lpx_get_row_type(lp, i)) + { case LPX_FR: + case LPX_LO: + ub = +DBL_MAX; + break; + case LPX_UP: + case LPX_DB: + case LPX_FX: + ub = lpx_get_row_ub(lp, i); + break; + default: + xassert(lp != lp); + } + return ub; +} + +static double get_col_lb(LPX *lp, int j) +{ /* this routine returns lower bound of column j or -DBL_MAX if + the column has no lower bound */ + double lb; + switch (lpx_get_col_type(lp, j)) + { case LPX_FR: + case LPX_UP: + lb = -DBL_MAX; + break; + case LPX_LO: + case LPX_DB: + case LPX_FX: + lb = lpx_get_col_lb(lp, j); + break; + default: + xassert(lp != lp); + } + return lb; +} + +static double get_col_ub(LPX *lp, int j) +{ /* this routine returns upper bound of column j or +DBL_MAX if + the column has no upper bound */ + double ub; + switch (lpx_get_col_type(lp, j)) + { case LPX_FR: + case LPX_LO: + ub = +DBL_MAX; + break; + case LPX_UP: + case LPX_DB: + case LPX_FX: + ub = lpx_get_col_ub(lp, j); + break; + default: + xassert(lp != lp); + } + return ub; +} + +static int is_binary(LPX *lp, int j) +{ /* this routine checks if variable x[j] is binary */ + return + lpx_get_col_kind(lp, j) == LPX_IV && + lpx_get_col_type(lp, j) == LPX_DB && + lpx_get_col_lb(lp, j) == 0.0 && lpx_get_col_ub(lp, j) == 1.0; +} + +static double eval_lf_min(LPX *lp, int len, int ind[], double val[]) +{ /* this routine computes the minimum of a specified linear form + + sum a[j]*x[j] + j + + using the formula: + + min = sum a[j]*lb[j] + sum a[j]*ub[j], + j in J+ j in J- + + where J+ = {j: a[j] > 0}, J- = {j: a[j] < 0}, lb[j] and ub[j] + are lower and upper bound of variable x[j], resp. */ + int j, t; + double lb, ub, sum; + sum = 0.0; + for (t = 1; t <= len; t++) + { j = ind[t]; + if (val[t] > 0.0) + { lb = get_col_lb(lp, j); + if (lb == -DBL_MAX) + { sum = -DBL_MAX; + break; + } + sum += val[t] * lb; + } + else if (val[t] < 0.0) + { ub = get_col_ub(lp, j); + if (ub == +DBL_MAX) + { sum = -DBL_MAX; + break; + } + sum += val[t] * ub; + } + else + xassert(val != val); + } + return sum; +} + +static double eval_lf_max(LPX *lp, int len, int ind[], double val[]) +{ /* this routine computes the maximum of a specified linear form + + sum a[j]*x[j] + j + + using the formula: + + max = sum a[j]*ub[j] + sum a[j]*lb[j], + j in J+ j in J- + + where J+ = {j: a[j] > 0}, J- = {j: a[j] < 0}, lb[j] and ub[j] + are lower and upper bound of variable x[j], resp. */ + int j, t; + double lb, ub, sum; + sum = 0.0; + for (t = 1; t <= len; t++) + { j = ind[t]; + if (val[t] > 0.0) + { ub = get_col_ub(lp, j); + if (ub == +DBL_MAX) + { sum = +DBL_MAX; + break; + } + sum += val[t] * ub; + } + else if (val[t] < 0.0) + { lb = get_col_lb(lp, j); + if (lb == -DBL_MAX) + { sum = +DBL_MAX; + break; + } + sum += val[t] * lb; + } + else + xassert(val != val); + } + return sum; +} + +/*---------------------------------------------------------------------- +-- probing - determine logical relation between binary variables. +-- +-- This routine tentatively sets a binary variable to 0 and then to 1 +-- and examines whether another binary variable is caused to be fixed. +-- +-- The examination is based only on one row (constraint), which is the +-- following: +-- +-- L <= sum a[j]*x[j] <= U. (1) +-- j +-- +-- Let x[p] be a probing variable, x[q] be an examined variable. Then +-- (1) can be written as: +-- +-- L <= sum a[j]*x[j] + a[p]*x[p] + a[q]*x[q] <= U, (2) +-- j in J' +-- +-- where J' = {j: j != p and j != q}. +-- +-- Let +-- +-- L' = L - a[p]*x[p], (3) +-- +-- U' = U - a[p]*x[p], (4) +-- +-- where x[p] is assumed to be fixed at 0 or 1. So (2) can be rewritten +-- as follows: +-- +-- L' <= sum a[j]*x[j] + a[q]*x[q] <= U', (5) +-- j in J' +-- +-- from where we have: +-- +-- L' - sum a[j]*x[j] <= a[q]*x[q] <= U' - sum a[j]*x[j]. (6) +-- j in J' j in J' +-- +-- Thus, +-- +-- min a[q]*x[q] = L' - MAX, (7) +-- +-- max a[q]*x[q] = U' - MIN, (8) +-- +-- where +-- +-- MIN = min sum a[j]*x[j], (9) +-- j in J' +-- +-- MAX = max sum a[j]*x[j]. (10) +-- j in J' +-- +-- Formulae (7) and (8) allows determining implied lower and upper +-- bounds of x[q]. +-- +-- Parameters len, val, L and U specify the constraint (1). +-- +-- Parameters lf_min and lf_max specify implied lower and upper bounds +-- of the linear form (1). It is assumed that these bounds are computed +-- with the routines eval_lf_min and eval_lf_max (see above). +-- +-- Parameter p specifies the probing variable x[p], which is set to 0 +-- (if set is 0) or to 1 (if set is 1). +-- +-- Parameter q specifies the examined variable x[q]. +-- +-- On exit the routine returns one of the following codes: +-- +-- 0 - there is no logical relation between x[p] and x[q]; +-- 1 - x[q] can take only on value 0; +-- 2 - x[q] can take only on value 1. */ + +static int probing(int len, double val[], double L, double U, + double lf_min, double lf_max, int p, int set, int q) +{ double temp; + xassert(1 <= p && p < q && q <= len); + /* compute L' (3) */ + if (L != -DBL_MAX && set) L -= val[p]; + /* compute U' (4) */ + if (U != +DBL_MAX && set) U -= val[p]; + /* compute MIN (9) */ + if (lf_min != -DBL_MAX) + { if (val[p] < 0.0) lf_min -= val[p]; + if (val[q] < 0.0) lf_min -= val[q]; + } + /* compute MAX (10) */ + if (lf_max != +DBL_MAX) + { if (val[p] > 0.0) lf_max -= val[p]; + if (val[q] > 0.0) lf_max -= val[q]; + } + /* compute implied lower bound of x[q]; see (7), (8) */ + if (val[q] > 0.0) + { if (L == -DBL_MAX || lf_max == +DBL_MAX) + temp = -DBL_MAX; + else + temp = (L - lf_max) / val[q]; + } + else + { if (U == +DBL_MAX || lf_min == -DBL_MAX) + temp = -DBL_MAX; + else + temp = (U - lf_min) / val[q]; + } + if (temp > 0.001) return 2; + /* compute implied upper bound of x[q]; see (7), (8) */ + if (val[q] > 0.0) + { if (U == +DBL_MAX || lf_min == -DBL_MAX) + temp = +DBL_MAX; + else + temp = (U - lf_min) / val[q]; + } + else + { if (L == -DBL_MAX || lf_max == +DBL_MAX) + temp = +DBL_MAX; + else + temp = (L - lf_max) / val[q]; + } + if (temp < 0.999) return 1; + /* there is no logical relation between x[p] and x[q] */ + return 0; +} + +struct COG +{ /* conflict graph; it represents logical relations between binary + variables and has a vertex for each binary variable and its + complement, and an edge between two vertices when at most one + of the variables represented by the vertices can equal one in + an optimal solution */ + int n; + /* number of variables */ + int nb; + /* number of binary variables represented in the graph (note that + not all binary variables can be represented); vertices which + correspond to binary variables have numbers 1, ..., nb while + vertices which correspond to complements of binary variables + have numbers nb+1, ..., nb+nb */ + int ne; + /* number of edges in the graph */ + int *vert; /* int vert[1+n]; */ + /* if x[j] is a binary variable represented in the graph, vert[j] + is the vertex number corresponding to x[j]; otherwise vert[j] + is zero */ + int *orig; /* int list[1:nb]; */ + /* if vert[j] = k > 0, then orig[k] = j */ + unsigned char *a; + /* adjacency matrix of the graph having 2*nb rows and columns; + only strict lower triangle is stored in dense packed form */ +}; + +/*---------------------------------------------------------------------- +-- lpx_create_cog - create the conflict graph. +-- +-- SYNOPSIS +-- +-- #include "glplpx.h" +-- void *lpx_create_cog(LPX *lp); +-- +-- DESCRIPTION +-- +-- The routine lpx_create_cog creates the conflict graph for a given +-- problem instance. +-- +-- RETURNS +-- +-- If the graph has been created, the routine returns a pointer to it. +-- Otherwise the routine returns NULL. */ + +#define MAX_NB 4000 +#define MAX_ROW_LEN 500 + +static void lpx_add_cog_edge(void *_cog, int i, int j); + +static void *lpx_create_cog(LPX *lp) +{ struct COG *cog = NULL; + int m, n, nb, i, j, p, q, len, *ind, *vert, *orig; + double L, U, lf_min, lf_max, *val; + xprintf("Creating the conflict graph...\n"); + m = lpx_get_num_rows(lp); + n = lpx_get_num_cols(lp); + /* determine which binary variables should be included in the + conflict graph */ + nb = 0; + vert = xcalloc(1+n, sizeof(int)); + for (j = 1; j <= n; j++) vert[j] = 0; + orig = xcalloc(1+n, sizeof(int)); + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + for (i = 1; i <= m; i++) + { L = get_row_lb(lp, i); + U = get_row_ub(lp, i); + if (L == -DBL_MAX && U == +DBL_MAX) continue; + len = lpx_get_mat_row(lp, i, ind, val); + if (len > MAX_ROW_LEN) continue; + lf_min = eval_lf_min(lp, len, ind, val); + lf_max = eval_lf_max(lp, len, ind, val); + for (p = 1; p <= len; p++) + { if (!is_binary(lp, ind[p])) continue; + for (q = p+1; q <= len; q++) + { if (!is_binary(lp, ind[q])) continue; + if (probing(len, val, L, U, lf_min, lf_max, p, 0, q) || + probing(len, val, L, U, lf_min, lf_max, p, 1, q)) + { /* there is a logical relation */ + /* include the first variable in the graph */ + j = ind[p]; + if (vert[j] == 0) nb++, vert[j] = nb, orig[nb] = j; + /* incude the second variable in the graph */ + j = ind[q]; + if (vert[j] == 0) nb++, vert[j] = nb, orig[nb] = j; + } + } + } + } + /* if the graph is either empty or has too many vertices, do not + create it */ + if (nb == 0 || nb > MAX_NB) + { xprintf("The conflict graph is either empty or too big\n"); + xfree(vert); + xfree(orig); + goto done; + } + /* create the conflict graph */ + cog = xmalloc(sizeof(struct COG)); + cog->n = n; + cog->nb = nb; + cog->ne = 0; + cog->vert = vert; + cog->orig = orig; + len = nb + nb; /* number of vertices */ + len = (len * (len - 1)) / 2; /* number of entries in triangle */ + len = (len + (CHAR_BIT - 1)) / CHAR_BIT; /* bytes needed */ + cog->a = xmalloc(len); + memset(cog->a, 0, len); + for (j = 1; j <= nb; j++) + { /* add edge between variable and its complement */ + lpx_add_cog_edge(cog, +orig[j], -orig[j]); + } + for (i = 1; i <= m; i++) + { L = get_row_lb(lp, i); + U = get_row_ub(lp, i); + if (L == -DBL_MAX && U == +DBL_MAX) continue; + len = lpx_get_mat_row(lp, i, ind, val); + if (len > MAX_ROW_LEN) continue; + lf_min = eval_lf_min(lp, len, ind, val); + lf_max = eval_lf_max(lp, len, ind, val); + for (p = 1; p <= len; p++) + { if (!is_binary(lp, ind[p])) continue; + for (q = p+1; q <= len; q++) + { if (!is_binary(lp, ind[q])) continue; + /* set x[p] to 0 and examine x[q] */ + switch (probing(len, val, L, U, lf_min, lf_max, p, 0, q)) + { case 0: + /* no logical relation */ + break; + case 1: + /* x[p] = 0 implies x[q] = 0 */ + lpx_add_cog_edge(cog, -ind[p], +ind[q]); + break; + case 2: + /* x[p] = 0 implies x[q] = 1 */ + lpx_add_cog_edge(cog, -ind[p], -ind[q]); + break; + default: + xassert(lp != lp); + } + /* set x[p] to 1 and examine x[q] */ + switch (probing(len, val, L, U, lf_min, lf_max, p, 1, q)) + { case 0: + /* no logical relation */ + break; + case 1: + /* x[p] = 1 implies x[q] = 0 */ + lpx_add_cog_edge(cog, +ind[p], +ind[q]); + break; + case 2: + /* x[p] = 1 implies x[q] = 1 */ + lpx_add_cog_edge(cog, +ind[p], -ind[q]); + break; + default: + xassert(lp != lp); + } + } + } + } + xprintf("The conflict graph has 2*%d vertices and %d edges\n", + cog->nb, cog->ne); +done: xfree(ind); + xfree(val); + return cog; +} + +/*---------------------------------------------------------------------- +-- lpx_add_cog_edge - add edge to the conflict graph. +-- +-- SYNOPSIS +-- +-- #include "glplpx.h" +-- void lpx_add_cog_edge(void *cog, int i, int j); +-- +-- DESCRIPTION +-- +-- The routine lpx_add_cog_edge adds an edge to the conflict graph. +-- The edge connects x[i] (if i > 0) or its complement (if i < 0) and +-- x[j] (if j > 0) or its complement (if j < 0), where i and j are +-- original ordinal numbers of corresponding variables. */ + +static void lpx_add_cog_edge(void *_cog, int i, int j) +{ struct COG *cog = _cog; + int k; + xassert(i != j); + /* determine indices of corresponding vertices */ + if (i > 0) + { xassert(1 <= i && i <= cog->n); + i = cog->vert[i]; + xassert(i != 0); + } + else + { i = -i; + xassert(1 <= i && i <= cog->n); + i = cog->vert[i]; + xassert(i != 0); + i += cog->nb; + } + if (j > 0) + { xassert(1 <= j && j <= cog->n); + j = cog->vert[j]; + xassert(j != 0); + } + else + { j = -j; + xassert(1 <= j && j <= cog->n); + j = cog->vert[j]; + xassert(j != 0); + j += cog->nb; + } + /* only lower triangle is stored, so we need i > j */ + if (i < j) k = i, i = j, j = k; + k = ((i - 1) * (i - 2)) / 2 + (j - 1); + cog->a[k / CHAR_BIT] |= + (unsigned char)(1 << ((CHAR_BIT - 1) - k % CHAR_BIT)); + cog->ne++; + return; +} + +/*---------------------------------------------------------------------- +-- MAXIMUM WEIGHT CLIQUE +-- +-- Two subroutines sub() and wclique() below are intended to find a +-- maximum weight clique in a given undirected graph. These subroutines +-- are slightly modified version of the program WCLIQUE developed by +-- Patric Ostergard and based +-- on ideas from the article "P. R. J. Ostergard, A new algorithm for +-- the maximum-weight clique problem, submitted for publication", which +-- in turn is a generalization of the algorithm for unweighted graphs +-- presented in "P. R. J. Ostergard, A fast algorithm for the maximum +-- clique problem, submitted for publication". +-- +-- USED WITH PERMISSION OF THE AUTHOR OF THE ORIGINAL CODE. */ + +struct dsa +{ /* dynamic storage area */ + int n; + /* number of vertices */ + int *wt; /* int wt[0:n-1]; */ + /* weights */ + unsigned char *a; + /* adjacency matrix (packed lower triangle without main diag.) */ + int record; + /* weight of best clique */ + int rec_level; + /* number of vertices in best clique */ + int *rec; /* int rec[0:n-1]; */ + /* best clique so far */ + int *clique; /* int clique[0:n-1]; */ + /* table for pruning */ + int *set; /* int set[0:n-1]; */ + /* current clique */ +}; + +#define n (dsa->n) +#define wt (dsa->wt) +#define a (dsa->a) +#define record (dsa->record) +#define rec_level (dsa->rec_level) +#define rec (dsa->rec) +#define clique (dsa->clique) +#define set (dsa->set) + +#if 0 +static int is_edge(struct dsa *dsa, int i, int j) +{ /* if there is arc (i,j), the routine returns true; otherwise + false; 0 <= i, j < n */ + int k; + xassert(0 <= i && i < n); + xassert(0 <= j && j < n); + if (i == j) return 0; + if (i < j) k = i, i = j, j = k; + k = (i * (i - 1)) / 2 + j; + return a[k / CHAR_BIT] & + (unsigned char)(1 << ((CHAR_BIT - 1) - k % CHAR_BIT)); +} +#else +#define is_edge(dsa, i, j) ((i) == (j) ? 0 : \ + (i) > (j) ? is_edge1(i, j) : is_edge1(j, i)) +#define is_edge1(i, j) is_edge2(((i) * ((i) - 1)) / 2 + (j)) +#define is_edge2(k) (a[(k) / CHAR_BIT] & \ + (unsigned char)(1 << ((CHAR_BIT - 1) - (k) % CHAR_BIT))) +#endif + +static void sub(struct dsa *dsa, int ct, int table[], int level, + int weight, int l_weight) +{ int i, j, k, curr_weight, left_weight, *p1, *p2, *newtable; + newtable = xcalloc(n, sizeof(int)); + if (ct <= 0) + { /* 0 or 1 elements left; include these */ + if (ct == 0) + { set[level++] = table[0]; + weight += l_weight; + } + if (weight > record) + { record = weight; + rec_level = level; + for (i = 0; i < level; i++) rec[i] = set[i]; + } + goto done; + } + for (i = ct; i >= 0; i--) + { if ((level == 0) && (i < ct)) goto done; + k = table[i]; + if ((level > 0) && (clique[k] <= (record - weight))) + goto done; /* prune */ + set[level] = k; + curr_weight = weight + wt[k]; + l_weight -= wt[k]; + if (l_weight <= (record - curr_weight)) + goto done; /* prune */ + p1 = newtable; + p2 = table; + left_weight = 0; + while (p2 < table + i) + { j = *p2++; + if (is_edge(dsa, j, k)) + { *p1++ = j; + left_weight += wt[j]; + } + } + if (left_weight <= (record - curr_weight)) continue; + sub(dsa, p1 - newtable - 1, newtable, level + 1, curr_weight, + left_weight); + } +done: xfree(newtable); + return; +} + +static int wclique(int _n, int w[], unsigned char _a[], int sol[]) +{ struct dsa _dsa, *dsa = &_dsa; + int i, j, p, max_wt, max_nwt, wth, *used, *nwt, *pos; + glp_long timer; + n = _n; + wt = &w[1]; + a = _a; + record = 0; + rec_level = 0; + rec = &sol[1]; + clique = xcalloc(n, sizeof(int)); + set = xcalloc(n, sizeof(int)); + used = xcalloc(n, sizeof(int)); + nwt = xcalloc(n, sizeof(int)); + pos = xcalloc(n, sizeof(int)); + /* start timer */ + timer = xtime(); + /* order vertices */ + for (i = 0; i < n; i++) + { nwt[i] = 0; + for (j = 0; j < n; j++) + if (is_edge(dsa, i, j)) nwt[i] += wt[j]; + } + for (i = 0; i < n; i++) + used[i] = 0; + for (i = n-1; i >= 0; i--) + { max_wt = -1; + max_nwt = -1; + for (j = 0; j < n; j++) + { if ((!used[j]) && ((wt[j] > max_wt) || (wt[j] == max_wt + && nwt[j] > max_nwt))) + { max_wt = wt[j]; + max_nwt = nwt[j]; + p = j; + } + } + pos[i] = p; + used[p] = 1; + for (j = 0; j < n; j++) + if ((!used[j]) && (j != p) && (is_edge(dsa, p, j))) + nwt[j] -= wt[p]; + } + /* main routine */ + wth = 0; + for (i = 0; i < n; i++) + { wth += wt[pos[i]]; + sub(dsa, i, pos, 0, 0, wth); + clique[pos[i]] = record; +#if 0 + if (utime() >= timer + 5.0) +#else + if (xdifftime(xtime(), timer) >= 5.0 - 0.001) +#endif + { /* print current record and reset timer */ + xprintf("level = %d (%d); best = %d\n", i+1, n, record); +#if 0 + timer = utime(); +#else + timer = xtime(); +#endif + } + } + xfree(clique); + xfree(set); + xfree(used); + xfree(nwt); + xfree(pos); + /* return the solution found */ + for (i = 1; i <= rec_level; i++) sol[i]++; + return rec_level; +} + +#undef n +#undef wt +#undef a +#undef record +#undef rec_level +#undef rec +#undef clique +#undef set + +/*---------------------------------------------------------------------- +-- lpx_clique_cut - generate cluque cut. +-- +-- SYNOPSIS +-- +-- #include "glplpx.h" +-- int lpx_clique_cut(LPX *lp, void *cog, int ind[], double val[]); +-- +-- DESCRIPTION +-- +-- The routine lpx_clique_cut generates a clique cut using the conflict +-- graph specified by the parameter cog. +-- +-- If a violated clique cut has been found, it has the following form: +-- +-- sum{j in J} a[j]*x[j] <= b. +-- +-- Variable indices j in J are stored in elements ind[1], ..., ind[len] +-- while corresponding constraint coefficients are stored in elements +-- val[1], ..., val[len], where len is returned on exit. The right-hand +-- side b is stored in element val[0]. +-- +-- RETURNS +-- +-- If the cutting plane has been successfully generated, the routine +-- returns 1 <= len <= n, which is the number of non-zero coefficients +-- in the inequality constraint. Otherwise, the routine returns zero. */ + +static int lpx_clique_cut(LPX *lp, void *_cog, int ind[], double val[]) +{ struct COG *cog = _cog; + int n = lpx_get_num_cols(lp); + int j, t, v, card, temp, len = 0, *w, *sol; + double x, sum, b, *vec; + /* allocate working arrays */ + w = xcalloc(1 + 2 * cog->nb, sizeof(int)); + sol = xcalloc(1 + 2 * cog->nb, sizeof(int)); + vec = xcalloc(1+n, sizeof(double)); + /* assign weights to vertices of the conflict graph */ + for (t = 1; t <= cog->nb; t++) + { j = cog->orig[t]; + x = lpx_get_col_prim(lp, j); + temp = (int)(100.0 * x + 0.5); + if (temp < 0) temp = 0; + if (temp > 100) temp = 100; + w[t] = temp; + w[cog->nb + t] = 100 - temp; + } + /* find a clique of maximum weight */ + card = wclique(2 * cog->nb, w, cog->a, sol); + /* compute the clique weight for unscaled values */ + sum = 0.0; + for ( t = 1; t <= card; t++) + { v = sol[t]; + xassert(1 <= v && v <= 2 * cog->nb); + if (v <= cog->nb) + { /* vertex v corresponds to binary variable x[j] */ + j = cog->orig[v]; + x = lpx_get_col_prim(lp, j); + sum += x; + } + else + { /* vertex v corresponds to the complement of x[j] */ + j = cog->orig[v - cog->nb]; + x = lpx_get_col_prim(lp, j); + sum += 1.0 - x; + } + } + /* if the sum of binary variables and their complements in the + clique greater than 1, the clique cut is violated */ + if (sum >= 1.01) + { /* construct the inquality */ + for (j = 1; j <= n; j++) vec[j] = 0; + b = 1.0; + for (t = 1; t <= card; t++) + { v = sol[t]; + if (v <= cog->nb) + { /* vertex v corresponds to binary variable x[j] */ + j = cog->orig[v]; + xassert(1 <= j && j <= n); + vec[j] += 1.0; + } + else + { /* vertex v corresponds to the complement of x[j] */ + j = cog->orig[v - cog->nb]; + xassert(1 <= j && j <= n); + vec[j] -= 1.0; + b -= 1.0; + } + } + xassert(len == 0); + for (j = 1; j <= n; j++) + { if (vec[j] != 0.0) + { len++; + ind[len] = j, val[len] = vec[j]; + } + } + ind[0] = 0, val[0] = b; + } + /* free working arrays */ + xfree(w); + xfree(sol); + xfree(vec); + /* return to the calling program */ + return len; +} + +/*---------------------------------------------------------------------- +-- lpx_delete_cog - delete the conflict graph. +-- +-- SYNOPSIS +-- +-- #include "glplpx.h" +-- void lpx_delete_cog(void *cog); +-- +-- DESCRIPTION +-- +-- The routine lpx_delete_cog deletes the conflict graph, which the +-- parameter cog points to, freeing all the memory allocated to this +-- object. */ + +static void lpx_delete_cog(void *_cog) +{ struct COG *cog = _cog; + xfree(cog->vert); + xfree(cog->orig); + xfree(cog->a); + xfree(cog); +} + +/**********************************************************************/ + +void *ios_clq_init(glp_tree *tree) +{ /* initialize clique cut generator */ + glp_prob *mip = tree->mip; + xassert(mip != NULL); + return lpx_create_cog(mip); +} + +/*********************************************************************** +* NAME +* +* ios_clq_gen - generate clique cuts +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_clq_gen(glp_tree *tree, void *gen); +* +* DESCRIPTION +* +* The routine ios_clq_gen generates clique cuts for the current point +* and adds them to the clique pool. */ + +void ios_clq_gen(glp_tree *tree, void *gen) +{ int n = lpx_get_num_cols(tree->mip); + int len, *ind; + double *val; + xassert(gen != NULL); + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + len = lpx_clique_cut(tree->mip, gen, ind, val); + if (len > 0) + { /* xprintf("len = %d\n", len); */ + glp_ios_add_row(tree, NULL, GLP_RF_CLQ, 0, len, ind, val, + GLP_UP, val[0]); + } + xfree(ind); + xfree(val); + return; +} + +/**********************************************************************/ + +void ios_clq_term(void *gen) +{ /* terminate clique cut generator */ + xassert(gen != NULL); + lpx_delete_cog(gen); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios09.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios09.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,659 @@ +/* glpios09.c (branching heuristics) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* NAME +* +* ios_choose_var - select variable to branch on +* +* SYNOPSIS +* +* #include "glpios.h" +* int ios_choose_var(glp_tree *T, int *next); +* +* The routine ios_choose_var chooses a variable from the candidate +* list to branch on. Additionally the routine provides a flag stored +* in the location next to suggests which of the child subproblems +* should be solved next. +* +* RETURNS +* +* The routine ios_choose_var returns the ordinal number of the column +* choosen. */ + +static int branch_first(glp_tree *T, int *next); +static int branch_last(glp_tree *T, int *next); +static int branch_mostf(glp_tree *T, int *next); +static int branch_drtom(glp_tree *T, int *next); + +int ios_choose_var(glp_tree *T, int *next) +{ int j; + if (T->parm->br_tech == GLP_BR_FFV) + { /* branch on first fractional variable */ + j = branch_first(T, next); + } + else if (T->parm->br_tech == GLP_BR_LFV) + { /* branch on last fractional variable */ + j = branch_last(T, next); + } + else if (T->parm->br_tech == GLP_BR_MFV) + { /* branch on most fractional variable */ + j = branch_mostf(T, next); + } + else if (T->parm->br_tech == GLP_BR_DTH) + { /* branch using the heuristic by Dreebeck and Tomlin */ + j = branch_drtom(T, next); + } + else if (T->parm->br_tech == GLP_BR_PCH) + { /* hybrid pseudocost heuristic */ + j = ios_pcost_branch(T, next); + } + else + xassert(T != T); + return j; +} + +/*********************************************************************** +* branch_first - choose first branching variable +* +* This routine looks up the list of structural variables and chooses +* the first one, which is of integer kind and has fractional value in +* optimal solution to the current LP relaxation. +* +* This routine also selects the branch to be solved next where integer +* infeasibility of the chosen variable is less than in other one. */ + +static int branch_first(glp_tree *T, int *_next) +{ int j, next; + double beta; + /* choose the column to branch on */ + for (j = 1; j <= T->n; j++) + if (T->non_int[j]) break; + xassert(1 <= j && j <= T->n); + /* select the branch to be solved next */ + beta = glp_get_col_prim(T->mip, j); + if (beta - floor(beta) < ceil(beta) - beta) + next = GLP_DN_BRNCH; + else + next = GLP_UP_BRNCH; + *_next = next; + return j; +} + +/*********************************************************************** +* branch_last - choose last branching variable +* +* This routine looks up the list of structural variables and chooses +* the last one, which is of integer kind and has fractional value in +* optimal solution to the current LP relaxation. +* +* This routine also selects the branch to be solved next where integer +* infeasibility of the chosen variable is less than in other one. */ + +static int branch_last(glp_tree *T, int *_next) +{ int j, next; + double beta; + /* choose the column to branch on */ + for (j = T->n; j >= 1; j--) + if (T->non_int[j]) break; + xassert(1 <= j && j <= T->n); + /* select the branch to be solved next */ + beta = glp_get_col_prim(T->mip, j); + if (beta - floor(beta) < ceil(beta) - beta) + next = GLP_DN_BRNCH; + else + next = GLP_UP_BRNCH; + *_next = next; + return j; +} + +/*********************************************************************** +* branch_mostf - choose most fractional branching variable +* +* This routine looks up the list of structural variables and chooses +* that one, which is of integer kind and has most fractional value in +* optimal solution to the current LP relaxation. +* +* This routine also selects the branch to be solved next where integer +* infeasibility of the chosen variable is less than in other one. +* +* (Alexander Martin notices that "...most infeasible is as good as +* random...".) */ + +static int branch_mostf(glp_tree *T, int *_next) +{ int j, jj, next; + double beta, most, temp; + /* choose the column to branch on */ + jj = 0, most = DBL_MAX; + for (j = 1; j <= T->n; j++) + { if (T->non_int[j]) + { beta = glp_get_col_prim(T->mip, j); + temp = floor(beta) + 0.5; + if (most > fabs(beta - temp)) + { jj = j, most = fabs(beta - temp); + if (beta < temp) + next = GLP_DN_BRNCH; + else + next = GLP_UP_BRNCH; + } + } + } + *_next = next; + return jj; +} + +/*********************************************************************** +* branch_drtom - choose branching var using Driebeck-Tomlin heuristic +* +* This routine chooses a structural variable, which is required to be +* integral and has fractional value in optimal solution of the current +* LP relaxation, using a heuristic proposed by Driebeck and Tomlin. +* +* The routine also selects the branch to be solved next, again due to +* Driebeck and Tomlin. +* +* This routine is based on the heuristic proposed in: +* +* Driebeck N.J. An algorithm for the solution of mixed-integer +* programming problems, Management Science, 12: 576-87 (1966); +* +* and improved in: +* +* Tomlin J.A. Branch and bound methods for integer and non-convex +* programming, in J.Abadie (ed.), Integer and Nonlinear Programming, +* North-Holland, Amsterdam, pp. 437-50 (1970). +* +* Must note that this heuristic is time-expensive, because computing +* one-step degradation (see the routine below) requires one BTRAN for +* each fractional-valued structural variable. */ + +static int branch_drtom(glp_tree *T, int *_next) +{ glp_prob *mip = T->mip; + int m = mip->m; + int n = mip->n; + char *non_int = T->non_int; + int j, jj, k, t, next, kase, len, stat, *ind; + double x, dk, alfa, delta_j, delta_k, delta_z, dz_dn, dz_up, + dd_dn, dd_up, degrad, *val; + /* basic solution of LP relaxation must be optimal */ + xassert(glp_get_status(mip) == GLP_OPT); + /* allocate working arrays */ + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + /* nothing has been chosen so far */ + jj = 0, degrad = -1.0; + /* walk through the list of columns (structural variables) */ + for (j = 1; j <= n; j++) + { /* if j-th column is not marked as fractional, skip it */ + if (!non_int[j]) continue; + /* obtain (fractional) value of j-th column in basic solution + of LP relaxation */ + x = glp_get_col_prim(mip, j); + /* since the value of j-th column is fractional, the column is + basic; compute corresponding row of the simplex table */ + len = glp_eval_tab_row(mip, m+j, ind, val); + /* the following fragment computes a change in the objective + function: delta Z = new Z - old Z, where old Z is the + objective value in the current optimal basis, and new Z is + the objective value in the adjacent basis, for two cases: + 1) if new upper bound ub' = floor(x[j]) is introduced for + j-th column (down branch); + 2) if new lower bound lb' = ceil(x[j]) is introduced for + j-th column (up branch); + since in both cases the solution remaining dual feasible + becomes primal infeasible, one implicit simplex iteration + is performed to determine the change delta Z; + it is obvious that new Z, which is never better than old Z, + is a lower (minimization) or upper (maximization) bound of + the objective function for down- and up-branches. */ + for (kase = -1; kase <= +1; kase += 2) + { /* if kase < 0, the new upper bound of x[j] is introduced; + in this case x[j] should decrease in order to leave the + basis and go to its new upper bound */ + /* if kase > 0, the new lower bound of x[j] is introduced; + in this case x[j] should increase in order to leave the + basis and go to its new lower bound */ + /* apply the dual ratio test in order to determine which + auxiliary or structural variable should enter the basis + to keep dual feasibility */ + k = glp_dual_rtest(mip, len, ind, val, kase, 1e-9); + if (k != 0) k = ind[k]; + /* if no non-basic variable has been chosen, LP relaxation + of corresponding branch being primal infeasible and dual + unbounded has no primal feasible solution; in this case + the change delta Z is formally set to infinity */ + if (k == 0) + { delta_z = + (T->mip->dir == GLP_MIN ? +DBL_MAX : -DBL_MAX); + goto skip; + } + /* row of the simplex table that corresponds to non-basic + variable x[k] choosen by the dual ratio test is: + x[j] = ... + alfa * x[k] + ... + where alfa is the influence coefficient (an element of + the simplex table row) */ + /* determine the coefficient alfa */ + for (t = 1; t <= len; t++) if (ind[t] == k) break; + xassert(1 <= t && t <= len); + alfa = val[t]; + /* since in the adjacent basis the variable x[j] becomes + non-basic, knowing its value in the current basis we can + determine its change delta x[j] = new x[j] - old x[j] */ + delta_j = (kase < 0 ? floor(x) : ceil(x)) - x; + /* and knowing the coefficient alfa we can determine the + corresponding change delta x[k] = new x[k] - old x[k], + where old x[k] is a value of x[k] in the current basis, + and new x[k] is a value of x[k] in the adjacent basis */ + delta_k = delta_j / alfa; + /* Tomlin noticed that if the variable x[k] is of integer + kind, its change cannot be less (eventually) than one in + the magnitude */ + if (k > m && glp_get_col_kind(mip, k-m) != GLP_CV) + { /* x[k] is structural integer variable */ + if (fabs(delta_k - floor(delta_k + 0.5)) > 1e-3) + { if (delta_k > 0.0) + delta_k = ceil(delta_k); /* +3.14 -> +4 */ + else + delta_k = floor(delta_k); /* -3.14 -> -4 */ + } + } + /* now determine the status and reduced cost of x[k] in the + current basis */ + if (k <= m) + { stat = glp_get_row_stat(mip, k); + dk = glp_get_row_dual(mip, k); + } + else + { stat = glp_get_col_stat(mip, k-m); + dk = glp_get_col_dual(mip, k-m); + } + /* if the current basis is dual degenerate, some reduced + costs which are close to zero may have wrong sign due to + round-off errors, so correct the sign of d[k] */ + switch (T->mip->dir) + { case GLP_MIN: + if (stat == GLP_NL && dk < 0.0 || + stat == GLP_NU && dk > 0.0 || + stat == GLP_NF) dk = 0.0; + break; + case GLP_MAX: + if (stat == GLP_NL && dk > 0.0 || + stat == GLP_NU && dk < 0.0 || + stat == GLP_NF) dk = 0.0; + break; + default: + xassert(T != T); + } + /* now knowing the change of x[k] and its reduced cost d[k] + we can compute the corresponding change in the objective + function delta Z = new Z - old Z = d[k] * delta x[k]; + note that due to Tomlin's modification new Z can be even + worse than in the adjacent basis */ + delta_z = dk * delta_k; +skip: /* new Z is never better than old Z, therefore the change + delta Z is always non-negative (in case of minimization) + or non-positive (in case of maximization) */ + switch (T->mip->dir) + { case GLP_MIN: xassert(delta_z >= 0.0); break; + case GLP_MAX: xassert(delta_z <= 0.0); break; + default: xassert(T != T); + } + /* save the change in the objective fnction for down- and + up-branches, respectively */ + if (kase < 0) dz_dn = delta_z; else dz_up = delta_z; + } + /* thus, in down-branch no integer feasible solution can be + better than Z + dz_dn, and in up-branch no integer feasible + solution can be better than Z + dz_up, where Z is value of + the objective function in the current basis */ + /* following the heuristic by Driebeck and Tomlin we choose a + column (i.e. structural variable) which provides largest + degradation of the objective function in some of branches; + besides, we select the branch with smaller degradation to + be solved next and keep other branch with larger degradation + in the active list hoping to minimize the number of further + backtrackings */ + if (degrad < fabs(dz_dn) || degrad < fabs(dz_up)) + { jj = j; + if (fabs(dz_dn) < fabs(dz_up)) + { /* select down branch to be solved next */ + next = GLP_DN_BRNCH; + degrad = fabs(dz_up); + } + else + { /* select up branch to be solved next */ + next = GLP_UP_BRNCH; + degrad = fabs(dz_dn); + } + /* save the objective changes for printing */ + dd_dn = dz_dn, dd_up = dz_up; + /* if down- or up-branch has no feasible solution, we does + not need to consider other candidates (in principle, the + corresponding branch could be pruned right now) */ + if (degrad == DBL_MAX) break; + } + } + /* free working arrays */ + xfree(ind); + xfree(val); + /* something must be chosen */ + xassert(1 <= jj && jj <= n); +#if 1 /* 02/XI-2009 */ + if (degrad < 1e-6 * (1.0 + 0.001 * fabs(mip->obj_val))) + { jj = branch_mostf(T, &next); + goto done; + } +#endif + if (T->parm->msg_lev >= GLP_MSG_DBG) + { xprintf("branch_drtom: column %d chosen to branch on\n", jj); + if (fabs(dd_dn) == DBL_MAX) + xprintf("branch_drtom: down-branch is infeasible\n"); + else + xprintf("branch_drtom: down-branch bound is %.9e\n", + lpx_get_obj_val(mip) + dd_dn); + if (fabs(dd_up) == DBL_MAX) + xprintf("branch_drtom: up-branch is infeasible\n"); + else + xprintf("branch_drtom: up-branch bound is %.9e\n", + lpx_get_obj_val(mip) + dd_up); + } +done: *_next = next; + return jj; +} + +/**********************************************************************/ + +struct csa +{ /* common storage area */ + int *dn_cnt; /* int dn_cnt[1+n]; */ + /* dn_cnt[j] is the number of subproblems, whose LP relaxations + have been solved and which are down-branches for variable x[j]; + dn_cnt[j] = 0 means the down pseudocost is uninitialized */ + double *dn_sum; /* double dn_sum[1+n]; */ + /* dn_sum[j] is the sum of per unit degradations of the objective + over all dn_cnt[j] subproblems */ + int *up_cnt; /* int up_cnt[1+n]; */ + /* up_cnt[j] is the number of subproblems, whose LP relaxations + have been solved and which are up-branches for variable x[j]; + up_cnt[j] = 0 means the up pseudocost is uninitialized */ + double *up_sum; /* double up_sum[1+n]; */ + /* up_sum[j] is the sum of per unit degradations of the objective + over all up_cnt[j] subproblems */ +}; + +void *ios_pcost_init(glp_tree *tree) +{ /* initialize working data used on pseudocost branching */ + struct csa *csa; + int n = tree->n, j; + csa = xmalloc(sizeof(struct csa)); + csa->dn_cnt = xcalloc(1+n, sizeof(int)); + csa->dn_sum = xcalloc(1+n, sizeof(double)); + csa->up_cnt = xcalloc(1+n, sizeof(int)); + csa->up_sum = xcalloc(1+n, sizeof(double)); + for (j = 1; j <= n; j++) + { csa->dn_cnt[j] = csa->up_cnt[j] = 0; + csa->dn_sum[j] = csa->up_sum[j] = 0.0; + } + return csa; +} + +static double eval_degrad(glp_prob *P, int j, double bnd) +{ /* compute degradation of the objective on fixing x[j] at given + value with a limited number of dual simplex iterations */ + /* this routine fixes column x[j] at specified value bnd, + solves resulting LP, and returns a lower bound to degradation + of the objective, degrad >= 0 */ + glp_prob *lp; + glp_smcp parm; + int ret; + double degrad; + /* the current basis must be optimal */ + xassert(glp_get_status(P) == GLP_OPT); + /* create a copy of P */ + lp = glp_create_prob(); + glp_copy_prob(lp, P, 0); + /* fix column x[j] at specified value */ + glp_set_col_bnds(lp, j, GLP_FX, bnd, bnd); + /* try to solve resulting LP */ + glp_init_smcp(&parm); + parm.msg_lev = GLP_MSG_OFF; + parm.meth = GLP_DUAL; + parm.it_lim = 30; + parm.out_dly = 1000; + parm.meth = GLP_DUAL; + ret = glp_simplex(lp, &parm); + if (ret == 0 || ret == GLP_EITLIM) + { if (glp_get_prim_stat(lp) == GLP_NOFEAS) + { /* resulting LP has no primal feasible solution */ + degrad = DBL_MAX; + } + else if (glp_get_dual_stat(lp) == GLP_FEAS) + { /* resulting basis is optimal or at least dual feasible, + so we have the correct lower bound to degradation */ + if (P->dir == GLP_MIN) + degrad = lp->obj_val - P->obj_val; + else if (P->dir == GLP_MAX) + degrad = P->obj_val - lp->obj_val; + else + xassert(P != P); + /* degradation cannot be negative by definition */ + /* note that the lower bound to degradation may be close + to zero even if its exact value is zero due to round-off + errors on computing the objective value */ + if (degrad < 1e-6 * (1.0 + 0.001 * fabs(P->obj_val))) + degrad = 0.0; + } + else + { /* the final basis reported by the simplex solver is dual + infeasible, so we cannot determine a non-trivial lower + bound to degradation */ + degrad = 0.0; + } + } + else + { /* the simplex solver failed */ + degrad = 0.0; + } + /* delete the copy of P */ + glp_delete_prob(lp); + return degrad; +} + +void ios_pcost_update(glp_tree *tree) +{ /* update history information for pseudocost branching */ + /* this routine is called every time when LP relaxation of the + current subproblem has been solved to optimality with all lazy + and cutting plane constraints included */ + int j; + double dx, dz, psi; + struct csa *csa = tree->pcost; + xassert(csa != NULL); + xassert(tree->curr != NULL); + /* if the current subproblem is the root, skip updating */ + if (tree->curr->up == NULL) goto skip; + /* determine branching variable x[j], which was used in the + parent subproblem to create the current subproblem */ + j = tree->curr->up->br_var; + xassert(1 <= j && j <= tree->n); + /* determine the change dx[j] = new x[j] - old x[j], + where new x[j] is a value of x[j] in optimal solution to LP + relaxation of the current subproblem, old x[j] is a value of + x[j] in optimal solution to LP relaxation of the parent + subproblem */ + dx = tree->mip->col[j]->prim - tree->curr->up->br_val; + xassert(dx != 0.0); + /* determine corresponding change dz = new dz - old dz in the + objective function value */ + dz = tree->mip->obj_val - tree->curr->up->lp_obj; + /* determine per unit degradation of the objective function */ + psi = fabs(dz / dx); + /* update history information */ + if (dx < 0.0) + { /* the current subproblem is down-branch */ + csa->dn_cnt[j]++; + csa->dn_sum[j] += psi; + } + else /* dx > 0.0 */ + { /* the current subproblem is up-branch */ + csa->up_cnt[j]++; + csa->up_sum[j] += psi; + } +skip: return; +} + +void ios_pcost_free(glp_tree *tree) +{ /* free working area used on pseudocost branching */ + struct csa *csa = tree->pcost; + xassert(csa != NULL); + xfree(csa->dn_cnt); + xfree(csa->dn_sum); + xfree(csa->up_cnt); + xfree(csa->up_sum); + xfree(csa); + tree->pcost = NULL; + return; +} + +static double eval_psi(glp_tree *T, int j, int brnch) +{ /* compute estimation of pseudocost of variable x[j] for down- + or up-branch */ + struct csa *csa = T->pcost; + double beta, degrad, psi; + xassert(csa != NULL); + xassert(1 <= j && j <= T->n); + if (brnch == GLP_DN_BRNCH) + { /* down-branch */ + if (csa->dn_cnt[j] == 0) + { /* initialize down pseudocost */ + beta = T->mip->col[j]->prim; + degrad = eval_degrad(T->mip, j, floor(beta)); + if (degrad == DBL_MAX) + { psi = DBL_MAX; + goto done; + } + csa->dn_cnt[j] = 1; + csa->dn_sum[j] = degrad / (beta - floor(beta)); + } + psi = csa->dn_sum[j] / (double)csa->dn_cnt[j]; + } + else if (brnch == GLP_UP_BRNCH) + { /* up-branch */ + if (csa->up_cnt[j] == 0) + { /* initialize up pseudocost */ + beta = T->mip->col[j]->prim; + degrad = eval_degrad(T->mip, j, ceil(beta)); + if (degrad == DBL_MAX) + { psi = DBL_MAX; + goto done; + } + csa->up_cnt[j] = 1; + csa->up_sum[j] = degrad / (ceil(beta) - beta); + } + psi = csa->up_sum[j] / (double)csa->up_cnt[j]; + } + else + xassert(brnch != brnch); +done: return psi; +} + +static void progress(glp_tree *T) +{ /* display progress of pseudocost initialization */ + struct csa *csa = T->pcost; + int j, nv = 0, ni = 0; + for (j = 1; j <= T->n; j++) + { if (glp_ios_can_branch(T, j)) + { nv++; + if (csa->dn_cnt[j] > 0 && csa->up_cnt[j] > 0) ni++; + } + } + xprintf("Pseudocosts initialized for %d of %d variables\n", + ni, nv); + return; +} + +int ios_pcost_branch(glp_tree *T, int *_next) +{ /* choose branching variable with pseudocost branching */ + glp_long t = xtime(); + int j, jjj, sel; + double beta, psi, d1, d2, d, dmax; + /* initialize the working arrays */ + if (T->pcost == NULL) + T->pcost = ios_pcost_init(T); + /* nothing has been chosen so far */ + jjj = 0, dmax = -1.0; + /* go through the list of branching candidates */ + for (j = 1; j <= T->n; j++) + { if (!glp_ios_can_branch(T, j)) continue; + /* determine primal value of x[j] in optimal solution to LP + relaxation of the current subproblem */ + beta = T->mip->col[j]->prim; + /* estimate pseudocost of x[j] for down-branch */ + psi = eval_psi(T, j, GLP_DN_BRNCH); + if (psi == DBL_MAX) + { /* down-branch has no primal feasible solution */ + jjj = j, sel = GLP_DN_BRNCH; + goto done; + } + /* estimate degradation of the objective for down-branch */ + d1 = psi * (beta - floor(beta)); + /* estimate pseudocost of x[j] for up-branch */ + psi = eval_psi(T, j, GLP_UP_BRNCH); + if (psi == DBL_MAX) + { /* up-branch has no primal feasible solution */ + jjj = j, sel = GLP_UP_BRNCH; + goto done; + } + /* estimate degradation of the objective for up-branch */ + d2 = psi * (ceil(beta) - beta); + /* determine d = max(d1, d2) */ + d = (d1 > d2 ? d1 : d2); + /* choose x[j] which provides maximal estimated degradation of + the objective either in down- or up-branch */ + if (dmax < d) + { dmax = d; + jjj = j; + /* continue the search from a subproblem, where degradation + is less than in other one */ + sel = (d1 <= d2 ? GLP_DN_BRNCH : GLP_UP_BRNCH); + } + /* display progress of pseudocost initialization */ + if (T->parm->msg_lev >= GLP_ON) + { if (xdifftime(xtime(), t) >= 10.0) + { progress(T); + t = xtime(); + } + } + } + if (dmax == 0.0) + { /* no degradation is indicated; choose a variable having most + fractional value */ + jjj = branch_mostf(T, &sel); + } +done: *_next = sel; + return jjj; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios10.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios10.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,348 @@ +/* glpios10.c (feasibility pump heuristic) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" +#include "glprng.h" + +/*********************************************************************** +* NAME +* +* ios_feas_pump - feasibility pump heuristic +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_feas_pump(glp_tree *T); +* +* DESCRIPTION +* +* The routine ios_feas_pump is a simple implementation of the Feasi- +* bility Pump heuristic. +* +* REFERENCES +* +* M.Fischetti, F.Glover, and A.Lodi. "The feasibility pump." Math. +* Program., Ser. A 104, pp. 91-104 (2005). */ + +struct VAR +{ /* binary variable */ + int j; + /* ordinal number */ + int x; + /* value in the rounded solution (0 or 1) */ + double d; + /* sorting key */ +}; + +static int fcmp(const void *x, const void *y) +{ /* comparison routine */ + const struct VAR *vx = x, *vy = y; + if (vx->d > vy->d) + return -1; + else if (vx->d < vy->d) + return +1; + else + return 0; +} + +void ios_feas_pump(glp_tree *T) +{ glp_prob *P = T->mip; + int n = P->n; + glp_prob *lp = NULL; + struct VAR *var = NULL; + RNG *rand = NULL; + GLPCOL *col; + glp_smcp parm; + int j, k, new_x, nfail, npass, nv, ret, stalling; + double dist, tol; + xassert(glp_get_status(P) == GLP_OPT); + /* this heuristic is applied only once on the root level */ + if (!(T->curr->level == 0 && T->curr->solved == 1)) goto done; + /* determine number of binary variables */ + nv = 0; + for (j = 1; j <= n; j++) + { col = P->col[j]; + /* if x[j] is continuous, skip it */ + if (col->kind == GLP_CV) continue; + /* if x[j] is fixed, skip it */ + if (col->type == GLP_FX) continue; + /* x[j] is non-fixed integer */ + xassert(col->kind == GLP_IV); + if (col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0) + { /* x[j] is binary */ + nv++; + } + else + { /* x[j] is general integer */ + if (T->parm->msg_lev >= GLP_MSG_ALL) + xprintf("FPUMP heuristic cannot be applied due to genera" + "l integer variables\n"); + goto done; + } + } + /* there must be at least one binary variable */ + if (nv == 0) goto done; + if (T->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Applying FPUMP heuristic...\n"); + /* build the list of binary variables */ + var = xcalloc(1+nv, sizeof(struct VAR)); + k = 0; + for (j = 1; j <= n; j++) + { col = P->col[j]; + if (col->kind == GLP_IV && col->type == GLP_DB) + var[++k].j = j; + } + xassert(k == nv); + /* create working problem object */ + lp = glp_create_prob(); +more: /* copy the original problem object to keep it intact */ + glp_copy_prob(lp, P, GLP_OFF); + /* we are interested to find an integer feasible solution, which + is better than the best known one */ + if (P->mip_stat == GLP_FEAS) + { int *ind; + double *val, bnd; + /* add a row and make it identical to the objective row */ + glp_add_rows(lp, 1); + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + for (j = 1; j <= n; j++) + { ind[j] = j; + val[j] = P->col[j]->coef; + } + glp_set_mat_row(lp, lp->m, n, ind, val); + xfree(ind); + xfree(val); + /* introduce upper (minimization) or lower (maximization) + bound to the original objective function; note that this + additional constraint is not violated at the optimal point + to LP relaxation */ +#if 0 /* modified by xypron */ + if (P->dir == GLP_MIN) + { bnd = P->mip_obj - 0.10 * (1.0 + fabs(P->mip_obj)); + if (bnd < P->obj_val) bnd = P->obj_val; + glp_set_row_bnds(lp, lp->m, GLP_UP, 0.0, bnd - P->c0); + } + else if (P->dir == GLP_MAX) + { bnd = P->mip_obj + 0.10 * (1.0 + fabs(P->mip_obj)); + if (bnd > P->obj_val) bnd = P->obj_val; + glp_set_row_bnds(lp, lp->m, GLP_LO, bnd - P->c0, 0.0); + } + else + xassert(P != P); +#else + bnd = 0.1 * P->obj_val + 0.9 * P->mip_obj; + /* xprintf("bnd = %f\n", bnd); */ + if (P->dir == GLP_MIN) + glp_set_row_bnds(lp, lp->m, GLP_UP, 0.0, bnd - P->c0); + else if (P->dir == GLP_MAX) + glp_set_row_bnds(lp, lp->m, GLP_LO, bnd - P->c0, 0.0); + else + xassert(P != P); +#endif + } + /* reset pass count */ + npass = 0; + /* invalidate the rounded point */ + for (k = 1; k <= nv; k++) + var[k].x = -1; +pass: /* next pass starts here */ + npass++; + if (T->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Pass %d\n", npass); + /* initialize minimal distance between the basic point and the + rounded one obtained during this pass */ + dist = DBL_MAX; + /* reset failure count (the number of succeeded iterations failed + to improve the distance) */ + nfail = 0; + /* if it is not the first pass, perturb the last rounded point + rather than construct it from the basic solution */ + if (npass > 1) + { double rho, temp; + if (rand == NULL) + rand = rng_create_rand(); + for (k = 1; k <= nv; k++) + { j = var[k].j; + col = lp->col[j]; + rho = rng_uniform(rand, -0.3, 0.7); + if (rho < 0.0) rho = 0.0; + temp = fabs((double)var[k].x - col->prim); + if (temp + rho > 0.5) var[k].x = 1 - var[k].x; + } + goto skip; + } +loop: /* innermost loop begins here */ + /* round basic solution (which is assumed primal feasible) */ + stalling = 1; + for (k = 1; k <= nv; k++) + { col = lp->col[var[k].j]; + if (col->prim < 0.5) + { /* rounded value is 0 */ + new_x = 0; + } + else + { /* rounded value is 1 */ + new_x = 1; + } + if (var[k].x != new_x) + { stalling = 0; + var[k].x = new_x; + } + } + /* if the rounded point has not changed (stalling), choose and + flip some its entries heuristically */ + if (stalling) + { /* compute d[j] = |x[j] - round(x[j])| */ + for (k = 1; k <= nv; k++) + { col = lp->col[var[k].j]; + var[k].d = fabs(col->prim - (double)var[k].x); + } + /* sort the list of binary variables by descending d[j] */ + qsort(&var[1], nv, sizeof(struct VAR), fcmp); + /* choose and flip some rounded components */ + for (k = 1; k <= nv; k++) + { if (k >= 5 && var[k].d < 0.35 || k >= 10) break; + var[k].x = 1 - var[k].x; + } + } +skip: /* check if the time limit has been exhausted */ + if (T->parm->tm_lim < INT_MAX && + (double)(T->parm->tm_lim - 1) <= + 1000.0 * xdifftime(xtime(), T->tm_beg)) goto done; + /* build the objective, which is the distance between the current + (basic) point and the rounded one */ + lp->dir = GLP_MIN; + lp->c0 = 0.0; + for (j = 1; j <= n; j++) + lp->col[j]->coef = 0.0; + for (k = 1; k <= nv; k++) + { j = var[k].j; + if (var[k].x == 0) + lp->col[j]->coef = +1.0; + else + { lp->col[j]->coef = -1.0; + lp->c0 += 1.0; + } + } + /* minimize the distance with the simplex method */ + glp_init_smcp(&parm); + if (T->parm->msg_lev <= GLP_MSG_ERR) + parm.msg_lev = T->parm->msg_lev; + else if (T->parm->msg_lev <= GLP_MSG_ALL) + { parm.msg_lev = GLP_MSG_ON; + parm.out_dly = 10000; + } + ret = glp_simplex(lp, &parm); + if (ret != 0) + { if (T->parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: glp_simplex returned %d\n", ret); + goto done; + } + ret = glp_get_status(lp); + if (ret != GLP_OPT) + { if (T->parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: glp_get_status returned %d\n", ret); + goto done; + } + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("delta = %g\n", lp->obj_val); + /* check if the basic solution is integer feasible; note that it + may be so even if the minimial distance is positive */ + tol = 0.3 * T->parm->tol_int; + for (k = 1; k <= nv; k++) + { col = lp->col[var[k].j]; + if (tol < col->prim && col->prim < 1.0 - tol) break; + } + if (k > nv) + { /* okay; the basic solution seems to be integer feasible */ + double *x = xcalloc(1+n, sizeof(double)); + for (j = 1; j <= n; j++) + { x[j] = lp->col[j]->prim; + if (P->col[j]->kind == GLP_IV) x[j] = floor(x[j] + 0.5); + } +#if 1 /* modified by xypron */ + /* reset direction and right-hand side of objective */ + lp->c0 = P->c0; + lp->dir = P->dir; + /* fix integer variables */ + for (k = 1; k <= nv; k++) + { lp->col[var[k].j]->lb = x[var[k].j]; + lp->col[var[k].j]->ub = x[var[k].j]; + lp->col[var[k].j]->type = GLP_FX; + } + /* copy original objective function */ + for (j = 1; j <= n; j++) + lp->col[j]->coef = P->col[j]->coef; + /* solve original LP and copy result */ + ret = glp_simplex(lp, &parm); + if (ret != 0) + { if (T->parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: glp_simplex returned %d\n", ret); + goto done; + } + ret = glp_get_status(lp); + if (ret != GLP_OPT) + { if (T->parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: glp_get_status returned %d\n", ret); + goto done; + } + for (j = 1; j <= n; j++) + if (P->col[j]->kind != GLP_IV) x[j] = lp->col[j]->prim; +#endif + ret = glp_ios_heur_sol(T, x); + xfree(x); + if (ret == 0) + { /* the integer solution is accepted */ + if (ios_is_hopeful(T, T->curr->bound)) + { /* it is reasonable to apply the heuristic once again */ + goto more; + } + else + { /* the best known integer feasible solution just found + is close to optimal solution to LP relaxation */ + goto done; + } + } + } + /* the basic solution is fractional */ + if (dist == DBL_MAX || + lp->obj_val <= dist - 1e-6 * (1.0 + dist)) + { /* the distance is reducing */ + nfail = 0, dist = lp->obj_val; + } + else + { /* improving the distance failed */ + nfail++; + } + if (nfail < 3) goto loop; + if (npass < 5) goto pass; +done: /* delete working objects */ + if (lp != NULL) glp_delete_prob(lp); + if (var != NULL) xfree(var); + if (rand != NULL) rng_delete_rand(rand); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios11.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios11.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,280 @@ +/* glpios11.c (process cuts stored in the local cut pool) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* NAME +* +* ios_process_cuts - process cuts stored in the local cut pool +* +* SYNOPSIS +* +* #include "glpios.h" +* void ios_process_cuts(glp_tree *T); +* +* DESCRIPTION +* +* The routine ios_process_cuts analyzes each cut currently stored in +* the local cut pool, which must be non-empty, and either adds the cut +* to the current subproblem or just discards it. All cuts are assumed +* to be locally valid. On exit the local cut pool remains unchanged. +* +* REFERENCES +* +* 1. E.Balas, S.Ceria, G.Cornuejols, "Mixed 0-1 Programming by +* Lift-and-Project in a Branch-and-Cut Framework", Management Sc., +* 42 (1996) 1229-1246. +* +* 2. G.Andreello, A.Caprara, and M.Fischetti, "Embedding Cuts in +* a Branch&Cut Framework: a Computational Study with {0,1/2}-Cuts", +* Preliminary Draft, October 28, 2003, pp.6-8. */ + +struct info +{ /* estimated cut efficiency */ + IOSCUT *cut; + /* pointer to cut in the cut pool */ + char flag; + /* if this flag is set, the cut is included into the current + subproblem */ + double eff; + /* cut efficacy (normalized residual) */ + double deg; + /* lower bound to objective degradation */ +}; + +static int fcmp(const void *arg1, const void *arg2) +{ const struct info *info1 = arg1, *info2 = arg2; + if (info1->deg == 0.0 && info2->deg == 0.0) + { if (info1->eff > info2->eff) return -1; + if (info1->eff < info2->eff) return +1; + } + else + { if (info1->deg > info2->deg) return -1; + if (info1->deg < info2->deg) return +1; + } + return 0; +} + +static double parallel(IOSCUT *a, IOSCUT *b, double work[]); + +void ios_process_cuts(glp_tree *T) +{ IOSPOOL *pool; + IOSCUT *cut; + IOSAIJ *aij; + struct info *info; + int k, kk, max_cuts, len, ret, *ind; + double *val, *work; + /* the current subproblem must exist */ + xassert(T->curr != NULL); + /* the pool must exist and be non-empty */ + pool = T->local; + xassert(pool != NULL); + xassert(pool->size > 0); + /* allocate working arrays */ + info = xcalloc(1+pool->size, sizeof(struct info)); + ind = xcalloc(1+T->n, sizeof(int)); + val = xcalloc(1+T->n, sizeof(double)); + work = xcalloc(1+T->n, sizeof(double)); + for (k = 1; k <= T->n; k++) work[k] = 0.0; + /* build the list of cuts stored in the cut pool */ + for (k = 0, cut = pool->head; cut != NULL; cut = cut->next) + k++, info[k].cut = cut, info[k].flag = 0; + xassert(k == pool->size); + /* estimate efficiency of all cuts in the cut pool */ + for (k = 1; k <= pool->size; k++) + { double temp, dy, dz; + cut = info[k].cut; + /* build the vector of cut coefficients and compute its + Euclidean norm */ + len = 0; temp = 0.0; + for (aij = cut->ptr; aij != NULL; aij = aij->next) + { xassert(1 <= aij->j && aij->j <= T->n); + len++, ind[len] = aij->j, val[len] = aij->val; + temp += aij->val * aij->val; + } + if (temp < DBL_EPSILON * DBL_EPSILON) temp = DBL_EPSILON; + /* transform the cut to express it only through non-basic + (auxiliary and structural) variables */ + len = glp_transform_row(T->mip, len, ind, val); + /* determine change in the cut value and in the objective + value for the adjacent basis by simulating one step of the + dual simplex */ + ret = _glp_analyze_row(T->mip, len, ind, val, cut->type, + cut->rhs, 1e-9, NULL, NULL, NULL, NULL, &dy, &dz); + /* determine normalized residual and lower bound to objective + degradation */ + if (ret == 0) + { info[k].eff = fabs(dy) / sqrt(temp); + /* if some reduced costs violates (slightly) their zero + bounds (i.e. have wrong signs) due to round-off errors, + dz also may have wrong sign being close to zero */ + if (T->mip->dir == GLP_MIN) + { if (dz < 0.0) dz = 0.0; + info[k].deg = + dz; + } + else /* GLP_MAX */ + { if (dz > 0.0) dz = 0.0; + info[k].deg = - dz; + } + } + else if (ret == 1) + { /* the constraint is not violated at the current point */ + info[k].eff = info[k].deg = 0.0; + } + else if (ret == 2) + { /* no dual feasible adjacent basis exists */ + info[k].eff = 1.0; + info[k].deg = DBL_MAX; + } + else + xassert(ret != ret); + /* if the degradation is too small, just ignore it */ + if (info[k].deg < 0.01) info[k].deg = 0.0; + } + /* sort the list of cuts by decreasing objective degradation and + then by decreasing efficacy */ + qsort(&info[1], pool->size, sizeof(struct info), fcmp); + /* only first (most efficient) max_cuts in the list are qualified + as candidates to be added to the current subproblem */ + max_cuts = (T->curr->level == 0 ? 90 : 10); + if (max_cuts > pool->size) max_cuts = pool->size; + /* add cuts to the current subproblem */ +#if 0 + xprintf("*** adding cuts ***\n"); +#endif + for (k = 1; k <= max_cuts; k++) + { int i, len; + /* if this cut seems to be inefficient, skip it */ + if (info[k].deg < 0.01 && info[k].eff < 0.01) continue; + /* if the angle between this cut and every other cut included + in the current subproblem is small, skip this cut */ + for (kk = 1; kk < k; kk++) + { if (info[kk].flag) + { if (parallel(info[k].cut, info[kk].cut, work) > 0.90) + break; + } + } + if (kk < k) continue; + /* add this cut to the current subproblem */ +#if 0 + xprintf("eff = %g; deg = %g\n", info[k].eff, info[k].deg); +#endif + cut = info[k].cut, info[k].flag = 1; + i = glp_add_rows(T->mip, 1); + if (cut->name != NULL) + glp_set_row_name(T->mip, i, cut->name); + xassert(T->mip->row[i]->origin == GLP_RF_CUT); + T->mip->row[i]->klass = cut->klass; + len = 0; + for (aij = cut->ptr; aij != NULL; aij = aij->next) + len++, ind[len] = aij->j, val[len] = aij->val; + glp_set_mat_row(T->mip, i, len, ind, val); + xassert(cut->type == GLP_LO || cut->type == GLP_UP); + glp_set_row_bnds(T->mip, i, cut->type, cut->rhs, cut->rhs); + } + /* free working arrays */ + xfree(info); + xfree(ind); + xfree(val); + xfree(work); + return; +} + +#if 0 +/*********************************************************************** +* Given a cut a * x >= b (<= b) the routine efficacy computes the cut +* efficacy as follows: +* +* eff = d * (a * x~ - b) / ||a||, +* +* where d is -1 (in case of '>= b') or +1 (in case of '<= b'), x~ is +* the vector of values of structural variables in optimal solution to +* LP relaxation of the current subproblem, ||a|| is the Euclidean norm +* of the vector of cut coefficients. +* +* If the cut is violated at point x~, the efficacy eff is positive, +* and its value is the Euclidean distance between x~ and the cut plane +* a * x = b in the space of structural variables. +* +* Following geometrical intuition, it is quite natural to consider +* this distance as a first-order measure of the expected efficacy of +* the cut: the larger the distance the better the cut [1]. */ + +static double efficacy(glp_tree *T, IOSCUT *cut) +{ glp_prob *mip = T->mip; + IOSAIJ *aij; + double s = 0.0, t = 0.0, temp; + for (aij = cut->ptr; aij != NULL; aij = aij->next) + { xassert(1 <= aij->j && aij->j <= mip->n); + s += aij->val * mip->col[aij->j]->prim; + t += aij->val * aij->val; + } + temp = sqrt(t); + if (temp < DBL_EPSILON) temp = DBL_EPSILON; + if (cut->type == GLP_LO) + temp = (s >= cut->rhs ? 0.0 : (cut->rhs - s) / temp); + else if (cut->type == GLP_UP) + temp = (s <= cut->rhs ? 0.0 : (s - cut->rhs) / temp); + else + xassert(cut != cut); + return temp; +} +#endif + +/*********************************************************************** +* Given two cuts a1 * x >= b1 (<= b1) and a2 * x >= b2 (<= b2) the +* routine parallel computes the cosine of angle between the cut planes +* a1 * x = b1 and a2 * x = b2 (which is the acute angle between two +* normals to these planes) in the space of structural variables as +* follows: +* +* cos phi = (a1' * a2) / (||a1|| * ||a2||), +* +* where (a1' * a2) is a dot product of vectors of cut coefficients, +* ||a1|| and ||a2|| are Euclidean norms of vectors a1 and a2. +* +* Note that requirement cos phi = 0 forces the cuts to be orthogonal, +* i.e. with disjoint support, while requirement cos phi <= 0.999 means +* only avoiding duplicate (parallel) cuts [1]. */ + +static double parallel(IOSCUT *a, IOSCUT *b, double work[]) +{ IOSAIJ *aij; + double s = 0.0, sa = 0.0, sb = 0.0, temp; + for (aij = a->ptr; aij != NULL; aij = aij->next) + { work[aij->j] = aij->val; + sa += aij->val * aij->val; + } + for (aij = b->ptr; aij != NULL; aij = aij->next) + { s += work[aij->j] * aij->val; + sb += aij->val * aij->val; + } + for (aij = a->ptr; aij != NULL; aij = aij->next) + work[aij->j] = 0.0; + temp = sqrt(sa) * sqrt(sb); + if (temp < DBL_EPSILON * DBL_EPSILON) temp = DBL_EPSILON; + return s / temp; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpios12.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpios12.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,176 @@ +/* glpios12.c (node selection heuristics) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpios.h" + +/*********************************************************************** +* NAME +* +* ios_choose_node - select subproblem to continue the search +* +* SYNOPSIS +* +* #include "glpios.h" +* int ios_choose_node(glp_tree *T); +* +* DESCRIPTION +* +* The routine ios_choose_node selects a subproblem from the active +* list to continue the search. The choice depends on the backtracking +* technique option. +* +* RETURNS +* +* The routine ios_choose_node return the reference number of the +* subproblem selected. */ + +static int most_feas(glp_tree *T); +static int best_proj(glp_tree *T); +static int best_node(glp_tree *T); + +int ios_choose_node(glp_tree *T) +{ int p; + if (T->parm->bt_tech == GLP_BT_DFS) + { /* depth first search */ + xassert(T->tail != NULL); + p = T->tail->p; + } + else if (T->parm->bt_tech == GLP_BT_BFS) + { /* breadth first search */ + xassert(T->head != NULL); + p = T->head->p; + } + else if (T->parm->bt_tech == GLP_BT_BLB) + { /* select node with best local bound */ + p = best_node(T); + } + else if (T->parm->bt_tech == GLP_BT_BPH) + { if (T->mip->mip_stat == GLP_UNDEF) + { /* "most integer feasible" subproblem */ + p = most_feas(T); + } + else + { /* best projection heuristic */ + p = best_proj(T); + } + } + else + xassert(T != T); + return p; +} + +static int most_feas(glp_tree *T) +{ /* select subproblem whose parent has minimal sum of integer + infeasibilities */ + IOSNPD *node; + int p; + double best; + p = 0, best = DBL_MAX; + for (node = T->head; node != NULL; node = node->next) + { xassert(node->up != NULL); + if (best > node->up->ii_sum) + p = node->p, best = node->up->ii_sum; + } + return p; +} + +static int best_proj(glp_tree *T) +{ /* select subproblem using the best projection heuristic */ + IOSNPD *root, *node; + int p; + double best, deg, obj; + /* the global bound must exist */ + xassert(T->mip->mip_stat == GLP_FEAS); + /* obtain pointer to the root node, which must exist */ + root = T->slot[1].node; + xassert(root != NULL); + /* deg estimates degradation of the objective function per unit + of the sum of integer infeasibilities */ + xassert(root->ii_sum > 0.0); + deg = (T->mip->mip_obj - root->bound) / root->ii_sum; + /* nothing has been selected so far */ + p = 0, best = DBL_MAX; + /* walk through the list of active subproblems */ + for (node = T->head; node != NULL; node = node->next) + { xassert(node->up != NULL); + /* obj estimates optimal objective value if the sum of integer + infeasibilities were zero */ + obj = node->up->bound + deg * node->up->ii_sum; + if (T->mip->dir == GLP_MAX) obj = - obj; + /* select the subproblem which has the best estimated optimal + objective value */ + if (best > obj) p = node->p, best = obj; + } + return p; +} + +static int best_node(glp_tree *T) +{ /* select subproblem with best local bound */ + IOSNPD *node, *best = NULL; + double bound, eps; + switch (T->mip->dir) + { case GLP_MIN: + bound = +DBL_MAX; + for (node = T->head; node != NULL; node = node->next) + if (bound > node->bound) bound = node->bound; + xassert(bound != +DBL_MAX); + eps = 0.001 * (1.0 + fabs(bound)); + for (node = T->head; node != NULL; node = node->next) + { if (node->bound <= bound + eps) + { xassert(node->up != NULL); + if (best == NULL || +#if 1 + best->up->ii_sum > node->up->ii_sum) best = node; +#else + best->lp_obj > node->lp_obj) best = node; +#endif + } + } + break; + case GLP_MAX: + bound = -DBL_MAX; + for (node = T->head; node != NULL; node = node->next) + if (bound < node->bound) bound = node->bound; + xassert(bound != -DBL_MAX); + eps = 0.001 * (1.0 + fabs(bound)); + for (node = T->head; node != NULL; node = node->next) + { if (node->bound >= bound - eps) + { xassert(node->up != NULL); + if (best == NULL || +#if 1 + best->up->ii_sum > node->up->ii_sum) best = node; +#else + best->lp_obj < node->lp_obj) best = node; +#endif + } + } + break; + default: + xassert(T != T); + } + xassert(best != NULL); + return best->p; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpipm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpipm.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1143 @@ +/* glpipm.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpipm.h" +#include "glpmat.h" + +#define ITER_MAX 100 +/* maximal number of iterations */ + +struct csa +{ /* common storage area */ + /*--------------------------------------------------------------*/ + /* LP data */ + int m; + /* number of rows (equality constraints) */ + int n; + /* number of columns (structural variables) */ + int *A_ptr; /* int A_ptr[1+m+1]; */ + int *A_ind; /* int A_ind[A_ptr[m+1]]; */ + double *A_val; /* double A_val[A_ptr[m+1]]; */ + /* mxn-matrix A in storage-by-rows format */ + double *b; /* double b[1+m]; */ + /* m-vector b of right-hand sides */ + double *c; /* double c[1+n]; */ + /* n-vector c of objective coefficients; c[0] is constant term of + the objective function */ + /*--------------------------------------------------------------*/ + /* LP solution */ + double *x; /* double x[1+n]; */ + double *y; /* double y[1+m]; */ + double *z; /* double z[1+n]; */ + /* current point in primal-dual space; the best point on exit */ + /*--------------------------------------------------------------*/ + /* control parameters */ + const glp_iptcp *parm; + /*--------------------------------------------------------------*/ + /* working arrays and variables */ + double *D; /* double D[1+n]; */ + /* diagonal nxn-matrix D = X*inv(Z), where X = diag(x[j]) and + Z = diag(z[j]) */ + int *P; /* int P[1+m+m]; */ + /* permutation mxm-matrix P used to minimize fill-in in Cholesky + factorization */ + int *S_ptr; /* int S_ptr[1+m+1]; */ + int *S_ind; /* int S_ind[S_ptr[m+1]]; */ + double *S_val; /* double S_val[S_ptr[m+1]]; */ + double *S_diag; /* double S_diag[1+m]; */ + /* symmetric mxm-matrix S = P*A*D*A'*P' whose upper triangular + part without diagonal elements is stored in S_ptr, S_ind, and + S_val in storage-by-rows format, diagonal elements are stored + in S_diag */ + int *U_ptr; /* int U_ptr[1+m+1]; */ + int *U_ind; /* int U_ind[U_ptr[m+1]]; */ + double *U_val; /* double U_val[U_ptr[m+1]]; */ + double *U_diag; /* double U_diag[1+m]; */ + /* upper triangular mxm-matrix U defining Cholesky factorization + S = U'*U; its non-diagonal elements are stored in U_ptr, U_ind, + U_val in storage-by-rows format, diagonal elements are stored + in U_diag */ + int iter; + /* iteration number (0, 1, 2, ...); iter = 0 corresponds to the + initial point */ + double obj; + /* current value of the objective function */ + double rpi; + /* relative primal infeasibility rpi = ||A*x-b||/(1+||b||) */ + double rdi; + /* relative dual infeasibility rdi = ||A'*y+z-c||/(1+||c||) */ + double gap; + /* primal-dual gap = |c'*x-b'*y|/(1+|c'*x|) which is a relative + difference between primal and dual objective functions */ + double phi; + /* merit function phi = ||A*x-b||/max(1,||b||) + + + ||A'*y+z-c||/max(1,||c||) + + + |c'*x-b'*y|/max(1,||b||,||c||) */ + double mu; + /* duality measure mu = x'*z/n (used as barrier parameter) */ + double rmu; + /* rmu = max(||A*x-b||,||A'*y+z-c||)/mu */ + double rmu0; + /* the initial value of rmu on iteration 0 */ + double *phi_min; /* double phi_min[1+ITER_MAX]; */ + /* phi_min[k] = min(phi[k]), where phi[k] is the value of phi on + k-th iteration, 0 <= k <= iter */ + int best_iter; + /* iteration number, on which the value of phi reached its best + (minimal) value */ + double *best_x; /* double best_x[1+n]; */ + double *best_y; /* double best_y[1+m]; */ + double *best_z; /* double best_z[1+n]; */ + /* best point (in the sense of the merit function phi) which has + been reached on iteration iter_best */ + double best_obj; + /* objective value at the best point */ + double *dx_aff; /* double dx_aff[1+n]; */ + double *dy_aff; /* double dy_aff[1+m]; */ + double *dz_aff; /* double dz_aff[1+n]; */ + /* affine scaling direction */ + double alfa_aff_p, alfa_aff_d; + /* maximal primal and dual stepsizes in affine scaling direction, + on which x and z are still non-negative */ + double mu_aff; + /* duality measure mu_aff = x_aff'*z_aff/n in the boundary point + x_aff' = x+alfa_aff_p*dx_aff, z_aff' = z+alfa_aff_d*dz_aff */ + double sigma; + /* Mehrotra's heuristic parameter (0 <= sigma <= 1) */ + double *dx_cc; /* double dx_cc[1+n]; */ + double *dy_cc; /* double dy_cc[1+m]; */ + double *dz_cc; /* double dz_cc[1+n]; */ + /* centering corrector direction */ + double *dx; /* double dx[1+n]; */ + double *dy; /* double dy[1+m]; */ + double *dz; /* double dz[1+n]; */ + /* final combined direction dx = dx_aff+dx_cc, dy = dy_aff+dy_cc, + dz = dz_aff+dz_cc */ + double alfa_max_p; + double alfa_max_d; + /* maximal primal and dual stepsizes in combined direction, on + which x and z are still non-negative */ +}; + +/*********************************************************************** +* initialize - allocate and initialize common storage area +* +* This routine allocates and initializes the common storage area (CSA) +* used by interior-point method routines. */ + +static void initialize(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + int i; + if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Matrix A has %d non-zeros\n", csa->A_ptr[m+1]-1); + csa->D = xcalloc(1+n, sizeof(double)); + /* P := I */ + csa->P = xcalloc(1+m+m, sizeof(int)); + for (i = 1; i <= m; i++) csa->P[i] = csa->P[m+i] = i; + /* S := A*A', symbolically */ + csa->S_ptr = xcalloc(1+m+1, sizeof(int)); + csa->S_ind = adat_symbolic(m, n, csa->P, csa->A_ptr, csa->A_ind, + csa->S_ptr); + if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Matrix S = A*A' has %d non-zeros (upper triangle)\n", + csa->S_ptr[m+1]-1 + m); + /* determine P using specified ordering algorithm */ + if (csa->parm->ord_alg == GLP_ORD_NONE) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Original ordering is being used\n"); + for (i = 1; i <= m; i++) + csa->P[i] = csa->P[m+i] = i; + } + else if (csa->parm->ord_alg == GLP_ORD_QMD) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Minimum degree ordering (QMD)...\n"); + min_degree(m, csa->S_ptr, csa->S_ind, csa->P); + } + else if (csa->parm->ord_alg == GLP_ORD_AMD) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Approximate minimum degree ordering (AMD)...\n"); + amd_order1(m, csa->S_ptr, csa->S_ind, csa->P); + } + else if (csa->parm->ord_alg == GLP_ORD_SYMAMD) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Approximate minimum degree ordering (SYMAMD)...\n") + ; + symamd_ord(m, csa->S_ptr, csa->S_ind, csa->P); + } + else + xassert(csa != csa); + /* S := P*A*A'*P', symbolically */ + xfree(csa->S_ind); + csa->S_ind = adat_symbolic(m, n, csa->P, csa->A_ptr, csa->A_ind, + csa->S_ptr); + csa->S_val = xcalloc(csa->S_ptr[m+1], sizeof(double)); + csa->S_diag = xcalloc(1+m, sizeof(double)); + /* compute Cholesky factorization S = U'*U, symbolically */ + if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Computing Cholesky factorization S = L*L'...\n"); + csa->U_ptr = xcalloc(1+m+1, sizeof(int)); + csa->U_ind = chol_symbolic(m, csa->S_ptr, csa->S_ind, csa->U_ptr); + if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Matrix L has %d non-zeros\n", csa->U_ptr[m+1]-1 + m); + csa->U_val = xcalloc(csa->U_ptr[m+1], sizeof(double)); + csa->U_diag = xcalloc(1+m, sizeof(double)); + csa->iter = 0; + csa->obj = 0.0; + csa->rpi = 0.0; + csa->rdi = 0.0; + csa->gap = 0.0; + csa->phi = 0.0; + csa->mu = 0.0; + csa->rmu = 0.0; + csa->rmu0 = 0.0; + csa->phi_min = xcalloc(1+ITER_MAX, sizeof(double)); + csa->best_iter = 0; + csa->best_x = xcalloc(1+n, sizeof(double)); + csa->best_y = xcalloc(1+m, sizeof(double)); + csa->best_z = xcalloc(1+n, sizeof(double)); + csa->best_obj = 0.0; + csa->dx_aff = xcalloc(1+n, sizeof(double)); + csa->dy_aff = xcalloc(1+m, sizeof(double)); + csa->dz_aff = xcalloc(1+n, sizeof(double)); + csa->alfa_aff_p = 0.0; + csa->alfa_aff_d = 0.0; + csa->mu_aff = 0.0; + csa->sigma = 0.0; + csa->dx_cc = xcalloc(1+n, sizeof(double)); + csa->dy_cc = xcalloc(1+m, sizeof(double)); + csa->dz_cc = xcalloc(1+n, sizeof(double)); + csa->dx = csa->dx_aff; + csa->dy = csa->dy_aff; + csa->dz = csa->dz_aff; + csa->alfa_max_p = 0.0; + csa->alfa_max_d = 0.0; + return; +} + +/*********************************************************************** +* A_by_vec - compute y = A*x +* +* This routine computes matrix-vector product y = A*x, where A is the +* constraint matrix. */ + +static void A_by_vec(struct csa *csa, double x[], double y[]) +{ /* compute y = A*x */ + int m = csa->m; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int i, t, beg, end; + double temp; + for (i = 1; i <= m; i++) + { temp = 0.0; + beg = A_ptr[i], end = A_ptr[i+1]; + for (t = beg; t < end; t++) temp += A_val[t] * x[A_ind[t]]; + y[i] = temp; + } + return; +} + +/*********************************************************************** +* AT_by_vec - compute y = A'*x +* +* This routine computes matrix-vector product y = A'*x, where A' is a +* matrix transposed to the constraint matrix A. */ + +static void AT_by_vec(struct csa *csa, double x[], double y[]) +{ /* compute y = A'*x, where A' is transposed to A */ + int m = csa->m; + int n = csa->n; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int i, j, t, beg, end; + double temp; + for (j = 1; j <= n; j++) y[j] = 0.0; + for (i = 1; i <= m; i++) + { temp = x[i]; + if (temp == 0.0) continue; + beg = A_ptr[i], end = A_ptr[i+1]; + for (t = beg; t < end; t++) y[A_ind[t]] += A_val[t] * temp; + } + return; +} + +/*********************************************************************** +* decomp_NE - numeric factorization of matrix S = P*A*D*A'*P' +* +* This routine implements numeric phase of Cholesky factorization of +* the matrix S = P*A*D*A'*P', which is a permuted matrix of the normal +* equation system. Matrix D is assumed to be already computed. */ + +static void decomp_NE(struct csa *csa) +{ adat_numeric(csa->m, csa->n, csa->P, csa->A_ptr, csa->A_ind, + csa->A_val, csa->D, csa->S_ptr, csa->S_ind, csa->S_val, + csa->S_diag); + chol_numeric(csa->m, csa->S_ptr, csa->S_ind, csa->S_val, + csa->S_diag, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag); + return; +} + +/*********************************************************************** +* solve_NE - solve normal equation system +* +* This routine solves the normal equation system: +* +* A*D*A'*y = h. +* +* It is assumed that the matrix A*D*A' has been previously factorized +* by the routine decomp_NE. +* +* On entry the array y contains the vector of right-hand sides h. On +* exit this array contains the computed vector of unknowns y. +* +* Once the vector y has been computed the routine checks for numeric +* stability. If the residual vector: +* +* r = A*D*A'*y - h +* +* is relatively small, the routine returns zero, otherwise non-zero is +* returned. */ + +static int solve_NE(struct csa *csa, double y[]) +{ int m = csa->m; + int n = csa->n; + int *P = csa->P; + int i, j, ret = 0; + double *h, *r, *w; + /* save vector of right-hand sides h */ + h = xcalloc(1+m, sizeof(double)); + for (i = 1; i <= m; i++) h[i] = y[i]; + /* solve normal equation system (A*D*A')*y = h */ + /* since S = P*A*D*A'*P' = U'*U, then A*D*A' = P'*U'*U*P, so we + have inv(A*D*A') = P'*inv(U)*inv(U')*P */ + /* w := P*h */ + w = xcalloc(1+m, sizeof(double)); + for (i = 1; i <= m; i++) w[i] = y[P[i]]; + /* w := inv(U')*w */ + ut_solve(m, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag, w); + /* w := inv(U)*w */ + u_solve(m, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag, w); + /* y := P'*w */ + for (i = 1; i <= m; i++) y[i] = w[P[m+i]]; + xfree(w); + /* compute residual vector r = A*D*A'*y - h */ + r = xcalloc(1+m, sizeof(double)); + /* w := A'*y */ + w = xcalloc(1+n, sizeof(double)); + AT_by_vec(csa, y, w); + /* w := D*w */ + for (j = 1; j <= n; j++) w[j] *= csa->D[j]; + /* r := A*w */ + A_by_vec(csa, w, r); + xfree(w); + /* r := r - h */ + for (i = 1; i <= m; i++) r[i] -= h[i]; + /* check for numeric stability */ + for (i = 1; i <= m; i++) + { if (fabs(r[i]) / (1.0 + fabs(h[i])) > 1e-4) + { ret = 1; + break; + } + } + xfree(h); + xfree(r); + return ret; +} + +/*********************************************************************** +* solve_NS - solve Newtonian system +* +* This routine solves the Newtonian system: +* +* A*dx = p +* +* A'*dy + dz = q +* +* Z*dx + X*dz = r +* +* where X = diag(x[j]), Z = diag(z[j]), by reducing it to the normal +* equation system: +* +* (A*inv(Z)*X*A')*dy = A*inv(Z)*(X*q-r)+p +* +* (it is assumed that the matrix A*inv(Z)*X*A' has been factorized by +* the routine decomp_NE). +* +* Once vector dy has been computed the routine computes vectors dx and +* dz as follows: +* +* dx = inv(Z)*(X*(A'*dy-q)+r) +* +* dz = inv(X)*(r-Z*dx) +* +* The routine solve_NS returns the same code which was reported by the +* routine solve_NE (see above). */ + +static int solve_NS(struct csa *csa, double p[], double q[], double r[], + double dx[], double dy[], double dz[]) +{ int m = csa->m; + int n = csa->n; + double *x = csa->x; + double *z = csa->z; + int i, j, ret; + double *w = dx; + /* compute the vector of right-hand sides A*inv(Z)*(X*q-r)+p for + the normal equation system */ + for (j = 1; j <= n; j++) + w[j] = (x[j] * q[j] - r[j]) / z[j]; + A_by_vec(csa, w, dy); + for (i = 1; i <= m; i++) dy[i] += p[i]; + /* solve the normal equation system to compute vector dy */ + ret = solve_NE(csa, dy); + /* compute vectors dx and dz */ + AT_by_vec(csa, dy, dx); + for (j = 1; j <= n; j++) + { dx[j] = (x[j] * (dx[j] - q[j]) + r[j]) / z[j]; + dz[j] = (r[j] - z[j] * dx[j]) / x[j]; + } + return ret; +} + +/*********************************************************************** +* initial_point - choose initial point using Mehrotra's heuristic +* +* This routine chooses a starting point using a heuristic proposed in +* the paper: +* +* S. Mehrotra. On the implementation of a primal-dual interior point +* method. SIAM J. on Optim., 2(4), pp. 575-601, 1992. +* +* The starting point x in the primal space is chosen as a solution of +* the following least squares problem: +* +* minimize ||x|| +* +* subject to A*x = b +* +* which can be computed explicitly as follows: +* +* x = A'*inv(A*A')*b +* +* Similarly, the starting point (y, z) in the dual space is chosen as +* a solution of the following least squares problem: +* +* minimize ||z|| +* +* subject to A'*y + z = c +* +* which can be computed explicitly as follows: +* +* y = inv(A*A')*A*c +* +* z = c - A'*y +* +* However, some components of the vectors x and z may be non-positive +* or close to zero, so the routine uses a Mehrotra's heuristic to find +* a more appropriate starting point. */ + +static void initial_point(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + double *b = csa->b; + double *c = csa->c; + double *x = csa->x; + double *y = csa->y; + double *z = csa->z; + double *D = csa->D; + int i, j; + double dp, dd, ex, ez, xz; + /* factorize A*A' */ + for (j = 1; j <= n; j++) D[j] = 1.0; + decomp_NE(csa); + /* x~ = A'*inv(A*A')*b */ + for (i = 1; i <= m; i++) y[i] = b[i]; + solve_NE(csa, y); + AT_by_vec(csa, y, x); + /* y~ = inv(A*A')*A*c */ + A_by_vec(csa, c, y); + solve_NE(csa, y); + /* z~ = c - A'*y~ */ + AT_by_vec(csa, y,z); + for (j = 1; j <= n; j++) z[j] = c[j] - z[j]; + /* use Mehrotra's heuristic in order to choose more appropriate + starting point with positive components of vectors x and z */ + dp = dd = 0.0; + for (j = 1; j <= n; j++) + { if (dp < -1.5 * x[j]) dp = -1.5 * x[j]; + if (dd < -1.5 * z[j]) dd = -1.5 * z[j]; + } + /* note that b = 0 involves x = 0, and c = 0 involves y = 0 and + z = 0, so we need to be careful */ + if (dp == 0.0) dp = 1.5; + if (dd == 0.0) dd = 1.5; + ex = ez = xz = 0.0; + for (j = 1; j <= n; j++) + { ex += (x[j] + dp); + ez += (z[j] + dd); + xz += (x[j] + dp) * (z[j] + dd); + } + dp += 0.5 * (xz / ez); + dd += 0.5 * (xz / ex); + for (j = 1; j <= n; j++) + { x[j] += dp; + z[j] += dd; + xassert(x[j] > 0.0 && z[j] > 0.0); + } + return; +} + +/*********************************************************************** +* basic_info - perform basic computations at the current point +* +* This routine computes the following quantities at the current point: +* +* 1) value of the objective function: +* +* F = c'*x + c[0] +* +* 2) relative primal infeasibility: +* +* rpi = ||A*x-b|| / (1+||b||) +* +* 3) relative dual infeasibility: +* +* rdi = ||A'*y+z-c|| / (1+||c||) +* +* 4) primal-dual gap (relative difference between the primal and the +* dual objective function values): +* +* gap = |c'*x-b'*y| / (1+|c'*x|) +* +* 5) merit function: +* +* phi = ||A*x-b|| / max(1,||b||) + ||A'*y+z-c|| / max(1,||c||) + +* +* + |c'*x-b'*y| / max(1,||b||,||c||) +* +* 6) duality measure: +* +* mu = x'*z / n +* +* 7) the ratio of infeasibility to mu: +* +* rmu = max(||A*x-b||,||A'*y+z-c||) / mu +* +* where ||*|| denotes euclidian norm, *' denotes transposition. */ + +static void basic_info(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + double *b = csa->b; + double *c = csa->c; + double *x = csa->x; + double *y = csa->y; + double *z = csa->z; + int i, j; + double norm1, bnorm, norm2, cnorm, cx, by, *work, temp; + /* compute value of the objective function */ + temp = c[0]; + for (j = 1; j <= n; j++) temp += c[j] * x[j]; + csa->obj = temp; + /* norm1 = ||A*x-b|| */ + work = xcalloc(1+m, sizeof(double)); + A_by_vec(csa, x, work); + norm1 = 0.0; + for (i = 1; i <= m; i++) + norm1 += (work[i] - b[i]) * (work[i] - b[i]); + norm1 = sqrt(norm1); + xfree(work); + /* bnorm = ||b|| */ + bnorm = 0.0; + for (i = 1; i <= m; i++) bnorm += b[i] * b[i]; + bnorm = sqrt(bnorm); + /* compute relative primal infeasibility */ + csa->rpi = norm1 / (1.0 + bnorm); + /* norm2 = ||A'*y+z-c|| */ + work = xcalloc(1+n, sizeof(double)); + AT_by_vec(csa, y, work); + norm2 = 0.0; + for (j = 1; j <= n; j++) + norm2 += (work[j] + z[j] - c[j]) * (work[j] + z[j] - c[j]); + norm2 = sqrt(norm2); + xfree(work); + /* cnorm = ||c|| */ + cnorm = 0.0; + for (j = 1; j <= n; j++) cnorm += c[j] * c[j]; + cnorm = sqrt(cnorm); + /* compute relative dual infeasibility */ + csa->rdi = norm2 / (1.0 + cnorm); + /* by = b'*y */ + by = 0.0; + for (i = 1; i <= m; i++) by += b[i] * y[i]; + /* cx = c'*x */ + cx = 0.0; + for (j = 1; j <= n; j++) cx += c[j] * x[j]; + /* compute primal-dual gap */ + csa->gap = fabs(cx - by) / (1.0 + fabs(cx)); + /* compute merit function */ + csa->phi = 0.0; + csa->phi += norm1 / (bnorm > 1.0 ? bnorm : 1.0); + csa->phi += norm2 / (cnorm > 1.0 ? cnorm : 1.0); + temp = 1.0; + if (temp < bnorm) temp = bnorm; + if (temp < cnorm) temp = cnorm; + csa->phi += fabs(cx - by) / temp; + /* compute duality measure */ + temp = 0.0; + for (j = 1; j <= n; j++) temp += x[j] * z[j]; + csa->mu = temp / (double)n; + /* compute the ratio of infeasibility to mu */ + csa->rmu = (norm1 > norm2 ? norm1 : norm2) / csa->mu; + return; +} + +/*********************************************************************** +* make_step - compute next point using Mehrotra's technique +* +* This routine computes the next point using the predictor-corrector +* technique proposed in the paper: +* +* S. Mehrotra. On the implementation of a primal-dual interior point +* method. SIAM J. on Optim., 2(4), pp. 575-601, 1992. +* +* At first, the routine computes so called affine scaling (predictor) +* direction (dx_aff,dy_aff,dz_aff) which is a solution of the system: +* +* A*dx_aff = b - A*x +* +* A'*dy_aff + dz_aff = c - A'*y - z +* +* Z*dx_aff + X*dz_aff = - X*Z*e +* +* where (x,y,z) is the current point, X = diag(x[j]), Z = diag(z[j]), +* e = (1,...,1)'. +* +* Then, the routine computes the centering parameter sigma, using the +* following Mehrotra's heuristic: +* +* alfa_aff_p = inf{0 <= alfa <= 1 | x+alfa*dx_aff >= 0} +* +* alfa_aff_d = inf{0 <= alfa <= 1 | z+alfa*dz_aff >= 0} +* +* mu_aff = (x+alfa_aff_p*dx_aff)'*(z+alfa_aff_d*dz_aff)/n +* +* sigma = (mu_aff/mu)^3 +* +* where alfa_aff_p is the maximal stepsize along the affine scaling +* direction in the primal space, alfa_aff_d is the maximal stepsize +* along the same direction in the dual space. +* +* After determining sigma the routine computes so called centering +* (corrector) direction (dx_cc,dy_cc,dz_cc) which is the solution of +* the system: +* +* A*dx_cc = 0 +* +* A'*dy_cc + dz_cc = 0 +* +* Z*dx_cc + X*dz_cc = sigma*mu*e - X*Z*e +* +* Finally, the routine computes the combined direction +* +* (dx,dy,dz) = (dx_aff,dy_aff,dz_aff) + (dx_cc,dy_cc,dz_cc) +* +* and determines maximal primal and dual stepsizes along the combined +* direction: +* +* alfa_max_p = inf{0 <= alfa <= 1 | x+alfa*dx >= 0} +* +* alfa_max_d = inf{0 <= alfa <= 1 | z+alfa*dz >= 0} +* +* In order to prevent the next point to be too close to the boundary +* of the positive ortant, the routine decreases maximal stepsizes: +* +* alfa_p = gamma_p * alfa_max_p +* +* alfa_d = gamma_d * alfa_max_d +* +* where gamma_p and gamma_d are scaling factors, and computes the next +* point: +* +* x_new = x + alfa_p * dx +* +* y_new = y + alfa_d * dy +* +* z_new = z + alfa_d * dz +* +* which becomes the current point on the next iteration. */ + +static int make_step(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + double *b = csa->b; + double *c = csa->c; + double *x = csa->x; + double *y = csa->y; + double *z = csa->z; + double *dx_aff = csa->dx_aff; + double *dy_aff = csa->dy_aff; + double *dz_aff = csa->dz_aff; + double *dx_cc = csa->dx_cc; + double *dy_cc = csa->dy_cc; + double *dz_cc = csa->dz_cc; + double *dx = csa->dx; + double *dy = csa->dy; + double *dz = csa->dz; + int i, j, ret = 0; + double temp, gamma_p, gamma_d, *p, *q, *r; + /* allocate working arrays */ + p = xcalloc(1+m, sizeof(double)); + q = xcalloc(1+n, sizeof(double)); + r = xcalloc(1+n, sizeof(double)); + /* p = b - A*x */ + A_by_vec(csa, x, p); + for (i = 1; i <= m; i++) p[i] = b[i] - p[i]; + /* q = c - A'*y - z */ + AT_by_vec(csa, y,q); + for (j = 1; j <= n; j++) q[j] = c[j] - q[j] - z[j]; + /* r = - X * Z * e */ + for (j = 1; j <= n; j++) r[j] = - x[j] * z[j]; + /* solve the first Newtonian system */ + if (solve_NS(csa, p, q, r, dx_aff, dy_aff, dz_aff)) + { ret = 1; + goto done; + } + /* alfa_aff_p = inf{0 <= alfa <= 1 | x + alfa*dx_aff >= 0} */ + /* alfa_aff_d = inf{0 <= alfa <= 1 | z + alfa*dz_aff >= 0} */ + csa->alfa_aff_p = csa->alfa_aff_d = 1.0; + for (j = 1; j <= n; j++) + { if (dx_aff[j] < 0.0) + { temp = - x[j] / dx_aff[j]; + if (csa->alfa_aff_p > temp) csa->alfa_aff_p = temp; + } + if (dz_aff[j] < 0.0) + { temp = - z[j] / dz_aff[j]; + if (csa->alfa_aff_d > temp) csa->alfa_aff_d = temp; + } + } + /* mu_aff = (x+alfa_aff_p*dx_aff)' * (z+alfa_aff_d*dz_aff) / n */ + temp = 0.0; + for (j = 1; j <= n; j++) + temp += (x[j] + csa->alfa_aff_p * dx_aff[j]) * + (z[j] + csa->alfa_aff_d * dz_aff[j]); + csa->mu_aff = temp / (double)n; + /* sigma = (mu_aff/mu)^3 */ + temp = csa->mu_aff / csa->mu; + csa->sigma = temp * temp * temp; + /* p = 0 */ + for (i = 1; i <= m; i++) p[i] = 0.0; + /* q = 0 */ + for (j = 1; j <= n; j++) q[j] = 0.0; + /* r = sigma * mu * e - X * Z * e */ + for (j = 1; j <= n; j++) + r[j] = csa->sigma * csa->mu - dx_aff[j] * dz_aff[j]; + /* solve the second Newtonian system with the same coefficients + but with altered right-hand sides */ + if (solve_NS(csa, p, q, r, dx_cc, dy_cc, dz_cc)) + { ret = 1; + goto done; + } + /* (dx,dy,dz) = (dx_aff,dy_aff,dz_aff) + (dx_cc,dy_cc,dz_cc) */ + for (j = 1; j <= n; j++) dx[j] = dx_aff[j] + dx_cc[j]; + for (i = 1; i <= m; i++) dy[i] = dy_aff[i] + dy_cc[i]; + for (j = 1; j <= n; j++) dz[j] = dz_aff[j] + dz_cc[j]; + /* alfa_max_p = inf{0 <= alfa <= 1 | x + alfa*dx >= 0} */ + /* alfa_max_d = inf{0 <= alfa <= 1 | z + alfa*dz >= 0} */ + csa->alfa_max_p = csa->alfa_max_d = 1.0; + for (j = 1; j <= n; j++) + { if (dx[j] < 0.0) + { temp = - x[j] / dx[j]; + if (csa->alfa_max_p > temp) csa->alfa_max_p = temp; + } + if (dz[j] < 0.0) + { temp = - z[j] / dz[j]; + if (csa->alfa_max_d > temp) csa->alfa_max_d = temp; + } + } + /* determine scale factors (not implemented yet) */ + gamma_p = 0.90; + gamma_d = 0.90; + /* compute the next point */ + for (j = 1; j <= n; j++) + { x[j] += gamma_p * csa->alfa_max_p * dx[j]; + xassert(x[j] > 0.0); + } + for (i = 1; i <= m; i++) + y[i] += gamma_d * csa->alfa_max_d * dy[i]; + for (j = 1; j <= n; j++) + { z[j] += gamma_d * csa->alfa_max_d * dz[j]; + xassert(z[j] > 0.0); + } +done: /* free working arrays */ + xfree(p); + xfree(q); + xfree(r); + return ret; +} + +/*********************************************************************** +* terminate - deallocate common storage area +* +* This routine frees all memory allocated to the common storage area +* used by interior-point method routines. */ + +static void terminate(struct csa *csa) +{ xfree(csa->D); + xfree(csa->P); + xfree(csa->S_ptr); + xfree(csa->S_ind); + xfree(csa->S_val); + xfree(csa->S_diag); + xfree(csa->U_ptr); + xfree(csa->U_ind); + xfree(csa->U_val); + xfree(csa->U_diag); + xfree(csa->phi_min); + xfree(csa->best_x); + xfree(csa->best_y); + xfree(csa->best_z); + xfree(csa->dx_aff); + xfree(csa->dy_aff); + xfree(csa->dz_aff); + xfree(csa->dx_cc); + xfree(csa->dy_cc); + xfree(csa->dz_cc); + return; +} + +/*********************************************************************** +* ipm_main - main interior-point method routine +* +* This is a main routine of the primal-dual interior-point method. +* +* The routine ipm_main returns one of the following codes: +* +* 0 - optimal solution found; +* 1 - problem has no feasible (primal or dual) solution; +* 2 - no convergence; +* 3 - iteration limit exceeded; +* 4 - numeric instability on solving Newtonian system. +* +* In case of non-zero return code the routine returns the best point, +* which has been reached during optimization. */ + +static int ipm_main(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + int i, j, status; + double temp; + /* choose initial point using Mehrotra's heuristic */ + if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Guessing initial point...\n"); + initial_point(csa); + /* main loop starts here */ + if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Optimization begins...\n"); + for (;;) + { /* perform basic computations at the current point */ + basic_info(csa); + /* save initial value of rmu */ + if (csa->iter == 0) csa->rmu0 = csa->rmu; + /* accumulate values of min(phi[k]) and save the best point */ + xassert(csa->iter <= ITER_MAX); + if (csa->iter == 0 || csa->phi_min[csa->iter-1] > csa->phi) + { csa->phi_min[csa->iter] = csa->phi; + csa->best_iter = csa->iter; + for (j = 1; j <= n; j++) csa->best_x[j] = csa->x[j]; + for (i = 1; i <= m; i++) csa->best_y[i] = csa->y[i]; + for (j = 1; j <= n; j++) csa->best_z[j] = csa->z[j]; + csa->best_obj = csa->obj; + } + else + csa->phi_min[csa->iter] = csa->phi_min[csa->iter-1]; + /* display information at the current point */ + if (csa->parm->msg_lev >= GLP_MSG_ON) + xprintf("%3d: obj = %17.9e; rpi = %8.1e; rdi = %8.1e; gap =" + " %8.1e\n", csa->iter, csa->obj, csa->rpi, csa->rdi, + csa->gap); + /* check if the current point is optimal */ + if (csa->rpi < 1e-8 && csa->rdi < 1e-8 && csa->gap < 1e-8) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("OPTIMAL SOLUTION FOUND\n"); + status = 0; + break; + } + /* check if the problem has no feasible solution */ + temp = 1e5 * csa->phi_min[csa->iter]; + if (temp < 1e-8) temp = 1e-8; + if (csa->phi >= temp) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS NO FEASIBLE PRIMAL/DUAL SOLUTION\n") + ; + status = 1; + break; + } + /* check for very slow convergence or divergence */ + if (((csa->rpi >= 1e-8 || csa->rdi >= 1e-8) && csa->rmu / + csa->rmu0 >= 1e6) || + (csa->iter >= 30 && csa->phi_min[csa->iter] >= 0.5 * + csa->phi_min[csa->iter - 30])) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("NO CONVERGENCE; SEARCH TERMINATED\n"); + status = 2; + break; + } + /* check for maximal number of iterations */ + if (csa->iter == ITER_MAX) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED\n"); + status = 3; + break; + } + /* start the next iteration */ + csa->iter++; + /* factorize normal equation system */ + for (j = 1; j <= n; j++) csa->D[j] = csa->x[j] / csa->z[j]; + decomp_NE(csa); + /* compute the next point using Mehrotra's predictor-corrector + technique */ + if (make_step(csa)) + { if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("NUMERIC INSTABILITY; SEARCH TERMINATED\n"); + status = 4; + break; + } + } + /* restore the best point */ + if (status != 0) + { for (j = 1; j <= n; j++) csa->x[j] = csa->best_x[j]; + for (i = 1; i <= m; i++) csa->y[i] = csa->best_y[i]; + for (j = 1; j <= n; j++) csa->z[j] = csa->best_z[j]; + if (csa->parm->msg_lev >= GLP_MSG_ALL) + xprintf("Best point %17.9e was reached on iteration %d\n", + csa->best_obj, csa->best_iter); + } + /* return to the calling program */ + return status; +} + +/*********************************************************************** +* NAME +* +* ipm_solve - core LP solver based on the interior-point method +* +* SYNOPSIS +* +* #include "glpipm.h" +* int ipm_solve(glp_prob *P, const glp_iptcp *parm); +* +* DESCRIPTION +* +* The routine ipm_solve is a core LP solver based on the primal-dual +* interior-point method. +* +* The routine assumes the following standard formulation of LP problem +* to be solved: +* +* minimize +* +* F = c[0] + c[1]*x[1] + c[2]*x[2] + ... + c[n]*x[n] +* +* subject to linear constraints +* +* a[1,1]*x[1] + a[1,2]*x[2] + ... + a[1,n]*x[n] = b[1] +* +* a[2,1]*x[1] + a[2,2]*x[2] + ... + a[2,n]*x[n] = b[2] +* +* . . . . . . +* +* a[m,1]*x[1] + a[m,2]*x[2] + ... + a[m,n]*x[n] = b[m] +* +* and non-negative variables +* +* x[1] >= 0, x[2] >= 0, ..., x[n] >= 0 +* +* where: +* F is the objective function; +* x[1], ..., x[n] are (structural) variables; +* c[0] is a constant term of the objective function; +* c[1], ..., c[n] are objective coefficients; +* a[1,1], ..., a[m,n] are constraint coefficients; +* b[1], ..., b[n] are right-hand sides. +* +* The solution is three vectors x, y, and z, which are stored by the +* routine in the arrays x, y, and z, respectively. These vectors +* correspond to the best primal-dual point found during optimization. +* They are approximate solution of the following system (which is the +* Karush-Kuhn-Tucker optimality conditions): +* +* A*x = b (primal feasibility condition) +* +* A'*y + z = c (dual feasibility condition) +* +* x'*z = 0 (primal-dual complementarity condition) +* +* x >= 0, z >= 0 (non-negativity condition) +* +* where: +* x[1], ..., x[n] are primal (structural) variables; +* y[1], ..., y[m] are dual variables (Lagrange multipliers) for +* equality constraints; +* z[1], ..., z[n] are dual variables (Lagrange multipliers) for +* non-negativity constraints. +* +* RETURNS +* +* 0 LP has been successfully solved. +* +* GLP_ENOCVG +* No convergence. +* +* GLP_EITLIM +* Iteration limit exceeded. +* +* GLP_EINSTAB +* Numeric instability on solving Newtonian system. +* +* In case of non-zero return code the routine returns the best point, +* which has been reached during optimization. */ + +int ipm_solve(glp_prob *P, const glp_iptcp *parm) +{ struct csa _dsa, *csa = &_dsa; + int m = P->m; + int n = P->n; + int nnz = P->nnz; + GLPROW *row; + GLPCOL *col; + GLPAIJ *aij; + int i, j, loc, ret, *A_ind, *A_ptr; + double dir, *A_val, *b, *c, *x, *y, *z; + xassert(m > 0); + xassert(n > 0); + /* allocate working arrays */ + A_ptr = xcalloc(1+m+1, sizeof(int)); + A_ind = xcalloc(1+nnz, sizeof(int)); + A_val = xcalloc(1+nnz, sizeof(double)); + b = xcalloc(1+m, sizeof(double)); + c = xcalloc(1+n, sizeof(double)); + x = xcalloc(1+n, sizeof(double)); + y = xcalloc(1+m, sizeof(double)); + z = xcalloc(1+n, sizeof(double)); + /* prepare rows and constraint coefficients */ + loc = 1; + for (i = 1; i <= m; i++) + { row = P->row[i]; + xassert(row->type == GLP_FX); + b[i] = row->lb * row->rii; + A_ptr[i] = loc; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { A_ind[loc] = aij->col->j; + A_val[loc] = row->rii * aij->val * aij->col->sjj; + loc++; + } + } + A_ptr[m+1] = loc; + xassert(loc-1 == nnz); + /* prepare columns and objective coefficients */ + if (P->dir == GLP_MIN) + dir = +1.0; + else if (P->dir == GLP_MAX) + dir = -1.0; + else + xassert(P != P); + c[0] = dir * P->c0; + for (j = 1; j <= n; j++) + { col = P->col[j]; + xassert(col->type == GLP_LO && col->lb == 0.0); + c[j] = dir * col->coef * col->sjj; + } + /* allocate and initialize the common storage area */ + csa->m = m; + csa->n = n; + csa->A_ptr = A_ptr; + csa->A_ind = A_ind; + csa->A_val = A_val; + csa->b = b; + csa->c = c; + csa->x = x; + csa->y = y; + csa->z = z; + csa->parm = parm; + initialize(csa); + /* solve LP with the interior-point method */ + ret = ipm_main(csa); + /* deallocate the common storage area */ + terminate(csa); + /* determine solution status */ + if (ret == 0) + { /* optimal solution found */ + P->ipt_stat = GLP_OPT; + ret = 0; + } + else if (ret == 1) + { /* problem has no feasible (primal or dual) solution */ + P->ipt_stat = GLP_NOFEAS; + ret = 0; + } + else if (ret == 2) + { /* no convergence */ + P->ipt_stat = GLP_INFEAS; + ret = GLP_ENOCVG; + } + else if (ret == 3) + { /* iteration limit exceeded */ + P->ipt_stat = GLP_INFEAS; + ret = GLP_EITLIM; + } + else if (ret == 4) + { /* numeric instability on solving Newtonian system */ + P->ipt_stat = GLP_INFEAS; + ret = GLP_EINSTAB; + } + else + xassert(ret != ret); + /* store row solution components */ + for (i = 1; i <= m; i++) + { row = P->row[i]; + row->pval = row->lb; + row->dval = dir * y[i] * row->rii; + } + /* store column solution components */ + P->ipt_obj = P->c0; + for (j = 1; j <= n; j++) + { col = P->col[j]; + col->pval = x[j] * col->sjj; + col->dval = dir * z[j] / col->sjj; + P->ipt_obj += col->coef * col->pval; + } + /* free working arrays */ + xfree(A_ptr); + xfree(A_ind); + xfree(A_val); + xfree(b); + xfree(c); + xfree(x); + xfree(y); + xfree(z); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpipm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpipm.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,36 @@ +/* glpipm.h (primal-dual interior-point method) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPIPM_H +#define GLPIPM_H + +#include "glpapi.h" + +#define ipm_solve _glp_ipm_solve +int ipm_solve(glp_prob *P, const glp_iptcp *parm); +/* core LP solver based on the interior-point method */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplib.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,135 @@ +/* glplib.h (miscellaneous library routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPLIB_H +#define GLPLIB_H + +#define bigmul _glp_lib_bigmul +void bigmul(int n, int m, unsigned short x[], unsigned short y[]); +/* multiply unsigned integer numbers of arbitrary precision */ + +#define bigdiv _glp_lib_bigdiv +void bigdiv(int n, int m, unsigned short x[], unsigned short y[]); +/* divide unsigned integer numbers of arbitrary precision */ + +#ifndef GLP_LONG_DEFINED +#define GLP_LONG_DEFINED +typedef struct { int lo, hi; } glp_long; +/* long integer data type */ +#endif + +typedef struct { glp_long quot, rem; } glp_ldiv; +/* result of long integer division */ + +#define xlset _glp_lib_xlset +glp_long xlset(int x); +/* expand integer to long integer */ + +#define xlneg _glp_lib_xlneg +glp_long xlneg(glp_long x); +/* negate long integer */ + +#define xladd _glp_lib_xladd +glp_long xladd(glp_long x, glp_long y); +/* add long integers */ + +#define xlsub _glp_lib_xlsub +glp_long xlsub(glp_long x, glp_long y); +/* subtract long integers */ + +#define xlcmp _glp_lib_xlcmp +int xlcmp(glp_long x, glp_long y); +/* compare long integers */ + +#define xlmul _glp_lib_xlmul +glp_long xlmul(glp_long x, glp_long y); +/* multiply long integers */ + +#define xldiv _glp_lib_xldiv +glp_ldiv xldiv(glp_long x, glp_long y); +/* divide long integers */ + +#define xltod _glp_lib_xltod +double xltod(glp_long x); +/* convert long integer to double */ + +#define xltoa _glp_lib_xltoa +char *xltoa(glp_long x, char *s); +/* convert long integer to character string */ + +#define str2int _glp_lib_str2int +int str2int(const char *str, int *val); +/* convert character string to value of int type */ + +#define str2num _glp_lib_str2num +int str2num(const char *str, double *val); +/* convert character string to value of double type */ + +#define strspx _glp_lib_strspx +char *strspx(char *str); +/* remove all spaces from character string */ + +#define strtrim _glp_lib_strtrim +char *strtrim(char *str); +/* remove trailing spaces from character string */ + +#define strrev _glp_lib_strrev +char *strrev(char *s); +/* reverse character string */ + +#define gcd _glp_lib_gcd +int gcd(int x, int y); +/* find greatest common divisor of two integers */ + +#define gcdn _glp_lib_gcdn +int gcdn(int n, int x[]); +/* find greatest common divisor of n integers */ + +#define lcm _glp_lib_lcm +int lcm(int x, int y); +/* find least common multiple of two integers */ + +#define lcmn _glp_lib_lcmn +int lcmn(int n, int x[]); +/* find least common multiple of n integers */ + +#define round2n _glp_lib_round2n +double round2n(double x); +/* round floating-point number to nearest power of two */ + +#define fp2rat _glp_lib_fp2rat +int fp2rat(double x, double eps, double *p, double *q); +/* convert floating-point number to rational number */ + +#define jday _glp_lib_jday +int jday(int d, int m, int y); +/* convert calendar date to Julian day number */ + +#define jdate _glp_lib_jdate +int jdate(int j, int *d, int *m, int *y); +/* convert Julian day number to calendar date */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplib01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplib01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,287 @@ +/* glplib01.c (bignum arithmetic) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glplib.h" + +/*********************************************************************** +* Two routines below are intended to multiply and divide unsigned +* integer numbers of arbitrary precision. +* +* The routines assume that an unsigned integer number is represented in +* the positional numeral system with the base 2^16 = 65536, i.e. each +* "digit" of the number is in the range [0, 65535] and represented as +* a 16-bit value of the unsigned short type. In other words, a number x +* has the following representation: +* +* n-1 +* x = sum d[j] * 65536^j, +* j=0 +* +* where n is the number of places (positions), and d[j] is j-th "digit" +* of x, 0 <= d[j] <= 65535. +***********************************************************************/ + +/*********************************************************************** +* NAME +* +* bigmul - multiply unsigned integer numbers of arbitrary precision +* +* SYNOPSIS +* +* #include "glplib.h" +* void bigmul(int n, int m, unsigned short x[], unsigned short y[]); +* +* DESCRIPTION +* +* The routine bigmul multiplies unsigned integer numbers of arbitrary +* precision. +* +* n is the number of digits of multiplicand, n >= 1; +* +* m is the number of digits of multiplier, m >= 1; +* +* x is an array containing digits of the multiplicand in elements +* x[m], x[m+1], ..., x[n+m-1]. Contents of x[0], x[1], ..., x[m-1] are +* ignored on entry. +* +* y is an array containing digits of the multiplier in elements y[0], +* y[1], ..., y[m-1]. +* +* On exit digits of the product are stored in elements x[0], x[1], ..., +* x[n+m-1]. The array y is not changed. */ + +void bigmul(int n, int m, unsigned short x[], unsigned short y[]) +{ int i, j; + unsigned int t; + xassert(n >= 1); + xassert(m >= 1); + for (j = 0; j < m; j++) x[j] = 0; + for (i = 0; i < n; i++) + { if (x[i+m]) + { t = 0; + for (j = 0; j < m; j++) + { t += (unsigned int)x[i+m] * (unsigned int)y[j] + + (unsigned int)x[i+j]; + x[i+j] = (unsigned short)t; + t >>= 16; + } + x[i+m] = (unsigned short)t; + } + } + return; +} + +/*********************************************************************** +* NAME +* +* bigdiv - divide unsigned integer numbers of arbitrary precision +* +* SYNOPSIS +* +* #include "glplib.h" +* void bigdiv(int n, int m, unsigned short x[], unsigned short y[]); +* +* DESCRIPTION +* +* The routine bigdiv divides one unsigned integer number of arbitrary +* precision by another with the algorithm described in [1]. +* +* n is the difference between the number of digits of dividend and the +* number of digits of divisor, n >= 0. +* +* m is the number of digits of divisor, m >= 1. +* +* x is an array containing digits of the dividend in elements x[0], +* x[1], ..., x[n+m-1]. +* +* y is an array containing digits of the divisor in elements y[0], +* y[1], ..., y[m-1]. The highest digit y[m-1] must be non-zero. +* +* On exit n+1 digits of the quotient are stored in elements x[m], +* x[m+1], ..., x[n+m], and m digits of the remainder are stored in +* elements x[0], x[1], ..., x[m-1]. The array y is changed but then +* restored. +* +* REFERENCES +* +* 1. D. Knuth. The Art of Computer Programming. Vol. 2: Seminumerical +* Algorithms. Stanford University, 1969. */ + +void bigdiv(int n, int m, unsigned short x[], unsigned short y[]) +{ int i, j; + unsigned int t; + unsigned short d, q, r; + xassert(n >= 0); + xassert(m >= 1); + xassert(y[m-1] != 0); + /* special case when divisor has the only digit */ + if (m == 1) + { d = 0; + for (i = n; i >= 0; i--) + { t = ((unsigned int)d << 16) + (unsigned int)x[i]; + x[i+1] = (unsigned short)(t / y[0]); + d = (unsigned short)(t % y[0]); + } + x[0] = d; + goto done; + } + /* multiply dividend and divisor by a normalizing coefficient in + order to provide the condition y[m-1] >= base / 2 */ + d = (unsigned short)(0x10000 / ((unsigned int)y[m-1] + 1)); + if (d == 1) + x[n+m] = 0; + else + { t = 0; + for (i = 0; i < n+m; i++) + { t += (unsigned int)x[i] * (unsigned int)d; + x[i] = (unsigned short)t; + t >>= 16; + } + x[n+m] = (unsigned short)t; + t = 0; + for (j = 0; j < m; j++) + { t += (unsigned int)y[j] * (unsigned int)d; + y[j] = (unsigned short)t; + t >>= 16; + } + } + /* main loop */ + for (i = n; i >= 0; i--) + { /* estimate and correct the current digit of quotient */ + if (x[i+m] < y[m-1]) + { t = ((unsigned int)x[i+m] << 16) + (unsigned int)x[i+m-1]; + q = (unsigned short)(t / (unsigned int)y[m-1]); + r = (unsigned short)(t % (unsigned int)y[m-1]); + if (q == 0) goto putq; else goto test; + } + q = 0; + r = x[i+m-1]; +decr: q--; /* if q = 0 then q-- = 0xFFFF */ + t = (unsigned int)r + (unsigned int)y[m-1]; + r = (unsigned short)t; + if (t > 0xFFFF) goto msub; +test: t = (unsigned int)y[m-2] * (unsigned int)q; + if ((unsigned short)(t >> 16) > r) goto decr; + if ((unsigned short)(t >> 16) < r) goto msub; + if ((unsigned short)t > x[i+m-2]) goto decr; +msub: /* now subtract divisor multiplied by the current digit of + quotient from the current dividend */ + if (q == 0) goto putq; + t = 0; + for (j = 0; j < m; j++) + { t += (unsigned int)y[j] * (unsigned int)q; + if (x[i+j] < (unsigned short)t) t += 0x10000; + x[i+j] -= (unsigned short)t; + t >>= 16; + } + if (x[i+m] >= (unsigned short)t) goto putq; + /* perform correcting addition, because the current digit of + quotient is greater by one than its correct value */ + q--; + t = 0; + for (j = 0; j < m; j++) + { t += (unsigned int)x[i+j] + (unsigned int)y[j]; + x[i+j] = (unsigned short)t; + t >>= 16; + } +putq: /* store the current digit of quotient */ + x[i+m] = q; + } + /* divide divisor and remainder by the normalizing coefficient in + order to restore their original values */ + if (d > 1) + { t = 0; + for (i = m-1; i >= 0; i--) + { t = (t << 16) + (unsigned int)x[i]; + x[i] = (unsigned short)(t / (unsigned int)d); + t %= (unsigned int)d; + } + t = 0; + for (j = m-1; j >= 0; j--) + { t = (t << 16) + (unsigned int)y[j]; + y[j] = (unsigned short)(t / (unsigned int)d); + t %= (unsigned int)d; + } + } +done: return; +} + +/**********************************************************************/ + +#if 0 +#include +#include +#include +#include "glprng.h" + +#define N_MAX 7 +/* maximal number of digits in multiplicand */ + +#define M_MAX 5 +/* maximal number of digits in multiplier */ + +#define N_TEST 1000000 +/* number of tests */ + +int main(void) +{ RNG *rand; + int d, j, n, m, test; + unsigned short x[N_MAX], y[M_MAX], z[N_MAX+M_MAX]; + rand = rng_create_rand(); + for (test = 1; test <= N_TEST; test++) + { /* x[0,...,n-1] := multiplicand */ + n = 1 + rng_unif_rand(rand, N_MAX-1); + assert(1 <= n && n <= N_MAX); + for (j = 0; j < n; j++) + { d = rng_unif_rand(rand, 65536); + assert(0 <= d && d <= 65535); + x[j] = (unsigned short)d; + } + /* y[0,...,m-1] := multiplier */ + m = 1 + rng_unif_rand(rand, M_MAX-1); + assert(1 <= m && m <= M_MAX); + for (j = 0; j < m; j++) + { d = rng_unif_rand(rand, 65536); + assert(0 <= d && d <= 65535); + y[j] = (unsigned short)d; + } + if (y[m-1] == 0) y[m-1] = 1; + /* z[0,...,n+m-1] := x * y */ + for (j = 0; j < n; j++) z[m+j] = x[j]; + bigmul(n, m, z, y); + /* z[0,...,m-1] := z mod y, z[m,...,n+m-1] := z div y */ + bigdiv(n, m, z, y); + /* z mod y must be 0 */ + for (j = 0; j < m; j++) assert(z[j] == 0); + /* z div y must be x */ + for (j = 0; j < n; j++) assert(z[m+j] == x[j]); + } + fprintf(stderr, "%d tests successfully passed\n", N_TEST); + rng_delete_rand(rand); + return 0; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplib02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplib02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,335 @@ +/* glplib02.c (64-bit arithmetic) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glplib.h" + +/*********************************************************************** +* NAME +* +* xlset - expand integer to long integer +* +* SYNOPSIS +* +* #include "glplib.h" +* glp_long xlset(int x); +* +* RETURNS +* +* The routine xlset returns x expanded to long integer. */ + +glp_long xlset(int x) +{ glp_long t; + t.lo = x, t.hi = (x >= 0 ? 0 : -1); + return t; +} + +/*********************************************************************** +* NAME +* +* xlneg - negate long integer +* +* SYNOPSIS +* +* #include "glplib.h" +* glp_long xlneg(glp_long x); +* +* RETURNS +* +* The routine xlneg returns the difference 0 - x. */ + +glp_long xlneg(glp_long x) +{ if (x.lo) + x.lo = - x.lo, x.hi = ~x.hi; + else + x.hi = - x.hi; + return x; +} + +/*********************************************************************** +* NAME +* +* xladd - add long integers +* +* SYNOPSIS +* +* #include "glplib.h" +* glp_long xladd(glp_long x, glp_long y); +* +* RETURNS +* +* The routine xladd returns the sum x + y. */ + +glp_long xladd(glp_long x, glp_long y) +{ if ((unsigned int)x.lo <= 0xFFFFFFFF - (unsigned int)y.lo) + x.lo += y.lo, x.hi += y.hi; + else + x.lo += y.lo, x.hi += y.hi + 1; + return x; +} + +/*********************************************************************** +* NAME +* +* xlsub - subtract long integers +* +* SYNOPSIS +* +* #include "glplib.h" +* glp_long xlsub(glp_long x, glp_long y); +* +* RETURNS +* +* The routine xlsub returns the difference x - y. */ + +glp_long xlsub(glp_long x, glp_long y) +{ return + xladd(x, xlneg(y)); +} + +/*********************************************************************** +* NAME +* +* xlcmp - compare long integers +* +* SYNOPSIS +* +* #include "glplib.h" +* int xlcmp(glp_long x, glp_long y); +* +* RETURNS +* +* The routine xlcmp returns the sign of the difference x - y. */ + +int xlcmp(glp_long x, glp_long y) +{ if (x.hi >= 0 && y.hi < 0) return +1; + if (x.hi < 0 && y.hi >= 0) return -1; + if ((unsigned int)x.hi < (unsigned int)y.hi) return -1; + if ((unsigned int)x.hi > (unsigned int)y.hi) return +1; + if ((unsigned int)x.lo < (unsigned int)y.lo) return -1; + if ((unsigned int)x.lo > (unsigned int)y.lo) return +1; + return 0; +} + +/*********************************************************************** +* NAME +* +* xlmul - multiply long integers +* +* SYNOPSIS +* +* #include "glplib.h" +* glp_long xlmul(glp_long x, glp_long y); +* +* RETURNS +* +* The routine xlmul returns the product x * y. */ + +glp_long xlmul(glp_long x, glp_long y) +{ unsigned short xx[8], yy[4]; + xx[4] = (unsigned short)x.lo; + xx[5] = (unsigned short)(x.lo >> 16); + xx[6] = (unsigned short)x.hi; + xx[7] = (unsigned short)(x.hi >> 16); + yy[0] = (unsigned short)y.lo; + yy[1] = (unsigned short)(y.lo >> 16); + yy[2] = (unsigned short)y.hi; + yy[3] = (unsigned short)(y.hi >> 16); + bigmul(4, 4, xx, yy); + x.lo = (unsigned int)xx[0] | ((unsigned int)xx[1] << 16); + x.hi = (unsigned int)xx[2] | ((unsigned int)xx[3] << 16); + return x; +} + +/*********************************************************************** +* NAME +* +* xldiv - divide long integers +* +* SYNOPSIS +* +* #include "glplib.h" +* glp_ldiv xldiv(glp_long x, glp_long y); +* +* RETURNS +* +* The routine xldiv returns a structure of type glp_ldiv containing +* members quot (the quotient) and rem (the remainder), both of type +* glp_long. */ + +glp_ldiv xldiv(glp_long x, glp_long y) +{ glp_ldiv t; + int m, sx, sy; + unsigned short xx[8], yy[4]; + /* sx := sign(x) */ + sx = (x.hi < 0); + /* sy := sign(y) */ + sy = (y.hi < 0); + /* x := |x| */ + if (sx) x = xlneg(x); + /* y := |y| */ + if (sy) y = xlneg(y); + /* compute x div y and x mod y */ + xx[0] = (unsigned short)x.lo; + xx[1] = (unsigned short)(x.lo >> 16); + xx[2] = (unsigned short)x.hi; + xx[3] = (unsigned short)(x.hi >> 16); + yy[0] = (unsigned short)y.lo; + yy[1] = (unsigned short)(y.lo >> 16); + yy[2] = (unsigned short)y.hi; + yy[3] = (unsigned short)(y.hi >> 16); + if (yy[3]) + m = 4; + else if (yy[2]) + m = 3; + else if (yy[1]) + m = 2; + else if (yy[0]) + m = 1; + else + xerror("xldiv: divide by zero\n"); + bigdiv(4 - m, m, xx, yy); + /* remainder in x[0], x[1], ..., x[m-1] */ + t.rem.lo = (unsigned int)xx[0], t.rem.hi = 0; + if (m >= 2) t.rem.lo |= (unsigned int)xx[1] << 16; + if (m >= 3) t.rem.hi = (unsigned int)xx[2]; + if (m >= 4) t.rem.hi |= (unsigned int)xx[3] << 16; + if (sx) t.rem = xlneg(t.rem); + /* quotient in x[m], x[m+1], ..., x[4] */ + t.quot.lo = (unsigned int)xx[m], t.quot.hi = 0; + if (m <= 3) t.quot.lo |= (unsigned int)xx[m+1] << 16; + if (m <= 2) t.quot.hi = (unsigned int)xx[m+2]; + if (m <= 1) t.quot.hi |= (unsigned int)xx[m+3] << 16; + if (sx ^ sy) t.quot = xlneg(t.quot); + return t; +} + +/*********************************************************************** +* NAME +* +* xltod - convert long integer to double +* +* SYNOPSIS +* +* #include "glplib.h" +* double xltod(glp_long x); +* +* RETURNS +* +* The routine xltod returns x converted to double. */ + +double xltod(glp_long x) +{ double s, z; + if (x.hi >= 0) + s = +1.0; + else + s = -1.0, x = xlneg(x); + if (x.hi >= 0) + z = 4294967296.0 * (double)x.hi + (double)(unsigned int)x.lo; + else + { xassert(x.hi == 0x80000000 && x.lo == 0x00000000); + z = 9223372036854775808.0; /* 2^63 */ + } + return s * z; +} + +char *xltoa(glp_long x, char *s) +{ /* convert long integer to character string */ + static const char *d = "0123456789"; + glp_ldiv t; + int neg, len; + if (x.hi >= 0) + neg = 0; + else + neg = 1, x = xlneg(x); + if (x.hi >= 0) + { len = 0; + while (!(x.hi == 0 && x.lo == 0)) + { t = xldiv(x, xlset(10)); + xassert(0 <= t.rem.lo && t.rem.lo <= 9); + s[len++] = d[t.rem.lo]; + x = t.quot; + } + if (len == 0) s[len++] = d[0]; + if (neg) s[len++] = '-'; + s[len] = '\0'; + strrev(s); + } + else + strcpy(s, "-9223372036854775808"); /* -2^63 */ + return s; +} + +/**********************************************************************/ + +#if 0 +#include "glprng.h" + +#define N_TEST 1000000 +/* number of tests */ + +static glp_long myrand(RNG *rand) +{ glp_long x; + int k; + k = rng_unif_rand(rand, 4); + xassert(0 <= k && k <= 3); + x.lo = rng_unif_rand(rand, 65536); + if (k == 1 || k == 3) + { x.lo <<= 16; + x.lo += rng_unif_rand(rand, 65536); + } + if (k <= 1) + x.hi = 0; + else + x.hi = rng_unif_rand(rand, 65536); + if (k == 3) + { x.hi <<= 16; + x.hi += rng_unif_rand(rand, 65536); + } + if (rng_unif_rand(rand, 2)) x = xlneg(x); + return x; +} + +int main(void) +{ RNG *rand; + glp_long x, y; + glp_ldiv z; + int test; + rand = rng_create_rand(); + for (test = 1; test <= N_TEST; test++) + { x = myrand(rand); + y = myrand(rand); + if (y.lo == 0 && y.hi == 0) y.lo = 1; + /* z.quot := x div y, z.rem := x mod y */ + z = xldiv(x, y); + /* x must be equal to y * z.quot + z.rem */ + xassert(xlcmp(x, xladd(xlmul(y, z.quot), z.rem)) == 0); + } + xprintf("%d tests successfully passed\n", N_TEST); + rng_delete_rand(rand); + return 0; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplib03.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplib03.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,692 @@ +/* glplib03.c (miscellaneous library routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glplib.h" + +/*********************************************************************** +* NAME +* +* str2int - convert character string to value of int type +* +* SYNOPSIS +* +* #include "glplib.h" +* int str2int(const char *str, int *val); +* +* DESCRIPTION +* +* The routine str2int converts the character string str to a value of +* integer type and stores the value into location, which the parameter +* val points to (in the case of error content of this location is not +* changed). +* +* RETURNS +* +* The routine returns one of the following error codes: +* +* 0 - no error; +* 1 - value out of range; +* 2 - character string is syntactically incorrect. */ + +int str2int(const char *str, int *_val) +{ int d, k, s, val = 0; + /* scan optional sign */ + if (str[0] == '+') + s = +1, k = 1; + else if (str[0] == '-') + s = -1, k = 1; + else + s = +1, k = 0; + /* check for the first digit */ + if (!isdigit((unsigned char)str[k])) return 2; + /* scan digits */ + while (isdigit((unsigned char)str[k])) + { d = str[k++] - '0'; + if (s > 0) + { if (val > INT_MAX / 10) return 1; + val *= 10; + if (val > INT_MAX - d) return 1; + val += d; + } + else + { if (val < INT_MIN / 10) return 1; + val *= 10; + if (val < INT_MIN + d) return 1; + val -= d; + } + } + /* check for terminator */ + if (str[k] != '\0') return 2; + /* conversion has been done */ + *_val = val; + return 0; +} + +/*********************************************************************** +* NAME +* +* str2num - convert character string to value of double type +* +* SYNOPSIS +* +* #include "glplib.h" +* int str2num(const char *str, double *val); +* +* DESCRIPTION +* +* The routine str2num converts the character string str to a value of +* double type and stores the value into location, which the parameter +* val points to (in the case of error content of this location is not +* changed). +* +* RETURNS +* +* The routine returns one of the following error codes: +* +* 0 - no error; +* 1 - value out of range; +* 2 - character string is syntactically incorrect. */ + +int str2num(const char *str, double *_val) +{ int k; + double val; + /* scan optional sign */ + k = (str[0] == '+' || str[0] == '-' ? 1 : 0); + /* check for decimal point */ + if (str[k] == '.') + { k++; + /* a digit should follow it */ + if (!isdigit((unsigned char)str[k])) return 2; + k++; + goto frac; + } + /* integer part should start with a digit */ + if (!isdigit((unsigned char)str[k])) return 2; + /* scan integer part */ + while (isdigit((unsigned char)str[k])) k++; + /* check for decimal point */ + if (str[k] == '.') k++; +frac: /* scan optional fraction part */ + while (isdigit((unsigned char)str[k])) k++; + /* check for decimal exponent */ + if (str[k] == 'E' || str[k] == 'e') + { k++; + /* scan optional sign */ + if (str[k] == '+' || str[k] == '-') k++; + /* a digit should follow E, E+ or E- */ + if (!isdigit((unsigned char)str[k])) return 2; + } + /* scan optional exponent part */ + while (isdigit((unsigned char)str[k])) k++; + /* check for terminator */ + if (str[k] != '\0') return 2; + /* perform conversion */ + { char *endptr; + val = strtod(str, &endptr); + if (*endptr != '\0') return 2; + } + /* check for overflow */ + if (!(-DBL_MAX <= val && val <= +DBL_MAX)) return 1; + /* check for underflow */ + if (-DBL_MIN < val && val < +DBL_MIN) val = 0.0; + /* conversion has been done */ + *_val = val; + return 0; +} + +/*********************************************************************** +* NAME +* +* strspx - remove all spaces from character string +* +* SYNOPSIS +* +* #include "glplib.h" +* char *strspx(char *str); +* +* DESCRIPTION +* +* The routine strspx removes all spaces from the character string str. +* +* RETURNS +* +* The routine returns a pointer to the character string. +* +* EXAMPLES +* +* strspx(" Errare humanum est ") => "Errarehumanumest" +* +* strspx(" ") => "" */ + +char *strspx(char *str) +{ char *s, *t; + for (s = t = str; *s; s++) if (*s != ' ') *t++ = *s; + *t = '\0'; + return str; +} + +/*********************************************************************** +* NAME +* +* strtrim - remove trailing spaces from character string +* +* SYNOPSIS +* +* #include "glplib.h" +* char *strtrim(char *str); +* +* DESCRIPTION +* +* The routine strtrim removes trailing spaces from the character +* string str. +* +* RETURNS +* +* The routine returns a pointer to the character string. +* +* EXAMPLES +* +* strtrim("Errare humanum est ") => "Errare humanum est" +* +* strtrim(" ") => "" */ + +char *strtrim(char *str) +{ char *t; + for (t = strrchr(str, '\0') - 1; t >= str; t--) + { if (*t != ' ') break; + *t = '\0'; + } + return str; +} + +/*********************************************************************** +* NAME +* +* strrev - reverse character string +* +* SYNOPSIS +* +* #include "glplib.h" +* char *strrev(char *s); +* +* DESCRIPTION +* +* The routine strrev changes characters in a character string s to the +* reverse order, except the terminating null character. +* +* RETURNS +* +* The routine returns the pointer s. +* +* EXAMPLES +* +* strrev("") => "" +* +* strrev("Today is Monday") => "yadnoM si yadoT" */ + +char *strrev(char *s) +{ int i, j; + char t; + for (i = 0, j = strlen(s)-1; i < j; i++, j--) + t = s[i], s[i] = s[j], s[j] = t; + return s; +} + +/*********************************************************************** +* NAME +* +* gcd - find greatest common divisor of two integers +* +* SYNOPSIS +* +* #include "glplib.h" +* int gcd(int x, int y); +* +* RETURNS +* +* The routine gcd returns gcd(x, y), the greatest common divisor of +* the two positive integers given. +* +* ALGORITHM +* +* The routine gcd is based on Euclid's algorithm. +* +* REFERENCES +* +* Don Knuth, The Art of Computer Programming, Vol.2: Seminumerical +* Algorithms, 3rd Edition, Addison-Wesley, 1997. Section 4.5.2: The +* Greatest Common Divisor, pp. 333-56. */ + +int gcd(int x, int y) +{ int r; + xassert(x > 0 && y > 0); + while (y > 0) + r = x % y, x = y, y = r; + return x; +} + +/*********************************************************************** +* NAME +* +* gcdn - find greatest common divisor of n integers +* +* SYNOPSIS +* +* #include "glplib.h" +* int gcdn(int n, int x[]); +* +* RETURNS +* +* The routine gcdn returns gcd(x[1], x[2], ..., x[n]), the greatest +* common divisor of n positive integers given, n > 0. +* +* BACKGROUND +* +* The routine gcdn is based on the following identity: +* +* gcd(x, y, z) = gcd(gcd(x, y), z). +* +* REFERENCES +* +* Don Knuth, The Art of Computer Programming, Vol.2: Seminumerical +* Algorithms, 3rd Edition, Addison-Wesley, 1997. Section 4.5.2: The +* Greatest Common Divisor, pp. 333-56. */ + +int gcdn(int n, int x[]) +{ int d, j; + xassert(n > 0); + for (j = 1; j <= n; j++) + { xassert(x[j] > 0); + if (j == 1) + d = x[1]; + else + d = gcd(d, x[j]); + if (d == 1) break; + } + return d; +} + +/*********************************************************************** +* NAME +* +* lcm - find least common multiple of two integers +* +* SYNOPSIS +* +* #include "glplib.h" +* int lcm(int x, int y); +* +* RETURNS +* +* The routine lcm returns lcm(x, y), the least common multiple of the +* two positive integers given. In case of integer overflow the routine +* returns zero. +* +* BACKGROUND +* +* The routine lcm is based on the following identity: +* +* lcm(x, y) = (x * y) / gcd(x, y) = x * [y / gcd(x, y)], +* +* where gcd(x, y) is the greatest common divisor of x and y. */ + +int lcm(int x, int y) +{ xassert(x > 0); + xassert(y > 0); + y /= gcd(x, y); + if (x > INT_MAX / y) return 0; + return x * y; +} + +/*********************************************************************** +* NAME +* +* lcmn - find least common multiple of n integers +* +* SYNOPSIS +* +* #include "glplib.h" +* int lcmn(int n, int x[]); +* +* RETURNS +* +* The routine lcmn returns lcm(x[1], x[2], ..., x[n]), the least +* common multiple of n positive integers given, n > 0. In case of +* integer overflow the routine returns zero. +* +* BACKGROUND +* +* The routine lcmn is based on the following identity: +* +* lcmn(x, y, z) = lcm(lcm(x, y), z), +* +* where lcm(x, y) is the least common multiple of x and y. */ + +int lcmn(int n, int x[]) +{ int m, j; + xassert(n > 0); + for (j = 1; j <= n; j++) + { xassert(x[j] > 0); + if (j == 1) + m = x[1]; + else + m = lcm(m, x[j]); + if (m == 0) break; + } + return m; +} + +/*********************************************************************** +* NAME +* +* round2n - round floating-point number to nearest power of two +* +* SYNOPSIS +* +* #include "glplib.h" +* double round2n(double x); +* +* RETURNS +* +* Given a positive floating-point value x the routine round2n returns +* 2^n such that |x - 2^n| is minimal. +* +* EXAMPLES +* +* round2n(10.1) = 2^3 = 8 +* round2n(15.3) = 2^4 = 16 +* round2n(0.01) = 2^(-7) = 0.0078125 +* +* BACKGROUND +* +* Let x = f * 2^e, where 0.5 <= f < 1 is a normalized fractional part, +* e is an integer exponent. Then, obviously, 0.5 * 2^e <= x < 2^e, so +* if x - 0.5 * 2^e <= 2^e - x, we choose 0.5 * 2^e = 2^(e-1), and 2^e +* otherwise. The latter condition can be written as 2 * x <= 1.5 * 2^e +* or 2 * f * 2^e <= 1.5 * 2^e or, finally, f <= 0.75. */ + +double round2n(double x) +{ int e; + double f; + xassert(x > 0.0); + f = frexp(x, &e); + return ldexp(1.0, f <= 0.75 ? e-1 : e); +} + +/*********************************************************************** +* NAME +* +* fp2rat - convert floating-point number to rational number +* +* SYNOPSIS +* +* #include "glplib.h" +* int fp2rat(double x, double eps, double *p, double *q); +* +* DESCRIPTION +* +* Given a floating-point number 0 <= x < 1 the routine fp2rat finds +* its "best" rational approximation p / q, where p >= 0 and q > 0 are +* integer numbers, such that |x - p / q| <= eps. +* +* RETURNS +* +* The routine fp2rat returns the number of iterations used to achieve +* the specified precision eps. +* +* EXAMPLES +* +* For x = sqrt(2) - 1 = 0.414213562373095 and eps = 1e-6 the routine +* gives p = 408 and q = 985, where 408 / 985 = 0.414213197969543. +* +* BACKGROUND +* +* It is well known that every positive real number x can be expressed +* as the following continued fraction: +* +* x = b[0] + a[1] +* ------------------------ +* b[1] + a[2] +* ----------------- +* b[2] + a[3] +* ---------- +* b[3] + ... +* +* where: +* +* a[k] = 1, k = 0, 1, 2, ... +* +* b[k] = floor(x[k]), k = 0, 1, 2, ... +* +* x[0] = x, +* +* x[k] = 1 / frac(x[k-1]), k = 1, 2, 3, ... +* +* To find the "best" rational approximation of x the routine computes +* partial fractions f[k] by dropping after k terms as follows: +* +* f[k] = A[k] / B[k], +* +* where: +* +* A[-1] = 1, A[0] = b[0], B[-1] = 0, B[0] = 1, +* +* A[k] = b[k] * A[k-1] + a[k] * A[k-2], +* +* B[k] = b[k] * B[k-1] + a[k] * B[k-2]. +* +* Once the condition +* +* |x - f[k]| <= eps +* +* has been satisfied, the routine reports p = A[k] and q = B[k] as the +* final answer. +* +* In the table below here is some statistics obtained for one million +* random numbers uniformly distributed in the range [0, 1). +* +* eps max p mean p max q mean q max k mean k +* ------------------------------------------------------------- +* 1e-1 8 1.6 9 3.2 3 1.4 +* 1e-2 98 6.2 99 12.4 5 2.4 +* 1e-3 997 20.7 998 41.5 8 3.4 +* 1e-4 9959 66.6 9960 133.5 10 4.4 +* 1e-5 97403 211.7 97404 424.2 13 5.3 +* 1e-6 479669 669.9 479670 1342.9 15 6.3 +* 1e-7 1579030 2127.3 3962146 4257.8 16 7.3 +* 1e-8 26188823 6749.4 26188824 13503.4 19 8.2 +* +* REFERENCES +* +* W. B. Jones and W. J. Thron, "Continued Fractions: Analytic Theory +* and Applications," Encyclopedia on Mathematics and Its Applications, +* Addison-Wesley, 1980. */ + +int fp2rat(double x, double eps, double *p, double *q) +{ int k; + double xk, Akm1, Ak, Bkm1, Bk, ak, bk, fk, temp; + if (!(0.0 <= x && x < 1.0)) + xerror("fp2rat: x = %g; number out of range\n", x); + for (k = 0; ; k++) + { xassert(k <= 100); + if (k == 0) + { /* x[0] = x */ + xk = x; + /* A[-1] = 1 */ + Akm1 = 1.0; + /* A[0] = b[0] = floor(x[0]) = 0 */ + Ak = 0.0; + /* B[-1] = 0 */ + Bkm1 = 0.0; + /* B[0] = 1 */ + Bk = 1.0; + } + else + { /* x[k] = 1 / frac(x[k-1]) */ + temp = xk - floor(xk); + xassert(temp != 0.0); + xk = 1.0 / temp; + /* a[k] = 1 */ + ak = 1.0; + /* b[k] = floor(x[k]) */ + bk = floor(xk); + /* A[k] = b[k] * A[k-1] + a[k] * A[k-2] */ + temp = bk * Ak + ak * Akm1; + Akm1 = Ak, Ak = temp; + /* B[k] = b[k] * B[k-1] + a[k] * B[k-2] */ + temp = bk * Bk + ak * Bkm1; + Bkm1 = Bk, Bk = temp; + } + /* f[k] = A[k] / B[k] */ + fk = Ak / Bk; +#if 0 + print("%.*g / %.*g = %.*g", DBL_DIG, Ak, DBL_DIG, Bk, DBL_DIG, + fk); +#endif + if (fabs(x - fk) <= eps) break; + } + *p = Ak; + *q = Bk; + return k; +} + +/*********************************************************************** +* NAME +* +* jday - convert calendar date to Julian day number +* +* SYNOPSIS +* +* #include "glplib.h" +* int jday(int d, int m, int y); +* +* DESCRIPTION +* +* The routine jday converts a calendar date, Gregorian calendar, to +* corresponding Julian day number j. +* +* From the given day d, month m, and year y, the Julian day number j +* is computed without using tables. +* +* The routine is valid for 1 <= y <= 4000. +* +* RETURNS +* +* The routine jday returns the Julian day number, or negative value if +* the specified date is incorrect. +* +* REFERENCES +* +* R. G. Tantzen, Algorithm 199: conversions between calendar date and +* Julian day number, Communications of the ACM, vol. 6, no. 8, p. 444, +* Aug. 1963. */ + +int jday(int d, int m, int y) +{ int c, ya, j, dd; + if (!(1 <= d && d <= 31 && 1 <= m && m <= 12 && 1 <= y && + y <= 4000)) + { j = -1; + goto done; + } + if (m >= 3) m -= 3; else m += 9, y--; + c = y / 100; + ya = y - 100 * c; + j = (146097 * c) / 4 + (1461 * ya) / 4 + (153 * m + 2) / 5 + d + + 1721119; + jdate(j, &dd, NULL, NULL); + if (d != dd) j = -1; +done: return j; +} + +/*********************************************************************** +* NAME +* +* jdate - convert Julian day number to calendar date +* +* SYNOPSIS +* +* #include "glplib.h" +* void jdate(int j, int *d, int *m, int *y); +* +* DESCRIPTION +* +* The routine jdate converts a Julian day number j to corresponding +* calendar date, Gregorian calendar. +* +* The day d, month m, and year y are computed without using tables and +* stored in corresponding locations. +* +* The routine is valid for 1721426 <= j <= 3182395. +* +* RETURNS +* +* If the conversion is successful, the routine returns zero, otherwise +* non-zero. +* +* REFERENCES +* +* R. G. Tantzen, Algorithm 199: conversions between calendar date and +* Julian day number, Communications of the ACM, vol. 6, no. 8, p. 444, +* Aug. 1963. */ + +int jdate(int j, int *_d, int *_m, int *_y) +{ int d, m, y, ret = 0; + if (!(1721426 <= j && j <= 3182395)) + { ret = 1; + goto done; + } + j -= 1721119; + y = (4 * j - 1) / 146097; + j = (4 * j - 1) % 146097; + d = j / 4; + j = (4 * d + 3) / 1461; + d = (4 * d + 3) % 1461; + d = (d + 4) / 4; + m = (5 * d - 3) / 153; + d = (5 * d - 3) % 153; + d = (d + 5) / 5; + y = 100 * y + j; + if (m <= 9) m += 3; else m -= 9, y++; + if (_d != NULL) *_d = d; + if (_m != NULL) *_m = m; + if (_y != NULL) *_y = y; +done: return ret; +} + +#if 0 +int main(void) +{ int jbeg, jend, j, d, m, y; + jbeg = jday(1, 1, 1); + jend = jday(31, 12, 4000); + for (j = jbeg; j <= jend; j++) + { xassert(jdate(j, &d, &m, &y) == 0); + xassert(jday(d, m, y) == j); + } + xprintf("Routines jday and jdate work correctly.\n"); + return 0; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplpf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplpf.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,978 @@ +/* glplpf.c (LP basis factorization, Schur complement version) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glplpf.h" +#include "glpenv.h" +#define xfault xerror + +#define _GLPLPF_DEBUG 0 + +/* CAUTION: DO NOT CHANGE THE LIMIT BELOW */ + +#define M_MAX 100000000 /* = 100*10^6 */ +/* maximal order of the basis matrix */ + +/*********************************************************************** +* NAME +* +* lpf_create_it - create LP basis factorization +* +* SYNOPSIS +* +* #include "glplpf.h" +* LPF *lpf_create_it(void); +* +* DESCRIPTION +* +* The routine lpf_create_it creates a program object, which represents +* a factorization of LP basis. +* +* RETURNS +* +* The routine lpf_create_it returns a pointer to the object created. */ + +LPF *lpf_create_it(void) +{ LPF *lpf; +#if _GLPLPF_DEBUG + xprintf("lpf_create_it: warning: debug mode enabled\n"); +#endif + lpf = xmalloc(sizeof(LPF)); + lpf->valid = 0; + lpf->m0_max = lpf->m0 = 0; + lpf->luf = luf_create_it(); + lpf->m = 0; + lpf->B = NULL; + lpf->n_max = 50; + lpf->n = 0; + lpf->R_ptr = lpf->R_len = NULL; + lpf->S_ptr = lpf->S_len = NULL; + lpf->scf = NULL; + lpf->P_row = lpf->P_col = NULL; + lpf->Q_row = lpf->Q_col = NULL; + lpf->v_size = 1000; + lpf->v_ptr = 0; + lpf->v_ind = NULL; + lpf->v_val = NULL; + lpf->work1 = lpf->work2 = NULL; + return lpf; +} + +/*********************************************************************** +* NAME +* +* lpf_factorize - compute LP basis factorization +* +* SYNOPSIS +* +* #include "glplpf.h" +* int lpf_factorize(LPF *lpf, int m, const int bh[], int (*col) +* (void *info, int j, int ind[], double val[]), void *info); +* +* DESCRIPTION +* +* The routine lpf_factorize computes the factorization of the basis +* matrix B specified by the routine col. +* +* The parameter lpf specified the basis factorization data structure +* created with the routine lpf_create_it. +* +* The parameter m specifies the order of B, m > 0. +* +* The array bh specifies the basis header: bh[j], 1 <= j <= m, is the +* number of j-th column of B in some original matrix. The array bh is +* optional and can be specified as NULL. +* +* The formal routine col specifies the matrix B to be factorized. To +* obtain j-th column of A the routine lpf_factorize calls the routine +* col with the parameter j (1 <= j <= n). In response the routine col +* should store row indices and numerical values of non-zero elements +* of j-th column of B to locations ind[1,...,len] and val[1,...,len], +* respectively, where len is the number of non-zeros in j-th column +* returned on exit. Neither zero nor duplicate elements are allowed. +* +* The parameter info is a transit pointer passed to the routine col. +* +* RETURNS +* +* 0 The factorization has been successfully computed. +* +* LPF_ESING +* The specified matrix is singular within the working precision. +* +* LPF_ECOND +* The specified matrix is ill-conditioned. +* +* For more details see comments to the routine luf_factorize. */ + +int lpf_factorize(LPF *lpf, int m, const int bh[], int (*col) + (void *info, int j, int ind[], double val[]), void *info) +{ int k, ret; +#if _GLPLPF_DEBUG + int i, j, len, *ind; + double *B, *val; +#endif + xassert(bh == bh); + if (m < 1) + xfault("lpf_factorize: m = %d; invalid parameter\n", m); + if (m > M_MAX) + xfault("lpf_factorize: m = %d; matrix too big\n", m); + lpf->m0 = lpf->m = m; + /* invalidate the factorization */ + lpf->valid = 0; + /* allocate/reallocate arrays, if necessary */ + if (lpf->R_ptr == NULL) + lpf->R_ptr = xcalloc(1+lpf->n_max, sizeof(int)); + if (lpf->R_len == NULL) + lpf->R_len = xcalloc(1+lpf->n_max, sizeof(int)); + if (lpf->S_ptr == NULL) + lpf->S_ptr = xcalloc(1+lpf->n_max, sizeof(int)); + if (lpf->S_len == NULL) + lpf->S_len = xcalloc(1+lpf->n_max, sizeof(int)); + if (lpf->scf == NULL) + lpf->scf = scf_create_it(lpf->n_max); + if (lpf->v_ind == NULL) + lpf->v_ind = xcalloc(1+lpf->v_size, sizeof(int)); + if (lpf->v_val == NULL) + lpf->v_val = xcalloc(1+lpf->v_size, sizeof(double)); + if (lpf->m0_max < m) + { if (lpf->P_row != NULL) xfree(lpf->P_row); + if (lpf->P_col != NULL) xfree(lpf->P_col); + if (lpf->Q_row != NULL) xfree(lpf->Q_row); + if (lpf->Q_col != NULL) xfree(lpf->Q_col); + if (lpf->work1 != NULL) xfree(lpf->work1); + if (lpf->work2 != NULL) xfree(lpf->work2); + lpf->m0_max = m + 100; + lpf->P_row = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(int)); + lpf->P_col = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(int)); + lpf->Q_row = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(int)); + lpf->Q_col = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(int)); + lpf->work1 = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(double)); + lpf->work2 = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(double)); + } + /* try to factorize the basis matrix */ + switch (luf_factorize(lpf->luf, m, col, info)) + { case 0: + break; + case LUF_ESING: + ret = LPF_ESING; + goto done; + case LUF_ECOND: + ret = LPF_ECOND; + goto done; + default: + xassert(lpf != lpf); + } + /* the basis matrix has been successfully factorized */ + lpf->valid = 1; +#if _GLPLPF_DEBUG + /* store the basis matrix for debugging */ + if (lpf->B != NULL) xfree(lpf->B); + xassert(m <= 32767); + lpf->B = B = xcalloc(1+m*m, sizeof(double)); + ind = xcalloc(1+m, sizeof(int)); + val = xcalloc(1+m, sizeof(double)); + for (k = 1; k <= m * m; k++) + B[k] = 0.0; + for (j = 1; j <= m; j++) + { len = col(info, j, ind, val); + xassert(0 <= len && len <= m); + for (k = 1; k <= len; k++) + { i = ind[k]; + xassert(1 <= i && i <= m); + xassert(B[(i - 1) * m + j] == 0.0); + xassert(val[k] != 0.0); + B[(i - 1) * m + j] = val[k]; + } + } + xfree(ind); + xfree(val); +#endif + /* B = B0, so there are no additional rows/columns */ + lpf->n = 0; + /* reset the Schur complement factorization */ + scf_reset_it(lpf->scf); + /* P := Q := I */ + for (k = 1; k <= m; k++) + { lpf->P_row[k] = lpf->P_col[k] = k; + lpf->Q_row[k] = lpf->Q_col[k] = k; + } + /* make all SVA locations free */ + lpf->v_ptr = 1; + ret = 0; +done: /* return to the calling program */ + return ret; +} + +/*********************************************************************** +* The routine r_prod computes the product y := y + alpha * R * x, +* where x is a n-vector, alpha is a scalar, y is a m0-vector. +* +* Since matrix R is available by columns, the product is computed as +* a linear combination: +* +* y := y + alpha * (R[1] * x[1] + ... + R[n] * x[n]), +* +* where R[j] is j-th column of R. */ + +static void r_prod(LPF *lpf, double y[], double a, const double x[]) +{ int n = lpf->n; + int *R_ptr = lpf->R_ptr; + int *R_len = lpf->R_len; + int *v_ind = lpf->v_ind; + double *v_val = lpf->v_val; + int j, beg, end, ptr; + double t; + for (j = 1; j <= n; j++) + { if (x[j] == 0.0) continue; + /* y := y + alpha * R[j] * x[j] */ + t = a * x[j]; + beg = R_ptr[j]; + end = beg + R_len[j]; + for (ptr = beg; ptr < end; ptr++) + y[v_ind[ptr]] += t * v_val[ptr]; + } + return; +} + +/*********************************************************************** +* The routine rt_prod computes the product y := y + alpha * R' * x, +* where R' is a matrix transposed to R, x is a m0-vector, alpha is a +* scalar, y is a n-vector. +* +* Since matrix R is available by columns, the product components are +* computed as inner products: +* +* y[j] := y[j] + alpha * (j-th column of R) * x +* +* for j = 1, 2, ..., n. */ + +static void rt_prod(LPF *lpf, double y[], double a, const double x[]) +{ int n = lpf->n; + int *R_ptr = lpf->R_ptr; + int *R_len = lpf->R_len; + int *v_ind = lpf->v_ind; + double *v_val = lpf->v_val; + int j, beg, end, ptr; + double t; + for (j = 1; j <= n; j++) + { /* t := (j-th column of R) * x */ + t = 0.0; + beg = R_ptr[j]; + end = beg + R_len[j]; + for (ptr = beg; ptr < end; ptr++) + t += v_val[ptr] * x[v_ind[ptr]]; + /* y[j] := y[j] + alpha * t */ + y[j] += a * t; + } + return; +} + +/*********************************************************************** +* The routine s_prod computes the product y := y + alpha * S * x, +* where x is a m0-vector, alpha is a scalar, y is a n-vector. +* +* Since matrix S is available by rows, the product components are +* computed as inner products: +* +* y[i] = y[i] + alpha * (i-th row of S) * x +* +* for i = 1, 2, ..., n. */ + +static void s_prod(LPF *lpf, double y[], double a, const double x[]) +{ int n = lpf->n; + int *S_ptr = lpf->S_ptr; + int *S_len = lpf->S_len; + int *v_ind = lpf->v_ind; + double *v_val = lpf->v_val; + int i, beg, end, ptr; + double t; + for (i = 1; i <= n; i++) + { /* t := (i-th row of S) * x */ + t = 0.0; + beg = S_ptr[i]; + end = beg + S_len[i]; + for (ptr = beg; ptr < end; ptr++) + t += v_val[ptr] * x[v_ind[ptr]]; + /* y[i] := y[i] + alpha * t */ + y[i] += a * t; + } + return; +} + +/*********************************************************************** +* The routine st_prod computes the product y := y + alpha * S' * x, +* where S' is a matrix transposed to S, x is a n-vector, alpha is a +* scalar, y is m0-vector. +* +* Since matrix R is available by rows, the product is computed as a +* linear combination: +* +* y := y + alpha * (S'[1] * x[1] + ... + S'[n] * x[n]), +* +* where S'[i] is i-th row of S. */ + +static void st_prod(LPF *lpf, double y[], double a, const double x[]) +{ int n = lpf->n; + int *S_ptr = lpf->S_ptr; + int *S_len = lpf->S_len; + int *v_ind = lpf->v_ind; + double *v_val = lpf->v_val; + int i, beg, end, ptr; + double t; + for (i = 1; i <= n; i++) + { if (x[i] == 0.0) continue; + /* y := y + alpha * S'[i] * x[i] */ + t = a * x[i]; + beg = S_ptr[i]; + end = beg + S_len[i]; + for (ptr = beg; ptr < end; ptr++) + y[v_ind[ptr]] += t * v_val[ptr]; + } + return; +} + +#if _GLPLPF_DEBUG +/*********************************************************************** +* The routine check_error computes the maximal relative error between +* left- and right-hand sides for the system B * x = b (if tr is zero) +* or B' * x = b (if tr is non-zero), where B' is a matrix transposed +* to B. (This routine is intended for debugging only.) */ + +static void check_error(LPF *lpf, int tr, const double x[], + const double b[]) +{ int m = lpf->m; + double *B = lpf->B; + int i, j; + double d, dmax = 0.0, s, t, tmax; + for (i = 1; i <= m; i++) + { s = 0.0; + tmax = 1.0; + for (j = 1; j <= m; j++) + { if (!tr) + t = B[m * (i - 1) + j] * x[j]; + else + t = B[m * (j - 1) + i] * x[j]; + if (tmax < fabs(t)) tmax = fabs(t); + s += t; + } + d = fabs(s - b[i]) / tmax; + if (dmax < d) dmax = d; + } + if (dmax > 1e-8) + xprintf("%s: dmax = %g; relative error too large\n", + !tr ? "lpf_ftran" : "lpf_btran", dmax); + return; +} +#endif + +/*********************************************************************** +* NAME +* +* lpf_ftran - perform forward transformation (solve system B*x = b) +* +* SYNOPSIS +* +* #include "glplpf.h" +* void lpf_ftran(LPF *lpf, double x[]); +* +* DESCRIPTION +* +* The routine lpf_ftran performs forward transformation, i.e. solves +* the system B*x = b, where B is the basis matrix, x is the vector of +* unknowns to be computed, b is the vector of right-hand sides. +* +* On entry elements of the vector b should be stored in dense format +* in locations x[1], ..., x[m], where m is the number of rows. On exit +* the routine stores elements of the vector x in the same locations. +* +* BACKGROUND +* +* Solution of the system B * x = b can be obtained by solving the +* following augmented system: +* +* ( B F^) ( x ) ( b ) +* ( ) ( ) = ( ) +* ( G^ H^) ( y ) ( 0 ) +* +* which, using the main equality, can be written as follows: +* +* ( L0 0 ) ( U0 R ) ( x ) ( b ) +* P ( ) ( ) Q ( ) = ( ) +* ( S I ) ( 0 C ) ( y ) ( 0 ) +* +* therefore, +* +* ( x ) ( U0 R )-1 ( L0 0 )-1 ( b ) +* ( ) = Q' ( ) ( ) P' ( ) +* ( y ) ( 0 C ) ( S I ) ( 0 ) +* +* Thus, computing the solution includes the following steps: +* +* 1. Compute +* +* ( f ) ( b ) +* ( ) = P' ( ) +* ( g ) ( 0 ) +* +* 2. Solve the system +* +* ( f1 ) ( L0 0 )-1 ( f ) ( L0 0 ) ( f1 ) ( f ) +* ( ) = ( ) ( ) => ( ) ( ) = ( ) +* ( g1 ) ( S I ) ( g ) ( S I ) ( g1 ) ( g ) +* +* from which it follows that: +* +* { L0 * f1 = f f1 = inv(L0) * f +* { => +* { S * f1 + g1 = g g1 = g - S * f1 +* +* 3. Solve the system +* +* ( f2 ) ( U0 R )-1 ( f1 ) ( U0 R ) ( f2 ) ( f1 ) +* ( ) = ( ) ( ) => ( ) ( ) = ( ) +* ( g2 ) ( 0 C ) ( g1 ) ( 0 C ) ( g2 ) ( g1 ) +* +* from which it follows that: +* +* { U0 * f2 + R * g2 = f1 f2 = inv(U0) * (f1 - R * g2) +* { => +* { C * g2 = g1 g2 = inv(C) * g1 +* +* 4. Compute +* +* ( x ) ( f2 ) +* ( ) = Q' ( ) +* ( y ) ( g2 ) */ + +void lpf_ftran(LPF *lpf, double x[]) +{ int m0 = lpf->m0; + int m = lpf->m; + int n = lpf->n; + int *P_col = lpf->P_col; + int *Q_col = lpf->Q_col; + double *fg = lpf->work1; + double *f = fg; + double *g = fg + m0; + int i, ii; +#if _GLPLPF_DEBUG + double *b; +#endif + if (!lpf->valid) + xfault("lpf_ftran: the factorization is not valid\n"); + xassert(0 <= m && m <= m0 + n); +#if _GLPLPF_DEBUG + /* save the right-hand side vector */ + b = xcalloc(1+m, sizeof(double)); + for (i = 1; i <= m; i++) b[i] = x[i]; +#endif + /* (f g) := inv(P) * (b 0) */ + for (i = 1; i <= m0 + n; i++) + fg[i] = ((ii = P_col[i]) <= m ? x[ii] : 0.0); + /* f1 := inv(L0) * f */ + luf_f_solve(lpf->luf, 0, f); + /* g1 := g - S * f1 */ + s_prod(lpf, g, -1.0, f); + /* g2 := inv(C) * g1 */ + scf_solve_it(lpf->scf, 0, g); + /* f2 := inv(U0) * (f1 - R * g2) */ + r_prod(lpf, f, -1.0, g); + luf_v_solve(lpf->luf, 0, f); + /* (x y) := inv(Q) * (f2 g2) */ + for (i = 1; i <= m; i++) + x[i] = fg[Q_col[i]]; +#if _GLPLPF_DEBUG + /* check relative error in solution */ + check_error(lpf, 0, x, b); + xfree(b); +#endif + return; +} + +/*********************************************************************** +* NAME +* +* lpf_btran - perform backward transformation (solve system B'*x = b) +* +* SYNOPSIS +* +* #include "glplpf.h" +* void lpf_btran(LPF *lpf, double x[]); +* +* DESCRIPTION +* +* The routine lpf_btran performs backward transformation, i.e. solves +* the system B'*x = b, where B' is a matrix transposed to the basis +* matrix B, x is the vector of unknowns to be computed, b is the vector +* of right-hand sides. +* +* On entry elements of the vector b should be stored in dense format +* in locations x[1], ..., x[m], where m is the number of rows. On exit +* the routine stores elements of the vector x in the same locations. +* +* BACKGROUND +* +* Solution of the system B' * x = b, where B' is a matrix transposed +* to B, can be obtained by solving the following augmented system: +* +* ( B F^)T ( x ) ( b ) +* ( ) ( ) = ( ) +* ( G^ H^) ( y ) ( 0 ) +* +* which, using the main equality, can be written as follows: +* +* T ( U0 R )T ( L0 0 )T T ( x ) ( b ) +* Q ( ) ( ) P ( ) = ( ) +* ( 0 C ) ( S I ) ( y ) ( 0 ) +* +* or, equivalently, as follows: +* +* ( U'0 0 ) ( L'0 S') ( x ) ( b ) +* Q' ( ) ( ) P' ( ) = ( ) +* ( R' C') ( 0 I ) ( y ) ( 0 ) +* +* therefore, +* +* ( x ) ( L'0 S')-1 ( U'0 0 )-1 ( b ) +* ( ) = P ( ) ( ) Q ( ) +* ( y ) ( 0 I ) ( R' C') ( 0 ) +* +* Thus, computing the solution includes the following steps: +* +* 1. Compute +* +* ( f ) ( b ) +* ( ) = Q ( ) +* ( g ) ( 0 ) +* +* 2. Solve the system +* +* ( f1 ) ( U'0 0 )-1 ( f ) ( U'0 0 ) ( f1 ) ( f ) +* ( ) = ( ) ( ) => ( ) ( ) = ( ) +* ( g1 ) ( R' C') ( g ) ( R' C') ( g1 ) ( g ) +* +* from which it follows that: +* +* { U'0 * f1 = f f1 = inv(U'0) * f +* { => +* { R' * f1 + C' * g1 = g g1 = inv(C') * (g - R' * f1) +* +* 3. Solve the system +* +* ( f2 ) ( L'0 S')-1 ( f1 ) ( L'0 S') ( f2 ) ( f1 ) +* ( ) = ( ) ( ) => ( ) ( ) = ( ) +* ( g2 ) ( 0 I ) ( g1 ) ( 0 I ) ( g2 ) ( g1 ) +* +* from which it follows that: +* +* { L'0 * f2 + S' * g2 = f1 +* { => f2 = inv(L'0) * ( f1 - S' * g2) +* { g2 = g1 +* +* 4. Compute +* +* ( x ) ( f2 ) +* ( ) = P ( ) +* ( y ) ( g2 ) */ + +void lpf_btran(LPF *lpf, double x[]) +{ int m0 = lpf->m0; + int m = lpf->m; + int n = lpf->n; + int *P_row = lpf->P_row; + int *Q_row = lpf->Q_row; + double *fg = lpf->work1; + double *f = fg; + double *g = fg + m0; + int i, ii; +#if _GLPLPF_DEBUG + double *b; +#endif + if (!lpf->valid) + xfault("lpf_btran: the factorization is not valid\n"); + xassert(0 <= m && m <= m0 + n); +#if _GLPLPF_DEBUG + /* save the right-hand side vector */ + b = xcalloc(1+m, sizeof(double)); + for (i = 1; i <= m; i++) b[i] = x[i]; +#endif + /* (f g) := Q * (b 0) */ + for (i = 1; i <= m0 + n; i++) + fg[i] = ((ii = Q_row[i]) <= m ? x[ii] : 0.0); + /* f1 := inv(U'0) * f */ + luf_v_solve(lpf->luf, 1, f); + /* g1 := inv(C') * (g - R' * f1) */ + rt_prod(lpf, g, -1.0, f); + scf_solve_it(lpf->scf, 1, g); + /* g2 := g1 */ + g = g; + /* f2 := inv(L'0) * (f1 - S' * g2) */ + st_prod(lpf, f, -1.0, g); + luf_f_solve(lpf->luf, 1, f); + /* (x y) := P * (f2 g2) */ + for (i = 1; i <= m; i++) + x[i] = fg[P_row[i]]; +#if _GLPLPF_DEBUG + /* check relative error in solution */ + check_error(lpf, 1, x, b); + xfree(b); +#endif + return; +} + +/*********************************************************************** +* The routine enlarge_sva enlarges the Sparse Vector Area to new_size +* locations by reallocating the arrays v_ind and v_val. */ + +static void enlarge_sva(LPF *lpf, int new_size) +{ int v_size = lpf->v_size; + int used = lpf->v_ptr - 1; + int *v_ind = lpf->v_ind; + double *v_val = lpf->v_val; + xassert(v_size < new_size); + while (v_size < new_size) v_size += v_size; + lpf->v_size = v_size; + lpf->v_ind = xcalloc(1+v_size, sizeof(int)); + lpf->v_val = xcalloc(1+v_size, sizeof(double)); + xassert(used >= 0); + memcpy(&lpf->v_ind[1], &v_ind[1], used * sizeof(int)); + memcpy(&lpf->v_val[1], &v_val[1], used * sizeof(double)); + xfree(v_ind); + xfree(v_val); + return; +} + +/*********************************************************************** +* NAME +* +* lpf_update_it - update LP basis factorization +* +* SYNOPSIS +* +* #include "glplpf.h" +* int lpf_update_it(LPF *lpf, int j, int bh, int len, const int ind[], +* const double val[]); +* +* DESCRIPTION +* +* The routine lpf_update_it updates the factorization of the basis +* matrix B after replacing its j-th column by a new vector. +* +* The parameter j specifies the number of column of B, which has been +* replaced, 1 <= j <= m, where m is the order of B. +* +* The parameter bh specifies the basis header entry for the new column +* of B, which is the number of the new column in some original matrix. +* This parameter is optional and can be specified as 0. +* +* Row indices and numerical values of non-zero elements of the new +* column of B should be placed in locations ind[1], ..., ind[len] and +* val[1], ..., val[len], resp., where len is the number of non-zeros +* in the column. Neither zero nor duplicate elements are allowed. +* +* RETURNS +* +* 0 The factorization has been successfully updated. +* +* LPF_ESING +* New basis B is singular within the working precision. +* +* LPF_ELIMIT +* Maximal number of additional rows and columns has been reached. +* +* BACKGROUND +* +* Let j-th column of the current basis matrix B have to be replaced by +* a new column a. This replacement is equivalent to removing the old +* j-th column by fixing it at zero and introducing the new column as +* follows: +* +* ( B F^| a ) +* ( B F^) ( | ) +* ( ) ---> ( G^ H^| 0 ) +* ( G^ H^) (-------+---) +* ( e'j 0 | 0 ) +* +* where ej is a unit vector with 1 in j-th position which used to fix +* the old j-th column of B (at zero). Then using the main equality we +* have: +* +* ( B F^| a ) ( B0 F | f ) +* ( | ) ( P 0 ) ( | ) ( Q 0 ) +* ( G^ H^| 0 ) = ( ) ( G H | g ) ( ) = +* (-------+---) ( 0 1 ) (-------+---) ( 0 1 ) +* ( e'j 0 | 0 ) ( v' w'| 0 ) +* +* [ ( B0 F )| ( f ) ] [ ( B0 F ) | ( f ) ] +* [ P ( )| P ( ) ] ( Q 0 ) [ P ( ) Q| P ( ) ] +* = [ ( G H )| ( g ) ] ( ) = [ ( G H ) | ( g ) ] +* [------------+-------- ] ( 0 1 ) [-------------+---------] +* [ ( v' w')| 0 ] [ ( v' w') Q| 0 ] +* +* where: +* +* ( a ) ( f ) ( f ) ( a ) +* ( ) = P ( ) => ( ) = P' * ( ) +* ( 0 ) ( g ) ( g ) ( 0 ) +* +* ( ej ) ( v ) ( v ) ( ej ) +* ( e'j 0 ) = ( v' w' ) Q => ( ) = Q' ( ) => ( ) = Q ( ) +* ( 0 ) ( w ) ( w ) ( 0 ) +* +* On the other hand: +* +* ( B0| F f ) +* ( P 0 ) (---+------) ( Q 0 ) ( B0 new F ) +* ( ) ( G | H g ) ( ) = new P ( ) new Q +* ( 0 1 ) ( | ) ( 0 1 ) ( new G new H ) +* ( v'| w' 0 ) +* +* where: +* ( G ) ( H g ) +* new F = ( F f ), new G = ( ), new H = ( ), +* ( v') ( w' 0 ) +* +* ( P 0 ) ( Q 0 ) +* new P = ( ) , new Q = ( ) . +* ( 0 1 ) ( 0 1 ) +* +* The factorization structure for the new augmented matrix remains the +* same, therefore: +* +* ( B0 new F ) ( L0 0 ) ( U0 new R ) +* new P ( ) new Q = ( ) ( ) +* ( new G new H ) ( new S I ) ( 0 new C ) +* +* where: +* +* new F = L0 * new R => +* +* new R = inv(L0) * new F = inv(L0) * (F f) = ( R inv(L0)*f ) +* +* new G = new S * U0 => +* +* ( G ) ( S ) +* new S = new G * inv(U0) = ( ) * inv(U0) = ( ) +* ( v') ( v'*inv(U0) ) +* +* new H = new S * new R + new C => +* +* new C = new H - new S * new R = +* +* ( H g ) ( S ) +* = ( ) - ( ) * ( R inv(L0)*f ) = +* ( w' 0 ) ( v'*inv(U0) ) +* +* ( H - S*R g - S*inv(L0)*f ) ( C x ) +* = ( ) = ( ) +* ( w'- v'*inv(U0)*R -v'*inv(U0)*inv(L0)*f) ( y' z ) +* +* Note that new C is resulted by expanding old C with new column x, +* row y', and diagonal element z, where: +* +* x = g - S * inv(L0) * f = g - S * (new column of R) +* +* y = w - R'* inv(U'0)* v = w - R'* (new row of S) +* +* z = - (new row of S) * (new column of R) +* +* Finally, to replace old B by new B we have to permute j-th and last +* (just added) columns of the matrix +* +* ( B F^| a ) +* ( | ) +* ( G^ H^| 0 ) +* (-------+---) +* ( e'j 0 | 0 ) +* +* and to keep the main equality do the same for matrix Q. */ + +int lpf_update_it(LPF *lpf, int j, int bh, int len, const int ind[], + const double val[]) +{ int m0 = lpf->m0; + int m = lpf->m; +#if _GLPLPF_DEBUG + double *B = lpf->B; +#endif + int n = lpf->n; + int *R_ptr = lpf->R_ptr; + int *R_len = lpf->R_len; + int *S_ptr = lpf->S_ptr; + int *S_len = lpf->S_len; + int *P_row = lpf->P_row; + int *P_col = lpf->P_col; + int *Q_row = lpf->Q_row; + int *Q_col = lpf->Q_col; + int v_ptr = lpf->v_ptr; + int *v_ind = lpf->v_ind; + double *v_val = lpf->v_val; + double *a = lpf->work2; /* new column */ + double *fg = lpf->work1, *f = fg, *g = fg + m0; + double *vw = lpf->work2, *v = vw, *w = vw + m0; + double *x = g, *y = w, z; + int i, ii, k, ret; + xassert(bh == bh); + if (!lpf->valid) + xfault("lpf_update_it: the factorization is not valid\n"); + if (!(1 <= j && j <= m)) + xfault("lpf_update_it: j = %d; column number out of range\n", + j); + xassert(0 <= m && m <= m0 + n); + /* check if the basis factorization can be expanded */ + if (n == lpf->n_max) + { lpf->valid = 0; + ret = LPF_ELIMIT; + goto done; + } + /* convert new j-th column of B to dense format */ + for (i = 1; i <= m; i++) + a[i] = 0.0; + for (k = 1; k <= len; k++) + { i = ind[k]; + if (!(1 <= i && i <= m)) + xfault("lpf_update_it: ind[%d] = %d; row number out of rang" + "e\n", k, i); + if (a[i] != 0.0) + xfault("lpf_update_it: ind[%d] = %d; duplicate row index no" + "t allowed\n", k, i); + if (val[k] == 0.0) + xfault("lpf_update_it: val[%d] = %g; zero element not allow" + "ed\n", k, val[k]); + a[i] = val[k]; + } +#if _GLPLPF_DEBUG + /* change column in the basis matrix for debugging */ + for (i = 1; i <= m; i++) + B[(i - 1) * m + j] = a[i]; +#endif + /* (f g) := inv(P) * (a 0) */ + for (i = 1; i <= m0+n; i++) + fg[i] = ((ii = P_col[i]) <= m ? a[ii] : 0.0); + /* (v w) := Q * (ej 0) */ + for (i = 1; i <= m0+n; i++) vw[i] = 0.0; + vw[Q_col[j]] = 1.0; + /* f1 := inv(L0) * f (new column of R) */ + luf_f_solve(lpf->luf, 0, f); + /* v1 := inv(U'0) * v (new row of S) */ + luf_v_solve(lpf->luf, 1, v); + /* we need at most 2 * m0 available locations in the SVA to store + new column of matrix R and new row of matrix S */ + if (lpf->v_size < v_ptr + m0 + m0) + { enlarge_sva(lpf, v_ptr + m0 + m0); + v_ind = lpf->v_ind; + v_val = lpf->v_val; + } + /* store new column of R */ + R_ptr[n+1] = v_ptr; + for (i = 1; i <= m0; i++) + { if (f[i] != 0.0) + v_ind[v_ptr] = i, v_val[v_ptr] = f[i], v_ptr++; + } + R_len[n+1] = v_ptr - lpf->v_ptr; + lpf->v_ptr = v_ptr; + /* store new row of S */ + S_ptr[n+1] = v_ptr; + for (i = 1; i <= m0; i++) + { if (v[i] != 0.0) + v_ind[v_ptr] = i, v_val[v_ptr] = v[i], v_ptr++; + } + S_len[n+1] = v_ptr - lpf->v_ptr; + lpf->v_ptr = v_ptr; + /* x := g - S * f1 (new column of C) */ + s_prod(lpf, x, -1.0, f); + /* y := w - R' * v1 (new row of C) */ + rt_prod(lpf, y, -1.0, v); + /* z := - v1 * f1 (new diagonal element of C) */ + z = 0.0; + for (i = 1; i <= m0; i++) z -= v[i] * f[i]; + /* update factorization of new matrix C */ + switch (scf_update_exp(lpf->scf, x, y, z)) + { case 0: + break; + case SCF_ESING: + lpf->valid = 0; + ret = LPF_ESING; + goto done; + case SCF_ELIMIT: + xassert(lpf != lpf); + default: + xassert(lpf != lpf); + } + /* expand matrix P */ + P_row[m0+n+1] = P_col[m0+n+1] = m0+n+1; + /* expand matrix Q */ + Q_row[m0+n+1] = Q_col[m0+n+1] = m0+n+1; + /* permute j-th and last (just added) column of matrix Q */ + i = Q_col[j], ii = Q_col[m0+n+1]; + Q_row[i] = m0+n+1, Q_col[m0+n+1] = i; + Q_row[ii] = j, Q_col[j] = ii; + /* increase the number of additional rows and columns */ + lpf->n++; + xassert(lpf->n <= lpf->n_max); + /* the factorization has been successfully updated */ + ret = 0; +done: /* return to the calling program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* lpf_delete_it - delete LP basis factorization +* +* SYNOPSIS +* +* #include "glplpf.h" +* void lpf_delete_it(LPF *lpf) +* +* DESCRIPTION +* +* The routine lpf_delete_it deletes LP basis factorization specified +* by the parameter lpf and frees all memory allocated to this program +* object. */ + +void lpf_delete_it(LPF *lpf) +{ luf_delete_it(lpf->luf); +#if _GLPLPF_DEBUG + if (lpf->B != NULL) xfree(lpf->B); +#else + xassert(lpf->B == NULL); +#endif + if (lpf->R_ptr != NULL) xfree(lpf->R_ptr); + if (lpf->R_len != NULL) xfree(lpf->R_len); + if (lpf->S_ptr != NULL) xfree(lpf->S_ptr); + if (lpf->S_len != NULL) xfree(lpf->S_len); + if (lpf->scf != NULL) scf_delete_it(lpf->scf); + if (lpf->P_row != NULL) xfree(lpf->P_row); + if (lpf->P_col != NULL) xfree(lpf->P_col); + if (lpf->Q_row != NULL) xfree(lpf->Q_row); + if (lpf->Q_col != NULL) xfree(lpf->Q_col); + if (lpf->v_ind != NULL) xfree(lpf->v_ind); + if (lpf->v_val != NULL) xfree(lpf->v_val); + if (lpf->work1 != NULL) xfree(lpf->work1); + if (lpf->work2 != NULL) xfree(lpf->work2); + xfree(lpf); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplpf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplpf.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,194 @@ +/* glplpf.h (LP basis factorization, Schur complement version) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPLPF_H +#define GLPLPF_H + +#include "glpscf.h" +#include "glpluf.h" + +/*********************************************************************** +* The structure LPF defines the factorization of the basis mxm matrix +* B, where m is the number of rows in corresponding problem instance. +* +* This factorization is the following septet: +* +* [B] = (L0, U0, R, S, C, P, Q), (1) +* +* and is based on the following main equality: +* +* ( B F^) ( B0 F ) ( L0 0 ) ( U0 R ) +* ( ) = P ( ) Q = P ( ) ( ) Q, (2) +* ( G^ H^) ( G H ) ( S I ) ( 0 C ) +* +* where: +* +* B is the current basis matrix (not stored); +* +* F^, G^, H^ are some additional matrices (not stored); +* +* B0 is some initial basis matrix (not stored); +* +* F, G, H are some additional matrices (not stored); +* +* P, Q are permutation matrices (stored in both row- and column-like +* formats); +* +* L0, U0 are some matrices that defines a factorization of the initial +* basis matrix B0 = L0 * U0 (stored in an invertable form); +* +* R is a matrix defined from L0 * R = F, so R = inv(L0) * F (stored in +* a column-wise sparse format); +* +* S is a matrix defined from S * U0 = G, so S = G * inv(U0) (stored in +* a row-wise sparse format); +* +* C is the Schur complement for matrix (B0 F G H). It is defined from +* S * R + C = H, so C = H - S * R = H - G * inv(U0) * inv(L0) * F = +* = H - G * inv(B0) * F. Matrix C is stored in an invertable form. +* +* REFERENCES +* +* 1. M.A.Saunders, "LUSOL: A basis package for constrained optimiza- +* tion," SCCM, Stanford University, 2006. +* +* 2. M.A.Saunders, "Notes 5: Basis Updates," CME 318, Stanford Univer- +* sity, Spring 2006. +* +* 3. M.A.Saunders, "Notes 6: LUSOL---a Basis Factorization Package," +* ibid. */ + +typedef struct LPF LPF; + +struct LPF +{ /* LP basis factorization */ + int valid; + /* the factorization is valid only if this flag is set */ + /*--------------------------------------------------------------*/ + /* initial basis matrix B0 */ + int m0_max; + /* maximal value of m0 (increased automatically, if necessary) */ + int m0; + /* the order of B0 */ + LUF *luf; + /* LU-factorization of B0 */ + /*--------------------------------------------------------------*/ + /* current basis matrix B */ + int m; + /* the order of B */ + double *B; /* double B[1+m*m]; */ + /* B in dense format stored by rows and used only for debugging; + normally this array is not allocated */ + /*--------------------------------------------------------------*/ + /* augmented matrix (B0 F G H) of the order m0+n */ + int n_max; + /* maximal number of additional rows and columns */ + int n; + /* current number of additional rows and columns */ + /*--------------------------------------------------------------*/ + /* m0xn matrix R in column-wise format */ + int *R_ptr; /* int R_ptr[1+n_max]; */ + /* R_ptr[j], 1 <= j <= n, is a pointer to j-th column */ + int *R_len; /* int R_len[1+n_max]; */ + /* R_len[j], 1 <= j <= n, is the length of j-th column */ + /*--------------------------------------------------------------*/ + /* nxm0 matrix S in row-wise format */ + int *S_ptr; /* int S_ptr[1+n_max]; */ + /* S_ptr[i], 1 <= i <= n, is a pointer to i-th row */ + int *S_len; /* int S_len[1+n_max]; */ + /* S_len[i], 1 <= i <= n, is the length of i-th row */ + /*--------------------------------------------------------------*/ + /* Schur complement C of the order n */ + SCF *scf; /* SCF scf[1:n_max]; */ + /* factorization of the Schur complement */ + /*--------------------------------------------------------------*/ + /* matrix P of the order m0+n */ + int *P_row; /* int P_row[1+m0_max+n_max]; */ + /* P_row[i] = j means that P[i,j] = 1 */ + int *P_col; /* int P_col[1+m0_max+n_max]; */ + /* P_col[j] = i means that P[i,j] = 1 */ + /*--------------------------------------------------------------*/ + /* matrix Q of the order m0+n */ + int *Q_row; /* int Q_row[1+m0_max+n_max]; */ + /* Q_row[i] = j means that Q[i,j] = 1 */ + int *Q_col; /* int Q_col[1+m0_max+n_max]; */ + /* Q_col[j] = i means that Q[i,j] = 1 */ + /*--------------------------------------------------------------*/ + /* Sparse Vector Area (SVA) is a set of locations intended to + store sparse vectors which represent columns of matrix R and + rows of matrix S; each location is a doublet (ind, val), where + ind is an index, val is a numerical value of a sparse vector + element; in the whole each sparse vector is a set of adjacent + locations defined by a pointer to its first element and its + length, i.e. the number of its elements */ + int v_size; + /* the SVA size, in locations; locations are numbered by integers + 1, 2, ..., v_size, and location 0 is not used */ + int v_ptr; + /* pointer to the first available location */ + int *v_ind; /* int v_ind[1+v_size]; */ + /* v_ind[k], 1 <= k <= v_size, is the index field of location k */ + double *v_val; /* double v_val[1+v_size]; */ + /* v_val[k], 1 <= k <= v_size, is the value field of location k */ + /*--------------------------------------------------------------*/ + double *work1; /* double work1[1+m0+n_max]; */ + /* working array */ + double *work2; /* double work2[1+m0+n_max]; */ + /* working array */ +}; + +/* return codes: */ +#define LPF_ESING 1 /* singular matrix */ +#define LPF_ECOND 2 /* ill-conditioned matrix */ +#define LPF_ELIMIT 3 /* update limit reached */ + +#define lpf_create_it _glp_lpf_create_it +LPF *lpf_create_it(void); +/* create LP basis factorization */ + +#define lpf_factorize _glp_lpf_factorize +int lpf_factorize(LPF *lpf, int m, const int bh[], int (*col) + (void *info, int j, int ind[], double val[]), void *info); +/* compute LP basis factorization */ + +#define lpf_ftran _glp_lpf_ftran +void lpf_ftran(LPF *lpf, double x[]); +/* perform forward transformation (solve system B*x = b) */ + +#define lpf_btran _glp_lpf_btran +void lpf_btran(LPF *lpf, double x[]); +/* perform backward transformation (solve system B'*x = b) */ + +#define lpf_update_it _glp_lpf_update_it +int lpf_update_it(LPF *lpf, int j, int bh, int len, const int ind[], + const double val[]); +/* update LP basis factorization */ + +#define lpf_delete_it _glp_lpf_delete_it +void lpf_delete_it(LPF *lpf); +/* delete LP basis factorization */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplpx01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplpx01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1542 @@ +/* glplpx01.c (obsolete API routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +struct LPXCPS +{ /* control parameters and statistics */ + int msg_lev; + /* level of messages output by the solver: + 0 - no output + 1 - error messages only + 2 - normal output + 3 - full output (includes informational messages) */ + int scale; + /* scaling option: + 0 - no scaling + 1 - equilibration scaling + 2 - geometric mean scaling + 3 - geometric mean scaling, then equilibration scaling */ + int dual; + /* dual simplex option: + 0 - use primal simplex + 1 - use dual simplex */ + int price; + /* pricing option (for both primal and dual simplex): + 0 - textbook pricing + 1 - steepest edge pricing */ + double relax; + /* relaxation parameter used in the ratio test; if it is zero, + the textbook ratio test is used; if it is non-zero (should be + positive), Harris' two-pass ratio test is used; in the latter + case on the first pass basic variables (in the case of primal + simplex) or reduced costs of non-basic variables (in the case + of dual simplex) are allowed to slightly violate their bounds, + but not more than (relax * tol_bnd) or (relax * tol_dj) (thus, + relax is a percentage of tol_bnd or tol_dj) */ + double tol_bnd; + /* relative tolerance used to check if the current basic solution + is primal feasible */ + double tol_dj; + /* absolute tolerance used to check if the current basic solution + is dual feasible */ + double tol_piv; + /* relative tolerance used to choose eligible pivotal elements of + the simplex table in the ratio test */ + int round; + /* solution rounding option: + 0 - report all computed values and reduced costs "as is" + 1 - if possible (allowed by the tolerances), replace computed + values and reduced costs which are close to zero by exact + zeros */ + double obj_ll; + /* lower limit of the objective function; if on the phase II the + objective function reaches this limit and continues decreasing, + the solver stops the search */ + double obj_ul; + /* upper limit of the objective function; if on the phase II the + objective function reaches this limit and continues increasing, + the solver stops the search */ + int it_lim; + /* simplex iterations limit; if this value is positive, it is + decreased by one each time when one simplex iteration has been + performed, and reaching zero value signals the solver to stop + the search; negative value means no iterations limit */ + double tm_lim; + /* searching time limit, in seconds; if this value is positive, + it is decreased each time when one simplex iteration has been + performed by the amount of time spent for the iteration, and + reaching zero value signals the solver to stop the search; + negative value means no time limit */ + int out_frq; + /* output frequency, in iterations; this parameter specifies how + frequently the solver sends information about the solution to + the standard output */ + double out_dly; + /* output delay, in seconds; this parameter specifies how long + the solver should delay sending information about the solution + to the standard output; zero value means no delay */ + int branch; /* MIP */ + /* branching heuristic: + 0 - branch on first variable + 1 - branch on last variable + 2 - branch using heuristic by Driebeck and Tomlin + 3 - branch on most fractional variable */ + int btrack; /* MIP */ + /* backtracking heuristic: + 0 - select most recent node (depth first search) + 1 - select earliest node (breadth first search) + 2 - select node using the best projection heuristic + 3 - select node with best local bound */ + double tol_int; /* MIP */ + /* absolute tolerance used to check if the current basic solution + is integer feasible */ + double tol_obj; /* MIP */ + /* relative tolerance used to check if the value of the objective + function is not better than in the best known integer feasible + solution */ + int mps_info; /* lpx_write_mps */ + /* if this flag is set, the routine lpx_write_mps outputs several + comment cards that contains some information about the problem; + otherwise the routine outputs no comment cards */ + int mps_obj; /* lpx_write_mps */ + /* this parameter tells the routine lpx_write_mps how to output + the objective function row: + 0 - never output objective function row + 1 - always output objective function row + 2 - output objective function row if and only if the problem + has no free rows */ + int mps_orig; /* lpx_write_mps */ + /* if this flag is set, the routine lpx_write_mps uses original + row and column symbolic names; otherwise the routine generates + plain names using ordinal numbers of rows and columns */ + int mps_wide; /* lpx_write_mps */ + /* if this flag is set, the routine lpx_write_mps uses all data + fields; otherwise the routine keeps fields 5 and 6 empty */ + int mps_free; /* lpx_write_mps */ + /* if this flag is set, the routine lpx_write_mps omits column + and vector names everytime if possible (free style); otherwise + the routine never omits these names (pedantic style) */ + int mps_skip; /* lpx_write_mps */ + /* if this flag is set, the routine lpx_write_mps skips empty + columns (i.e. which has no constraint coefficients); otherwise + the routine outputs all columns */ + int lpt_orig; /* lpx_write_lpt */ + /* if this flag is set, the routine lpx_write_lpt uses original + row and column symbolic names; otherwise the routine generates + plain names using ordinal numbers of rows and columns */ + int presol; /* lpx_simplex */ + /* LP presolver option: + 0 - do not use LP presolver + 1 - use LP presolver */ + int binarize; /* lpx_intopt */ + /* if this flag is set, the routine lpx_intopt replaces integer + columns by binary ones */ + int use_cuts; /* lpx_intopt */ + /* if this flag is set, the routine lpx_intopt tries generating + cutting planes: + LPX_C_COVER - mixed cover cuts + LPX_C_CLIQUE - clique cuts + LPX_C_GOMORY - Gomory's mixed integer cuts + LPX_C_ALL - all cuts */ + double mip_gap; /* MIP */ + /* relative MIP gap tolerance */ +}; + +LPX *lpx_create_prob(void) +{ /* create problem object */ + return glp_create_prob(); +} + +void lpx_set_prob_name(LPX *lp, const char *name) +{ /* assign (change) problem name */ + glp_set_prob_name(lp, name); + return; +} + +void lpx_set_obj_name(LPX *lp, const char *name) +{ /* assign (change) objective function name */ + glp_set_obj_name(lp, name); + return; +} + +void lpx_set_obj_dir(LPX *lp, int dir) +{ /* set (change) optimization direction flag */ + glp_set_obj_dir(lp, dir - LPX_MIN + GLP_MIN); + return; +} + +int lpx_add_rows(LPX *lp, int nrs) +{ /* add new rows to problem object */ + return glp_add_rows(lp, nrs); +} + +int lpx_add_cols(LPX *lp, int ncs) +{ /* add new columns to problem object */ + return glp_add_cols(lp, ncs); +} + +void lpx_set_row_name(LPX *lp, int i, const char *name) +{ /* assign (change) row name */ + glp_set_row_name(lp, i, name); + return; +} + +void lpx_set_col_name(LPX *lp, int j, const char *name) +{ /* assign (change) column name */ + glp_set_col_name(lp, j, name); + return; +} + +void lpx_set_row_bnds(LPX *lp, int i, int type, double lb, double ub) +{ /* set (change) row bounds */ + glp_set_row_bnds(lp, i, type - LPX_FR + GLP_FR, lb, ub); + return; +} + +void lpx_set_col_bnds(LPX *lp, int j, int type, double lb, double ub) +{ /* set (change) column bounds */ + glp_set_col_bnds(lp, j, type - LPX_FR + GLP_FR, lb, ub); + return; +} + +void lpx_set_obj_coef(glp_prob *lp, int j, double coef) +{ /* set (change) obj. coefficient or constant term */ + glp_set_obj_coef(lp, j, coef); + return; +} + +void lpx_set_mat_row(LPX *lp, int i, int len, const int ind[], + const double val[]) +{ /* set (replace) row of the constraint matrix */ + glp_set_mat_row(lp, i, len, ind, val); + return; +} + +void lpx_set_mat_col(LPX *lp, int j, int len, const int ind[], + const double val[]) +{ /* set (replace) column of the constraint matrix */ + glp_set_mat_col(lp, j, len, ind, val); + return; +} + +void lpx_load_matrix(LPX *lp, int ne, const int ia[], const int ja[], + const double ar[]) +{ /* load (replace) the whole constraint matrix */ + glp_load_matrix(lp, ne, ia, ja, ar); + return; +} + +void lpx_del_rows(LPX *lp, int nrs, const int num[]) +{ /* delete specified rows from problem object */ + glp_del_rows(lp, nrs, num); + return; +} + +void lpx_del_cols(LPX *lp, int ncs, const int num[]) +{ /* delete specified columns from problem object */ + glp_del_cols(lp, ncs, num); + return; +} + +void lpx_delete_prob(LPX *lp) +{ /* delete problem object */ + glp_delete_prob(lp); + return; +} + +const char *lpx_get_prob_name(LPX *lp) +{ /* retrieve problem name */ + return glp_get_prob_name(lp); +} + +const char *lpx_get_obj_name(LPX *lp) +{ /* retrieve objective function name */ + return glp_get_obj_name(lp); +} + +int lpx_get_obj_dir(LPX *lp) +{ /* retrieve optimization direction flag */ + return glp_get_obj_dir(lp) - GLP_MIN + LPX_MIN; +} + +int lpx_get_num_rows(LPX *lp) +{ /* retrieve number of rows */ + return glp_get_num_rows(lp); +} + +int lpx_get_num_cols(LPX *lp) +{ /* retrieve number of columns */ + return glp_get_num_cols(lp); +} + +const char *lpx_get_row_name(LPX *lp, int i) +{ /* retrieve row name */ + return glp_get_row_name(lp, i); +} + +const char *lpx_get_col_name(LPX *lp, int j) +{ /* retrieve column name */ + return glp_get_col_name(lp, j); +} + +int lpx_get_row_type(LPX *lp, int i) +{ /* retrieve row type */ + return glp_get_row_type(lp, i) - GLP_FR + LPX_FR; +} + +double lpx_get_row_lb(glp_prob *lp, int i) +{ /* retrieve row lower bound */ + double lb; + lb = glp_get_row_lb(lp, i); + if (lb == -DBL_MAX) lb = 0.0; + return lb; +} + +double lpx_get_row_ub(glp_prob *lp, int i) +{ /* retrieve row upper bound */ + double ub; + ub = glp_get_row_ub(lp, i); + if (ub == +DBL_MAX) ub = 0.0; + return ub; +} + +void lpx_get_row_bnds(glp_prob *lp, int i, int *typx, double *lb, + double *ub) +{ /* retrieve row bounds */ + if (typx != NULL) *typx = lpx_get_row_type(lp, i); + if (lb != NULL) *lb = lpx_get_row_lb(lp, i); + if (ub != NULL) *ub = lpx_get_row_ub(lp, i); + return; +} + +int lpx_get_col_type(LPX *lp, int j) +{ /* retrieve column type */ + return glp_get_col_type(lp, j) - GLP_FR + LPX_FR; +} + +double lpx_get_col_lb(glp_prob *lp, int j) +{ /* retrieve column lower bound */ + double lb; + lb = glp_get_col_lb(lp, j); + if (lb == -DBL_MAX) lb = 0.0; + return lb; +} + +double lpx_get_col_ub(glp_prob *lp, int j) +{ /* retrieve column upper bound */ + double ub; + ub = glp_get_col_ub(lp, j); + if (ub == +DBL_MAX) ub = 0.0; + return ub; +} + +void lpx_get_col_bnds(glp_prob *lp, int j, int *typx, double *lb, + double *ub) +{ /* retrieve column bounds */ + if (typx != NULL) *typx = lpx_get_col_type(lp, j); + if (lb != NULL) *lb = lpx_get_col_lb(lp, j); + if (ub != NULL) *ub = lpx_get_col_ub(lp, j); + return; +} + +double lpx_get_obj_coef(LPX *lp, int j) +{ /* retrieve obj. coefficient or constant term */ + return glp_get_obj_coef(lp, j); +} + +int lpx_get_num_nz(LPX *lp) +{ /* retrieve number of constraint coefficients */ + return glp_get_num_nz(lp); +} + +int lpx_get_mat_row(LPX *lp, int i, int ind[], double val[]) +{ /* retrieve row of the constraint matrix */ + return glp_get_mat_row(lp, i, ind, val); +} + +int lpx_get_mat_col(LPX *lp, int j, int ind[], double val[]) +{ /* retrieve column of the constraint matrix */ + return glp_get_mat_col(lp, j, ind, val); +} + +void lpx_create_index(LPX *lp) +{ /* create the name index */ + glp_create_index(lp); + return; +} + +int lpx_find_row(LPX *lp, const char *name) +{ /* find row by its name */ + return glp_find_row(lp, name); +} + +int lpx_find_col(LPX *lp, const char *name) +{ /* find column by its name */ + return glp_find_col(lp, name); +} + +void lpx_delete_index(LPX *lp) +{ /* delete the name index */ + glp_delete_index(lp); + return; +} + +void lpx_scale_prob(LPX *lp) +{ /* scale problem data */ + switch (lpx_get_int_parm(lp, LPX_K_SCALE)) + { case 0: + /* no scaling */ + glp_unscale_prob(lp); + break; + case 1: + /* equilibration scaling */ + glp_scale_prob(lp, GLP_SF_EQ); + break; + case 2: + /* geometric mean scaling */ + glp_scale_prob(lp, GLP_SF_GM); + break; + case 3: + /* geometric mean scaling, then equilibration scaling */ + glp_scale_prob(lp, GLP_SF_GM | GLP_SF_EQ); + break; + default: + xassert(lp != lp); + } + return; +} + +void lpx_unscale_prob(LPX *lp) +{ /* unscale problem data */ + glp_unscale_prob(lp); + return; +} + +void lpx_set_row_stat(LPX *lp, int i, int stat) +{ /* set (change) row status */ + glp_set_row_stat(lp, i, stat - LPX_BS + GLP_BS); + return; +} + +void lpx_set_col_stat(LPX *lp, int j, int stat) +{ /* set (change) column status */ + glp_set_col_stat(lp, j, stat - LPX_BS + GLP_BS); + return; +} + +void lpx_std_basis(LPX *lp) +{ /* construct standard initial LP basis */ + glp_std_basis(lp); + return; +} + +void lpx_adv_basis(LPX *lp) +{ /* construct advanced initial LP basis */ + glp_adv_basis(lp, 0); + return; +} + +void lpx_cpx_basis(LPX *lp) +{ /* construct Bixby's initial LP basis */ + glp_cpx_basis(lp); + return; +} + +static void fill_smcp(LPX *lp, glp_smcp *parm) +{ glp_init_smcp(parm); + switch (lpx_get_int_parm(lp, LPX_K_MSGLEV)) + { case 0: parm->msg_lev = GLP_MSG_OFF; break; + case 1: parm->msg_lev = GLP_MSG_ERR; break; + case 2: parm->msg_lev = GLP_MSG_ON; break; + case 3: parm->msg_lev = GLP_MSG_ALL; break; + default: xassert(lp != lp); + } + switch (lpx_get_int_parm(lp, LPX_K_DUAL)) + { case 0: parm->meth = GLP_PRIMAL; break; + case 1: parm->meth = GLP_DUAL; break; + default: xassert(lp != lp); + } + switch (lpx_get_int_parm(lp, LPX_K_PRICE)) + { case 0: parm->pricing = GLP_PT_STD; break; + case 1: parm->pricing = GLP_PT_PSE; break; + default: xassert(lp != lp); + } + if (lpx_get_real_parm(lp, LPX_K_RELAX) == 0.0) + parm->r_test = GLP_RT_STD; + else + parm->r_test = GLP_RT_HAR; + parm->tol_bnd = lpx_get_real_parm(lp, LPX_K_TOLBND); + parm->tol_dj = lpx_get_real_parm(lp, LPX_K_TOLDJ); + parm->tol_piv = lpx_get_real_parm(lp, LPX_K_TOLPIV); + parm->obj_ll = lpx_get_real_parm(lp, LPX_K_OBJLL); + parm->obj_ul = lpx_get_real_parm(lp, LPX_K_OBJUL); + if (lpx_get_int_parm(lp, LPX_K_ITLIM) < 0) + parm->it_lim = INT_MAX; + else + parm->it_lim = lpx_get_int_parm(lp, LPX_K_ITLIM); + if (lpx_get_real_parm(lp, LPX_K_TMLIM) < 0.0) + parm->tm_lim = INT_MAX; + else + parm->tm_lim = + (int)(1000.0 * lpx_get_real_parm(lp, LPX_K_TMLIM)); + parm->out_frq = lpx_get_int_parm(lp, LPX_K_OUTFRQ); + parm->out_dly = + (int)(1000.0 * lpx_get_real_parm(lp, LPX_K_OUTDLY)); + switch (lpx_get_int_parm(lp, LPX_K_PRESOL)) + { case 0: parm->presolve = GLP_OFF; break; + case 1: parm->presolve = GLP_ON; break; + default: xassert(lp != lp); + } + return; +} + +int lpx_simplex(LPX *lp) +{ /* easy-to-use driver to the simplex method */ + glp_smcp parm; + int ret; + fill_smcp(lp, &parm); + ret = glp_simplex(lp, &parm); + switch (ret) + { case 0: ret = LPX_E_OK; break; + case GLP_EBADB: + case GLP_ESING: + case GLP_ECOND: + case GLP_EBOUND: ret = LPX_E_FAULT; break; + case GLP_EFAIL: ret = LPX_E_SING; break; + case GLP_EOBJLL: ret = LPX_E_OBJLL; break; + case GLP_EOBJUL: ret = LPX_E_OBJUL; break; + case GLP_EITLIM: ret = LPX_E_ITLIM; break; + case GLP_ETMLIM: ret = LPX_E_TMLIM; break; + case GLP_ENOPFS: ret = LPX_E_NOPFS; break; + case GLP_ENODFS: ret = LPX_E_NODFS; break; + default: xassert(ret != ret); + } + return ret; +} + +int lpx_exact(LPX *lp) +{ /* easy-to-use driver to the exact simplex method */ + glp_smcp parm; + int ret; + fill_smcp(lp, &parm); + ret = glp_exact(lp, &parm); + switch (ret) + { case 0: ret = LPX_E_OK; break; + case GLP_EBADB: + case GLP_ESING: + case GLP_EBOUND: + case GLP_EFAIL: ret = LPX_E_FAULT; break; + case GLP_EITLIM: ret = LPX_E_ITLIM; break; + case GLP_ETMLIM: ret = LPX_E_TMLIM; break; + default: xassert(ret != ret); + } + return ret; +} + +int lpx_get_status(glp_prob *lp) +{ /* retrieve generic status of basic solution */ + int status; + switch (glp_get_status(lp)) + { case GLP_OPT: status = LPX_OPT; break; + case GLP_FEAS: status = LPX_FEAS; break; + case GLP_INFEAS: status = LPX_INFEAS; break; + case GLP_NOFEAS: status = LPX_NOFEAS; break; + case GLP_UNBND: status = LPX_UNBND; break; + case GLP_UNDEF: status = LPX_UNDEF; break; + default: xassert(lp != lp); + } + return status; +} + +int lpx_get_prim_stat(glp_prob *lp) +{ /* retrieve status of primal basic solution */ + return glp_get_prim_stat(lp) - GLP_UNDEF + LPX_P_UNDEF; +} + +int lpx_get_dual_stat(glp_prob *lp) +{ /* retrieve status of dual basic solution */ + return glp_get_dual_stat(lp) - GLP_UNDEF + LPX_D_UNDEF; +} + +double lpx_get_obj_val(LPX *lp) +{ /* retrieve objective value (basic solution) */ + return glp_get_obj_val(lp); +} + +int lpx_get_row_stat(LPX *lp, int i) +{ /* retrieve row status (basic solution) */ + return glp_get_row_stat(lp, i) - GLP_BS + LPX_BS; +} + +double lpx_get_row_prim(LPX *lp, int i) +{ /* retrieve row primal value (basic solution) */ + return glp_get_row_prim(lp, i); +} + +double lpx_get_row_dual(LPX *lp, int i) +{ /* retrieve row dual value (basic solution) */ + return glp_get_row_dual(lp, i); +} + +void lpx_get_row_info(glp_prob *lp, int i, int *tagx, double *vx, + double *dx) +{ /* obtain row solution information */ + if (tagx != NULL) *tagx = lpx_get_row_stat(lp, i); + if (vx != NULL) *vx = lpx_get_row_prim(lp, i); + if (dx != NULL) *dx = lpx_get_row_dual(lp, i); + return; +} + +int lpx_get_col_stat(LPX *lp, int j) +{ /* retrieve column status (basic solution) */ + return glp_get_col_stat(lp, j) - GLP_BS + LPX_BS; +} + +double lpx_get_col_prim(LPX *lp, int j) +{ /* retrieve column primal value (basic solution) */ + return glp_get_col_prim(lp, j); +} + +double lpx_get_col_dual(glp_prob *lp, int j) +{ /* retrieve column dual value (basic solution) */ + return glp_get_col_dual(lp, j); +} + +void lpx_get_col_info(glp_prob *lp, int j, int *tagx, double *vx, + double *dx) +{ /* obtain column solution information */ + if (tagx != NULL) *tagx = lpx_get_col_stat(lp, j); + if (vx != NULL) *vx = lpx_get_col_prim(lp, j); + if (dx != NULL) *dx = lpx_get_col_dual(lp, j); + return; +} + +int lpx_get_ray_info(LPX *lp) +{ /* determine what causes primal unboundness */ + return glp_get_unbnd_ray(lp); +} + +void lpx_check_kkt(LPX *lp, int scaled, LPXKKT *kkt) +{ /* check Karush-Kuhn-Tucker conditions */ + int ae_ind, re_ind; + double ae_max, re_max; + xassert(scaled == scaled); + _glp_check_kkt(lp, GLP_SOL, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, + &re_ind); + kkt->pe_ae_max = ae_max; + kkt->pe_ae_row = ae_ind; + kkt->pe_re_max = re_max; + kkt->pe_re_row = re_ind; + if (re_max <= 1e-9) + kkt->pe_quality = 'H'; + else if (re_max <= 1e-6) + kkt->pe_quality = 'M'; + else if (re_max <= 1e-3) + kkt->pe_quality = 'L'; + else + kkt->pe_quality = '?'; + _glp_check_kkt(lp, GLP_SOL, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, + &re_ind); + kkt->pb_ae_max = ae_max; + kkt->pb_ae_ind = ae_ind; + kkt->pb_re_max = re_max; + kkt->pb_re_ind = re_ind; + if (re_max <= 1e-9) + kkt->pb_quality = 'H'; + else if (re_max <= 1e-6) + kkt->pb_quality = 'M'; + else if (re_max <= 1e-3) + kkt->pb_quality = 'L'; + else + kkt->pb_quality = '?'; + _glp_check_kkt(lp, GLP_SOL, GLP_KKT_DE, &ae_max, &ae_ind, &re_max, + &re_ind); + kkt->de_ae_max = ae_max; + if (ae_ind == 0) + kkt->de_ae_col = 0; + else + kkt->de_ae_col = ae_ind - lp->m; + kkt->de_re_max = re_max; + if (re_ind == 0) + kkt->de_re_col = 0; + else + kkt->de_re_col = ae_ind - lp->m; + if (re_max <= 1e-9) + kkt->de_quality = 'H'; + else if (re_max <= 1e-6) + kkt->de_quality = 'M'; + else if (re_max <= 1e-3) + kkt->de_quality = 'L'; + else + kkt->de_quality = '?'; + _glp_check_kkt(lp, GLP_SOL, GLP_KKT_DB, &ae_max, &ae_ind, &re_max, + &re_ind); + kkt->db_ae_max = ae_max; + kkt->db_ae_ind = ae_ind; + kkt->db_re_max = re_max; + kkt->db_re_ind = re_ind; + if (re_max <= 1e-9) + kkt->db_quality = 'H'; + else if (re_max <= 1e-6) + kkt->db_quality = 'M'; + else if (re_max <= 1e-3) + kkt->db_quality = 'L'; + else + kkt->db_quality = '?'; + kkt->cs_ae_max = 0.0, kkt->cs_ae_ind = 0; + kkt->cs_re_max = 0.0, kkt->cs_re_ind = 0; + kkt->cs_quality = 'H'; + return; +} + +int lpx_warm_up(LPX *lp) +{ /* "warm up" LP basis */ + int ret; + ret = glp_warm_up(lp); + if (ret == 0) + ret = LPX_E_OK; + else if (ret == GLP_EBADB) + ret = LPX_E_BADB; + else if (ret == GLP_ESING) + ret = LPX_E_SING; + else if (ret == GLP_ECOND) + ret = LPX_E_SING; + else + xassert(ret != ret); + return ret; +} + +int lpx_eval_tab_row(LPX *lp, int k, int ind[], double val[]) +{ /* compute row of the simplex tableau */ + return glp_eval_tab_row(lp, k, ind, val); +} + +int lpx_eval_tab_col(LPX *lp, int k, int ind[], double val[]) +{ /* compute column of the simplex tableau */ + return glp_eval_tab_col(lp, k, ind, val); +} + +int lpx_transform_row(LPX *lp, int len, int ind[], double val[]) +{ /* transform explicitly specified row */ + return glp_transform_row(lp, len, ind, val); +} + +int lpx_transform_col(LPX *lp, int len, int ind[], double val[]) +{ /* transform explicitly specified column */ + return glp_transform_col(lp, len, ind, val); +} + +int lpx_prim_ratio_test(LPX *lp, int len, const int ind[], + const double val[], int how, double tol) +{ /* perform primal ratio test */ + int piv; + piv = glp_prim_rtest(lp, len, ind, val, how, tol); + xassert(0 <= piv && piv <= len); + return piv == 0 ? 0 : ind[piv]; +} + +int lpx_dual_ratio_test(LPX *lp, int len, const int ind[], + const double val[], int how, double tol) +{ /* perform dual ratio test */ + int piv; + piv = glp_dual_rtest(lp, len, ind, val, how, tol); + xassert(0 <= piv && piv <= len); + return piv == 0 ? 0 : ind[piv]; +} + +int lpx_interior(LPX *lp) +{ /* easy-to-use driver to the interior-point method */ + int ret; + ret = glp_interior(lp, NULL); + switch (ret) + { case 0: ret = LPX_E_OK; break; + case GLP_EFAIL: ret = LPX_E_FAULT; break; + case GLP_ENOFEAS: ret = LPX_E_NOFEAS; break; + case GLP_ENOCVG: ret = LPX_E_NOCONV; break; + case GLP_EITLIM: ret = LPX_E_ITLIM; break; + case GLP_EINSTAB: ret = LPX_E_INSTAB; break; + default: xassert(ret != ret); + } + return ret; +} + +int lpx_ipt_status(glp_prob *lp) +{ /* retrieve status of interior-point solution */ + int status; + switch (glp_ipt_status(lp)) + { case GLP_UNDEF: status = LPX_T_UNDEF; break; + case GLP_OPT: status = LPX_T_OPT; break; + default: xassert(lp != lp); + } + return status; +} + +double lpx_ipt_obj_val(LPX *lp) +{ /* retrieve objective value (interior point) */ + return glp_ipt_obj_val(lp); +} + +double lpx_ipt_row_prim(LPX *lp, int i) +{ /* retrieve row primal value (interior point) */ + return glp_ipt_row_prim(lp, i); +} + +double lpx_ipt_row_dual(LPX *lp, int i) +{ /* retrieve row dual value (interior point) */ + return glp_ipt_row_dual(lp, i); +} + +double lpx_ipt_col_prim(LPX *lp, int j) +{ /* retrieve column primal value (interior point) */ + return glp_ipt_col_prim(lp, j); +} + +double lpx_ipt_col_dual(LPX *lp, int j) +{ /* retrieve column dual value (interior point) */ + return glp_ipt_col_dual(lp, j); +} + +void lpx_set_class(LPX *lp, int klass) +{ /* set problem class */ + xassert(lp == lp); + if (!(klass == LPX_LP || klass == LPX_MIP)) + xerror("lpx_set_class: invalid problem class\n"); + return; +} + +int lpx_get_class(LPX *lp) +{ /* determine problem klass */ + return glp_get_num_int(lp) == 0 ? LPX_LP : LPX_MIP; +} + +void lpx_set_col_kind(LPX *lp, int j, int kind) +{ /* set (change) column kind */ + glp_set_col_kind(lp, j, kind - LPX_CV + GLP_CV); + return; +} + +int lpx_get_col_kind(LPX *lp, int j) +{ /* retrieve column kind */ + return glp_get_col_kind(lp, j) == GLP_CV ? LPX_CV : LPX_IV; +} + +int lpx_get_num_int(LPX *lp) +{ /* retrieve number of integer columns */ + return glp_get_num_int(lp); +} + +int lpx_get_num_bin(LPX *lp) +{ /* retrieve number of binary columns */ + return glp_get_num_bin(lp); +} + +static int solve_mip(LPX *lp, int presolve) +{ glp_iocp parm; + int ret; + glp_init_iocp(&parm); + switch (lpx_get_int_parm(lp, LPX_K_MSGLEV)) + { case 0: parm.msg_lev = GLP_MSG_OFF; break; + case 1: parm.msg_lev = GLP_MSG_ERR; break; + case 2: parm.msg_lev = GLP_MSG_ON; break; + case 3: parm.msg_lev = GLP_MSG_ALL; break; + default: xassert(lp != lp); + } + switch (lpx_get_int_parm(lp, LPX_K_BRANCH)) + { case 0: parm.br_tech = GLP_BR_FFV; break; + case 1: parm.br_tech = GLP_BR_LFV; break; + case 2: parm.br_tech = GLP_BR_DTH; break; + case 3: parm.br_tech = GLP_BR_MFV; break; + default: xassert(lp != lp); + } + switch (lpx_get_int_parm(lp, LPX_K_BTRACK)) + { case 0: parm.bt_tech = GLP_BT_DFS; break; + case 1: parm.bt_tech = GLP_BT_BFS; break; + case 2: parm.bt_tech = GLP_BT_BPH; break; + case 3: parm.bt_tech = GLP_BT_BLB; break; + default: xassert(lp != lp); + } + parm.tol_int = lpx_get_real_parm(lp, LPX_K_TOLINT); + parm.tol_obj = lpx_get_real_parm(lp, LPX_K_TOLOBJ); + if (lpx_get_real_parm(lp, LPX_K_TMLIM) < 0.0 || + lpx_get_real_parm(lp, LPX_K_TMLIM) > 1e6) + parm.tm_lim = INT_MAX; + else + parm.tm_lim = + (int)(1000.0 * lpx_get_real_parm(lp, LPX_K_TMLIM)); + parm.mip_gap = lpx_get_real_parm(lp, LPX_K_MIPGAP); + if (lpx_get_int_parm(lp, LPX_K_USECUTS) & LPX_C_GOMORY) + parm.gmi_cuts = GLP_ON; + else + parm.gmi_cuts = GLP_OFF; + if (lpx_get_int_parm(lp, LPX_K_USECUTS) & LPX_C_MIR) + parm.mir_cuts = GLP_ON; + else + parm.mir_cuts = GLP_OFF; + if (lpx_get_int_parm(lp, LPX_K_USECUTS) & LPX_C_COVER) + parm.cov_cuts = GLP_ON; + else + parm.cov_cuts = GLP_OFF; + if (lpx_get_int_parm(lp, LPX_K_USECUTS) & LPX_C_CLIQUE) + parm.clq_cuts = GLP_ON; + else + parm.clq_cuts = GLP_OFF; + parm.presolve = presolve; + if (lpx_get_int_parm(lp, LPX_K_BINARIZE)) + parm.binarize = GLP_ON; + ret = glp_intopt(lp, &parm); + switch (ret) + { case 0: ret = LPX_E_OK; break; + case GLP_ENOPFS: ret = LPX_E_NOPFS; break; + case GLP_ENODFS: ret = LPX_E_NODFS; break; + case GLP_EBOUND: + case GLP_EROOT: ret = LPX_E_FAULT; break; + case GLP_EFAIL: ret = LPX_E_SING; break; + case GLP_EMIPGAP: ret = LPX_E_MIPGAP; break; + case GLP_ETMLIM: ret = LPX_E_TMLIM; break; + default: xassert(ret != ret); + } + return ret; +} + +int lpx_integer(LPX *lp) +{ /* easy-to-use driver to the branch-and-bound method */ + return solve_mip(lp, GLP_OFF); +} + +int lpx_intopt(LPX *lp) +{ /* easy-to-use driver to the branch-and-bound method */ + return solve_mip(lp, GLP_ON); +} + +int lpx_mip_status(glp_prob *lp) +{ /* retrieve status of MIP solution */ + int status; + switch (glp_mip_status(lp)) + { case GLP_UNDEF: status = LPX_I_UNDEF; break; + case GLP_OPT: status = LPX_I_OPT; break; + case GLP_FEAS: status = LPX_I_FEAS; break; + case GLP_NOFEAS: status = LPX_I_NOFEAS; break; + default: xassert(lp != lp); + } + return status; +} + +double lpx_mip_obj_val(LPX *lp) +{ /* retrieve objective value (MIP solution) */ + return glp_mip_obj_val(lp); +} + +double lpx_mip_row_val(LPX *lp, int i) +{ /* retrieve row value (MIP solution) */ + return glp_mip_row_val(lp, i); +} + +double lpx_mip_col_val(LPX *lp, int j) +{ /* retrieve column value (MIP solution) */ + return glp_mip_col_val(lp, j); +} + +void lpx_check_int(LPX *lp, LPXKKT *kkt) +{ /* check integer feasibility conditions */ + int ae_ind, re_ind; + double ae_max, re_max; + _glp_check_kkt(lp, GLP_MIP, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, + &re_ind); + kkt->pe_ae_max = ae_max; + kkt->pe_ae_row = ae_ind; + kkt->pe_re_max = re_max; + kkt->pe_re_row = re_ind; + if (re_max <= 1e-9) + kkt->pe_quality = 'H'; + else if (re_max <= 1e-6) + kkt->pe_quality = 'M'; + else if (re_max <= 1e-3) + kkt->pe_quality = 'L'; + else + kkt->pe_quality = '?'; + _glp_check_kkt(lp, GLP_MIP, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, + &re_ind); + kkt->pb_ae_max = ae_max; + kkt->pb_ae_ind = ae_ind; + kkt->pb_re_max = re_max; + kkt->pb_re_ind = re_ind; + if (re_max <= 1e-9) + kkt->pb_quality = 'H'; + else if (re_max <= 1e-6) + kkt->pb_quality = 'M'; + else if (re_max <= 1e-3) + kkt->pb_quality = 'L'; + else + kkt->pb_quality = '?'; + return; +} + +#if 1 /* 17/XI-2009 */ +static void reset_parms(LPX *lp) +{ /* reset control parameters to default values */ + struct LPXCPS *cps = lp->parms; + xassert(cps != NULL); + cps->msg_lev = 3; + cps->scale = 1; + cps->dual = 0; + cps->price = 1; + cps->relax = 0.07; + cps->tol_bnd = 1e-7; + cps->tol_dj = 1e-7; + cps->tol_piv = 1e-9; + cps->round = 0; + cps->obj_ll = -DBL_MAX; + cps->obj_ul = +DBL_MAX; + cps->it_lim = -1; +#if 0 /* 02/XII-2010 */ + lp->it_cnt = 0; +#endif + cps->tm_lim = -1.0; + cps->out_frq = 200; + cps->out_dly = 0.0; + cps->branch = 2; + cps->btrack = 3; + cps->tol_int = 1e-5; + cps->tol_obj = 1e-7; + cps->mps_info = 1; + cps->mps_obj = 2; + cps->mps_orig = 0; + cps->mps_wide = 1; + cps->mps_free = 0; + cps->mps_skip = 0; + cps->lpt_orig = 0; + cps->presol = 0; + cps->binarize = 0; + cps->use_cuts = 0; + cps->mip_gap = 0.0; + return; +} +#endif + +#if 1 /* 17/XI-2009 */ +static struct LPXCPS *access_parms(LPX *lp) +{ /* allocate and initialize control parameters, if necessary */ + if (lp->parms == NULL) + { lp->parms = xmalloc(sizeof(struct LPXCPS)); + reset_parms(lp); + } + return lp->parms; +} +#endif + +#if 1 /* 17/XI-2009 */ +void lpx_reset_parms(LPX *lp) +{ /* reset control parameters to default values */ + access_parms(lp); + reset_parms(lp); + return; +} +#endif + +void lpx_set_int_parm(LPX *lp, int parm, int val) +{ /* set (change) integer control parameter */ +#if 0 /* 17/XI-2009 */ + struct LPXCPS *cps = lp->cps; +#else + struct LPXCPS *cps = access_parms(lp); +#endif + switch (parm) + { case LPX_K_MSGLEV: + if (!(0 <= val && val <= 3)) + xerror("lpx_set_int_parm: MSGLEV = %d; invalid value\n", + val); + cps->msg_lev = val; + break; + case LPX_K_SCALE: + if (!(0 <= val && val <= 3)) + xerror("lpx_set_int_parm: SCALE = %d; invalid value\n", + val); + cps->scale = val; + break; + case LPX_K_DUAL: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: DUAL = %d; invalid value\n", + val); + cps->dual = val; + break; + case LPX_K_PRICE: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: PRICE = %d; invalid value\n", + val); + cps->price = val; + break; + case LPX_K_ROUND: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: ROUND = %d; invalid value\n", + val); + cps->round = val; + break; + case LPX_K_ITLIM: + cps->it_lim = val; + break; + case LPX_K_ITCNT: + lp->it_cnt = val; + break; + case LPX_K_OUTFRQ: + if (!(val > 0)) + xerror("lpx_set_int_parm: OUTFRQ = %d; invalid value\n", + val); + cps->out_frq = val; + break; + case LPX_K_BRANCH: + if (!(val == 0 || val == 1 || val == 2 || val == 3)) + xerror("lpx_set_int_parm: BRANCH = %d; invalid value\n", + val); + cps->branch = val; + break; + case LPX_K_BTRACK: + if (!(val == 0 || val == 1 || val == 2 || val == 3)) + xerror("lpx_set_int_parm: BTRACK = %d; invalid value\n", + val); + cps->btrack = val; + break; + case LPX_K_MPSINFO: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: MPSINFO = %d; invalid value\n", + val); + cps->mps_info = val; + break; + case LPX_K_MPSOBJ: + if (!(val == 0 || val == 1 || val == 2)) + xerror("lpx_set_int_parm: MPSOBJ = %d; invalid value\n", + val); + cps->mps_obj = val; + break; + case LPX_K_MPSORIG: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: MPSORIG = %d; invalid value\n", + val); + cps->mps_orig = val; + break; + case LPX_K_MPSWIDE: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: MPSWIDE = %d; invalid value\n", + val); + cps->mps_wide = val; + break; + case LPX_K_MPSFREE: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: MPSFREE = %d; invalid value\n", + val); + cps->mps_free = val; + break; + case LPX_K_MPSSKIP: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: MPSSKIP = %d; invalid value\n", + val); + cps->mps_skip = val; + break; + case LPX_K_LPTORIG: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: LPTORIG = %d; invalid value\n", + val); + cps->lpt_orig = val; + break; + case LPX_K_PRESOL: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: PRESOL = %d; invalid value\n", + val); + cps->presol = val; + break; + case LPX_K_BINARIZE: + if (!(val == 0 || val == 1)) + xerror("lpx_set_int_parm: BINARIZE = %d; invalid value\n" + , val); + cps->binarize = val; + break; + case LPX_K_USECUTS: + if (val & ~LPX_C_ALL) + xerror("lpx_set_int_parm: USECUTS = 0x%X; invalid value\n", + val); + cps->use_cuts = val; + break; + case LPX_K_BFTYPE: +#if 0 + if (!(1 <= val && val <= 3)) + xerror("lpx_set_int_parm: BFTYPE = %d; invalid value\n", + val); + cps->bf_type = val; +#else + { glp_bfcp parm; + glp_get_bfcp(lp, &parm); + switch (val) + { case 1: + parm.type = GLP_BF_FT; break; + case 2: + parm.type = GLP_BF_BG; break; + case 3: + parm.type = GLP_BF_GR; break; + default: + xerror("lpx_set_int_parm: BFTYPE = %d; invalid val" + "ue\n", val); + } + glp_set_bfcp(lp, &parm); + } +#endif + break; + default: + xerror("lpx_set_int_parm: parm = %d; invalid parameter\n", + parm); + } + return; +} + +int lpx_get_int_parm(LPX *lp, int parm) +{ /* query integer control parameter */ +#if 0 /* 17/XI-2009 */ + struct LPXCPS *cps = lp->cps; +#else + struct LPXCPS *cps = access_parms(lp); +#endif + int val = 0; + switch (parm) + { case LPX_K_MSGLEV: + val = cps->msg_lev; break; + case LPX_K_SCALE: + val = cps->scale; break; + case LPX_K_DUAL: + val = cps->dual; break; + case LPX_K_PRICE: + val = cps->price; break; + case LPX_K_ROUND: + val = cps->round; break; + case LPX_K_ITLIM: + val = cps->it_lim; break; + case LPX_K_ITCNT: + val = lp->it_cnt; break; + case LPX_K_OUTFRQ: + val = cps->out_frq; break; + case LPX_K_BRANCH: + val = cps->branch; break; + case LPX_K_BTRACK: + val = cps->btrack; break; + case LPX_K_MPSINFO: + val = cps->mps_info; break; + case LPX_K_MPSOBJ: + val = cps->mps_obj; break; + case LPX_K_MPSORIG: + val = cps->mps_orig; break; + case LPX_K_MPSWIDE: + val = cps->mps_wide; break; + case LPX_K_MPSFREE: + val = cps->mps_free; break; + case LPX_K_MPSSKIP: + val = cps->mps_skip; break; + case LPX_K_LPTORIG: + val = cps->lpt_orig; break; + case LPX_K_PRESOL: + val = cps->presol; break; + case LPX_K_BINARIZE: + val = cps->binarize; break; + case LPX_K_USECUTS: + val = cps->use_cuts; break; + case LPX_K_BFTYPE: +#if 0 + val = cps->bf_type; break; +#else + { glp_bfcp parm; + glp_get_bfcp(lp, &parm); + switch (parm.type) + { case GLP_BF_FT: + val = 1; break; + case GLP_BF_BG: + val = 2; break; + case GLP_BF_GR: + val = 3; break; + default: + xassert(lp != lp); + } + } + break; +#endif + default: + xerror("lpx_get_int_parm: parm = %d; invalid parameter\n", + parm); + } + return val; +} + +void lpx_set_real_parm(LPX *lp, int parm, double val) +{ /* set (change) real control parameter */ +#if 0 /* 17/XI-2009 */ + struct LPXCPS *cps = lp->cps; +#else + struct LPXCPS *cps = access_parms(lp); +#endif + switch (parm) + { case LPX_K_RELAX: + if (!(0.0 <= val && val <= 1.0)) + xerror("lpx_set_real_parm: RELAX = %g; invalid value\n", + val); + cps->relax = val; + break; + case LPX_K_TOLBND: + if (!(DBL_EPSILON <= val && val <= 0.001)) + xerror("lpx_set_real_parm: TOLBND = %g; invalid value\n", + val); +#if 0 + if (cps->tol_bnd > val) + { /* invalidate the basic solution */ + lp->p_stat = LPX_P_UNDEF; + lp->d_stat = LPX_D_UNDEF; + } +#endif + cps->tol_bnd = val; + break; + case LPX_K_TOLDJ: + if (!(DBL_EPSILON <= val && val <= 0.001)) + xerror("lpx_set_real_parm: TOLDJ = %g; invalid value\n", + val); +#if 0 + if (cps->tol_dj > val) + { /* invalidate the basic solution */ + lp->p_stat = LPX_P_UNDEF; + lp->d_stat = LPX_D_UNDEF; + } +#endif + cps->tol_dj = val; + break; + case LPX_K_TOLPIV: + if (!(DBL_EPSILON <= val && val <= 0.001)) + xerror("lpx_set_real_parm: TOLPIV = %g; invalid value\n", + val); + cps->tol_piv = val; + break; + case LPX_K_OBJLL: + cps->obj_ll = val; + break; + case LPX_K_OBJUL: + cps->obj_ul = val; + break; + case LPX_K_TMLIM: + cps->tm_lim = val; + break; + case LPX_K_OUTDLY: + cps->out_dly = val; + break; + case LPX_K_TOLINT: + if (!(DBL_EPSILON <= val && val <= 0.001)) + xerror("lpx_set_real_parm: TOLINT = %g; invalid value\n", + val); + cps->tol_int = val; + break; + case LPX_K_TOLOBJ: + if (!(DBL_EPSILON <= val && val <= 0.001)) + xerror("lpx_set_real_parm: TOLOBJ = %g; invalid value\n", + val); + cps->tol_obj = val; + break; + case LPX_K_MIPGAP: + if (val < 0.0) + xerror("lpx_set_real_parm: MIPGAP = %g; invalid value\n", + val); + cps->mip_gap = val; + break; + default: + xerror("lpx_set_real_parm: parm = %d; invalid parameter\n", + parm); + } + return; +} + +double lpx_get_real_parm(LPX *lp, int parm) +{ /* query real control parameter */ +#if 0 /* 17/XI-2009 */ + struct LPXCPS *cps = lp->cps; +#else + struct LPXCPS *cps = access_parms(lp); +#endif + double val = 0.0; + switch (parm) + { case LPX_K_RELAX: + val = cps->relax; + break; + case LPX_K_TOLBND: + val = cps->tol_bnd; + break; + case LPX_K_TOLDJ: + val = cps->tol_dj; + break; + case LPX_K_TOLPIV: + val = cps->tol_piv; + break; + case LPX_K_OBJLL: + val = cps->obj_ll; + break; + case LPX_K_OBJUL: + val = cps->obj_ul; + break; + case LPX_K_TMLIM: + val = cps->tm_lim; + break; + case LPX_K_OUTDLY: + val = cps->out_dly; + break; + case LPX_K_TOLINT: + val = cps->tol_int; + break; + case LPX_K_TOLOBJ: + val = cps->tol_obj; + break; + case LPX_K_MIPGAP: + val = cps->mip_gap; + break; + default: + xerror("lpx_get_real_parm: parm = %d; invalid parameter\n", + parm); + } + return val; +} + +LPX *lpx_read_mps(const char *fname) +{ /* read problem data in fixed MPS format */ + LPX *lp = lpx_create_prob(); + if (glp_read_mps(lp, GLP_MPS_DECK, NULL, fname)) + lpx_delete_prob(lp), lp = NULL; + return lp; +} + +int lpx_write_mps(LPX *lp, const char *fname) +{ /* write problem data in fixed MPS format */ + return glp_write_mps(lp, GLP_MPS_DECK, NULL, fname); +} + +int lpx_read_bas(LPX *lp, const char *fname) +{ /* read LP basis in fixed MPS format */ +#if 0 /* 13/IV-2009 */ + return read_bas(lp, fname); +#else + xassert(lp == lp); + xassert(fname == fname); + xerror("lpx_read_bas: operation not supported\n"); + return 0; +#endif +} + +int lpx_write_bas(LPX *lp, const char *fname) +{ /* write LP basis in fixed MPS format */ +#if 0 /* 13/IV-2009 */ + return write_bas(lp, fname); +#else + xassert(lp == lp); + xassert(fname == fname); + xerror("lpx_write_bas: operation not supported\n"); + return 0; +#endif +} + +LPX *lpx_read_freemps(const char *fname) +{ /* read problem data in free MPS format */ + LPX *lp = lpx_create_prob(); + if (glp_read_mps(lp, GLP_MPS_FILE, NULL, fname)) + lpx_delete_prob(lp), lp = NULL; + return lp; +} + +int lpx_write_freemps(LPX *lp, const char *fname) +{ /* write problem data in free MPS format */ + return glp_write_mps(lp, GLP_MPS_FILE, NULL, fname); +} + +LPX *lpx_read_cpxlp(const char *fname) +{ /* read problem data in CPLEX LP format */ + LPX *lp; + lp = lpx_create_prob(); + if (glp_read_lp(lp, NULL, fname)) + lpx_delete_prob(lp), lp = NULL; + return lp; +} + +int lpx_write_cpxlp(LPX *lp, const char *fname) +{ /* write problem data in CPLEX LP format */ + return glp_write_lp(lp, NULL, fname); +} + +LPX *lpx_read_model(const char *model, const char *data, const char + *output) +{ /* read LP/MIP model written in GNU MathProg language */ + LPX *lp = NULL; + glp_tran *tran; + /* allocate the translator workspace */ + tran = glp_mpl_alloc_wksp(); + /* read model section and optional data section */ + if (glp_mpl_read_model(tran, model, data != NULL)) goto done; + /* read separate data section, if required */ + if (data != NULL) + if (glp_mpl_read_data(tran, data)) goto done; + /* generate the model */ + if (glp_mpl_generate(tran, output)) goto done; + /* build the problem instance from the model */ + lp = glp_create_prob(); + glp_mpl_build_prob(tran, lp); +done: /* free the translator workspace */ + glp_mpl_free_wksp(tran); + /* bring the problem object to the calling program */ + return lp; +} + +int lpx_print_prob(LPX *lp, const char *fname) +{ /* write problem data in plain text format */ + return glp_write_lp(lp, NULL, fname); +} + +int lpx_print_sol(LPX *lp, const char *fname) +{ /* write LP problem solution in printable format */ + return glp_print_sol(lp, fname); +} + +int lpx_print_sens_bnds(LPX *lp, const char *fname) +{ /* write bounds sensitivity information */ + if (glp_get_status(lp) == GLP_OPT && !glp_bf_exists(lp)) + glp_factorize(lp); + return glp_print_ranges(lp, 0, NULL, 0, fname); +} + +int lpx_print_ips(LPX *lp, const char *fname) +{ /* write interior point solution in printable format */ + return glp_print_ipt(lp, fname); +} + +int lpx_print_mip(LPX *lp, const char *fname) +{ /* write MIP problem solution in printable format */ + return glp_print_mip(lp, fname); +} + +int lpx_is_b_avail(glp_prob *lp) +{ /* check if LP basis is available */ + return glp_bf_exists(lp); +} + +int lpx_main(int argc, const char *argv[]) +{ /* stand-alone LP/MIP solver */ + return glp_main(argc, argv); +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplpx02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplpx02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,264 @@ +/* glplpx02.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* lpx_put_solution - store basic solution components +* +* SYNOPSIS +* +* void lpx_put_solution(glp_prob *lp, int inval, const int *p_stat, +* const int *d_stat, const double *obj_val, const int r_stat[], +* const double r_prim[], const double r_dual[], const int c_stat[], +* const double c_prim[], const double c_dual[]) +* +* DESCRIPTION +* +* The routine lpx_put_solution stores basic solution components to the +* specified problem object. +* +* The parameter inval is the basis factorization invalidity flag. +* If this flag is clear, the current status of the basis factorization +* remains unchanged. If this flag is set, the routine invalidates the +* basis factorization. +* +* The parameter p_stat is a pointer to the status of primal basic +* solution, which should be specified as follows: +* +* GLP_UNDEF - primal solution is undefined; +* GLP_FEAS - primal solution is feasible; +* GLP_INFEAS - primal solution is infeasible; +* GLP_NOFEAS - no primal feasible solution exists. +* +* If the parameter p_stat is NULL, the current status of primal basic +* solution remains unchanged. +* +* The parameter d_stat is a pointer to the status of dual basic +* solution, which should be specified as follows: +* +* GLP_UNDEF - dual solution is undefined; +* GLP_FEAS - dual solution is feasible; +* GLP_INFEAS - dual solution is infeasible; +* GLP_NOFEAS - no dual feasible solution exists. +* +* If the parameter d_stat is NULL, the current status of dual basic +* solution remains unchanged. +* +* The parameter obj_val is a pointer to the objective function value. +* If it is NULL, the current value of the objective function remains +* unchanged. +* +* The array element r_stat[i], 1 <= i <= m (where m is the number of +* rows in the problem object), specifies the status of i-th auxiliary +* variable, which should be specified as follows: +* +* GLP_BS - basic variable; +* GLP_NL - non-basic variable on lower bound; +* GLP_NU - non-basic variable on upper bound; +* GLP_NF - non-basic free variable; +* GLP_NS - non-basic fixed variable. +* +* If the parameter r_stat is NULL, the current statuses of auxiliary +* variables remain unchanged. +* +* The array element r_prim[i], 1 <= i <= m (where m is the number of +* rows in the problem object), specifies a primal value of i-th +* auxiliary variable. If the parameter r_prim is NULL, the current +* primal values of auxiliary variables remain unchanged. +* +* The array element r_dual[i], 1 <= i <= m (where m is the number of +* rows in the problem object), specifies a dual value (reduced cost) +* of i-th auxiliary variable. If the parameter r_dual is NULL, the +* current dual values of auxiliary variables remain unchanged. +* +* The array element c_stat[j], 1 <= j <= n (where n is the number of +* columns in the problem object), specifies the status of j-th +* structural variable, which should be specified as follows: +* +* GLP_BS - basic variable; +* GLP_NL - non-basic variable on lower bound; +* GLP_NU - non-basic variable on upper bound; +* GLP_NF - non-basic free variable; +* GLP_NS - non-basic fixed variable. +* +* If the parameter c_stat is NULL, the current statuses of structural +* variables remain unchanged. +* +* The array element c_prim[j], 1 <= j <= n (where n is the number of +* columns in the problem object), specifies a primal value of j-th +* structural variable. If the parameter c_prim is NULL, the current +* primal values of structural variables remain unchanged. +* +* The array element c_dual[j], 1 <= j <= n (where n is the number of +* columns in the problem object), specifies a dual value (reduced cost) +* of j-th structural variable. If the parameter c_dual is NULL, the +* current dual values of structural variables remain unchanged. */ + +void lpx_put_solution(glp_prob *lp, int inval, const int *p_stat, + const int *d_stat, const double *obj_val, const int r_stat[], + const double r_prim[], const double r_dual[], const int c_stat[], + const double c_prim[], const double c_dual[]) +{ GLPROW *row; + GLPCOL *col; + int i, j; + /* invalidate the basis factorization, if required */ + if (inval) lp->valid = 0; + /* store primal status */ + if (p_stat != NULL) + { if (!(*p_stat == GLP_UNDEF || *p_stat == GLP_FEAS || + *p_stat == GLP_INFEAS || *p_stat == GLP_NOFEAS)) + xerror("lpx_put_solution: p_stat = %d; invalid primal statu" + "s\n", *p_stat); + lp->pbs_stat = *p_stat; + } + /* store dual status */ + if (d_stat != NULL) + { if (!(*d_stat == GLP_UNDEF || *d_stat == GLP_FEAS || + *d_stat == GLP_INFEAS || *d_stat == GLP_NOFEAS)) + xerror("lpx_put_solution: d_stat = %d; invalid dual status " + "\n", *d_stat); + lp->dbs_stat = *d_stat; + } + /* store objective function value */ + if (obj_val != NULL) lp->obj_val = *obj_val; + /* store row solution components */ + for (i = 1; i <= lp->m; i++) + { row = lp->row[i]; + if (r_stat != NULL) + { if (!(r_stat[i] == GLP_BS || + row->type == GLP_FR && r_stat[i] == GLP_NF || + row->type == GLP_LO && r_stat[i] == GLP_NL || + row->type == GLP_UP && r_stat[i] == GLP_NU || + row->type == GLP_DB && r_stat[i] == GLP_NL || + row->type == GLP_DB && r_stat[i] == GLP_NU || + row->type == GLP_FX && r_stat[i] == GLP_NS)) + xerror("lpx_put_solution: r_stat[%d] = %d; invalid row s" + "tatus\n", i, r_stat[i]); + row->stat = r_stat[i]; + } + if (r_prim != NULL) row->prim = r_prim[i]; + if (r_dual != NULL) row->dual = r_dual[i]; + } + /* store column solution components */ + for (j = 1; j <= lp->n; j++) + { col = lp->col[j]; + if (c_stat != NULL) + { if (!(c_stat[j] == GLP_BS || + col->type == GLP_FR && c_stat[j] == GLP_NF || + col->type == GLP_LO && c_stat[j] == GLP_NL || + col->type == GLP_UP && c_stat[j] == GLP_NU || + col->type == GLP_DB && c_stat[j] == GLP_NL || + col->type == GLP_DB && c_stat[j] == GLP_NU || + col->type == GLP_FX && c_stat[j] == GLP_NS)) + xerror("lpx_put_solution: c_stat[%d] = %d; invalid colum" + "n status\n", j, c_stat[j]); + col->stat = c_stat[j]; + } + if (c_prim != NULL) col->prim = c_prim[j]; + if (c_dual != NULL) col->dual = c_dual[j]; + } + return; +} + +/*---------------------------------------------------------------------- +-- lpx_put_mip_soln - store mixed integer solution components. +-- +-- *Synopsis* +-- +-- #include "glplpx.h" +-- void lpx_put_mip_soln(glp_prob *lp, int i_stat, double row_mipx[], +-- double col_mipx[]); +-- +-- *Description* +-- +-- The routine lpx_put_mip_soln stores solution components obtained by +-- branch-and-bound solver into the specified problem object. +-- +-- NOTE: This routine is intended for internal use only. */ + +void lpx_put_mip_soln(glp_prob *lp, int i_stat, double row_mipx[], + double col_mipx[]) +{ GLPROW *row; + GLPCOL *col; + int i, j; + double sum; + /* store mixed integer status */ +#if 0 + if (!(i_stat == LPX_I_UNDEF || i_stat == LPX_I_OPT || + i_stat == LPX_I_FEAS || i_stat == LPX_I_NOFEAS)) + fault("lpx_put_mip_soln: i_stat = %d; invalid mixed integer st" + "atus", i_stat); + lp->i_stat = i_stat; +#else + switch (i_stat) + { case LPX_I_UNDEF: + lp->mip_stat = GLP_UNDEF; break; + case LPX_I_OPT: + lp->mip_stat = GLP_OPT; break; + case LPX_I_FEAS: + lp->mip_stat = GLP_FEAS; break; + case LPX_I_NOFEAS: + lp->mip_stat = GLP_NOFEAS; break; + default: + xerror("lpx_put_mip_soln: i_stat = %d; invalid mixed intege" + "r status\n", i_stat); + } +#endif + /* store row solution components */ + if (row_mipx != NULL) + { for (i = 1; i <= lp->m; i++) + { row = lp->row[i]; + row->mipx = row_mipx[i]; + } + } + /* store column solution components */ + if (col_mipx != NULL) + { for (j = 1; j <= lp->n; j++) + { col = lp->col[j]; + col->mipx = col_mipx[j]; + } + } + /* if the solution is claimed to be integer feasible, check it */ + if (lp->mip_stat == GLP_OPT || lp->mip_stat == GLP_FEAS) + { for (j = 1; j <= lp->n; j++) + { col = lp->col[j]; + if (col->kind == GLP_IV && col->mipx != floor(col->mipx)) + xerror("lpx_put_mip_soln: col_mipx[%d] = %.*g; must be i" + "ntegral\n", j, DBL_DIG, col->mipx); + } + } + /* compute the objective function value */ + sum = lp->c0; + for (j = 1; j <= lp->n; j++) + { col = lp->col[j]; + sum += col->coef * col->mipx; + } + lp->mip_obj = sum; + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplpx03.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplpx03.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,302 @@ +/* glplpx03.c (OPB format) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Author: Oscar Gustafsson . +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_ERRNO +#define _GLPSTD_STDIO +#include "glpapi.h" +#if 0 /* 24/XII-2009; by mao */ +#include "glpipp.h" +#endif + +/*---------------------------------------------------------------------- +-- lpx_write_pb - write problem data in (normalized) OPB format. +-- +-- *Synopsis* +-- +-- #include "glplpx.h" +-- int lpx_write_pb(LPX *lp, const char *fname, int normalized, +-- int binarize); +-- +-- *Description* +-- +-- The routine lpx_write_pb writes problem data in OPB format +-- to an output text file whose name is the character string fname. +-- If normalized is non-zero the output will be generated in a +-- normalized form with sequentially numbered variables, x1, x2 etc. +-- If binarize, any integer variable will be repalzec by binary ones, +-- see ipp_binarize +-- +-- *Returns* +-- +-- If the operation was successful, the routine returns zero. Otherwise +-- the routine prints an error message and returns non-zero. */ + +#if 1 /* 24/XII-2009; by mao (disabled, because IPP was removed) */ +int lpx_write_pb(LPX *lp, const char *fname, int normalized, + int binarize) +{ xassert(lp == lp); + xassert(fname == fname); + xassert(normalized == normalized); + xassert(binarize == binarize); + xprintf("lpx_write_pb: sorry, currently this operation is not ava" + "ilable\n"); + return 1; +} +#else +int lpx_write_pb(LPX *lp, const char *fname, int normalized, + int binarize) +{ + FILE* fp; + int m,n,i,j,k,o,nonfree=0, obj_dir, dbl, *ndx, row_type, emptylhs=0; + double coeff, *val, bound, constant/*=0.0*/; + char* objconstname = "dummy_one"; + char* emptylhsname = "dummy_zero"; + + /* Variables needed for possible binarization */ + /*LPX* tlp;*/ + IPP *ipp = NULL; + /*tlp=lp;*/ + + if(binarize) /* Transform integer variables to binary ones */ + { + ipp = ipp_create_wksp(); + ipp_load_orig(ipp, lp); + ipp_binarize(ipp); + lp = ipp_build_prob(ipp); + } + fp = fopen(fname, "w"); + + if(fp!= NULL) + { + xprintf( + "lpx_write_pb: writing problem in %sOPB format to `%s'...\n", + (normalized?"normalized ":""), fname); + + m = glp_get_num_rows(lp); + n = glp_get_num_cols(lp); + for(i=1;i<=m;i++) + { + switch(glp_get_row_type(lp,i)) + { + case GLP_LO: + case GLP_UP: + case GLP_FX: + { + nonfree += 1; + break; + } + case GLP_DB: + { + nonfree += 2; + break; + } + } + } + constant=glp_get_obj_coef(lp,0); + fprintf(fp,"* #variables = %d #constraints = %d\n", + n + (constant == 0?1:0), nonfree + (constant == 0?1:0)); + /* Objective function */ + obj_dir = glp_get_obj_dir(lp); + fprintf(fp,"min: "); + for(i=1;i<=n;i++) + { + coeff = glp_get_obj_coef(lp,i); + if(coeff != 0.0) + { + if(obj_dir == GLP_MAX) + coeff=-coeff; + if(normalized) + fprintf(fp, " %d x%d", (int)coeff, i); + else + fprintf(fp, " %d*%s", (int)coeff, + glp_get_col_name(lp,i)); + + } + } + if(constant) + { + if(normalized) + fprintf(fp, " %d x%d", (int)constant, n+1); + else + fprintf(fp, " %d*%s", (int)constant, objconstname); + } + fprintf(fp,";\n"); + + if(normalized && !binarize) /* Name substitution */ + { + fprintf(fp,"* Variable name substitution:\n"); + for(j=1;j<=n;j++) + { + fprintf(fp, "* x%d = %s\n", j, glp_get_col_name(lp,j)); + } + if(constant) + fprintf(fp, "* x%d = %s\n", n+1, objconstname); + } + + ndx = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); + + /* Constraints */ + for(j=1;j<=m;j++) + { + row_type=glp_get_row_type(lp,j); + if(row_type!=GLP_FR) + { + if(row_type == GLP_DB) + { + dbl=2; + row_type = GLP_UP; + } + else + { + dbl=1; + } + k=glp_get_mat_row(lp, j, ndx, val); + for(o=1;o<=dbl;o++) + { + if(o==2) + { + row_type = GLP_LO; + } + if(k==0) /* Empty LHS */ + { + emptylhs = 1; + if(normalized) + { + fprintf(fp, "0 x%d ", n+2); + } + else + { + fprintf(fp, "0*%s ", emptylhsname); + } + } + + for(i=1;i<=k;i++) + { + if(val[i] != 0.0) + { + + if(normalized) + { + fprintf(fp, "%d x%d ", + (row_type==GLP_UP)?(-(int)val[i]):((int)val[i]), ndx[i]); + } + else + { + fprintf(fp, "%d*%s ", (int)val[i], + glp_get_col_name(lp,ndx[i])); + } + } + } + switch(row_type) + { + case GLP_LO: + { + fprintf(fp, ">="); + bound = glp_get_row_lb(lp,j); + break; + } + case GLP_UP: + { + if(normalized) + { + fprintf(fp, ">="); + bound = -glp_get_row_ub(lp,j); + } + else + { + fprintf(fp, "<="); + bound = glp_get_row_ub(lp,j); + } + + break; + } + case GLP_FX: + { + fprintf(fp, "="); + bound = glp_get_row_lb(lp,j); + break; + } + } + fprintf(fp," %d;\n",(int)bound); + } + } + } + xfree(ndx); + xfree(val); + + if(constant) + { + xprintf( + "lpx_write_pb: adding constant objective function variable\n"); + + if(normalized) + fprintf(fp, "1 x%d = 1;\n", n+1); + else + fprintf(fp, "1*%s = 1;\n", objconstname); + } + if(emptylhs) + { + xprintf( + "lpx_write_pb: adding dummy variable for empty left-hand si" + "de constraint\n"); + + if(normalized) + fprintf(fp, "1 x%d = 0;\n", n+2); + else + fprintf(fp, "1*%s = 0;\n", emptylhsname); + } + + } + else + { + xprintf("Problems opening file for writing: %s\n", fname); + return(1); + } + fflush(fp); + if (ferror(fp)) + { xprintf("lpx_write_pb: can't write to `%s' - %s\n", fname, + strerror(errno)); + goto fail; + } + fclose(fp); + + + if(binarize) + { + /* delete the resultant problem object */ + if (lp != NULL) lpx_delete_prob(lp); + /* delete MIP presolver workspace */ + if (ipp != NULL) ipp_delete_wksp(ipp); + /*lp=tlp;*/ + } + return 0; + fail: if (fp != NULL) fclose(fp); + return 1; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpluf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpluf.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1846 @@ +/* glpluf.c (LU-factorization) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glpluf.h" +#define xfault xerror + +/* CAUTION: DO NOT CHANGE THE LIMIT BELOW */ + +#define N_MAX 100000000 /* = 100*10^6 */ +/* maximal order of the original matrix */ + +/*********************************************************************** +* NAME +* +* luf_create_it - create LU-factorization +* +* SYNOPSIS +* +* #include "glpluf.h" +* LUF *luf_create_it(void); +* +* DESCRIPTION +* +* The routine luf_create_it creates a program object, which represents +* LU-factorization of a square matrix. +* +* RETURNS +* +* The routine luf_create_it returns a pointer to the object created. */ + +LUF *luf_create_it(void) +{ LUF *luf; + luf = xmalloc(sizeof(LUF)); + luf->n_max = luf->n = 0; + luf->valid = 0; + luf->fr_ptr = luf->fr_len = NULL; + luf->fc_ptr = luf->fc_len = NULL; + luf->vr_ptr = luf->vr_len = luf->vr_cap = NULL; + luf->vr_piv = NULL; + luf->vc_ptr = luf->vc_len = luf->vc_cap = NULL; + luf->pp_row = luf->pp_col = NULL; + luf->qq_row = luf->qq_col = NULL; + luf->sv_size = 0; + luf->sv_beg = luf->sv_end = 0; + luf->sv_ind = NULL; + luf->sv_val = NULL; + luf->sv_head = luf->sv_tail = 0; + luf->sv_prev = luf->sv_next = NULL; + luf->vr_max = NULL; + luf->rs_head = luf->rs_prev = luf->rs_next = NULL; + luf->cs_head = luf->cs_prev = luf->cs_next = NULL; + luf->flag = NULL; + luf->work = NULL; + luf->new_sva = 0; + luf->piv_tol = 0.10; + luf->piv_lim = 4; + luf->suhl = 1; + luf->eps_tol = 1e-15; + luf->max_gro = 1e+10; + luf->nnz_a = luf->nnz_f = luf->nnz_v = 0; + luf->max_a = luf->big_v = 0.0; + luf->rank = 0; + return luf; +} + +/*********************************************************************** +* NAME +* +* luf_defrag_sva - defragment the sparse vector area +* +* SYNOPSIS +* +* #include "glpluf.h" +* void luf_defrag_sva(LUF *luf); +* +* DESCRIPTION +* +* The routine luf_defrag_sva defragments the sparse vector area (SVA) +* gathering all unused locations in one continuous extent. In order to +* do that the routine moves all unused locations from the left part of +* SVA (which contains rows and columns of the matrix V) to the middle +* part (which contains free locations). This is attained by relocating +* elements of rows and columns of the matrix V toward the beginning of +* the left part. +* +* NOTE that this "garbage collection" involves changing row and column +* pointers of the matrix V. */ + +void luf_defrag_sva(LUF *luf) +{ int n = luf->n; + int *vr_ptr = luf->vr_ptr; + int *vr_len = luf->vr_len; + int *vr_cap = luf->vr_cap; + int *vc_ptr = luf->vc_ptr; + int *vc_len = luf->vc_len; + int *vc_cap = luf->vc_cap; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + int *sv_next = luf->sv_next; + int sv_beg = 1; + int i, j, k; + /* skip rows and columns, which do not need to be relocated */ + for (k = luf->sv_head; k != 0; k = sv_next[k]) + { if (k <= n) + { /* i-th row of the matrix V */ + i = k; + if (vr_ptr[i] != sv_beg) break; + vr_cap[i] = vr_len[i]; + sv_beg += vr_cap[i]; + } + else + { /* j-th column of the matrix V */ + j = k - n; + if (vc_ptr[j] != sv_beg) break; + vc_cap[j] = vc_len[j]; + sv_beg += vc_cap[j]; + } + } + /* relocate other rows and columns in order to gather all unused + locations in one continuous extent */ + for (k = k; k != 0; k = sv_next[k]) + { if (k <= n) + { /* i-th row of the matrix V */ + i = k; + memmove(&sv_ind[sv_beg], &sv_ind[vr_ptr[i]], + vr_len[i] * sizeof(int)); + memmove(&sv_val[sv_beg], &sv_val[vr_ptr[i]], + vr_len[i] * sizeof(double)); + vr_ptr[i] = sv_beg; + vr_cap[i] = vr_len[i]; + sv_beg += vr_cap[i]; + } + else + { /* j-th column of the matrix V */ + j = k - n; + memmove(&sv_ind[sv_beg], &sv_ind[vc_ptr[j]], + vc_len[j] * sizeof(int)); + memmove(&sv_val[sv_beg], &sv_val[vc_ptr[j]], + vc_len[j] * sizeof(double)); + vc_ptr[j] = sv_beg; + vc_cap[j] = vc_len[j]; + sv_beg += vc_cap[j]; + } + } + /* set new pointer to the beginning of the free part */ + luf->sv_beg = sv_beg; + return; +} + +/*********************************************************************** +* NAME +* +* luf_enlarge_row - enlarge row capacity +* +* SYNOPSIS +* +* #include "glpluf.h" +* int luf_enlarge_row(LUF *luf, int i, int cap); +* +* DESCRIPTION +* +* The routine luf_enlarge_row enlarges capacity of the i-th row of the +* matrix V to cap locations (assuming that its current capacity is less +* than cap). In order to do that the routine relocates elements of the +* i-th row to the end of the left part of SVA (which contains rows and +* columns of the matrix V) and then expands the left part by allocating +* cap free locations from the free part. If there are less than cap +* free locations, the routine defragments the sparse vector area. +* +* Due to "garbage collection" this operation may change row and column +* pointers of the matrix V. +* +* RETURNS +* +* If no error occured, the routine returns zero. Otherwise, in case of +* overflow of the sparse vector area, the routine returns non-zero. */ + +int luf_enlarge_row(LUF *luf, int i, int cap) +{ int n = luf->n; + int *vr_ptr = luf->vr_ptr; + int *vr_len = luf->vr_len; + int *vr_cap = luf->vr_cap; + int *vc_cap = luf->vc_cap; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + int *sv_prev = luf->sv_prev; + int *sv_next = luf->sv_next; + int ret = 0; + int cur, k, kk; + xassert(1 <= i && i <= n); + xassert(vr_cap[i] < cap); + /* if there are less than cap free locations, defragment SVA */ + if (luf->sv_end - luf->sv_beg < cap) + { luf_defrag_sva(luf); + if (luf->sv_end - luf->sv_beg < cap) + { ret = 1; + goto done; + } + } + /* save current capacity of the i-th row */ + cur = vr_cap[i]; + /* copy existing elements to the beginning of the free part */ + memmove(&sv_ind[luf->sv_beg], &sv_ind[vr_ptr[i]], + vr_len[i] * sizeof(int)); + memmove(&sv_val[luf->sv_beg], &sv_val[vr_ptr[i]], + vr_len[i] * sizeof(double)); + /* set new pointer and new capacity of the i-th row */ + vr_ptr[i] = luf->sv_beg; + vr_cap[i] = cap; + /* set new pointer to the beginning of the free part */ + luf->sv_beg += cap; + /* now the i-th row starts in the rightmost location among other + rows and columns of the matrix V, so its node should be moved + to the end of the row/column linked list */ + k = i; + /* remove the i-th row node from the linked list */ + if (sv_prev[k] == 0) + luf->sv_head = sv_next[k]; + else + { /* capacity of the previous row/column can be increased at the + expense of old locations of the i-th row */ + kk = sv_prev[k]; + if (kk <= n) vr_cap[kk] += cur; else vc_cap[kk-n] += cur; + sv_next[sv_prev[k]] = sv_next[k]; + } + if (sv_next[k] == 0) + luf->sv_tail = sv_prev[k]; + else + sv_prev[sv_next[k]] = sv_prev[k]; + /* insert the i-th row node to the end of the linked list */ + sv_prev[k] = luf->sv_tail; + sv_next[k] = 0; + if (sv_prev[k] == 0) + luf->sv_head = k; + else + sv_next[sv_prev[k]] = k; + luf->sv_tail = k; +done: return ret; +} + +/*********************************************************************** +* NAME +* +* luf_enlarge_col - enlarge column capacity +* +* SYNOPSIS +* +* #include "glpluf.h" +* int luf_enlarge_col(LUF *luf, int j, int cap); +* +* DESCRIPTION +* +* The routine luf_enlarge_col enlarges capacity of the j-th column of +* the matrix V to cap locations (assuming that its current capacity is +* less than cap). In order to do that the routine relocates elements +* of the j-th column to the end of the left part of SVA (which contains +* rows and columns of the matrix V) and then expands the left part by +* allocating cap free locations from the free part. If there are less +* than cap free locations, the routine defragments the sparse vector +* area. +* +* Due to "garbage collection" this operation may change row and column +* pointers of the matrix V. +* +* RETURNS +* +* If no error occured, the routine returns zero. Otherwise, in case of +* overflow of the sparse vector area, the routine returns non-zero. */ + +int luf_enlarge_col(LUF *luf, int j, int cap) +{ int n = luf->n; + int *vr_cap = luf->vr_cap; + int *vc_ptr = luf->vc_ptr; + int *vc_len = luf->vc_len; + int *vc_cap = luf->vc_cap; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + int *sv_prev = luf->sv_prev; + int *sv_next = luf->sv_next; + int ret = 0; + int cur, k, kk; + xassert(1 <= j && j <= n); + xassert(vc_cap[j] < cap); + /* if there are less than cap free locations, defragment SVA */ + if (luf->sv_end - luf->sv_beg < cap) + { luf_defrag_sva(luf); + if (luf->sv_end - luf->sv_beg < cap) + { ret = 1; + goto done; + } + } + /* save current capacity of the j-th column */ + cur = vc_cap[j]; + /* copy existing elements to the beginning of the free part */ + memmove(&sv_ind[luf->sv_beg], &sv_ind[vc_ptr[j]], + vc_len[j] * sizeof(int)); + memmove(&sv_val[luf->sv_beg], &sv_val[vc_ptr[j]], + vc_len[j] * sizeof(double)); + /* set new pointer and new capacity of the j-th column */ + vc_ptr[j] = luf->sv_beg; + vc_cap[j] = cap; + /* set new pointer to the beginning of the free part */ + luf->sv_beg += cap; + /* now the j-th column starts in the rightmost location among + other rows and columns of the matrix V, so its node should be + moved to the end of the row/column linked list */ + k = n + j; + /* remove the j-th column node from the linked list */ + if (sv_prev[k] == 0) + luf->sv_head = sv_next[k]; + else + { /* capacity of the previous row/column can be increased at the + expense of old locations of the j-th column */ + kk = sv_prev[k]; + if (kk <= n) vr_cap[kk] += cur; else vc_cap[kk-n] += cur; + sv_next[sv_prev[k]] = sv_next[k]; + } + if (sv_next[k] == 0) + luf->sv_tail = sv_prev[k]; + else + sv_prev[sv_next[k]] = sv_prev[k]; + /* insert the j-th column node to the end of the linked list */ + sv_prev[k] = luf->sv_tail; + sv_next[k] = 0; + if (sv_prev[k] == 0) + luf->sv_head = k; + else + sv_next[sv_prev[k]] = k; + luf->sv_tail = k; +done: return ret; +} + +/*********************************************************************** +* reallocate - reallocate LU-factorization arrays +* +* This routine reallocates arrays, whose size depends of n, the order +* of the matrix A to be factorized. */ + +static void reallocate(LUF *luf, int n) +{ int n_max = luf->n_max; + luf->n = n; + if (n <= n_max) goto done; + if (luf->fr_ptr != NULL) xfree(luf->fr_ptr); + if (luf->fr_len != NULL) xfree(luf->fr_len); + if (luf->fc_ptr != NULL) xfree(luf->fc_ptr); + if (luf->fc_len != NULL) xfree(luf->fc_len); + if (luf->vr_ptr != NULL) xfree(luf->vr_ptr); + if (luf->vr_len != NULL) xfree(luf->vr_len); + if (luf->vr_cap != NULL) xfree(luf->vr_cap); + if (luf->vr_piv != NULL) xfree(luf->vr_piv); + if (luf->vc_ptr != NULL) xfree(luf->vc_ptr); + if (luf->vc_len != NULL) xfree(luf->vc_len); + if (luf->vc_cap != NULL) xfree(luf->vc_cap); + if (luf->pp_row != NULL) xfree(luf->pp_row); + if (luf->pp_col != NULL) xfree(luf->pp_col); + if (luf->qq_row != NULL) xfree(luf->qq_row); + if (luf->qq_col != NULL) xfree(luf->qq_col); + if (luf->sv_prev != NULL) xfree(luf->sv_prev); + if (luf->sv_next != NULL) xfree(luf->sv_next); + if (luf->vr_max != NULL) xfree(luf->vr_max); + if (luf->rs_head != NULL) xfree(luf->rs_head); + if (luf->rs_prev != NULL) xfree(luf->rs_prev); + if (luf->rs_next != NULL) xfree(luf->rs_next); + if (luf->cs_head != NULL) xfree(luf->cs_head); + if (luf->cs_prev != NULL) xfree(luf->cs_prev); + if (luf->cs_next != NULL) xfree(luf->cs_next); + if (luf->flag != NULL) xfree(luf->flag); + if (luf->work != NULL) xfree(luf->work); + luf->n_max = n_max = n + 100; + luf->fr_ptr = xcalloc(1+n_max, sizeof(int)); + luf->fr_len = xcalloc(1+n_max, sizeof(int)); + luf->fc_ptr = xcalloc(1+n_max, sizeof(int)); + luf->fc_len = xcalloc(1+n_max, sizeof(int)); + luf->vr_ptr = xcalloc(1+n_max, sizeof(int)); + luf->vr_len = xcalloc(1+n_max, sizeof(int)); + luf->vr_cap = xcalloc(1+n_max, sizeof(int)); + luf->vr_piv = xcalloc(1+n_max, sizeof(double)); + luf->vc_ptr = xcalloc(1+n_max, sizeof(int)); + luf->vc_len = xcalloc(1+n_max, sizeof(int)); + luf->vc_cap = xcalloc(1+n_max, sizeof(int)); + luf->pp_row = xcalloc(1+n_max, sizeof(int)); + luf->pp_col = xcalloc(1+n_max, sizeof(int)); + luf->qq_row = xcalloc(1+n_max, sizeof(int)); + luf->qq_col = xcalloc(1+n_max, sizeof(int)); + luf->sv_prev = xcalloc(1+n_max+n_max, sizeof(int)); + luf->sv_next = xcalloc(1+n_max+n_max, sizeof(int)); + luf->vr_max = xcalloc(1+n_max, sizeof(double)); + luf->rs_head = xcalloc(1+n_max, sizeof(int)); + luf->rs_prev = xcalloc(1+n_max, sizeof(int)); + luf->rs_next = xcalloc(1+n_max, sizeof(int)); + luf->cs_head = xcalloc(1+n_max, sizeof(int)); + luf->cs_prev = xcalloc(1+n_max, sizeof(int)); + luf->cs_next = xcalloc(1+n_max, sizeof(int)); + luf->flag = xcalloc(1+n_max, sizeof(int)); + luf->work = xcalloc(1+n_max, sizeof(double)); +done: return; +} + +/*********************************************************************** +* initialize - initialize LU-factorization data structures +* +* This routine initializes data structures for subsequent computing +* the LU-factorization of a given matrix A, which is specified by the +* formal routine col. On exit V = A and F = P = Q = I, where I is the +* unity matrix. (Row-wise representation of the matrix F is not used +* at the factorization stage and therefore is not initialized.) +* +* If no error occured, the routine returns zero. Otherwise, in case of +* overflow of the sparse vector area, the routine returns non-zero. */ + +static int initialize(LUF *luf, int (*col)(void *info, int j, int rn[], + double aj[]), void *info) +{ int n = luf->n; + int *fc_ptr = luf->fc_ptr; + int *fc_len = luf->fc_len; + int *vr_ptr = luf->vr_ptr; + int *vr_len = luf->vr_len; + int *vr_cap = luf->vr_cap; + int *vc_ptr = luf->vc_ptr; + int *vc_len = luf->vc_len; + int *vc_cap = luf->vc_cap; + int *pp_row = luf->pp_row; + int *pp_col = luf->pp_col; + int *qq_row = luf->qq_row; + int *qq_col = luf->qq_col; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + int *sv_prev = luf->sv_prev; + int *sv_next = luf->sv_next; + double *vr_max = luf->vr_max; + int *rs_head = luf->rs_head; + int *rs_prev = luf->rs_prev; + int *rs_next = luf->rs_next; + int *cs_head = luf->cs_head; + int *cs_prev = luf->cs_prev; + int *cs_next = luf->cs_next; + int *flag = luf->flag; + double *work = luf->work; + int ret = 0; + int i, i_ptr, j, j_beg, j_end, k, len, nnz, sv_beg, sv_end, ptr; + double big, val; + /* free all locations of the sparse vector area */ + sv_beg = 1; + sv_end = luf->sv_size + 1; + /* (row-wise representation of the matrix F is not initialized, + because it is not used at the factorization stage) */ + /* build the matrix F in column-wise format (initially F = I) */ + for (j = 1; j <= n; j++) + { fc_ptr[j] = sv_end; + fc_len[j] = 0; + } + /* clear rows of the matrix V; clear the flag array */ + for (i = 1; i <= n; i++) + vr_len[i] = vr_cap[i] = 0, flag[i] = 0; + /* build the matrix V in column-wise format (initially V = A); + count non-zeros in rows of this matrix; count total number of + non-zeros; compute largest of absolute values of elements */ + nnz = 0; + big = 0.0; + for (j = 1; j <= n; j++) + { int *rn = pp_row; + double *aj = work; + /* obtain j-th column of the matrix A */ + len = col(info, j, rn, aj); + if (!(0 <= len && len <= n)) + xfault("luf_factorize: j = %d; len = %d; invalid column len" + "gth\n", j, len); + /* check for free locations */ + if (sv_end - sv_beg < len) + { /* overflow of the sparse vector area */ + ret = 1; + goto done; + } + /* set pointer to the j-th column */ + vc_ptr[j] = sv_beg; + /* set length of the j-th column */ + vc_len[j] = vc_cap[j] = len; + /* count total number of non-zeros */ + nnz += len; + /* walk through elements of the j-th column */ + for (ptr = 1; ptr <= len; ptr++) + { /* get row index and numerical value of a[i,j] */ + i = rn[ptr]; + val = aj[ptr]; + if (!(1 <= i && i <= n)) + xfault("luf_factorize: i = %d; j = %d; invalid row index" + "\n", i, j); + if (flag[i]) + xfault("luf_factorize: i = %d; j = %d; duplicate element" + " not allowed\n", i, j); + if (val == 0.0) + xfault("luf_factorize: i = %d; j = %d; zero element not " + "allowed\n", i, j); + /* add new element v[i,j] = a[i,j] to j-th column */ + sv_ind[sv_beg] = i; + sv_val[sv_beg] = val; + sv_beg++; + /* big := max(big, |a[i,j]|) */ + if (val < 0.0) val = - val; + if (big < val) big = val; + /* mark non-zero in the i-th position of the j-th column */ + flag[i] = 1; + /* increase length of the i-th row */ + vr_cap[i]++; + } + /* reset all non-zero marks */ + for (ptr = 1; ptr <= len; ptr++) flag[rn[ptr]] = 0; + } + /* allocate rows of the matrix V */ + for (i = 1; i <= n; i++) + { /* get length of the i-th row */ + len = vr_cap[i]; + /* check for free locations */ + if (sv_end - sv_beg < len) + { /* overflow of the sparse vector area */ + ret = 1; + goto done; + } + /* set pointer to the i-th row */ + vr_ptr[i] = sv_beg; + /* reserve locations for the i-th row */ + sv_beg += len; + } + /* build the matrix V in row-wise format using representation of + this matrix in column-wise format */ + for (j = 1; j <= n; j++) + { /* walk through elements of the j-th column */ + j_beg = vc_ptr[j]; + j_end = j_beg + vc_len[j] - 1; + for (k = j_beg; k <= j_end; k++) + { /* get row index and numerical value of v[i,j] */ + i = sv_ind[k]; + val = sv_val[k]; + /* store element in the i-th row */ + i_ptr = vr_ptr[i] + vr_len[i]; + sv_ind[i_ptr] = j; + sv_val[i_ptr] = val; + /* increase count of the i-th row */ + vr_len[i]++; + } + } + /* initialize the matrices P and Q (initially P = Q = I) */ + for (k = 1; k <= n; k++) + pp_row[k] = pp_col[k] = qq_row[k] = qq_col[k] = k; + /* set sva partitioning pointers */ + luf->sv_beg = sv_beg; + luf->sv_end = sv_end; + /* the initial physical order of rows and columns of the matrix V + is n+1, ..., n+n, 1, ..., n (firstly columns, then rows) */ + luf->sv_head = n+1; + luf->sv_tail = n; + for (i = 1; i <= n; i++) + { sv_prev[i] = i-1; + sv_next[i] = i+1; + } + sv_prev[1] = n+n; + sv_next[n] = 0; + for (j = 1; j <= n; j++) + { sv_prev[n+j] = n+j-1; + sv_next[n+j] = n+j+1; + } + sv_prev[n+1] = 0; + sv_next[n+n] = 1; + /* clear working arrays */ + for (k = 1; k <= n; k++) + { flag[k] = 0; + work[k] = 0.0; + } + /* initialize some statistics */ + luf->nnz_a = nnz; + luf->nnz_f = 0; + luf->nnz_v = nnz; + luf->max_a = big; + luf->big_v = big; + luf->rank = -1; + /* initially the active submatrix is the entire matrix V */ + /* largest of absolute values of elements in each active row is + unknown yet */ + for (i = 1; i <= n; i++) vr_max[i] = -1.0; + /* build linked lists of active rows */ + for (len = 0; len <= n; len++) rs_head[len] = 0; + for (i = 1; i <= n; i++) + { len = vr_len[i]; + rs_prev[i] = 0; + rs_next[i] = rs_head[len]; + if (rs_next[i] != 0) rs_prev[rs_next[i]] = i; + rs_head[len] = i; + } + /* build linked lists of active columns */ + for (len = 0; len <= n; len++) cs_head[len] = 0; + for (j = 1; j <= n; j++) + { len = vc_len[j]; + cs_prev[j] = 0; + cs_next[j] = cs_head[len]; + if (cs_next[j] != 0) cs_prev[cs_next[j]] = j; + cs_head[len] = j; + } +done: /* return to the factorizing routine */ + return ret; +} + +/*********************************************************************** +* find_pivot - choose a pivot element +* +* This routine chooses a pivot element in the active submatrix of the +* matrix U = P*V*Q. +* +* It is assumed that on entry the matrix U has the following partially +* triangularized form: +* +* 1 k n +* 1 x x x x x x x x x x +* . x x x x x x x x x +* . . x x x x x x x x +* . . . x x x x x x x +* k . . . . * * * * * * +* . . . . * * * * * * +* . . . . * * * * * * +* . . . . * * * * * * +* . . . . * * * * * * +* n . . . . * * * * * * +* +* where rows and columns k, k+1, ..., n belong to the active submatrix +* (elements of the active submatrix are marked by '*'). +* +* Since the matrix U = P*V*Q is not stored, the routine works with the +* matrix V. It is assumed that the row-wise representation corresponds +* to the matrix V, but the column-wise representation corresponds to +* the active submatrix of the matrix V, i.e. elements of the matrix V, +* which doesn't belong to the active submatrix, are missing from the +* column linked lists. It is also assumed that each active row of the +* matrix V is in the set R[len], where len is number of non-zeros in +* the row, and each active column of the matrix V is in the set C[len], +* where len is number of non-zeros in the column (in the latter case +* only elements of the active submatrix are counted; such elements are +* marked by '*' on the figure above). +* +* For the reason of numerical stability the routine applies so called +* threshold pivoting proposed by J.Reid. It is assumed that an element +* v[i,j] can be selected as a pivot candidate if it is not very small +* (in absolute value) among other elements in the same row, i.e. if it +* satisfies to the stability condition |v[i,j]| >= tol * max|v[i,*]|, +* where 0 < tol < 1 is a given tolerance. +* +* In order to keep sparsity of the matrix V the routine uses Markowitz +* strategy, trying to choose such element v[p,q], which satisfies to +* the stability condition (see above) and has smallest Markowitz cost +* (nr[p]-1) * (nc[q]-1), where nr[p] and nc[q] are numbers of non-zero +* elements, respectively, in the p-th row and in the q-th column of the +* active submatrix. +* +* In order to reduce the search, i.e. not to walk through all elements +* of the active submatrix, the routine exploits a technique proposed by +* I.Duff. This technique is based on using the sets R[len] and C[len] +* of active rows and columns. +* +* If the pivot element v[p,q] has been chosen, the routine stores its +* indices to the locations *p and *q and returns zero. Otherwise, if +* the active submatrix is empty and therefore the pivot element can't +* be chosen, the routine returns non-zero. */ + +static int find_pivot(LUF *luf, int *_p, int *_q) +{ int n = luf->n; + int *vr_ptr = luf->vr_ptr; + int *vr_len = luf->vr_len; + int *vc_ptr = luf->vc_ptr; + int *vc_len = luf->vc_len; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + double *vr_max = luf->vr_max; + int *rs_head = luf->rs_head; + int *rs_next = luf->rs_next; + int *cs_head = luf->cs_head; + int *cs_prev = luf->cs_prev; + int *cs_next = luf->cs_next; + double piv_tol = luf->piv_tol; + int piv_lim = luf->piv_lim; + int suhl = luf->suhl; + int p, q, len, i, i_beg, i_end, i_ptr, j, j_beg, j_end, j_ptr, + ncand, next_j, min_p, min_q, min_len; + double best, cost, big, temp; + /* initially no pivot candidates have been found so far */ + p = q = 0, best = DBL_MAX, ncand = 0; + /* if in the active submatrix there is a column that has the only + non-zero (column singleton), choose it as pivot */ + j = cs_head[1]; + if (j != 0) + { xassert(vc_len[j] == 1); + p = sv_ind[vc_ptr[j]], q = j; + goto done; + } + /* if in the active submatrix there is a row that has the only + non-zero (row singleton), choose it as pivot */ + i = rs_head[1]; + if (i != 0) + { xassert(vr_len[i] == 1); + p = i, q = sv_ind[vr_ptr[i]]; + goto done; + } + /* there are no singletons in the active submatrix; walk through + other non-empty rows and columns */ + for (len = 2; len <= n; len++) + { /* consider active columns that have len non-zeros */ + for (j = cs_head[len]; j != 0; j = next_j) + { /* the j-th column has len non-zeros */ + j_beg = vc_ptr[j]; + j_end = j_beg + vc_len[j] - 1; + /* save pointer to the next column with the same length */ + next_j = cs_next[j]; + /* find an element in the j-th column, which is placed in a + row with minimal number of non-zeros and satisfies to the + stability condition (such element may not exist) */ + min_p = min_q = 0, min_len = INT_MAX; + for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) + { /* get row index of v[i,j] */ + i = sv_ind[j_ptr]; + i_beg = vr_ptr[i]; + i_end = i_beg + vr_len[i] - 1; + /* if the i-th row is not shorter than that one, where + minimal element is currently placed, skip v[i,j] */ + if (vr_len[i] >= min_len) continue; + /* determine the largest of absolute values of elements + in the i-th row */ + big = vr_max[i]; + if (big < 0.0) + { /* the largest value is unknown yet; compute it */ + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + { temp = sv_val[i_ptr]; + if (temp < 0.0) temp = - temp; + if (big < temp) big = temp; + } + vr_max[i] = big; + } + /* find v[i,j] in the i-th row */ + for (i_ptr = vr_ptr[i]; sv_ind[i_ptr] != j; i_ptr++); + xassert(i_ptr <= i_end); + /* if v[i,j] doesn't satisfy to the stability condition, + skip it */ + temp = sv_val[i_ptr]; + if (temp < 0.0) temp = - temp; + if (temp < piv_tol * big) continue; + /* v[i,j] is better than the current minimal element */ + min_p = i, min_q = j, min_len = vr_len[i]; + /* if Markowitz cost of the current minimal element is + not greater than (len-1)**2, it can be chosen right + now; this heuristic reduces the search and works well + in many cases */ + if (min_len <= len) + { p = min_p, q = min_q; + goto done; + } + } + /* the j-th column has been scanned */ + if (min_p != 0) + { /* the minimal element is a next pivot candidate */ + ncand++; + /* compute its Markowitz cost */ + cost = (double)(min_len - 1) * (double)(len - 1); + /* choose between the minimal element and the current + candidate */ + if (cost < best) p = min_p, q = min_q, best = cost; + /* if piv_lim candidates have been considered, there are + doubts that a much better candidate exists; therefore + it's time to terminate the search */ + if (ncand == piv_lim) goto done; + } + else + { /* the j-th column has no elements, which satisfy to the + stability condition; Uwe Suhl suggests to exclude such + column from the further consideration until it becomes + a column singleton; in hard cases this significantly + reduces a time needed for pivot searching */ + if (suhl) + { /* remove the j-th column from the active set */ + if (cs_prev[j] == 0) + cs_head[len] = cs_next[j]; + else + cs_next[cs_prev[j]] = cs_next[j]; + if (cs_next[j] == 0) + /* nop */; + else + cs_prev[cs_next[j]] = cs_prev[j]; + /* the following assignment is used to avoid an error + when the routine eliminate (see below) will try to + remove the j-th column from the active set */ + cs_prev[j] = cs_next[j] = j; + } + } + } + /* consider active rows that have len non-zeros */ + for (i = rs_head[len]; i != 0; i = rs_next[i]) + { /* the i-th row has len non-zeros */ + i_beg = vr_ptr[i]; + i_end = i_beg + vr_len[i] - 1; + /* determine the largest of absolute values of elements in + the i-th row */ + big = vr_max[i]; + if (big < 0.0) + { /* the largest value is unknown yet; compute it */ + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + { temp = sv_val[i_ptr]; + if (temp < 0.0) temp = - temp; + if (big < temp) big = temp; + } + vr_max[i] = big; + } + /* find an element in the i-th row, which is placed in a + column with minimal number of non-zeros and satisfies to + the stability condition (such element always exists) */ + min_p = min_q = 0, min_len = INT_MAX; + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + { /* get column index of v[i,j] */ + j = sv_ind[i_ptr]; + /* if the j-th column is not shorter than that one, where + minimal element is currently placed, skip v[i,j] */ + if (vc_len[j] >= min_len) continue; + /* if v[i,j] doesn't satisfy to the stability condition, + skip it */ + temp = sv_val[i_ptr]; + if (temp < 0.0) temp = - temp; + if (temp < piv_tol * big) continue; + /* v[i,j] is better than the current minimal element */ + min_p = i, min_q = j, min_len = vc_len[j]; + /* if Markowitz cost of the current minimal element is + not greater than (len-1)**2, it can be chosen right + now; this heuristic reduces the search and works well + in many cases */ + if (min_len <= len) + { p = min_p, q = min_q; + goto done; + } + } + /* the i-th row has been scanned */ + if (min_p != 0) + { /* the minimal element is a next pivot candidate */ + ncand++; + /* compute its Markowitz cost */ + cost = (double)(len - 1) * (double)(min_len - 1); + /* choose between the minimal element and the current + candidate */ + if (cost < best) p = min_p, q = min_q, best = cost; + /* if piv_lim candidates have been considered, there are + doubts that a much better candidate exists; therefore + it's time to terminate the search */ + if (ncand == piv_lim) goto done; + } + else + { /* this can't be because this can never be */ + xassert(min_p != min_p); + } + } + } +done: /* bring the pivot to the factorizing routine */ + *_p = p, *_q = q; + return (p == 0); +} + +/*********************************************************************** +* eliminate - perform gaussian elimination. +* +* This routine performs elementary gaussian transformations in order +* to eliminate subdiagonal elements in the k-th column of the matrix +* U = P*V*Q using the pivot element u[k,k], where k is the number of +* the current elimination step. +* +* The parameters p and q are, respectively, row and column indices of +* the element v[p,q], which corresponds to the element u[k,k]. +* +* Each time when the routine applies the elementary transformation to +* a non-pivot row of the matrix V, it stores the corresponding element +* to the matrix F in order to keep the main equality A = F*V. +* +* The routine assumes that on entry the matrices L = P*F*inv(P) and +* U = P*V*Q are the following: +* +* 1 k 1 k n +* 1 1 . . . . . . . . . 1 x x x x x x x x x x +* x 1 . . . . . . . . . x x x x x x x x x +* x x 1 . . . . . . . . . x x x x x x x x +* x x x 1 . . . . . . . . . x x x x x x x +* k x x x x 1 . . . . . k . . . . * * * * * * +* x x x x _ 1 . . . . . . . . # * * * * * +* x x x x _ . 1 . . . . . . . # * * * * * +* x x x x _ . . 1 . . . . . . # * * * * * +* x x x x _ . . . 1 . . . . . # * * * * * +* n x x x x _ . . . . 1 n . . . . # * * * * * +* +* matrix L matrix U +* +* where rows and columns of the matrix U with numbers k, k+1, ..., n +* form the active submatrix (eliminated elements are marked by '#' and +* other elements of the active submatrix are marked by '*'). Note that +* each eliminated non-zero element u[i,k] of the matrix U gives the +* corresponding element l[i,k] of the matrix L (marked by '_'). +* +* Actually all operations are performed on the matrix V. Should note +* that the row-wise representation corresponds to the matrix V, but the +* column-wise representation corresponds to the active submatrix of the +* matrix V, i.e. elements of the matrix V, which doesn't belong to the +* active submatrix, are missing from the column linked lists. +* +* Let u[k,k] = v[p,q] be the pivot. In order to eliminate subdiagonal +* elements u[i',k] = v[i,q], i' = k+1, k+2, ..., n, the routine applies +* the following elementary gaussian transformations: +* +* (i-th row of V) := (i-th row of V) - f[i,p] * (p-th row of V), +* +* where f[i,p] = v[i,q] / v[p,q] is a gaussian multiplier. +* +* Additionally, in order to keep the main equality A = F*V, each time +* when the routine applies the transformation to i-th row of the matrix +* V, it also adds f[i,p] as a new element to the matrix F. +* +* IMPORTANT: On entry the working arrays flag and work should contain +* zeros. This status is provided by the routine on exit. +* +* If no error occured, the routine returns zero. Otherwise, in case of +* overflow of the sparse vector area, the routine returns non-zero. */ + +static int eliminate(LUF *luf, int p, int q) +{ int n = luf->n; + int *fc_ptr = luf->fc_ptr; + int *fc_len = luf->fc_len; + int *vr_ptr = luf->vr_ptr; + int *vr_len = luf->vr_len; + int *vr_cap = luf->vr_cap; + double *vr_piv = luf->vr_piv; + int *vc_ptr = luf->vc_ptr; + int *vc_len = luf->vc_len; + int *vc_cap = luf->vc_cap; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + int *sv_prev = luf->sv_prev; + int *sv_next = luf->sv_next; + double *vr_max = luf->vr_max; + int *rs_head = luf->rs_head; + int *rs_prev = luf->rs_prev; + int *rs_next = luf->rs_next; + int *cs_head = luf->cs_head; + int *cs_prev = luf->cs_prev; + int *cs_next = luf->cs_next; + int *flag = luf->flag; + double *work = luf->work; + double eps_tol = luf->eps_tol; + /* at this stage the row-wise representation of the matrix F is + not used, so fr_len can be used as a working array */ + int *ndx = luf->fr_len; + int ret = 0; + int len, fill, i, i_beg, i_end, i_ptr, j, j_beg, j_end, j_ptr, k, + p_beg, p_end, p_ptr, q_beg, q_end, q_ptr; + double fip, val, vpq, temp; + xassert(1 <= p && p <= n); + xassert(1 <= q && q <= n); + /* remove the p-th (pivot) row from the active set; this row will + never return there */ + if (rs_prev[p] == 0) + rs_head[vr_len[p]] = rs_next[p]; + else + rs_next[rs_prev[p]] = rs_next[p]; + if (rs_next[p] == 0) + ; + else + rs_prev[rs_next[p]] = rs_prev[p]; + /* remove the q-th (pivot) column from the active set; this column + will never return there */ + if (cs_prev[q] == 0) + cs_head[vc_len[q]] = cs_next[q]; + else + cs_next[cs_prev[q]] = cs_next[q]; + if (cs_next[q] == 0) + ; + else + cs_prev[cs_next[q]] = cs_prev[q]; + /* find the pivot v[p,q] = u[k,k] in the p-th row */ + p_beg = vr_ptr[p]; + p_end = p_beg + vr_len[p] - 1; + for (p_ptr = p_beg; sv_ind[p_ptr] != q; p_ptr++) /* nop */; + xassert(p_ptr <= p_end); + /* store value of the pivot */ + vpq = (vr_piv[p] = sv_val[p_ptr]); + /* remove the pivot from the p-th row */ + sv_ind[p_ptr] = sv_ind[p_end]; + sv_val[p_ptr] = sv_val[p_end]; + vr_len[p]--; + p_end--; + /* find the pivot v[p,q] = u[k,k] in the q-th column */ + q_beg = vc_ptr[q]; + q_end = q_beg + vc_len[q] - 1; + for (q_ptr = q_beg; sv_ind[q_ptr] != p; q_ptr++) /* nop */; + xassert(q_ptr <= q_end); + /* remove the pivot from the q-th column */ + sv_ind[q_ptr] = sv_ind[q_end]; + vc_len[q]--; + q_end--; + /* walk through the p-th (pivot) row, which doesn't contain the + pivot v[p,q] already, and do the following... */ + for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++) + { /* get column index of v[p,j] */ + j = sv_ind[p_ptr]; + /* store v[p,j] to the working array */ + flag[j] = 1; + work[j] = sv_val[p_ptr]; + /* remove the j-th column from the active set; this column will + return there later with new length */ + if (cs_prev[j] == 0) + cs_head[vc_len[j]] = cs_next[j]; + else + cs_next[cs_prev[j]] = cs_next[j]; + if (cs_next[j] == 0) + ; + else + cs_prev[cs_next[j]] = cs_prev[j]; + /* find v[p,j] in the j-th column */ + j_beg = vc_ptr[j]; + j_end = j_beg + vc_len[j] - 1; + for (j_ptr = j_beg; sv_ind[j_ptr] != p; j_ptr++) /* nop */; + xassert(j_ptr <= j_end); + /* since v[p,j] leaves the active submatrix, remove it from the + j-th column; however, v[p,j] is kept in the p-th row */ + sv_ind[j_ptr] = sv_ind[j_end]; + vc_len[j]--; + } + /* walk through the q-th (pivot) column, which doesn't contain the + pivot v[p,q] already, and perform gaussian elimination */ + while (q_beg <= q_end) + { /* element v[i,q] should be eliminated */ + /* get row index of v[i,q] */ + i = sv_ind[q_beg]; + /* remove the i-th row from the active set; later this row will + return there with new length */ + if (rs_prev[i] == 0) + rs_head[vr_len[i]] = rs_next[i]; + else + rs_next[rs_prev[i]] = rs_next[i]; + if (rs_next[i] == 0) + ; + else + rs_prev[rs_next[i]] = rs_prev[i]; + /* find v[i,q] in the i-th row */ + i_beg = vr_ptr[i]; + i_end = i_beg + vr_len[i] - 1; + for (i_ptr = i_beg; sv_ind[i_ptr] != q; i_ptr++) /* nop */; + xassert(i_ptr <= i_end); + /* compute gaussian multiplier f[i,p] = v[i,q] / v[p,q] */ + fip = sv_val[i_ptr] / vpq; + /* since v[i,q] should be eliminated, remove it from the i-th + row */ + sv_ind[i_ptr] = sv_ind[i_end]; + sv_val[i_ptr] = sv_val[i_end]; + vr_len[i]--; + i_end--; + /* and from the q-th column */ + sv_ind[q_beg] = sv_ind[q_end]; + vc_len[q]--; + q_end--; + /* perform gaussian transformation: + (i-th row) := (i-th row) - f[i,p] * (p-th row) + note that now the p-th row, which is in the working array, + doesn't contain the pivot v[p,q], and the i-th row doesn't + contain the eliminated element v[i,q] */ + /* walk through the i-th row and transform existing non-zero + elements */ + fill = vr_len[p]; + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + { /* get column index of v[i,j] */ + j = sv_ind[i_ptr]; + /* v[i,j] := v[i,j] - f[i,p] * v[p,j] */ + if (flag[j]) + { /* v[p,j] != 0 */ + temp = (sv_val[i_ptr] -= fip * work[j]); + if (temp < 0.0) temp = - temp; + flag[j] = 0; + fill--; /* since both v[i,j] and v[p,j] exist */ + if (temp == 0.0 || temp < eps_tol) + { /* new v[i,j] is closer to zero; replace it by exact + zero, i.e. remove it from the active submatrix */ + /* remove v[i,j] from the i-th row */ + sv_ind[i_ptr] = sv_ind[i_end]; + sv_val[i_ptr] = sv_val[i_end]; + vr_len[i]--; + i_ptr--; + i_end--; + /* find v[i,j] in the j-th column */ + j_beg = vc_ptr[j]; + j_end = j_beg + vc_len[j] - 1; + for (j_ptr = j_beg; sv_ind[j_ptr] != i; j_ptr++); + xassert(j_ptr <= j_end); + /* remove v[i,j] from the j-th column */ + sv_ind[j_ptr] = sv_ind[j_end]; + vc_len[j]--; + } + else + { /* v_big := max(v_big, |v[i,j]|) */ + if (luf->big_v < temp) luf->big_v = temp; + } + } + } + /* now flag is the pattern of the set v[p,*] \ v[i,*], and fill + is number of non-zeros in this set; therefore up to fill new + non-zeros may appear in the i-th row */ + if (vr_len[i] + fill > vr_cap[i]) + { /* enlarge the i-th row */ + if (luf_enlarge_row(luf, i, vr_len[i] + fill)) + { /* overflow of the sparse vector area */ + ret = 1; + goto done; + } + /* defragmentation may change row and column pointers of the + matrix V */ + p_beg = vr_ptr[p]; + p_end = p_beg + vr_len[p] - 1; + q_beg = vc_ptr[q]; + q_end = q_beg + vc_len[q] - 1; + } + /* walk through the p-th (pivot) row and create new elements + of the i-th row that appear due to fill-in; column indices + of these new elements are accumulated in the array ndx */ + len = 0; + for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++) + { /* get column index of v[p,j], which may cause fill-in */ + j = sv_ind[p_ptr]; + if (flag[j]) + { /* compute new non-zero v[i,j] = 0 - f[i,p] * v[p,j] */ + temp = (val = - fip * work[j]); + if (temp < 0.0) temp = - temp; + if (temp == 0.0 || temp < eps_tol) + /* if v[i,j] is closer to zero; just ignore it */; + else + { /* add v[i,j] to the i-th row */ + i_ptr = vr_ptr[i] + vr_len[i]; + sv_ind[i_ptr] = j; + sv_val[i_ptr] = val; + vr_len[i]++; + /* remember column index of v[i,j] */ + ndx[++len] = j; + /* big_v := max(big_v, |v[i,j]|) */ + if (luf->big_v < temp) luf->big_v = temp; + } + } + else + { /* there is no fill-in, because v[i,j] already exists in + the i-th row; restore the flag of the element v[p,j], + which was reset before */ + flag[j] = 1; + } + } + /* add new non-zeros v[i,j] to the corresponding columns */ + for (k = 1; k <= len; k++) + { /* get column index of new non-zero v[i,j] */ + j = ndx[k]; + /* one free location is needed in the j-th column */ + if (vc_len[j] + 1 > vc_cap[j]) + { /* enlarge the j-th column */ + if (luf_enlarge_col(luf, j, vc_len[j] + 10)) + { /* overflow of the sparse vector area */ + ret = 1; + goto done; + } + /* defragmentation may change row and column pointers of + the matrix V */ + p_beg = vr_ptr[p]; + p_end = p_beg + vr_len[p] - 1; + q_beg = vc_ptr[q]; + q_end = q_beg + vc_len[q] - 1; + } + /* add new non-zero v[i,j] to the j-th column */ + j_ptr = vc_ptr[j] + vc_len[j]; + sv_ind[j_ptr] = i; + vc_len[j]++; + } + /* now the i-th row has been completely transformed, therefore + it can return to the active set with new length */ + rs_prev[i] = 0; + rs_next[i] = rs_head[vr_len[i]]; + if (rs_next[i] != 0) rs_prev[rs_next[i]] = i; + rs_head[vr_len[i]] = i; + /* the largest of absolute values of elements in the i-th row + is currently unknown */ + vr_max[i] = -1.0; + /* at least one free location is needed to store the gaussian + multiplier */ + if (luf->sv_end - luf->sv_beg < 1) + { /* there are no free locations at all; defragment SVA */ + luf_defrag_sva(luf); + if (luf->sv_end - luf->sv_beg < 1) + { /* overflow of the sparse vector area */ + ret = 1; + goto done; + } + /* defragmentation may change row and column pointers of the + matrix V */ + p_beg = vr_ptr[p]; + p_end = p_beg + vr_len[p] - 1; + q_beg = vc_ptr[q]; + q_end = q_beg + vc_len[q] - 1; + } + /* add the element f[i,p], which is the gaussian multiplier, + to the matrix F */ + luf->sv_end--; + sv_ind[luf->sv_end] = i; + sv_val[luf->sv_end] = fip; + fc_len[p]++; + /* end of elimination loop */ + } + /* at this point the q-th (pivot) column should be empty */ + xassert(vc_len[q] == 0); + /* reset capacity of the q-th column */ + vc_cap[q] = 0; + /* remove node of the q-th column from the addressing list */ + k = n + q; + if (sv_prev[k] == 0) + luf->sv_head = sv_next[k]; + else + sv_next[sv_prev[k]] = sv_next[k]; + if (sv_next[k] == 0) + luf->sv_tail = sv_prev[k]; + else + sv_prev[sv_next[k]] = sv_prev[k]; + /* the p-th column of the matrix F has been completely built; set + its pointer */ + fc_ptr[p] = luf->sv_end; + /* walk through the p-th (pivot) row and do the following... */ + for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++) + { /* get column index of v[p,j] */ + j = sv_ind[p_ptr]; + /* erase v[p,j] from the working array */ + flag[j] = 0; + work[j] = 0.0; + /* the j-th column has been completely transformed, therefore + it can return to the active set with new length; however + the special case c_prev[j] = c_next[j] = j means that the + routine find_pivot excluded the j-th column from the active + set due to Uwe Suhl's rule, and therefore in this case the + column can return to the active set only if it is a column + singleton */ + if (!(vc_len[j] != 1 && cs_prev[j] == j && cs_next[j] == j)) + { cs_prev[j] = 0; + cs_next[j] = cs_head[vc_len[j]]; + if (cs_next[j] != 0) cs_prev[cs_next[j]] = j; + cs_head[vc_len[j]] = j; + } + } +done: /* return to the factorizing routine */ + return ret; +} + +/*********************************************************************** +* build_v_cols - build the matrix V in column-wise format +* +* This routine builds the column-wise representation of the matrix V +* using its row-wise representation. +* +* If no error occured, the routine returns zero. Otherwise, in case of +* overflow of the sparse vector area, the routine returns non-zero. */ + +static int build_v_cols(LUF *luf) +{ int n = luf->n; + int *vr_ptr = luf->vr_ptr; + int *vr_len = luf->vr_len; + int *vc_ptr = luf->vc_ptr; + int *vc_len = luf->vc_len; + int *vc_cap = luf->vc_cap; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + int *sv_prev = luf->sv_prev; + int *sv_next = luf->sv_next; + int ret = 0; + int i, i_beg, i_end, i_ptr, j, j_ptr, k, nnz; + /* it is assumed that on entry all columns of the matrix V are + empty, i.e. vc_len[j] = vc_cap[j] = 0 for all j = 1, ..., n, + and have been removed from the addressing list */ + /* count non-zeros in columns of the matrix V; count total number + of non-zeros in this matrix */ + nnz = 0; + for (i = 1; i <= n; i++) + { /* walk through elements of the i-th row and count non-zeros + in the corresponding columns */ + i_beg = vr_ptr[i]; + i_end = i_beg + vr_len[i] - 1; + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + vc_cap[sv_ind[i_ptr]]++; + /* count total number of non-zeros */ + nnz += vr_len[i]; + } + /* store total number of non-zeros */ + luf->nnz_v = nnz; + /* check for free locations */ + if (luf->sv_end - luf->sv_beg < nnz) + { /* overflow of the sparse vector area */ + ret = 1; + goto done; + } + /* allocate columns of the matrix V */ + for (j = 1; j <= n; j++) + { /* set pointer to the j-th column */ + vc_ptr[j] = luf->sv_beg; + /* reserve locations for the j-th column */ + luf->sv_beg += vc_cap[j]; + } + /* build the matrix V in column-wise format using this matrix in + row-wise format */ + for (i = 1; i <= n; i++) + { /* walk through elements of the i-th row */ + i_beg = vr_ptr[i]; + i_end = i_beg + vr_len[i] - 1; + for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) + { /* get column index */ + j = sv_ind[i_ptr]; + /* store element in the j-th column */ + j_ptr = vc_ptr[j] + vc_len[j]; + sv_ind[j_ptr] = i; + sv_val[j_ptr] = sv_val[i_ptr]; + /* increase length of the j-th column */ + vc_len[j]++; + } + } + /* now columns are placed in the sparse vector area behind rows + in the order n+1, n+2, ..., n+n; so insert column nodes in the + addressing list using this order */ + for (k = n+1; k <= n+n; k++) + { sv_prev[k] = k-1; + sv_next[k] = k+1; + } + sv_prev[n+1] = luf->sv_tail; + sv_next[luf->sv_tail] = n+1; + sv_next[n+n] = 0; + luf->sv_tail = n+n; +done: /* return to the factorizing routine */ + return ret; +} + +/*********************************************************************** +* build_f_rows - build the matrix F in row-wise format +* +* This routine builds the row-wise representation of the matrix F using +* its column-wise representation. +* +* If no error occured, the routine returns zero. Otherwise, in case of +* overflow of the sparse vector area, the routine returns non-zero. */ + +static int build_f_rows(LUF *luf) +{ int n = luf->n; + int *fr_ptr = luf->fr_ptr; + int *fr_len = luf->fr_len; + int *fc_ptr = luf->fc_ptr; + int *fc_len = luf->fc_len; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + int ret = 0; + int i, j, j_beg, j_end, j_ptr, ptr, nnz; + /* clear rows of the matrix F */ + for (i = 1; i <= n; i++) fr_len[i] = 0; + /* count non-zeros in rows of the matrix F; count total number of + non-zeros in this matrix */ + nnz = 0; + for (j = 1; j <= n; j++) + { /* walk through elements of the j-th column and count non-zeros + in the corresponding rows */ + j_beg = fc_ptr[j]; + j_end = j_beg + fc_len[j] - 1; + for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) + fr_len[sv_ind[j_ptr]]++; + /* increase total number of non-zeros */ + nnz += fc_len[j]; + } + /* store total number of non-zeros */ + luf->nnz_f = nnz; + /* check for free locations */ + if (luf->sv_end - luf->sv_beg < nnz) + { /* overflow of the sparse vector area */ + ret = 1; + goto done; + } + /* allocate rows of the matrix F */ + for (i = 1; i <= n; i++) + { /* set pointer to the end of the i-th row; later this pointer + will be set to the beginning of the i-th row */ + fr_ptr[i] = luf->sv_end; + /* reserve locations for the i-th row */ + luf->sv_end -= fr_len[i]; + } + /* build the matrix F in row-wise format using this matrix in + column-wise format */ + for (j = 1; j <= n; j++) + { /* walk through elements of the j-th column */ + j_beg = fc_ptr[j]; + j_end = j_beg + fc_len[j] - 1; + for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) + { /* get row index */ + i = sv_ind[j_ptr]; + /* store element in the i-th row */ + ptr = --fr_ptr[i]; + sv_ind[ptr] = j; + sv_val[ptr] = sv_val[j_ptr]; + } + } +done: /* return to the factorizing routine */ + return ret; +} + +/*********************************************************************** +* NAME +* +* luf_factorize - compute LU-factorization +* +* SYNOPSIS +* +* #include "glpluf.h" +* int luf_factorize(LUF *luf, int n, int (*col)(void *info, int j, +* int ind[], double val[]), void *info); +* +* DESCRIPTION +* +* The routine luf_factorize computes LU-factorization of a specified +* square matrix A. +* +* The parameter luf specifies LU-factorization program object created +* by the routine luf_create_it. +* +* The parameter n specifies the order of A, n > 0. +* +* The formal routine col specifies the matrix A to be factorized. To +* obtain j-th column of A the routine luf_factorize calls the routine +* col with the parameter j (1 <= j <= n). In response the routine col +* should store row indices and numerical values of non-zero elements +* of j-th column of A to locations ind[1,...,len] and val[1,...,len], +* respectively, where len is the number of non-zeros in j-th column +* returned on exit. Neither zero nor duplicate elements are allowed. +* +* The parameter info is a transit pointer passed to the routine col. +* +* RETURNS +* +* 0 LU-factorization has been successfully computed. +* +* LUF_ESING +* The specified matrix is singular within the working precision. +* (On some elimination step the active submatrix is exactly zero, +* so no pivot can be chosen.) +* +* LUF_ECOND +* The specified matrix is ill-conditioned. +* (On some elimination step too intensive growth of elements of the +* active submatix has been detected.) +* +* If matrix A is well scaled, the return code LUF_ECOND may also mean +* that the threshold pivoting tolerance piv_tol should be increased. +* +* In case of non-zero return code the factorization becomes invalid. +* It should not be used in other operations until the cause of failure +* has been eliminated and the factorization has been recomputed again +* with the routine luf_factorize. +* +* REPAIRING SINGULAR MATRIX +* +* If the routine luf_factorize returns non-zero code, it provides all +* necessary information that can be used for "repairing" the matrix A, +* where "repairing" means replacing linearly dependent columns of the +* matrix A by appropriate columns of the unity matrix. This feature is +* needed when this routine is used for factorizing the basis matrix +* within the simplex method procedure. +* +* On exit linearly dependent columns of the (partially transformed) +* matrix U have numbers rank+1, rank+2, ..., n, where rank is estimated +* rank of the matrix A stored by the routine to the member luf->rank. +* The correspondence between columns of A and U is the same as between +* columns of V and U. Thus, linearly dependent columns of the matrix A +* have numbers qq_col[rank+1], qq_col[rank+2], ..., qq_col[n], where +* qq_col is the column-like representation of the permutation matrix Q. +* It is understood that each j-th linearly dependent column of the +* matrix U should be replaced by the unity vector, where all elements +* are zero except the unity diagonal element u[j,j]. On the other hand +* j-th row of the matrix U corresponds to the row of the matrix V (and +* therefore of the matrix A) with the number pp_row[j], where pp_row is +* the row-like representation of the permutation matrix P. Thus, each +* j-th linearly dependent column of the matrix U should be replaced by +* column of the unity matrix with the number pp_row[j]. +* +* The code that repairs the matrix A may look like follows: +* +* for (j = rank+1; j <= n; j++) +* { replace the column qq_col[j] of the matrix A by the column +* pp_row[j] of the unity matrix; +* } +* +* where rank, pp_row, and qq_col are members of the structure LUF. */ + +int luf_factorize(LUF *luf, int n, int (*col)(void *info, int j, + int ind[], double val[]), void *info) +{ int *pp_row, *pp_col, *qq_row, *qq_col; + double max_gro = luf->max_gro; + int i, j, k, p, q, t, ret; + if (n < 1) + xfault("luf_factorize: n = %d; invalid parameter\n", n); + if (n > N_MAX) + xfault("luf_factorize: n = %d; matrix too big\n", n); + /* invalidate the factorization */ + luf->valid = 0; + /* reallocate arrays, if necessary */ + reallocate(luf, n); + pp_row = luf->pp_row; + pp_col = luf->pp_col; + qq_row = luf->qq_row; + qq_col = luf->qq_col; + /* estimate initial size of the SVA, if not specified */ + if (luf->sv_size == 0 && luf->new_sva == 0) + luf->new_sva = 5 * (n + 10); +more: /* reallocate the sparse vector area, if required */ + if (luf->new_sva > 0) + { if (luf->sv_ind != NULL) xfree(luf->sv_ind); + if (luf->sv_val != NULL) xfree(luf->sv_val); + luf->sv_size = luf->new_sva; + luf->sv_ind = xcalloc(1+luf->sv_size, sizeof(int)); + luf->sv_val = xcalloc(1+luf->sv_size, sizeof(double)); + luf->new_sva = 0; + } + /* initialize LU-factorization data structures */ + if (initialize(luf, col, info)) + { /* overflow of the sparse vector area */ + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + goto more; + } + /* main elimination loop */ + for (k = 1; k <= n; k++) + { /* choose a pivot element v[p,q] */ + if (find_pivot(luf, &p, &q)) + { /* no pivot can be chosen, because the active submatrix is + exactly zero */ + luf->rank = k - 1; + ret = LUF_ESING; + goto done; + } + /* let v[p,q] correspond to u[i',j']; permute k-th and i'-th + rows and k-th and j'-th columns of the matrix U = P*V*Q to + move the element u[i',j'] to the position u[k,k] */ + i = pp_col[p], j = qq_row[q]; + xassert(k <= i && i <= n && k <= j && j <= n); + /* permute k-th and i-th rows of the matrix U */ + t = pp_row[k]; + pp_row[i] = t, pp_col[t] = i; + pp_row[k] = p, pp_col[p] = k; + /* permute k-th and j-th columns of the matrix U */ + t = qq_col[k]; + qq_col[j] = t, qq_row[t] = j; + qq_col[k] = q, qq_row[q] = k; + /* eliminate subdiagonal elements of k-th column of the matrix + U = P*V*Q using the pivot element u[k,k] = v[p,q] */ + if (eliminate(luf, p, q)) + { /* overflow of the sparse vector area */ + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + goto more; + } + /* check relative growth of elements of the matrix V */ + if (luf->big_v > max_gro * luf->max_a) + { /* the growth is too intensive, therefore most probably the + matrix A is ill-conditioned */ + luf->rank = k - 1; + ret = LUF_ECOND; + goto done; + } + } + /* now the matrix U = P*V*Q is upper triangular, the matrix V has + been built in row-wise format, and the matrix F has been built + in column-wise format */ + /* defragment the sparse vector area in order to merge all free + locations in one continuous extent */ + luf_defrag_sva(luf); + /* build the matrix V in column-wise format */ + if (build_v_cols(luf)) + { /* overflow of the sparse vector area */ + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + goto more; + } + /* build the matrix F in row-wise format */ + if (build_f_rows(luf)) + { /* overflow of the sparse vector area */ + luf->new_sva = luf->sv_size + luf->sv_size; + xassert(luf->new_sva > luf->sv_size); + goto more; + } + /* the LU-factorization has been successfully computed */ + luf->valid = 1; + luf->rank = n; + ret = 0; + /* if there are few free locations in the sparse vector area, try + increasing its size in the future */ + t = 3 * (n + luf->nnz_v) + 2 * luf->nnz_f; + if (luf->sv_size < t) + { luf->new_sva = luf->sv_size; + while (luf->new_sva < t) + { k = luf->new_sva; + luf->new_sva = k + k; + xassert(luf->new_sva > k); + } + } +done: /* return to the calling program */ + return ret; +} + +/*********************************************************************** +* NAME +* +* luf_f_solve - solve system F*x = b or F'*x = b +* +* SYNOPSIS +* +* #include "glpluf.h" +* void luf_f_solve(LUF *luf, int tr, double x[]); +* +* DESCRIPTION +* +* The routine luf_f_solve solves either the system F*x = b (if the +* flag tr is zero) or the system F'*x = b (if the flag tr is non-zero), +* where the matrix F is a component of LU-factorization specified by +* the parameter luf, F' is a matrix transposed to F. +* +* On entry the array x should contain elements of the right-hand side +* vector b in locations x[1], ..., x[n], where n is the order of the +* matrix F. On exit this array will contain elements of the solution +* vector x in the same locations. */ + +void luf_f_solve(LUF *luf, int tr, double x[]) +{ int n = luf->n; + int *fr_ptr = luf->fr_ptr; + int *fr_len = luf->fr_len; + int *fc_ptr = luf->fc_ptr; + int *fc_len = luf->fc_len; + int *pp_row = luf->pp_row; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + int i, j, k, beg, end, ptr; + double xk; + if (!luf->valid) + xfault("luf_f_solve: LU-factorization is not valid\n"); + if (!tr) + { /* solve the system F*x = b */ + for (j = 1; j <= n; j++) + { k = pp_row[j]; + xk = x[k]; + if (xk != 0.0) + { beg = fc_ptr[k]; + end = beg + fc_len[k] - 1; + for (ptr = beg; ptr <= end; ptr++) + x[sv_ind[ptr]] -= sv_val[ptr] * xk; + } + } + } + else + { /* solve the system F'*x = b */ + for (i = n; i >= 1; i--) + { k = pp_row[i]; + xk = x[k]; + if (xk != 0.0) + { beg = fr_ptr[k]; + end = beg + fr_len[k] - 1; + for (ptr = beg; ptr <= end; ptr++) + x[sv_ind[ptr]] -= sv_val[ptr] * xk; + } + } + } + return; +} + +/*********************************************************************** +* NAME +* +* luf_v_solve - solve system V*x = b or V'*x = b +* +* SYNOPSIS +* +* #include "glpluf.h" +* void luf_v_solve(LUF *luf, int tr, double x[]); +* +* DESCRIPTION +* +* The routine luf_v_solve solves either the system V*x = b (if the +* flag tr is zero) or the system V'*x = b (if the flag tr is non-zero), +* where the matrix V is a component of LU-factorization specified by +* the parameter luf, V' is a matrix transposed to V. +* +* On entry the array x should contain elements of the right-hand side +* vector b in locations x[1], ..., x[n], where n is the order of the +* matrix V. On exit this array will contain elements of the solution +* vector x in the same locations. */ + +void luf_v_solve(LUF *luf, int tr, double x[]) +{ int n = luf->n; + int *vr_ptr = luf->vr_ptr; + int *vr_len = luf->vr_len; + double *vr_piv = luf->vr_piv; + int *vc_ptr = luf->vc_ptr; + int *vc_len = luf->vc_len; + int *pp_row = luf->pp_row; + int *qq_col = luf->qq_col; + int *sv_ind = luf->sv_ind; + double *sv_val = luf->sv_val; + double *b = luf->work; + int i, j, k, beg, end, ptr; + double temp; + if (!luf->valid) + xfault("luf_v_solve: LU-factorization is not valid\n"); + for (k = 1; k <= n; k++) b[k] = x[k], x[k] = 0.0; + if (!tr) + { /* solve the system V*x = b */ + for (k = n; k >= 1; k--) + { i = pp_row[k], j = qq_col[k]; + temp = b[i]; + if (temp != 0.0) + { x[j] = (temp /= vr_piv[i]); + beg = vc_ptr[j]; + end = beg + vc_len[j] - 1; + for (ptr = beg; ptr <= end; ptr++) + b[sv_ind[ptr]] -= sv_val[ptr] * temp; + } + } + } + else + { /* solve the system V'*x = b */ + for (k = 1; k <= n; k++) + { i = pp_row[k], j = qq_col[k]; + temp = b[j]; + if (temp != 0.0) + { x[i] = (temp /= vr_piv[i]); + beg = vr_ptr[i]; + end = beg + vr_len[i] - 1; + for (ptr = beg; ptr <= end; ptr++) + b[sv_ind[ptr]] -= sv_val[ptr] * temp; + } + } + } + return; +} + +/*********************************************************************** +* NAME +* +* luf_a_solve - solve system A*x = b or A'*x = b +* +* SYNOPSIS +* +* #include "glpluf.h" +* void luf_a_solve(LUF *luf, int tr, double x[]); +* +* DESCRIPTION +* +* The routine luf_a_solve solves either the system A*x = b (if the +* flag tr is zero) or the system A'*x = b (if the flag tr is non-zero), +* where the parameter luf specifies LU-factorization of the matrix A, +* A' is a matrix transposed to A. +* +* On entry the array x should contain elements of the right-hand side +* vector b in locations x[1], ..., x[n], where n is the order of the +* matrix A. On exit this array will contain elements of the solution +* vector x in the same locations. */ + +void luf_a_solve(LUF *luf, int tr, double x[]) +{ if (!luf->valid) + xfault("luf_a_solve: LU-factorization is not valid\n"); + if (!tr) + { /* A = F*V, therefore inv(A) = inv(V)*inv(F) */ + luf_f_solve(luf, 0, x); + luf_v_solve(luf, 0, x); + } + else + { /* A' = V'*F', therefore inv(A') = inv(F')*inv(V') */ + luf_v_solve(luf, 1, x); + luf_f_solve(luf, 1, x); + } + return; +} + +/*********************************************************************** +* NAME +* +* luf_delete_it - delete LU-factorization +* +* SYNOPSIS +* +* #include "glpluf.h" +* void luf_delete_it(LUF *luf); +* +* DESCRIPTION +* +* The routine luf_delete deletes LU-factorization specified by the +* parameter luf and frees all the memory allocated to this program +* object. */ + +void luf_delete_it(LUF *luf) +{ if (luf->fr_ptr != NULL) xfree(luf->fr_ptr); + if (luf->fr_len != NULL) xfree(luf->fr_len); + if (luf->fc_ptr != NULL) xfree(luf->fc_ptr); + if (luf->fc_len != NULL) xfree(luf->fc_len); + if (luf->vr_ptr != NULL) xfree(luf->vr_ptr); + if (luf->vr_len != NULL) xfree(luf->vr_len); + if (luf->vr_cap != NULL) xfree(luf->vr_cap); + if (luf->vr_piv != NULL) xfree(luf->vr_piv); + if (luf->vc_ptr != NULL) xfree(luf->vc_ptr); + if (luf->vc_len != NULL) xfree(luf->vc_len); + if (luf->vc_cap != NULL) xfree(luf->vc_cap); + if (luf->pp_row != NULL) xfree(luf->pp_row); + if (luf->pp_col != NULL) xfree(luf->pp_col); + if (luf->qq_row != NULL) xfree(luf->qq_row); + if (luf->qq_col != NULL) xfree(luf->qq_col); + if (luf->sv_ind != NULL) xfree(luf->sv_ind); + if (luf->sv_val != NULL) xfree(luf->sv_val); + if (luf->sv_prev != NULL) xfree(luf->sv_prev); + if (luf->sv_next != NULL) xfree(luf->sv_next); + if (luf->vr_max != NULL) xfree(luf->vr_max); + if (luf->rs_head != NULL) xfree(luf->rs_head); + if (luf->rs_prev != NULL) xfree(luf->rs_prev); + if (luf->rs_next != NULL) xfree(luf->rs_next); + if (luf->cs_head != NULL) xfree(luf->cs_head); + if (luf->cs_prev != NULL) xfree(luf->cs_prev); + if (luf->cs_next != NULL) xfree(luf->cs_next); + if (luf->flag != NULL) xfree(luf->flag); + if (luf->work != NULL) xfree(luf->work); + xfree(luf); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpluf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpluf.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,326 @@ +/* glpluf.h (LU-factorization) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPLUF_H +#define GLPLUF_H + +/*********************************************************************** +* The structure LUF defines LU-factorization of a square matrix A and +* is the following quartet: +* +* [A] = (F, V, P, Q), (1) +* +* where F and V are such matrices that +* +* A = F * V, (2) +* +* and P and Q are such permutation matrices that the matrix +* +* L = P * F * inv(P) (3) +* +* is lower triangular with unity diagonal, and the matrix +* +* U = P * V * Q (4) +* +* is upper triangular. All the matrices have the order n. +* +* Matrices F and V are stored in row- and column-wise sparse format +* as row and column linked lists of non-zero elements. Unity elements +* on the main diagonal of matrix F are not stored. Pivot elements of +* matrix V (which correspond to diagonal elements of matrix U) are +* stored separately in an ordinary array. +* +* Permutation matrices P and Q are stored in ordinary arrays in both +* row- and column-like formats. +* +* Matrices L and U are completely defined by matrices F, V, P, and Q +* and therefore not stored explicitly. +* +* The factorization (1)-(4) is a version of LU-factorization. Indeed, +* from (3) and (4) it follows that: +* +* F = inv(P) * L * P, +* +* U = inv(P) * U * inv(Q), +* +* and substitution into (2) leads to: +* +* A = F * V = inv(P) * L * U * inv(Q). +* +* For more details see the program documentation. */ + +typedef struct LUF LUF; + +struct LUF +{ /* LU-factorization of a square matrix */ + int n_max; + /* maximal value of n (increased automatically, if necessary) */ + int n; + /* the order of matrices A, F, V, P, Q */ + int valid; + /* the factorization is valid only if this flag is set */ + /*--------------------------------------------------------------*/ + /* matrix F in row-wise format */ + int *fr_ptr; /* int fr_ptr[1+n_max]; */ + /* fr_ptr[i], i = 1,...,n, is a pointer to the first element of + i-th row in SVA */ + int *fr_len; /* int fr_len[1+n_max]; */ + /* fr_len[i], i = 1,...,n, is the number of elements in i-th row + (except unity diagonal element) */ + /*--------------------------------------------------------------*/ + /* matrix F in column-wise format */ + int *fc_ptr; /* int fc_ptr[1+n_max]; */ + /* fc_ptr[j], j = 1,...,n, is a pointer to the first element of + j-th column in SVA */ + int *fc_len; /* int fc_len[1+n_max]; */ + /* fc_len[j], j = 1,...,n, is the number of elements in j-th + column (except unity diagonal element) */ + /*--------------------------------------------------------------*/ + /* matrix V in row-wise format */ + int *vr_ptr; /* int vr_ptr[1+n_max]; */ + /* vr_ptr[i], i = 1,...,n, is a pointer to the first element of + i-th row in SVA */ + int *vr_len; /* int vr_len[1+n_max]; */ + /* vr_len[i], i = 1,...,n, is the number of elements in i-th row + (except pivot element) */ + int *vr_cap; /* int vr_cap[1+n_max]; */ + /* vr_cap[i], i = 1,...,n, is the capacity of i-th row, i.e. + maximal number of elements which can be stored in the row + without relocating it, vr_cap[i] >= vr_len[i] */ + double *vr_piv; /* double vr_piv[1+n_max]; */ + /* vr_piv[p], p = 1,...,n, is the pivot element v[p,q] which + corresponds to a diagonal element of matrix U = P*V*Q */ + /*--------------------------------------------------------------*/ + /* matrix V in column-wise format */ + int *vc_ptr; /* int vc_ptr[1+n_max]; */ + /* vc_ptr[j], j = 1,...,n, is a pointer to the first element of + j-th column in SVA */ + int *vc_len; /* int vc_len[1+n_max]; */ + /* vc_len[j], j = 1,...,n, is the number of elements in j-th + column (except pivot element) */ + int *vc_cap; /* int vc_cap[1+n_max]; */ + /* vc_cap[j], j = 1,...,n, is the capacity of j-th column, i.e. + maximal number of elements which can be stored in the column + without relocating it, vc_cap[j] >= vc_len[j] */ + /*--------------------------------------------------------------*/ + /* matrix P */ + int *pp_row; /* int pp_row[1+n_max]; */ + /* pp_row[i] = j means that P[i,j] = 1 */ + int *pp_col; /* int pp_col[1+n_max]; */ + /* pp_col[j] = i means that P[i,j] = 1 */ + /* if i-th row or column of matrix F is i'-th row or column of + matrix L, or if i-th row of matrix V is i'-th row of matrix U, + then pp_row[i'] = i and pp_col[i] = i' */ + /*--------------------------------------------------------------*/ + /* matrix Q */ + int *qq_row; /* int qq_row[1+n_max]; */ + /* qq_row[i] = j means that Q[i,j] = 1 */ + int *qq_col; /* int qq_col[1+n_max]; */ + /* qq_col[j] = i means that Q[i,j] = 1 */ + /* if j-th column of matrix V is j'-th column of matrix U, then + qq_row[j] = j' and qq_col[j'] = j */ + /*--------------------------------------------------------------*/ + /* the Sparse Vector Area (SVA) is a set of locations used to + store sparse vectors representing rows and columns of matrices + F and V; each location is a doublet (ind, val), where ind is + an index, and val is a numerical value of a sparse vector + element; in the whole each sparse vector is a set of adjacent + locations defined by a pointer to the first element and the + number of elements; these pointer and number are stored in the + corresponding matrix data structure (see above); the left part + of SVA is used to store rows and columns of matrix V, and its + right part is used to store rows and columns of matrix F; the + middle part of SVA contains free (unused) locations */ + int sv_size; + /* the size of SVA, in locations; all locations are numbered by + integers 1, ..., n, and location 0 is not used; if necessary, + the SVA size is automatically increased */ + int sv_beg, sv_end; + /* SVA partitioning pointers: + locations from 1 to sv_beg-1 belong to the left part + locations from sv_beg to sv_end-1 belong to the middle part + locations from sv_end to sv_size belong to the right part + the size of the middle part is (sv_end - sv_beg) */ + int *sv_ind; /* sv_ind[1+sv_size]; */ + /* sv_ind[k], 1 <= k <= sv_size, is the index field of k-th + location */ + double *sv_val; /* sv_val[1+sv_size]; */ + /* sv_val[k], 1 <= k <= sv_size, is the value field of k-th + location */ + /*--------------------------------------------------------------*/ + /* in order to efficiently defragment the left part of SVA there + is a doubly linked list of rows and columns of matrix V, where + rows are numbered by 1, ..., n, while columns are numbered by + n+1, ..., n+n, that allows uniquely identifying each row and + column of V by only one integer; in this list rows and columns + are ordered by ascending their pointers vr_ptr and vc_ptr */ + int sv_head; + /* the number of leftmost row/column */ + int sv_tail; + /* the number of rightmost row/column */ + int *sv_prev; /* int sv_prev[1+n_max+n_max]; */ + /* sv_prev[k], k = 1,...,n+n, is the number of a row/column which + precedes k-th row/column */ + int *sv_next; /* int sv_next[1+n_max+n_max]; */ + /* sv_next[k], k = 1,...,n+n, is the number of a row/column which + succedes k-th row/column */ + /*--------------------------------------------------------------*/ + /* working segment (used only during factorization) */ + double *vr_max; /* int vr_max[1+n_max]; */ + /* vr_max[i], 1 <= i <= n, is used only if i-th row of matrix V + is active (i.e. belongs to the active submatrix), and is the + largest magnitude of elements in i-th row; if vr_max[i] < 0, + the largest magnitude is not known yet and should be computed + by the pivoting routine */ + /*--------------------------------------------------------------*/ + /* in order to efficiently implement Markowitz strategy and Duff + search technique there are two families {R[0], R[1], ..., R[n]} + and {C[0], C[1], ..., C[n]}; member R[k] is the set of active + rows of matrix V, which have k non-zeros, and member C[k] is + the set of active columns of V, which have k non-zeros in the + active submatrix (i.e. in the active rows); each set R[k] and + C[k] is implemented as a separate doubly linked list */ + int *rs_head; /* int rs_head[1+n_max]; */ + /* rs_head[k], 0 <= k <= n, is the number of first active row, + which has k non-zeros */ + int *rs_prev; /* int rs_prev[1+n_max]; */ + /* rs_prev[i], 1 <= i <= n, is the number of previous row, which + has the same number of non-zeros as i-th row */ + int *rs_next; /* int rs_next[1+n_max]; */ + /* rs_next[i], 1 <= i <= n, is the number of next row, which has + the same number of non-zeros as i-th row */ + int *cs_head; /* int cs_head[1+n_max]; */ + /* cs_head[k], 0 <= k <= n, is the number of first active column, + which has k non-zeros (in the active rows) */ + int *cs_prev; /* int cs_prev[1+n_max]; */ + /* cs_prev[j], 1 <= j <= n, is the number of previous column, + which has the same number of non-zeros (in the active rows) as + j-th column */ + int *cs_next; /* int cs_next[1+n_max]; */ + /* cs_next[j], 1 <= j <= n, is the number of next column, which + has the same number of non-zeros (in the active rows) as j-th + column */ + /* (end of working segment) */ + /*--------------------------------------------------------------*/ + /* working arrays */ + int *flag; /* int flag[1+n_max]; */ + /* integer working array */ + double *work; /* double work[1+n_max]; */ + /* floating-point working array */ + /*--------------------------------------------------------------*/ + /* control parameters */ + int new_sva; + /* new required size of the sparse vector area, in locations; set + automatically by the factorizing routine */ + double piv_tol; + /* threshold pivoting tolerance, 0 < piv_tol < 1; element v[i,j] + of the active submatrix fits to be pivot if it satisfies to the + stability criterion |v[i,j]| >= piv_tol * max |v[i,*]|, i.e. if + it is not very small in the magnitude among other elements in + the same row; decreasing this parameter gives better sparsity + at the expense of numerical accuracy and vice versa */ + int piv_lim; + /* maximal allowable number of pivot candidates to be considered; + if piv_lim pivot candidates have been considered, the pivoting + routine terminates the search with the best candidate found */ + int suhl; + /* if this flag is set, the pivoting routine applies a heuristic + proposed by Uwe Suhl: if a column of the active submatrix has + no eligible pivot candidates (i.e. all its elements do not + satisfy to the stability criterion), the routine excludes it + from futher consideration until it becomes column singleton; + in many cases this allows reducing the time needed for pivot + searching */ + double eps_tol; + /* epsilon tolerance; each element of the active submatrix, whose + magnitude is less than eps_tol, is replaced by exact zero */ + double max_gro; + /* maximal allowable growth of elements of matrix V during all + the factorization process; if on some eliminaion step the ratio + big_v / max_a (see below) becomes greater than max_gro, matrix + A is considered as ill-conditioned (assuming that the pivoting + tolerance piv_tol has an appropriate value) */ + /*--------------------------------------------------------------*/ + /* some statistics */ + int nnz_a; + /* the number of non-zeros in matrix A */ + int nnz_f; + /* the number of non-zeros in matrix F (except diagonal elements, + which are not stored) */ + int nnz_v; + /* the number of non-zeros in matrix V (except its pivot elements, + which are stored in a separate array) */ + double max_a; + /* the largest magnitude of elements of matrix A */ + double big_v; + /* the largest magnitude of elements of matrix V appeared in the + active submatrix during all the factorization process */ + int rank; + /* estimated rank of matrix A */ +}; + +/* return codes: */ +#define LUF_ESING 1 /* singular matrix */ +#define LUF_ECOND 2 /* ill-conditioned matrix */ + +#define luf_create_it _glp_luf_create_it +LUF *luf_create_it(void); +/* create LU-factorization */ + +#define luf_defrag_sva _glp_luf_defrag_sva +void luf_defrag_sva(LUF *luf); +/* defragment the sparse vector area */ + +#define luf_enlarge_row _glp_luf_enlarge_row +int luf_enlarge_row(LUF *luf, int i, int cap); +/* enlarge row capacity */ + +#define luf_enlarge_col _glp_luf_enlarge_col +int luf_enlarge_col(LUF *luf, int j, int cap); +/* enlarge column capacity */ + +#define luf_factorize _glp_luf_factorize +int luf_factorize(LUF *luf, int n, int (*col)(void *info, int j, + int ind[], double val[]), void *info); +/* compute LU-factorization */ + +#define luf_f_solve _glp_luf_f_solve +void luf_f_solve(LUF *luf, int tr, double x[]); +/* solve system F*x = b or F'*x = b */ + +#define luf_v_solve _glp_luf_v_solve +void luf_v_solve(LUF *luf, int tr, double x[]); +/* solve system V*x = b or V'*x = b */ + +#define luf_a_solve _glp_luf_a_solve +void luf_a_solve(LUF *luf, int tr, double x[]); +/* solve system A*x = b or A'*x = b */ + +#define luf_delete_it _glp_luf_delete_it +void luf_delete_it(LUF *luf); +/* delete LU-factorization */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplux.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplux.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1028 @@ +/* glplux.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glplux.h" +#define xfault xerror +#define dmp_create_poolx(size) dmp_create_pool() + +/*---------------------------------------------------------------------- +// lux_create - create LU-factorization. +// +// SYNOPSIS +// +// #include "glplux.h" +// LUX *lux_create(int n); +// +// DESCRIPTION +// +// The routine lux_create creates LU-factorization data structure for +// a matrix of the order n. Initially the factorization corresponds to +// the unity matrix (F = V = P = Q = I, so A = I). +// +// RETURNS +// +// The routine returns a pointer to the created LU-factorization data +// structure, which represents the unity matrix of the order n. */ + +LUX *lux_create(int n) +{ LUX *lux; + int k; + if (n < 1) + xfault("lux_create: n = %d; invalid parameter\n", n); + lux = xmalloc(sizeof(LUX)); + lux->n = n; + lux->pool = dmp_create_poolx(sizeof(LUXELM)); + lux->F_row = xcalloc(1+n, sizeof(LUXELM *)); + lux->F_col = xcalloc(1+n, sizeof(LUXELM *)); + lux->V_piv = xcalloc(1+n, sizeof(mpq_t)); + lux->V_row = xcalloc(1+n, sizeof(LUXELM *)); + lux->V_col = xcalloc(1+n, sizeof(LUXELM *)); + lux->P_row = xcalloc(1+n, sizeof(int)); + lux->P_col = xcalloc(1+n, sizeof(int)); + lux->Q_row = xcalloc(1+n, sizeof(int)); + lux->Q_col = xcalloc(1+n, sizeof(int)); + for (k = 1; k <= n; k++) + { lux->F_row[k] = lux->F_col[k] = NULL; + mpq_init(lux->V_piv[k]); + mpq_set_si(lux->V_piv[k], 1, 1); + lux->V_row[k] = lux->V_col[k] = NULL; + lux->P_row[k] = lux->P_col[k] = k; + lux->Q_row[k] = lux->Q_col[k] = k; + } + lux->rank = n; + return lux; +} + +/*---------------------------------------------------------------------- +// initialize - initialize LU-factorization data structures. +// +// This routine initializes data structures for subsequent computing +// the LU-factorization of a given matrix A, which is specified by the +// formal routine col. On exit V = A and F = P = Q = I, where I is the +// unity matrix. */ + +static void initialize(LUX *lux, int (*col)(void *info, int j, + int ind[], mpq_t val[]), void *info, LUXWKA *wka) +{ int n = lux->n; + DMP *pool = lux->pool; + LUXELM **F_row = lux->F_row; + LUXELM **F_col = lux->F_col; + mpq_t *V_piv = lux->V_piv; + LUXELM **V_row = lux->V_row; + LUXELM **V_col = lux->V_col; + int *P_row = lux->P_row; + int *P_col = lux->P_col; + int *Q_row = lux->Q_row; + int *Q_col = lux->Q_col; + int *R_len = wka->R_len; + int *R_head = wka->R_head; + int *R_prev = wka->R_prev; + int *R_next = wka->R_next; + int *C_len = wka->C_len; + int *C_head = wka->C_head; + int *C_prev = wka->C_prev; + int *C_next = wka->C_next; + LUXELM *fij, *vij; + int i, j, k, len, *ind; + mpq_t *val; + /* F := I */ + for (i = 1; i <= n; i++) + { while (F_row[i] != NULL) + { fij = F_row[i], F_row[i] = fij->r_next; + mpq_clear(fij->val); + dmp_free_atom(pool, fij, sizeof(LUXELM)); + } + } + for (j = 1; j <= n; j++) F_col[j] = NULL; + /* V := 0 */ + for (k = 1; k <= n; k++) mpq_set_si(V_piv[k], 0, 1); + for (i = 1; i <= n; i++) + { while (V_row[i] != NULL) + { vij = V_row[i], V_row[i] = vij->r_next; + mpq_clear(vij->val); + dmp_free_atom(pool, vij, sizeof(LUXELM)); + } + } + for (j = 1; j <= n; j++) V_col[j] = NULL; + /* V := A */ + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(mpq_t)); + for (k = 1; k <= n; k++) mpq_init(val[k]); + for (j = 1; j <= n; j++) + { /* obtain j-th column of matrix A */ + len = col(info, j, ind, val); + if (!(0 <= len && len <= n)) + xfault("lux_decomp: j = %d: len = %d; invalid column length" + "\n", j, len); + /* copy elements of j-th column to matrix V */ + for (k = 1; k <= len; k++) + { /* get row index of a[i,j] */ + i = ind[k]; + if (!(1 <= i && i <= n)) + xfault("lux_decomp: j = %d: i = %d; row index out of ran" + "ge\n", j, i); + /* check for duplicate indices */ + if (V_row[i] != NULL && V_row[i]->j == j) + xfault("lux_decomp: j = %d: i = %d; duplicate row indice" + "s not allowed\n", j, i); + /* check for zero value */ + if (mpq_sgn(val[k]) == 0) + xfault("lux_decomp: j = %d: i = %d; zero elements not al" + "lowed\n", j, i); + /* add new element v[i,j] = a[i,j] to V */ + vij = dmp_get_atom(pool, sizeof(LUXELM)); + vij->i = i, vij->j = j; + mpq_init(vij->val); + mpq_set(vij->val, val[k]); + vij->r_prev = NULL; + vij->r_next = V_row[i]; + vij->c_prev = NULL; + vij->c_next = V_col[j]; + if (vij->r_next != NULL) vij->r_next->r_prev = vij; + if (vij->c_next != NULL) vij->c_next->c_prev = vij; + V_row[i] = V_col[j] = vij; + } + } + xfree(ind); + for (k = 1; k <= n; k++) mpq_clear(val[k]); + xfree(val); + /* P := Q := I */ + for (k = 1; k <= n; k++) + P_row[k] = P_col[k] = Q_row[k] = Q_col[k] = k; + /* the rank of A and V is not determined yet */ + lux->rank = -1; + /* initially the entire matrix V is active */ + /* determine its row lengths */ + for (i = 1; i <= n; i++) + { len = 0; + for (vij = V_row[i]; vij != NULL; vij = vij->r_next) len++; + R_len[i] = len; + } + /* build linked lists of active rows */ + for (len = 0; len <= n; len++) R_head[len] = 0; + for (i = 1; i <= n; i++) + { len = R_len[i]; + R_prev[i] = 0; + R_next[i] = R_head[len]; + if (R_next[i] != 0) R_prev[R_next[i]] = i; + R_head[len] = i; + } + /* determine its column lengths */ + for (j = 1; j <= n; j++) + { len = 0; + for (vij = V_col[j]; vij != NULL; vij = vij->c_next) len++; + C_len[j] = len; + } + /* build linked lists of active columns */ + for (len = 0; len <= n; len++) C_head[len] = 0; + for (j = 1; j <= n; j++) + { len = C_len[j]; + C_prev[j] = 0; + C_next[j] = C_head[len]; + if (C_next[j] != 0) C_prev[C_next[j]] = j; + C_head[len] = j; + } + return; +} + +/*---------------------------------------------------------------------- +// find_pivot - choose a pivot element. +// +// This routine chooses a pivot element v[p,q] in the active submatrix +// of matrix U = P*V*Q. +// +// It is assumed that on entry the matrix U has the following partially +// triangularized form: +// +// 1 k n +// 1 x x x x x x x x x x +// . x x x x x x x x x +// . . x x x x x x x x +// . . . x x x x x x x +// k . . . . * * * * * * +// . . . . * * * * * * +// . . . . * * * * * * +// . . . . * * * * * * +// . . . . * * * * * * +// n . . . . * * * * * * +// +// where rows and columns k, k+1, ..., n belong to the active submatrix +// (elements of the active submatrix are marked by '*'). +// +// Since the matrix U = P*V*Q is not stored, the routine works with the +// matrix V. It is assumed that the row-wise representation corresponds +// to the matrix V, but the column-wise representation corresponds to +// the active submatrix of the matrix V, i.e. elements of the matrix V, +// which does not belong to the active submatrix, are missing from the +// column linked lists. It is also assumed that each active row of the +// matrix V is in the set R[len], where len is number of non-zeros in +// the row, and each active column of the matrix V is in the set C[len], +// where len is number of non-zeros in the column (in the latter case +// only elements of the active submatrix are counted; such elements are +// marked by '*' on the figure above). +// +// Due to exact arithmetic any non-zero element of the active submatrix +// can be chosen as a pivot. However, to keep sparsity of the matrix V +// the routine uses Markowitz strategy, trying to choose such element +// v[p,q], which has smallest Markowitz cost (nr[p]-1) * (nc[q]-1), +// where nr[p] and nc[q] are the number of non-zero elements, resp., in +// p-th row and in q-th column of the active submatrix. +// +// In order to reduce the search, i.e. not to walk through all elements +// of the active submatrix, the routine exploits a technique proposed by +// I.Duff. This technique is based on using the sets R[len] and C[len] +// of active rows and columns. +// +// On exit the routine returns a pointer to a pivot v[p,q] chosen, or +// NULL, if the active submatrix is empty. */ + +static LUXELM *find_pivot(LUX *lux, LUXWKA *wka) +{ int n = lux->n; + LUXELM **V_row = lux->V_row; + LUXELM **V_col = lux->V_col; + int *R_len = wka->R_len; + int *R_head = wka->R_head; + int *R_next = wka->R_next; + int *C_len = wka->C_len; + int *C_head = wka->C_head; + int *C_next = wka->C_next; + LUXELM *piv, *some, *vij; + int i, j, len, min_len, ncand, piv_lim = 5; + double best, cost; + /* nothing is chosen so far */ + piv = NULL, best = DBL_MAX, ncand = 0; + /* if in the active submatrix there is a column that has the only + non-zero (column singleton), choose it as a pivot */ + j = C_head[1]; + if (j != 0) + { xassert(C_len[j] == 1); + piv = V_col[j]; + xassert(piv != NULL && piv->c_next == NULL); + goto done; + } + /* if in the active submatrix there is a row that has the only + non-zero (row singleton), choose it as a pivot */ + i = R_head[1]; + if (i != 0) + { xassert(R_len[i] == 1); + piv = V_row[i]; + xassert(piv != NULL && piv->r_next == NULL); + goto done; + } + /* there are no singletons in the active submatrix; walk through + other non-empty rows and columns */ + for (len = 2; len <= n; len++) + { /* consider active columns having len non-zeros */ + for (j = C_head[len]; j != 0; j = C_next[j]) + { /* j-th column has len non-zeros */ + /* find an element in the row of minimal length */ + some = NULL, min_len = INT_MAX; + for (vij = V_col[j]; vij != NULL; vij = vij->c_next) + { if (min_len > R_len[vij->i]) + some = vij, min_len = R_len[vij->i]; + /* if Markowitz cost of this element is not greater than + (len-1)**2, it can be chosen right now; this heuristic + reduces the search and works well in many cases */ + if (min_len <= len) + { piv = some; + goto done; + } + } + /* j-th column has been scanned */ + /* the minimal element found is a next pivot candidate */ + xassert(some != NULL); + ncand++; + /* compute its Markowitz cost */ + cost = (double)(min_len - 1) * (double)(len - 1); + /* choose between the current candidate and this element */ + if (cost < best) piv = some, best = cost; + /* if piv_lim candidates have been considered, there is a + doubt that a much better candidate exists; therefore it + is the time to terminate the search */ + if (ncand == piv_lim) goto done; + } + /* now consider active rows having len non-zeros */ + for (i = R_head[len]; i != 0; i = R_next[i]) + { /* i-th row has len non-zeros */ + /* find an element in the column of minimal length */ + some = NULL, min_len = INT_MAX; + for (vij = V_row[i]; vij != NULL; vij = vij->r_next) + { if (min_len > C_len[vij->j]) + some = vij, min_len = C_len[vij->j]; + /* if Markowitz cost of this element is not greater than + (len-1)**2, it can be chosen right now; this heuristic + reduces the search and works well in many cases */ + if (min_len <= len) + { piv = some; + goto done; + } + } + /* i-th row has been scanned */ + /* the minimal element found is a next pivot candidate */ + xassert(some != NULL); + ncand++; + /* compute its Markowitz cost */ + cost = (double)(len - 1) * (double)(min_len - 1); + /* choose between the current candidate and this element */ + if (cost < best) piv = some, best = cost; + /* if piv_lim candidates have been considered, there is a + doubt that a much better candidate exists; therefore it + is the time to terminate the search */ + if (ncand == piv_lim) goto done; + } + } +done: /* bring the pivot v[p,q] to the factorizing routine */ + return piv; +} + +/*---------------------------------------------------------------------- +// eliminate - perform gaussian elimination. +// +// This routine performs elementary gaussian transformations in order +// to eliminate subdiagonal elements in the k-th column of the matrix +// U = P*V*Q using the pivot element u[k,k], where k is the number of +// the current elimination step. +// +// The parameter piv specifies the pivot element v[p,q] = u[k,k]. +// +// Each time when the routine applies the elementary transformation to +// a non-pivot row of the matrix V, it stores the corresponding element +// to the matrix F in order to keep the main equality A = F*V. +// +// The routine assumes that on entry the matrices L = P*F*inv(P) and +// U = P*V*Q are the following: +// +// 1 k 1 k n +// 1 1 . . . . . . . . . 1 x x x x x x x x x x +// x 1 . . . . . . . . . x x x x x x x x x +// x x 1 . . . . . . . . . x x x x x x x x +// x x x 1 . . . . . . . . . x x x x x x x +// k x x x x 1 . . . . . k . . . . * * * * * * +// x x x x _ 1 . . . . . . . . # * * * * * +// x x x x _ . 1 . . . . . . . # * * * * * +// x x x x _ . . 1 . . . . . . # * * * * * +// x x x x _ . . . 1 . . . . . # * * * * * +// n x x x x _ . . . . 1 n . . . . # * * * * * +// +// matrix L matrix U +// +// where rows and columns of the matrix U with numbers k, k+1, ..., n +// form the active submatrix (eliminated elements are marked by '#' and +// other elements of the active submatrix are marked by '*'). Note that +// each eliminated non-zero element u[i,k] of the matrix U gives the +// corresponding element l[i,k] of the matrix L (marked by '_'). +// +// Actually all operations are performed on the matrix V. Should note +// that the row-wise representation corresponds to the matrix V, but the +// column-wise representation corresponds to the active submatrix of the +// matrix V, i.e. elements of the matrix V, which doesn't belong to the +// active submatrix, are missing from the column linked lists. +// +// Let u[k,k] = v[p,q] be the pivot. In order to eliminate subdiagonal +// elements u[i',k] = v[i,q], i' = k+1, k+2, ..., n, the routine applies +// the following elementary gaussian transformations: +// +// (i-th row of V) := (i-th row of V) - f[i,p] * (p-th row of V), +// +// where f[i,p] = v[i,q] / v[p,q] is a gaussian multiplier. +// +// Additionally, in order to keep the main equality A = F*V, each time +// when the routine applies the transformation to i-th row of the matrix +// V, it also adds f[i,p] as a new element to the matrix F. +// +// IMPORTANT: On entry the working arrays flag and work should contain +// zeros. This status is provided by the routine on exit. */ + +static void eliminate(LUX *lux, LUXWKA *wka, LUXELM *piv, int flag[], + mpq_t work[]) +{ DMP *pool = lux->pool; + LUXELM **F_row = lux->F_row; + LUXELM **F_col = lux->F_col; + mpq_t *V_piv = lux->V_piv; + LUXELM **V_row = lux->V_row; + LUXELM **V_col = lux->V_col; + int *R_len = wka->R_len; + int *R_head = wka->R_head; + int *R_prev = wka->R_prev; + int *R_next = wka->R_next; + int *C_len = wka->C_len; + int *C_head = wka->C_head; + int *C_prev = wka->C_prev; + int *C_next = wka->C_next; + LUXELM *fip, *vij, *vpj, *viq, *next; + mpq_t temp; + int i, j, p, q; + mpq_init(temp); + /* determine row and column indices of the pivot v[p,q] */ + xassert(piv != NULL); + p = piv->i, q = piv->j; + /* remove p-th (pivot) row from the active set; it will never + return there */ + if (R_prev[p] == 0) + R_head[R_len[p]] = R_next[p]; + else + R_next[R_prev[p]] = R_next[p]; + if (R_next[p] == 0) + ; + else + R_prev[R_next[p]] = R_prev[p]; + /* remove q-th (pivot) column from the active set; it will never + return there */ + if (C_prev[q] == 0) + C_head[C_len[q]] = C_next[q]; + else + C_next[C_prev[q]] = C_next[q]; + if (C_next[q] == 0) + ; + else + C_prev[C_next[q]] = C_prev[q]; + /* store the pivot value in a separate array */ + mpq_set(V_piv[p], piv->val); + /* remove the pivot from p-th row */ + if (piv->r_prev == NULL) + V_row[p] = piv->r_next; + else + piv->r_prev->r_next = piv->r_next; + if (piv->r_next == NULL) + ; + else + piv->r_next->r_prev = piv->r_prev; + R_len[p]--; + /* remove the pivot from q-th column */ + if (piv->c_prev == NULL) + V_col[q] = piv->c_next; + else + piv->c_prev->c_next = piv->c_next; + if (piv->c_next == NULL) + ; + else + piv->c_next->c_prev = piv->c_prev; + C_len[q]--; + /* free the space occupied by the pivot */ + mpq_clear(piv->val); + dmp_free_atom(pool, piv, sizeof(LUXELM)); + /* walk through p-th (pivot) row, which already does not contain + the pivot v[p,q], and do the following... */ + for (vpj = V_row[p]; vpj != NULL; vpj = vpj->r_next) + { /* get column index of v[p,j] */ + j = vpj->j; + /* store v[p,j] in the working array */ + flag[j] = 1; + mpq_set(work[j], vpj->val); + /* remove j-th column from the active set; it will return there + later with a new length */ + if (C_prev[j] == 0) + C_head[C_len[j]] = C_next[j]; + else + C_next[C_prev[j]] = C_next[j]; + if (C_next[j] == 0) + ; + else + C_prev[C_next[j]] = C_prev[j]; + /* v[p,j] leaves the active submatrix, so remove it from j-th + column; however, v[p,j] is kept in p-th row */ + if (vpj->c_prev == NULL) + V_col[j] = vpj->c_next; + else + vpj->c_prev->c_next = vpj->c_next; + if (vpj->c_next == NULL) + ; + else + vpj->c_next->c_prev = vpj->c_prev; + C_len[j]--; + } + /* now walk through q-th (pivot) column, which already does not + contain the pivot v[p,q], and perform gaussian elimination */ + while (V_col[q] != NULL) + { /* element v[i,q] has to be eliminated */ + viq = V_col[q]; + /* get row index of v[i,q] */ + i = viq->i; + /* remove i-th row from the active set; later it will return + there with a new length */ + if (R_prev[i] == 0) + R_head[R_len[i]] = R_next[i]; + else + R_next[R_prev[i]] = R_next[i]; + if (R_next[i] == 0) + ; + else + R_prev[R_next[i]] = R_prev[i]; + /* compute gaussian multiplier f[i,p] = v[i,q] / v[p,q] and + store it in the matrix F */ + fip = dmp_get_atom(pool, sizeof(LUXELM)); + fip->i = i, fip->j = p; + mpq_init(fip->val); + mpq_div(fip->val, viq->val, V_piv[p]); + fip->r_prev = NULL; + fip->r_next = F_row[i]; + fip->c_prev = NULL; + fip->c_next = F_col[p]; + if (fip->r_next != NULL) fip->r_next->r_prev = fip; + if (fip->c_next != NULL) fip->c_next->c_prev = fip; + F_row[i] = F_col[p] = fip; + /* v[i,q] has to be eliminated, so remove it from i-th row */ + if (viq->r_prev == NULL) + V_row[i] = viq->r_next; + else + viq->r_prev->r_next = viq->r_next; + if (viq->r_next == NULL) + ; + else + viq->r_next->r_prev = viq->r_prev; + R_len[i]--; + /* and also from q-th column */ + V_col[q] = viq->c_next; + C_len[q]--; + /* free the space occupied by v[i,q] */ + mpq_clear(viq->val); + dmp_free_atom(pool, viq, sizeof(LUXELM)); + /* perform gaussian transformation: + (i-th row) := (i-th row) - f[i,p] * (p-th row) + note that now p-th row, which is in the working array, + does not contain the pivot v[p,q], and i-th row does not + contain the element v[i,q] to be eliminated */ + /* walk through i-th row and transform existing non-zero + elements */ + for (vij = V_row[i]; vij != NULL; vij = next) + { next = vij->r_next; + /* get column index of v[i,j] */ + j = vij->j; + /* v[i,j] := v[i,j] - f[i,p] * v[p,j] */ + if (flag[j]) + { /* v[p,j] != 0 */ + flag[j] = 0; + mpq_mul(temp, fip->val, work[j]); + mpq_sub(vij->val, vij->val, temp); + if (mpq_sgn(vij->val) == 0) + { /* new v[i,j] is zero, so remove it from the active + submatrix */ + /* remove v[i,j] from i-th row */ + if (vij->r_prev == NULL) + V_row[i] = vij->r_next; + else + vij->r_prev->r_next = vij->r_next; + if (vij->r_next == NULL) + ; + else + vij->r_next->r_prev = vij->r_prev; + R_len[i]--; + /* remove v[i,j] from j-th column */ + if (vij->c_prev == NULL) + V_col[j] = vij->c_next; + else + vij->c_prev->c_next = vij->c_next; + if (vij->c_next == NULL) + ; + else + vij->c_next->c_prev = vij->c_prev; + C_len[j]--; + /* free the space occupied by v[i,j] */ + mpq_clear(vij->val); + dmp_free_atom(pool, vij, sizeof(LUXELM)); + } + } + } + /* now flag is the pattern of the set v[p,*] \ v[i,*] */ + /* walk through p-th (pivot) row and create new elements in + i-th row, which appear due to fill-in */ + for (vpj = V_row[p]; vpj != NULL; vpj = vpj->r_next) + { j = vpj->j; + if (flag[j]) + { /* create new non-zero v[i,j] = 0 - f[i,p] * v[p,j] and + add it to i-th row and j-th column */ + vij = dmp_get_atom(pool, sizeof(LUXELM)); + vij->i = i, vij->j = j; + mpq_init(vij->val); + mpq_mul(vij->val, fip->val, work[j]); + mpq_neg(vij->val, vij->val); + vij->r_prev = NULL; + vij->r_next = V_row[i]; + vij->c_prev = NULL; + vij->c_next = V_col[j]; + if (vij->r_next != NULL) vij->r_next->r_prev = vij; + if (vij->c_next != NULL) vij->c_next->c_prev = vij; + V_row[i] = V_col[j] = vij; + R_len[i]++, C_len[j]++; + } + else + { /* there is no fill-in, because v[i,j] already exists in + i-th row; restore the flag, which was reset before */ + flag[j] = 1; + } + } + /* now i-th row has been completely transformed and can return + to the active set with a new length */ + R_prev[i] = 0; + R_next[i] = R_head[R_len[i]]; + if (R_next[i] != 0) R_prev[R_next[i]] = i; + R_head[R_len[i]] = i; + } + /* at this point q-th (pivot) column must be empty */ + xassert(C_len[q] == 0); + /* walk through p-th (pivot) row again and do the following... */ + for (vpj = V_row[p]; vpj != NULL; vpj = vpj->r_next) + { /* get column index of v[p,j] */ + j = vpj->j; + /* erase v[p,j] from the working array */ + flag[j] = 0; + mpq_set_si(work[j], 0, 1); + /* now j-th column has been completely transformed, so it can + return to the active list with a new length */ + C_prev[j] = 0; + C_next[j] = C_head[C_len[j]]; + if (C_next[j] != 0) C_prev[C_next[j]] = j; + C_head[C_len[j]] = j; + } + mpq_clear(temp); + /* return to the factorizing routine */ + return; +} + +/*---------------------------------------------------------------------- +// lux_decomp - compute LU-factorization. +// +// SYNOPSIS +// +// #include "glplux.h" +// int lux_decomp(LUX *lux, int (*col)(void *info, int j, int ind[], +// mpq_t val[]), void *info); +// +// DESCRIPTION +// +// The routine lux_decomp computes LU-factorization of a given square +// matrix A. +// +// The parameter lux specifies LU-factorization data structure built by +// means of the routine lux_create. +// +// The formal routine col specifies the original matrix A. In order to +// obtain j-th column of the matrix A the routine lux_decomp calls the +// routine col with the parameter j (1 <= j <= n, where n is the order +// of A). In response the routine col should store row indices and +// numerical values of non-zero elements of j-th column of A to the +// locations ind[1], ..., ind[len] and val[1], ..., val[len], resp., +// where len is the number of non-zeros in j-th column, which should be +// returned on exit. Neiter zero nor duplicate elements are allowed. +// +// The parameter info is a transit pointer passed to the formal routine +// col; it can be used for various purposes. +// +// RETURNS +// +// The routine lux_decomp returns the singularity flag. Zero flag means +// that the original matrix A is non-singular while non-zero flag means +// that A is (exactly!) singular. +// +// Note that LU-factorization is valid in both cases, however, in case +// of singularity some rows of the matrix V (including pivot elements) +// will be empty. +// +// REPAIRING SINGULAR MATRIX +// +// If the routine lux_decomp returns non-zero flag, it provides all +// necessary information that can be used for "repairing" the matrix A, +// where "repairing" means replacing linearly dependent columns of the +// matrix A by appropriate columns of the unity matrix. This feature is +// needed when the routine lux_decomp is used for reinverting the basis +// matrix within the simplex method procedure. +// +// On exit linearly dependent columns of the matrix U have the numbers +// rank+1, rank+2, ..., n, where rank is the exact rank of the matrix A +// stored by the routine to the member lux->rank. The correspondence +// between columns of A and U is the same as between columns of V and U. +// Thus, linearly dependent columns of the matrix A have the numbers +// Q_col[rank+1], Q_col[rank+2], ..., Q_col[n], where Q_col is an array +// representing the permutation matrix Q in column-like format. It is +// understood that each j-th linearly dependent column of the matrix U +// should be replaced by the unity vector, where all elements are zero +// except the unity diagonal element u[j,j]. On the other hand j-th row +// of the matrix U corresponds to the row of the matrix V (and therefore +// of the matrix A) with the number P_row[j], where P_row is an array +// representing the permutation matrix P in row-like format. Thus, each +// j-th linearly dependent column of the matrix U should be replaced by +// a column of the unity matrix with the number P_row[j]. +// +// The code that repairs the matrix A may look like follows: +// +// for (j = rank+1; j <= n; j++) +// { replace column Q_col[j] of the matrix A by column P_row[j] of +// the unity matrix; +// } +// +// where rank, P_row, and Q_col are members of the structure LUX. */ + +int lux_decomp(LUX *lux, int (*col)(void *info, int j, int ind[], + mpq_t val[]), void *info) +{ int n = lux->n; + LUXELM **V_row = lux->V_row; + LUXELM **V_col = lux->V_col; + int *P_row = lux->P_row; + int *P_col = lux->P_col; + int *Q_row = lux->Q_row; + int *Q_col = lux->Q_col; + LUXELM *piv, *vij; + LUXWKA *wka; + int i, j, k, p, q, t, *flag; + mpq_t *work; + /* allocate working area */ + wka = xmalloc(sizeof(LUXWKA)); + wka->R_len = xcalloc(1+n, sizeof(int)); + wka->R_head = xcalloc(1+n, sizeof(int)); + wka->R_prev = xcalloc(1+n, sizeof(int)); + wka->R_next = xcalloc(1+n, sizeof(int)); + wka->C_len = xcalloc(1+n, sizeof(int)); + wka->C_head = xcalloc(1+n, sizeof(int)); + wka->C_prev = xcalloc(1+n, sizeof(int)); + wka->C_next = xcalloc(1+n, sizeof(int)); + /* initialize LU-factorization data structures */ + initialize(lux, col, info, wka); + /* allocate working arrays */ + flag = xcalloc(1+n, sizeof(int)); + work = xcalloc(1+n, sizeof(mpq_t)); + for (k = 1; k <= n; k++) + { flag[k] = 0; + mpq_init(work[k]); + } + /* main elimination loop */ + for (k = 1; k <= n; k++) + { /* choose a pivot element v[p,q] */ + piv = find_pivot(lux, wka); + if (piv == NULL) + { /* no pivot can be chosen, because the active submatrix is + empty */ + break; + } + /* determine row and column indices of the pivot element */ + p = piv->i, q = piv->j; + /* let v[p,q] correspond to u[i',j']; permute k-th and i'-th + rows and k-th and j'-th columns of the matrix U = P*V*Q to + move the element u[i',j'] to the position u[k,k] */ + i = P_col[p], j = Q_row[q]; + xassert(k <= i && i <= n && k <= j && j <= n); + /* permute k-th and i-th rows of the matrix U */ + t = P_row[k]; + P_row[i] = t, P_col[t] = i; + P_row[k] = p, P_col[p] = k; + /* permute k-th and j-th columns of the matrix U */ + t = Q_col[k]; + Q_col[j] = t, Q_row[t] = j; + Q_col[k] = q, Q_row[q] = k; + /* eliminate subdiagonal elements of k-th column of the matrix + U = P*V*Q using the pivot element u[k,k] = v[p,q] */ + eliminate(lux, wka, piv, flag, work); + } + /* determine the rank of A (and V) */ + lux->rank = k - 1; + /* free working arrays */ + xfree(flag); + for (k = 1; k <= n; k++) mpq_clear(work[k]); + xfree(work); + /* build column lists of the matrix V using its row lists */ + for (j = 1; j <= n; j++) + xassert(V_col[j] == NULL); + for (i = 1; i <= n; i++) + { for (vij = V_row[i]; vij != NULL; vij = vij->r_next) + { j = vij->j; + vij->c_prev = NULL; + vij->c_next = V_col[j]; + if (vij->c_next != NULL) vij->c_next->c_prev = vij; + V_col[j] = vij; + } + } + /* free working area */ + xfree(wka->R_len); + xfree(wka->R_head); + xfree(wka->R_prev); + xfree(wka->R_next); + xfree(wka->C_len); + xfree(wka->C_head); + xfree(wka->C_prev); + xfree(wka->C_next); + xfree(wka); + /* return to the calling program */ + return (lux->rank < n); +} + +/*---------------------------------------------------------------------- +// lux_f_solve - solve system F*x = b or F'*x = b. +// +// SYNOPSIS +// +// #include "glplux.h" +// void lux_f_solve(LUX *lux, int tr, mpq_t x[]); +// +// DESCRIPTION +// +// The routine lux_f_solve solves either the system F*x = b (if the +// flag tr is zero) or the system F'*x = b (if the flag tr is non-zero), +// where the matrix F is a component of LU-factorization specified by +// the parameter lux, F' is a matrix transposed to F. +// +// On entry the array x should contain elements of the right-hand side +// vector b in locations x[1], ..., x[n], where n is the order of the +// matrix F. On exit this array will contain elements of the solution +// vector x in the same locations. */ + +void lux_f_solve(LUX *lux, int tr, mpq_t x[]) +{ int n = lux->n; + LUXELM **F_row = lux->F_row; + LUXELM **F_col = lux->F_col; + int *P_row = lux->P_row; + LUXELM *fik, *fkj; + int i, j, k; + mpq_t temp; + mpq_init(temp); + if (!tr) + { /* solve the system F*x = b */ + for (j = 1; j <= n; j++) + { k = P_row[j]; + if (mpq_sgn(x[k]) != 0) + { for (fik = F_col[k]; fik != NULL; fik = fik->c_next) + { mpq_mul(temp, fik->val, x[k]); + mpq_sub(x[fik->i], x[fik->i], temp); + } + } + } + } + else + { /* solve the system F'*x = b */ + for (i = n; i >= 1; i--) + { k = P_row[i]; + if (mpq_sgn(x[k]) != 0) + { for (fkj = F_row[k]; fkj != NULL; fkj = fkj->r_next) + { mpq_mul(temp, fkj->val, x[k]); + mpq_sub(x[fkj->j], x[fkj->j], temp); + } + } + } + } + mpq_clear(temp); + return; +} + +/*---------------------------------------------------------------------- +// lux_v_solve - solve system V*x = b or V'*x = b. +// +// SYNOPSIS +// +// #include "glplux.h" +// void lux_v_solve(LUX *lux, int tr, double x[]); +// +// DESCRIPTION +// +// The routine lux_v_solve solves either the system V*x = b (if the +// flag tr is zero) or the system V'*x = b (if the flag tr is non-zero), +// where the matrix V is a component of LU-factorization specified by +// the parameter lux, V' is a matrix transposed to V. +// +// On entry the array x should contain elements of the right-hand side +// vector b in locations x[1], ..., x[n], where n is the order of the +// matrix V. On exit this array will contain elements of the solution +// vector x in the same locations. */ + +void lux_v_solve(LUX *lux, int tr, mpq_t x[]) +{ int n = lux->n; + mpq_t *V_piv = lux->V_piv; + LUXELM **V_row = lux->V_row; + LUXELM **V_col = lux->V_col; + int *P_row = lux->P_row; + int *Q_col = lux->Q_col; + LUXELM *vij; + int i, j, k; + mpq_t *b, temp; + b = xcalloc(1+n, sizeof(mpq_t)); + for (k = 1; k <= n; k++) + mpq_init(b[k]), mpq_set(b[k], x[k]), mpq_set_si(x[k], 0, 1); + mpq_init(temp); + if (!tr) + { /* solve the system V*x = b */ + for (k = n; k >= 1; k--) + { i = P_row[k], j = Q_col[k]; + if (mpq_sgn(b[i]) != 0) + { mpq_set(x[j], b[i]); + mpq_div(x[j], x[j], V_piv[i]); + for (vij = V_col[j]; vij != NULL; vij = vij->c_next) + { mpq_mul(temp, vij->val, x[j]); + mpq_sub(b[vij->i], b[vij->i], temp); + } + } + } + } + else + { /* solve the system V'*x = b */ + for (k = 1; k <= n; k++) + { i = P_row[k], j = Q_col[k]; + if (mpq_sgn(b[j]) != 0) + { mpq_set(x[i], b[j]); + mpq_div(x[i], x[i], V_piv[i]); + for (vij = V_row[i]; vij != NULL; vij = vij->r_next) + { mpq_mul(temp, vij->val, x[i]); + mpq_sub(b[vij->j], b[vij->j], temp); + } + } + } + } + for (k = 1; k <= n; k++) mpq_clear(b[k]); + mpq_clear(temp); + xfree(b); + return; +} + +/*---------------------------------------------------------------------- +// lux_solve - solve system A*x = b or A'*x = b. +// +// SYNOPSIS +// +// #include "glplux.h" +// void lux_solve(LUX *lux, int tr, mpq_t x[]); +// +// DESCRIPTION +// +// The routine lux_solve solves either the system A*x = b (if the flag +// tr is zero) or the system A'*x = b (if the flag tr is non-zero), +// where the parameter lux specifies LU-factorization of the matrix A, +// A' is a matrix transposed to A. +// +// On entry the array x should contain elements of the right-hand side +// vector b in locations x[1], ..., x[n], where n is the order of the +// matrix A. On exit this array will contain elements of the solution +// vector x in the same locations. */ + +void lux_solve(LUX *lux, int tr, mpq_t x[]) +{ if (lux->rank < lux->n) + xfault("lux_solve: LU-factorization has incomplete rank\n"); + if (!tr) + { /* A = F*V, therefore inv(A) = inv(V)*inv(F) */ + lux_f_solve(lux, 0, x); + lux_v_solve(lux, 0, x); + } + else + { /* A' = V'*F', therefore inv(A') = inv(F')*inv(V') */ + lux_v_solve(lux, 1, x); + lux_f_solve(lux, 1, x); + } + return; +} + +/*---------------------------------------------------------------------- +// lux_delete - delete LU-factorization. +// +// SYNOPSIS +// +// #include "glplux.h" +// void lux_delete(LUX *lux); +// +// DESCRIPTION +// +// The routine lux_delete deletes LU-factorization data structure, +// which the parameter lux points to, freeing all the memory allocated +// to this object. */ + +void lux_delete(LUX *lux) +{ int n = lux->n; + LUXELM *fij, *vij; + int i; + for (i = 1; i <= n; i++) + { for (fij = lux->F_row[i]; fij != NULL; fij = fij->r_next) + mpq_clear(fij->val); + mpq_clear(lux->V_piv[i]); + for (vij = lux->V_row[i]; vij != NULL; vij = vij->r_next) + mpq_clear(vij->val); + } + dmp_delete_pool(lux->pool); + xfree(lux->F_row); + xfree(lux->F_col); + xfree(lux->V_piv); + xfree(lux->V_row); + xfree(lux->V_col); + xfree(lux->P_row); + xfree(lux->P_col); + xfree(lux->Q_row); + xfree(lux->Q_col); + xfree(lux); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glplux.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glplux.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,221 @@ +/* glplux.h (LU-factorization, bignum arithmetic) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPLUX_H +#define GLPLUX_H + +#include "glpdmp.h" +#include "glpgmp.h" + +/*---------------------------------------------------------------------- +// The structure LUX defines LU-factorization of a square matrix A, +// which is the following quartet: +// +// [A] = (F, V, P, Q), (1) +// +// where F and V are such matrices that +// +// A = F * V, (2) +// +// and P and Q are such permutation matrices that the matrix +// +// L = P * F * inv(P) (3) +// +// is lower triangular with unity diagonal, and the matrix +// +// U = P * V * Q (4) +// +// is upper triangular. All the matrices have the order n. +// +// The matrices F and V are stored in row/column-wise sparse format as +// row and column linked lists of non-zero elements. Unity elements on +// the main diagonal of the matrix F are not stored. Pivot elements of +// the matrix V (that correspond to diagonal elements of the matrix U) +// are also missing from the row and column lists and stored separately +// in an ordinary array. +// +// The permutation matrices P and Q are stored as ordinary arrays using +// both row- and column-like formats. +// +// The matrices L and U being completely defined by the matrices F, V, +// P, and Q are not stored explicitly. +// +// It is easy to show that the factorization (1)-(3) is some version of +// LU-factorization. Indeed, from (3) and (4) it follows that: +// +// F = inv(P) * L * P, +// +// V = inv(P) * U * inv(Q), +// +// and substitution into (2) gives: +// +// A = F * V = inv(P) * L * U * inv(Q). +// +// For more details see the program documentation. */ + +typedef struct LUX LUX; +typedef struct LUXELM LUXELM; +typedef struct LUXWKA LUXWKA; + +struct LUX +{ /* LU-factorization of a square matrix */ + int n; + /* the order of matrices A, F, V, P, Q */ + DMP *pool; + /* memory pool for elements of matrices F and V */ + LUXELM **F_row; /* LUXELM *F_row[1+n]; */ + /* F_row[0] is not used; + F_row[i], 1 <= i <= n, is a pointer to the list of elements in + i-th row of matrix F (diagonal elements are not stored) */ + LUXELM **F_col; /* LUXELM *F_col[1+n]; */ + /* F_col[0] is not used; + F_col[j], 1 <= j <= n, is a pointer to the list of elements in + j-th column of matrix F (diagonal elements are not stored) */ + mpq_t *V_piv; /* mpq_t V_piv[1+n]; */ + /* V_piv[0] is not used; + V_piv[p], 1 <= p <= n, is a pivot element v[p,q] corresponding + to a diagonal element u[k,k] of matrix U = P*V*Q (used on k-th + elimination step, k = 1, 2, ..., n) */ + LUXELM **V_row; /* LUXELM *V_row[1+n]; */ + /* V_row[0] is not used; + V_row[i], 1 <= i <= n, is a pointer to the list of elements in + i-th row of matrix V (except pivot elements) */ + LUXELM **V_col; /* LUXELM *V_col[1+n]; */ + /* V_col[0] is not used; + V_col[j], 1 <= j <= n, is a pointer to the list of elements in + j-th column of matrix V (except pivot elements) */ + int *P_row; /* int P_row[1+n]; */ + /* P_row[0] is not used; + P_row[i] = j means that p[i,j] = 1, where p[i,j] is an element + of permutation matrix P */ + int *P_col; /* int P_col[1+n]; */ + /* P_col[0] is not used; + P_col[j] = i means that p[i,j] = 1, where p[i,j] is an element + of permutation matrix P */ + /* if i-th row or column of matrix F is i'-th row or column of + matrix L = P*F*inv(P), or if i-th row of matrix V is i'-th row + of matrix U = P*V*Q, then P_row[i'] = i and P_col[i] = i' */ + int *Q_row; /* int Q_row[1+n]; */ + /* Q_row[0] is not used; + Q_row[i] = j means that q[i,j] = 1, where q[i,j] is an element + of permutation matrix Q */ + int *Q_col; /* int Q_col[1+n]; */ + /* Q_col[0] is not used; + Q_col[j] = i means that q[i,j] = 1, where q[i,j] is an element + of permutation matrix Q */ + /* if j-th column of matrix V is j'-th column of matrix U = P*V*Q, + then Q_row[j] = j' and Q_col[j'] = j */ + int rank; + /* the (exact) rank of matrices A and V */ +}; + +struct LUXELM +{ /* element of matrix F or V */ + int i; + /* row index, 1 <= i <= m */ + int j; + /* column index, 1 <= j <= n */ + mpq_t val; + /* numeric (non-zero) element value */ + LUXELM *r_prev; + /* pointer to previous element in the same row */ + LUXELM *r_next; + /* pointer to next element in the same row */ + LUXELM *c_prev; + /* pointer to previous element in the same column */ + LUXELM *c_next; + /* pointer to next element in the same column */ +}; + +struct LUXWKA +{ /* working area (used only during factorization) */ + /* in order to efficiently implement Markowitz strategy and Duff + search technique there are two families {R[0], R[1], ..., R[n]} + and {C[0], C[1], ..., C[n]}; member R[k] is a set of active + rows of matrix V having k non-zeros, and member C[k] is a set + of active columns of matrix V having k non-zeros (in the active + submatrix); each set R[k] and C[k] is implemented as a separate + doubly linked list */ + int *R_len; /* int R_len[1+n]; */ + /* R_len[0] is not used; + R_len[i], 1 <= i <= n, is the number of non-zero elements in + i-th row of matrix V (that is the length of i-th row) */ + int *R_head; /* int R_head[1+n]; */ + /* R_head[k], 0 <= k <= n, is the number of a first row, which is + active and whose length is k */ + int *R_prev; /* int R_prev[1+n]; */ + /* R_prev[0] is not used; + R_prev[i], 1 <= i <= n, is the number of a previous row, which + is active and has the same length as i-th row */ + int *R_next; /* int R_next[1+n]; */ + /* R_prev[0] is not used; + R_prev[i], 1 <= i <= n, is the number of a next row, which is + active and has the same length as i-th row */ + int *C_len; /* int C_len[1+n]; */ + /* C_len[0] is not used; + C_len[j], 1 <= j <= n, is the number of non-zero elements in + j-th column of the active submatrix of matrix V (that is the + length of j-th column in the active submatrix) */ + int *C_head; /* int C_head[1+n]; */ + /* C_head[k], 0 <= k <= n, is the number of a first column, which + is active and whose length is k */ + int *C_prev; /* int C_prev[1+n]; */ + /* C_prev[0] is not used; + C_prev[j], 1 <= j <= n, is the number of a previous column, + which is active and has the same length as j-th column */ + int *C_next; /* int C_next[1+n]; */ + /* C_next[0] is not used; + C_next[j], 1 <= j <= n, is the number of a next column, which + is active and has the same length as j-th column */ +}; + +#define lux_create _glp_lux_create +#define lux_decomp _glp_lux_decomp +#define lux_f_solve _glp_lux_f_solve +#define lux_v_solve _glp_lux_v_solve +#define lux_solve _glp_lux_solve +#define lux_delete _glp_lux_delete + +LUX *lux_create(int n); +/* create LU-factorization */ + +int lux_decomp(LUX *lux, int (*col)(void *info, int j, int ind[], + mpq_t val[]), void *info); +/* compute LU-factorization */ + +void lux_f_solve(LUX *lux, int tr, mpq_t x[]); +/* solve system F*x = b or F'*x = b */ + +void lux_v_solve(LUX *lux, int tr, mpq_t x[]); +/* solve system V*x = b or V'*x = b */ + +void lux_solve(LUX *lux, int tr, mpq_t x[]); +/* solve system A*x = b or A'*x = b */ + +void lux_delete(LUX *lux); +/* delete LU-factorization */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmat.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,924 @@ +/* glpmat.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glpmat.h" +#include "glpqmd.h" +#include "amd/amd.h" +#include "colamd/colamd.h" + +/*---------------------------------------------------------------------- +-- check_fvs - check sparse vector in full-vector storage format. +-- +-- SYNOPSIS +-- +-- #include "glpmat.h" +-- int check_fvs(int n, int nnz, int ind[], double vec[]); +-- +-- DESCRIPTION +-- +-- The routine check_fvs checks if a given vector of dimension n in +-- full-vector storage format has correct representation. +-- +-- RETURNS +-- +-- The routine returns one of the following codes: +-- +-- 0 - the vector is correct; +-- 1 - the number of elements (n) is negative; +-- 2 - the number of non-zero elements (nnz) is negative; +-- 3 - some element index is out of range; +-- 4 - some element index is duplicate; +-- 5 - some non-zero element is out of pattern. */ + +int check_fvs(int n, int nnz, int ind[], double vec[]) +{ int i, t, ret, *flag = NULL; + /* check the number of elements */ + if (n < 0) + { ret = 1; + goto done; + } + /* check the number of non-zero elements */ + if (nnz < 0) + { ret = 2; + goto done; + } + /* check vector indices */ + flag = xcalloc(1+n, sizeof(int)); + for (i = 1; i <= n; i++) flag[i] = 0; + for (t = 1; t <= nnz; t++) + { i = ind[t]; + if (!(1 <= i && i <= n)) + { ret = 3; + goto done; + } + if (flag[i]) + { ret = 4; + goto done; + } + flag[i] = 1; + } + /* check vector elements */ + for (i = 1; i <= n; i++) + { if (!flag[i] && vec[i] != 0.0) + { ret = 5; + goto done; + } + } + /* the vector is ok */ + ret = 0; +done: if (flag != NULL) xfree(flag); + return ret; +} + +/*---------------------------------------------------------------------- +-- check_pattern - check pattern of sparse matrix. +-- +-- SYNOPSIS +-- +-- #include "glpmat.h" +-- int check_pattern(int m, int n, int A_ptr[], int A_ind[]); +-- +-- DESCRIPTION +-- +-- The routine check_pattern checks the pattern of a given mxn matrix +-- in storage-by-rows format. +-- +-- RETURNS +-- +-- The routine returns one of the following codes: +-- +-- 0 - the pattern is correct; +-- 1 - the number of rows (m) is negative; +-- 2 - the number of columns (n) is negative; +-- 3 - A_ptr[1] is not 1; +-- 4 - some column index is out of range; +-- 5 - some column indices are duplicate. */ + +int check_pattern(int m, int n, int A_ptr[], int A_ind[]) +{ int i, j, ptr, ret, *flag = NULL; + /* check the number of rows */ + if (m < 0) + { ret = 1; + goto done; + } + /* check the number of columns */ + if (n < 0) + { ret = 2; + goto done; + } + /* check location A_ptr[1] */ + if (A_ptr[1] != 1) + { ret = 3; + goto done; + } + /* check row patterns */ + flag = xcalloc(1+n, sizeof(int)); + for (j = 1; j <= n; j++) flag[j] = 0; + for (i = 1; i <= m; i++) + { /* check pattern of row i */ + for (ptr = A_ptr[i]; ptr < A_ptr[i+1]; ptr++) + { j = A_ind[ptr]; + /* check column index */ + if (!(1 <= j && j <= n)) + { ret = 4; + goto done; + } + /* check for duplication */ + if (flag[j]) + { ret = 5; + goto done; + } + flag[j] = 1; + } + /* clear flags */ + for (ptr = A_ptr[i]; ptr < A_ptr[i+1]; ptr++) + { j = A_ind[ptr]; + flag[j] = 0; + } + } + /* the pattern is ok */ + ret = 0; +done: if (flag != NULL) xfree(flag); + return ret; +} + +/*---------------------------------------------------------------------- +-- transpose - transpose sparse matrix. +-- +-- *Synopsis* +-- +-- #include "glpmat.h" +-- void transpose(int m, int n, int A_ptr[], int A_ind[], +-- double A_val[], int AT_ptr[], int AT_ind[], double AT_val[]); +-- +-- *Description* +-- +-- For a given mxn sparse matrix A the routine transpose builds a nxm +-- sparse matrix A' which is a matrix transposed to A. +-- +-- The arrays A_ptr, A_ind, and A_val specify a given mxn matrix A to +-- be transposed in storage-by-rows format. The parameter A_val can be +-- NULL, in which case numeric values are not copied. The arrays A_ptr, +-- A_ind, and A_val are not changed on exit. +-- +-- On entry the arrays AT_ptr, AT_ind, and AT_val must be allocated, +-- but their content is ignored. On exit the routine stores a resultant +-- nxm matrix A' in these arrays in storage-by-rows format. Note that +-- if the parameter A_val is NULL, the array AT_val is not used. +-- +-- The routine transpose has a side effect that elements in rows of the +-- resultant matrix A' follow in ascending their column indices. */ + +void transpose(int m, int n, int A_ptr[], int A_ind[], double A_val[], + int AT_ptr[], int AT_ind[], double AT_val[]) +{ int i, j, t, beg, end, pos, len; + /* determine row lengths of resultant matrix */ + for (j = 1; j <= n; j++) AT_ptr[j] = 0; + for (i = 1; i <= m; i++) + { beg = A_ptr[i], end = A_ptr[i+1]; + for (t = beg; t < end; t++) AT_ptr[A_ind[t]]++; + } + /* set up row pointers of resultant matrix */ + pos = 1; + for (j = 1; j <= n; j++) + len = AT_ptr[j], pos += len, AT_ptr[j] = pos; + AT_ptr[n+1] = pos; + /* build resultant matrix */ + for (i = m; i >= 1; i--) + { beg = A_ptr[i], end = A_ptr[i+1]; + for (t = beg; t < end; t++) + { pos = --AT_ptr[A_ind[t]]; + AT_ind[pos] = i; + if (A_val != NULL) AT_val[pos] = A_val[t]; + } + } + return; +} + +/*---------------------------------------------------------------------- +-- adat_symbolic - compute S = P*A*D*A'*P' (symbolic phase). +-- +-- *Synopsis* +-- +-- #include "glpmat.h" +-- int *adat_symbolic(int m, int n, int P_per[], int A_ptr[], +-- int A_ind[], int S_ptr[]); +-- +-- *Description* +-- +-- The routine adat_symbolic implements the symbolic phase to compute +-- symmetric matrix S = P*A*D*A'*P', where P is a permutation matrix, +-- A is a given sparse matrix, D is a diagonal matrix, A' is a matrix +-- transposed to A, P' is an inverse of P. +-- +-- The parameter m is the number of rows in A and the order of P. +-- +-- The parameter n is the number of columns in A and the order of D. +-- +-- The array P_per specifies permutation matrix P. It is not changed on +-- exit. +-- +-- The arrays A_ptr and A_ind specify the pattern of matrix A. They are +-- not changed on exit. +-- +-- On exit the routine stores the pattern of upper triangular part of +-- matrix S without diagonal elements in the arrays S_ptr and S_ind in +-- storage-by-rows format. The array S_ptr should be allocated on entry, +-- however, its content is ignored. The array S_ind is allocated by the +-- routine itself which returns a pointer to it. +-- +-- *Returns* +-- +-- The routine returns a pointer to the array S_ind. */ + +int *adat_symbolic(int m, int n, int P_per[], int A_ptr[], int A_ind[], + int S_ptr[]) +{ int i, j, t, ii, jj, tt, k, size, len; + int *S_ind, *AT_ptr, *AT_ind, *ind, *map, *temp; + /* build the pattern of A', which is a matrix transposed to A, to + efficiently access A in column-wise manner */ + AT_ptr = xcalloc(1+n+1, sizeof(int)); + AT_ind = xcalloc(A_ptr[m+1], sizeof(int)); + transpose(m, n, A_ptr, A_ind, NULL, AT_ptr, AT_ind, NULL); + /* allocate the array S_ind */ + size = A_ptr[m+1] - 1; + if (size < m) size = m; + S_ind = xcalloc(1+size, sizeof(int)); + /* allocate and initialize working arrays */ + ind = xcalloc(1+m, sizeof(int)); + map = xcalloc(1+m, sizeof(int)); + for (jj = 1; jj <= m; jj++) map[jj] = 0; + /* compute pattern of S; note that symbolically S = B*B', where + B = P*A, B' is matrix transposed to B */ + S_ptr[1] = 1; + for (ii = 1; ii <= m; ii++) + { /* compute pattern of ii-th row of S */ + len = 0; + i = P_per[ii]; /* i-th row of A = ii-th row of B */ + for (t = A_ptr[i]; t < A_ptr[i+1]; t++) + { k = A_ind[t]; + /* walk through k-th column of A */ + for (tt = AT_ptr[k]; tt < AT_ptr[k+1]; tt++) + { j = AT_ind[tt]; + jj = P_per[m+j]; /* j-th row of A = jj-th row of B */ + /* a[i,k] != 0 and a[j,k] != 0 ergo s[ii,jj] != 0 */ + if (ii < jj && !map[jj]) ind[++len] = jj, map[jj] = 1; + } + } + /* now (ind) is pattern of ii-th row of S */ + S_ptr[ii+1] = S_ptr[ii] + len; + /* at least (S_ptr[ii+1] - 1) locations should be available in + the array S_ind */ + if (S_ptr[ii+1] - 1 > size) + { temp = S_ind; + size += size; + S_ind = xcalloc(1+size, sizeof(int)); + memcpy(&S_ind[1], &temp[1], (S_ptr[ii] - 1) * sizeof(int)); + xfree(temp); + } + xassert(S_ptr[ii+1] - 1 <= size); + /* (ii-th row of S) := (ind) */ + memcpy(&S_ind[S_ptr[ii]], &ind[1], len * sizeof(int)); + /* clear the row pattern map */ + for (t = 1; t <= len; t++) map[ind[t]] = 0; + } + /* free working arrays */ + xfree(AT_ptr); + xfree(AT_ind); + xfree(ind); + xfree(map); + /* reallocate the array S_ind to free unused locations */ + temp = S_ind; + size = S_ptr[m+1] - 1; + S_ind = xcalloc(1+size, sizeof(int)); + memcpy(&S_ind[1], &temp[1], size * sizeof(int)); + xfree(temp); + return S_ind; +} + +/*---------------------------------------------------------------------- +-- adat_numeric - compute S = P*A*D*A'*P' (numeric phase). +-- +-- *Synopsis* +-- +-- #include "glpmat.h" +-- void adat_numeric(int m, int n, int P_per[], +-- int A_ptr[], int A_ind[], double A_val[], double D_diag[], +-- int S_ptr[], int S_ind[], double S_val[], double S_diag[]); +-- +-- *Description* +-- +-- The routine adat_numeric implements the numeric phase to compute +-- symmetric matrix S = P*A*D*A'*P', where P is a permutation matrix, +-- A is a given sparse matrix, D is a diagonal matrix, A' is a matrix +-- transposed to A, P' is an inverse of P. +-- +-- The parameter m is the number of rows in A and the order of P. +-- +-- The parameter n is the number of columns in A and the order of D. +-- +-- The matrix P is specified in the array P_per, which is not changed +-- on exit. +-- +-- The matrix A is specified in the arrays A_ptr, A_ind, and A_val in +-- storage-by-rows format. These arrays are not changed on exit. +-- +-- Diagonal elements of the matrix D are specified in the array D_diag, +-- where D_diag[0] is not used, D_diag[i] = d[i,i] for i = 1, ..., n. +-- The array D_diag is not changed on exit. +-- +-- The pattern of the upper triangular part of the matrix S without +-- diagonal elements (previously computed by the routine adat_symbolic) +-- is specified in the arrays S_ptr and S_ind, which are not changed on +-- exit. Numeric values of non-diagonal elements of S are stored in +-- corresponding locations of the array S_val, and values of diagonal +-- elements of S are stored in locations S_diag[1], ..., S_diag[n]. */ + +void adat_numeric(int m, int n, int P_per[], + int A_ptr[], int A_ind[], double A_val[], double D_diag[], + int S_ptr[], int S_ind[], double S_val[], double S_diag[]) +{ int i, j, t, ii, jj, tt, beg, end, beg1, end1, k; + double sum, *work; + work = xcalloc(1+n, sizeof(double)); + for (j = 1; j <= n; j++) work[j] = 0.0; + /* compute S = B*D*B', where B = P*A, B' is a matrix transposed + to B */ + for (ii = 1; ii <= m; ii++) + { i = P_per[ii]; /* i-th row of A = ii-th row of B */ + /* (work) := (i-th row of A) */ + beg = A_ptr[i], end = A_ptr[i+1]; + for (t = beg; t < end; t++) + work[A_ind[t]] = A_val[t]; + /* compute ii-th row of S */ + beg = S_ptr[ii], end = S_ptr[ii+1]; + for (t = beg; t < end; t++) + { jj = S_ind[t]; + j = P_per[jj]; /* j-th row of A = jj-th row of B */ + /* s[ii,jj] := sum a[i,k] * d[k,k] * a[j,k] */ + sum = 0.0; + beg1 = A_ptr[j], end1 = A_ptr[j+1]; + for (tt = beg1; tt < end1; tt++) + { k = A_ind[tt]; + sum += work[k] * D_diag[k] * A_val[tt]; + } + S_val[t] = sum; + } + /* s[ii,ii] := sum a[i,k] * d[k,k] * a[i,k] */ + sum = 0.0; + beg = A_ptr[i], end = A_ptr[i+1]; + for (t = beg; t < end; t++) + { k = A_ind[t]; + sum += A_val[t] * D_diag[k] * A_val[t]; + work[k] = 0.0; + } + S_diag[ii] = sum; + } + xfree(work); + return; +} + +/*---------------------------------------------------------------------- +-- min_degree - minimum degree ordering. +-- +-- *Synopsis* +-- +-- #include "glpmat.h" +-- void min_degree(int n, int A_ptr[], int A_ind[], int P_per[]); +-- +-- *Description* +-- +-- The routine min_degree uses the minimum degree ordering algorithm +-- to find a permutation matrix P for a given sparse symmetric positive +-- matrix A which minimizes the number of non-zeros in upper triangular +-- factor U for Cholesky factorization P*A*P' = U'*U. +-- +-- The parameter n is the order of matrices A and P. +-- +-- The pattern of the given matrix A is specified on entry in the arrays +-- A_ptr and A_ind in storage-by-rows format. Only the upper triangular +-- part without diagonal elements (which all are assumed to be non-zero) +-- should be specified as if A were upper triangular. The arrays A_ptr +-- and A_ind are not changed on exit. +-- +-- The permutation matrix P is stored by the routine in the array P_per +-- on exit. +-- +-- *Algorithm* +-- +-- The routine min_degree is based on some subroutines from the package +-- SPARSPAK (see comments in the module glpqmd). */ + +void min_degree(int n, int A_ptr[], int A_ind[], int P_per[]) +{ int i, j, ne, t, pos, len; + int *xadj, *adjncy, *deg, *marker, *rchset, *nbrhd, *qsize, + *qlink, nofsub; + /* determine number of non-zeros in complete pattern */ + ne = A_ptr[n+1] - 1; + ne += ne; + /* allocate working arrays */ + xadj = xcalloc(1+n+1, sizeof(int)); + adjncy = xcalloc(1+ne, sizeof(int)); + deg = xcalloc(1+n, sizeof(int)); + marker = xcalloc(1+n, sizeof(int)); + rchset = xcalloc(1+n, sizeof(int)); + nbrhd = xcalloc(1+n, sizeof(int)); + qsize = xcalloc(1+n, sizeof(int)); + qlink = xcalloc(1+n, sizeof(int)); + /* determine row lengths in complete pattern */ + for (i = 1; i <= n; i++) xadj[i] = 0; + for (i = 1; i <= n; i++) + { for (t = A_ptr[i]; t < A_ptr[i+1]; t++) + { j = A_ind[t]; + xassert(i < j && j <= n); + xadj[i]++, xadj[j]++; + } + } + /* set up row pointers for complete pattern */ + pos = 1; + for (i = 1; i <= n; i++) + len = xadj[i], pos += len, xadj[i] = pos; + xadj[n+1] = pos; + xassert(pos - 1 == ne); + /* construct complete pattern */ + for (i = 1; i <= n; i++) + { for (t = A_ptr[i]; t < A_ptr[i+1]; t++) + { j = A_ind[t]; + adjncy[--xadj[i]] = j, adjncy[--xadj[j]] = i; + } + } + /* call the main minimimum degree ordering routine */ + genqmd(&n, xadj, adjncy, P_per, P_per + n, deg, marker, rchset, + nbrhd, qsize, qlink, &nofsub); + /* make sure that permutation matrix P is correct */ + for (i = 1; i <= n; i++) + { j = P_per[i]; + xassert(1 <= j && j <= n); + xassert(P_per[n+j] == i); + } + /* free working arrays */ + xfree(xadj); + xfree(adjncy); + xfree(deg); + xfree(marker); + xfree(rchset); + xfree(nbrhd); + xfree(qsize); + xfree(qlink); + return; +} + +/**********************************************************************/ + +void amd_order1(int n, int A_ptr[], int A_ind[], int P_per[]) +{ /* approximate minimum degree ordering (AMD) */ + int k, ret; + double Control[AMD_CONTROL], Info[AMD_INFO]; + /* get the default parameters */ + amd_defaults(Control); +#if 0 + /* and print them */ + amd_control(Control); +#endif + /* make all indices 0-based */ + for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]--; + for (k = 1; k <= n+1; k++) A_ptr[k]--; + /* call the ordering routine */ + ret = amd_order(n, &A_ptr[1], &A_ind[1], &P_per[1], Control, Info) + ; +#if 0 + amd_info(Info); +#endif + xassert(ret == AMD_OK || ret == AMD_OK_BUT_JUMBLED); + /* retsore 1-based indices */ + for (k = 1; k <= n+1; k++) A_ptr[k]++; + for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]++; + /* patch up permutation matrix */ + memset(&P_per[n+1], 0, n * sizeof(int)); + for (k = 1; k <= n; k++) + { P_per[k]++; + xassert(1 <= P_per[k] && P_per[k] <= n); + xassert(P_per[n+P_per[k]] == 0); + P_per[n+P_per[k]] = k; + } + return; +} + +/**********************************************************************/ + +static void *allocate(size_t n, size_t size) +{ void *ptr; + ptr = xcalloc(n, size); + memset(ptr, 0, n * size); + return ptr; +} + +static void release(void *ptr) +{ xfree(ptr); + return; +} + +void symamd_ord(int n, int A_ptr[], int A_ind[], int P_per[]) +{ /* approximate minimum degree ordering (SYMAMD) */ + int k, ok; + int stats[COLAMD_STATS]; + /* make all indices 0-based */ + for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]--; + for (k = 1; k <= n+1; k++) A_ptr[k]--; + /* call the ordering routine */ + ok = symamd(n, &A_ind[1], &A_ptr[1], &P_per[1], NULL, stats, + allocate, release); +#if 0 + symamd_report(stats); +#endif + xassert(ok); + /* restore 1-based indices */ + for (k = 1; k <= n+1; k++) A_ptr[k]++; + for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]++; + /* patch up permutation matrix */ + memset(&P_per[n+1], 0, n * sizeof(int)); + for (k = 1; k <= n; k++) + { P_per[k]++; + xassert(1 <= P_per[k] && P_per[k] <= n); + xassert(P_per[n+P_per[k]] == 0); + P_per[n+P_per[k]] = k; + } + return; +} + +/*---------------------------------------------------------------------- +-- chol_symbolic - compute Cholesky factorization (symbolic phase). +-- +-- *Synopsis* +-- +-- #include "glpmat.h" +-- int *chol_symbolic(int n, int A_ptr[], int A_ind[], int U_ptr[]); +-- +-- *Description* +-- +-- The routine chol_symbolic implements the symbolic phase of Cholesky +-- factorization A = U'*U, where A is a given sparse symmetric positive +-- definite matrix, U is a resultant upper triangular factor, U' is a +-- matrix transposed to U. +-- +-- The parameter n is the order of matrices A and U. +-- +-- The pattern of the given matrix A is specified on entry in the arrays +-- A_ptr and A_ind in storage-by-rows format. Only the upper triangular +-- part without diagonal elements (which all are assumed to be non-zero) +-- should be specified as if A were upper triangular. The arrays A_ptr +-- and A_ind are not changed on exit. +-- +-- The pattern of the matrix U without diagonal elements (which all are +-- assumed to be non-zero) is stored on exit from the routine in the +-- arrays U_ptr and U_ind in storage-by-rows format. The array U_ptr +-- should be allocated on entry, however, its content is ignored. The +-- array U_ind is allocated by the routine which returns a pointer to it +-- on exit. +-- +-- *Returns* +-- +-- The routine returns a pointer to the array U_ind. +-- +-- *Method* +-- +-- The routine chol_symbolic computes the pattern of the matrix U in a +-- row-wise manner. No pivoting is used. +-- +-- It is known that to compute the pattern of row k of the matrix U we +-- need to merge the pattern of row k of the matrix A and the patterns +-- of each row i of U, where u[i,k] is non-zero (these rows are already +-- computed and placed above row k). +-- +-- However, to reduce the number of rows to be merged the routine uses +-- an advanced algorithm proposed in: +-- +-- D.J.Rose, R.E.Tarjan, and G.S.Lueker. Algorithmic aspects of vertex +-- elimination on graphs. SIAM J. Comput. 5, 1976, 266-83. +-- +-- The authors of the cited paper show that we have the same result if +-- we merge row k of the matrix A and such rows of the matrix U (among +-- rows 1, ..., k-1) whose leftmost non-diagonal non-zero element is +-- placed in k-th column. This feature signficantly reduces the number +-- of rows to be merged, especially on the final steps, where rows of +-- the matrix U become quite dense. +-- +-- To determine rows, which should be merged on k-th step, for a fixed +-- time the routine uses linked lists of row numbers of the matrix U. +-- Location head[k] contains the number of a first row, whose leftmost +-- non-diagonal non-zero element is placed in column k, and location +-- next[i] contains the number of a next row with the same property as +-- row i. */ + +int *chol_symbolic(int n, int A_ptr[], int A_ind[], int U_ptr[]) +{ int i, j, k, t, len, size, beg, end, min_j, *U_ind, *head, *next, + *ind, *map, *temp; + /* initially we assume that on computing the pattern of U fill-in + will double the number of non-zeros in A */ + size = A_ptr[n+1] - 1; + if (size < n) size = n; + size += size; + U_ind = xcalloc(1+size, sizeof(int)); + /* allocate and initialize working arrays */ + head = xcalloc(1+n, sizeof(int)); + for (i = 1; i <= n; i++) head[i] = 0; + next = xcalloc(1+n, sizeof(int)); + ind = xcalloc(1+n, sizeof(int)); + map = xcalloc(1+n, sizeof(int)); + for (j = 1; j <= n; j++) map[j] = 0; + /* compute the pattern of matrix U */ + U_ptr[1] = 1; + for (k = 1; k <= n; k++) + { /* compute the pattern of k-th row of U, which is the union of + k-th row of A and those rows of U (among 1, ..., k-1) whose + leftmost non-diagonal non-zero is placed in k-th column */ + /* (ind) := (k-th row of A) */ + len = A_ptr[k+1] - A_ptr[k]; + memcpy(&ind[1], &A_ind[A_ptr[k]], len * sizeof(int)); + for (t = 1; t <= len; t++) + { j = ind[t]; + xassert(k < j && j <= n); + map[j] = 1; + } + /* walk through rows of U whose leftmost non-diagonal non-zero + is placed in k-th column */ + for (i = head[k]; i != 0; i = next[i]) + { /* (ind) := (ind) union (i-th row of U) */ + beg = U_ptr[i], end = U_ptr[i+1]; + for (t = beg; t < end; t++) + { j = U_ind[t]; + if (j > k && !map[j]) ind[++len] = j, map[j] = 1; + } + } + /* now (ind) is the pattern of k-th row of U */ + U_ptr[k+1] = U_ptr[k] + len; + /* at least (U_ptr[k+1] - 1) locations should be available in + the array U_ind */ + if (U_ptr[k+1] - 1 > size) + { temp = U_ind; + size += size; + U_ind = xcalloc(1+size, sizeof(int)); + memcpy(&U_ind[1], &temp[1], (U_ptr[k] - 1) * sizeof(int)); + xfree(temp); + } + xassert(U_ptr[k+1] - 1 <= size); + /* (k-th row of U) := (ind) */ + memcpy(&U_ind[U_ptr[k]], &ind[1], len * sizeof(int)); + /* determine column index of leftmost non-diagonal non-zero in + k-th row of U and clear the row pattern map */ + min_j = n + 1; + for (t = 1; t <= len; t++) + { j = ind[t], map[j] = 0; + if (min_j > j) min_j = j; + } + /* include k-th row into corresponding linked list */ + if (min_j <= n) next[k] = head[min_j], head[min_j] = k; + } + /* free working arrays */ + xfree(head); + xfree(next); + xfree(ind); + xfree(map); + /* reallocate the array U_ind to free unused locations */ + temp = U_ind; + size = U_ptr[n+1] - 1; + U_ind = xcalloc(1+size, sizeof(int)); + memcpy(&U_ind[1], &temp[1], size * sizeof(int)); + xfree(temp); + return U_ind; +} + +/*---------------------------------------------------------------------- +-- chol_numeric - compute Cholesky factorization (numeric phase). +-- +-- *Synopsis* +-- +-- #include "glpmat.h" +-- int chol_numeric(int n, +-- int A_ptr[], int A_ind[], double A_val[], double A_diag[], +-- int U_ptr[], int U_ind[], double U_val[], double U_diag[]); +-- +-- *Description* +-- +-- The routine chol_symbolic implements the numeric phase of Cholesky +-- factorization A = U'*U, where A is a given sparse symmetric positive +-- definite matrix, U is a resultant upper triangular factor, U' is a +-- matrix transposed to U. +-- +-- The parameter n is the order of matrices A and U. +-- +-- Upper triangular part of the matrix A without diagonal elements is +-- specified in the arrays A_ptr, A_ind, and A_val in storage-by-rows +-- format. Diagonal elements of A are specified in the array A_diag, +-- where A_diag[0] is not used, A_diag[i] = a[i,i] for i = 1, ..., n. +-- The arrays A_ptr, A_ind, A_val, and A_diag are not changed on exit. +-- +-- The pattern of the matrix U without diagonal elements (previously +-- computed with the routine chol_symbolic) is specified in the arrays +-- U_ptr and U_ind, which are not changed on exit. Numeric values of +-- non-diagonal elements of U are stored in corresponding locations of +-- the array U_val, and values of diagonal elements of U are stored in +-- locations U_diag[1], ..., U_diag[n]. +-- +-- *Returns* +-- +-- The routine returns the number of non-positive diagonal elements of +-- the matrix U which have been replaced by a huge positive number (see +-- the method description below). Zero return code means the matrix A +-- has been successfully factorized. +-- +-- *Method* +-- +-- The routine chol_numeric computes the matrix U in a row-wise manner +-- using standard gaussian elimination technique. No pivoting is used. +-- +-- Initially the routine sets U = A, and before k-th elimination step +-- the matrix U is the following: +-- +-- 1 k n +-- 1 x x x x x x x x x x +-- . x x x x x x x x x +-- . . x x x x x x x x +-- . . . x x x x x x x +-- k . . . . * * * * * * +-- . . . . * * * * * * +-- . . . . * * * * * * +-- . . . . * * * * * * +-- . . . . * * * * * * +-- n . . . . * * * * * * +-- +-- where 'x' are elements of already computed rows, '*' are elements of +-- the active submatrix. (Note that the lower triangular part of the +-- active submatrix being symmetric is not stored and diagonal elements +-- are stored separately in the array U_diag.) +-- +-- The matrix A is assumed to be positive definite. However, if it is +-- close to semi-definite, on some elimination step a pivot u[k,k] may +-- happen to be non-positive due to round-off errors. In this case the +-- routine uses a technique proposed in: +-- +-- S.J.Wright. The Cholesky factorization in interior-point and barrier +-- methods. Preprint MCS-P600-0596, Mathematics and Computer Science +-- Division, Argonne National Laboratory, Argonne, Ill., May 1996. +-- +-- The routine just replaces non-positive u[k,k] by a huge positive +-- number. This involves non-diagonal elements in k-th row of U to be +-- close to zero that, in turn, involves k-th component of a solution +-- vector to be close to zero. Note, however, that this technique works +-- only if the system A*x = b is consistent. */ + +int chol_numeric(int n, + int A_ptr[], int A_ind[], double A_val[], double A_diag[], + int U_ptr[], int U_ind[], double U_val[], double U_diag[]) +{ int i, j, k, t, t1, beg, end, beg1, end1, count = 0; + double ukk, uki, *work; + work = xcalloc(1+n, sizeof(double)); + for (j = 1; j <= n; j++) work[j] = 0.0; + /* U := (upper triangle of A) */ + /* note that the upper traingle of A is a subset of U */ + for (i = 1; i <= n; i++) + { beg = A_ptr[i], end = A_ptr[i+1]; + for (t = beg; t < end; t++) + j = A_ind[t], work[j] = A_val[t]; + beg = U_ptr[i], end = U_ptr[i+1]; + for (t = beg; t < end; t++) + j = U_ind[t], U_val[t] = work[j], work[j] = 0.0; + U_diag[i] = A_diag[i]; + } + /* main elimination loop */ + for (k = 1; k <= n; k++) + { /* transform k-th row of U */ + ukk = U_diag[k]; + if (ukk > 0.0) + U_diag[k] = ukk = sqrt(ukk); + else + U_diag[k] = ukk = DBL_MAX, count++; + /* (work) := (transformed k-th row) */ + beg = U_ptr[k], end = U_ptr[k+1]; + for (t = beg; t < end; t++) + work[U_ind[t]] = (U_val[t] /= ukk); + /* transform other rows of U */ + for (t = beg; t < end; t++) + { i = U_ind[t]; + xassert(i > k); + /* (i-th row) := (i-th row) - u[k,i] * (k-th row) */ + uki = work[i]; + beg1 = U_ptr[i], end1 = U_ptr[i+1]; + for (t1 = beg1; t1 < end1; t1++) + U_val[t1] -= uki * work[U_ind[t1]]; + U_diag[i] -= uki * uki; + } + /* (work) := 0 */ + for (t = beg; t < end; t++) + work[U_ind[t]] = 0.0; + } + xfree(work); + return count; +} + +/*---------------------------------------------------------------------- +-- u_solve - solve upper triangular system U*x = b. +-- +-- *Synopsis* +-- +-- #include "glpmat.h" +-- void u_solve(int n, int U_ptr[], int U_ind[], double U_val[], +-- double U_diag[], double x[]); +-- +-- *Description* +-- +-- The routine u_solve solves an linear system U*x = b, where U is an +-- upper triangular matrix. +-- +-- The parameter n is the order of matrix U. +-- +-- The matrix U without diagonal elements is specified in the arrays +-- U_ptr, U_ind, and U_val in storage-by-rows format. Diagonal elements +-- of U are specified in the array U_diag, where U_diag[0] is not used, +-- U_diag[i] = u[i,i] for i = 1, ..., n. All these four arrays are not +-- changed on exit. +-- +-- The right-hand side vector b is specified on entry in the array x, +-- where x[0] is not used, and x[i] = b[i] for i = 1, ..., n. On exit +-- the routine stores computed components of the vector of unknowns x +-- in the array x in the same manner. */ + +void u_solve(int n, int U_ptr[], int U_ind[], double U_val[], + double U_diag[], double x[]) +{ int i, t, beg, end; + double temp; + for (i = n; i >= 1; i--) + { temp = x[i]; + beg = U_ptr[i], end = U_ptr[i+1]; + for (t = beg; t < end; t++) + temp -= U_val[t] * x[U_ind[t]]; + xassert(U_diag[i] != 0.0); + x[i] = temp / U_diag[i]; + } + return; +} + +/*---------------------------------------------------------------------- +-- ut_solve - solve lower triangular system U'*x = b. +-- +-- *Synopsis* +-- +-- #include "glpmat.h" +-- void ut_solve(int n, int U_ptr[], int U_ind[], double U_val[], +-- double U_diag[], double x[]); +-- +-- *Description* +-- +-- The routine ut_solve solves an linear system U'*x = b, where U is a +-- matrix transposed to an upper triangular matrix. +-- +-- The parameter n is the order of matrix U. +-- +-- The matrix U without diagonal elements is specified in the arrays +-- U_ptr, U_ind, and U_val in storage-by-rows format. Diagonal elements +-- of U are specified in the array U_diag, where U_diag[0] is not used, +-- U_diag[i] = u[i,i] for i = 1, ..., n. All these four arrays are not +-- changed on exit. +-- +-- The right-hand side vector b is specified on entry in the array x, +-- where x[0] is not used, and x[i] = b[i] for i = 1, ..., n. On exit +-- the routine stores computed components of the vector of unknowns x +-- in the array x in the same manner. */ + +void ut_solve(int n, int U_ptr[], int U_ind[], double U_val[], + double U_diag[], double x[]) +{ int i, t, beg, end; + double temp; + for (i = 1; i <= n; i++) + { xassert(U_diag[i] != 0.0); + temp = (x[i] /= U_diag[i]); + if (temp == 0.0) continue; + beg = U_ptr[i], end = U_ptr[i+1]; + for (t = beg; t < end; t++) + x[U_ind[t]] -= U_val[t] * temp; + } + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmat.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmat.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,198 @@ +/* glpmat.h (linear algebra routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPMAT_H +#define GLPMAT_H + +/*********************************************************************** +* FULL-VECTOR STORAGE +* +* For a sparse vector x having n elements, ne of which are non-zero, +* the full-vector storage format uses two arrays x_ind and x_vec, which +* are set up as follows: +* +* x_ind is an integer array of length [1+ne]. Location x_ind[0] is +* not used, and locations x_ind[1], ..., x_ind[ne] contain indices of +* non-zero elements in vector x. +* +* x_vec is a floating-point array of length [1+n]. Location x_vec[0] +* is not used, and locations x_vec[1], ..., x_vec[n] contain numeric +* values of ALL elements in vector x, including its zero elements. +* +* Let, for example, the following sparse vector x be given: +* +* (0, 1, 0, 0, 2, 3, 0, 4) +* +* Then the arrays are: +* +* x_ind = { X; 2, 5, 6, 8 } +* +* x_vec = { X; 0, 1, 0, 0, 2, 3, 0, 4 } +* +* COMPRESSED-VECTOR STORAGE +* +* For a sparse vector x having n elements, ne of which are non-zero, +* the compressed-vector storage format uses two arrays x_ind and x_vec, +* which are set up as follows: +* +* x_ind is an integer array of length [1+ne]. Location x_ind[0] is +* not used, and locations x_ind[1], ..., x_ind[ne] contain indices of +* non-zero elements in vector x. +* +* x_vec is a floating-point array of length [1+ne]. Location x_vec[0] +* is not used, and locations x_vec[1], ..., x_vec[ne] contain numeric +* values of corresponding non-zero elements in vector x. +* +* Let, for example, the following sparse vector x be given: +* +* (0, 1, 0, 0, 2, 3, 0, 4) +* +* Then the arrays are: +* +* x_ind = { X; 2, 5, 6, 8 } +* +* x_vec = { X; 1, 2, 3, 4 } +* +* STORAGE-BY-ROWS +* +* For a sparse matrix A, which has m rows, n columns, and ne non-zero +* elements the storage-by-rows format uses three arrays A_ptr, A_ind, +* and A_val, which are set up as follows: +* +* A_ptr is an integer array of length [1+m+1] also called "row pointer +* array". It contains the relative starting positions of each row of A +* in the arrays A_ind and A_val, i.e. element A_ptr[i], 1 <= i <= m, +* indicates where row i begins in the arrays A_ind and A_val. If all +* elements in row i are zero, then A_ptr[i] = A_ptr[i+1]. Location +* A_ptr[0] is not used, location A_ptr[1] must contain 1, and location +* A_ptr[m+1] must contain ne+1 that indicates the position after the +* last element in the arrays A_ind and A_val. +* +* A_ind is an integer array of length [1+ne]. Location A_ind[0] is not +* used, and locations A_ind[1], ..., A_ind[ne] contain column indices +* of (non-zero) elements in matrix A. +* +* A_val is a floating-point array of length [1+ne]. Location A_val[0] +* is not used, and locations A_val[1], ..., A_val[ne] contain numeric +* values of non-zero elements in matrix A. +* +* Non-zero elements of matrix A are stored contiguously, and the rows +* of matrix A are stored consecutively from 1 to m in the arrays A_ind +* and A_val. The elements in each row of A may be stored in any order +* in A_ind and A_val. Note that elements with duplicate column indices +* are not allowed. +* +* Let, for example, the following sparse matrix A be given: +* +* | 11 . 13 . . . | +* | 21 22 . 24 . . | +* | . 32 33 . . . | +* | . . 43 44 . 46 | +* | . . . . . . | +* | 61 62 . . . 66 | +* +* Then the arrays are: +* +* A_ptr = { X; 1, 3, 6, 8, 11, 11; 14 } +* +* A_ind = { X; 1, 3; 4, 2, 1; 2, 3; 4, 3, 6; 1, 2, 6 } +* +* A_val = { X; 11, 13; 24, 22, 21; 32, 33; 44, 43, 46; 61, 62, 66 } +* +* PERMUTATION MATRICES +* +* Let P be a permutation matrix of the order n. It is represented as +* an integer array P_per of length [1+n+n] as follows: if p[i,j] = 1, +* then P_per[i] = j and P_per[n+j] = i. Location P_per[0] is not used. +* +* Let A' = P*A. If i-th row of A corresponds to i'-th row of A', then +* P_per[i'] = i and P_per[n+i] = i'. +* +* References: +* +* 1. Gustavson F.G. Some basic techniques for solving sparse systems of +* linear equations. In Rose and Willoughby (1972), pp. 41-52. +* +* 2. Basic Linear Algebra Subprograms Technical (BLAST) Forum Standard. +* University of Tennessee (2001). */ + +#define check_fvs _glp_mat_check_fvs +int check_fvs(int n, int nnz, int ind[], double vec[]); +/* check sparse vector in full-vector storage format */ + +#define check_pattern _glp_mat_check_pattern +int check_pattern(int m, int n, int A_ptr[], int A_ind[]); +/* check pattern of sparse matrix */ + +#define transpose _glp_mat_transpose +void transpose(int m, int n, int A_ptr[], int A_ind[], double A_val[], + int AT_ptr[], int AT_ind[], double AT_val[]); +/* transpose sparse matrix */ + +#define adat_symbolic _glp_mat_adat_symbolic +int *adat_symbolic(int m, int n, int P_per[], int A_ptr[], int A_ind[], + int S_ptr[]); +/* compute S = P*A*D*A'*P' (symbolic phase) */ + +#define adat_numeric _glp_mat_adat_numeric +void adat_numeric(int m, int n, int P_per[], + int A_ptr[], int A_ind[], double A_val[], double D_diag[], + int S_ptr[], int S_ind[], double S_val[], double S_diag[]); +/* compute S = P*A*D*A'*P' (numeric phase) */ + +#define min_degree _glp_mat_min_degree +void min_degree(int n, int A_ptr[], int A_ind[], int P_per[]); +/* minimum degree ordering */ + +#define amd_order1 _glp_mat_amd_order1 +void amd_order1(int n, int A_ptr[], int A_ind[], int P_per[]); +/* approximate minimum degree ordering (AMD) */ + +#define symamd_ord _glp_mat_symamd_ord +void symamd_ord(int n, int A_ptr[], int A_ind[], int P_per[]); +/* approximate minimum degree ordering (SYMAMD) */ + +#define chol_symbolic _glp_mat_chol_symbolic +int *chol_symbolic(int n, int A_ptr[], int A_ind[], int U_ptr[]); +/* compute Cholesky factorization (symbolic phase) */ + +#define chol_numeric _glp_mat_chol_numeric +int chol_numeric(int n, + int A_ptr[], int A_ind[], double A_val[], double A_diag[], + int U_ptr[], int U_ind[], double U_val[], double U_diag[]); +/* compute Cholesky factorization (numeric phase) */ + +#define u_solve _glp_mat_u_solve +void u_solve(int n, int U_ptr[], int U_ind[], double U_val[], + double U_diag[], double x[]); +/* solve upper triangular system U*x = b */ + +#define ut_solve _glp_mat_ut_solve +void ut_solve(int n, int U_ptr[], int U_ind[], double U_val[], + double U_diag[], double x[]); +/* solve lower triangular system U'*x = b */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmpl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmpl.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,2583 @@ +/* glpmpl.h (GNU MathProg translator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPMPL_H +#define GLPMPL_H + +#include "glpavl.h" +#include "glprng.h" + +typedef struct MPL MPL; +typedef char STRING; +typedef struct SYMBOL SYMBOL; +typedef struct TUPLE TUPLE; +typedef struct ARRAY ELEMSET; +typedef struct ELEMVAR ELEMVAR; +typedef struct FORMULA FORMULA; +typedef struct ELEMCON ELEMCON; +typedef union VALUE VALUE; +typedef struct ARRAY ARRAY; +typedef struct MEMBER MEMBER; +#if 1 +/* many C compilers have DOMAIN declared in :( */ +#undef DOMAIN +#define DOMAIN DOMAIN1 +#endif +typedef struct DOMAIN DOMAIN; +typedef struct DOMAIN_BLOCK DOMAIN_BLOCK; +typedef struct DOMAIN_SLOT DOMAIN_SLOT; +typedef struct SET SET; +typedef struct WITHIN WITHIN; +typedef struct GADGET GADGET; +typedef struct PARAMETER PARAMETER; +typedef struct CONDITION CONDITION; +typedef struct VARIABLE VARIABLE; +typedef struct CONSTRAINT CONSTRAINT; +typedef struct TABLE TABLE; +typedef struct TABARG TABARG; +typedef struct TABFLD TABFLD; +typedef struct TABIN TABIN; +typedef struct TABOUT TABOUT; +typedef struct TABDCA TABDCA; +typedef union OPERANDS OPERANDS; +typedef struct ARG_LIST ARG_LIST; +typedef struct CODE CODE; +typedef struct CHECK CHECK; +typedef struct DISPLAY DISPLAY; +typedef struct DISPLAY1 DISPLAY1; +typedef struct PRINTF PRINTF; +typedef struct PRINTF1 PRINTF1; +typedef struct FOR FOR; +typedef struct STATEMENT STATEMENT; +typedef struct TUPLE SLICE; + +/**********************************************************************/ +/* * * TRANSLATOR DATABASE * * */ +/**********************************************************************/ + +#define A_BINARY 101 /* something binary */ +#define A_CHECK 102 /* check statement */ +#define A_CONSTRAINT 103 /* model constraint */ +#define A_DISPLAY 104 /* display statement */ +#define A_ELEMCON 105 /* elemental constraint/objective */ +#define A_ELEMSET 106 /* elemental set */ +#define A_ELEMVAR 107 /* elemental variable */ +#define A_EXPRESSION 108 /* expression */ +#define A_FOR 109 /* for statement */ +#define A_FORMULA 110 /* formula */ +#define A_INDEX 111 /* dummy index */ +#define A_INPUT 112 /* input table */ +#define A_INTEGER 113 /* something integer */ +#define A_LOGICAL 114 /* something logical */ +#define A_MAXIMIZE 115 /* objective has to be maximized */ +#define A_MINIMIZE 116 /* objective has to be minimized */ +#define A_NONE 117 /* nothing */ +#define A_NUMERIC 118 /* something numeric */ +#define A_OUTPUT 119 /* output table */ +#define A_PARAMETER 120 /* model parameter */ +#define A_PRINTF 121 /* printf statement */ +#define A_SET 122 /* model set */ +#define A_SOLVE 123 /* solve statement */ +#define A_SYMBOLIC 124 /* something symbolic */ +#define A_TABLE 125 /* data table */ +#define A_TUPLE 126 /* n-tuple */ +#define A_VARIABLE 127 /* model variable */ + +#define MAX_LENGTH 100 +/* maximal length of any symbolic value (this includes symbolic names, + numeric and string literals, and all symbolic values that may appear + during the evaluation phase) */ + +#define CONTEXT_SIZE 60 +/* size of the context queue, in characters */ + +#define OUTBUF_SIZE 1024 +/* size of the output buffer, in characters */ + +struct MPL +{ /* translator database */ + /*--------------------------------------------------------------*/ + /* scanning segment */ + int line; + /* number of the current text line */ + int c; + /* the current character or EOF */ + int token; + /* the current token: */ +#define T_EOF 201 /* end of file */ +#define T_NAME 202 /* symbolic name (model section only) */ +#define T_SYMBOL 203 /* symbol (data section only) */ +#define T_NUMBER 204 /* numeric literal */ +#define T_STRING 205 /* string literal */ +#define T_AND 206 /* and && */ +#define T_BY 207 /* by */ +#define T_CROSS 208 /* cross */ +#define T_DIFF 209 /* diff */ +#define T_DIV 210 /* div */ +#define T_ELSE 211 /* else */ +#define T_IF 212 /* if */ +#define T_IN 213 /* in */ +#define T_INFINITY 214 /* Infinity */ +#define T_INTER 215 /* inter */ +#define T_LESS 216 /* less */ +#define T_MOD 217 /* mod */ +#define T_NOT 218 /* not ! */ +#define T_OR 219 /* or || */ +#define T_SPTP 220 /* s.t. */ +#define T_SYMDIFF 221 /* symdiff */ +#define T_THEN 222 /* then */ +#define T_UNION 223 /* union */ +#define T_WITHIN 224 /* within */ +#define T_PLUS 225 /* + */ +#define T_MINUS 226 /* - */ +#define T_ASTERISK 227 /* * */ +#define T_SLASH 228 /* / */ +#define T_POWER 229 /* ^ ** */ +#define T_LT 230 /* < */ +#define T_LE 231 /* <= */ +#define T_EQ 232 /* = == */ +#define T_GE 233 /* >= */ +#define T_GT 234 /* > */ +#define T_NE 235 /* <> != */ +#define T_CONCAT 236 /* & */ +#define T_BAR 237 /* | */ +#define T_POINT 238 /* . */ +#define T_COMMA 239 /* , */ +#define T_COLON 240 /* : */ +#define T_SEMICOLON 241 /* ; */ +#define T_ASSIGN 242 /* := */ +#define T_DOTS 243 /* .. */ +#define T_LEFT 244 /* ( */ +#define T_RIGHT 245 /* ) */ +#define T_LBRACKET 246 /* [ */ +#define T_RBRACKET 247 /* ] */ +#define T_LBRACE 248 /* { */ +#define T_RBRACE 249 /* } */ +#define T_APPEND 250 /* >> */ +#define T_TILDE 251 /* ~ */ +#define T_INPUT 252 /* <- */ + int imlen; + /* length of the current token */ + char *image; /* char image[MAX_LENGTH+1]; */ + /* image of the current token */ + double value; + /* value of the current token (for T_NUMBER only) */ + int b_token; + /* the previous token */ + int b_imlen; + /* length of the previous token */ + char *b_image; /* char b_image[MAX_LENGTH+1]; */ + /* image of the previous token */ + double b_value; + /* value of the previous token (if token is T_NUMBER) */ + int f_dots; + /* if this flag is set, the next token should be recognized as + T_DOTS, not as T_POINT */ + int f_scan; + /* if this flag is set, the next token is already scanned */ + int f_token; + /* the next token */ + int f_imlen; + /* length of the next token */ + char *f_image; /* char f_image[MAX_LENGTH+1]; */ + /* image of the next token */ + double f_value; + /* value of the next token (if token is T_NUMBER) */ + char *context; /* char context[CONTEXT_SIZE]; */ + /* context circular queue (not null-terminated!) */ + int c_ptr; + /* pointer to the current position in the context queue */ + int flag_d; + /* if this flag is set, the data section is being processed */ + /*--------------------------------------------------------------*/ + /* translating segment */ + DMP *pool; + /* memory pool used to allocate all data instances created during + the translation phase */ + AVL *tree; + /* symbolic name table: + node.type = A_INDEX => node.link -> DOMAIN_SLOT + node.type = A_SET => node.link -> SET + node.type = A_PARAMETER => node.link -> PARAMETER + node.type = A_VARIABLE => node.link -> VARIABLE + node.type = A_CONSTRANT => node.link -> CONSTRAINT */ + STATEMENT *model; + /* linked list of model statements in the original order */ + int flag_x; + /* if this flag is set, the current token being left parenthesis + begins a slice that allows recognizing any undeclared symbolic + names as dummy indices; this flag is automatically reset once + the next token has been scanned */ + int as_within; + /* the warning "in understood as within" has been issued */ + int as_in; + /* the warning "within understood as in" has been issued */ + int as_binary; + /* the warning "logical understood as binary" has been issued */ + int flag_s; + /* if this flag is set, the solve statement has been parsed */ + /*--------------------------------------------------------------*/ + /* common segment */ + DMP *strings; + /* memory pool to allocate STRING data structures */ + DMP *symbols; + /* memory pool to allocate SYMBOL data structures */ + DMP *tuples; + /* memory pool to allocate TUPLE data structures */ + DMP *arrays; + /* memory pool to allocate ARRAY data structures */ + DMP *members; + /* memory pool to allocate MEMBER data structures */ + DMP *elemvars; + /* memory pool to allocate ELEMVAR data structures */ + DMP *formulae; + /* memory pool to allocate FORMULA data structures */ + DMP *elemcons; + /* memory pool to allocate ELEMCON data structures */ + ARRAY *a_list; + /* linked list of all arrays in the database */ + char *sym_buf; /* char sym_buf[255+1]; */ + /* working buffer used by the routine format_symbol */ + char *tup_buf; /* char tup_buf[255+1]; */ + /* working buffer used by the routine format_tuple */ + /*--------------------------------------------------------------*/ + /* generating/postsolving segment */ + RNG *rand; + /* pseudo-random number generator */ + int flag_p; + /* if this flag is set, the postsolving phase is in effect */ + STATEMENT *stmt; + /* model statement being currently executed */ + TABDCA *dca; + /* pointer to table driver communication area for table statement + currently executed */ + int m; + /* number of rows in the problem, m >= 0 */ + int n; + /* number of columns in the problem, n >= 0 */ + ELEMCON **row; /* ELEMCON *row[1+m]; */ + /* row[0] is not used; + row[i] is elemental constraint or objective, which corresponds + to i-th row of the problem, 1 <= i <= m */ + ELEMVAR **col; /* ELEMVAR *col[1+n]; */ + /* col[0] is not used; + col[j] is elemental variable, which corresponds to j-th column + of the problem, 1 <= j <= n */ + /*--------------------------------------------------------------*/ + /* input/output segment */ + XFILE *in_fp; + /* stream assigned to the input text file */ + char *in_file; + /* name of the input text file */ + XFILE *out_fp; + /* stream assigned to the output text file used to write all data + produced by display and printf statements; NULL means the data + should be sent to stdout via the routine xprintf */ + char *out_file; + /* name of the output text file */ +#if 0 /* 08/XI-2009 */ + char *out_buf; /* char out_buf[OUTBUF_SIZE] */ + /* buffer to accumulate output data */ + int out_cnt; + /* count of data bytes stored in the output buffer */ +#endif + XFILE *prt_fp; + /* stream assigned to the print text file; may be NULL */ + char *prt_file; + /* name of the output print file */ + /*--------------------------------------------------------------*/ + /* solver interface segment */ + jmp_buf jump; + /* jump address for non-local go to in case of error */ + int phase; + /* phase of processing: + 0 - database is being or has been initialized + 1 - model section is being or has been read + 2 - data section is being or has been read + 3 - model is being or has been generated/postsolved + 4 - model processing error has occurred */ + char *mod_file; + /* name of the input text file, which contains model section */ + char *mpl_buf; /* char mpl_buf[255+1]; */ + /* working buffer used by some interface routines */ +}; + +/**********************************************************************/ +/* * * PROCESSING MODEL SECTION * * */ +/**********************************************************************/ + +#define alloc(type) ((type *)dmp_get_atomv(mpl->pool, sizeof(type))) +/* allocate atom of given type */ + +#define enter_context _glp_mpl_enter_context +void enter_context(MPL *mpl); +/* enter current token into context queue */ + +#define print_context _glp_mpl_print_context +void print_context(MPL *mpl); +/* print current content of context queue */ + +#define get_char _glp_mpl_get_char +void get_char(MPL *mpl); +/* scan next character from input text file */ + +#define append_char _glp_mpl_append_char +void append_char(MPL *mpl); +/* append character to current token */ + +#define get_token _glp_mpl_get_token +void get_token(MPL *mpl); +/* scan next token from input text file */ + +#define unget_token _glp_mpl_unget_token +void unget_token(MPL *mpl); +/* return current token back to input stream */ + +#define is_keyword _glp_mpl_is_keyword +int is_keyword(MPL *mpl, char *keyword); +/* check if current token is given non-reserved keyword */ + +#define is_reserved _glp_mpl_is_reserved +int is_reserved(MPL *mpl); +/* check if current token is reserved keyword */ + +#define make_code _glp_mpl_make_code +CODE *make_code(MPL *mpl, int op, OPERANDS *arg, int type, int dim); +/* generate pseudo-code (basic routine) */ + +#define make_unary _glp_mpl_make_unary +CODE *make_unary(MPL *mpl, int op, CODE *x, int type, int dim); +/* generate pseudo-code for unary operation */ + +#define make_binary _glp_mpl_make_binary +CODE *make_binary(MPL *mpl, int op, CODE *x, CODE *y, int type, + int dim); +/* generate pseudo-code for binary operation */ + +#define make_ternary _glp_mpl_make_ternary +CODE *make_ternary(MPL *mpl, int op, CODE *x, CODE *y, CODE *z, + int type, int dim); +/* generate pseudo-code for ternary operation */ + +#define numeric_literal _glp_mpl_numeric_literal +CODE *numeric_literal(MPL *mpl); +/* parse reference to numeric literal */ + +#define string_literal _glp_mpl_string_literal +CODE *string_literal(MPL *mpl); +/* parse reference to string literal */ + +#define create_arg_list _glp_mpl_create_arg_list +ARG_LIST *create_arg_list(MPL *mpl); +/* create empty operands list */ + +#define expand_arg_list _glp_mpl_expand_arg_list +ARG_LIST *expand_arg_list(MPL *mpl, ARG_LIST *list, CODE *x); +/* append operand to operands list */ + +#define arg_list_len _glp_mpl_arg_list_len +int arg_list_len(MPL *mpl, ARG_LIST *list); +/* determine length of operands list */ + +#define subscript_list _glp_mpl_subscript_list +ARG_LIST *subscript_list(MPL *mpl); +/* parse subscript list */ + +#define object_reference _glp_mpl_object_reference +CODE *object_reference(MPL *mpl); +/* parse reference to named object */ + +#define numeric_argument _glp_mpl_numeric_argument +CODE *numeric_argument(MPL *mpl, char *func); +/* parse argument passed to built-in function */ + +#define symbolic_argument _glp_mpl_symbolic_argument +CODE *symbolic_argument(MPL *mpl, char *func); + +#define elemset_argument _glp_mpl_elemset_argument +CODE *elemset_argument(MPL *mpl, char *func); + +#define function_reference _glp_mpl_function_reference +CODE *function_reference(MPL *mpl); +/* parse reference to built-in function */ + +#define create_domain _glp_mpl_create_domain +DOMAIN *create_domain(MPL *mpl); +/* create empty domain */ + +#define create_block _glp_mpl_create_block +DOMAIN_BLOCK *create_block(MPL *mpl); +/* create empty domain block */ + +#define append_block _glp_mpl_append_block +void append_block(MPL *mpl, DOMAIN *domain, DOMAIN_BLOCK *block); +/* append domain block to specified domain */ + +#define append_slot _glp_mpl_append_slot +DOMAIN_SLOT *append_slot(MPL *mpl, DOMAIN_BLOCK *block, char *name, + CODE *code); +/* create and append new slot to domain block */ + +#define expression_list _glp_mpl_expression_list +CODE *expression_list(MPL *mpl); +/* parse expression list */ + +#define literal_set _glp_mpl_literal_set +CODE *literal_set(MPL *mpl, CODE *code); +/* parse literal set */ + +#define indexing_expression _glp_mpl_indexing_expression +DOMAIN *indexing_expression(MPL *mpl); +/* parse indexing expression */ + +#define close_scope _glp_mpl_close_scope +void close_scope(MPL *mpl, DOMAIN *domain); +/* close scope of indexing expression */ + +#define iterated_expression _glp_mpl_iterated_expression +CODE *iterated_expression(MPL *mpl); +/* parse iterated expression */ + +#define domain_arity _glp_mpl_domain_arity +int domain_arity(MPL *mpl, DOMAIN *domain); +/* determine arity of domain */ + +#define set_expression _glp_mpl_set_expression +CODE *set_expression(MPL *mpl); +/* parse set expression */ + +#define branched_expression _glp_mpl_branched_expression +CODE *branched_expression(MPL *mpl); +/* parse conditional expression */ + +#define primary_expression _glp_mpl_primary_expression +CODE *primary_expression(MPL *mpl); +/* parse primary expression */ + +#define error_preceding _glp_mpl_error_preceding +void error_preceding(MPL *mpl, char *opstr); +/* raise error if preceding operand has wrong type */ + +#define error_following _glp_mpl_error_following +void error_following(MPL *mpl, char *opstr); +/* raise error if following operand has wrong type */ + +#define error_dimension _glp_mpl_error_dimension +void error_dimension(MPL *mpl, char *opstr, int dim1, int dim2); +/* raise error if operands have different dimension */ + +#define expression_0 _glp_mpl_expression_0 +CODE *expression_0(MPL *mpl); +/* parse expression of level 0 */ + +#define expression_1 _glp_mpl_expression_1 +CODE *expression_1(MPL *mpl); +/* parse expression of level 1 */ + +#define expression_2 _glp_mpl_expression_2 +CODE *expression_2(MPL *mpl); +/* parse expression of level 2 */ + +#define expression_3 _glp_mpl_expression_3 +CODE *expression_3(MPL *mpl); +/* parse expression of level 3 */ + +#define expression_4 _glp_mpl_expression_4 +CODE *expression_4(MPL *mpl); +/* parse expression of level 4 */ + +#define expression_5 _glp_mpl_expression_5 +CODE *expression_5(MPL *mpl); +/* parse expression of level 5 */ + +#define expression_6 _glp_mpl_expression_6 +CODE *expression_6(MPL *mpl); +/* parse expression of level 6 */ + +#define expression_7 _glp_mpl_expression_7 +CODE *expression_7(MPL *mpl); +/* parse expression of level 7 */ + +#define expression_8 _glp_mpl_expression_8 +CODE *expression_8(MPL *mpl); +/* parse expression of level 8 */ + +#define expression_9 _glp_mpl_expression_9 +CODE *expression_9(MPL *mpl); +/* parse expression of level 9 */ + +#define expression_10 _glp_mpl_expression_10 +CODE *expression_10(MPL *mpl); +/* parse expression of level 10 */ + +#define expression_11 _glp_mpl_expression_11 +CODE *expression_11(MPL *mpl); +/* parse expression of level 11 */ + +#define expression_12 _glp_mpl_expression_12 +CODE *expression_12(MPL *mpl); +/* parse expression of level 12 */ + +#define expression_13 _glp_mpl_expression_13 +CODE *expression_13(MPL *mpl); +/* parse expression of level 13 */ + +#define set_statement _glp_mpl_set_statement +SET *set_statement(MPL *mpl); +/* parse set statement */ + +#define parameter_statement _glp_mpl_parameter_statement +PARAMETER *parameter_statement(MPL *mpl); +/* parse parameter statement */ + +#define variable_statement _glp_mpl_variable_statement +VARIABLE *variable_statement(MPL *mpl); +/* parse variable statement */ + +#define constraint_statement _glp_mpl_constraint_statement +CONSTRAINT *constraint_statement(MPL *mpl); +/* parse constraint statement */ + +#define objective_statement _glp_mpl_objective_statement +CONSTRAINT *objective_statement(MPL *mpl); +/* parse objective statement */ + +#define table_statement _glp_mpl_table_statement +TABLE *table_statement(MPL *mpl); +/* parse table statement */ + +#define solve_statement _glp_mpl_solve_statement +void *solve_statement(MPL *mpl); +/* parse solve statement */ + +#define check_statement _glp_mpl_check_statement +CHECK *check_statement(MPL *mpl); +/* parse check statement */ + +#define display_statement _glp_mpl_display_statement +DISPLAY *display_statement(MPL *mpl); +/* parse display statement */ + +#define printf_statement _glp_mpl_printf_statement +PRINTF *printf_statement(MPL *mpl); +/* parse printf statement */ + +#define for_statement _glp_mpl_for_statement +FOR *for_statement(MPL *mpl); +/* parse for statement */ + +#define end_statement _glp_mpl_end_statement +void end_statement(MPL *mpl); +/* parse end statement */ + +#define simple_statement _glp_mpl_simple_statement +STATEMENT *simple_statement(MPL *mpl, int spec); +/* parse simple statement */ + +#define model_section _glp_mpl_model_section +void model_section(MPL *mpl); +/* parse model section */ + +/**********************************************************************/ +/* * * PROCESSING DATA SECTION * * */ +/**********************************************************************/ + +#if 2 + 2 == 5 +struct SLICE /* see TUPLE */ +{ /* component of slice; the slice itself is associated with its + first component; slices are similar to n-tuples with exception + that some slice components (which are indicated by asterisks) + don't refer to any symbols */ + SYMBOL *sym; + /* symbol, which this component refers to; can be NULL */ + SLICE *next; + /* the next component of slice */ +}; +#endif + +#define create_slice _glp_mpl_create_slice +SLICE *create_slice(MPL *mpl); +/* create slice */ + +#define expand_slice _glp_mpl_expand_slice +SLICE *expand_slice +( MPL *mpl, + SLICE *slice, /* destroyed */ + SYMBOL *sym /* destroyed */ +); +/* append new component to slice */ + +#define slice_dimen _glp_mpl_slice_dimen +int slice_dimen +( MPL *mpl, + SLICE *slice /* not changed */ +); +/* determine dimension of slice */ + +#define slice_arity _glp_mpl_slice_arity +int slice_arity +( MPL *mpl, + SLICE *slice /* not changed */ +); +/* determine arity of slice */ + +#define fake_slice _glp_mpl_fake_slice +SLICE *fake_slice(MPL *mpl, int dim); +/* create fake slice of all asterisks */ + +#define delete_slice _glp_mpl_delete_slice +void delete_slice +( MPL *mpl, + SLICE *slice /* destroyed */ +); +/* delete slice */ + +#define is_number _glp_mpl_is_number +int is_number(MPL *mpl); +/* check if current token is number */ + +#define is_symbol _glp_mpl_is_symbol +int is_symbol(MPL *mpl); +/* check if current token is symbol */ + +#define is_literal _glp_mpl_is_literal +int is_literal(MPL *mpl, char *literal); +/* check if current token is given symbolic literal */ + +#define read_number _glp_mpl_read_number +double read_number(MPL *mpl); +/* read number */ + +#define read_symbol _glp_mpl_read_symbol +SYMBOL *read_symbol(MPL *mpl); +/* read symbol */ + +#define read_slice _glp_mpl_read_slice +SLICE *read_slice +( MPL *mpl, + char *name, /* not changed */ + int dim +); +/* read slice */ + +#define select_set _glp_mpl_select_set +SET *select_set +( MPL *mpl, + char *name /* not changed */ +); +/* select set to saturate it with elemental sets */ + +#define simple_format _glp_mpl_simple_format +void simple_format +( MPL *mpl, + SET *set, /* not changed */ + MEMBER *memb, /* modified */ + SLICE *slice /* not changed */ +); +/* read set data block in simple format */ + +#define matrix_format _glp_mpl_matrix_format +void matrix_format +( MPL *mpl, + SET *set, /* not changed */ + MEMBER *memb, /* modified */ + SLICE *slice, /* not changed */ + int tr +); +/* read set data block in matrix format */ + +#define set_data _glp_mpl_set_data +void set_data(MPL *mpl); +/* read set data */ + +#define select_parameter _glp_mpl_select_parameter +PARAMETER *select_parameter +( MPL *mpl, + char *name /* not changed */ +); +/* select parameter to saturate it with data */ + +#define set_default _glp_mpl_set_default +void set_default +( MPL *mpl, + PARAMETER *par, /* not changed */ + SYMBOL *altval /* destroyed */ +); +/* set default parameter value */ + +#define read_value _glp_mpl_read_value +MEMBER *read_value +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* destroyed */ +); +/* read value and assign it to parameter member */ + +#define plain_format _glp_mpl_plain_format +void plain_format +( MPL *mpl, + PARAMETER *par, /* not changed */ + SLICE *slice /* not changed */ +); +/* read parameter data block in plain format */ + +#define tabular_format _glp_mpl_tabular_format +void tabular_format +( MPL *mpl, + PARAMETER *par, /* not changed */ + SLICE *slice, /* not changed */ + int tr +); +/* read parameter data block in tabular format */ + +#define tabbing_format _glp_mpl_tabbing_format +void tabbing_format +( MPL *mpl, + SYMBOL *altval /* not changed */ +); +/* read parameter data block in tabbing format */ + +#define parameter_data _glp_mpl_parameter_data +void parameter_data(MPL *mpl); +/* read parameter data */ + +#define data_section _glp_mpl_data_section +void data_section(MPL *mpl); +/* read data section */ + +/**********************************************************************/ +/* * * FLOATING-POINT NUMBERS * * */ +/**********************************************************************/ + +#define fp_add _glp_mpl_fp_add +double fp_add(MPL *mpl, double x, double y); +/* floating-point addition */ + +#define fp_sub _glp_mpl_fp_sub +double fp_sub(MPL *mpl, double x, double y); +/* floating-point subtraction */ + +#define fp_less _glp_mpl_fp_less +double fp_less(MPL *mpl, double x, double y); +/* floating-point non-negative subtraction */ + +#define fp_mul _glp_mpl_fp_mul +double fp_mul(MPL *mpl, double x, double y); +/* floating-point multiplication */ + +#define fp_div _glp_mpl_fp_div +double fp_div(MPL *mpl, double x, double y); +/* floating-point division */ + +#define fp_idiv _glp_mpl_fp_idiv +double fp_idiv(MPL *mpl, double x, double y); +/* floating-point quotient of exact division */ + +#define fp_mod _glp_mpl_fp_mod +double fp_mod(MPL *mpl, double x, double y); +/* floating-point remainder of exact division */ + +#define fp_power _glp_mpl_fp_power +double fp_power(MPL *mpl, double x, double y); +/* floating-point exponentiation (raise to power) */ + +#define fp_exp _glp_mpl_fp_exp +double fp_exp(MPL *mpl, double x); +/* floating-point base-e exponential */ + +#define fp_log _glp_mpl_fp_log +double fp_log(MPL *mpl, double x); +/* floating-point natural logarithm */ + +#define fp_log10 _glp_mpl_fp_log10 +double fp_log10(MPL *mpl, double x); +/* floating-point common (decimal) logarithm */ + +#define fp_sqrt _glp_mpl_fp_sqrt +double fp_sqrt(MPL *mpl, double x); +/* floating-point square root */ + +#define fp_sin _glp_mpl_fp_sin +double fp_sin(MPL *mpl, double x); +/* floating-point trigonometric sine */ + +#define fp_cos _glp_mpl_fp_cos +double fp_cos(MPL *mpl, double x); +/* floating-point trigonometric cosine */ + +#define fp_atan _glp_mpl_fp_atan +double fp_atan(MPL *mpl, double x); +/* floating-point trigonometric arctangent */ + +#define fp_atan2 _glp_mpl_fp_atan2 +double fp_atan2(MPL *mpl, double y, double x); +/* floating-point trigonometric arctangent */ + +#define fp_round _glp_mpl_fp_round +double fp_round(MPL *mpl, double x, double n); +/* round floating-point value to n fractional digits */ + +#define fp_trunc _glp_mpl_fp_trunc +double fp_trunc(MPL *mpl, double x, double n); +/* truncate floating-point value to n fractional digits */ + +/**********************************************************************/ +/* * * PSEUDO-RANDOM NUMBER GENERATORS * * */ +/**********************************************************************/ + +#define fp_irand224 _glp_mpl_fp_irand224 +double fp_irand224(MPL *mpl); +/* pseudo-random integer in the range [0, 2^24) */ + +#define fp_uniform01 _glp_mpl_fp_uniform01 +double fp_uniform01(MPL *mpl); +/* pseudo-random number in the range [0, 1) */ + +#define fp_uniform _glp_mpl_uniform +double fp_uniform(MPL *mpl, double a, double b); +/* pseudo-random number in the range [a, b) */ + +#define fp_normal01 _glp_mpl_fp_normal01 +double fp_normal01(MPL *mpl); +/* Gaussian random variate with mu = 0 and sigma = 1 */ + +#define fp_normal _glp_mpl_fp_normal +double fp_normal(MPL *mpl, double mu, double sigma); +/* Gaussian random variate with specified mu and sigma */ + +/**********************************************************************/ +/* * * DATE/TIME * * */ +/**********************************************************************/ + +#define fn_gmtime _glp_mpl_fn_gmtime +double fn_gmtime(MPL *mpl); +/* obtain the current calendar time (UTC) */ + +#define fn_str2time _glp_mpl_fn_str2time +double fn_str2time(MPL *mpl, const char *str, const char *fmt); +/* convert character string to the calendar time */ + +#define fn_time2str _glp_mpl_fn_time2str +void fn_time2str(MPL *mpl, char *str, double t, const char *fmt); +/* convert the calendar time to character string */ + +/**********************************************************************/ +/* * * CHARACTER STRINGS * * */ +/**********************************************************************/ + +#define create_string _glp_mpl_create_string +STRING *create_string +( MPL *mpl, + char buf[MAX_LENGTH+1] /* not changed */ +); +/* create character string */ + +#define copy_string _glp_mpl_copy_string +STRING *copy_string +( MPL *mpl, + STRING *str /* not changed */ +); +/* make copy of character string */ + +#define compare_strings _glp_mpl_compare_strings +int compare_strings +( MPL *mpl, + STRING *str1, /* not changed */ + STRING *str2 /* not changed */ +); +/* compare one character string with another */ + +#define fetch_string _glp_mpl_fetch_string +char *fetch_string +( MPL *mpl, + STRING *str, /* not changed */ + char buf[MAX_LENGTH+1] /* modified */ +); +/* extract content of character string */ + +#define delete_string _glp_mpl_delete_string +void delete_string +( MPL *mpl, + STRING *str /* destroyed */ +); +/* delete character string */ + +/**********************************************************************/ +/* * * SYMBOLS * * */ +/**********************************************************************/ + +struct SYMBOL +{ /* symbol (numeric or abstract quantity) */ + double num; + /* numeric value of symbol (used only if str == NULL) */ + STRING *str; + /* abstract value of symbol (used only if str != NULL) */ +}; + +#define create_symbol_num _glp_mpl_create_symbol_num +SYMBOL *create_symbol_num(MPL *mpl, double num); +/* create symbol of numeric type */ + +#define create_symbol_str _glp_mpl_create_symbol_str +SYMBOL *create_symbol_str +( MPL *mpl, + STRING *str /* destroyed */ +); +/* create symbol of abstract type */ + +#define copy_symbol _glp_mpl_copy_symbol +SYMBOL *copy_symbol +( MPL *mpl, + SYMBOL *sym /* not changed */ +); +/* make copy of symbol */ + +#define compare_symbols _glp_mpl_compare_symbols +int compare_symbols +( MPL *mpl, + SYMBOL *sym1, /* not changed */ + SYMBOL *sym2 /* not changed */ +); +/* compare one symbol with another */ + +#define delete_symbol _glp_mpl_delete_symbol +void delete_symbol +( MPL *mpl, + SYMBOL *sym /* destroyed */ +); +/* delete symbol */ + +#define format_symbol _glp_mpl_format_symbol +char *format_symbol +( MPL *mpl, + SYMBOL *sym /* not changed */ +); +/* format symbol for displaying or printing */ + +#define concat_symbols _glp_mpl_concat_symbols +SYMBOL *concat_symbols +( MPL *mpl, + SYMBOL *sym1, /* destroyed */ + SYMBOL *sym2 /* destroyed */ +); +/* concatenate one symbol with another */ + +/**********************************************************************/ +/* * * N-TUPLES * * */ +/**********************************************************************/ + +struct TUPLE +{ /* component of n-tuple; the n-tuple itself is associated with + its first component; (note that 0-tuple has no components) */ + SYMBOL *sym; + /* symbol, which the component refers to; cannot be NULL */ + TUPLE *next; + /* the next component of n-tuple */ +}; + +#define create_tuple _glp_mpl_create_tuple +TUPLE *create_tuple(MPL *mpl); +/* create n-tuple */ + +#define expand_tuple _glp_mpl_expand_tuple +TUPLE *expand_tuple +( MPL *mpl, + TUPLE *tuple, /* destroyed */ + SYMBOL *sym /* destroyed */ +); +/* append symbol to n-tuple */ + +#define tuple_dimen _glp_mpl_tuple_dimen +int tuple_dimen +( MPL *mpl, + TUPLE *tuple /* not changed */ +); +/* determine dimension of n-tuple */ + +#define copy_tuple _glp_mpl_copy_tuple +TUPLE *copy_tuple +( MPL *mpl, + TUPLE *tuple /* not changed */ +); +/* make copy of n-tuple */ + +#define compare_tuples _glp_mpl_compare_tuples +int compare_tuples +( MPL *mpl, + TUPLE *tuple1, /* not changed */ + TUPLE *tuple2 /* not changed */ +); +/* compare one n-tuple with another */ + +#define build_subtuple _glp_mpl_build_subtuple +TUPLE *build_subtuple +( MPL *mpl, + TUPLE *tuple, /* not changed */ + int dim +); +/* build subtuple of given n-tuple */ + +#define delete_tuple _glp_mpl_delete_tuple +void delete_tuple +( MPL *mpl, + TUPLE *tuple /* destroyed */ +); +/* delete n-tuple */ + +#define format_tuple _glp_mpl_format_tuple +char *format_tuple +( MPL *mpl, + int c, + TUPLE *tuple /* not changed */ +); +/* format n-tuple for displaying or printing */ + +/**********************************************************************/ +/* * * ELEMENTAL SETS * * */ +/**********************************************************************/ + +#if 2 + 2 == 5 +struct ELEMSET /* see ARRAY */ +{ /* elemental set of n-tuples; formally it is a "value" assigned + to members of model sets (like numbers and symbols, which are + values assigned to members of model parameters); note that a + simple model set is not an elemental set, it is 0-dimensional + array, the only member of which (if it exists) is assigned an + elemental set */ +#endif + +#define create_elemset _glp_mpl_create_elemset +ELEMSET *create_elemset(MPL *mpl, int dim); +/* create elemental set */ + +#define find_tuple _glp_mpl_find_tuple +MEMBER *find_tuple +( MPL *mpl, + ELEMSET *set, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* check if elemental set contains given n-tuple */ + +#define add_tuple _glp_mpl_add_tuple +MEMBER *add_tuple +( MPL *mpl, + ELEMSET *set, /* modified */ + TUPLE *tuple /* destroyed */ +); +/* add new n-tuple to elemental set */ + +#define check_then_add _glp_mpl_check_then_add +MEMBER *check_then_add +( MPL *mpl, + ELEMSET *set, /* modified */ + TUPLE *tuple /* destroyed */ +); +/* check and add new n-tuple to elemental set */ + +#define copy_elemset _glp_mpl_copy_elemset +ELEMSET *copy_elemset +( MPL *mpl, + ELEMSET *set /* not changed */ +); +/* make copy of elemental set */ + +#define delete_elemset _glp_mpl_delete_elemset +void delete_elemset +( MPL *mpl, + ELEMSET *set /* destroyed */ +); +/* delete elemental set */ + +#define arelset_size _glp_mpl_arelset_size +int arelset_size(MPL *mpl, double t0, double tf, double dt); +/* compute size of "arithmetic" elemental set */ + +#define arelset_member _glp_mpl_arelset_member +double arelset_member(MPL *mpl, double t0, double tf, double dt, int j); +/* compute member of "arithmetic" elemental set */ + +#define create_arelset _glp_mpl_create_arelset +ELEMSET *create_arelset(MPL *mpl, double t0, double tf, double dt); +/* create "arithmetic" elemental set */ + +#define set_union _glp_mpl_set_union +ELEMSET *set_union +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +); +/* union of two elemental sets */ + +#define set_diff _glp_mpl_set_diff +ELEMSET *set_diff +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +); +/* difference between two elemental sets */ + +#define set_symdiff _glp_mpl_set_symdiff +ELEMSET *set_symdiff +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +); +/* symmetric difference between two elemental sets */ + +#define set_inter _glp_mpl_set_inter +ELEMSET *set_inter +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +); +/* intersection of two elemental sets */ + +#define set_cross _glp_mpl_set_cross +ELEMSET *set_cross +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +); +/* cross (Cartesian) product of two elemental sets */ + +/**********************************************************************/ +/* * * ELEMENTAL VARIABLES * * */ +/**********************************************************************/ + +struct ELEMVAR +{ /* elemental variable; formally it is a "value" assigned to + members of model variables (like numbers and symbols, which + are values assigned to members of model parameters) */ + int j; + /* LP column number assigned to this elemental variable */ + VARIABLE *var; + /* model variable, which contains this elemental variable */ + MEMBER *memb; + /* array member, which is assigned this elemental variable */ + double lbnd; + /* lower bound */ + double ubnd; + /* upper bound */ + double temp; + /* working quantity used in operations on linear forms; normally + it contains floating-point zero */ +#if 1 /* 15/V-2010 */ + int stat; + double prim, dual; + /* solution components provided by the solver */ +#endif +}; + +/**********************************************************************/ +/* * * LINEAR FORMS * * */ +/**********************************************************************/ + +struct FORMULA +{ /* term of linear form c * x, where c is a coefficient, x is an + elemental variable; the linear form itself is the sum of terms + and is associated with its first term; (note that the linear + form may be empty that means the sum is equal to zero) */ + double coef; + /* coefficient at elemental variable or constant term */ + ELEMVAR *var; + /* reference to elemental variable; NULL means constant term */ + FORMULA *next; + /* the next term of linear form */ +}; + +#define constant_term _glp_mpl_constant_term +FORMULA *constant_term(MPL *mpl, double coef); +/* create constant term */ + +#define single_variable _glp_mpl_single_variable +FORMULA *single_variable +( MPL *mpl, + ELEMVAR *var /* referenced */ +); +/* create single variable */ + +#define copy_formula _glp_mpl_copy_formula +FORMULA *copy_formula +( MPL *mpl, + FORMULA *form /* not changed */ +); +/* make copy of linear form */ + +#define delete_formula _glp_mpl_delete_formula +void delete_formula +( MPL *mpl, + FORMULA *form /* destroyed */ +); +/* delete linear form */ + +#define linear_comb _glp_mpl_linear_comb +FORMULA *linear_comb +( MPL *mpl, + double a, FORMULA *fx, /* destroyed */ + double b, FORMULA *fy /* destroyed */ +); +/* linear combination of two linear forms */ + +#define remove_constant _glp_mpl_remove_constant +FORMULA *remove_constant +( MPL *mpl, + FORMULA *form, /* destroyed */ + double *coef /* modified */ +); +/* remove constant term from linear form */ + +#define reduce_terms _glp_mpl_reduce_terms +FORMULA *reduce_terms +( MPL *mpl, + FORMULA *form /* destroyed */ +); +/* reduce identical terms in linear form */ + +/**********************************************************************/ +/* * * ELEMENTAL CONSTRAINTS * * */ +/**********************************************************************/ + +struct ELEMCON +{ /* elemental constraint; formally it is a "value" assigned to + members of model constraints (like numbers or symbols, which + are values assigned to members of model parameters) */ + int i; + /* LP row number assigned to this elemental constraint */ + CONSTRAINT *con; + /* model constraint, which contains this elemental constraint */ + MEMBER *memb; + /* array member, which is assigned this elemental constraint */ + FORMULA *form; + /* linear form */ + double lbnd; + /* lower bound */ + double ubnd; + /* upper bound */ +#if 1 /* 15/V-2010 */ + int stat; + double prim, dual; + /* solution components provided by the solver */ +#endif +}; + +/**********************************************************************/ +/* * * GENERIC VALUES * * */ +/**********************************************************************/ + +union VALUE +{ /* generic value, which can be assigned to object member or be a + result of evaluation of expression */ + /* indicator that specifies the particular type of generic value + is stored in the corresponding array or pseudo-code descriptor + and can be one of the following: + A_NONE - no value + A_NUMERIC - floating-point number + A_SYMBOLIC - symbol + A_LOGICAL - logical value + A_TUPLE - n-tuple + A_ELEMSET - elemental set + A_ELEMVAR - elemental variable + A_FORMULA - linear form + A_ELEMCON - elemental constraint */ + void *none; /* null */ + double num; /* value */ + SYMBOL *sym; /* value */ + int bit; /* value */ + TUPLE *tuple; /* value */ + ELEMSET *set; /* value */ + ELEMVAR *var; /* reference */ + FORMULA *form; /* value */ + ELEMCON *con; /* reference */ +}; + +#define delete_value _glp_mpl_delete_value +void delete_value +( MPL *mpl, + int type, + VALUE *value /* content destroyed */ +); +/* delete generic value */ + +/**********************************************************************/ +/* * * SYMBOLICALLY INDEXED ARRAYS * * */ +/**********************************************************************/ + +struct ARRAY +{ /* multi-dimensional array, a set of members indexed over simple + or compound sets of symbols; arrays are used to represent the + contents of model objects (i.e. sets, parameters, variables, + constraints, and objectives); arrays also are used as "values" + that are assigned to members of set objects, in which case the + array itself represents an elemental set */ + int type; + /* type of generic values assigned to the array members: + A_NONE - none (members have no assigned values) + A_NUMERIC - floating-point numbers + A_SYMBOLIC - symbols + A_ELEMSET - elemental sets + A_ELEMVAR - elemental variables + A_ELEMCON - elemental constraints */ + int dim; + /* dimension of the array that determines number of components in + n-tuples for all members of the array, dim >= 0; dim = 0 means + the array is 0-dimensional */ + int size; + /* size of the array, i.e. number of its members */ + MEMBER *head; + /* the first array member; NULL means the array is empty */ + MEMBER *tail; + /* the last array member; NULL means the array is empty */ + AVL *tree; + /* the search tree intended to find array members for logarithmic + time; NULL means the search tree doesn't exist */ + ARRAY *prev; + /* the previous array in the translator database */ + ARRAY *next; + /* the next array in the translator database */ +}; + +struct MEMBER +{ /* array member */ + TUPLE *tuple; + /* n-tuple, which identifies the member; number of its components + is the same for all members within the array and determined by + the array dimension; duplicate members are not allowed */ + MEMBER *next; + /* the next array member */ + VALUE value; + /* generic value assigned to the member */ +}; + +#define create_array _glp_mpl_create_array +ARRAY *create_array(MPL *mpl, int type, int dim); +/* create array */ + +#define find_member _glp_mpl_find_member +MEMBER *find_member +( MPL *mpl, + ARRAY *array, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* find array member with given n-tuple */ + +#define add_member _glp_mpl_add_member +MEMBER *add_member +( MPL *mpl, + ARRAY *array, /* modified */ + TUPLE *tuple /* destroyed */ +); +/* add new member to array */ + +#define delete_array _glp_mpl_delete_array +void delete_array +( MPL *mpl, + ARRAY *array /* destroyed */ +); +/* delete array */ + +/**********************************************************************/ +/* * * DOMAINS AND DUMMY INDICES * * */ +/**********************************************************************/ + +struct DOMAIN +{ /* domain (a simple or compound set); syntactically domain looks + like '{ i in I, (j,k) in S, t in T : }'; domains + are used to define sets, over which model objects are indexed, + and also as constituents of iterated operators */ + DOMAIN_BLOCK *list; + /* linked list of domain blocks (in the example above such blocks + are 'i in I', '(j,k) in S', and 't in T'); this list cannot be + empty */ + CODE *code; + /* pseudo-code for computing the logical predicate, which follows + the colon; NULL means no predicate is specified */ +}; + +struct DOMAIN_BLOCK +{ /* domain block; syntactically domain blocks look like 'i in I', + '(j,k) in S', and 't in T' in the example above (in the sequel + sets like I, S, and T are called basic sets) */ + DOMAIN_SLOT *list; + /* linked list of domain slots (i.e. indexing positions); number + of slots in this list is the same as dimension of n-tuples in + the basic set; this list cannot be empty */ + CODE *code; + /* pseudo-code for computing basic set; cannot be NULL */ + TUPLE *backup; + /* if this n-tuple is not empty, current values of dummy indices + in the domain block are the same as components of this n-tuple + (note that this n-tuple may have larger dimension than number + of dummy indices in this block, in which case extra components + are ignored); this n-tuple is used to restore former values of + dummy indices, if they were changed due to recursive calls to + the domain block */ + DOMAIN_BLOCK *next; + /* the next block in the same domain */ +}; + +struct DOMAIN_SLOT +{ /* domain slot; it specifies an individual indexing position and + defines the corresponding dummy index */ + char *name; + /* symbolic name of the dummy index; null pointer means the dummy + index is not explicitly specified */ + CODE *code; + /* pseudo-code for computing symbolic value, at which the dummy + index is bound; NULL means the dummy index is free within the + domain scope */ + SYMBOL *value; + /* current value assigned to the dummy index; NULL means no value + is assigned at the moment */ + CODE *list; + /* linked list of pseudo-codes with operation O_INDEX referring + to this slot; this linked list is used to invalidate resultant + values of the operation, which depend on this dummy index */ + DOMAIN_SLOT *next; + /* the next slot in the same domain block */ +}; + +#define assign_dummy_index _glp_mpl_assign_dummy_index +void assign_dummy_index +( MPL *mpl, + DOMAIN_SLOT *slot, /* modified */ + SYMBOL *value /* not changed */ +); +/* assign new value to dummy index */ + +#define update_dummy_indices _glp_mpl_update_dummy_indices +void update_dummy_indices +( MPL *mpl, + DOMAIN_BLOCK *block /* not changed */ +); +/* update current values of dummy indices */ + +#define enter_domain_block _glp_mpl_enter_domain_block +int enter_domain_block +( MPL *mpl, + DOMAIN_BLOCK *block, /* not changed */ + TUPLE *tuple, /* not changed */ + void *info, void (*func)(MPL *mpl, void *info) +); +/* enter domain block */ + +#define eval_within_domain _glp_mpl_eval_within_domain +int eval_within_domain +( MPL *mpl, + DOMAIN *domain, /* not changed */ + TUPLE *tuple, /* not changed */ + void *info, void (*func)(MPL *mpl, void *info) +); +/* perform evaluation within domain scope */ + +#define loop_within_domain _glp_mpl_loop_within_domain +void loop_within_domain +( MPL *mpl, + DOMAIN *domain, /* not changed */ + void *info, int (*func)(MPL *mpl, void *info) +); +/* perform iterations within domain scope */ + +#define out_of_domain _glp_mpl_out_of_domain +void out_of_domain +( MPL *mpl, + char *name, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* raise domain exception */ + +#define get_domain_tuple _glp_mpl_get_domain_tuple +TUPLE *get_domain_tuple +( MPL *mpl, + DOMAIN *domain /* not changed */ +); +/* obtain current n-tuple from domain */ + +#define clean_domain _glp_mpl_clean_domain +void clean_domain(MPL *mpl, DOMAIN *domain); +/* clean domain */ + +/**********************************************************************/ +/* * * MODEL SETS * * */ +/**********************************************************************/ + +struct SET +{ /* model set */ + char *name; + /* symbolic name; cannot be NULL */ + char *alias; + /* alias; NULL means alias is not specified */ + int dim; /* aka arity */ + /* dimension (number of subscripts); dim = 0 means 0-dimensional + (unsubscripted) set, dim > 0 means set of sets */ + DOMAIN *domain; + /* subscript domain; NULL for 0-dimensional set */ + int dimen; + /* dimension of n-tuples, which members of this set consist of + (note that the model set itself is an array of elemental sets, + which are its members; so, don't confuse this dimension with + dimension of the model set); always non-zero */ + WITHIN *within; + /* list of supersets, which restrict each member of the set to be + in every superset from this list; this list can be empty */ + CODE *assign; + /* pseudo-code for computing assigned value; can be NULL */ + CODE *option; + /* pseudo-code for computing default value; can be NULL */ + GADGET *gadget; + /* plain set used to initialize the array of sets; can be NULL */ + int data; + /* data status flag: + 0 - no data are provided in the data section + 1 - data are provided, but not checked yet + 2 - data are provided and have been checked */ + ARRAY *array; + /* array of members, which are assigned elemental sets */ +}; + +struct WITHIN +{ /* restricting superset list entry */ + CODE *code; + /* pseudo-code for computing the superset; cannot be NULL */ + WITHIN *next; + /* the next entry for the same set or parameter */ +}; + +struct GADGET +{ /* plain set used to initialize the array of sets with data */ + SET *set; + /* pointer to plain set; cannot be NULL */ + int ind[20]; /* ind[dim+dimen]; */ + /* permutation of integers 1, 2, ..., dim+dimen */ +}; + +#define check_elem_set _glp_mpl_check_elem_set +void check_elem_set +( MPL *mpl, + SET *set, /* not changed */ + TUPLE *tuple, /* not changed */ + ELEMSET *refer /* not changed */ +); +/* check elemental set assigned to set member */ + +#define take_member_set _glp_mpl_take_member_set +ELEMSET *take_member_set /* returns reference, not value */ +( MPL *mpl, + SET *set, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* obtain elemental set assigned to set member */ + +#define eval_member_set _glp_mpl_eval_member_set +ELEMSET *eval_member_set /* returns reference, not value */ +( MPL *mpl, + SET *set, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* evaluate elemental set assigned to set member */ + +#define eval_whole_set _glp_mpl_eval_whole_set +void eval_whole_set(MPL *mpl, SET *set); +/* evaluate model set over entire domain */ + +#define clean_set _glp_mpl_clean_set +void clean_set(MPL *mpl, SET *set); +/* clean model set */ + +/**********************************************************************/ +/* * * MODEL PARAMETERS * * */ +/**********************************************************************/ + +struct PARAMETER +{ /* model parameter */ + char *name; + /* symbolic name; cannot be NULL */ + char *alias; + /* alias; NULL means alias is not specified */ + int dim; /* aka arity */ + /* dimension (number of subscripts); dim = 0 means 0-dimensional + (unsubscripted) parameter */ + DOMAIN *domain; + /* subscript domain; NULL for 0-dimensional parameter */ + int type; + /* parameter type: + A_NUMERIC - numeric + A_INTEGER - integer + A_BINARY - binary + A_SYMBOLIC - symbolic */ + CONDITION *cond; + /* list of conditions, which restrict each parameter member to + satisfy to every condition from this list; this list is used + only for numeric parameters and can be empty */ + WITHIN *in; + /* list of supersets, which restrict each parameter member to be + in every superset from this list; this list is used only for + symbolic parameters and can be empty */ + CODE *assign; + /* pseudo-code for computing assigned value; can be NULL */ + CODE *option; + /* pseudo-code for computing default value; can be NULL */ + int data; + /* data status flag: + 0 - no data are provided in the data section + 1 - data are provided, but not checked yet + 2 - data are provided and have been checked */ + SYMBOL *defval; + /* default value provided in the data section; can be NULL */ + ARRAY *array; + /* array of members, which are assigned numbers or symbols */ +}; + +struct CONDITION +{ /* restricting condition list entry */ + int rho; + /* flag that specifies the form of the condition: + O_LT - less than + O_LE - less than or equal to + O_EQ - equal to + O_GE - greater than or equal to + O_GT - greater than + O_NE - not equal to */ + CODE *code; + /* pseudo-code for computing the reference value */ + CONDITION *next; + /* the next entry for the same parameter */ +}; + +#define check_value_num _glp_mpl_check_value_num +void check_value_num +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple, /* not changed */ + double value +); +/* check numeric value assigned to parameter member */ + +#define take_member_num _glp_mpl_take_member_num +double take_member_num +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* obtain numeric value assigned to parameter member */ + +#define eval_member_num _glp_mpl_eval_member_num +double eval_member_num +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* evaluate numeric value assigned to parameter member */ + +#define check_value_sym _glp_mpl_check_value_sym +void check_value_sym +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple, /* not changed */ + SYMBOL *value /* not changed */ +); +/* check symbolic value assigned to parameter member */ + +#define take_member_sym _glp_mpl_take_member_sym +SYMBOL *take_member_sym /* returns value, not reference */ +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* obtain symbolic value assigned to parameter member */ + +#define eval_member_sym _glp_mpl_eval_member_sym +SYMBOL *eval_member_sym /* returns value, not reference */ +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* evaluate symbolic value assigned to parameter member */ + +#define eval_whole_par _glp_mpl_eval_whole_par +void eval_whole_par(MPL *mpl, PARAMETER *par); +/* evaluate model parameter over entire domain */ + +#define clean_parameter _glp_mpl_clean_parameter +void clean_parameter(MPL *mpl, PARAMETER *par); +/* clean model parameter */ + +/**********************************************************************/ +/* * * MODEL VARIABLES * * */ +/**********************************************************************/ + +struct VARIABLE +{ /* model variable */ + char *name; + /* symbolic name; cannot be NULL */ + char *alias; + /* alias; NULL means alias is not specified */ + int dim; /* aka arity */ + /* dimension (number of subscripts); dim = 0 means 0-dimensional + (unsubscripted) variable */ + DOMAIN *domain; + /* subscript domain; NULL for 0-dimensional variable */ + int type; + /* variable type: + A_NUMERIC - continuous + A_INTEGER - integer + A_BINARY - binary */ + CODE *lbnd; + /* pseudo-code for computing lower bound; NULL means lower bound + is not specified */ + CODE *ubnd; + /* pseudo-code for computing upper bound; NULL means upper bound + is not specified */ + /* if both the pointers lbnd and ubnd refer to the same code, the + variable is fixed at the corresponding value */ + ARRAY *array; + /* array of members, which are assigned elemental variables */ +}; + +#define take_member_var _glp_mpl_take_member_var +ELEMVAR *take_member_var /* returns reference */ +( MPL *mpl, + VARIABLE *var, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* obtain reference to elemental variable */ + +#define eval_member_var _glp_mpl_eval_member_var +ELEMVAR *eval_member_var /* returns reference */ +( MPL *mpl, + VARIABLE *var, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* evaluate reference to elemental variable */ + +#define eval_whole_var _glp_mpl_eval_whole_var +void eval_whole_var(MPL *mpl, VARIABLE *var); +/* evaluate model variable over entire domain */ + +#define clean_variable _glp_mpl_clean_variable +void clean_variable(MPL *mpl, VARIABLE *var); +/* clean model variable */ + +/**********************************************************************/ +/* * * MODEL CONSTRAINTS AND OBJECTIVES * * */ +/**********************************************************************/ + +struct CONSTRAINT +{ /* model constraint or objective */ + char *name; + /* symbolic name; cannot be NULL */ + char *alias; + /* alias; NULL means alias is not specified */ + int dim; /* aka arity */ + /* dimension (number of subscripts); dim = 0 means 0-dimensional + (unsubscripted) constraint */ + DOMAIN *domain; + /* subscript domain; NULL for 0-dimensional constraint */ + int type; + /* constraint type: + A_CONSTRAINT - constraint + A_MINIMIZE - objective (minimization) + A_MAXIMIZE - objective (maximization) */ + CODE *code; + /* pseudo-code for computing main linear form; cannot be NULL */ + CODE *lbnd; + /* pseudo-code for computing lower bound; NULL means lower bound + is not specified */ + CODE *ubnd; + /* pseudo-code for computing upper bound; NULL means upper bound + is not specified */ + /* if both the pointers lbnd and ubnd refer to the same code, the + constraint has the form of equation */ + ARRAY *array; + /* array of members, which are assigned elemental constraints */ +}; + +#define take_member_con _glp_mpl_take_member_con +ELEMCON *take_member_con /* returns reference */ +( MPL *mpl, + CONSTRAINT *con, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* obtain reference to elemental constraint */ + +#define eval_member_con _glp_mpl_eval_member_con +ELEMCON *eval_member_con /* returns reference */ +( MPL *mpl, + CONSTRAINT *con, /* not changed */ + TUPLE *tuple /* not changed */ +); +/* evaluate reference to elemental constraint */ + +#define eval_whole_con _glp_mpl_eval_whole_con +void eval_whole_con(MPL *mpl, CONSTRAINT *con); +/* evaluate model constraint over entire domain */ + +#define clean_constraint _glp_mpl_clean_constraint +void clean_constraint(MPL *mpl, CONSTRAINT *con); +/* clean model constraint */ + +/**********************************************************************/ +/* * * DATA TABLES * * */ +/**********************************************************************/ + +struct TABLE +{ /* data table */ + char *name; + /* symbolic name; cannot be NULL */ + char *alias; + /* alias; NULL means alias is not specified */ + int type; + /* table type: + A_INPUT - input table + A_OUTPUT - output table */ + TABARG *arg; + /* argument list; cannot be empty */ + union + { struct + { SET *set; + /* input set; NULL means the set is not specified */ + TABFLD *fld; + /* field list; cannot be empty */ + TABIN *list; + /* input list; can be empty */ + } in; + struct + { DOMAIN *domain; + /* subscript domain; cannot be NULL */ + TABOUT *list; + /* output list; cannot be empty */ + } out; + } u; +}; + +struct TABARG +{ /* table argument list entry */ + CODE *code; + /* pseudo-code for computing the argument */ + TABARG *next; + /* next entry for the same table */ +}; + +struct TABFLD +{ /* table field list entry */ + char *name; + /* field name; cannot be NULL */ + TABFLD *next; + /* next entry for the same table */ +}; + +struct TABIN +{ /* table input list entry */ + PARAMETER *par; + /* parameter to be read; cannot be NULL */ + char *name; + /* column name; cannot be NULL */ + TABIN *next; + /* next entry for the same table */ +}; + +struct TABOUT +{ /* table output list entry */ + CODE *code; + /* pseudo-code for computing the value to be written */ + char *name; + /* column name; cannot be NULL */ + TABOUT *next; + /* next entry for the same table */ +}; + +struct TABDCA +{ /* table driver communication area */ + int id; + /* driver identifier (set by mpl_tab_drv_open) */ + void *link; + /* driver link pointer (set by mpl_tab_drv_open) */ + int na; + /* number of arguments */ + char **arg; /* char *arg[1+ns]; */ + /* arg[k], 1 <= k <= ns, is pointer to k-th argument */ + int nf; + /* number of fields */ + char **name; /* char *name[1+nc]; */ + /* name[k], 1 <= k <= nc, is name of k-th field */ + int *type; /* int type[1+nc]; */ + /* type[k], 1 <= k <= nc, is type of k-th field: + '?' - value not assigned + 'N' - number + 'S' - character string */ + double *num; /* double num[1+nc]; */ + /* num[k], 1 <= k <= nc, is numeric value of k-th field */ + char **str; + /* str[k], 1 <= k <= nc, is string value of k-th field */ +}; + +#define mpl_tab_num_args _glp_mpl_tab_num_args +int mpl_tab_num_args(TABDCA *dca); + +#define mpl_tab_get_arg _glp_mpl_tab_get_arg +const char *mpl_tab_get_arg(TABDCA *dca, int k); + +#define mpl_tab_num_flds _glp_mpl_tab_num_flds +int mpl_tab_num_flds(TABDCA *dca); + +#define mpl_tab_get_name _glp_mpl_tab_get_name +const char *mpl_tab_get_name(TABDCA *dca, int k); + +#define mpl_tab_get_type _glp_mpl_tab_get_type +int mpl_tab_get_type(TABDCA *dca, int k); + +#define mpl_tab_get_num _glp_mpl_tab_get_num +double mpl_tab_get_num(TABDCA *dca, int k); + +#define mpl_tab_get_str _glp_mpl_tab_get_str +const char *mpl_tab_get_str(TABDCA *dca, int k); + +#define mpl_tab_set_num _glp_mpl_tab_set_num +void mpl_tab_set_num(TABDCA *dca, int k, double num); + +#define mpl_tab_set_str _glp_mpl_tab_set_str +void mpl_tab_set_str(TABDCA *dca, int k, const char *str); + +#define mpl_tab_drv_open _glp_mpl_tab_drv_open +void mpl_tab_drv_open(MPL *mpl, int mode); + +#define mpl_tab_drv_read _glp_mpl_tab_drv_read +int mpl_tab_drv_read(MPL *mpl); + +#define mpl_tab_drv_write _glp_mpl_tab_drv_write +void mpl_tab_drv_write(MPL *mpl); + +#define mpl_tab_drv_close _glp_mpl_tab_drv_close +void mpl_tab_drv_close(MPL *mpl); + +/**********************************************************************/ +/* * * PSEUDO-CODE * * */ +/**********************************************************************/ + +union OPERANDS +{ /* operands that participate in pseudo-code operation (choice of + particular operands depends on the operation code) */ + /*--------------------------------------------------------------*/ + double num; /* O_NUMBER */ + /* floaing-point number to be taken */ + /*--------------------------------------------------------------*/ + char *str; /* O_STRING */ + /* character string to be taken */ + /*--------------------------------------------------------------*/ + struct /* O_INDEX */ + { DOMAIN_SLOT *slot; + /* domain slot, which contains dummy index to be taken */ + CODE *next; + /* the next pseudo-code with op = O_INDEX, which refers to the + same slot as this one; pointer to the beginning of this list + is stored in the corresponding domain slot */ + } index; + /*--------------------------------------------------------------*/ + struct /* O_MEMNUM, O_MEMSYM */ + { PARAMETER *par; + /* model parameter, which contains member to be taken */ + ARG_LIST *list; + /* list of subscripts; NULL for 0-dimensional parameter */ + } par; + /*--------------------------------------------------------------*/ + struct /* O_MEMSET */ + { SET *set; + /* model set, which contains member to be taken */ + ARG_LIST *list; + /* list of subscripts; NULL for 0-dimensional set */ + } set; + /*--------------------------------------------------------------*/ + struct /* O_MEMVAR */ + { VARIABLE *var; + /* model variable, which contains member to be taken */ + ARG_LIST *list; + /* list of subscripts; NULL for 0-dimensional variable */ +#if 1 /* 15/V-2010 */ + int suff; + /* suffix specified: */ +#define DOT_NONE 0x00 /* none (means variable itself) */ +#define DOT_LB 0x01 /* .lb (lower bound) */ +#define DOT_UB 0x02 /* .ub (upper bound) */ +#define DOT_STATUS 0x03 /* .status (status) */ +#define DOT_VAL 0x04 /* .val (primal value) */ +#define DOT_DUAL 0x05 /* .dual (dual value) */ +#endif + } var; +#if 1 /* 15/V-2010 */ + /*--------------------------------------------------------------*/ + struct /* O_MEMCON */ + { CONSTRAINT *con; + /* model constraint, which contains member to be taken */ + ARG_LIST *list; + /* list of subscripys; NULL for 0-dimensional constraint */ + int suff; + /* suffix specified (see O_MEMVAR above) */ + } con; +#endif + /*--------------------------------------------------------------*/ + ARG_LIST *list; /* O_TUPLE, O_MAKE, n-ary operations */ + /* list of operands */ + /*--------------------------------------------------------------*/ + DOMAIN_BLOCK *slice; /* O_SLICE */ + /* domain block, which specifies slice (i.e. n-tuple that contains + free dummy indices); this operation is never evaluated */ + /*--------------------------------------------------------------*/ + struct /* unary, binary, ternary operations */ + { CODE *x; + /* pseudo-code for computing first operand */ + CODE *y; + /* pseudo-code for computing second operand */ + CODE *z; + /* pseudo-code for computing third operand */ + } arg; + /*--------------------------------------------------------------*/ + struct /* iterated operations */ + { DOMAIN *domain; + /* domain, over which the operation is performed */ + CODE *x; + /* pseudo-code for computing "integrand" */ + } loop; + /*--------------------------------------------------------------*/ +}; + +struct ARG_LIST +{ /* operands list entry */ + CODE *x; + /* pseudo-code for computing operand */ + ARG_LIST *next; + /* the next operand of the same operation */ +}; + +struct CODE +{ /* pseudo-code (internal form of expressions) */ + int op; + /* operation code: */ +#define O_NUMBER 301 /* take floating-point number */ +#define O_STRING 302 /* take character string */ +#define O_INDEX 303 /* take dummy index */ +#define O_MEMNUM 304 /* take member of numeric parameter */ +#define O_MEMSYM 305 /* take member of symbolic parameter */ +#define O_MEMSET 306 /* take member of set */ +#define O_MEMVAR 307 /* take member of variable */ +#define O_MEMCON 308 /* take member of constraint */ +#define O_TUPLE 309 /* make n-tuple */ +#define O_MAKE 310 /* make elemental set of n-tuples */ +#define O_SLICE 311 /* define domain block (dummy op) */ + /* 0-ary operations --------------------*/ +#define O_IRAND224 312 /* pseudo-random in [0, 2^24-1] */ +#define O_UNIFORM01 313 /* pseudo-random in [0, 1) */ +#define O_NORMAL01 314 /* gaussian random, mu = 0, sigma = 1 */ +#define O_GMTIME 315 /* current calendar time (UTC) */ + /* unary operations --------------------*/ +#define O_CVTNUM 316 /* conversion to numeric */ +#define O_CVTSYM 317 /* conversion to symbolic */ +#define O_CVTLOG 318 /* conversion to logical */ +#define O_CVTTUP 319 /* conversion to 1-tuple */ +#define O_CVTLFM 320 /* conversion to linear form */ +#define O_PLUS 321 /* unary plus */ +#define O_MINUS 322 /* unary minus */ +#define O_NOT 323 /* negation (logical "not") */ +#define O_ABS 324 /* absolute value */ +#define O_CEIL 325 /* round upward ("ceiling of x") */ +#define O_FLOOR 326 /* round downward ("floor of x") */ +#define O_EXP 327 /* base-e exponential */ +#define O_LOG 328 /* natural logarithm */ +#define O_LOG10 329 /* common (decimal) logarithm */ +#define O_SQRT 330 /* square root */ +#define O_SIN 331 /* trigonometric sine */ +#define O_COS 332 /* trigonometric cosine */ +#define O_ATAN 333 /* trigonometric arctangent */ +#define O_ROUND 334 /* round to nearest integer */ +#define O_TRUNC 335 /* truncate to nearest integer */ +#define O_CARD 336 /* cardinality of set */ +#define O_LENGTH 337 /* length of symbolic value */ + /* binary operations -------------------*/ +#define O_ADD 338 /* addition */ +#define O_SUB 339 /* subtraction */ +#define O_LESS 340 /* non-negative subtraction */ +#define O_MUL 341 /* multiplication */ +#define O_DIV 342 /* division */ +#define O_IDIV 343 /* quotient of exact division */ +#define O_MOD 344 /* remainder of exact division */ +#define O_POWER 345 /* exponentiation (raise to power) */ +#define O_ATAN2 346 /* trigonometric arctangent */ +#define O_ROUND2 347 /* round to n fractional digits */ +#define O_TRUNC2 348 /* truncate to n fractional digits */ +#define O_UNIFORM 349 /* pseudo-random in [a, b) */ +#define O_NORMAL 350 /* gaussian random, given mu and sigma */ +#define O_CONCAT 351 /* concatenation */ +#define O_LT 352 /* comparison on 'less than' */ +#define O_LE 353 /* comparison on 'not greater than' */ +#define O_EQ 354 /* comparison on 'equal to' */ +#define O_GE 355 /* comparison on 'not less than' */ +#define O_GT 356 /* comparison on 'greater than' */ +#define O_NE 357 /* comparison on 'not equal to' */ +#define O_AND 358 /* conjunction (logical "and") */ +#define O_OR 359 /* disjunction (logical "or") */ +#define O_UNION 360 /* union */ +#define O_DIFF 361 /* difference */ +#define O_SYMDIFF 362 /* symmetric difference */ +#define O_INTER 363 /* intersection */ +#define O_CROSS 364 /* cross (Cartesian) product */ +#define O_IN 365 /* test on 'x in Y' */ +#define O_NOTIN 366 /* test on 'x not in Y' */ +#define O_WITHIN 367 /* test on 'X within Y' */ +#define O_NOTWITHIN 368 /* test on 'X not within Y' */ +#define O_SUBSTR 369 /* substring */ +#define O_STR2TIME 370 /* convert string to time */ +#define O_TIME2STR 371 /* convert time to string */ + /* ternary operations ------------------*/ +#define O_DOTS 372 /* build "arithmetic" set */ +#define O_FORK 373 /* if-then-else */ +#define O_SUBSTR3 374 /* substring */ + /* n-ary operations --------------------*/ +#define O_MIN 375 /* minimal value (n-ary) */ +#define O_MAX 376 /* maximal value (n-ary) */ + /* iterated operations -----------------*/ +#define O_SUM 377 /* summation */ +#define O_PROD 378 /* multiplication */ +#define O_MINIMUM 379 /* minimum */ +#define O_MAXIMUM 380 /* maximum */ +#define O_FORALL 381 /* conjunction (A-quantification) */ +#define O_EXISTS 382 /* disjunction (E-quantification) */ +#define O_SETOF 383 /* compute elemental set */ +#define O_BUILD 384 /* build elemental set */ + OPERANDS arg; + /* operands that participate in the operation */ + int type; + /* type of the resultant value: + A_NUMERIC - numeric + A_SYMBOLIC - symbolic + A_LOGICAL - logical + A_TUPLE - n-tuple + A_ELEMSET - elemental set + A_FORMULA - linear form */ + int dim; + /* dimension of the resultant value; for A_TUPLE and A_ELEMSET it + is the dimension of the corresponding n-tuple(s) and cannot be + zero; for other resultant types it is always zero */ + CODE *up; + /* parent pseudo-code, which refers to this pseudo-code as to its + operand; NULL means this pseudo-code has no parent and defines + an expression, which is not contained in another expression */ + int vflag; + /* volatile flag; being set this flag means that this operation + has a side effect; for primary expressions this flag is set + directly by corresponding parsing routines (for example, if + primary expression is a reference to a function that generates + pseudo-random numbers); in other cases this flag is inherited + from operands */ + int valid; + /* if this flag is set, the resultant value, which is a temporary + result of evaluating this operation on particular values of + operands, is valid; if this flag is clear, the resultant value + doesn't exist and therefore not valid; having been evaluated + the resultant value is stored here and not destroyed until the + dummy indices, which this value depends on, have been changed + (and if it doesn't depend on dummy indices at all, it is never + destroyed); thus, if the resultant value is valid, evaluating + routine can immediately take its copy not computing the result + from scratch; this mechanism is similar to moving invariants + out of loops and allows improving efficiency at the expense of + some extra memory needed to keep temporary results */ + /* however, if the volatile flag (see above) is set, even if the + resultant value is valid, evaluating routine computes it as if + it were not valid, i.e. caching is not used in this case */ + VALUE value; + /* resultant value in generic format */ +}; + +#define eval_numeric _glp_mpl_eval_numeric +double eval_numeric(MPL *mpl, CODE *code); +/* evaluate pseudo-code to determine numeric value */ + +#define eval_symbolic _glp_mpl_eval_symbolic +SYMBOL *eval_symbolic(MPL *mpl, CODE *code); +/* evaluate pseudo-code to determine symbolic value */ + +#define eval_logical _glp_mpl_eval_logical +int eval_logical(MPL *mpl, CODE *code); +/* evaluate pseudo-code to determine logical value */ + +#define eval_tuple _glp_mpl_eval_tuple +TUPLE *eval_tuple(MPL *mpl, CODE *code); +/* evaluate pseudo-code to construct n-tuple */ + +#define eval_elemset _glp_mpl_eval_elemset +ELEMSET *eval_elemset(MPL *mpl, CODE *code); +/* evaluate pseudo-code to construct elemental set */ + +#define is_member _glp_mpl_is_member +int is_member(MPL *mpl, CODE *code, TUPLE *tuple); +/* check if n-tuple is in set specified by pseudo-code */ + +#define eval_formula _glp_mpl_eval_formula +FORMULA *eval_formula(MPL *mpl, CODE *code); +/* evaluate pseudo-code to construct linear form */ + +#define clean_code _glp_mpl_clean_code +void clean_code(MPL *mpl, CODE *code); +/* clean pseudo-code */ + +/**********************************************************************/ +/* * * MODEL STATEMENTS * * */ +/**********************************************************************/ + +struct CHECK +{ /* check statement */ + DOMAIN *domain; + /* subscript domain; NULL means domain is not used */ + CODE *code; + /* code for computing the predicate to be checked */ +}; + +struct DISPLAY +{ /* display statement */ + DOMAIN *domain; + /* subscript domain; NULL means domain is not used */ + DISPLAY1 *list; + /* display list; cannot be empty */ +}; + +struct DISPLAY1 +{ /* display list entry */ + int type; + /* item type: + A_INDEX - dummy index + A_SET - model set + A_PARAMETER - model parameter + A_VARIABLE - model variable + A_CONSTRAINT - model constraint/objective + A_EXPRESSION - expression */ + union + { DOMAIN_SLOT *slot; + SET *set; + PARAMETER *par; + VARIABLE *var; + CONSTRAINT *con; + CODE *code; + } u; + /* item to be displayed */ +#if 0 /* 15/V-2010 */ + ARG_LIST *list; + /* optional subscript list (for constraint/objective only) */ +#endif + DISPLAY1 *next; + /* the next entry for the same statement */ +}; + +struct PRINTF +{ /* printf statement */ + DOMAIN *domain; + /* subscript domain; NULL means domain is not used */ + CODE *fmt; + /* pseudo-code for computing format string */ + PRINTF1 *list; + /* printf list; can be empty */ + CODE *fname; + /* pseudo-code for computing filename to redirect the output; + NULL means the output goes to stdout */ + int app; + /* if this flag is set, the output is appended */ +}; + +struct PRINTF1 +{ /* printf list entry */ + CODE *code; + /* pseudo-code for computing value to be printed */ + PRINTF1 *next; + /* the next entry for the same statement */ +}; + +struct FOR +{ /* for statement */ + DOMAIN *domain; + /* subscript domain; cannot be NULL */ + STATEMENT *list; + /* linked list of model statements within this for statement in + the original order */ +}; + +struct STATEMENT +{ /* model statement */ + int line; + /* number of source text line, where statement begins */ + int type; + /* statement type: + A_SET - set statement + A_PARAMETER - parameter statement + A_VARIABLE - variable statement + A_CONSTRAINT - constraint/objective statement + A_TABLE - table statement + A_SOLVE - solve statement + A_CHECK - check statement + A_DISPLAY - display statement + A_PRINTF - printf statement + A_FOR - for statement */ + union + { SET *set; + PARAMETER *par; + VARIABLE *var; + CONSTRAINT *con; + TABLE *tab; + void *slv; /* currently not used (set to NULL) */ + CHECK *chk; + DISPLAY *dpy; + PRINTF *prt; + FOR *fur; + } u; + /* specific part of statement */ + STATEMENT *next; + /* the next statement; in this list statements follow in the same + order as they appear in the model section */ +}; + +#define execute_table _glp_mpl_execute_table +void execute_table(MPL *mpl, TABLE *tab); +/* execute table statement */ + +#define free_dca _glp_mpl_free_dca +void free_dca(MPL *mpl); +/* free table driver communucation area */ + +#define clean_table _glp_mpl_clean_table +void clean_table(MPL *mpl, TABLE *tab); +/* clean table statement */ + +#define execute_check _glp_mpl_execute_check +void execute_check(MPL *mpl, CHECK *chk); +/* execute check statement */ + +#define clean_check _glp_mpl_clean_check +void clean_check(MPL *mpl, CHECK *chk); +/* clean check statement */ + +#define execute_display _glp_mpl_execute_display +void execute_display(MPL *mpl, DISPLAY *dpy); +/* execute display statement */ + +#define clean_display _glp_mpl_clean_display +void clean_display(MPL *mpl, DISPLAY *dpy); +/* clean display statement */ + +#define execute_printf _glp_mpl_execute_printf +void execute_printf(MPL *mpl, PRINTF *prt); +/* execute printf statement */ + +#define clean_printf _glp_mpl_clean_printf +void clean_printf(MPL *mpl, PRINTF *prt); +/* clean printf statement */ + +#define execute_for _glp_mpl_execute_for +void execute_for(MPL *mpl, FOR *fur); +/* execute for statement */ + +#define clean_for _glp_mpl_clean_for +void clean_for(MPL *mpl, FOR *fur); +/* clean for statement */ + +#define execute_statement _glp_mpl_execute_statement +void execute_statement(MPL *mpl, STATEMENT *stmt); +/* execute specified model statement */ + +#define clean_statement _glp_mpl_clean_statement +void clean_statement(MPL *mpl, STATEMENT *stmt); +/* clean specified model statement */ + +/**********************************************************************/ +/* * * GENERATING AND POSTSOLVING MODEL * * */ +/**********************************************************************/ + +#define alloc_content _glp_mpl_alloc_content +void alloc_content(MPL *mpl); +/* allocate content arrays for all model objects */ + +#define generate_model _glp_mpl_generate_model +void generate_model(MPL *mpl); +/* generate model */ + +#define build_problem _glp_mpl_build_problem +void build_problem(MPL *mpl); +/* build problem instance */ + +#define postsolve_model _glp_mpl_postsolve_model +void postsolve_model(MPL *mpl); +/* postsolve model */ + +#define clean_model _glp_mpl_clean_model +void clean_model(MPL *mpl); +/* clean model content */ + +/**********************************************************************/ +/* * * INPUT/OUTPUT * * */ +/**********************************************************************/ + +#define open_input _glp_mpl_open_input +void open_input(MPL *mpl, char *file); +/* open input text file */ + +#define read_char _glp_mpl_read_char +int read_char(MPL *mpl); +/* read next character from input text file */ + +#define close_input _glp_mpl_close_input +void close_input(MPL *mpl); +/* close input text file */ + +#define open_output _glp_mpl_open_output +void open_output(MPL *mpl, char *file); +/* open output text file */ + +#define write_char _glp_mpl_write_char +void write_char(MPL *mpl, int c); +/* write next character to output text file */ + +#define write_text _glp_mpl_write_text +void write_text(MPL *mpl, char *fmt, ...); +/* format and write text to output text file */ + +#define flush_output _glp_mpl_flush_output +void flush_output(MPL *mpl); +/* finalize writing data to output text file */ + +/**********************************************************************/ +/* * * SOLVER INTERFACE * * */ +/**********************************************************************/ + +#define MPL_FR 401 /* free (unbounded) */ +#define MPL_LO 402 /* lower bound */ +#define MPL_UP 403 /* upper bound */ +#define MPL_DB 404 /* both lower and upper bounds */ +#define MPL_FX 405 /* fixed */ + +#define MPL_ST 411 /* constraint */ +#define MPL_MIN 412 /* objective (minimization) */ +#define MPL_MAX 413 /* objective (maximization) */ + +#define MPL_NUM 421 /* continuous */ +#define MPL_INT 422 /* integer */ +#define MPL_BIN 423 /* binary */ + +#define error _glp_mpl_error +void error(MPL *mpl, char *fmt, ...); +/* print error message and terminate model processing */ + +#define warning _glp_mpl_warning +void warning(MPL *mpl, char *fmt, ...); +/* print warning message and continue model processing */ + +#define mpl_initialize _glp_mpl_initialize +MPL *mpl_initialize(void); +/* create and initialize translator database */ + +#define mpl_read_model _glp_mpl_read_model +int mpl_read_model(MPL *mpl, char *file, int skip_data); +/* read model section and optional data section */ + +#define mpl_read_data _glp_mpl_read_data +int mpl_read_data(MPL *mpl, char *file); +/* read data section */ + +#define mpl_generate _glp_mpl_generate +int mpl_generate(MPL *mpl, char *file); +/* generate model */ + +#define mpl_get_prob_name _glp_mpl_get_prob_name +char *mpl_get_prob_name(MPL *mpl); +/* obtain problem (model) name */ + +#define mpl_get_num_rows _glp_mpl_get_num_rows +int mpl_get_num_rows(MPL *mpl); +/* determine number of rows */ + +#define mpl_get_num_cols _glp_mpl_get_num_cols +int mpl_get_num_cols(MPL *mpl); +/* determine number of columns */ + +#define mpl_get_row_name _glp_mpl_get_row_name +char *mpl_get_row_name(MPL *mpl, int i); +/* obtain row name */ + +#define mpl_get_row_kind _glp_mpl_get_row_kind +int mpl_get_row_kind(MPL *mpl, int i); +/* determine row kind */ + +#define mpl_get_row_bnds _glp_mpl_get_row_bnds +int mpl_get_row_bnds(MPL *mpl, int i, double *lb, double *ub); +/* obtain row bounds */ + +#define mpl_get_mat_row _glp_mpl_get_mat_row +int mpl_get_mat_row(MPL *mpl, int i, int ndx[], double val[]); +/* obtain row of the constraint matrix */ + +#define mpl_get_row_c0 _glp_mpl_get_row_c0 +double mpl_get_row_c0(MPL *mpl, int i); +/* obtain constant term of free row */ + +#define mpl_get_col_name _glp_mpl_get_col_name +char *mpl_get_col_name(MPL *mpl, int j); +/* obtain column name */ + +#define mpl_get_col_kind _glp_mpl_get_col_kind +int mpl_get_col_kind(MPL *mpl, int j); +/* determine column kind */ + +#define mpl_get_col_bnds _glp_mpl_get_col_bnds +int mpl_get_col_bnds(MPL *mpl, int j, double *lb, double *ub); +/* obtain column bounds */ + +#define mpl_has_solve_stmt _glp_mpl_has_solve_stmt +int mpl_has_solve_stmt(MPL *mpl); +/* check if model has solve statement */ + +#if 1 /* 15/V-2010 */ +#define mpl_put_row_soln _glp_mpl_put_row_soln +void mpl_put_row_soln(MPL *mpl, int i, int stat, double prim, + double dual); +/* store row (constraint/objective) solution components */ +#endif + +#if 1 /* 15/V-2010 */ +#define mpl_put_col_soln _glp_mpl_put_col_soln +void mpl_put_col_soln(MPL *mpl, int j, int stat, double prim, + double dual); +/* store column (variable) solution components */ +#endif + +#if 0 /* 15/V-2010 */ +#define mpl_put_col_value _glp_mpl_put_col_value +void mpl_put_col_value(MPL *mpl, int j, double val); +/* store column value */ +#endif + +#define mpl_postsolve _glp_mpl_postsolve +int mpl_postsolve(MPL *mpl); +/* postsolve model */ + +#define mpl_terminate _glp_mpl_terminate +void mpl_terminate(MPL *mpl); +/* free all resources used by translator */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmpl01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmpl01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,4715 @@ +/* glpmpl01.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_STDIO +#include "glpmpl.h" +#define dmp_get_atomv dmp_get_atom + +/**********************************************************************/ +/* * * PROCESSING MODEL SECTION * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- enter_context - enter current token into context queue. +-- +-- This routine enters the current token into the context queue. */ + +void enter_context(MPL *mpl) +{ char *image, *s; + if (mpl->token == T_EOF) + image = "_|_"; + else if (mpl->token == T_STRING) + image = "'...'"; + else + image = mpl->image; + xassert(0 <= mpl->c_ptr && mpl->c_ptr < CONTEXT_SIZE); + mpl->context[mpl->c_ptr++] = ' '; + if (mpl->c_ptr == CONTEXT_SIZE) mpl->c_ptr = 0; + for (s = image; *s != '\0'; s++) + { mpl->context[mpl->c_ptr++] = *s; + if (mpl->c_ptr == CONTEXT_SIZE) mpl->c_ptr = 0; + } + return; +} + +/*---------------------------------------------------------------------- +-- print_context - print current content of context queue. +-- +-- This routine prints current content of the context queue. */ + +void print_context(MPL *mpl) +{ int c; + while (mpl->c_ptr > 0) + { mpl->c_ptr--; + c = mpl->context[0]; + memmove(mpl->context, mpl->context+1, CONTEXT_SIZE-1); + mpl->context[CONTEXT_SIZE-1] = (char)c; + } + xprintf("Context: %s%.*s\n", mpl->context[0] == ' ' ? "" : "...", + CONTEXT_SIZE, mpl->context); + return; +} + +/*---------------------------------------------------------------------- +-- get_char - scan next character from input text file. +-- +-- This routine scans a next ASCII character from the input text file. +-- In case of end-of-file, the character is assigned EOF. */ + +void get_char(MPL *mpl) +{ int c; + if (mpl->c == EOF) goto done; + if (mpl->c == '\n') mpl->line++; + c = read_char(mpl); + if (c == EOF) + { if (mpl->c == '\n') + mpl->line--; + else + warning(mpl, "final NL missing before end of file"); + } + else if (c == '\n') + ; + else if (isspace(c)) + c = ' '; + else if (iscntrl(c)) + { enter_context(mpl); + error(mpl, "control character 0x%02X not allowed", c); + } + mpl->c = c; +done: return; +} + +/*---------------------------------------------------------------------- +-- append_char - append character to current token. +-- +-- This routine appends the current character to the current token and +-- then scans a next character. */ + +void append_char(MPL *mpl) +{ xassert(0 <= mpl->imlen && mpl->imlen <= MAX_LENGTH); + if (mpl->imlen == MAX_LENGTH) + { switch (mpl->token) + { case T_NAME: + enter_context(mpl); + error(mpl, "symbolic name %s... too long", mpl->image); + case T_SYMBOL: + enter_context(mpl); + error(mpl, "symbol %s... too long", mpl->image); + case T_NUMBER: + enter_context(mpl); + error(mpl, "numeric literal %s... too long", mpl->image); + case T_STRING: + enter_context(mpl); + error(mpl, "string literal too long"); + default: + xassert(mpl != mpl); + } + } + mpl->image[mpl->imlen++] = (char)mpl->c; + mpl->image[mpl->imlen] = '\0'; + get_char(mpl); + return; +} + +/*---------------------------------------------------------------------- +-- get_token - scan next token from input text file. +-- +-- This routine scans a next token from the input text file using the +-- standard finite automation technique. */ + +void get_token(MPL *mpl) +{ /* save the current token */ + mpl->b_token = mpl->token; + mpl->b_imlen = mpl->imlen; + strcpy(mpl->b_image, mpl->image); + mpl->b_value = mpl->value; + /* if the next token is already scanned, make it current */ + if (mpl->f_scan) + { mpl->f_scan = 0; + mpl->token = mpl->f_token; + mpl->imlen = mpl->f_imlen; + strcpy(mpl->image, mpl->f_image); + mpl->value = mpl->f_value; + goto done; + } +loop: /* nothing has been scanned so far */ + mpl->token = 0; + mpl->imlen = 0; + mpl->image[0] = '\0'; + mpl->value = 0.0; + /* skip any uninteresting characters */ + while (mpl->c == ' ' || mpl->c == '\n') get_char(mpl); + /* recognize and construct the token */ + if (mpl->c == EOF) + { /* end-of-file reached */ + mpl->token = T_EOF; + } + else if (mpl->c == '#') + { /* comment; skip anything until end-of-line */ + while (mpl->c != '\n' && mpl->c != EOF) get_char(mpl); + goto loop; + } + else if (!mpl->flag_d && (isalpha(mpl->c) || mpl->c == '_')) + { /* symbolic name or reserved keyword */ + mpl->token = T_NAME; + while (isalnum(mpl->c) || mpl->c == '_') append_char(mpl); + if (strcmp(mpl->image, "and") == 0) + mpl->token = T_AND; + else if (strcmp(mpl->image, "by") == 0) + mpl->token = T_BY; + else if (strcmp(mpl->image, "cross") == 0) + mpl->token = T_CROSS; + else if (strcmp(mpl->image, "diff") == 0) + mpl->token = T_DIFF; + else if (strcmp(mpl->image, "div") == 0) + mpl->token = T_DIV; + else if (strcmp(mpl->image, "else") == 0) + mpl->token = T_ELSE; + else if (strcmp(mpl->image, "if") == 0) + mpl->token = T_IF; + else if (strcmp(mpl->image, "in") == 0) + mpl->token = T_IN; +#if 1 /* 21/VII-2006 */ + else if (strcmp(mpl->image, "Infinity") == 0) + mpl->token = T_INFINITY; +#endif + else if (strcmp(mpl->image, "inter") == 0) + mpl->token = T_INTER; + else if (strcmp(mpl->image, "less") == 0) + mpl->token = T_LESS; + else if (strcmp(mpl->image, "mod") == 0) + mpl->token = T_MOD; + else if (strcmp(mpl->image, "not") == 0) + mpl->token = T_NOT; + else if (strcmp(mpl->image, "or") == 0) + mpl->token = T_OR; + else if (strcmp(mpl->image, "s") == 0 && mpl->c == '.') + { mpl->token = T_SPTP; + append_char(mpl); + if (mpl->c != 't') +sptp: { enter_context(mpl); + error(mpl, "keyword s.t. incomplete"); + } + append_char(mpl); + if (mpl->c != '.') goto sptp; + append_char(mpl); + } + else if (strcmp(mpl->image, "symdiff") == 0) + mpl->token = T_SYMDIFF; + else if (strcmp(mpl->image, "then") == 0) + mpl->token = T_THEN; + else if (strcmp(mpl->image, "union") == 0) + mpl->token = T_UNION; + else if (strcmp(mpl->image, "within") == 0) + mpl->token = T_WITHIN; + } + else if (!mpl->flag_d && isdigit(mpl->c)) + { /* numeric literal */ + mpl->token = T_NUMBER; + /* scan integer part */ + while (isdigit(mpl->c)) append_char(mpl); + /* scan optional fractional part */ + if (mpl->c == '.') + { append_char(mpl); + if (mpl->c == '.') + { /* hmm, it is not the fractional part, it is dots that + follow the integer part */ + mpl->imlen--; + mpl->image[mpl->imlen] = '\0'; + mpl->f_dots = 1; + goto conv; + } +frac: while (isdigit(mpl->c)) append_char(mpl); + } + /* scan optional decimal exponent */ + if (mpl->c == 'e' || mpl->c == 'E') + { append_char(mpl); + if (mpl->c == '+' || mpl->c == '-') append_char(mpl); + if (!isdigit(mpl->c)) + { enter_context(mpl); + error(mpl, "numeric literal %s incomplete", mpl->image); + } + while (isdigit(mpl->c)) append_char(mpl); + } + /* there must be no letter following the numeric literal */ + if (isalpha(mpl->c) || mpl->c == '_') + { enter_context(mpl); + error(mpl, "symbol %s%c... should be enclosed in quotes", + mpl->image, mpl->c); + } +conv: /* convert numeric literal to floating-point */ + if (str2num(mpl->image, &mpl->value)) +err: { enter_context(mpl); + error(mpl, "cannot convert numeric literal %s to floating-p" + "oint number", mpl->image); + } + } + else if (mpl->c == '\'' || mpl->c == '"') + { /* character string */ + int quote = mpl->c; + mpl->token = T_STRING; + get_char(mpl); + for (;;) + { if (mpl->c == '\n' || mpl->c == EOF) + { enter_context(mpl); + error(mpl, "unexpected end of line; string literal incom" + "plete"); + } + if (mpl->c == quote) + { get_char(mpl); + if (mpl->c != quote) break; + } + append_char(mpl); + } + } + else if (!mpl->flag_d && mpl->c == '+') + mpl->token = T_PLUS, append_char(mpl); + else if (!mpl->flag_d && mpl->c == '-') + mpl->token = T_MINUS, append_char(mpl); + else if (mpl->c == '*') + { mpl->token = T_ASTERISK, append_char(mpl); + if (mpl->c == '*') + mpl->token = T_POWER, append_char(mpl); + } + else if (mpl->c == '/') + { mpl->token = T_SLASH, append_char(mpl); + if (mpl->c == '*') + { /* comment sequence */ + get_char(mpl); + for (;;) + { if (mpl->c == EOF) + { /* do not call enter_context at this point */ + error(mpl, "unexpected end of file; comment sequence " + "incomplete"); + } + else if (mpl->c == '*') + { get_char(mpl); + if (mpl->c == '/') break; + } + else + get_char(mpl); + } + get_char(mpl); + goto loop; + } + } + else if (mpl->c == '^') + mpl->token = T_POWER, append_char(mpl); + else if (mpl->c == '<') + { mpl->token = T_LT, append_char(mpl); + if (mpl->c == '=') + mpl->token = T_LE, append_char(mpl); + else if (mpl->c == '>') + mpl->token = T_NE, append_char(mpl); +#if 1 /* 11/II-2008 */ + else if (mpl->c == '-') + mpl->token = T_INPUT, append_char(mpl); +#endif + } + else if (mpl->c == '=') + { mpl->token = T_EQ, append_char(mpl); + if (mpl->c == '=') append_char(mpl); + } + else if (mpl->c == '>') + { mpl->token = T_GT, append_char(mpl); + if (mpl->c == '=') + mpl->token = T_GE, append_char(mpl); +#if 1 /* 14/VII-2006 */ + else if (mpl->c == '>') + mpl->token = T_APPEND, append_char(mpl); +#endif + } + else if (mpl->c == '!') + { mpl->token = T_NOT, append_char(mpl); + if (mpl->c == '=') + mpl->token = T_NE, append_char(mpl); + } + else if (mpl->c == '&') + { mpl->token = T_CONCAT, append_char(mpl); + if (mpl->c == '&') + mpl->token = T_AND, append_char(mpl); + } + else if (mpl->c == '|') + { mpl->token = T_BAR, append_char(mpl); + if (mpl->c == '|') + mpl->token = T_OR, append_char(mpl); + } + else if (!mpl->flag_d && mpl->c == '.') + { mpl->token = T_POINT, append_char(mpl); + if (mpl->f_dots) + { /* dots; the first dot was read on the previous call to the + scanner, so the current character is the second dot */ + mpl->token = T_DOTS; + mpl->imlen = 2; + strcpy(mpl->image, ".."); + mpl->f_dots = 0; + } + else if (mpl->c == '.') + mpl->token = T_DOTS, append_char(mpl); + else if (isdigit(mpl->c)) + { /* numeric literal that begins with the decimal point */ + mpl->token = T_NUMBER, append_char(mpl); + goto frac; + } + } + else if (mpl->c == ',') + mpl->token = T_COMMA, append_char(mpl); + else if (mpl->c == ':') + { mpl->token = T_COLON, append_char(mpl); + if (mpl->c == '=') + mpl->token = T_ASSIGN, append_char(mpl); + } + else if (mpl->c == ';') + mpl->token = T_SEMICOLON, append_char(mpl); + else if (mpl->c == '(') + mpl->token = T_LEFT, append_char(mpl); + else if (mpl->c == ')') + mpl->token = T_RIGHT, append_char(mpl); + else if (mpl->c == '[') + mpl->token = T_LBRACKET, append_char(mpl); + else if (mpl->c == ']') + mpl->token = T_RBRACKET, append_char(mpl); + else if (mpl->c == '{') + mpl->token = T_LBRACE, append_char(mpl); + else if (mpl->c == '}') + mpl->token = T_RBRACE, append_char(mpl); +#if 1 /* 11/II-2008 */ + else if (mpl->c == '~') + mpl->token = T_TILDE, append_char(mpl); +#endif + else if (isalnum(mpl->c) || strchr("+-._", mpl->c) != NULL) + { /* symbol */ + xassert(mpl->flag_d); + mpl->token = T_SYMBOL; + while (isalnum(mpl->c) || strchr("+-._", mpl->c) != NULL) + append_char(mpl); + switch (str2num(mpl->image, &mpl->value)) + { case 0: + mpl->token = T_NUMBER; + break; + case 1: + goto err; + case 2: + break; + default: + xassert(mpl != mpl); + } + } + else + { enter_context(mpl); + error(mpl, "character %c not allowed", mpl->c); + } + /* enter the current token into the context queue */ + enter_context(mpl); + /* reset the flag, which may be set by indexing_expression() and + is used by expression_list() */ + mpl->flag_x = 0; +done: return; +} + +/*---------------------------------------------------------------------- +-- unget_token - return current token back to input stream. +-- +-- This routine returns the current token back to the input stream, so +-- the previously scanned token becomes the current one. */ + +void unget_token(MPL *mpl) +{ /* save the current token, which becomes the next one */ + xassert(!mpl->f_scan); + mpl->f_scan = 1; + mpl->f_token = mpl->token; + mpl->f_imlen = mpl->imlen; + strcpy(mpl->f_image, mpl->image); + mpl->f_value = mpl->value; + /* restore the previous token, which becomes the current one */ + mpl->token = mpl->b_token; + mpl->imlen = mpl->b_imlen; + strcpy(mpl->image, mpl->b_image); + mpl->value = mpl->b_value; + return; +} + +/*---------------------------------------------------------------------- +-- is_keyword - check if current token is given non-reserved keyword. +-- +-- If the current token is given (non-reserved) keyword, this routine +-- returns non-zero. Otherwise zero is returned. */ + +int is_keyword(MPL *mpl, char *keyword) +{ return + mpl->token == T_NAME && strcmp(mpl->image, keyword) == 0; +} + +/*---------------------------------------------------------------------- +-- is_reserved - check if current token is reserved keyword. +-- +-- If the current token is a reserved keyword, this routine returns +-- non-zero. Otherwise zero is returned. */ + +int is_reserved(MPL *mpl) +{ return + mpl->token == T_AND && mpl->image[0] == 'a' || + mpl->token == T_BY || + mpl->token == T_CROSS || + mpl->token == T_DIFF || + mpl->token == T_DIV || + mpl->token == T_ELSE || + mpl->token == T_IF || + mpl->token == T_IN || + mpl->token == T_INTER || + mpl->token == T_LESS || + mpl->token == T_MOD || + mpl->token == T_NOT && mpl->image[0] == 'n' || + mpl->token == T_OR && mpl->image[0] == 'o' || + mpl->token == T_SYMDIFF || + mpl->token == T_THEN || + mpl->token == T_UNION || + mpl->token == T_WITHIN; +} + +/*---------------------------------------------------------------------- +-- make_code - generate pseudo-code (basic routine). +-- +-- This routine generates specified pseudo-code. It is assumed that all +-- other translator routines use this basic routine. */ + +CODE *make_code(MPL *mpl, int op, OPERANDS *arg, int type, int dim) +{ CODE *code; + DOMAIN *domain; + DOMAIN_BLOCK *block; + ARG_LIST *e; + /* generate pseudo-code */ + code = alloc(CODE); + code->op = op; + code->vflag = 0; /* is inherited from operand(s) */ + /* copy operands and also make them referring to the pseudo-code + being generated, because the latter becomes the parent for all + its operands */ + memset(&code->arg, '?', sizeof(OPERANDS)); + switch (op) + { case O_NUMBER: + code->arg.num = arg->num; + break; + case O_STRING: + code->arg.str = arg->str; + break; + case O_INDEX: + code->arg.index.slot = arg->index.slot; + code->arg.index.next = arg->index.next; + break; + case O_MEMNUM: + case O_MEMSYM: + for (e = arg->par.list; e != NULL; e = e->next) + { xassert(e->x != NULL); + xassert(e->x->up == NULL); + e->x->up = code; + code->vflag |= e->x->vflag; + } + code->arg.par.par = arg->par.par; + code->arg.par.list = arg->par.list; + break; + case O_MEMSET: + for (e = arg->set.list; e != NULL; e = e->next) + { xassert(e->x != NULL); + xassert(e->x->up == NULL); + e->x->up = code; + code->vflag |= e->x->vflag; + } + code->arg.set.set = arg->set.set; + code->arg.set.list = arg->set.list; + break; + case O_MEMVAR: + for (e = arg->var.list; e != NULL; e = e->next) + { xassert(e->x != NULL); + xassert(e->x->up == NULL); + e->x->up = code; + code->vflag |= e->x->vflag; + } + code->arg.var.var = arg->var.var; + code->arg.var.list = arg->var.list; +#if 1 /* 15/V-2010 */ + code->arg.var.suff = arg->var.suff; +#endif + break; +#if 1 /* 15/V-2010 */ + case O_MEMCON: + for (e = arg->con.list; e != NULL; e = e->next) + { xassert(e->x != NULL); + xassert(e->x->up == NULL); + e->x->up = code; + code->vflag |= e->x->vflag; + } + code->arg.con.con = arg->con.con; + code->arg.con.list = arg->con.list; + code->arg.con.suff = arg->con.suff; + break; +#endif + case O_TUPLE: + case O_MAKE: + for (e = arg->list; e != NULL; e = e->next) + { xassert(e->x != NULL); + xassert(e->x->up == NULL); + e->x->up = code; + code->vflag |= e->x->vflag; + } + code->arg.list = arg->list; + break; + case O_SLICE: + xassert(arg->slice != NULL); + code->arg.slice = arg->slice; + break; + case O_IRAND224: + case O_UNIFORM01: + case O_NORMAL01: + case O_GMTIME: + code->vflag = 1; + break; + case O_CVTNUM: + case O_CVTSYM: + case O_CVTLOG: + case O_CVTTUP: + case O_CVTLFM: + case O_PLUS: + case O_MINUS: + case O_NOT: + case O_ABS: + case O_CEIL: + case O_FLOOR: + case O_EXP: + case O_LOG: + case O_LOG10: + case O_SQRT: + case O_SIN: + case O_COS: + case O_ATAN: + case O_ROUND: + case O_TRUNC: + case O_CARD: + case O_LENGTH: + /* unary operation */ + xassert(arg->arg.x != NULL); + xassert(arg->arg.x->up == NULL); + arg->arg.x->up = code; + code->vflag |= arg->arg.x->vflag; + code->arg.arg.x = arg->arg.x; + break; + case O_ADD: + case O_SUB: + case O_LESS: + case O_MUL: + case O_DIV: + case O_IDIV: + case O_MOD: + case O_POWER: + case O_ATAN2: + case O_ROUND2: + case O_TRUNC2: + case O_UNIFORM: + if (op == O_UNIFORM) code->vflag = 1; + case O_NORMAL: + if (op == O_NORMAL) code->vflag = 1; + case O_CONCAT: + case O_LT: + case O_LE: + case O_EQ: + case O_GE: + case O_GT: + case O_NE: + case O_AND: + case O_OR: + case O_UNION: + case O_DIFF: + case O_SYMDIFF: + case O_INTER: + case O_CROSS: + case O_IN: + case O_NOTIN: + case O_WITHIN: + case O_NOTWITHIN: + case O_SUBSTR: + case O_STR2TIME: + case O_TIME2STR: + /* binary operation */ + xassert(arg->arg.x != NULL); + xassert(arg->arg.x->up == NULL); + arg->arg.x->up = code; + code->vflag |= arg->arg.x->vflag; + xassert(arg->arg.y != NULL); + xassert(arg->arg.y->up == NULL); + arg->arg.y->up = code; + code->vflag |= arg->arg.y->vflag; + code->arg.arg.x = arg->arg.x; + code->arg.arg.y = arg->arg.y; + break; + case O_DOTS: + case O_FORK: + case O_SUBSTR3: + /* ternary operation */ + xassert(arg->arg.x != NULL); + xassert(arg->arg.x->up == NULL); + arg->arg.x->up = code; + code->vflag |= arg->arg.x->vflag; + xassert(arg->arg.y != NULL); + xassert(arg->arg.y->up == NULL); + arg->arg.y->up = code; + code->vflag |= arg->arg.y->vflag; + if (arg->arg.z != NULL) + { xassert(arg->arg.z->up == NULL); + arg->arg.z->up = code; + code->vflag |= arg->arg.z->vflag; + } + code->arg.arg.x = arg->arg.x; + code->arg.arg.y = arg->arg.y; + code->arg.arg.z = arg->arg.z; + break; + case O_MIN: + case O_MAX: + /* n-ary operation */ + for (e = arg->list; e != NULL; e = e->next) + { xassert(e->x != NULL); + xassert(e->x->up == NULL); + e->x->up = code; + code->vflag |= e->x->vflag; + } + code->arg.list = arg->list; + break; + case O_SUM: + case O_PROD: + case O_MINIMUM: + case O_MAXIMUM: + case O_FORALL: + case O_EXISTS: + case O_SETOF: + case O_BUILD: + /* iterated operation */ + domain = arg->loop.domain; + xassert(domain != NULL); + if (domain->code != NULL) + { xassert(domain->code->up == NULL); + domain->code->up = code; + code->vflag |= domain->code->vflag; + } + for (block = domain->list; block != NULL; block = + block->next) + { xassert(block->code != NULL); + xassert(block->code->up == NULL); + block->code->up = code; + code->vflag |= block->code->vflag; + } + if (arg->loop.x != NULL) + { xassert(arg->loop.x->up == NULL); + arg->loop.x->up = code; + code->vflag |= arg->loop.x->vflag; + } + code->arg.loop.domain = arg->loop.domain; + code->arg.loop.x = arg->loop.x; + break; + default: + xassert(op != op); + } + /* set other attributes of the pseudo-code */ + code->type = type; + code->dim = dim; + code->up = NULL; + code->valid = 0; + memset(&code->value, '?', sizeof(VALUE)); + return code; +} + +/*---------------------------------------------------------------------- +-- make_unary - generate pseudo-code for unary operation. +-- +-- This routine generates pseudo-code for unary operation. */ + +CODE *make_unary(MPL *mpl, int op, CODE *x, int type, int dim) +{ CODE *code; + OPERANDS arg; + xassert(x != NULL); + arg.arg.x = x; + code = make_code(mpl, op, &arg, type, dim); + return code; +} + +/*---------------------------------------------------------------------- +-- make_binary - generate pseudo-code for binary operation. +-- +-- This routine generates pseudo-code for binary operation. */ + +CODE *make_binary(MPL *mpl, int op, CODE *x, CODE *y, int type, + int dim) +{ CODE *code; + OPERANDS arg; + xassert(x != NULL); + xassert(y != NULL); + arg.arg.x = x; + arg.arg.y = y; + code = make_code(mpl, op, &arg, type, dim); + return code; +} + +/*---------------------------------------------------------------------- +-- make_ternary - generate pseudo-code for ternary operation. +-- +-- This routine generates pseudo-code for ternary operation. */ + +CODE *make_ternary(MPL *mpl, int op, CODE *x, CODE *y, CODE *z, + int type, int dim) +{ CODE *code; + OPERANDS arg; + xassert(x != NULL); + xassert(y != NULL); + /* third operand can be NULL */ + arg.arg.x = x; + arg.arg.y = y; + arg.arg.z = z; + code = make_code(mpl, op, &arg, type, dim); + return code; +} + +/*---------------------------------------------------------------------- +-- numeric_literal - parse reference to numeric literal. +-- +-- This routine parses primary expression using the syntax: +-- +-- ::= */ + +CODE *numeric_literal(MPL *mpl) +{ CODE *code; + OPERANDS arg; + xassert(mpl->token == T_NUMBER); + arg.num = mpl->value; + code = make_code(mpl, O_NUMBER, &arg, A_NUMERIC, 0); + get_token(mpl /* */); + return code; +} + +/*---------------------------------------------------------------------- +-- string_literal - parse reference to string literal. +-- +-- This routine parses primary expression using the syntax: +-- +-- ::= */ + +CODE *string_literal(MPL *mpl) +{ CODE *code; + OPERANDS arg; + xassert(mpl->token == T_STRING); + arg.str = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(arg.str, mpl->image); + code = make_code(mpl, O_STRING, &arg, A_SYMBOLIC, 0); + get_token(mpl /* */); + return code; +} + +/*---------------------------------------------------------------------- +-- create_arg_list - create empty operands list. +-- +-- This routine creates operands list, which is initially empty. */ + +ARG_LIST *create_arg_list(MPL *mpl) +{ ARG_LIST *list; + xassert(mpl == mpl); + list = NULL; + return list; +} + +/*---------------------------------------------------------------------- +-- expand_arg_list - append operand to operands list. +-- +-- This routine appends new operand to specified operands list. */ + +ARG_LIST *expand_arg_list(MPL *mpl, ARG_LIST *list, CODE *x) +{ ARG_LIST *tail, *temp; + xassert(x != NULL); + /* create new operands list entry */ + tail = alloc(ARG_LIST); + tail->x = x; + tail->next = NULL; + /* and append it to the operands list */ + if (list == NULL) + list = tail; + else + { for (temp = list; temp->next != NULL; temp = temp->next); + temp->next = tail; + } + return list; +} + +/*---------------------------------------------------------------------- +-- arg_list_len - determine length of operands list. +-- +-- This routine returns the number of operands in operands list. */ + +int arg_list_len(MPL *mpl, ARG_LIST *list) +{ ARG_LIST *temp; + int len; + xassert(mpl == mpl); + len = 0; + for (temp = list; temp != NULL; temp = temp->next) len++; + return len; +} + +/*---------------------------------------------------------------------- +-- subscript_list - parse subscript list. +-- +-- This routine parses subscript list using the syntax: +-- +-- ::= +-- ::= , +-- ::= */ + +ARG_LIST *subscript_list(MPL *mpl) +{ ARG_LIST *list; + CODE *x; + list = create_arg_list(mpl); + for (;;) + { /* parse subscript expression */ + x = expression_5(mpl); + /* convert it to symbolic type, if necessary */ + if (x->type == A_NUMERIC) + x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); + /* check that now the expression is of symbolic type */ + if (x->type != A_SYMBOLIC) + error(mpl, "subscript expression has invalid type"); + xassert(x->dim == 0); + /* and append it to the subscript list */ + list = expand_arg_list(mpl, list, x); + /* check a token that follows the subscript expression */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_RBRACKET) + break; + else + error(mpl, "syntax error in subscript list"); + } + return list; +} + +#if 1 /* 15/V-2010 */ +/*---------------------------------------------------------------------- +-- object_reference - parse reference to named object. +-- +-- This routine parses primary expression using the syntax: +-- +-- ::= +-- ::= +-- ::= [ ] +-- ::= +-- ::= [ ] +-- ::= +-- ::= [ ] +-- +-- ::= +-- ::= [ ] +-- +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= | .lb | .ub | .status | .val | .dual */ + +CODE *object_reference(MPL *mpl) +{ AVLNODE *node; + DOMAIN_SLOT *slot; + SET *set; + PARAMETER *par; + VARIABLE *var; + CONSTRAINT *con; + ARG_LIST *list; + OPERANDS arg; + CODE *code; + char *name; + int dim, suff; + /* find the object in the symbolic name table */ + xassert(mpl->token == T_NAME); + node = avl_find_node(mpl->tree, mpl->image); + if (node == NULL) + error(mpl, "%s not defined", mpl->image); + /* check the object type and obtain its dimension */ + switch (avl_get_node_type(node)) + { case A_INDEX: + /* dummy index */ + slot = (DOMAIN_SLOT *)avl_get_node_link(node); + name = slot->name; + dim = 0; + break; + case A_SET: + /* model set */ + set = (SET *)avl_get_node_link(node); + name = set->name; + dim = set->dim; + /* if a set object is referenced in its own declaration and + the dimen attribute is not specified yet, use dimen 1 by + default */ + if (set->dimen == 0) set->dimen = 1; + break; + case A_PARAMETER: + /* model parameter */ + par = (PARAMETER *)avl_get_node_link(node); + name = par->name; + dim = par->dim; + break; + case A_VARIABLE: + /* model variable */ + var = (VARIABLE *)avl_get_node_link(node); + name = var->name; + dim = var->dim; + break; + case A_CONSTRAINT: + /* model constraint or objective */ + con = (CONSTRAINT *)avl_get_node_link(node); + name = con->name; + dim = con->dim; + break; + default: + xassert(node != node); + } + get_token(mpl /* */); + /* parse optional subscript list */ + if (mpl->token == T_LBRACKET) + { /* subscript list is specified */ + if (dim == 0) + error(mpl, "%s cannot be subscripted", name); + get_token(mpl /* [ */); + list = subscript_list(mpl); + if (dim != arg_list_len(mpl, list)) + error(mpl, "%s must have %d subscript%s rather than %d", + name, dim, dim == 1 ? "" : "s", arg_list_len(mpl, list)); + xassert(mpl->token == T_RBRACKET); + get_token(mpl /* ] */); + } + else + { /* subscript list is not specified */ + if (dim != 0) + error(mpl, "%s must be subscripted", name); + list = create_arg_list(mpl); + } + /* parse optional suffix */ + if (!mpl->flag_s && avl_get_node_type(node) == A_VARIABLE) + suff = DOT_NONE; + else + suff = DOT_VAL; + if (mpl->token == T_POINT) + { get_token(mpl /* . */); + if (mpl->token != T_NAME) + error(mpl, "invalid use of period"); + if (!(avl_get_node_type(node) == A_VARIABLE || + avl_get_node_type(node) == A_CONSTRAINT)) + error(mpl, "%s cannot have a suffix", name); + if (strcmp(mpl->image, "lb") == 0) + suff = DOT_LB; + else if (strcmp(mpl->image, "ub") == 0) + suff = DOT_UB; + else if (strcmp(mpl->image, "status") == 0) + suff = DOT_STATUS; + else if (strcmp(mpl->image, "val") == 0) + suff = DOT_VAL; + else if (strcmp(mpl->image, "dual") == 0) + suff = DOT_DUAL; + else + error(mpl, "suffix .%s invalid", mpl->image); + get_token(mpl /* suffix */); + } + /* generate pseudo-code to take value of the object */ + switch (avl_get_node_type(node)) + { case A_INDEX: + arg.index.slot = slot; + arg.index.next = slot->list; + code = make_code(mpl, O_INDEX, &arg, A_SYMBOLIC, 0); + slot->list = code; + break; + case A_SET: + arg.set.set = set; + arg.set.list = list; + code = make_code(mpl, O_MEMSET, &arg, A_ELEMSET, + set->dimen); + break; + case A_PARAMETER: + arg.par.par = par; + arg.par.list = list; + if (par->type == A_SYMBOLIC) + code = make_code(mpl, O_MEMSYM, &arg, A_SYMBOLIC, 0); + else + code = make_code(mpl, O_MEMNUM, &arg, A_NUMERIC, 0); + break; + case A_VARIABLE: + if (!mpl->flag_s && (suff == DOT_STATUS || suff == DOT_VAL + || suff == DOT_DUAL)) + error(mpl, "invalid reference to status, primal value, o" + "r dual value of variable %s above solve statement", + var->name); + arg.var.var = var; + arg.var.list = list; + arg.var.suff = suff; + code = make_code(mpl, O_MEMVAR, &arg, suff == DOT_NONE ? + A_FORMULA : A_NUMERIC, 0); + break; + case A_CONSTRAINT: + if (!mpl->flag_s && (suff == DOT_STATUS || suff == DOT_VAL + || suff == DOT_DUAL)) + error(mpl, "invalid reference to status, primal value, o" + "r dual value of %s %s above solve statement", + con->type == A_CONSTRAINT ? "constraint" : "objective" + , con->name); + arg.con.con = con; + arg.con.list = list; + arg.con.suff = suff; + code = make_code(mpl, O_MEMCON, &arg, A_NUMERIC, 0); + break; + default: + xassert(node != node); + } + return code; +} +#endif + +/*---------------------------------------------------------------------- +-- numeric_argument - parse argument passed to built-in function. +-- +-- This routine parses an argument passed to numeric built-in function +-- using the syntax: +-- +-- ::= */ + +CODE *numeric_argument(MPL *mpl, char *func) +{ CODE *x; + x = expression_5(mpl); + /* convert the argument to numeric type, if necessary */ + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + /* check that now the argument is of numeric type */ + if (x->type != A_NUMERIC) + error(mpl, "argument for %s has invalid type", func); + xassert(x->dim == 0); + return x; +} + +#if 1 /* 15/VII-2006 */ +CODE *symbolic_argument(MPL *mpl, char *func) +{ CODE *x; + x = expression_5(mpl); + /* convert the argument to symbolic type, if necessary */ + if (x->type == A_NUMERIC) + x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); + /* check that now the argument is of symbolic type */ + if (x->type != A_SYMBOLIC) + error(mpl, "argument for %s has invalid type", func); + xassert(x->dim == 0); + return x; +} +#endif + +#if 1 /* 15/VII-2006 */ +CODE *elemset_argument(MPL *mpl, char *func) +{ CODE *x; + x = expression_9(mpl); + if (x->type != A_ELEMSET) + error(mpl, "argument for %s has invalid type", func); + xassert(x->dim > 0); + return x; +} +#endif + +/*---------------------------------------------------------------------- +-- function_reference - parse reference to built-in function. +-- +-- This routine parses primary expression using the syntax: +-- +-- ::= abs ( ) +-- ::= ceil ( ) +-- ::= floor ( ) +-- ::= exp ( ) +-- ::= log ( ) +-- ::= log10 ( ) +-- ::= max ( ) +-- ::= min ( ) +-- ::= sqrt ( ) +-- ::= sin ( ) +-- ::= cos ( ) +-- ::= atan ( ) +-- ::= atan2 ( , ) +-- ::= round ( ) +-- ::= round ( , ) +-- ::= trunc ( ) +-- ::= trunc ( , ) +-- ::= Irand224 ( ) +-- ::= Uniform01 ( ) +-- ::= Uniform ( , ) +-- ::= Normal01 ( ) +-- ::= Normal ( , ) +-- ::= card ( ) +-- ::= length ( ) +-- ::= substr ( , ) +-- ::= substr ( , , ) +-- ::= str2time ( , ) +-- ::= time2str ( , ) +-- ::= gmtime ( ) +-- ::= +-- ::= , */ + +CODE *function_reference(MPL *mpl) +{ CODE *code; + OPERANDS arg; + int op; + char func[15+1]; + /* determine operation code */ + xassert(mpl->token == T_NAME); + if (strcmp(mpl->image, "abs") == 0) + op = O_ABS; + else if (strcmp(mpl->image, "ceil") == 0) + op = O_CEIL; + else if (strcmp(mpl->image, "floor") == 0) + op = O_FLOOR; + else if (strcmp(mpl->image, "exp") == 0) + op = O_EXP; + else if (strcmp(mpl->image, "log") == 0) + op = O_LOG; + else if (strcmp(mpl->image, "log10") == 0) + op = O_LOG10; + else if (strcmp(mpl->image, "sqrt") == 0) + op = O_SQRT; + else if (strcmp(mpl->image, "sin") == 0) + op = O_SIN; + else if (strcmp(mpl->image, "cos") == 0) + op = O_COS; + else if (strcmp(mpl->image, "atan") == 0) + op = O_ATAN; + else if (strcmp(mpl->image, "min") == 0) + op = O_MIN; + else if (strcmp(mpl->image, "max") == 0) + op = O_MAX; + else if (strcmp(mpl->image, "round") == 0) + op = O_ROUND; + else if (strcmp(mpl->image, "trunc") == 0) + op = O_TRUNC; + else if (strcmp(mpl->image, "Irand224") == 0) + op = O_IRAND224; + else if (strcmp(mpl->image, "Uniform01") == 0) + op = O_UNIFORM01; + else if (strcmp(mpl->image, "Uniform") == 0) + op = O_UNIFORM; + else if (strcmp(mpl->image, "Normal01") == 0) + op = O_NORMAL01; + else if (strcmp(mpl->image, "Normal") == 0) + op = O_NORMAL; + else if (strcmp(mpl->image, "card") == 0) + op = O_CARD; + else if (strcmp(mpl->image, "length") == 0) + op = O_LENGTH; + else if (strcmp(mpl->image, "substr") == 0) + op = O_SUBSTR; + else if (strcmp(mpl->image, "str2time") == 0) + op = O_STR2TIME; + else if (strcmp(mpl->image, "time2str") == 0) + op = O_TIME2STR; + else if (strcmp(mpl->image, "gmtime") == 0) + op = O_GMTIME; + else + error(mpl, "function %s unknown", mpl->image); + /* save symbolic name of the function */ + strcpy(func, mpl->image); + xassert(strlen(func) < sizeof(func)); + get_token(mpl /* */); + /* check the left parenthesis that follows the function name */ + xassert(mpl->token == T_LEFT); + get_token(mpl /* ( */); + /* parse argument list */ + if (op == O_MIN || op == O_MAX) + { /* min and max allow arbitrary number of arguments */ + arg.list = create_arg_list(mpl); + /* parse argument list */ + for (;;) + { /* parse argument and append it to the operands list */ + arg.list = expand_arg_list(mpl, arg.list, + numeric_argument(mpl, func)); + /* check a token that follows the argument */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_RIGHT) + break; + else + error(mpl, "syntax error in argument list for %s", func); + } + } + else if (op == O_IRAND224 || op == O_UNIFORM01 || op == + O_NORMAL01 || op == O_GMTIME) + { /* Irand224, Uniform01, Normal01, gmtime need no arguments */ + if (mpl->token != T_RIGHT) + error(mpl, "%s needs no arguments", func); + } + else if (op == O_UNIFORM || op == O_NORMAL) + { /* Uniform and Normal need two arguments */ + /* parse the first argument */ + arg.arg.x = numeric_argument(mpl, func); + /* check a token that follows the first argument */ + if (mpl->token == T_COMMA) + ; + else if (mpl->token == T_RIGHT) + error(mpl, "%s needs two arguments", func); + else + error(mpl, "syntax error in argument for %s", func); + get_token(mpl /* , */); + /* parse the second argument */ + arg.arg.y = numeric_argument(mpl, func); + /* check a token that follows the second argument */ + if (mpl->token == T_COMMA) + error(mpl, "%s needs two argument", func); + else if (mpl->token == T_RIGHT) + ; + else + error(mpl, "syntax error in argument for %s", func); + } + else if (op == O_ATAN || op == O_ROUND || op == O_TRUNC) + { /* atan, round, and trunc need one or two arguments */ + /* parse the first argument */ + arg.arg.x = numeric_argument(mpl, func); + /* parse the second argument, if specified */ + if (mpl->token == T_COMMA) + { switch (op) + { case O_ATAN: op = O_ATAN2; break; + case O_ROUND: op = O_ROUND2; break; + case O_TRUNC: op = O_TRUNC2; break; + default: xassert(op != op); + } + get_token(mpl /* , */); + arg.arg.y = numeric_argument(mpl, func); + } + /* check a token that follows the last argument */ + if (mpl->token == T_COMMA) + error(mpl, "%s needs one or two arguments", func); + else if (mpl->token == T_RIGHT) + ; + else + error(mpl, "syntax error in argument for %s", func); + } + else if (op == O_SUBSTR) + { /* substr needs two or three arguments */ + /* parse the first argument */ + arg.arg.x = symbolic_argument(mpl, func); + /* check a token that follows the first argument */ + if (mpl->token == T_COMMA) + ; + else if (mpl->token == T_RIGHT) + error(mpl, "%s needs two or three arguments", func); + else + error(mpl, "syntax error in argument for %s", func); + get_token(mpl /* , */); + /* parse the second argument */ + arg.arg.y = numeric_argument(mpl, func); + /* parse the third argument, if specified */ + if (mpl->token == T_COMMA) + { op = O_SUBSTR3; + get_token(mpl /* , */); + arg.arg.z = numeric_argument(mpl, func); + } + /* check a token that follows the last argument */ + if (mpl->token == T_COMMA) + error(mpl, "%s needs two or three arguments", func); + else if (mpl->token == T_RIGHT) + ; + else + error(mpl, "syntax error in argument for %s", func); + } + else if (op == O_STR2TIME) + { /* str2time needs two arguments, both symbolic */ + /* parse the first argument */ + arg.arg.x = symbolic_argument(mpl, func); + /* check a token that follows the first argument */ + if (mpl->token == T_COMMA) + ; + else if (mpl->token == T_RIGHT) + error(mpl, "%s needs two arguments", func); + else + error(mpl, "syntax error in argument for %s", func); + get_token(mpl /* , */); + /* parse the second argument */ + arg.arg.y = symbolic_argument(mpl, func); + /* check a token that follows the second argument */ + if (mpl->token == T_COMMA) + error(mpl, "%s needs two argument", func); + else if (mpl->token == T_RIGHT) + ; + else + error(mpl, "syntax error in argument for %s", func); + } + else if (op == O_TIME2STR) + { /* time2str needs two arguments, numeric and symbolic */ + /* parse the first argument */ + arg.arg.x = numeric_argument(mpl, func); + /* check a token that follows the first argument */ + if (mpl->token == T_COMMA) + ; + else if (mpl->token == T_RIGHT) + error(mpl, "%s needs two arguments", func); + else + error(mpl, "syntax error in argument for %s", func); + get_token(mpl /* , */); + /* parse the second argument */ + arg.arg.y = symbolic_argument(mpl, func); + /* check a token that follows the second argument */ + if (mpl->token == T_COMMA) + error(mpl, "%s needs two argument", func); + else if (mpl->token == T_RIGHT) + ; + else + error(mpl, "syntax error in argument for %s", func); + } + else + { /* other functions need one argument */ + if (op == O_CARD) + arg.arg.x = elemset_argument(mpl, func); + else if (op == O_LENGTH) + arg.arg.x = symbolic_argument(mpl, func); + else + arg.arg.x = numeric_argument(mpl, func); + /* check a token that follows the argument */ + if (mpl->token == T_COMMA) + error(mpl, "%s needs one argument", func); + else if (mpl->token == T_RIGHT) + ; + else + error(mpl, "syntax error in argument for %s", func); + } + /* make pseudo-code to call the built-in function */ + if (op == O_SUBSTR || op == O_SUBSTR3 || op == O_TIME2STR) + code = make_code(mpl, op, &arg, A_SYMBOLIC, 0); + else + code = make_code(mpl, op, &arg, A_NUMERIC, 0); + /* the reference ends with the right parenthesis */ + xassert(mpl->token == T_RIGHT); + get_token(mpl /* ) */); + return code; +} + +/*---------------------------------------------------------------------- +-- create_domain - create empty domain. +-- +-- This routine creates empty domain, which is initially empty, i.e. +-- has no domain blocks. */ + +DOMAIN *create_domain(MPL *mpl) +{ DOMAIN *domain; + domain = alloc(DOMAIN); + domain->list = NULL; + domain->code = NULL; + return domain; +} + +/*---------------------------------------------------------------------- +-- create_block - create empty domain block. +-- +-- This routine creates empty domain block, which is initially empty, +-- i.e. has no domain slots. */ + +DOMAIN_BLOCK *create_block(MPL *mpl) +{ DOMAIN_BLOCK *block; + block = alloc(DOMAIN_BLOCK); + block->list = NULL; + block->code = NULL; + block->backup = NULL; + block->next = NULL; + return block; +} + +/*---------------------------------------------------------------------- +-- append_block - append domain block to specified domain. +-- +-- This routine adds given domain block to the end of the block list of +-- specified domain. */ + +void append_block(MPL *mpl, DOMAIN *domain, DOMAIN_BLOCK *block) +{ DOMAIN_BLOCK *temp; + xassert(mpl == mpl); + xassert(domain != NULL); + xassert(block != NULL); + xassert(block->next == NULL); + if (domain->list == NULL) + domain->list = block; + else + { for (temp = domain->list; temp->next != NULL; temp = + temp->next); + temp->next = block; + } + return; +} + +/*---------------------------------------------------------------------- +-- append_slot - create and append new slot to domain block. +-- +-- This routine creates new domain slot and adds it to the end of slot +-- list of specified domain block. +-- +-- The parameter name is symbolic name of the dummy index associated +-- with the slot (the character string must be allocated). NULL means +-- the dummy index is not explicitly specified. +-- +-- The parameter code is pseudo-code for computing symbolic value, at +-- which the dummy index is bounded. NULL means the dummy index is free +-- in the domain scope. */ + +DOMAIN_SLOT *append_slot(MPL *mpl, DOMAIN_BLOCK *block, char *name, + CODE *code) +{ DOMAIN_SLOT *slot, *temp; + xassert(block != NULL); + slot = alloc(DOMAIN_SLOT); + slot->name = name; + slot->code = code; + slot->value = NULL; + slot->list = NULL; + slot->next = NULL; + if (block->list == NULL) + block->list = slot; + else + { for (temp = block->list; temp->next != NULL; temp = + temp->next); + temp->next = slot; + } + return slot; +} + +/*---------------------------------------------------------------------- +-- expression_list - parse expression list. +-- +-- This routine parses a list of one or more expressions enclosed into +-- the parentheses using the syntax: +-- +-- ::= ( ) +-- ::= +-- ::= , +-- +-- Note that this construction may have three different meanings: +-- +-- 1. If consists of only one expression, is a parenthesized expression, which may be of any +-- valid type (not necessarily 1-tuple). +-- +-- 2. If consists of several expressions separated by +-- commae, where no expression is undeclared symbolic name, is a n-tuple. +-- +-- 3. If consists of several expressions separated by +-- commae, where at least one expression is undeclared symbolic name +-- (that denotes a dummy index), is a slice and +-- can be only used as constituent of indexing expression. */ + +#define max_dim 20 +/* maximal number of components allowed within parentheses */ + +CODE *expression_list(MPL *mpl) +{ CODE *code; + OPERANDS arg; + struct { char *name; CODE *code; } list[1+max_dim]; + int flag_x, next_token, dim, j, slice = 0; + xassert(mpl->token == T_LEFT); + /* the flag, which allows recognizing undeclared symbolic names + as dummy indices, will be automatically reset by get_token(), + so save it before scanning the next token */ + flag_x = mpl->flag_x; + get_token(mpl /* ( */); + /* parse */ + for (dim = 1; ; dim++) + { if (dim > max_dim) + error(mpl, "too many components within parentheses"); + /* current component of can be either dummy + index or expression */ + if (mpl->token == T_NAME) + { /* symbolic name is recognized as dummy index only if: + the flag, which allows that, is set, and + the name is followed by comma or right parenthesis, and + the name is undeclared */ + get_token(mpl /* */); + next_token = mpl->token; + unget_token(mpl); + if (!(flag_x && + (next_token == T_COMMA || next_token == T_RIGHT) && + avl_find_node(mpl->tree, mpl->image) == NULL)) + { /* this is not dummy index */ + goto expr; + } + /* all dummy indices within the same slice must have unique + symbolic names */ + for (j = 1; j < dim; j++) + { if (list[j].name != NULL && strcmp(list[j].name, + mpl->image) == 0) + error(mpl, "duplicate dummy index %s not allowed", + mpl->image); + } + /* current component of is dummy index */ + list[dim].name + = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(list[dim].name, mpl->image); + list[dim].code = NULL; + get_token(mpl /* */); + /* is a slice, because at least one dummy + index has appeared */ + slice = 1; + /* note that the context ( ) is not allowed, + i.e. in this case is considered as + a parenthesized expression */ + if (dim == 1 && mpl->token == T_RIGHT) + error(mpl, "%s not defined", list[dim].name); + } + else +expr: { /* current component of is expression */ + code = expression_13(mpl); + /* if the current expression is followed by comma or it is + not the very first expression, entire + is n-tuple or slice, in which case the current expression + should be converted to symbolic type, if necessary */ + if (mpl->token == T_COMMA || dim > 1) + { if (code->type == A_NUMERIC) + code = make_unary(mpl, O_CVTSYM, code, A_SYMBOLIC, 0); + /* now the expression must be of symbolic type */ + if (code->type != A_SYMBOLIC) + error(mpl, "component expression has invalid type"); + xassert(code->dim == 0); + } + list[dim].name = NULL; + list[dim].code = code; + } + /* check a token that follows the current component */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_RIGHT) + break; + else + error(mpl, "right parenthesis missing where expected"); + } + /* generate pseudo-code for */ + if (dim == 1 && !slice) + { /* is a parenthesized expression */ + code = list[1].code; + } + else if (!slice) + { /* is a n-tuple */ + arg.list = create_arg_list(mpl); + for (j = 1; j <= dim; j++) + arg.list = expand_arg_list(mpl, arg.list, list[j].code); + code = make_code(mpl, O_TUPLE, &arg, A_TUPLE, dim); + } + else + { /* is a slice */ + arg.slice = create_block(mpl); + for (j = 1; j <= dim; j++) + append_slot(mpl, arg.slice, list[j].name, list[j].code); + /* note that actually pseudo-codes with op = O_SLICE are never + evaluated */ + code = make_code(mpl, O_SLICE, &arg, A_TUPLE, dim); + } + get_token(mpl /* ) */); + /* if is a slice, there must be the keyword + 'in', which follows the right parenthesis */ + if (slice && mpl->token != T_IN) + error(mpl, "keyword in missing where expected"); + /* if the slice flag is set and there is the keyword 'in', which + follows , the latter must be a slice */ + if (flag_x && mpl->token == T_IN && !slice) + { if (dim == 1) + error(mpl, "syntax error in indexing expression"); + else + error(mpl, "0-ary slice not allowed"); + } + return code; +} + +/*---------------------------------------------------------------------- +-- literal set - parse literal set. +-- +-- This routine parses literal set using the syntax: +-- +-- ::= { } +-- ::= +-- ::= , +-- ::= +-- +-- It is assumed that the left curly brace and the very first member +-- expression that follows it are already parsed. The right curly brace +-- remains unscanned on exit. */ + +CODE *literal_set(MPL *mpl, CODE *code) +{ OPERANDS arg; + int j; + xassert(code != NULL); + arg.list = create_arg_list(mpl); + /* parse */ + for (j = 1; ; j++) + { /* all member expressions must be n-tuples; so, if the current + expression is not n-tuple, convert it to 1-tuple */ + if (code->type == A_NUMERIC) + code = make_unary(mpl, O_CVTSYM, code, A_SYMBOLIC, 0); + if (code->type == A_SYMBOLIC) + code = make_unary(mpl, O_CVTTUP, code, A_TUPLE, 1); + /* now the expression must be n-tuple */ + if (code->type != A_TUPLE) + error(mpl, "member expression has invalid type"); + /* all member expressions must have identical dimension */ + if (arg.list != NULL && arg.list->x->dim != code->dim) + error(mpl, "member %d has %d component%s while member %d ha" + "s %d component%s", + j-1, arg.list->x->dim, arg.list->x->dim == 1 ? "" : "s", + j, code->dim, code->dim == 1 ? "" : "s"); + /* append the current expression to the member list */ + arg.list = expand_arg_list(mpl, arg.list, code); + /* check a token that follows the current expression */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_RBRACE) + break; + else + error(mpl, "syntax error in literal set"); + /* parse the next expression that follows the comma */ + code = expression_5(mpl); + } + /* generate pseudo-code for */ + code = make_code(mpl, O_MAKE, &arg, A_ELEMSET, arg.list->x->dim); + return code; +} + +/*---------------------------------------------------------------------- +-- indexing_expression - parse indexing expression. +-- +-- This routine parses indexing expression using the syntax: +-- +-- ::= +-- ::= { } +-- ::= { : } +-- ::= +-- ::= , +-- ::= +-- ::= in +-- ::= in +-- ::= +-- ::= ( ) +-- ::= +-- ::= +-- +-- This routine creates domain for , where each +-- domain block corresponds to , and each domain slot +-- corresponds to individual indexing position. */ + +DOMAIN *indexing_expression(MPL *mpl) +{ DOMAIN *domain; + DOMAIN_BLOCK *block; + DOMAIN_SLOT *slot; + CODE *code; + xassert(mpl->token == T_LBRACE); + get_token(mpl /* { */); + if (mpl->token == T_RBRACE) + error(mpl, "empty indexing expression not allowed"); + /* create domain to be constructed */ + domain = create_domain(mpl); + /* parse either or that follows the + left brace */ + for (;;) + { /* domain block for is not created yet */ + block = NULL; + /* pseudo-code for is not generated yet */ + code = NULL; + /* check a token, which begins with */ + if (mpl->token == T_NAME) + { /* it is a symbolic name */ + int next_token; + char *name; + /* symbolic name is recognized as dummy index only if it is + followed by the keyword 'in' and not declared */ + get_token(mpl /* */); + next_token = mpl->token; + unget_token(mpl); + if (!(next_token == T_IN && + avl_find_node(mpl->tree, mpl->image) == NULL)) + { /* this is not dummy index; the symbolic name begins an + expression, which is either or the + very first in */ + goto expr; + } + /* create domain block with one slot, which is assigned the + dummy index */ + block = create_block(mpl); + name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(name, mpl->image); + append_slot(mpl, block, name, NULL); + get_token(mpl /* */); + /* the keyword 'in' is already checked above */ + xassert(mpl->token == T_IN); + get_token(mpl /* in */); + /* that follows the keyword 'in' will be + parsed below */ + } + else if (mpl->token == T_LEFT) + { /* it is the left parenthesis; parse expression that begins + with this parenthesis (the flag is set in order to allow + recognizing slices; see the routine expression_list) */ + mpl->flag_x = 1; + code = expression_9(mpl); + if (code->op != O_SLICE) + { /* this is either or the very first + in */ + goto expr; + } + /* this is a slice; besides the corresponding domain block + is already created by expression_list() */ + block = code->arg.slice; + code = NULL; /* is not parsed yet */ + /* the keyword 'in' following the slice is already checked + by expression_list() */ + xassert(mpl->token == T_IN); + get_token(mpl /* in */); + /* that follows the keyword 'in' will be + parsed below */ + } +expr: /* parse expression that follows either the keyword 'in' (in + which case it can be as well as the + very first in ); note that + this expression can be already parsed above */ + if (code == NULL) code = expression_9(mpl); + /* check the type of the expression just parsed */ + if (code->type != A_ELEMSET) + { /* it is not and therefore it can only + be the very first in ; + however, then there must be no dummy index neither slice + between the left brace and this expression */ + if (block != NULL) + error(mpl, "domain expression has invalid type"); + /* parse the rest part of and make this set + be , i.e. the construction {a, b, c} + is parsed as it were written as {A}, where A = {a, b, c} + is a temporary elemental set */ + code = literal_set(mpl, code); + } + /* now pseudo-code for has been built */ + xassert(code != NULL); + xassert(code->type == A_ELEMSET); + xassert(code->dim > 0); + /* if domain block for the current is still + not created, create it for fake slice of the same dimension + as */ + if (block == NULL) + { int j; + block = create_block(mpl); + for (j = 1; j <= code->dim; j++) + append_slot(mpl, block, NULL, NULL); + } + /* number of indexing positions in must be + the same as dimension of n-tuples in basic set */ + { int dim = 0; + for (slot = block->list; slot != NULL; slot = slot->next) + dim++; + if (dim != code->dim) + error(mpl,"%d %s specified for set of dimension %d", + dim, dim == 1 ? "index" : "indices", code->dim); + } + /* store pseudo-code for in the domain block */ + xassert(block->code == NULL); + block->code = code; + /* and append the domain block to the domain */ + append_block(mpl, domain, block); + /* the current has been completely parsed; + include all its dummy indices into the symbolic name table + to make them available for referencing from expressions; + implicit declarations of dummy indices remain valid while + the corresponding domain scope is valid */ + for (slot = block->list; slot != NULL; slot = slot->next) + if (slot->name != NULL) + { AVLNODE *node; + xassert(avl_find_node(mpl->tree, slot->name) == NULL); + node = avl_insert_node(mpl->tree, slot->name); + avl_set_node_type(node, A_INDEX); + avl_set_node_link(node, (void *)slot); + } + /* check a token that follows */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_COLON || mpl->token == T_RBRACE) + break; + else + error(mpl, "syntax error in indexing expression"); + } + /* parse that follows the colon */ + if (mpl->token == T_COLON) + { get_token(mpl /* : */); + code = expression_13(mpl); + /* convert the expression to logical type, if necessary */ + if (code->type == A_SYMBOLIC) + code = make_unary(mpl, O_CVTNUM, code, A_NUMERIC, 0); + if (code->type == A_NUMERIC) + code = make_unary(mpl, O_CVTLOG, code, A_LOGICAL, 0); + /* now the expression must be of logical type */ + if (code->type != A_LOGICAL) + error(mpl, "expression following colon has invalid type"); + xassert(code->dim == 0); + domain->code = code; + /* the right brace must follow the logical expression */ + if (mpl->token != T_RBRACE) + error(mpl, "syntax error in indexing expression"); + } + get_token(mpl /* } */); + return domain; +} + +/*---------------------------------------------------------------------- +-- close_scope - close scope of indexing expression. +-- +-- The routine closes the scope of indexing expression specified by its +-- domain and thereby makes all dummy indices introduced in the indexing +-- expression no longer available for referencing. */ + +void close_scope(MPL *mpl, DOMAIN *domain) +{ DOMAIN_BLOCK *block; + DOMAIN_SLOT *slot; + AVLNODE *node; + xassert(domain != NULL); + /* remove all dummy indices from the symbolic names table */ + for (block = domain->list; block != NULL; block = block->next) + { for (slot = block->list; slot != NULL; slot = slot->next) + { if (slot->name != NULL) + { node = avl_find_node(mpl->tree, slot->name); + xassert(node != NULL); + xassert(avl_get_node_type(node) == A_INDEX); + avl_delete_node(mpl->tree, node); + } + } + } + return; +} + +/*---------------------------------------------------------------------- +-- iterated_expression - parse iterated expression. +-- +-- This routine parses primary expression using the syntax: +-- +-- ::= +-- ::= sum +-- ::= prod +-- ::= min +-- ::= max +-- ::= exists +-- +-- ::= forall +-- +-- ::= setof +-- +-- Note that parsing "integrand" depends on the iterated operator. */ + +#if 1 /* 07/IX-2008 */ +static void link_up(CODE *code) +{ /* if we have something like sum{(i+1,j,k-1) in E} x[i,j,k], + where i and k are dummy indices defined out of the iterated + expression, we should link up pseudo-code for computing i+1 + and k-1 to pseudo-code for computing the iterated expression; + this is needed to invalidate current value of the iterated + expression once i or k have been changed */ + DOMAIN_BLOCK *block; + DOMAIN_SLOT *slot; + for (block = code->arg.loop.domain->list; block != NULL; + block = block->next) + { for (slot = block->list; slot != NULL; slot = slot->next) + { if (slot->code != NULL) + { xassert(slot->code->up == NULL); + slot->code->up = code; + } + } + } + return; +} +#endif + +CODE *iterated_expression(MPL *mpl) +{ CODE *code; + OPERANDS arg; + int op; + char opstr[8]; + /* determine operation code */ + xassert(mpl->token == T_NAME); + if (strcmp(mpl->image, "sum") == 0) + op = O_SUM; + else if (strcmp(mpl->image, "prod") == 0) + op = O_PROD; + else if (strcmp(mpl->image, "min") == 0) + op = O_MINIMUM; + else if (strcmp(mpl->image, "max") == 0) + op = O_MAXIMUM; + else if (strcmp(mpl->image, "forall") == 0) + op = O_FORALL; + else if (strcmp(mpl->image, "exists") == 0) + op = O_EXISTS; + else if (strcmp(mpl->image, "setof") == 0) + op = O_SETOF; + else + error(mpl, "operator %s unknown", mpl->image); + strcpy(opstr, mpl->image); + xassert(strlen(opstr) < sizeof(opstr)); + get_token(mpl /* */); + /* check the left brace that follows the operator name */ + xassert(mpl->token == T_LBRACE); + /* parse indexing expression that controls iterating */ + arg.loop.domain = indexing_expression(mpl); + /* parse "integrand" expression and generate pseudo-code */ + switch (op) + { case O_SUM: + case O_PROD: + case O_MINIMUM: + case O_MAXIMUM: + arg.loop.x = expression_3(mpl); + /* convert the integrand to numeric type, if necessary */ + if (arg.loop.x->type == A_SYMBOLIC) + arg.loop.x = make_unary(mpl, O_CVTNUM, arg.loop.x, + A_NUMERIC, 0); + /* now the integrand must be of numeric type or linear form + (the latter is only allowed for the sum operator) */ + if (!(arg.loop.x->type == A_NUMERIC || + op == O_SUM && arg.loop.x->type == A_FORMULA)) +err: error(mpl, "integrand following %s{...} has invalid type" + , opstr); + xassert(arg.loop.x->dim == 0); + /* generate pseudo-code */ + code = make_code(mpl, op, &arg, arg.loop.x->type, 0); + break; + case O_FORALL: + case O_EXISTS: + arg.loop.x = expression_12(mpl); + /* convert the integrand to logical type, if necessary */ + if (arg.loop.x->type == A_SYMBOLIC) + arg.loop.x = make_unary(mpl, O_CVTNUM, arg.loop.x, + A_NUMERIC, 0); + if (arg.loop.x->type == A_NUMERIC) + arg.loop.x = make_unary(mpl, O_CVTLOG, arg.loop.x, + A_LOGICAL, 0); + /* now the integrand must be of logical type */ + if (arg.loop.x->type != A_LOGICAL) goto err; + xassert(arg.loop.x->dim == 0); + /* generate pseudo-code */ + code = make_code(mpl, op, &arg, A_LOGICAL, 0); + break; + case O_SETOF: + arg.loop.x = expression_5(mpl); + /* convert the integrand to 1-tuple, if necessary */ + if (arg.loop.x->type == A_NUMERIC) + arg.loop.x = make_unary(mpl, O_CVTSYM, arg.loop.x, + A_SYMBOLIC, 0); + if (arg.loop.x->type == A_SYMBOLIC) + arg.loop.x = make_unary(mpl, O_CVTTUP, arg.loop.x, + A_TUPLE, 1); + /* now the integrand must be n-tuple */ + if (arg.loop.x->type != A_TUPLE) goto err; + xassert(arg.loop.x->dim > 0); + /* generate pseudo-code */ + code = make_code(mpl, op, &arg, A_ELEMSET, arg.loop.x->dim); + break; + default: + xassert(op != op); + } + /* close the scope of the indexing expression */ + close_scope(mpl, arg.loop.domain); +#if 1 /* 07/IX-2008 */ + link_up(code); +#endif + return code; +} + +/*---------------------------------------------------------------------- +-- domain_arity - determine arity of domain. +-- +-- This routine returns arity of specified domain, which is number of +-- its free dummy indices. */ + +int domain_arity(MPL *mpl, DOMAIN *domain) +{ DOMAIN_BLOCK *block; + DOMAIN_SLOT *slot; + int arity; + xassert(mpl == mpl); + arity = 0; + for (block = domain->list; block != NULL; block = block->next) + for (slot = block->list; slot != NULL; slot = slot->next) + if (slot->code == NULL) arity++; + return arity; +} + +/*---------------------------------------------------------------------- +-- set_expression - parse set expression. +-- +-- This routine parses primary expression using the syntax: +-- +-- ::= { } +-- ::= */ + +CODE *set_expression(MPL *mpl) +{ CODE *code; + OPERANDS arg; + xassert(mpl->token == T_LBRACE); + get_token(mpl /* { */); + /* check a token that follows the left brace */ + if (mpl->token == T_RBRACE) + { /* it is the right brace, so the resultant is an empty set of + dimension 1 */ + arg.list = NULL; + /* generate pseudo-code to build the resultant set */ + code = make_code(mpl, O_MAKE, &arg, A_ELEMSET, 1); + get_token(mpl /* } */); + } + else + { /* the next token begins an indexing expression */ + unget_token(mpl); + arg.loop.domain = indexing_expression(mpl); + arg.loop.x = NULL; /* integrand is not used */ + /* close the scope of the indexing expression */ + close_scope(mpl, arg.loop.domain); + /* generate pseudo-code to build the resultant set */ + code = make_code(mpl, O_BUILD, &arg, A_ELEMSET, + domain_arity(mpl, arg.loop.domain)); +#if 1 /* 07/IX-2008 */ + link_up(code); +#endif + } + return code; +} + +/*---------------------------------------------------------------------- +-- branched_expression - parse conditional expression. +-- +-- This routine parses primary expression using the syntax: +-- +-- ::= +-- ::= if then +-- ::= if then +-- else +-- ::= */ + +CODE *branched_expression(MPL *mpl) +{ CODE *code, *x, *y, *z; + xassert(mpl->token == T_IF); + get_token(mpl /* if */); + /* parse that follows 'if' */ + x = expression_13(mpl); + /* convert the expression to logical type, if necessary */ + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type == A_NUMERIC) + x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0); + /* now the expression must be of logical type */ + if (x->type != A_LOGICAL) + error(mpl, "expression following if has invalid type"); + xassert(x->dim == 0); + /* the keyword 'then' must follow the logical expression */ + if (mpl->token != T_THEN) + error(mpl, "keyword then missing where expected"); + get_token(mpl /* then */); + /* parse that follows 'then' and check its type */ + y = expression_9(mpl); + if (!(y->type == A_NUMERIC || y->type == A_SYMBOLIC || + y->type == A_ELEMSET || y->type == A_FORMULA)) + error(mpl, "expression following then has invalid type"); + /* if the expression that follows the keyword 'then' is elemental + set, the keyword 'else' cannot be omitted; otherwise else-part + is optional */ + if (mpl->token != T_ELSE) + { if (y->type == A_ELEMSET) + error(mpl, "keyword else missing where expected"); + z = NULL; + goto skip; + } + get_token(mpl /* else */); + /* parse that follow 'else' and check its type */ + z = expression_9(mpl); + if (!(z->type == A_NUMERIC || z->type == A_SYMBOLIC || + z->type == A_ELEMSET || z->type == A_FORMULA)) + error(mpl, "expression following else has invalid type"); + /* convert to identical types, if necessary */ + if (y->type == A_FORMULA || z->type == A_FORMULA) + { if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type == A_NUMERIC) + y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0); + if (z->type == A_SYMBOLIC) + z = make_unary(mpl, O_CVTNUM, z, A_NUMERIC, 0); + if (z->type == A_NUMERIC) + z = make_unary(mpl, O_CVTLFM, z, A_FORMULA, 0); + } + if (y->type == A_SYMBOLIC || z->type == A_SYMBOLIC) + { if (y->type == A_NUMERIC) + y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0); + if (z->type == A_NUMERIC) + z = make_unary(mpl, O_CVTSYM, z, A_SYMBOLIC, 0); + } + /* now both expressions must have identical types */ + if (y->type != z->type) + error(mpl, "expressions following then and else have incompati" + "ble types"); + /* and identical dimensions */ + if (y->dim != z->dim) + error(mpl, "expressions following then and else have different" + " dimensions %d and %d, respectively", y->dim, z->dim); +skip: /* generate pseudo-code to perform branching */ + code = make_ternary(mpl, O_FORK, x, y, z, y->type, y->dim); + return code; +} + +/*---------------------------------------------------------------------- +-- primary_expression - parse primary expression. +-- +-- This routine parses primary expression using the syntax: +-- +-- ::= +-- ::= Infinity +-- ::= +-- ::= +-- ::= +-- ::= [ ] +-- ::= +-- ::= [ ] +-- ::= +-- ::= [ ] +-- ::= ( ) +-- ::= ( ) +-- ::= +-- ::= { } +-- ::= +-- ::= +-- +-- For complete list of syntactic rules for see +-- comments to the corresponding parsing routines. */ + +CODE *primary_expression(MPL *mpl) +{ CODE *code; + if (mpl->token == T_NUMBER) + { /* parse numeric literal */ + code = numeric_literal(mpl); + } +#if 1 /* 21/VII-2006 */ + else if (mpl->token == T_INFINITY) + { /* parse "infinity" */ + OPERANDS arg; + arg.num = DBL_MAX; + code = make_code(mpl, O_NUMBER, &arg, A_NUMERIC, 0); + get_token(mpl /* Infinity */); + } +#endif + else if (mpl->token == T_STRING) + { /* parse string literal */ + code = string_literal(mpl); + } + else if (mpl->token == T_NAME) + { int next_token; + get_token(mpl /* */); + next_token = mpl->token; + unget_token(mpl); + /* check a token that follows */ + switch (next_token) + { case T_LBRACKET: + /* parse reference to subscripted object */ + code = object_reference(mpl); + break; + case T_LEFT: + /* parse reference to built-in function */ + code = function_reference(mpl); + break; + case T_LBRACE: + /* parse iterated expression */ + code = iterated_expression(mpl); + break; + default: + /* parse reference to unsubscripted object */ + code = object_reference(mpl); + break; + } + } + else if (mpl->token == T_LEFT) + { /* parse parenthesized expression */ + code = expression_list(mpl); + } + else if (mpl->token == T_LBRACE) + { /* parse set expression */ + code = set_expression(mpl); + } + else if (mpl->token == T_IF) + { /* parse conditional expression */ + code = branched_expression(mpl); + } + else if (is_reserved(mpl)) + { /* other reserved keywords cannot be used here */ + error(mpl, "invalid use of reserved keyword %s", mpl->image); + } + else + error(mpl, "syntax error in expression"); + return code; +} + +/*---------------------------------------------------------------------- +-- error_preceding - raise error if preceding operand has wrong type. +-- +-- This routine is called to raise error if operand that precedes some +-- infix operator has invalid type. */ + +void error_preceding(MPL *mpl, char *opstr) +{ error(mpl, "operand preceding %s has invalid type", opstr); + /* no return */ +} + +/*---------------------------------------------------------------------- +-- error_following - raise error if following operand has wrong type. +-- +-- This routine is called to raise error if operand that follows some +-- infix operator has invalid type. */ + +void error_following(MPL *mpl, char *opstr) +{ error(mpl, "operand following %s has invalid type", opstr); + /* no return */ +} + +/*---------------------------------------------------------------------- +-- error_dimension - raise error if operands have different dimension. +-- +-- This routine is called to raise error if two operands of some infix +-- operator have different dimension. */ + +void error_dimension(MPL *mpl, char *opstr, int dim1, int dim2) +{ error(mpl, "operands preceding and following %s have different di" + "mensions %d and %d, respectively", opstr, dim1, dim2); + /* no return */ +} + +/*---------------------------------------------------------------------- +-- expression_0 - parse expression of level 0. +-- +-- This routine parses expression of level 0 using the syntax: +-- +-- ::= */ + +CODE *expression_0(MPL *mpl) +{ CODE *code; + code = primary_expression(mpl); + return code; +} + +/*---------------------------------------------------------------------- +-- expression_1 - parse expression of level 1. +-- +-- This routine parses expression of level 1 using the syntax: +-- +-- ::= +-- ::= +-- ::= +-- ::= ^ | ** */ + +CODE *expression_1(MPL *mpl) +{ CODE *x, *y; + char opstr[8]; + x = expression_0(mpl); + if (mpl->token == T_POWER) + { strcpy(opstr, mpl->image); + xassert(strlen(opstr) < sizeof(opstr)); + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type != A_NUMERIC) + error_preceding(mpl, opstr); + get_token(mpl /* ^ | ** */); + if (mpl->token == T_PLUS || mpl->token == T_MINUS) + y = expression_2(mpl); + else + y = expression_1(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type != A_NUMERIC) + error_following(mpl, opstr); + x = make_binary(mpl, O_POWER, x, y, A_NUMERIC, 0); + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_2 - parse expression of level 2. +-- +-- This routine parses expression of level 2 using the syntax: +-- +-- ::= +-- ::= + +-- ::= - */ + +CODE *expression_2(MPL *mpl) +{ CODE *x; + if (mpl->token == T_PLUS) + { get_token(mpl /* + */); + x = expression_1(mpl); + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) + error_following(mpl, "+"); + x = make_unary(mpl, O_PLUS, x, x->type, 0); + } + else if (mpl->token == T_MINUS) + { get_token(mpl /* - */); + x = expression_1(mpl); + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) + error_following(mpl, "-"); + x = make_unary(mpl, O_MINUS, x, x->type, 0); + } + else + x = expression_1(mpl); + return x; +} + +/*---------------------------------------------------------------------- +-- expression_3 - parse expression of level 3. +-- +-- This routine parses expression of level 3 using the syntax: +-- +-- ::= +-- ::= * +-- ::= / +-- ::= div +-- ::= mod */ + +CODE *expression_3(MPL *mpl) +{ CODE *x, *y; + x = expression_2(mpl); + for (;;) + { if (mpl->token == T_ASTERISK) + { if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) + error_preceding(mpl, "*"); + get_token(mpl /* * */); + y = expression_2(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (!(y->type == A_NUMERIC || y->type == A_FORMULA)) + error_following(mpl, "*"); + if (x->type == A_FORMULA && y->type == A_FORMULA) + error(mpl, "multiplication of linear forms not allowed"); + if (x->type == A_NUMERIC && y->type == A_NUMERIC) + x = make_binary(mpl, O_MUL, x, y, A_NUMERIC, 0); + else + x = make_binary(mpl, O_MUL, x, y, A_FORMULA, 0); + } + else if (mpl->token == T_SLASH) + { if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) + error_preceding(mpl, "/"); + get_token(mpl /* / */); + y = expression_2(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type != A_NUMERIC) + error_following(mpl, "/"); + if (x->type == A_NUMERIC) + x = make_binary(mpl, O_DIV, x, y, A_NUMERIC, 0); + else + x = make_binary(mpl, O_DIV, x, y, A_FORMULA, 0); + } + else if (mpl->token == T_DIV) + { if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type != A_NUMERIC) + error_preceding(mpl, "div"); + get_token(mpl /* div */); + y = expression_2(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type != A_NUMERIC) + error_following(mpl, "div"); + x = make_binary(mpl, O_IDIV, x, y, A_NUMERIC, 0); + } + else if (mpl->token == T_MOD) + { if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type != A_NUMERIC) + error_preceding(mpl, "mod"); + get_token(mpl /* mod */); + y = expression_2(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type != A_NUMERIC) + error_following(mpl, "mod"); + x = make_binary(mpl, O_MOD, x, y, A_NUMERIC, 0); + } + else + break; + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_4 - parse expression of level 4. +-- +-- This routine parses expression of level 4 using the syntax: +-- +-- ::= +-- ::= + +-- ::= - +-- ::= less */ + +CODE *expression_4(MPL *mpl) +{ CODE *x, *y; + x = expression_3(mpl); + for (;;) + { if (mpl->token == T_PLUS) + { if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) + error_preceding(mpl, "+"); + get_token(mpl /* + */); + y = expression_3(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (!(y->type == A_NUMERIC || y->type == A_FORMULA)) + error_following(mpl, "+"); + if (x->type == A_NUMERIC && y->type == A_FORMULA) + x = make_unary(mpl, O_CVTLFM, x, A_FORMULA, 0); + if (x->type == A_FORMULA && y->type == A_NUMERIC) + y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0); + x = make_binary(mpl, O_ADD, x, y, x->type, 0); + } + else if (mpl->token == T_MINUS) + { if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) + error_preceding(mpl, "-"); + get_token(mpl /* - */); + y = expression_3(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (!(y->type == A_NUMERIC || y->type == A_FORMULA)) + error_following(mpl, "-"); + if (x->type == A_NUMERIC && y->type == A_FORMULA) + x = make_unary(mpl, O_CVTLFM, x, A_FORMULA, 0); + if (x->type == A_FORMULA && y->type == A_NUMERIC) + y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0); + x = make_binary(mpl, O_SUB, x, y, x->type, 0); + } + else if (mpl->token == T_LESS) + { if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type != A_NUMERIC) + error_preceding(mpl, "less"); + get_token(mpl /* less */); + y = expression_3(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type != A_NUMERIC) + error_following(mpl, "less"); + x = make_binary(mpl, O_LESS, x, y, A_NUMERIC, 0); + } + else + break; + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_5 - parse expression of level 5. +-- +-- This routine parses expression of level 5 using the syntax: +-- +-- ::= +-- ::= & */ + +CODE *expression_5(MPL *mpl) +{ CODE *x, *y; + x = expression_4(mpl); + for (;;) + { if (mpl->token == T_CONCAT) + { if (x->type == A_NUMERIC) + x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); + if (x->type != A_SYMBOLIC) + error_preceding(mpl, "&"); + get_token(mpl /* & */); + y = expression_4(mpl); + if (y->type == A_NUMERIC) + y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0); + if (y->type != A_SYMBOLIC) + error_following(mpl, "&"); + x = make_binary(mpl, O_CONCAT, x, y, A_SYMBOLIC, 0); + } + else + break; + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_6 - parse expression of level 6. +-- +-- This routine parses expression of level 6 using the syntax: +-- +-- ::= +-- ::= .. +-- ::= .. by +-- */ + +CODE *expression_6(MPL *mpl) +{ CODE *x, *y, *z; + x = expression_5(mpl); + if (mpl->token == T_DOTS) + { if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type != A_NUMERIC) + error_preceding(mpl, ".."); + get_token(mpl /* .. */); + y = expression_5(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type != A_NUMERIC) + error_following(mpl, ".."); + if (mpl->token == T_BY) + { get_token(mpl /* by */); + z = expression_5(mpl); + if (z->type == A_SYMBOLIC) + z = make_unary(mpl, O_CVTNUM, z, A_NUMERIC, 0); + if (z->type != A_NUMERIC) + error_following(mpl, "by"); + } + else + z = NULL; + x = make_ternary(mpl, O_DOTS, x, y, z, A_ELEMSET, 1); + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_7 - parse expression of level 7. +-- +-- This routine parses expression of level 7 using the syntax: +-- +-- ::= +-- ::= cross */ + +CODE *expression_7(MPL *mpl) +{ CODE *x, *y; + x = expression_6(mpl); + for (;;) + { if (mpl->token == T_CROSS) + { if (x->type != A_ELEMSET) + error_preceding(mpl, "cross"); + get_token(mpl /* cross */); + y = expression_6(mpl); + if (y->type != A_ELEMSET) + error_following(mpl, "cross"); + x = make_binary(mpl, O_CROSS, x, y, A_ELEMSET, + x->dim + y->dim); + } + else + break; + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_8 - parse expression of level 8. +-- +-- This routine parses expression of level 8 using the syntax: +-- +-- ::= +-- ::= inter */ + +CODE *expression_8(MPL *mpl) +{ CODE *x, *y; + x = expression_7(mpl); + for (;;) + { if (mpl->token == T_INTER) + { if (x->type != A_ELEMSET) + error_preceding(mpl, "inter"); + get_token(mpl /* inter */); + y = expression_7(mpl); + if (y->type != A_ELEMSET) + error_following(mpl, "inter"); + if (x->dim != y->dim) + error_dimension(mpl, "inter", x->dim, y->dim); + x = make_binary(mpl, O_INTER, x, y, A_ELEMSET, x->dim); + } + else + break; + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_9 - parse expression of level 9. +-- +-- This routine parses expression of level 9 using the syntax: +-- +-- ::= +-- ::= union +-- ::= diff +-- ::= symdiff */ + +CODE *expression_9(MPL *mpl) +{ CODE *x, *y; + x = expression_8(mpl); + for (;;) + { if (mpl->token == T_UNION) + { if (x->type != A_ELEMSET) + error_preceding(mpl, "union"); + get_token(mpl /* union */); + y = expression_8(mpl); + if (y->type != A_ELEMSET) + error_following(mpl, "union"); + if (x->dim != y->dim) + error_dimension(mpl, "union", x->dim, y->dim); + x = make_binary(mpl, O_UNION, x, y, A_ELEMSET, x->dim); + } + else if (mpl->token == T_DIFF) + { if (x->type != A_ELEMSET) + error_preceding(mpl, "diff"); + get_token(mpl /* diff */); + y = expression_8(mpl); + if (y->type != A_ELEMSET) + error_following(mpl, "diff"); + if (x->dim != y->dim) + error_dimension(mpl, "diff", x->dim, y->dim); + x = make_binary(mpl, O_DIFF, x, y, A_ELEMSET, x->dim); + } + else if (mpl->token == T_SYMDIFF) + { if (x->type != A_ELEMSET) + error_preceding(mpl, "symdiff"); + get_token(mpl /* symdiff */); + y = expression_8(mpl); + if (y->type != A_ELEMSET) + error_following(mpl, "symdiff"); + if (x->dim != y->dim) + error_dimension(mpl, "symdiff", x->dim, y->dim); + x = make_binary(mpl, O_SYMDIFF, x, y, A_ELEMSET, x->dim); + } + else + break; + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_10 - parse expression of level 10. +-- +-- This routine parses expression of level 10 using the syntax: +-- +-- ::= +-- ::= +-- ::= < | <= | = | == | >= | > | <> | != | in | not in | ! in | +-- within | not within | ! within */ + +CODE *expression_10(MPL *mpl) +{ CODE *x, *y; + int op = -1; + char opstr[16]; + x = expression_9(mpl); + strcpy(opstr, ""); + switch (mpl->token) + { case T_LT: + op = O_LT; break; + case T_LE: + op = O_LE; break; + case T_EQ: + op = O_EQ; break; + case T_GE: + op = O_GE; break; + case T_GT: + op = O_GT; break; + case T_NE: + op = O_NE; break; + case T_IN: + op = O_IN; break; + case T_WITHIN: + op = O_WITHIN; break; + case T_NOT: + strcpy(opstr, mpl->image); + get_token(mpl /* not | ! */); + if (mpl->token == T_IN) + op = O_NOTIN; + else if (mpl->token == T_WITHIN) + op = O_NOTWITHIN; + else + error(mpl, "invalid use of %s", opstr); + strcat(opstr, " "); + break; + default: + goto done; + } + strcat(opstr, mpl->image); + xassert(strlen(opstr) < sizeof(opstr)); + switch (op) + { case O_EQ: + case O_NE: +#if 1 /* 02/VIII-2008 */ + case O_LT: + case O_LE: + case O_GT: + case O_GE: +#endif + if (!(x->type == A_NUMERIC || x->type == A_SYMBOLIC)) + error_preceding(mpl, opstr); + get_token(mpl /* */); + y = expression_9(mpl); + if (!(y->type == A_NUMERIC || y->type == A_SYMBOLIC)) + error_following(mpl, opstr); + if (x->type == A_NUMERIC && y->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); + if (x->type == A_SYMBOLIC && y->type == A_NUMERIC) + y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0); + x = make_binary(mpl, op, x, y, A_LOGICAL, 0); + break; +#if 0 /* 02/VIII-2008 */ + case O_LT: + case O_LE: + case O_GT: + case O_GE: + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type != A_NUMERIC) + error_preceding(mpl, opstr); + get_token(mpl /* */); + y = expression_9(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type != A_NUMERIC) + error_following(mpl, opstr); + x = make_binary(mpl, op, x, y, A_LOGICAL, 0); + break; +#endif + case O_IN: + case O_NOTIN: + if (x->type == A_NUMERIC) + x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTTUP, x, A_TUPLE, 1); + if (x->type != A_TUPLE) + error_preceding(mpl, opstr); + get_token(mpl /* */); + y = expression_9(mpl); + if (y->type != A_ELEMSET) + error_following(mpl, opstr); + if (x->dim != y->dim) + error_dimension(mpl, opstr, x->dim, y->dim); + x = make_binary(mpl, op, x, y, A_LOGICAL, 0); + break; + case O_WITHIN: + case O_NOTWITHIN: + if (x->type != A_ELEMSET) + error_preceding(mpl, opstr); + get_token(mpl /* */); + y = expression_9(mpl); + if (y->type != A_ELEMSET) + error_following(mpl, opstr); + if (x->dim != y->dim) + error_dimension(mpl, opstr, x->dim, y->dim); + x = make_binary(mpl, op, x, y, A_LOGICAL, 0); + break; + default: + xassert(op != op); + } +done: return x; +} + +/*---------------------------------------------------------------------- +-- expression_11 - parse expression of level 11. +-- +-- This routine parses expression of level 11 using the syntax: +-- +-- ::= +-- ::= not +-- ::= ! */ + +CODE *expression_11(MPL *mpl) +{ CODE *x; + char opstr[8]; + if (mpl->token == T_NOT) + { strcpy(opstr, mpl->image); + xassert(strlen(opstr) < sizeof(opstr)); + get_token(mpl /* not | ! */); + x = expression_10(mpl); + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type == A_NUMERIC) + x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0); + if (x->type != A_LOGICAL) + error_following(mpl, opstr); + x = make_unary(mpl, O_NOT, x, A_LOGICAL, 0); + } + else + x = expression_10(mpl); + return x; +} + +/*---------------------------------------------------------------------- +-- expression_12 - parse expression of level 12. +-- +-- This routine parses expression of level 12 using the syntax: +-- +-- ::= +-- ::= and +-- ::= && */ + +CODE *expression_12(MPL *mpl) +{ CODE *x, *y; + char opstr[8]; + x = expression_11(mpl); + for (;;) + { if (mpl->token == T_AND) + { strcpy(opstr, mpl->image); + xassert(strlen(opstr) < sizeof(opstr)); + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type == A_NUMERIC) + x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0); + if (x->type != A_LOGICAL) + error_preceding(mpl, opstr); + get_token(mpl /* and | && */); + y = expression_11(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type == A_NUMERIC) + y = make_unary(mpl, O_CVTLOG, y, A_LOGICAL, 0); + if (y->type != A_LOGICAL) + error_following(mpl, opstr); + x = make_binary(mpl, O_AND, x, y, A_LOGICAL, 0); + } + else + break; + } + return x; +} + +/*---------------------------------------------------------------------- +-- expression_13 - parse expression of level 13. +-- +-- This routine parses expression of level 13 using the syntax: +-- +-- ::= +-- ::= or +-- ::= || */ + +CODE *expression_13(MPL *mpl) +{ CODE *x, *y; + char opstr[8]; + x = expression_12(mpl); + for (;;) + { if (mpl->token == T_OR) + { strcpy(opstr, mpl->image); + xassert(strlen(opstr) < sizeof(opstr)); + if (x->type == A_SYMBOLIC) + x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); + if (x->type == A_NUMERIC) + x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0); + if (x->type != A_LOGICAL) + error_preceding(mpl, opstr); + get_token(mpl /* or | || */); + y = expression_12(mpl); + if (y->type == A_SYMBOLIC) + y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); + if (y->type == A_NUMERIC) + y = make_unary(mpl, O_CVTLOG, y, A_LOGICAL, 0); + if (y->type != A_LOGICAL) + error_following(mpl, opstr); + x = make_binary(mpl, O_OR, x, y, A_LOGICAL, 0); + } + else + break; + } + return x; +} + +/*---------------------------------------------------------------------- +-- set_statement - parse set statement. +-- +-- This routine parses set statement using the syntax: +-- +-- ::= set +-- ; +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= , dimen +-- ::= , within +-- ::= , := +-- ::= , default +-- +-- Commae in are optional and may be omitted anywhere. */ + +SET *set_statement(MPL *mpl) +{ SET *set; + int dimen_used = 0; + xassert(is_keyword(mpl, "set")); + get_token(mpl /* set */); + /* symbolic name must follow the keyword 'set' */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "symbolic name missing where expected"); + /* there must be no other object with the same name */ + if (avl_find_node(mpl->tree, mpl->image) != NULL) + error(mpl, "%s multiply declared", mpl->image); + /* create model set */ + set = alloc(SET); + set->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(set->name, mpl->image); + set->alias = NULL; + set->dim = 0; + set->domain = NULL; + set->dimen = 0; + set->within = NULL; + set->assign = NULL; + set->option = NULL; + set->gadget = NULL; + set->data = 0; + set->array = NULL; + get_token(mpl /* */); + /* parse optional alias */ + if (mpl->token == T_STRING) + { set->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(set->alias, mpl->image); + get_token(mpl /* */); + } + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + { set->domain = indexing_expression(mpl); + set->dim = domain_arity(mpl, set->domain); + } + /* include the set name in the symbolic names table */ + { AVLNODE *node; + node = avl_insert_node(mpl->tree, set->name); + avl_set_node_type(node, A_SET); + avl_set_node_link(node, (void *)set); + } + /* parse the list of optional attributes */ + for (;;) + { if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_SEMICOLON) + break; + if (is_keyword(mpl, "dimen")) + { /* dimension of set members */ + int dimen; + get_token(mpl /* dimen */); + if (!(mpl->token == T_NUMBER && + 1.0 <= mpl->value && mpl->value <= 20.0 && + floor(mpl->value) == mpl->value)) + error(mpl, "dimension must be integer between 1 and 20"); + dimen = (int)(mpl->value + 0.5); + if (dimen_used) + error(mpl, "at most one dimension attribute allowed"); + if (set->dimen > 0) + error(mpl, "dimension %d conflicts with dimension %d alr" + "eady determined", dimen, set->dimen); + set->dimen = dimen; + dimen_used = 1; + get_token(mpl /* */); + } + else if (mpl->token == T_WITHIN || mpl->token == T_IN) + { /* restricting superset */ + WITHIN *within, *temp; + if (mpl->token == T_IN && !mpl->as_within) + { warning(mpl, "keyword in understood as within"); + mpl->as_within = 1; + } + get_token(mpl /* within */); + /* create new restricting superset list entry and append it + to the within-list */ + within = alloc(WITHIN); + within->code = NULL; + within->next = NULL; + if (set->within == NULL) + set->within = within; + else + { for (temp = set->within; temp->next != NULL; temp = + temp->next); + temp->next = within; + } + /* parse an expression that follows 'within' */ + within->code = expression_9(mpl); + if (within->code->type != A_ELEMSET) + error(mpl, "expression following within has invalid type" + ); + xassert(within->code->dim > 0); + /* check/set dimension of set members */ + if (set->dimen == 0) set->dimen = within->code->dim; + if (set->dimen != within->code->dim) + error(mpl, "set expression following within must have di" + "mension %d rather than %d", + set->dimen, within->code->dim); + } + else if (mpl->token == T_ASSIGN) + { /* assignment expression */ + if (!(set->assign == NULL && set->option == NULL && + set->gadget == NULL)) +err: error(mpl, "at most one := or default/data allowed"); + get_token(mpl /* := */); + /* parse an expression that follows ':=' */ + set->assign = expression_9(mpl); + if (set->assign->type != A_ELEMSET) + error(mpl, "expression following := has invalid type"); + xassert(set->assign->dim > 0); + /* check/set dimension of set members */ + if (set->dimen == 0) set->dimen = set->assign->dim; + if (set->dimen != set->assign->dim) + error(mpl, "set expression following := must have dimens" + "ion %d rather than %d", + set->dimen, set->assign->dim); + } + else if (is_keyword(mpl, "default")) + { /* expression for default value */ + if (!(set->assign == NULL && set->option == NULL)) goto err; + get_token(mpl /* := */); + /* parse an expression that follows 'default' */ + set->option = expression_9(mpl); + if (set->option->type != A_ELEMSET) + error(mpl, "expression following default has invalid typ" + "e"); + xassert(set->option->dim > 0); + /* check/set dimension of set members */ + if (set->dimen == 0) set->dimen = set->option->dim; + if (set->dimen != set->option->dim) + error(mpl, "set expression following default must have d" + "imension %d rather than %d", + set->dimen, set->option->dim); + } +#if 1 /* 12/XII-2008 */ + else if (is_keyword(mpl, "data")) + { /* gadget to initialize the set by data from plain set */ + GADGET *gadget; + AVLNODE *node; + int i, k, fff[20]; + if (!(set->assign == NULL && set->gadget == NULL)) goto err; + get_token(mpl /* data */); + set->gadget = gadget = alloc(GADGET); + /* set name must follow the keyword 'data' */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", + mpl->image); + else + error(mpl, "set name missing where expected"); + /* find the set in the symbolic name table */ + node = avl_find_node(mpl->tree, mpl->image); + if (node == NULL) + error(mpl, "%s not defined", mpl->image); + if (avl_get_node_type(node) != A_SET) +err1: error(mpl, "%s not a plain set", mpl->image); + gadget->set = avl_get_node_link(node); + if (gadget->set->dim != 0) goto err1; + if (gadget->set == set) + error(mpl, "set cannot be initialized by itself"); + /* check and set dimensions */ + if (set->dim >= gadget->set->dimen) +err2: error(mpl, "dimension of %s too small", mpl->image); + if (set->dimen == 0) + set->dimen = gadget->set->dimen - set->dim; + if (set->dim + set->dimen > gadget->set->dimen) + goto err2; + else if (set->dim + set->dimen < gadget->set->dimen) + error(mpl, "dimension of %s too big", mpl->image); + get_token(mpl /* set name */); + /* left parenthesis must follow the set name */ + if (mpl->token == T_LEFT) + get_token(mpl /* ( */); + else + error(mpl, "left parenthesis missing where expected"); + /* parse permutation of component numbers */ + for (k = 0; k < gadget->set->dimen; k++) fff[k] = 0; + k = 0; + for (;;) + { if (mpl->token != T_NUMBER) + error(mpl, "component number missing where expected"); + if (str2int(mpl->image, &i) != 0) +err3: error(mpl, "component number must be integer between " + "1 and %d", gadget->set->dimen); + if (!(1 <= i && i <= gadget->set->dimen)) goto err3; + if (fff[i-1] != 0) + error(mpl, "component %d multiply specified", i); + gadget->ind[k++] = i, fff[i-1] = 1; + xassert(k <= gadget->set->dimen); + get_token(mpl /* number */); + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_RIGHT) + break; + else + error(mpl, "syntax error in data attribute"); + } + if (k < gadget->set->dimen) + error(mpl, "there are must be %d components rather than " + "%d", gadget->set->dimen, k); + get_token(mpl /* ) */); + } +#endif + else + error(mpl, "syntax error in set statement"); + } + /* close the domain scope */ + if (set->domain != NULL) close_scope(mpl, set->domain); + /* if dimension of set members is still unknown, set it to 1 */ + if (set->dimen == 0) set->dimen = 1; + /* the set statement has been completely parsed */ + xassert(mpl->token == T_SEMICOLON); + get_token(mpl /* ; */); + return set; +} + +/*---------------------------------------------------------------------- +-- parameter_statement - parse parameter statement. +-- +-- This routine parses parameter statement using the syntax: +-- +-- ::= param +-- ; +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= , integer +-- ::= , binary +-- ::= , symbolic +-- ::= , +-- ::= , in +-- ::= , := +-- ::= , default +-- ::= < | <= | = | == | >= | > | <> | != +-- +-- Commae in are optional and may be omitted anywhere. */ + +PARAMETER *parameter_statement(MPL *mpl) +{ PARAMETER *par; + int integer_used = 0, binary_used = 0, symbolic_used = 0; + xassert(is_keyword(mpl, "param")); + get_token(mpl /* param */); + /* symbolic name must follow the keyword 'param' */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "symbolic name missing where expected"); + /* there must be no other object with the same name */ + if (avl_find_node(mpl->tree, mpl->image) != NULL) + error(mpl, "%s multiply declared", mpl->image); + /* create model parameter */ + par = alloc(PARAMETER); + par->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(par->name, mpl->image); + par->alias = NULL; + par->dim = 0; + par->domain = NULL; + par->type = A_NUMERIC; + par->cond = NULL; + par->in = NULL; + par->assign = NULL; + par->option = NULL; + par->data = 0; + par->defval = NULL; + par->array = NULL; + get_token(mpl /* */); + /* parse optional alias */ + if (mpl->token == T_STRING) + { par->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(par->alias, mpl->image); + get_token(mpl /* */); + } + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + { par->domain = indexing_expression(mpl); + par->dim = domain_arity(mpl, par->domain); + } + /* include the parameter name in the symbolic names table */ + { AVLNODE *node; + node = avl_insert_node(mpl->tree, par->name); + avl_set_node_type(node, A_PARAMETER); + avl_set_node_link(node, (void *)par); + } + /* parse the list of optional attributes */ + for (;;) + { if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_SEMICOLON) + break; + if (is_keyword(mpl, "integer")) + { if (integer_used) + error(mpl, "at most one integer allowed"); + if (par->type == A_SYMBOLIC) + error(mpl, "symbolic parameter cannot be integer"); + if (par->type != A_BINARY) par->type = A_INTEGER; + integer_used = 1; + get_token(mpl /* integer */); + } + else if (is_keyword(mpl, "binary")) +bin: { if (binary_used) + error(mpl, "at most one binary allowed"); + if (par->type == A_SYMBOLIC) + error(mpl, "symbolic parameter cannot be binary"); + par->type = A_BINARY; + binary_used = 1; + get_token(mpl /* binary */); + } + else if (is_keyword(mpl, "logical")) + { if (!mpl->as_binary) + { warning(mpl, "keyword logical understood as binary"); + mpl->as_binary = 1; + } + goto bin; + } + else if (is_keyword(mpl, "symbolic")) + { if (symbolic_used) + error(mpl, "at most one symbolic allowed"); + if (par->type != A_NUMERIC) + error(mpl, "integer or binary parameter cannot be symbol" + "ic"); + /* the parameter may be referenced from expressions given + in the same parameter declaration, so its type must be + completed before parsing that expressions */ + if (!(par->cond == NULL && par->in == NULL && + par->assign == NULL && par->option == NULL)) + error(mpl, "keyword symbolic must precede any other para" + "meter attributes"); + par->type = A_SYMBOLIC; + symbolic_used = 1; + get_token(mpl /* symbolic */); + } + else if (mpl->token == T_LT || mpl->token == T_LE || + mpl->token == T_EQ || mpl->token == T_GE || + mpl->token == T_GT || mpl->token == T_NE) + { /* restricting condition */ + CONDITION *cond, *temp; + char opstr[8]; + /* create new restricting condition list entry and append + it to the conditions list */ + cond = alloc(CONDITION); + switch (mpl->token) + { case T_LT: + cond->rho = O_LT, strcpy(opstr, mpl->image); break; + case T_LE: + cond->rho = O_LE, strcpy(opstr, mpl->image); break; + case T_EQ: + cond->rho = O_EQ, strcpy(opstr, mpl->image); break; + case T_GE: + cond->rho = O_GE, strcpy(opstr, mpl->image); break; + case T_GT: + cond->rho = O_GT, strcpy(opstr, mpl->image); break; + case T_NE: + cond->rho = O_NE, strcpy(opstr, mpl->image); break; + default: + xassert(mpl->token != mpl->token); + } + xassert(strlen(opstr) < sizeof(opstr)); + cond->code = NULL; + cond->next = NULL; + if (par->cond == NULL) + par->cond = cond; + else + { for (temp = par->cond; temp->next != NULL; temp = + temp->next); + temp->next = cond; + } +#if 0 /* 13/VIII-2008 */ + if (par->type == A_SYMBOLIC && + !(cond->rho == O_EQ || cond->rho == O_NE)) + error(mpl, "inequality restriction not allowed"); +#endif + get_token(mpl /* rho */); + /* parse an expression that follows relational operator */ + cond->code = expression_5(mpl); + if (!(cond->code->type == A_NUMERIC || + cond->code->type == A_SYMBOLIC)) + error(mpl, "expression following %s has invalid type", + opstr); + xassert(cond->code->dim == 0); + /* convert to the parameter type, if necessary */ + if (par->type != A_SYMBOLIC && cond->code->type == + A_SYMBOLIC) + cond->code = make_unary(mpl, O_CVTNUM, cond->code, + A_NUMERIC, 0); + if (par->type == A_SYMBOLIC && cond->code->type != + A_SYMBOLIC) + cond->code = make_unary(mpl, O_CVTSYM, cond->code, + A_SYMBOLIC, 0); + } + else if (mpl->token == T_IN || mpl->token == T_WITHIN) + { /* restricting superset */ + WITHIN *in, *temp; + if (mpl->token == T_WITHIN && !mpl->as_in) + { warning(mpl, "keyword within understood as in"); + mpl->as_in = 1; + } + get_token(mpl /* in */); + /* create new restricting superset list entry and append it + to the in-list */ + in = alloc(WITHIN); + in->code = NULL; + in->next = NULL; + if (par->in == NULL) + par->in = in; + else + { for (temp = par->in; temp->next != NULL; temp = + temp->next); + temp->next = in; + } + /* parse an expression that follows 'in' */ + in->code = expression_9(mpl); + if (in->code->type != A_ELEMSET) + error(mpl, "expression following in has invalid type"); + xassert(in->code->dim > 0); + if (in->code->dim != 1) + error(mpl, "set expression following in must have dimens" + "ion 1 rather than %d", in->code->dim); + } + else if (mpl->token == T_ASSIGN) + { /* assignment expression */ + if (!(par->assign == NULL && par->option == NULL)) +err: error(mpl, "at most one := or default allowed"); + get_token(mpl /* := */); + /* parse an expression that follows ':=' */ + par->assign = expression_5(mpl); + /* the expression must be of numeric/symbolic type */ + if (!(par->assign->type == A_NUMERIC || + par->assign->type == A_SYMBOLIC)) + error(mpl, "expression following := has invalid type"); + xassert(par->assign->dim == 0); + /* convert to the parameter type, if necessary */ + if (par->type != A_SYMBOLIC && par->assign->type == + A_SYMBOLIC) + par->assign = make_unary(mpl, O_CVTNUM, par->assign, + A_NUMERIC, 0); + if (par->type == A_SYMBOLIC && par->assign->type != + A_SYMBOLIC) + par->assign = make_unary(mpl, O_CVTSYM, par->assign, + A_SYMBOLIC, 0); + } + else if (is_keyword(mpl, "default")) + { /* expression for default value */ + if (!(par->assign == NULL && par->option == NULL)) goto err; + get_token(mpl /* default */); + /* parse an expression that follows 'default' */ + par->option = expression_5(mpl); + if (!(par->option->type == A_NUMERIC || + par->option->type == A_SYMBOLIC)) + error(mpl, "expression following default has invalid typ" + "e"); + xassert(par->option->dim == 0); + /* convert to the parameter type, if necessary */ + if (par->type != A_SYMBOLIC && par->option->type == + A_SYMBOLIC) + par->option = make_unary(mpl, O_CVTNUM, par->option, + A_NUMERIC, 0); + if (par->type == A_SYMBOLIC && par->option->type != + A_SYMBOLIC) + par->option = make_unary(mpl, O_CVTSYM, par->option, + A_SYMBOLIC, 0); + } + else + error(mpl, "syntax error in parameter statement"); + } + /* close the domain scope */ + if (par->domain != NULL) close_scope(mpl, par->domain); + /* the parameter statement has been completely parsed */ + xassert(mpl->token == T_SEMICOLON); + get_token(mpl /* ; */); + return par; +} + +/*---------------------------------------------------------------------- +-- variable_statement - parse variable statement. +-- +-- This routine parses variable statement using the syntax: +-- +-- ::= var +-- ; +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= , integer +-- ::= , binary +-- ::= , +-- ::= >= | <= | = | == +-- +-- Commae in are optional and may be omitted anywhere. */ + +VARIABLE *variable_statement(MPL *mpl) +{ VARIABLE *var; + int integer_used = 0, binary_used = 0; + xassert(is_keyword(mpl, "var")); + if (mpl->flag_s) + error(mpl, "variable statement must precede solve statement"); + get_token(mpl /* var */); + /* symbolic name must follow the keyword 'var' */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "symbolic name missing where expected"); + /* there must be no other object with the same name */ + if (avl_find_node(mpl->tree, mpl->image) != NULL) + error(mpl, "%s multiply declared", mpl->image); + /* create model variable */ + var = alloc(VARIABLE); + var->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(var->name, mpl->image); + var->alias = NULL; + var->dim = 0; + var->domain = NULL; + var->type = A_NUMERIC; + var->lbnd = NULL; + var->ubnd = NULL; + var->array = NULL; + get_token(mpl /* */); + /* parse optional alias */ + if (mpl->token == T_STRING) + { var->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(var->alias, mpl->image); + get_token(mpl /* */); + } + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + { var->domain = indexing_expression(mpl); + var->dim = domain_arity(mpl, var->domain); + } + /* include the variable name in the symbolic names table */ + { AVLNODE *node; + node = avl_insert_node(mpl->tree, var->name); + avl_set_node_type(node, A_VARIABLE); + avl_set_node_link(node, (void *)var); + } + /* parse the list of optional attributes */ + for (;;) + { if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_SEMICOLON) + break; + if (is_keyword(mpl, "integer")) + { if (integer_used) + error(mpl, "at most one integer allowed"); + if (var->type != A_BINARY) var->type = A_INTEGER; + integer_used = 1; + get_token(mpl /* integer */); + } + else if (is_keyword(mpl, "binary")) +bin: { if (binary_used) + error(mpl, "at most one binary allowed"); + var->type = A_BINARY; + binary_used = 1; + get_token(mpl /* binary */); + } + else if (is_keyword(mpl, "logical")) + { if (!mpl->as_binary) + { warning(mpl, "keyword logical understood as binary"); + mpl->as_binary = 1; + } + goto bin; + } + else if (is_keyword(mpl, "symbolic")) + error(mpl, "variable cannot be symbolic"); + else if (mpl->token == T_GE) + { /* lower bound */ + if (var->lbnd != NULL) + { if (var->lbnd == var->ubnd) + error(mpl, "both fixed value and lower bound not allo" + "wed"); + else + error(mpl, "at most one lower bound allowed"); + } + get_token(mpl /* >= */); + /* parse an expression that specifies the lower bound */ + var->lbnd = expression_5(mpl); + if (var->lbnd->type == A_SYMBOLIC) + var->lbnd = make_unary(mpl, O_CVTNUM, var->lbnd, + A_NUMERIC, 0); + if (var->lbnd->type != A_NUMERIC) + error(mpl, "expression following >= has invalid type"); + xassert(var->lbnd->dim == 0); + } + else if (mpl->token == T_LE) + { /* upper bound */ + if (var->ubnd != NULL) + { if (var->ubnd == var->lbnd) + error(mpl, "both fixed value and upper bound not allo" + "wed"); + else + error(mpl, "at most one upper bound allowed"); + } + get_token(mpl /* <= */); + /* parse an expression that specifies the upper bound */ + var->ubnd = expression_5(mpl); + if (var->ubnd->type == A_SYMBOLIC) + var->ubnd = make_unary(mpl, O_CVTNUM, var->ubnd, + A_NUMERIC, 0); + if (var->ubnd->type != A_NUMERIC) + error(mpl, "expression following <= has invalid type"); + xassert(var->ubnd->dim == 0); + } + else if (mpl->token == T_EQ) + { /* fixed value */ + char opstr[8]; + if (!(var->lbnd == NULL && var->ubnd == NULL)) + { if (var->lbnd == var->ubnd) + error(mpl, "at most one fixed value allowed"); + else if (var->lbnd != NULL) + error(mpl, "both lower bound and fixed value not allo" + "wed"); + else + error(mpl, "both upper bound and fixed value not allo" + "wed"); + } + strcpy(opstr, mpl->image); + xassert(strlen(opstr) < sizeof(opstr)); + get_token(mpl /* = | == */); + /* parse an expression that specifies the fixed value */ + var->lbnd = expression_5(mpl); + if (var->lbnd->type == A_SYMBOLIC) + var->lbnd = make_unary(mpl, O_CVTNUM, var->lbnd, + A_NUMERIC, 0); + if (var->lbnd->type != A_NUMERIC) + error(mpl, "expression following %s has invalid type", + opstr); + xassert(var->lbnd->dim == 0); + /* indicate that the variable is fixed, not bounded */ + var->ubnd = var->lbnd; + } + else if (mpl->token == T_LT || mpl->token == T_GT || + mpl->token == T_NE) + error(mpl, "strict bound not allowed"); + else + error(mpl, "syntax error in variable statement"); + } + /* close the domain scope */ + if (var->domain != NULL) close_scope(mpl, var->domain); + /* the variable statement has been completely parsed */ + xassert(mpl->token == T_SEMICOLON); + get_token(mpl /* ; */); + return var; +} + +/*---------------------------------------------------------------------- +-- constraint_statement - parse constraint statement. +-- +-- This routine parses constraint statement using the syntax: +-- +-- ::= +-- : ; +-- ::= +-- ::= subject to +-- ::= subj to +-- ::= s.t. +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= , >= +-- ::= , <= +-- ::= , = +-- ::= , <= , <= +-- ::= , >= , >= +-- ::= +-- +-- Commae in are optional and may be omitted anywhere. */ + +CONSTRAINT *constraint_statement(MPL *mpl) +{ CONSTRAINT *con; + CODE *first, *second, *third; + int rho; + char opstr[8]; + if (mpl->flag_s) + error(mpl, "constraint statement must precede solve statement") + ; + if (is_keyword(mpl, "subject")) + { get_token(mpl /* subject */); + if (!is_keyword(mpl, "to")) + error(mpl, "keyword subject to incomplete"); + get_token(mpl /* to */); + } + else if (is_keyword(mpl, "subj")) + { get_token(mpl /* subj */); + if (!is_keyword(mpl, "to")) + error(mpl, "keyword subj to incomplete"); + get_token(mpl /* to */); + } + else if (mpl->token == T_SPTP) + get_token(mpl /* s.t. */); + /* the current token must be symbolic name of constraint */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "symbolic name missing where expected"); + /* there must be no other object with the same name */ + if (avl_find_node(mpl->tree, mpl->image) != NULL) + error(mpl, "%s multiply declared", mpl->image); + /* create model constraint */ + con = alloc(CONSTRAINT); + con->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(con->name, mpl->image); + con->alias = NULL; + con->dim = 0; + con->domain = NULL; + con->type = A_CONSTRAINT; + con->code = NULL; + con->lbnd = NULL; + con->ubnd = NULL; + con->array = NULL; + get_token(mpl /* */); + /* parse optional alias */ + if (mpl->token == T_STRING) + { con->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(con->alias, mpl->image); + get_token(mpl /* */); + } + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + { con->domain = indexing_expression(mpl); + con->dim = domain_arity(mpl, con->domain); + } + /* include the constraint name in the symbolic names table */ + { AVLNODE *node; + node = avl_insert_node(mpl->tree, con->name); + avl_set_node_type(node, A_CONSTRAINT); + avl_set_node_link(node, (void *)con); + } + /* the colon must precede the first expression */ + if (mpl->token != T_COLON) + error(mpl, "colon missing where expected"); + get_token(mpl /* : */); + /* parse the first expression */ + first = expression_5(mpl); + if (first->type == A_SYMBOLIC) + first = make_unary(mpl, O_CVTNUM, first, A_NUMERIC, 0); + if (!(first->type == A_NUMERIC || first->type == A_FORMULA)) + error(mpl, "expression following colon has invalid type"); + xassert(first->dim == 0); + /* relational operator must follow the first expression */ + if (mpl->token == T_COMMA) get_token(mpl /* , */); + switch (mpl->token) + { case T_LE: + case T_GE: + case T_EQ: + break; + case T_LT: + case T_GT: + case T_NE: + error(mpl, "strict inequality not allowed"); + case T_SEMICOLON: + error(mpl, "constraint must be equality or inequality"); + default: + goto err; + } + rho = mpl->token; + strcpy(opstr, mpl->image); + xassert(strlen(opstr) < sizeof(opstr)); + get_token(mpl /* rho */); + /* parse the second expression */ + second = expression_5(mpl); + if (second->type == A_SYMBOLIC) + second = make_unary(mpl, O_CVTNUM, second, A_NUMERIC, 0); + if (!(second->type == A_NUMERIC || second->type == A_FORMULA)) + error(mpl, "expression following %s has invalid type", opstr); + xassert(second->dim == 0); + /* check a token that follow the second expression */ + if (mpl->token == T_COMMA) + { get_token(mpl /* , */); + if (mpl->token == T_SEMICOLON) goto err; + } + if (mpl->token == T_LT || mpl->token == T_LE || + mpl->token == T_EQ || mpl->token == T_GE || + mpl->token == T_GT || mpl->token == T_NE) + { /* it is another relational operator, therefore the constraint + is double inequality */ + if (rho == T_EQ || mpl->token != rho) + error(mpl, "double inequality must be ... <= ... <= ... or " + "... >= ... >= ..."); + /* the first expression cannot be linear form */ + if (first->type == A_FORMULA) + error(mpl, "leftmost expression in double inequality cannot" + " be linear form"); + get_token(mpl /* rho */); + /* parse the third expression */ + third = expression_5(mpl); + if (third->type == A_SYMBOLIC) + third = make_unary(mpl, O_CVTNUM, second, A_NUMERIC, 0); + if (!(third->type == A_NUMERIC || third->type == A_FORMULA)) + error(mpl, "rightmost expression in double inequality const" + "raint has invalid type"); + xassert(third->dim == 0); + /* the third expression also cannot be linear form */ + if (third->type == A_FORMULA) + error(mpl, "rightmost expression in double inequality canno" + "t be linear form"); + } + else + { /* the constraint is equality or single inequality */ + third = NULL; + } + /* close the domain scope */ + if (con->domain != NULL) close_scope(mpl, con->domain); + /* convert all expressions to linear form, if necessary */ + if (first->type != A_FORMULA) + first = make_unary(mpl, O_CVTLFM, first, A_FORMULA, 0); + if (second->type != A_FORMULA) + second = make_unary(mpl, O_CVTLFM, second, A_FORMULA, 0); + if (third != NULL) + third = make_unary(mpl, O_CVTLFM, third, A_FORMULA, 0); + /* arrange expressions in the constraint */ + if (third == NULL) + { /* the constraint is equality or single inequality */ + switch (rho) + { case T_LE: + /* first <= second */ + con->code = first; + con->lbnd = NULL; + con->ubnd = second; + break; + case T_GE: + /* first >= second */ + con->code = first; + con->lbnd = second; + con->ubnd = NULL; + break; + case T_EQ: + /* first = second */ + con->code = first; + con->lbnd = second; + con->ubnd = second; + break; + default: + xassert(rho != rho); + } + } + else + { /* the constraint is double inequality */ + switch (rho) + { case T_LE: + /* first <= second <= third */ + con->code = second; + con->lbnd = first; + con->ubnd = third; + break; + case T_GE: + /* first >= second >= third */ + con->code = second; + con->lbnd = third; + con->ubnd = first; + break; + default: + xassert(rho != rho); + } + } + /* the constraint statement has been completely parsed */ + if (mpl->token != T_SEMICOLON) +err: error(mpl, "syntax error in constraint statement"); + get_token(mpl /* ; */); + return con; +} + +/*---------------------------------------------------------------------- +-- objective_statement - parse objective statement. +-- +-- This routine parses objective statement using the syntax: +-- +-- ::= : +-- ; +-- ::= minimize +-- ::= maximize +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= */ + +CONSTRAINT *objective_statement(MPL *mpl) +{ CONSTRAINT *obj; + int type; + if (is_keyword(mpl, "minimize")) + type = A_MINIMIZE; + else if (is_keyword(mpl, "maximize")) + type = A_MAXIMIZE; + else + xassert(mpl != mpl); + if (mpl->flag_s) + error(mpl, "objective statement must precede solve statement"); + get_token(mpl /* minimize | maximize */); + /* symbolic name must follow the verb 'minimize' or 'maximize' */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "symbolic name missing where expected"); + /* there must be no other object with the same name */ + if (avl_find_node(mpl->tree, mpl->image) != NULL) + error(mpl, "%s multiply declared", mpl->image); + /* create model objective */ + obj = alloc(CONSTRAINT); + obj->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(obj->name, mpl->image); + obj->alias = NULL; + obj->dim = 0; + obj->domain = NULL; + obj->type = type; + obj->code = NULL; + obj->lbnd = NULL; + obj->ubnd = NULL; + obj->array = NULL; + get_token(mpl /* */); + /* parse optional alias */ + if (mpl->token == T_STRING) + { obj->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(obj->alias, mpl->image); + get_token(mpl /* */); + } + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + { obj->domain = indexing_expression(mpl); + obj->dim = domain_arity(mpl, obj->domain); + } + /* include the constraint name in the symbolic names table */ + { AVLNODE *node; + node = avl_insert_node(mpl->tree, obj->name); + avl_set_node_type(node, A_CONSTRAINT); + avl_set_node_link(node, (void *)obj); + } + /* the colon must precede the objective expression */ + if (mpl->token != T_COLON) + error(mpl, "colon missing where expected"); + get_token(mpl /* : */); + /* parse the objective expression */ + obj->code = expression_5(mpl); + if (obj->code->type == A_SYMBOLIC) + obj->code = make_unary(mpl, O_CVTNUM, obj->code, A_NUMERIC, 0); + if (obj->code->type == A_NUMERIC) + obj->code = make_unary(mpl, O_CVTLFM, obj->code, A_FORMULA, 0); + if (obj->code->type != A_FORMULA) + error(mpl, "expression following colon has invalid type"); + xassert(obj->code->dim == 0); + /* close the domain scope */ + if (obj->domain != NULL) close_scope(mpl, obj->domain); + /* the objective statement has been completely parsed */ + if (mpl->token != T_SEMICOLON) + error(mpl, "syntax error in objective statement"); + get_token(mpl /* ; */); + return obj; +} + +#if 1 /* 11/II-2008 */ +/*********************************************************************** +* table_statement - parse table statement +* +* This routine parses table statement using the syntax: +* +* ::= +*
::= +* +* ::= +* table
IN : +* [ ] , ; +* ::= +* ::= +* ::= +* ::= +* ::= , +* ::= +* ::= <- +* ::= +* ::= , +* ::= +* ::= , +* ::= +* ::= ~ +* +* ::= +* table
OUT : +* ; +* ::= +* ::= +* ::= , +* ::= +* ::= ~ */ + +TABLE *table_statement(MPL *mpl) +{ TABLE *tab; + TABARG *last_arg, *arg; + TABFLD *last_fld, *fld; + TABIN *last_in, *in; + TABOUT *last_out, *out; + AVLNODE *node; + int nflds; + char name[MAX_LENGTH+1]; + xassert(is_keyword(mpl, "table")); + get_token(mpl /* solve */); + /* symbolic name must follow the keyword table */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "symbolic name missing where expected"); + /* there must be no other object with the same name */ + if (avl_find_node(mpl->tree, mpl->image) != NULL) + error(mpl, "%s multiply declared", mpl->image); + /* create data table */ + tab = alloc(TABLE); + tab->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(tab->name, mpl->image); + get_token(mpl /* */); + /* parse optional alias */ + if (mpl->token == T_STRING) + { tab->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(tab->alias, mpl->image); + get_token(mpl /* */); + } + else + tab->alias = NULL; + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + { /* this is output table */ + tab->type = A_OUTPUT; + tab->u.out.domain = indexing_expression(mpl); + if (!is_keyword(mpl, "OUT")) + error(mpl, "keyword OUT missing where expected"); + get_token(mpl /* OUT */); + } + else + { /* this is input table */ + tab->type = A_INPUT; + if (!is_keyword(mpl, "IN")) + error(mpl, "keyword IN missing where expected"); + get_token(mpl /* IN */); + } + /* parse argument list */ + tab->arg = last_arg = NULL; + for (;;) + { /* create argument list entry */ + arg = alloc(TABARG); + /* parse argument expression */ + if (mpl->token == T_COMMA || mpl->token == T_COLON || + mpl->token == T_SEMICOLON) + error(mpl, "argument expression missing where expected"); + arg->code = expression_5(mpl); + /* convert the result to symbolic type, if necessary */ + if (arg->code->type == A_NUMERIC) + arg->code = + make_unary(mpl, O_CVTSYM, arg->code, A_SYMBOLIC, 0); + /* check that now the result is of symbolic type */ + if (arg->code->type != A_SYMBOLIC) + error(mpl, "argument expression has invalid type"); + /* add the entry to the end of the list */ + arg->next = NULL; + if (last_arg == NULL) + tab->arg = arg; + else + last_arg->next = arg; + last_arg = arg; + /* argument expression has been parsed */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_COLON || mpl->token == T_SEMICOLON) + break; + } + xassert(tab->arg != NULL); + /* argument list must end with colon */ + if (mpl->token == T_COLON) + get_token(mpl /* : */); + else + error(mpl, "colon missing where expected"); + /* parse specific part of the table statement */ + switch (tab->type) + { case A_INPUT: goto input_table; + case A_OUTPUT: goto output_table; + default: xassert(tab != tab); + } +input_table: + /* parse optional set name */ + if (mpl->token == T_NAME) + { node = avl_find_node(mpl->tree, mpl->image); + if (node == NULL) + error(mpl, "%s not defined", mpl->image); + if (avl_get_node_type(node) != A_SET) + error(mpl, "%s not a set", mpl->image); + tab->u.in.set = (SET *)avl_get_node_link(node); + if (tab->u.in.set->assign != NULL) + error(mpl, "%s needs no data", mpl->image); + if (tab->u.in.set->dim != 0) + error(mpl, "%s must be a simple set", mpl->image); + get_token(mpl /* */); + if (mpl->token == T_INPUT) + get_token(mpl /* <- */); + else + error(mpl, "delimiter <- missing where expected"); + } + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", mpl->image); + else + tab->u.in.set = NULL; + /* parse field list */ + tab->u.in.fld = last_fld = NULL; + nflds = 0; + if (mpl->token == T_LBRACKET) + get_token(mpl /* [ */); + else + error(mpl, "field list missing where expected"); + for (;;) + { /* create field list entry */ + fld = alloc(TABFLD); + /* parse field name */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, + "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "field name missing where expected"); + fld->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); + strcpy(fld->name, mpl->image); + get_token(mpl /* */); + /* add the entry to the end of the list */ + fld->next = NULL; + if (last_fld == NULL) + tab->u.in.fld = fld; + else + last_fld->next = fld; + last_fld = fld; + nflds++; + /* field name has been parsed */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_RBRACKET) + break; + else + error(mpl, "syntax error in field list"); + } + /* check that the set dimen is equal to the number of fields */ + if (tab->u.in.set != NULL && tab->u.in.set->dimen != nflds) + error(mpl, "there must be %d field%s rather than %d", + tab->u.in.set->dimen, tab->u.in.set->dimen == 1 ? "" : "s", + nflds); + get_token(mpl /* ] */); + /* parse optional input list */ + tab->u.in.list = last_in = NULL; + while (mpl->token == T_COMMA) + { get_token(mpl /* , */); + /* create input list entry */ + in = alloc(TABIN); + /* parse parameter name */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, + "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "parameter name missing where expected"); + node = avl_find_node(mpl->tree, mpl->image); + if (node == NULL) + error(mpl, "%s not defined", mpl->image); + if (avl_get_node_type(node) != A_PARAMETER) + error(mpl, "%s not a parameter", mpl->image); + in->par = (PARAMETER *)avl_get_node_link(node); + if (in->par->dim != nflds) + error(mpl, "%s must have %d subscript%s rather than %d", + mpl->image, nflds, nflds == 1 ? "" : "s", in->par->dim); + if (in->par->assign != NULL) + error(mpl, "%s needs no data", mpl->image); + get_token(mpl /* */); + /* parse optional field name */ + if (mpl->token == T_TILDE) + { get_token(mpl /* ~ */); + /* parse field name */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, + "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "field name missing where expected"); + xassert(strlen(mpl->image) < sizeof(name)); + strcpy(name, mpl->image); + get_token(mpl /* */); + } + else + { /* field name is the same as the parameter name */ + xassert(strlen(in->par->name) < sizeof(name)); + strcpy(name, in->par->name); + } + /* assign field name */ + in->name = dmp_get_atomv(mpl->pool, strlen(name)+1); + strcpy(in->name, name); + /* add the entry to the end of the list */ + in->next = NULL; + if (last_in == NULL) + tab->u.in.list = in; + else + last_in->next = in; + last_in = in; + } + goto end_of_table; +output_table: + /* parse output list */ + tab->u.out.list = last_out = NULL; + for (;;) + { /* create output list entry */ + out = alloc(TABOUT); + /* parse expression */ + if (mpl->token == T_COMMA || mpl->token == T_SEMICOLON) + error(mpl, "expression missing where expected"); + if (mpl->token == T_NAME) + { xassert(strlen(mpl->image) < sizeof(name)); + strcpy(name, mpl->image); + } + else + name[0] = '\0'; + out->code = expression_5(mpl); + /* parse optional field name */ + if (mpl->token == T_TILDE) + { get_token(mpl /* ~ */); + /* parse field name */ + if (mpl->token == T_NAME) + ; + else if (is_reserved(mpl)) + error(mpl, + "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "field name missing where expected"); + xassert(strlen(mpl->image) < sizeof(name)); + strcpy(name, mpl->image); + get_token(mpl /* */); + } + /* assign field name */ + if (name[0] == '\0') + error(mpl, "field name required"); + out->name = dmp_get_atomv(mpl->pool, strlen(name)+1); + strcpy(out->name, name); + /* add the entry to the end of the list */ + out->next = NULL; + if (last_out == NULL) + tab->u.out.list = out; + else + last_out->next = out; + last_out = out; + /* output item has been parsed */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_SEMICOLON) + break; + else + error(mpl, "syntax error in output list"); + } + /* close the domain scope */ + close_scope(mpl,tab->u.out.domain); +end_of_table: + /* the table statement must end with semicolon */ + if (mpl->token != T_SEMICOLON) + error(mpl, "syntax error in table statement"); + get_token(mpl /* ; */); + return tab; +} +#endif + +/*---------------------------------------------------------------------- +-- solve_statement - parse solve statement. +-- +-- This routine parses solve statement using the syntax: +-- +-- ::= solve ; +-- +-- The solve statement can be used at most once. */ + +void *solve_statement(MPL *mpl) +{ xassert(is_keyword(mpl, "solve")); + if (mpl->flag_s) + error(mpl, "at most one solve statement allowed"); + mpl->flag_s = 1; + get_token(mpl /* solve */); + /* semicolon must follow solve statement */ + if (mpl->token != T_SEMICOLON) + error(mpl, "syntax error in solve statement"); + get_token(mpl /* ; */); + return NULL; +} + +/*---------------------------------------------------------------------- +-- check_statement - parse check statement. +-- +-- This routine parses check statement using the syntax: +-- +-- ::= check : ; +-- ::= +-- ::= +-- +-- If is omitted, colon following it may also be omitted. */ + +CHECK *check_statement(MPL *mpl) +{ CHECK *chk; + xassert(is_keyword(mpl, "check")); + /* create check descriptor */ + chk = alloc(CHECK); + chk->domain = NULL; + chk->code = NULL; + get_token(mpl /* check */); + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + { chk->domain = indexing_expression(mpl); +#if 0 + if (mpl->token != T_COLON) + error(mpl, "colon missing where expected"); +#endif + } + /* skip optional colon */ + if (mpl->token == T_COLON) get_token(mpl /* : */); + /* parse logical expression */ + chk->code = expression_13(mpl); + if (chk->code->type != A_LOGICAL) + error(mpl, "expression has invalid type"); + xassert(chk->code->dim == 0); + /* close the domain scope */ + if (chk->domain != NULL) close_scope(mpl, chk->domain); + /* the check statement has been completely parsed */ + if (mpl->token != T_SEMICOLON) + error(mpl, "syntax error in check statement"); + get_token(mpl /* ; */); + return chk; +} + +#if 1 /* 15/V-2010 */ +/*---------------------------------------------------------------------- +-- display_statement - parse display statement. +-- +-- This routine parses display statement using the syntax: +-- +-- ::= display : ; +-- ::= display ; +-- ::= +-- ::= +-- ::= +-- ::= , +-- ::= +-- ::= +-- ::= [ ] +-- ::= +-- ::= [ ] +-- ::= +-- ::= [ ] +-- ::= +-- ::= [ ] +-- ::= */ + +DISPLAY *display_statement(MPL *mpl) +{ DISPLAY *dpy; + DISPLAY1 *entry, *last_entry; + xassert(is_keyword(mpl, "display")); + /* create display descriptor */ + dpy = alloc(DISPLAY); + dpy->domain = NULL; + dpy->list = last_entry = NULL; + get_token(mpl /* display */); + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + dpy->domain = indexing_expression(mpl); + /* skip optional colon */ + if (mpl->token == T_COLON) get_token(mpl /* : */); + /* parse display list */ + for (;;) + { /* create new display entry */ + entry = alloc(DISPLAY1); + entry->type = 0; + entry->next = NULL; + /* and append it to the display list */ + if (dpy->list == NULL) + dpy->list = entry; + else + last_entry->next = entry; + last_entry = entry; + /* parse display entry */ + if (mpl->token == T_NAME) + { AVLNODE *node; + int next_token; + get_token(mpl /* */); + next_token = mpl->token; + unget_token(mpl); + if (!(next_token == T_COMMA || next_token == T_SEMICOLON)) + { /* symbolic name begins expression */ + goto expr; + } + /* display entry is dummy index or model object */ + node = avl_find_node(mpl->tree, mpl->image); + if (node == NULL) + error(mpl, "%s not defined", mpl->image); + entry->type = avl_get_node_type(node); + switch (avl_get_node_type(node)) + { case A_INDEX: + entry->u.slot = + (DOMAIN_SLOT *)avl_get_node_link(node); + break; + case A_SET: + entry->u.set = (SET *)avl_get_node_link(node); + break; + case A_PARAMETER: + entry->u.par = (PARAMETER *)avl_get_node_link(node); + break; + case A_VARIABLE: + entry->u.var = (VARIABLE *)avl_get_node_link(node); + if (!mpl->flag_s) + error(mpl, "invalid reference to variable %s above" + " solve statement", entry->u.var->name); + break; + case A_CONSTRAINT: + entry->u.con = (CONSTRAINT *)avl_get_node_link(node); + if (!mpl->flag_s) + error(mpl, "invalid reference to %s %s above solve" + " statement", + entry->u.con->type == A_CONSTRAINT ? + "constraint" : "objective", entry->u.con->name); + break; + default: + xassert(node != node); + } + get_token(mpl /* */); + } + else +expr: { /* display entry is expression */ + entry->type = A_EXPRESSION; + entry->u.code = expression_13(mpl); + } + /* check a token that follows the entry parsed */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else + break; + } + /* close the domain scope */ + if (dpy->domain != NULL) close_scope(mpl, dpy->domain); + /* the display statement has been completely parsed */ + if (mpl->token != T_SEMICOLON) + error(mpl, "syntax error in display statement"); + get_token(mpl /* ; */); + return dpy; +} +#endif + +/*---------------------------------------------------------------------- +-- printf_statement - parse printf statement. +-- +-- This routine parses print statement using the syntax: +-- +-- ::= ; +-- ::= > ; +-- ::= >> ; +-- ::= printf : +-- ::= printf +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= , +-- ::= +-- ::= */ + +PRINTF *printf_statement(MPL *mpl) +{ PRINTF *prt; + PRINTF1 *entry, *last_entry; + xassert(is_keyword(mpl, "printf")); + /* create printf descriptor */ + prt = alloc(PRINTF); + prt->domain = NULL; + prt->fmt = NULL; + prt->list = last_entry = NULL; + get_token(mpl /* printf */); + /* parse optional indexing expression */ + if (mpl->token == T_LBRACE) + { prt->domain = indexing_expression(mpl); +#if 0 + if (mpl->token != T_COLON) + error(mpl, "colon missing where expected"); +#endif + } + /* skip optional colon */ + if (mpl->token == T_COLON) get_token(mpl /* : */); + /* parse expression for format string */ + prt->fmt = expression_5(mpl); + /* convert it to symbolic type, if necessary */ + if (prt->fmt->type == A_NUMERIC) + prt->fmt = make_unary(mpl, O_CVTSYM, prt->fmt, A_SYMBOLIC, 0); + /* check that now the expression is of symbolic type */ + if (prt->fmt->type != A_SYMBOLIC) + error(mpl, "format expression has invalid type"); + /* parse printf list */ + while (mpl->token == T_COMMA) + { get_token(mpl /* , */); + /* create new printf entry */ + entry = alloc(PRINTF1); + entry->code = NULL; + entry->next = NULL; + /* and append it to the printf list */ + if (prt->list == NULL) + prt->list = entry; + else + last_entry->next = entry; + last_entry = entry; + /* parse printf entry */ + entry->code = expression_9(mpl); + if (!(entry->code->type == A_NUMERIC || + entry->code->type == A_SYMBOLIC || + entry->code->type == A_LOGICAL)) + error(mpl, "only numeric, symbolic, or logical expression a" + "llowed"); + } + /* close the domain scope */ + if (prt->domain != NULL) close_scope(mpl, prt->domain); +#if 1 /* 14/VII-2006 */ + /* parse optional redirection */ + prt->fname = NULL, prt->app = 0; + if (mpl->token == T_GT || mpl->token == T_APPEND) + { prt->app = (mpl->token == T_APPEND); + get_token(mpl /* > or >> */); + /* parse expression for file name string */ + prt->fname = expression_5(mpl); + /* convert it to symbolic type, if necessary */ + if (prt->fname->type == A_NUMERIC) + prt->fname = make_unary(mpl, O_CVTSYM, prt->fname, + A_SYMBOLIC, 0); + /* check that now the expression is of symbolic type */ + if (prt->fname->type != A_SYMBOLIC) + error(mpl, "file name expression has invalid type"); + } +#endif + /* the printf statement has been completely parsed */ + if (mpl->token != T_SEMICOLON) + error(mpl, "syntax error in printf statement"); + get_token(mpl /* ; */); + return prt; +} + +/*---------------------------------------------------------------------- +-- for_statement - parse for statement. +-- +-- This routine parses for statement using the syntax: +-- +-- ::= for +-- ::= for { } +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= */ + +FOR *for_statement(MPL *mpl) +{ FOR *fur; + STATEMENT *stmt, *last_stmt; + xassert(is_keyword(mpl, "for")); + /* create for descriptor */ + fur = alloc(FOR); + fur->domain = NULL; + fur->list = last_stmt = NULL; + get_token(mpl /* for */); + /* parse indexing expression */ + if (mpl->token != T_LBRACE) + error(mpl, "indexing expression missing where expected"); + fur->domain = indexing_expression(mpl); + /* skip optional colon */ + if (mpl->token == T_COLON) get_token(mpl /* : */); + /* parse for statement body */ + if (mpl->token != T_LBRACE) + { /* parse simple statement */ + fur->list = simple_statement(mpl, 1); + } + else + { /* parse compound statement */ + get_token(mpl /* { */); + while (mpl->token != T_RBRACE) + { /* parse statement */ + stmt = simple_statement(mpl, 1); + /* and append it to the end of the statement list */ + if (last_stmt == NULL) + fur->list = stmt; + else + last_stmt->next = stmt; + last_stmt = stmt; + } + get_token(mpl /* } */); + } + /* close the domain scope */ + xassert(fur->domain != NULL); + close_scope(mpl, fur->domain); + /* the for statement has been completely parsed */ + return fur; +} + +/*---------------------------------------------------------------------- +-- end_statement - parse end statement. +-- +-- This routine parses end statement using the syntax: +-- +-- ::= end ; */ + +void end_statement(MPL *mpl) +{ if (!mpl->flag_d && is_keyword(mpl, "end") || + mpl->flag_d && is_literal(mpl, "end")) + { get_token(mpl /* end */); + if (mpl->token == T_SEMICOLON) + get_token(mpl /* ; */); + else + warning(mpl, "no semicolon following end statement; missing" + " semicolon inserted"); + } + else + warning(mpl, "unexpected end of file; missing end statement in" + "serted"); + if (mpl->token != T_EOF) + warning(mpl, "some text detected beyond end statement; text ig" + "nored"); + return; +} + +/*---------------------------------------------------------------------- +-- simple_statement - parse simple statement. +-- +-- This routine parses simple statement using the syntax: +-- +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- ::= +-- +-- If the flag spec is set, some statements cannot be used. */ + +STATEMENT *simple_statement(MPL *mpl, int spec) +{ STATEMENT *stmt; + stmt = alloc(STATEMENT); + stmt->line = mpl->line; + stmt->next = NULL; + if (is_keyword(mpl, "set")) + { if (spec) + error(mpl, "set statement not allowed here"); + stmt->type = A_SET; + stmt->u.set = set_statement(mpl); + } + else if (is_keyword(mpl, "param")) + { if (spec) + error(mpl, "parameter statement not allowed here"); + stmt->type = A_PARAMETER; + stmt->u.par = parameter_statement(mpl); + } + else if (is_keyword(mpl, "var")) + { if (spec) + error(mpl, "variable statement not allowed here"); + stmt->type = A_VARIABLE; + stmt->u.var = variable_statement(mpl); + } + else if (is_keyword(mpl, "subject") || + is_keyword(mpl, "subj") || + mpl->token == T_SPTP) + { if (spec) + error(mpl, "constraint statement not allowed here"); + stmt->type = A_CONSTRAINT; + stmt->u.con = constraint_statement(mpl); + } + else if (is_keyword(mpl, "minimize") || + is_keyword(mpl, "maximize")) + { if (spec) + error(mpl, "objective statement not allowed here"); + stmt->type = A_CONSTRAINT; + stmt->u.con = objective_statement(mpl); + } +#if 1 /* 11/II-2008 */ + else if (is_keyword(mpl, "table")) + { if (spec) + error(mpl, "table statement not allowed here"); + stmt->type = A_TABLE; + stmt->u.tab = table_statement(mpl); + } +#endif + else if (is_keyword(mpl, "solve")) + { if (spec) + error(mpl, "solve statement not allowed here"); + stmt->type = A_SOLVE; + stmt->u.slv = solve_statement(mpl); + } + else if (is_keyword(mpl, "check")) + { stmt->type = A_CHECK; + stmt->u.chk = check_statement(mpl); + } + else if (is_keyword(mpl, "display")) + { stmt->type = A_DISPLAY; + stmt->u.dpy = display_statement(mpl); + } + else if (is_keyword(mpl, "printf")) + { stmt->type = A_PRINTF; + stmt->u.prt = printf_statement(mpl); + } + else if (is_keyword(mpl, "for")) + { stmt->type = A_FOR; + stmt->u.fur = for_statement(mpl); + } + else if (mpl->token == T_NAME) + { if (spec) + error(mpl, "constraint statement not allowed here"); + stmt->type = A_CONSTRAINT; + stmt->u.con = constraint_statement(mpl); + } + else if (is_reserved(mpl)) + error(mpl, "invalid use of reserved keyword %s", mpl->image); + else + error(mpl, "syntax error in model section"); + return stmt; +} + +/*---------------------------------------------------------------------- +-- model_section - parse model section. +-- +-- This routine parses model section using the syntax: +-- +-- ::= +-- ::= +-- +-- Parsing model section is terminated by either the keyword 'data', or +-- the keyword 'end', or the end of file. */ + +void model_section(MPL *mpl) +{ STATEMENT *stmt, *last_stmt; + xassert(mpl->model == NULL); + last_stmt = NULL; + while (!(mpl->token == T_EOF || is_keyword(mpl, "data") || + is_keyword(mpl, "end"))) + { /* parse statement */ + stmt = simple_statement(mpl, 0); + /* and append it to the end of the statement list */ + if (last_stmt == NULL) + mpl->model = stmt; + else + last_stmt->next = stmt; + last_stmt = stmt; + } + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmpl02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmpl02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1205 @@ +/* glpmpl02.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_STDIO +#include "glpenv.h" +#include "glpmpl.h" + +/**********************************************************************/ +/* * * PROCESSING DATA SECTION * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- create_slice - create slice. +-- +-- This routine creates a slice, which initially has no components. */ + +SLICE *create_slice(MPL *mpl) +{ SLICE *slice; + xassert(mpl == mpl); + slice = NULL; + return slice; +} + +/*---------------------------------------------------------------------- +-- expand_slice - append new component to slice. +-- +-- This routine expands slice appending to it either a given symbol or +-- null component, which becomes the last component of the slice. */ + +SLICE *expand_slice +( MPL *mpl, + SLICE *slice, /* destroyed */ + SYMBOL *sym /* destroyed */ +) +{ SLICE *tail, *temp; + /* create a new component */ + tail = dmp_get_atom(mpl->tuples, sizeof(SLICE)); + tail->sym = sym; + tail->next = NULL; + /* and append it to the component list */ + if (slice == NULL) + slice = tail; + else + { for (temp = slice; temp->next != NULL; temp = temp->next); + temp->next = tail; + } + return slice; +} + +/*---------------------------------------------------------------------- +-- slice_dimen - determine dimension of slice. +-- +-- This routine returns dimension of slice, which is number of all its +-- components including null ones. */ + +int slice_dimen +( MPL *mpl, + SLICE *slice /* not changed */ +) +{ SLICE *temp; + int dim; + xassert(mpl == mpl); + dim = 0; + for (temp = slice; temp != NULL; temp = temp->next) dim++; + return dim; +} + +/*---------------------------------------------------------------------- +-- slice_arity - determine arity of slice. +-- +-- This routine returns arity of slice, i.e. number of null components +-- (indicated by asterisks) in the slice. */ + +int slice_arity +( MPL *mpl, + SLICE *slice /* not changed */ +) +{ SLICE *temp; + int arity; + xassert(mpl == mpl); + arity = 0; + for (temp = slice; temp != NULL; temp = temp->next) + if (temp->sym == NULL) arity++; + return arity; +} + +/*---------------------------------------------------------------------- +-- fake_slice - create fake slice of all asterisks. +-- +-- This routine creates a fake slice of given dimension, which contains +-- asterisks in all components. Zero dimension is allowed. */ + +SLICE *fake_slice(MPL *mpl, int dim) +{ SLICE *slice; + slice = create_slice(mpl); + while (dim-- > 0) slice = expand_slice(mpl, slice, NULL); + return slice; +} + +/*---------------------------------------------------------------------- +-- delete_slice - delete slice. +-- +-- This routine deletes specified slice. */ + +void delete_slice +( MPL *mpl, + SLICE *slice /* destroyed */ +) +{ SLICE *temp; + while (slice != NULL) + { temp = slice; + slice = temp->next; + if (temp->sym != NULL) delete_symbol(mpl, temp->sym); +xassert(sizeof(SLICE) == sizeof(TUPLE)); + dmp_free_atom(mpl->tuples, temp, sizeof(TUPLE)); + } + return; +} + +/*---------------------------------------------------------------------- +-- is_number - check if current token is number. +-- +-- If the current token is a number, this routine returns non-zero. +-- Otherwise zero is returned. */ + +int is_number(MPL *mpl) +{ return + mpl->token == T_NUMBER; +} + +/*---------------------------------------------------------------------- +-- is_symbol - check if current token is symbol. +-- +-- If the current token is suitable to be a symbol, the routine returns +-- non-zero. Otherwise zero is returned. */ + +int is_symbol(MPL *mpl) +{ return + mpl->token == T_NUMBER || + mpl->token == T_SYMBOL || + mpl->token == T_STRING; +} + +/*---------------------------------------------------------------------- +-- is_literal - check if current token is given symbolic literal. +-- +-- If the current token is given symbolic literal, this routine returns +-- non-zero. Otherwise zero is returned. +-- +-- This routine is used on processing the data section in the same way +-- as the routine is_keyword on processing the model section. */ + +int is_literal(MPL *mpl, char *literal) +{ return + is_symbol(mpl) && strcmp(mpl->image, literal) == 0; +} + +/*---------------------------------------------------------------------- +-- read_number - read number. +-- +-- This routine reads the current token, which must be a number, and +-- returns its numeric value. */ + +double read_number(MPL *mpl) +{ double num; + xassert(is_number(mpl)); + num = mpl->value; + get_token(mpl /* */); + return num; +} + +/*---------------------------------------------------------------------- +-- read_symbol - read symbol. +-- +-- This routine reads the current token, which must be a symbol, and +-- returns its symbolic value. */ + +SYMBOL *read_symbol(MPL *mpl) +{ SYMBOL *sym; + xassert(is_symbol(mpl)); + if (is_number(mpl)) + sym = create_symbol_num(mpl, mpl->value); + else + sym = create_symbol_str(mpl, create_string(mpl, mpl->image)); + get_token(mpl /* */); + return sym; +} + +/*---------------------------------------------------------------------- +-- read_slice - read slice. +-- +-- This routine reads slice using the syntax: +-- +-- ::= [ ] +-- ::= ( ) +-- ::= +-- ::= , +-- ::= +-- ::= * +-- +-- The bracketed form of slice is used for members of multi-dimensional +-- objects while the parenthesized form is used for elemental sets. */ + +SLICE *read_slice +( MPL *mpl, + char *name, /* not changed */ + int dim +) +{ SLICE *slice; + int close; + xassert(name != NULL); + switch (mpl->token) + { case T_LBRACKET: + close = T_RBRACKET; + break; + case T_LEFT: + xassert(dim > 0); + close = T_RIGHT; + break; + default: + xassert(mpl != mpl); + } + if (dim == 0) + error(mpl, "%s cannot be subscripted", name); + get_token(mpl /* ( | [ */); + /* read slice components */ + slice = create_slice(mpl); + for (;;) + { /* the current token must be a symbol or asterisk */ + if (is_symbol(mpl)) + slice = expand_slice(mpl, slice, read_symbol(mpl)); + else if (mpl->token == T_ASTERISK) + { slice = expand_slice(mpl, slice, NULL); + get_token(mpl /* * */); + } + else + error(mpl, "number, symbol, or asterisk missing where expec" + "ted"); + /* check a token that follows the symbol */ + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == close) + break; + else + error(mpl, "syntax error in slice"); + } + /* number of slice components must be the same as the appropriate + dimension */ + if (slice_dimen(mpl, slice) != dim) + { switch (close) + { case T_RBRACKET: + error(mpl, "%s must have %d subscript%s, not %d", name, + dim, dim == 1 ? "" : "s", slice_dimen(mpl, slice)); + break; + case T_RIGHT: + error(mpl, "%s has dimension %d, not %d", name, dim, + slice_dimen(mpl, slice)); + break; + default: + xassert(close != close); + } + } + get_token(mpl /* ) | ] */); + return slice; +} + +/*---------------------------------------------------------------------- +-- select_set - select set to saturate it with elemental sets. +-- +-- This routine selects set to saturate it with elemental sets provided +-- in the data section. */ + +SET *select_set +( MPL *mpl, + char *name /* not changed */ +) +{ SET *set; + AVLNODE *node; + xassert(name != NULL); + node = avl_find_node(mpl->tree, name); + if (node == NULL || avl_get_node_type(node) != A_SET) + error(mpl, "%s not a set", name); + set = (SET *)avl_get_node_link(node); + if (set->assign != NULL || set->gadget != NULL) + error(mpl, "%s needs no data", name); + set->data = 1; + return set; +} + +/*---------------------------------------------------------------------- +-- simple_format - read set data block in simple format. +-- +-- This routine reads set data block using the syntax: +-- +-- ::= , , ... , +-- +-- where are used to construct a complete n-tuple, which is +-- included in elemental set assigned to the set member. Commae between +-- symbols are optional and may be omitted anywhere. +-- +-- Number of components in the slice must be the same as dimension of +-- n-tuples in elemental sets assigned to the set members. To construct +-- complete n-tuple the routine replaces null positions in the slice by +-- corresponding . +-- +-- If the slice contains at least one null position, the current token +-- must be symbol. Otherwise, the routine reads no symbols to construct +-- the n-tuple, so the current token is not checked. */ + +void simple_format +( MPL *mpl, + SET *set, /* not changed */ + MEMBER *memb, /* modified */ + SLICE *slice /* not changed */ +) +{ TUPLE *tuple; + SLICE *temp; + SYMBOL *sym, *with = NULL; + xassert(set != NULL); + xassert(memb != NULL); + xassert(slice != NULL); + xassert(set->dimen == slice_dimen(mpl, slice)); + xassert(memb->value.set->dim == set->dimen); + if (slice_arity(mpl, slice) > 0) xassert(is_symbol(mpl)); + /* read symbols and construct complete n-tuple */ + tuple = create_tuple(mpl); + for (temp = slice; temp != NULL; temp = temp->next) + { if (temp->sym == NULL) + { /* substitution is needed; read symbol */ + if (!is_symbol(mpl)) + { int lack = slice_arity(mpl, temp); + /* with cannot be null due to assertion above */ + xassert(with != NULL); + if (lack == 1) + error(mpl, "one item missing in data group beginning " + "with %s", format_symbol(mpl, with)); + else + error(mpl, "%d items missing in data group beginning " + "with %s", lack, format_symbol(mpl, with)); + } + sym = read_symbol(mpl); + if (with == NULL) with = sym; + } + else + { /* copy symbol from the slice */ + sym = copy_symbol(mpl, temp->sym); + } + /* append the symbol to the n-tuple */ + tuple = expand_tuple(mpl, tuple, sym); + /* skip optional comma *between* */ + if (temp->next != NULL && mpl->token == T_COMMA) + get_token(mpl /* , */); + } + /* add constructed n-tuple to elemental set */ + check_then_add(mpl, memb->value.set, tuple); + return; +} + +/*---------------------------------------------------------------------- +-- matrix_format - read set data block in matrix format. +-- +-- This routine reads set data block using the syntax: +-- +-- ::= ... := +-- +/- +/- ... +/- +-- +/- +/- ... +/- +-- . . . . . . . . . . . +-- +/- +/- ... +/- +-- +-- where are symbols that denote rows of the matrix, +-- are symbols that denote columns of the matrix, "+" and "-" indicate +-- whether corresponding n-tuple needs to be included in the elemental +-- set or not, respectively. +-- +-- Number of the slice components must be the same as dimension of the +-- elemental set. The slice must have two null positions. To construct +-- complete n-tuple for particular element of the matrix the routine +-- replaces first null position of the slice by the corresponding +-- (or , if the flag tr is on) and second null position by the +-- corresponding (or by , if the flag tr is on). */ + +void matrix_format +( MPL *mpl, + SET *set, /* not changed */ + MEMBER *memb, /* modified */ + SLICE *slice, /* not changed */ + int tr +) +{ SLICE *list, *col, *temp; + TUPLE *tuple; + SYMBOL *row; + xassert(set != NULL); + xassert(memb != NULL); + xassert(slice != NULL); + xassert(set->dimen == slice_dimen(mpl, slice)); + xassert(memb->value.set->dim == set->dimen); + xassert(slice_arity(mpl, slice) == 2); + /* read the matrix heading that contains column symbols (there + may be no columns at all) */ + list = create_slice(mpl); + while (mpl->token != T_ASSIGN) + { /* read column symbol and append it to the column list */ + if (!is_symbol(mpl)) + error(mpl, "number, symbol, or := missing where expected"); + list = expand_slice(mpl, list, read_symbol(mpl)); + } + get_token(mpl /* := */); + /* read zero or more rows that contain matrix data */ + while (is_symbol(mpl)) + { /* read row symbol (if the matrix has no columns, row symbols + are just ignored) */ + row = read_symbol(mpl); + /* read the matrix row accordingly to the column list */ + for (col = list; col != NULL; col = col->next) + { int which = 0; + /* check indicator */ + if (is_literal(mpl, "+")) + ; + else if (is_literal(mpl, "-")) + { get_token(mpl /* - */); + continue; + } + else + { int lack = slice_dimen(mpl, col); + if (lack == 1) + error(mpl, "one item missing in data group beginning " + "with %s", format_symbol(mpl, row)); + else + error(mpl, "%d items missing in data group beginning " + "with %s", lack, format_symbol(mpl, row)); + } + /* construct complete n-tuple */ + tuple = create_tuple(mpl); + for (temp = slice; temp != NULL; temp = temp->next) + { if (temp->sym == NULL) + { /* substitution is needed */ + switch (++which) + { case 1: + /* substitute in the first null position */ + tuple = expand_tuple(mpl, tuple, + copy_symbol(mpl, tr ? col->sym : row)); + break; + case 2: + /* substitute in the second null position */ + tuple = expand_tuple(mpl, tuple, + copy_symbol(mpl, tr ? row : col->sym)); + break; + default: + xassert(which != which); + } + } + else + { /* copy symbol from the slice */ + tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, + temp->sym)); + } + } + xassert(which == 2); + /* add constructed n-tuple to elemental set */ + check_then_add(mpl, memb->value.set, tuple); + get_token(mpl /* + */); + } + /* delete the row symbol */ + delete_symbol(mpl, row); + } + /* delete the column list */ + delete_slice(mpl, list); + return; +} + +/*---------------------------------------------------------------------- +-- set_data - read set data. +-- +-- This routine reads set data using the syntax: +-- +-- ::= set ; +-- ::= set [ ] ; +-- ::= +-- ::= +-- ::= , := +-- ::= , ( ) +-- ::= , +-- ::= , : +-- ::= , (tr) +-- ::= , (tr) : +-- +-- Commae in are optional and may be omitted anywhere. */ + +void set_data(MPL *mpl) +{ SET *set; + TUPLE *tuple; + MEMBER *memb; + SLICE *slice; + int tr = 0; + xassert(is_literal(mpl, "set")); + get_token(mpl /* set */); + /* symbolic name of set must follows the keyword 'set' */ + if (!is_symbol(mpl)) + error(mpl, "set name missing where expected"); + /* select the set to saturate it with data */ + set = select_set(mpl, mpl->image); + get_token(mpl /* */); + /* read optional subscript list, which identifies member of the + set to be read */ + tuple = create_tuple(mpl); + if (mpl->token == T_LBRACKET) + { /* subscript list is specified */ + if (set->dim == 0) + error(mpl, "%s cannot be subscripted", set->name); + get_token(mpl /* [ */); + /* read symbols and construct subscript list */ + for (;;) + { if (!is_symbol(mpl)) + error(mpl, "number or symbol missing where expected"); + tuple = expand_tuple(mpl, tuple, read_symbol(mpl)); + if (mpl->token == T_COMMA) + get_token(mpl /* , */); + else if (mpl->token == T_RBRACKET) + break; + else + error(mpl, "syntax error in subscript list"); + } + if (set->dim != tuple_dimen(mpl, tuple)) + error(mpl, "%s must have %d subscript%s rather than %d", + set->name, set->dim, set->dim == 1 ? "" : "s", + tuple_dimen(mpl, tuple)); + get_token(mpl /* ] */); + } + else + { /* subscript list is not specified */ + if (set->dim != 0) + error(mpl, "%s must be subscripted", set->name); + } + /* there must be no member with the same subscript list */ + if (find_member(mpl, set->array, tuple) != NULL) + error(mpl, "%s%s already defined", + set->name, format_tuple(mpl, '[', tuple)); + /* add new member to the set and assign it empty elemental set */ + memb = add_member(mpl, set->array, tuple); + memb->value.set = create_elemset(mpl, set->dimen); + /* create an initial fake slice of all asterisks */ + slice = fake_slice(mpl, set->dimen); + /* read zero or more data assignments */ + for (;;) + { /* skip optional comma */ + if (mpl->token == T_COMMA) get_token(mpl /* , */); + /* process assignment element */ + if (mpl->token == T_ASSIGN) + { /* assignment ligature is non-significant element */ + get_token(mpl /* := */); + } + else if (mpl->token == T_LEFT) + { /* left parenthesis begins either new slice or "transpose" + indicator */ + int is_tr; + get_token(mpl /* ( */); + is_tr = is_literal(mpl, "tr"); + unget_token(mpl /* ( */); + if (is_tr) goto left; + /* delete the current slice and read new one */ + delete_slice(mpl, slice); + slice = read_slice(mpl, set->name, set->dimen); + /* each new slice resets the "transpose" indicator */ + tr = 0; + /* if the new slice is 0-ary, formally there is one 0-tuple + (in the simple format) that follows it */ + if (slice_arity(mpl, slice) == 0) + simple_format(mpl, set, memb, slice); + } + else if (is_symbol(mpl)) + { /* number or symbol begins data in the simple format */ + simple_format(mpl, set, memb, slice); + } + else if (mpl->token == T_COLON) + { /* colon begins data in the matrix format */ + if (slice_arity(mpl, slice) != 2) +err1: error(mpl, "slice currently used must specify 2 asterisk" + "s, not %d", slice_arity(mpl, slice)); + get_token(mpl /* : */); + /* read elemental set data in the matrix format */ + matrix_format(mpl, set, memb, slice, tr); + } + else if (mpl->token == T_LEFT) +left: { /* left parenthesis begins the "transpose" indicator, which + is followed by data in the matrix format */ + get_token(mpl /* ( */); + if (!is_literal(mpl, "tr")) +err2: error(mpl, "transpose indicator (tr) incomplete"); + if (slice_arity(mpl, slice) != 2) goto err1; + get_token(mpl /* tr */); + if (mpl->token != T_RIGHT) goto err2; + get_token(mpl /* ) */); + /* in this case the colon is optional */ + if (mpl->token == T_COLON) get_token(mpl /* : */); + /* set the "transpose" indicator */ + tr = 1; + /* read elemental set data in the matrix format */ + matrix_format(mpl, set, memb, slice, tr); + } + else if (mpl->token == T_SEMICOLON) + { /* semicolon terminates the data block */ + get_token(mpl /* ; */); + break; + } + else + error(mpl, "syntax error in set data block"); + } + /* delete the current slice */ + delete_slice(mpl, slice); + return; +} + +/*---------------------------------------------------------------------- +-- select_parameter - select parameter to saturate it with data. +-- +-- This routine selects parameter to saturate it with data provided in +-- the data section. */ + +PARAMETER *select_parameter +( MPL *mpl, + char *name /* not changed */ +) +{ PARAMETER *par; + AVLNODE *node; + xassert(name != NULL); + node = avl_find_node(mpl->tree, name); + if (node == NULL || avl_get_node_type(node) != A_PARAMETER) + error(mpl, "%s not a parameter", name); + par = (PARAMETER *)avl_get_node_link(node); + if (par->assign != NULL) + error(mpl, "%s needs no data", name); + if (par->data) + error(mpl, "%s already provided with data", name); + par->data = 1; + return par; +} + +/*---------------------------------------------------------------------- +-- set_default - set default parameter value. +-- +-- This routine sets default value for specified parameter. */ + +void set_default +( MPL *mpl, + PARAMETER *par, /* not changed */ + SYMBOL *altval /* destroyed */ +) +{ xassert(par != NULL); + xassert(altval != NULL); + if (par->option != NULL) + error(mpl, "default value for %s already specified in model se" + "ction", par->name); + xassert(par->defval == NULL); + par->defval = altval; + return; +} + +/*---------------------------------------------------------------------- +-- read_value - read value and assign it to parameter member. +-- +-- This routine reads numeric or symbolic value from the input stream +-- and assigns to new parameter member specified by its n-tuple, which +-- (the member) is created and added to the parameter array. */ + +MEMBER *read_value +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* destroyed */ +) +{ MEMBER *memb; + xassert(par != NULL); + xassert(is_symbol(mpl)); + /* there must be no member with the same n-tuple */ + if (find_member(mpl, par->array, tuple) != NULL) + error(mpl, "%s%s already defined", + par->name, format_tuple(mpl, '[', tuple)); + /* create new parameter member with given n-tuple */ + memb = add_member(mpl, par->array, tuple); + /* read value and assigns it to the new parameter member */ + switch (par->type) + { case A_NUMERIC: + case A_INTEGER: + case A_BINARY: + if (!is_number(mpl)) + error(mpl, "%s requires numeric data", par->name); + memb->value.num = read_number(mpl); + break; + case A_SYMBOLIC: + memb->value.sym = read_symbol(mpl); + break; + default: + xassert(par != par); + } + return memb; +} + +/*---------------------------------------------------------------------- +-- plain_format - read parameter data block in plain format. +-- +-- This routine reads parameter data block using the syntax: +-- +-- ::= , , ... , , +-- +-- where are used to determine a complete subscript list for +-- parameter member, is a numeric or symbolic value assigned to +-- the parameter member. Commae between data items are optional and may +-- be omitted anywhere. +-- +-- Number of components in the slice must be the same as dimension of +-- the parameter. To construct the complete subscript list the routine +-- replaces null positions in the slice by corresponding . */ + +void plain_format +( MPL *mpl, + PARAMETER *par, /* not changed */ + SLICE *slice /* not changed */ +) +{ TUPLE *tuple; + SLICE *temp; + SYMBOL *sym, *with = NULL; + xassert(par != NULL); + xassert(par->dim == slice_dimen(mpl, slice)); + xassert(is_symbol(mpl)); + /* read symbols and construct complete subscript list */ + tuple = create_tuple(mpl); + for (temp = slice; temp != NULL; temp = temp->next) + { if (temp->sym == NULL) + { /* substitution is needed; read symbol */ + if (!is_symbol(mpl)) + { int lack = slice_arity(mpl, temp) + 1; + xassert(with != NULL); + xassert(lack > 1); + error(mpl, "%d items missing in data group beginning wit" + "h %s", lack, format_symbol(mpl, with)); + } + sym = read_symbol(mpl); + if (with == NULL) with = sym; + } + else + { /* copy symbol from the slice */ + sym = copy_symbol(mpl, temp->sym); + } + /* append the symbol to the subscript list */ + tuple = expand_tuple(mpl, tuple, sym); + /* skip optional comma */ + if (mpl->token == T_COMMA) get_token(mpl /* , */); + } + /* read value and assign it to new parameter member */ + if (!is_symbol(mpl)) + { xassert(with != NULL); + error(mpl, "one item missing in data group beginning with %s", + format_symbol(mpl, with)); + } + read_value(mpl, par, tuple); + return; +} + +/*---------------------------------------------------------------------- +-- tabular_format - read parameter data block in tabular format. +-- +-- This routine reads parameter data block using the syntax: +-- +-- ::= ... := +-- ... +-- ... +-- . . . . . . . . . . . +-- ... +-- +-- where are symbols that denote rows of the table, +-- are symbols that denote columns of the table, are numeric +-- or symbolic values assigned to the corresponding parameter members. +-- If is specified as single point, no value is provided. +-- +-- Number of components in the slice must be the same as dimension of +-- the parameter. The slice must have two null positions. To construct +-- complete subscript list for particular the routine replaces +-- the first null position of the slice by the corresponding (or +-- , if the flag tr is on) and the second null position by the +-- corresponding (or by , if the flag tr is on). */ + +void tabular_format +( MPL *mpl, + PARAMETER *par, /* not changed */ + SLICE *slice, /* not changed */ + int tr +) +{ SLICE *list, *col, *temp; + TUPLE *tuple; + SYMBOL *row; + xassert(par != NULL); + xassert(par->dim == slice_dimen(mpl, slice)); + xassert(slice_arity(mpl, slice) == 2); + /* read the table heading that contains column symbols (the table + may have no columns) */ + list = create_slice(mpl); + while (mpl->token != T_ASSIGN) + { /* read column symbol and append it to the column list */ + if (!is_symbol(mpl)) + error(mpl, "number, symbol, or := missing where expected"); + list = expand_slice(mpl, list, read_symbol(mpl)); + } + get_token(mpl /* := */); + /* read zero or more rows that contain tabular data */ + while (is_symbol(mpl)) + { /* read row symbol (if the table has no columns, these symbols + are just ignored) */ + row = read_symbol(mpl); + /* read values accordingly to the column list */ + for (col = list; col != NULL; col = col->next) + { int which = 0; + /* if the token is single point, no value is provided */ + if (is_literal(mpl, ".")) + { get_token(mpl /* . */); + continue; + } + /* construct complete subscript list */ + tuple = create_tuple(mpl); + for (temp = slice; temp != NULL; temp = temp->next) + { if (temp->sym == NULL) + { /* substitution is needed */ + switch (++which) + { case 1: + /* substitute in the first null position */ + tuple = expand_tuple(mpl, tuple, + copy_symbol(mpl, tr ? col->sym : row)); + break; + case 2: + /* substitute in the second null position */ + tuple = expand_tuple(mpl, tuple, + copy_symbol(mpl, tr ? row : col->sym)); + break; + default: + xassert(which != which); + } + } + else + { /* copy symbol from the slice */ + tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, + temp->sym)); + } + } + xassert(which == 2); + /* read value and assign it to new parameter member */ + if (!is_symbol(mpl)) + { int lack = slice_dimen(mpl, col); + if (lack == 1) + error(mpl, "one item missing in data group beginning " + "with %s", format_symbol(mpl, row)); + else + error(mpl, "%d items missing in data group beginning " + "with %s", lack, format_symbol(mpl, row)); + } + read_value(mpl, par, tuple); + } + /* delete the row symbol */ + delete_symbol(mpl, row); + } + /* delete the column list */ + delete_slice(mpl, list); + return; +} + +/*---------------------------------------------------------------------- +-- tabbing_format - read parameter data block in tabbing format. +-- +-- This routine reads parameter data block using the syntax: +-- +-- ::= , ... , , := , +-- , ... , , , ... , , +-- , ... , , , ... , , +-- . . . . . . . . . . . . . . . . . +-- , ... , , , ... , +-- ::= +-- ::= : +-- +-- where are names of parameters (all the parameters must be +-- subscripted and have identical dimensions), are symbols +-- used to define subscripts of parameter members, are numeric +-- or symbolic values assigned to the corresponding parameter members. +-- Optional may specify a simple set, in which case n-tuples +-- built of for each row of the data table (i.e. subscripts +-- of parameter members) are added to the specified set. Commae between +-- data items are optional and may be omitted anywhere. +-- +-- If the parameter altval is not NULL, it specifies a default value +-- provided for all the parameters specified in the data block. */ + +void tabbing_format +( MPL *mpl, + SYMBOL *altval /* not changed */ +) +{ SET *set = NULL; + PARAMETER *par; + SLICE *list, *col; + TUPLE *tuple; + int next_token, j, dim = 0; + char *last_name = NULL; + /* read the optional */ + if (is_symbol(mpl)) + { get_token(mpl /* */); + next_token = mpl->token; + unget_token(mpl /* */); + if (next_token == T_COLON) + { /* select the set to saturate it with data */ + set = select_set(mpl, mpl->image); + /* the set must be simple (i.e. not set of sets) */ + if (set->dim != 0) + error(mpl, "%s must be a simple set", set->name); + /* and must not be defined yet */ + if (set->array->head != NULL) + error(mpl, "%s already defined", set->name); + /* add new (the only) member to the set and assign it empty + elemental set */ + add_member(mpl, set->array, NULL)->value.set = + create_elemset(mpl, set->dimen); + last_name = set->name, dim = set->dimen; + get_token(mpl /* */); + xassert(mpl->token == T_COLON); + get_token(mpl /* : */); + } + } + /* read the table heading that contains parameter names */ + list = create_slice(mpl); + while (mpl->token != T_ASSIGN) + { /* there must be symbolic name of parameter */ + if (!is_symbol(mpl)) + error(mpl, "parameter name or := missing where expected"); + /* select the parameter to saturate it with data */ + par = select_parameter(mpl, mpl->image); + /* the parameter must be subscripted */ + if (par->dim == 0) + error(mpl, "%s not a subscripted parameter", mpl->image); + /* the set (if specified) and all the parameters in the data + block must have identical dimension */ + if (dim != 0 && par->dim != dim) + { xassert(last_name != NULL); + error(mpl, "%s has dimension %d while %s has dimension %d", + last_name, dim, par->name, par->dim); + } + /* set default value for the parameter (if specified) */ + if (altval != NULL) + set_default(mpl, par, copy_symbol(mpl, altval)); + /* append the parameter to the column list */ + list = expand_slice(mpl, list, (SYMBOL *)par); + last_name = par->name, dim = par->dim; + get_token(mpl /* */); + /* skip optional comma */ + if (mpl->token == T_COMMA) get_token(mpl /* , */); + } + if (slice_dimen(mpl, list) == 0) + error(mpl, "at least one parameter name required"); + get_token(mpl /* := */); + /* skip optional comma */ + if (mpl->token == T_COMMA) get_token(mpl /* , */); + /* read rows that contain tabbing data */ + while (is_symbol(mpl)) + { /* read subscript list */ + tuple = create_tuple(mpl); + for (j = 1; j <= dim; j++) + { /* read j-th subscript */ + if (!is_symbol(mpl)) + { int lack = slice_dimen(mpl, list) + dim - j + 1; + xassert(tuple != NULL); + xassert(lack > 1); + error(mpl, "%d items missing in data group beginning wit" + "h %s", lack, format_symbol(mpl, tuple->sym)); + } + /* read and append j-th subscript to the n-tuple */ + tuple = expand_tuple(mpl, tuple, read_symbol(mpl)); + /* skip optional comma *between* */ + if (j < dim && mpl->token == T_COMMA) + get_token(mpl /* , */); + } + /* if the set is specified, add to it new n-tuple, which is a + copy of the subscript list just read */ + if (set != NULL) + check_then_add(mpl, set->array->head->value.set, + copy_tuple(mpl, tuple)); + /* skip optional comma between and */ + if (mpl->token == T_COMMA) get_token(mpl /* , */); + /* read values accordingly to the column list */ + for (col = list; col != NULL; col = col->next) + { /* if the token is single point, no value is provided */ + if (is_literal(mpl, ".")) + { get_token(mpl /* . */); + continue; + } + /* read value and assign it to new parameter member */ + if (!is_symbol(mpl)) + { int lack = slice_dimen(mpl, col); + xassert(tuple != NULL); + if (lack == 1) + error(mpl, "one item missing in data group beginning " + "with %s", format_symbol(mpl, tuple->sym)); + else + error(mpl, "%d items missing in data group beginning " + "with %s", lack, format_symbol(mpl, tuple->sym)); + } + read_value(mpl, (PARAMETER *)col->sym, copy_tuple(mpl, + tuple)); + /* skip optional comma preceding the next value */ + if (col->next != NULL && mpl->token == T_COMMA) + get_token(mpl /* , */); + } + /* delete the original subscript list */ + delete_tuple(mpl, tuple); + /* skip optional comma (only if there is next data group) */ + if (mpl->token == T_COMMA) + { get_token(mpl /* , */); + if (!is_symbol(mpl)) unget_token(mpl /* , */); + } + } + /* delete the column list (it contains parameters, not symbols, + so nullify it before) */ + for (col = list; col != NULL; col = col->next) col->sym = NULL; + delete_slice(mpl, list); + return; +} + +/*---------------------------------------------------------------------- +-- parameter_data - read parameter data. +-- +-- This routine reads parameter data using the syntax: +-- +-- ::= param : ; +-- ::= param +-- ; +-- ::= +-- ::= +-- ::= default +-- ::= +-- ::= , := +-- ::= , [ ] +-- ::= , +-- ::= , : +-- ::= , (tr) +-- ::= , (tr) : +-- +-- Commae in are optional and may be omitted anywhere. */ + +void parameter_data(MPL *mpl) +{ PARAMETER *par; + SYMBOL *altval = NULL; + SLICE *slice; + int tr = 0; + xassert(is_literal(mpl, "param")); + get_token(mpl /* param */); + /* read optional default value */ + if (is_literal(mpl, "default")) + { get_token(mpl /* default */); + if (!is_symbol(mpl)) + error(mpl, "default value missing where expected"); + altval = read_symbol(mpl); + /* if the default value follows the keyword 'param', the next + token must be only the colon */ + if (mpl->token != T_COLON) + error(mpl, "colon missing where expected"); + } + /* being used after the keyword 'param' or the optional default + value the colon begins data in the tabbing format */ + if (mpl->token == T_COLON) + { get_token(mpl /* : */); + /* skip optional comma */ + if (mpl->token == T_COMMA) get_token(mpl /* , */); + /* read parameter data in the tabbing format */ + tabbing_format(mpl, altval); + /* on reading data in the tabbing format the default value is + always copied, so delete the original symbol */ + if (altval != NULL) delete_symbol(mpl, altval); + /* the next token must be only semicolon */ + if (mpl->token != T_SEMICOLON) + error(mpl, "symbol, number, or semicolon missing where expe" + "cted"); + get_token(mpl /* ; */); + goto done; + } + /* in other cases there must be symbolic name of parameter, which + follows the keyword 'param' */ + if (!is_symbol(mpl)) + error(mpl, "parameter name missing where expected"); + /* select the parameter to saturate it with data */ + par = select_parameter(mpl, mpl->image); + get_token(mpl /* */); + /* read optional default value */ + if (is_literal(mpl, "default")) + { get_token(mpl /* default */); + if (!is_symbol(mpl)) + error(mpl, "default value missing where expected"); + altval = read_symbol(mpl); + /* set default value for the parameter */ + set_default(mpl, par, altval); + } + /* create initial fake slice of all asterisks */ + slice = fake_slice(mpl, par->dim); + /* read zero or more data assignments */ + for (;;) + { /* skip optional comma */ + if (mpl->token == T_COMMA) get_token(mpl /* , */); + /* process current assignment */ + if (mpl->token == T_ASSIGN) + { /* assignment ligature is non-significant element */ + get_token(mpl /* := */); + } + else if (mpl->token == T_LBRACKET) + { /* left bracket begins new slice; delete the current slice + and read new one */ + delete_slice(mpl, slice); + slice = read_slice(mpl, par->name, par->dim); + /* each new slice resets the "transpose" indicator */ + tr = 0; + } + else if (is_symbol(mpl)) + { /* number or symbol begins data in the plain format */ + plain_format(mpl, par, slice); + } + else if (mpl->token == T_COLON) + { /* colon begins data in the tabular format */ + if (par->dim == 0) +err1: error(mpl, "%s not a subscripted parameter", + par->name); + if (slice_arity(mpl, slice) != 2) +err2: error(mpl, "slice currently used must specify 2 asterisk" + "s, not %d", slice_arity(mpl, slice)); + get_token(mpl /* : */); + /* read parameter data in the tabular format */ + tabular_format(mpl, par, slice, tr); + } + else if (mpl->token == T_LEFT) + { /* left parenthesis begins the "transpose" indicator, which + is followed by data in the tabular format */ + get_token(mpl /* ( */); + if (!is_literal(mpl, "tr")) +err3: error(mpl, "transpose indicator (tr) incomplete"); + if (par->dim == 0) goto err1; + if (slice_arity(mpl, slice) != 2) goto err2; + get_token(mpl /* tr */); + if (mpl->token != T_RIGHT) goto err3; + get_token(mpl /* ) */); + /* in this case the colon is optional */ + if (mpl->token == T_COLON) get_token(mpl /* : */); + /* set the "transpose" indicator */ + tr = 1; + /* read parameter data in the tabular format */ + tabular_format(mpl, par, slice, tr); + } + else if (mpl->token == T_SEMICOLON) + { /* semicolon terminates the data block */ + get_token(mpl /* ; */); + break; + } + else + error(mpl, "syntax error in parameter data block"); + } + /* delete the current slice */ + delete_slice(mpl, slice); +done: return; +} + +/*---------------------------------------------------------------------- +-- data_section - read data section. +-- +-- This routine reads data section using the syntax: +-- +-- ::= +-- ::= ; +-- ::= +-- ::= +-- +-- Reading data section is terminated by either the keyword 'end' or +-- the end of file. */ + +void data_section(MPL *mpl) +{ while (!(mpl->token == T_EOF || is_literal(mpl, "end"))) + { if (is_literal(mpl, "set")) + set_data(mpl); + else if (is_literal(mpl, "param")) + parameter_data(mpl); + else + error(mpl, "syntax error in data section"); + } + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmpl03.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmpl03.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,6078 @@ +/* glpmpl03.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_ERRNO +#define _GLPSTD_STDIO +#include "glpenv.h" +#include "glpmpl.h" + +/**********************************************************************/ +/* * * FLOATING-POINT NUMBERS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- fp_add - floating-point addition. +-- +-- This routine computes the sum x + y. */ + +double fp_add(MPL *mpl, double x, double y) +{ if (x > 0.0 && y > 0.0 && x > + 0.999 * DBL_MAX - y || + x < 0.0 && y < 0.0 && x < - 0.999 * DBL_MAX - y) + error(mpl, "%.*g + %.*g; floating-point overflow", + DBL_DIG, x, DBL_DIG, y); + return x + y; +} + +/*---------------------------------------------------------------------- +-- fp_sub - floating-point subtraction. +-- +-- This routine computes the difference x - y. */ + +double fp_sub(MPL *mpl, double x, double y) +{ if (x > 0.0 && y < 0.0 && x > + 0.999 * DBL_MAX + y || + x < 0.0 && y > 0.0 && x < - 0.999 * DBL_MAX + y) + error(mpl, "%.*g - %.*g; floating-point overflow", + DBL_DIG, x, DBL_DIG, y); + return x - y; +} + +/*---------------------------------------------------------------------- +-- fp_less - floating-point non-negative subtraction. +-- +-- This routine computes the non-negative difference max(0, x - y). */ + +double fp_less(MPL *mpl, double x, double y) +{ if (x < y) return 0.0; + if (x > 0.0 && y < 0.0 && x > + 0.999 * DBL_MAX + y) + error(mpl, "%.*g less %.*g; floating-point overflow", + DBL_DIG, x, DBL_DIG, y); + return x - y; +} + +/*---------------------------------------------------------------------- +-- fp_mul - floating-point multiplication. +-- +-- This routine computes the product x * y. */ + +double fp_mul(MPL *mpl, double x, double y) +{ if (fabs(y) > 1.0 && fabs(x) > (0.999 * DBL_MAX) / fabs(y)) + error(mpl, "%.*g * %.*g; floating-point overflow", + DBL_DIG, x, DBL_DIG, y); + return x * y; +} + +/*---------------------------------------------------------------------- +-- fp_div - floating-point division. +-- +-- This routine computes the quotient x / y. */ + +double fp_div(MPL *mpl, double x, double y) +{ if (fabs(y) < DBL_MIN) + error(mpl, "%.*g / %.*g; floating-point zero divide", + DBL_DIG, x, DBL_DIG, y); + if (fabs(y) < 1.0 && fabs(x) > (0.999 * DBL_MAX) * fabs(y)) + error(mpl, "%.*g / %.*g; floating-point overflow", + DBL_DIG, x, DBL_DIG, y); + return x / y; +} + +/*---------------------------------------------------------------------- +-- fp_idiv - floating-point quotient of exact division. +-- +-- This routine computes the quotient of exact division x div y. */ + +double fp_idiv(MPL *mpl, double x, double y) +{ if (fabs(y) < DBL_MIN) + error(mpl, "%.*g div %.*g; floating-point zero divide", + DBL_DIG, x, DBL_DIG, y); + if (fabs(y) < 1.0 && fabs(x) > (0.999 * DBL_MAX) * fabs(y)) + error(mpl, "%.*g div %.*g; floating-point overflow", + DBL_DIG, x, DBL_DIG, y); + x /= y; + return x > 0.0 ? floor(x) : x < 0.0 ? ceil(x) : 0.0; +} + +/*---------------------------------------------------------------------- +-- fp_mod - floating-point remainder of exact division. +-- +-- This routine computes the remainder of exact division x mod y. +-- +-- NOTE: By definition x mod y = x - y * floor(x / y). */ + +double fp_mod(MPL *mpl, double x, double y) +{ double r; + xassert(mpl == mpl); + if (x == 0.0) + r = 0.0; + else if (y == 0.0) + r = x; + else + { r = fmod(fabs(x), fabs(y)); + if (r != 0.0) + { if (x < 0.0) r = - r; + if (x > 0.0 && y < 0.0 || x < 0.0 && y > 0.0) r += y; + } + } + return r; +} + +/*---------------------------------------------------------------------- +-- fp_power - floating-point exponentiation (raise to power). +-- +-- This routine computes the exponentiation x ** y. */ + +double fp_power(MPL *mpl, double x, double y) +{ double r; + if (x == 0.0 && y <= 0.0 || x < 0.0 && y != floor(y)) + error(mpl, "%.*g ** %.*g; result undefined", + DBL_DIG, x, DBL_DIG, y); + if (x == 0.0) goto eval; + if (fabs(x) > 1.0 && y > +1.0 && + +log(fabs(x)) > (0.999 * log(DBL_MAX)) / y || + fabs(x) < 1.0 && y < -1.0 && + +log(fabs(x)) < (0.999 * log(DBL_MAX)) / y) + error(mpl, "%.*g ** %.*g; floating-point overflow", + DBL_DIG, x, DBL_DIG, y); + if (fabs(x) > 1.0 && y < -1.0 && + -log(fabs(x)) < (0.999 * log(DBL_MAX)) / y || + fabs(x) < 1.0 && y > +1.0 && + -log(fabs(x)) > (0.999 * log(DBL_MAX)) / y) + r = 0.0; + else +eval: r = pow(x, y); + return r; +} + +/*---------------------------------------------------------------------- +-- fp_exp - floating-point base-e exponential. +-- +-- This routine computes the base-e exponential e ** x. */ + +double fp_exp(MPL *mpl, double x) +{ if (x > 0.999 * log(DBL_MAX)) + error(mpl, "exp(%.*g); floating-point overflow", DBL_DIG, x); + return exp(x); +} + +/*---------------------------------------------------------------------- +-- fp_log - floating-point natural logarithm. +-- +-- This routine computes the natural logarithm log x. */ + +double fp_log(MPL *mpl, double x) +{ if (x <= 0.0) + error(mpl, "log(%.*g); non-positive argument", DBL_DIG, x); + return log(x); +} + +/*---------------------------------------------------------------------- +-- fp_log10 - floating-point common (decimal) logarithm. +-- +-- This routine computes the common (decimal) logarithm lg x. */ + +double fp_log10(MPL *mpl, double x) +{ if (x <= 0.0) + error(mpl, "log10(%.*g); non-positive argument", DBL_DIG, x); + return log10(x); +} + +/*---------------------------------------------------------------------- +-- fp_sqrt - floating-point square root. +-- +-- This routine computes the square root x ** 0.5. */ + +double fp_sqrt(MPL *mpl, double x) +{ if (x < 0.0) + error(mpl, "sqrt(%.*g); negative argument", DBL_DIG, x); + return sqrt(x); +} + +/*---------------------------------------------------------------------- +-- fp_sin - floating-point trigonometric sine. +-- +-- This routine computes the trigonometric sine sin(x). */ + +double fp_sin(MPL *mpl, double x) +{ if (!(-1e6 <= x && x <= +1e6)) + error(mpl, "sin(%.*g); argument too large", DBL_DIG, x); + return sin(x); +} + +/*---------------------------------------------------------------------- +-- fp_cos - floating-point trigonometric cosine. +-- +-- This routine computes the trigonometric cosine cos(x). */ + +double fp_cos(MPL *mpl, double x) +{ if (!(-1e6 <= x && x <= +1e6)) + error(mpl, "cos(%.*g); argument too large", DBL_DIG, x); + return cos(x); +} + +/*---------------------------------------------------------------------- +-- fp_atan - floating-point trigonometric arctangent. +-- +-- This routine computes the trigonometric arctangent atan(x). */ + +double fp_atan(MPL *mpl, double x) +{ xassert(mpl == mpl); + return atan(x); +} + +/*---------------------------------------------------------------------- +-- fp_atan2 - floating-point trigonometric arctangent. +-- +-- This routine computes the trigonometric arctangent atan(y / x). */ + +double fp_atan2(MPL *mpl, double y, double x) +{ xassert(mpl == mpl); + return atan2(y, x); +} + +/*---------------------------------------------------------------------- +-- fp_round - round floating-point value to n fractional digits. +-- +-- This routine rounds given floating-point value x to n fractional +-- digits with the formula: +-- +-- round(x, n) = floor(x * 10^n + 0.5) / 10^n. +-- +-- The parameter n is assumed to be integer. */ + +double fp_round(MPL *mpl, double x, double n) +{ double ten_to_n; + if (n != floor(n)) + error(mpl, "round(%.*g, %.*g); non-integer second argument", + DBL_DIG, x, DBL_DIG, n); + if (n <= DBL_DIG + 2) + { ten_to_n = pow(10.0, n); + if (fabs(x) < (0.999 * DBL_MAX) / ten_to_n) + { x = floor(x * ten_to_n + 0.5); + if (x != 0.0) x /= ten_to_n; + } + } + return x; +} + +/*---------------------------------------------------------------------- +-- fp_trunc - truncate floating-point value to n fractional digits. +-- +-- This routine truncates given floating-point value x to n fractional +-- digits with the formula: +-- +-- ( floor(x * 10^n) / 10^n, if x >= 0 +-- trunc(x, n) = < +-- ( ceil(x * 10^n) / 10^n, if x < 0 +-- +-- The parameter n is assumed to be integer. */ + +double fp_trunc(MPL *mpl, double x, double n) +{ double ten_to_n; + if (n != floor(n)) + error(mpl, "trunc(%.*g, %.*g); non-integer second argument", + DBL_DIG, x, DBL_DIG, n); + if (n <= DBL_DIG + 2) + { ten_to_n = pow(10.0, n); + if (fabs(x) < (0.999 * DBL_MAX) / ten_to_n) + { x = (x >= 0.0 ? floor(x * ten_to_n) : ceil(x * ten_to_n)); + if (x != 0.0) x /= ten_to_n; + } + } + return x; +} + +/**********************************************************************/ +/* * * PSEUDO-RANDOM NUMBER GENERATORS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- fp_irand224 - pseudo-random integer in the range [0, 2^24). +-- +-- This routine returns a next pseudo-random integer (converted to +-- floating-point) which is uniformly distributed between 0 and 2^24-1, +-- inclusive. */ + +#define two_to_the_24 0x1000000 + +double fp_irand224(MPL *mpl) +{ return + (double)rng_unif_rand(mpl->rand, two_to_the_24); +} + +/*---------------------------------------------------------------------- +-- fp_uniform01 - pseudo-random number in the range [0, 1). +-- +-- This routine returns a next pseudo-random number which is uniformly +-- distributed in the range [0, 1). */ + +#define two_to_the_31 ((unsigned int)0x80000000) + +double fp_uniform01(MPL *mpl) +{ return + (double)rng_next_rand(mpl->rand) / (double)two_to_the_31; +} + +/*---------------------------------------------------------------------- +-- fp_uniform - pseudo-random number in the range [a, b). +-- +-- This routine returns a next pseudo-random number which is uniformly +-- distributed in the range [a, b). */ + +double fp_uniform(MPL *mpl, double a, double b) +{ double x; + if (a >= b) + error(mpl, "Uniform(%.*g, %.*g); invalid range", + DBL_DIG, a, DBL_DIG, b); + x = fp_uniform01(mpl); +#if 0 + x = a * (1.0 - x) + b * x; +#else + x = fp_add(mpl, a * (1.0 - x), b * x); +#endif + return x; +} + +/*---------------------------------------------------------------------- +-- fp_normal01 - Gaussian random variate with mu = 0 and sigma = 1. +-- +-- This routine returns a Gaussian random variate with zero mean and +-- unit standard deviation. The polar (Box-Mueller) method is used. +-- +-- This code is a modified version of the routine gsl_ran_gaussian from +-- the GNU Scientific Library Version 1.0. */ + +double fp_normal01(MPL *mpl) +{ double x, y, r2; + do + { /* choose x, y in uniform square (-1,-1) to (+1,+1) */ + x = -1.0 + 2.0 * fp_uniform01(mpl); + y = -1.0 + 2.0 * fp_uniform01(mpl); + /* see if it is in the unit circle */ + r2 = x * x + y * y; + } while (r2 > 1.0 || r2 == 0.0); + /* Box-Muller transform */ + return y * sqrt(-2.0 * log (r2) / r2); +} + +/*---------------------------------------------------------------------- +-- fp_normal - Gaussian random variate with specified mu and sigma. +-- +-- This routine returns a Gaussian random variate with mean mu and +-- standard deviation sigma. */ + +double fp_normal(MPL *mpl, double mu, double sigma) +{ double x; +#if 0 + x = mu + sigma * fp_normal01(mpl); +#else + x = fp_add(mpl, mu, fp_mul(mpl, sigma, fp_normal01(mpl))); +#endif + return x; +} + +/**********************************************************************/ +/* * * SEGMENTED CHARACTER STRINGS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- create_string - create character string. +-- +-- This routine creates a segmented character string, which is exactly +-- equivalent to specified character string. */ + +STRING *create_string +( MPL *mpl, + char buf[MAX_LENGTH+1] /* not changed */ +) +#if 0 +{ STRING *head, *tail; + int i, j; + xassert(buf != NULL); + xassert(strlen(buf) <= MAX_LENGTH); + head = tail = dmp_get_atom(mpl->strings, sizeof(STRING)); + for (i = j = 0; ; i++) + { if ((tail->seg[j++] = buf[i]) == '\0') break; + if (j == STRSEG_SIZE) +tail = (tail->next = dmp_get_atom(mpl->strings, sizeof(STRING))), j = 0; + } + tail->next = NULL; + return head; +} +#else +{ STRING *str; + xassert(strlen(buf) <= MAX_LENGTH); + str = dmp_get_atom(mpl->strings, strlen(buf)+1); + strcpy(str, buf); + return str; +} +#endif + +/*---------------------------------------------------------------------- +-- copy_string - make copy of character string. +-- +-- This routine returns an exact copy of segmented character string. */ + +STRING *copy_string +( MPL *mpl, + STRING *str /* not changed */ +) +#if 0 +{ STRING *head, *tail; + xassert(str != NULL); + head = tail = dmp_get_atom(mpl->strings, sizeof(STRING)); + for (; str != NULL; str = str->next) + { memcpy(tail->seg, str->seg, STRSEG_SIZE); + if (str->next != NULL) +tail = (tail->next = dmp_get_atom(mpl->strings, sizeof(STRING))); + } + tail->next = NULL; + return head; +} +#else +{ xassert(mpl == mpl); + return create_string(mpl, str); +} +#endif + +/*---------------------------------------------------------------------- +-- compare_strings - compare one character string with another. +-- +-- This routine compares one segmented character strings with another +-- and returns the result of comparison as follows: +-- +-- = 0 - both strings are identical; +-- < 0 - the first string precedes the second one; +-- > 0 - the first string follows the second one. */ + +int compare_strings +( MPL *mpl, + STRING *str1, /* not changed */ + STRING *str2 /* not changed */ +) +#if 0 +{ int j, c1, c2; + xassert(mpl == mpl); + for (;; str1 = str1->next, str2 = str2->next) + { xassert(str1 != NULL); + xassert(str2 != NULL); + for (j = 0; j < STRSEG_SIZE; j++) + { c1 = (unsigned char)str1->seg[j]; + c2 = (unsigned char)str2->seg[j]; + if (c1 < c2) return -1; + if (c1 > c2) return +1; + if (c1 == '\0') goto done; + } + } +done: return 0; +} +#else +{ xassert(mpl == mpl); + return strcmp(str1, str2); +} +#endif + +/*---------------------------------------------------------------------- +-- fetch_string - extract content of character string. +-- +-- This routine returns a character string, which is exactly equivalent +-- to specified segmented character string. */ + +char *fetch_string +( MPL *mpl, + STRING *str, /* not changed */ + char buf[MAX_LENGTH+1] /* modified */ +) +#if 0 +{ int i, j; + xassert(mpl == mpl); + xassert(buf != NULL); + for (i = 0; ; str = str->next) + { xassert(str != NULL); + for (j = 0; j < STRSEG_SIZE; j++) + if ((buf[i++] = str->seg[j]) == '\0') goto done; + } +done: xassert(strlen(buf) <= MAX_LENGTH); + return buf; +} +#else +{ xassert(mpl == mpl); + return strcpy(buf, str); +} +#endif + +/*---------------------------------------------------------------------- +-- delete_string - delete character string. +-- +-- This routine deletes specified segmented character string. */ + +void delete_string +( MPL *mpl, + STRING *str /* destroyed */ +) +#if 0 +{ STRING *temp; + xassert(str != NULL); + while (str != NULL) + { temp = str; + str = str->next; + dmp_free_atom(mpl->strings, temp, sizeof(STRING)); + } + return; +} +#else +{ dmp_free_atom(mpl->strings, str, strlen(str)+1); + return; +} +#endif + +/**********************************************************************/ +/* * * SYMBOLS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- create_symbol_num - create symbol of numeric type. +-- +-- This routine creates a symbol, which has a numeric value specified +-- as floating-point number. */ + +SYMBOL *create_symbol_num(MPL *mpl, double num) +{ SYMBOL *sym; + sym = dmp_get_atom(mpl->symbols, sizeof(SYMBOL)); + sym->num = num; + sym->str = NULL; + return sym; +} + +/*---------------------------------------------------------------------- +-- create_symbol_str - create symbol of abstract type. +-- +-- This routine creates a symbol, which has an abstract value specified +-- as segmented character string. */ + +SYMBOL *create_symbol_str +( MPL *mpl, + STRING *str /* destroyed */ +) +{ SYMBOL *sym; + xassert(str != NULL); + sym = dmp_get_atom(mpl->symbols, sizeof(SYMBOL)); + sym->num = 0.0; + sym->str = str; + return sym; +} + +/*---------------------------------------------------------------------- +-- copy_symbol - make copy of symbol. +-- +-- This routine returns an exact copy of symbol. */ + +SYMBOL *copy_symbol +( MPL *mpl, + SYMBOL *sym /* not changed */ +) +{ SYMBOL *copy; + xassert(sym != NULL); + copy = dmp_get_atom(mpl->symbols, sizeof(SYMBOL)); + if (sym->str == NULL) + { copy->num = sym->num; + copy->str = NULL; + } + else + { copy->num = 0.0; + copy->str = copy_string(mpl, sym->str); + } + return copy; +} + +/*---------------------------------------------------------------------- +-- compare_symbols - compare one symbol with another. +-- +-- This routine compares one symbol with another and returns the result +-- of comparison as follows: +-- +-- = 0 - both symbols are identical; +-- < 0 - the first symbol precedes the second one; +-- > 0 - the first symbol follows the second one. +-- +-- Note that the linear order, in which symbols follow each other, is +-- implementation-dependent. It may be not an alphabetical order. */ + +int compare_symbols +( MPL *mpl, + SYMBOL *sym1, /* not changed */ + SYMBOL *sym2 /* not changed */ +) +{ xassert(sym1 != NULL); + xassert(sym2 != NULL); + /* let all numeric quantities precede all symbolic quantities */ + if (sym1->str == NULL && sym2->str == NULL) + { if (sym1->num < sym2->num) return -1; + if (sym1->num > sym2->num) return +1; + return 0; + } + if (sym1->str == NULL) return -1; + if (sym2->str == NULL) return +1; + return compare_strings(mpl, sym1->str, sym2->str); +} + +/*---------------------------------------------------------------------- +-- delete_symbol - delete symbol. +-- +-- This routine deletes specified symbol. */ + +void delete_symbol +( MPL *mpl, + SYMBOL *sym /* destroyed */ +) +{ xassert(sym != NULL); + if (sym->str != NULL) delete_string(mpl, sym->str); + dmp_free_atom(mpl->symbols, sym, sizeof(SYMBOL)); + return; +} + +/*---------------------------------------------------------------------- +-- format_symbol - format symbol for displaying or printing. +-- +-- This routine converts specified symbol to a charater string, which +-- is suitable for displaying or printing. +-- +-- The resultant string is never longer than 255 characters. If it gets +-- longer, it is truncated from the right and appended by dots. */ + +char *format_symbol +( MPL *mpl, + SYMBOL *sym /* not changed */ +) +{ char *buf = mpl->sym_buf; + xassert(sym != NULL); + if (sym->str == NULL) + sprintf(buf, "%.*g", DBL_DIG, sym->num); + else + { char str[MAX_LENGTH+1]; + int quoted, j, len; + fetch_string(mpl, sym->str, str); + if (!(isalpha((unsigned char)str[0]) || str[0] == '_')) + quoted = 1; + else + { quoted = 0; + for (j = 1; str[j] != '\0'; j++) + { if (!(isalnum((unsigned char)str[j]) || + strchr("+-._", (unsigned char)str[j]) != NULL)) + { quoted = 1; + break; + } + } + } +# define safe_append(c) \ + (void)(len < 255 ? (buf[len++] = (char)(c)) : 0) + buf[0] = '\0', len = 0; + if (quoted) safe_append('\''); + for (j = 0; str[j] != '\0'; j++) + { if (quoted && str[j] == '\'') safe_append('\''); + safe_append(str[j]); + } + if (quoted) safe_append('\''); +# undef safe_append + buf[len] = '\0'; + if (len == 255) strcpy(buf+252, "..."); + } + xassert(strlen(buf) <= 255); + return buf; +} + +/*---------------------------------------------------------------------- +-- concat_symbols - concatenate one symbol with another. +-- +-- This routine concatenates values of two given symbols and assigns +-- the resultant character string to a new symbol, which is returned on +-- exit. Both original symbols are destroyed. */ + +SYMBOL *concat_symbols +( MPL *mpl, + SYMBOL *sym1, /* destroyed */ + SYMBOL *sym2 /* destroyed */ +) +{ char str1[MAX_LENGTH+1], str2[MAX_LENGTH+1]; + xassert(MAX_LENGTH >= DBL_DIG + DBL_DIG); + if (sym1->str == NULL) + sprintf(str1, "%.*g", DBL_DIG, sym1->num); + else + fetch_string(mpl, sym1->str, str1); + if (sym2->str == NULL) + sprintf(str2, "%.*g", DBL_DIG, sym2->num); + else + fetch_string(mpl, sym2->str, str2); + if (strlen(str1) + strlen(str2) > MAX_LENGTH) + { char buf[255+1]; + strcpy(buf, format_symbol(mpl, sym1)); + xassert(strlen(buf) < sizeof(buf)); + error(mpl, "%s & %s; resultant symbol exceeds %d characters", + buf, format_symbol(mpl, sym2), MAX_LENGTH); + } + delete_symbol(mpl, sym1); + delete_symbol(mpl, sym2); + return create_symbol_str(mpl, create_string(mpl, strcat(str1, + str2))); +} + +/**********************************************************************/ +/* * * N-TUPLES * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- create_tuple - create n-tuple. +-- +-- This routine creates a n-tuple, which initially has no components, +-- i.e. which is 0-tuple. */ + +TUPLE *create_tuple(MPL *mpl) +{ TUPLE *tuple; + xassert(mpl == mpl); + tuple = NULL; + return tuple; +} + +/*---------------------------------------------------------------------- +-- expand_tuple - append symbol to n-tuple. +-- +-- This routine expands n-tuple appending to it a given symbol, which +-- becomes its new last component. */ + +TUPLE *expand_tuple +( MPL *mpl, + TUPLE *tuple, /* destroyed */ + SYMBOL *sym /* destroyed */ +) +{ TUPLE *tail, *temp; + xassert(sym != NULL); + /* create a new component */ + tail = dmp_get_atom(mpl->tuples, sizeof(TUPLE)); + tail->sym = sym; + tail->next = NULL; + /* and append it to the component list */ + if (tuple == NULL) + tuple = tail; + else + { for (temp = tuple; temp->next != NULL; temp = temp->next); + temp->next = tail; + } + return tuple; +} + +/*---------------------------------------------------------------------- +-- tuple_dimen - determine dimension of n-tuple. +-- +-- This routine returns dimension of n-tuple, i.e. number of components +-- in the n-tuple. */ + +int tuple_dimen +( MPL *mpl, + TUPLE *tuple /* not changed */ +) +{ TUPLE *temp; + int dim = 0; + xassert(mpl == mpl); + for (temp = tuple; temp != NULL; temp = temp->next) dim++; + return dim; +} + +/*---------------------------------------------------------------------- +-- copy_tuple - make copy of n-tuple. +-- +-- This routine returns an exact copy of n-tuple. */ + +TUPLE *copy_tuple +( MPL *mpl, + TUPLE *tuple /* not changed */ +) +{ TUPLE *head, *tail; + if (tuple == NULL) + head = NULL; + else + { head = tail = dmp_get_atom(mpl->tuples, sizeof(TUPLE)); + for (; tuple != NULL; tuple = tuple->next) + { xassert(tuple->sym != NULL); + tail->sym = copy_symbol(mpl, tuple->sym); + if (tuple->next != NULL) +tail = (tail->next = dmp_get_atom(mpl->tuples, sizeof(TUPLE))); + } + tail->next = NULL; + } + return head; +} + +/*---------------------------------------------------------------------- +-- compare_tuples - compare one n-tuple with another. +-- +-- This routine compares two given n-tuples, which must have the same +-- dimension (not checked for the sake of efficiency), and returns one +-- of the following codes: +-- +-- = 0 - both n-tuples are identical; +-- < 0 - the first n-tuple precedes the second one; +-- > 0 - the first n-tuple follows the second one. +-- +-- Note that the linear order, in which n-tuples follow each other, is +-- implementation-dependent. It may be not an alphabetical order. */ + +int compare_tuples +( MPL *mpl, + TUPLE *tuple1, /* not changed */ + TUPLE *tuple2 /* not changed */ +) +{ TUPLE *item1, *item2; + int ret; + xassert(mpl == mpl); + for (item1 = tuple1, item2 = tuple2; item1 != NULL; + item1 = item1->next, item2 = item2->next) + { xassert(item2 != NULL); + xassert(item1->sym != NULL); + xassert(item2->sym != NULL); + ret = compare_symbols(mpl, item1->sym, item2->sym); + if (ret != 0) return ret; + } + xassert(item2 == NULL); + return 0; +} + +/*---------------------------------------------------------------------- +-- build_subtuple - build subtuple of given n-tuple. +-- +-- This routine builds subtuple, which consists of first dim components +-- of given n-tuple. */ + +TUPLE *build_subtuple +( MPL *mpl, + TUPLE *tuple, /* not changed */ + int dim +) +{ TUPLE *head, *temp; + int j; + head = create_tuple(mpl); + for (j = 1, temp = tuple; j <= dim; j++, temp = temp->next) + { xassert(temp != NULL); + head = expand_tuple(mpl, head, copy_symbol(mpl, temp->sym)); + } + return head; +} + +/*---------------------------------------------------------------------- +-- delete_tuple - delete n-tuple. +-- +-- This routine deletes specified n-tuple. */ + +void delete_tuple +( MPL *mpl, + TUPLE *tuple /* destroyed */ +) +{ TUPLE *temp; + while (tuple != NULL) + { temp = tuple; + tuple = temp->next; + xassert(temp->sym != NULL); + delete_symbol(mpl, temp->sym); + dmp_free_atom(mpl->tuples, temp, sizeof(TUPLE)); + } + return; +} + +/*---------------------------------------------------------------------- +-- format_tuple - format n-tuple for displaying or printing. +-- +-- This routine converts specified n-tuple to a character string, which +-- is suitable for displaying or printing. +-- +-- The resultant string is never longer than 255 characters. If it gets +-- longer, it is truncated from the right and appended by dots. */ + +char *format_tuple +( MPL *mpl, + int c, + TUPLE *tuple /* not changed */ +) +{ TUPLE *temp; + int dim, j, len; + char *buf = mpl->tup_buf, str[255+1], *save; +# define safe_append(c) \ + (void)(len < 255 ? (buf[len++] = (char)(c)) : 0) + buf[0] = '\0', len = 0; + dim = tuple_dimen(mpl, tuple); + if (c == '[' && dim > 0) safe_append('['); + if (c == '(' && dim > 1) safe_append('('); + for (temp = tuple; temp != NULL; temp = temp->next) + { if (temp != tuple) safe_append(','); + xassert(temp->sym != NULL); + save = mpl->sym_buf; + mpl->sym_buf = str; + format_symbol(mpl, temp->sym); + mpl->sym_buf = save; + xassert(strlen(str) < sizeof(str)); + for (j = 0; str[j] != '\0'; j++) safe_append(str[j]); + } + if (c == '[' && dim > 0) safe_append(']'); + if (c == '(' && dim > 1) safe_append(')'); +# undef safe_append + buf[len] = '\0'; + if (len == 255) strcpy(buf+252, "..."); + xassert(strlen(buf) <= 255); + return buf; +} + +/**********************************************************************/ +/* * * ELEMENTAL SETS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- create_elemset - create elemental set. +-- +-- This routine creates an elemental set, whose members are n-tuples of +-- specified dimension. Being created the set is initially empty. */ + +ELEMSET *create_elemset(MPL *mpl, int dim) +{ ELEMSET *set; + xassert(dim > 0); + set = create_array(mpl, A_NONE, dim); + return set; +} + +/*---------------------------------------------------------------------- +-- find_tuple - check if elemental set contains given n-tuple. +-- +-- This routine finds given n-tuple in specified elemental set in order +-- to check if the set contains that n-tuple. If the n-tuple is found, +-- the routine returns pointer to corresponding array member. Otherwise +-- null pointer is returned. */ + +MEMBER *find_tuple +( MPL *mpl, + ELEMSET *set, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ xassert(set != NULL); + xassert(set->type == A_NONE); + xassert(set->dim == tuple_dimen(mpl, tuple)); + return find_member(mpl, set, tuple); +} + +/*---------------------------------------------------------------------- +-- add_tuple - add new n-tuple to elemental set. +-- +-- This routine adds given n-tuple to specified elemental set. +-- +-- For the sake of efficiency this routine doesn't check whether the +-- set already contains the same n-tuple or not. Therefore the calling +-- program should use the routine find_tuple (if necessary) in order to +-- make sure that the given n-tuple is not contained in the set, since +-- duplicate n-tuples within the same set are not allowed. */ + +MEMBER *add_tuple +( MPL *mpl, + ELEMSET *set, /* modified */ + TUPLE *tuple /* destroyed */ +) +{ MEMBER *memb; + xassert(set != NULL); + xassert(set->type == A_NONE); + xassert(set->dim == tuple_dimen(mpl, tuple)); + memb = add_member(mpl, set, tuple); + memb->value.none = NULL; + return memb; +} + +/*---------------------------------------------------------------------- +-- check_then_add - check and add new n-tuple to elemental set. +-- +-- This routine is equivalent to the routine add_tuple except that it +-- does check for duplicate n-tuples. */ + +MEMBER *check_then_add +( MPL *mpl, + ELEMSET *set, /* modified */ + TUPLE *tuple /* destroyed */ +) +{ if (find_tuple(mpl, set, tuple) != NULL) + error(mpl, "duplicate tuple %s detected", format_tuple(mpl, + '(', tuple)); + return add_tuple(mpl, set, tuple); +} + +/*---------------------------------------------------------------------- +-- copy_elemset - make copy of elemental set. +-- +-- This routine makes an exact copy of elemental set. */ + +ELEMSET *copy_elemset +( MPL *mpl, + ELEMSET *set /* not changed */ +) +{ ELEMSET *copy; + MEMBER *memb; + xassert(set != NULL); + xassert(set->type == A_NONE); + xassert(set->dim > 0); + copy = create_elemset(mpl, set->dim); + for (memb = set->head; memb != NULL; memb = memb->next) + add_tuple(mpl, copy, copy_tuple(mpl, memb->tuple)); + return copy; +} + +/*---------------------------------------------------------------------- +-- delete_elemset - delete elemental set. +-- +-- This routine deletes specified elemental set. */ + +void delete_elemset +( MPL *mpl, + ELEMSET *set /* destroyed */ +) +{ xassert(set != NULL); + xassert(set->type == A_NONE); + delete_array(mpl, set); + return; +} + +/*---------------------------------------------------------------------- +-- arelset_size - compute size of "arithmetic" elemental set. +-- +-- This routine computes the size of "arithmetic" elemental set, which +-- is specified in the form of arithmetic progression: +-- +-- { t0 .. tf by dt }. +-- +-- The size is computed using the formula: +-- +-- n = max(0, floor((tf - t0) / dt) + 1). */ + +int arelset_size(MPL *mpl, double t0, double tf, double dt) +{ double temp; + if (dt == 0.0) + error(mpl, "%.*g .. %.*g by %.*g; zero stride not allowed", + DBL_DIG, t0, DBL_DIG, tf, DBL_DIG, dt); + if (tf > 0.0 && t0 < 0.0 && tf > + 0.999 * DBL_MAX + t0) + temp = +DBL_MAX; + else if (tf < 0.0 && t0 > 0.0 && tf < - 0.999 * DBL_MAX + t0) + temp = -DBL_MAX; + else + temp = tf - t0; + if (fabs(dt) < 1.0 && fabs(temp) > (0.999 * DBL_MAX) * fabs(dt)) + { if (temp > 0.0 && dt > 0.0 || temp < 0.0 && dt < 0.0) + temp = +DBL_MAX; + else + temp = 0.0; + } + else + { temp = floor(temp / dt) + 1.0; + if (temp < 0.0) temp = 0.0; + } + xassert(temp >= 0.0); + if (temp > (double)(INT_MAX - 1)) + error(mpl, "%.*g .. %.*g by %.*g; set too large", + DBL_DIG, t0, DBL_DIG, tf, DBL_DIG, dt); + return (int)(temp + 0.5); +} + +/*---------------------------------------------------------------------- +-- arelset_member - compute member of "arithmetic" elemental set. +-- +-- This routine returns a numeric value of symbol, which is equivalent +-- to j-th member of given "arithmetic" elemental set specified in the +-- form of arithmetic progression: +-- +-- { t0 .. tf by dt }. +-- +-- The symbol value is computed with the formula: +-- +-- j-th member = t0 + (j - 1) * dt, +-- +-- The number j must satisfy to the restriction 1 <= j <= n, where n is +-- the set size computed by the routine arelset_size. */ + +double arelset_member(MPL *mpl, double t0, double tf, double dt, int j) +{ xassert(1 <= j && j <= arelset_size(mpl, t0, tf, dt)); + return t0 + (double)(j - 1) * dt; +} + +/*---------------------------------------------------------------------- +-- create_arelset - create "arithmetic" elemental set. +-- +-- This routine creates "arithmetic" elemental set, which is specified +-- in the form of arithmetic progression: +-- +-- { t0 .. tf by dt }. +-- +-- Components of this set are 1-tuples. */ + +ELEMSET *create_arelset(MPL *mpl, double t0, double tf, double dt) +{ ELEMSET *set; + int j, n; + set = create_elemset(mpl, 1); + n = arelset_size(mpl, t0, tf, dt); + for (j = 1; j <= n; j++) + { add_tuple + ( mpl, + set, + expand_tuple + ( mpl, + create_tuple(mpl), + create_symbol_num + ( mpl, + arelset_member(mpl, t0, tf, dt, j) + ) + ) + ); + } + return set; +} + +/*---------------------------------------------------------------------- +-- set_union - union of two elemental sets. +-- +-- This routine computes the union: +-- +-- X U Y = { j | (j in X) or (j in Y) }, +-- +-- where X and Y are given elemental sets (destroyed on exit). */ + +ELEMSET *set_union +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +) +{ MEMBER *memb; + xassert(X != NULL); + xassert(X->type == A_NONE); + xassert(X->dim > 0); + xassert(Y != NULL); + xassert(Y->type == A_NONE); + xassert(Y->dim > 0); + xassert(X->dim == Y->dim); + for (memb = Y->head; memb != NULL; memb = memb->next) + { if (find_tuple(mpl, X, memb->tuple) == NULL) + add_tuple(mpl, X, copy_tuple(mpl, memb->tuple)); + } + delete_elemset(mpl, Y); + return X; +} + +/*---------------------------------------------------------------------- +-- set_diff - difference between two elemental sets. +-- +-- This routine computes the difference: +-- +-- X \ Y = { j | (j in X) and (j not in Y) }, +-- +-- where X and Y are given elemental sets (destroyed on exit). */ + +ELEMSET *set_diff +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +) +{ ELEMSET *Z; + MEMBER *memb; + xassert(X != NULL); + xassert(X->type == A_NONE); + xassert(X->dim > 0); + xassert(Y != NULL); + xassert(Y->type == A_NONE); + xassert(Y->dim > 0); + xassert(X->dim == Y->dim); + Z = create_elemset(mpl, X->dim); + for (memb = X->head; memb != NULL; memb = memb->next) + { if (find_tuple(mpl, Y, memb->tuple) == NULL) + add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple)); + } + delete_elemset(mpl, X); + delete_elemset(mpl, Y); + return Z; +} + +/*---------------------------------------------------------------------- +-- set_symdiff - symmetric difference between two elemental sets. +-- +-- This routine computes the symmetric difference: +-- +-- X (+) Y = (X \ Y) U (Y \ X), +-- +-- where X and Y are given elemental sets (destroyed on exit). */ + +ELEMSET *set_symdiff +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +) +{ ELEMSET *Z; + MEMBER *memb; + xassert(X != NULL); + xassert(X->type == A_NONE); + xassert(X->dim > 0); + xassert(Y != NULL); + xassert(Y->type == A_NONE); + xassert(Y->dim > 0); + xassert(X->dim == Y->dim); + /* Z := X \ Y */ + Z = create_elemset(mpl, X->dim); + for (memb = X->head; memb != NULL; memb = memb->next) + { if (find_tuple(mpl, Y, memb->tuple) == NULL) + add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple)); + } + /* Z := Z U (Y \ X) */ + for (memb = Y->head; memb != NULL; memb = memb->next) + { if (find_tuple(mpl, X, memb->tuple) == NULL) + add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple)); + } + delete_elemset(mpl, X); + delete_elemset(mpl, Y); + return Z; +} + +/*---------------------------------------------------------------------- +-- set_inter - intersection of two elemental sets. +-- +-- This routine computes the intersection: +-- +-- X ^ Y = { j | (j in X) and (j in Y) }, +-- +-- where X and Y are given elemental sets (destroyed on exit). */ + +ELEMSET *set_inter +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +) +{ ELEMSET *Z; + MEMBER *memb; + xassert(X != NULL); + xassert(X->type == A_NONE); + xassert(X->dim > 0); + xassert(Y != NULL); + xassert(Y->type == A_NONE); + xassert(Y->dim > 0); + xassert(X->dim == Y->dim); + Z = create_elemset(mpl, X->dim); + for (memb = X->head; memb != NULL; memb = memb->next) + { if (find_tuple(mpl, Y, memb->tuple) != NULL) + add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple)); + } + delete_elemset(mpl, X); + delete_elemset(mpl, Y); + return Z; +} + +/*---------------------------------------------------------------------- +-- set_cross - cross (Cartesian) product of two elemental sets. +-- +-- This routine computes the cross (Cartesian) product: +-- +-- X x Y = { (i,j) | (i in X) and (j in Y) }, +-- +-- where X and Y are given elemental sets (destroyed on exit). */ + +ELEMSET *set_cross +( MPL *mpl, + ELEMSET *X, /* destroyed */ + ELEMSET *Y /* destroyed */ +) +{ ELEMSET *Z; + MEMBER *memx, *memy; + TUPLE *tuple, *temp; + xassert(X != NULL); + xassert(X->type == A_NONE); + xassert(X->dim > 0); + xassert(Y != NULL); + xassert(Y->type == A_NONE); + xassert(Y->dim > 0); + Z = create_elemset(mpl, X->dim + Y->dim); + for (memx = X->head; memx != NULL; memx = memx->next) + { for (memy = Y->head; memy != NULL; memy = memy->next) + { tuple = copy_tuple(mpl, memx->tuple); + for (temp = memy->tuple; temp != NULL; temp = temp->next) + tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, + temp->sym)); + add_tuple(mpl, Z, tuple); + } + } + delete_elemset(mpl, X); + delete_elemset(mpl, Y); + return Z; +} + +/**********************************************************************/ +/* * * ELEMENTAL VARIABLES * * */ +/**********************************************************************/ + +/* (there are no specific routines for elemental variables) */ + +/**********************************************************************/ +/* * * LINEAR FORMS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- constant_term - create constant term. +-- +-- This routine creates the linear form, which is a constant term. */ + +FORMULA *constant_term(MPL *mpl, double coef) +{ FORMULA *form; + if (coef == 0.0) + form = NULL; + else + { form = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); + form->coef = coef; + form->var = NULL; + form->next = NULL; + } + return form; +} + +/*---------------------------------------------------------------------- +-- single_variable - create single variable. +-- +-- This routine creates the linear form, which is a single elemental +-- variable. */ + +FORMULA *single_variable +( MPL *mpl, + ELEMVAR *var /* referenced */ +) +{ FORMULA *form; + xassert(var != NULL); + form = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); + form->coef = 1.0; + form->var = var; + form->next = NULL; + return form; +} + +/*---------------------------------------------------------------------- +-- copy_formula - make copy of linear form. +-- +-- This routine returns an exact copy of linear form. */ + +FORMULA *copy_formula +( MPL *mpl, + FORMULA *form /* not changed */ +) +{ FORMULA *head, *tail; + if (form == NULL) + head = NULL; + else + { head = tail = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); + for (; form != NULL; form = form->next) + { tail->coef = form->coef; + tail->var = form->var; + if (form->next != NULL) +tail = (tail->next = dmp_get_atom(mpl->formulae, sizeof(FORMULA))); + } + tail->next = NULL; + } + return head; +} + +/*---------------------------------------------------------------------- +-- delete_formula - delete linear form. +-- +-- This routine deletes specified linear form. */ + +void delete_formula +( MPL *mpl, + FORMULA *form /* destroyed */ +) +{ FORMULA *temp; + while (form != NULL) + { temp = form; + form = form->next; + dmp_free_atom(mpl->formulae, temp, sizeof(FORMULA)); + } + return; +} + +/*---------------------------------------------------------------------- +-- linear_comb - linear combination of two linear forms. +-- +-- This routine computes the linear combination: +-- +-- a * fx + b * fy, +-- +-- where a and b are numeric coefficients, fx and fy are linear forms +-- (destroyed on exit). */ + +FORMULA *linear_comb +( MPL *mpl, + double a, FORMULA *fx, /* destroyed */ + double b, FORMULA *fy /* destroyed */ +) +{ FORMULA *form = NULL, *term, *temp; + double c0 = 0.0; + for (term = fx; term != NULL; term = term->next) + { if (term->var == NULL) + c0 = fp_add(mpl, c0, fp_mul(mpl, a, term->coef)); + else + term->var->temp = + fp_add(mpl, term->var->temp, fp_mul(mpl, a, term->coef)); + } + for (term = fy; term != NULL; term = term->next) + { if (term->var == NULL) + c0 = fp_add(mpl, c0, fp_mul(mpl, b, term->coef)); + else + term->var->temp = + fp_add(mpl, term->var->temp, fp_mul(mpl, b, term->coef)); + } + for (term = fx; term != NULL; term = term->next) + { if (term->var != NULL && term->var->temp != 0.0) + { temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); + temp->coef = term->var->temp, temp->var = term->var; + temp->next = form, form = temp; + term->var->temp = 0.0; + } + } + for (term = fy; term != NULL; term = term->next) + { if (term->var != NULL && term->var->temp != 0.0) + { temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); + temp->coef = term->var->temp, temp->var = term->var; + temp->next = form, form = temp; + term->var->temp = 0.0; + } + } + if (c0 != 0.0) + { temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); + temp->coef = c0, temp->var = NULL; + temp->next = form, form = temp; + } + delete_formula(mpl, fx); + delete_formula(mpl, fy); + return form; +} + +/*---------------------------------------------------------------------- +-- remove_constant - remove constant term from linear form. +-- +-- This routine removes constant term from linear form and stores its +-- value to given location. */ + +FORMULA *remove_constant +( MPL *mpl, + FORMULA *form, /* destroyed */ + double *coef /* modified */ +) +{ FORMULA *head = NULL, *temp; + *coef = 0.0; + while (form != NULL) + { temp = form; + form = form->next; + if (temp->var == NULL) + { /* constant term */ + *coef = fp_add(mpl, *coef, temp->coef); + dmp_free_atom(mpl->formulae, temp, sizeof(FORMULA)); + } + else + { /* linear term */ + temp->next = head; + head = temp; + } + } + return head; +} + +/*---------------------------------------------------------------------- +-- reduce_terms - reduce identical terms in linear form. +-- +-- This routine reduces identical terms in specified linear form. */ + +FORMULA *reduce_terms +( MPL *mpl, + FORMULA *form /* destroyed */ +) +{ FORMULA *term, *next_term; + double c0 = 0.0; + for (term = form; term != NULL; term = term->next) + { if (term->var == NULL) + c0 = fp_add(mpl, c0, term->coef); + else + term->var->temp = fp_add(mpl, term->var->temp, term->coef); + } + next_term = form, form = NULL; + for (term = next_term; term != NULL; term = next_term) + { next_term = term->next; + if (term->var == NULL && c0 != 0.0) + { term->coef = c0, c0 = 0.0; + term->next = form, form = term; + } + else if (term->var != NULL && term->var->temp != 0.0) + { term->coef = term->var->temp, term->var->temp = 0.0; + term->next = form, form = term; + } + else + dmp_free_atom(mpl->formulae, term, sizeof(FORMULA)); + } + return form; +} + +/**********************************************************************/ +/* * * ELEMENTAL CONSTRAINTS * * */ +/**********************************************************************/ + +/* (there are no specific routines for elemental constraints) */ + +/**********************************************************************/ +/* * * GENERIC VALUES * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- delete_value - delete generic value. +-- +-- This routine deletes specified generic value. +-- +-- NOTE: The generic value to be deleted must be valid. */ + +void delete_value +( MPL *mpl, + int type, + VALUE *value /* content destroyed */ +) +{ xassert(value != NULL); + switch (type) + { case A_NONE: + value->none = NULL; + break; + case A_NUMERIC: + value->num = 0.0; + break; + case A_SYMBOLIC: + delete_symbol(mpl, value->sym), value->sym = NULL; + break; + case A_LOGICAL: + value->bit = 0; + break; + case A_TUPLE: + delete_tuple(mpl, value->tuple), value->tuple = NULL; + break; + case A_ELEMSET: + delete_elemset(mpl, value->set), value->set = NULL; + break; + case A_ELEMVAR: + value->var = NULL; + break; + case A_FORMULA: + delete_formula(mpl, value->form), value->form = NULL; + break; + case A_ELEMCON: + value->con = NULL; + break; + default: + xassert(type != type); + } + return; +} + +/**********************************************************************/ +/* * * SYMBOLICALLY INDEXED ARRAYS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- create_array - create array. +-- +-- This routine creates an array of specified type and dimension. Being +-- created the array is initially empty. +-- +-- The type indicator determines generic values, which can be assigned +-- to the array members: +-- +-- A_NONE - none (members have no assigned values) +-- A_NUMERIC - floating-point numbers +-- A_SYMBOLIC - symbols +-- A_ELEMSET - elemental sets +-- A_ELEMVAR - elemental variables +-- A_ELEMCON - elemental constraints +-- +-- The dimension may be 0, in which case the array consists of the only +-- member (such arrays represent 0-dimensional objects). */ + +ARRAY *create_array(MPL *mpl, int type, int dim) +{ ARRAY *array; + xassert(type == A_NONE || type == A_NUMERIC || + type == A_SYMBOLIC || type == A_ELEMSET || + type == A_ELEMVAR || type == A_ELEMCON); + xassert(dim >= 0); + array = dmp_get_atom(mpl->arrays, sizeof(ARRAY)); + array->type = type; + array->dim = dim; + array->size = 0; + array->head = NULL; + array->tail = NULL; + array->tree = NULL; + array->prev = NULL; + array->next = mpl->a_list; + /* include the array in the global array list */ + if (array->next != NULL) array->next->prev = array; + mpl->a_list = array; + return array; +} + +/*---------------------------------------------------------------------- +-- find_member - find array member with given n-tuple. +-- +-- This routine finds an array member, which has given n-tuple. If the +-- array is short, the linear search is used. Otherwise the routine +-- autimatically creates the search tree (i.e. the array index) to find +-- members for logarithmic time. */ + +static int compare_member_tuples(void *info, const void *key1, + const void *key2) +{ /* this is an auxiliary routine used to compare keys, which are + n-tuples assigned to array members */ + return compare_tuples((MPL *)info, (TUPLE *)key1, (TUPLE *)key2); +} + +MEMBER *find_member +( MPL *mpl, + ARRAY *array, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ MEMBER *memb; + xassert(array != NULL); + /* the n-tuple must have the same dimension as the array */ + xassert(tuple_dimen(mpl, tuple) == array->dim); + /* if the array is large enough, create the search tree and index + all existing members of the array */ + if (array->size > 30 && array->tree == NULL) + { array->tree = avl_create_tree(compare_member_tuples, mpl); + for (memb = array->head; memb != NULL; memb = memb->next) +avl_set_node_link(avl_insert_node(array->tree, memb->tuple), + (void *)memb); + } + /* find a member, which has the given tuple */ + if (array->tree == NULL) + { /* the search tree doesn't exist; use the linear search */ + for (memb = array->head; memb != NULL; memb = memb->next) + if (compare_tuples(mpl, memb->tuple, tuple) == 0) break; + } + else + { /* the search tree exists; use the binary search */ + AVLNODE *node; + node = avl_find_node(array->tree, tuple); +memb = (MEMBER *)(node == NULL ? NULL : avl_get_node_link(node)); + } + return memb; +} + +/*---------------------------------------------------------------------- +-- add_member - add new member to array. +-- +-- This routine creates a new member with given n-tuple and adds it to +-- specified array. +-- +-- For the sake of efficiency this routine doesn't check whether the +-- array already contains a member with the given n-tuple or not. Thus, +-- if necessary, the calling program should use the routine find_member +-- in order to be sure that the array contains no member with the same +-- n-tuple, because members with duplicate n-tuples are not allowed. +-- +-- This routine assigns no generic value to the new member, because the +-- calling program must do that. */ + +MEMBER *add_member +( MPL *mpl, + ARRAY *array, /* modified */ + TUPLE *tuple /* destroyed */ +) +{ MEMBER *memb; + xassert(array != NULL); + /* the n-tuple must have the same dimension as the array */ + xassert(tuple_dimen(mpl, tuple) == array->dim); + /* create new member */ + memb = dmp_get_atom(mpl->members, sizeof(MEMBER)); + memb->tuple = tuple; + memb->next = NULL; + memset(&memb->value, '?', sizeof(VALUE)); + /* and append it to the member list */ + array->size++; + if (array->head == NULL) + array->head = memb; + else + array->tail->next = memb; + array->tail = memb; + /* if the search tree exists, index the new member */ + if (array->tree != NULL) +avl_set_node_link(avl_insert_node(array->tree, memb->tuple), + (void *)memb); + return memb; +} + +/*---------------------------------------------------------------------- +-- delete_array - delete array. +-- +-- This routine deletes specified array. +-- +-- Generic values assigned to the array members are not deleted by this +-- routine. The calling program itself must delete all assigned generic +-- values before deleting the array. */ + +void delete_array +( MPL *mpl, + ARRAY *array /* destroyed */ +) +{ MEMBER *memb; + xassert(array != NULL); + /* delete all existing array members */ + while (array->head != NULL) + { memb = array->head; + array->head = memb->next; + delete_tuple(mpl, memb->tuple); + dmp_free_atom(mpl->members, memb, sizeof(MEMBER)); + } + /* if the search tree exists, also delete it */ + if (array->tree != NULL) avl_delete_tree(array->tree); + /* remove the array from the global array list */ + if (array->prev == NULL) + mpl->a_list = array->next; + else + array->prev->next = array->next; + if (array->next == NULL) + ; + else + array->next->prev = array->prev; + /* delete the array descriptor */ + dmp_free_atom(mpl->arrays, array, sizeof(ARRAY)); + return; +} + +/**********************************************************************/ +/* * * DOMAINS AND DUMMY INDICES * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- assign_dummy_index - assign new value to dummy index. +-- +-- This routine assigns new value to specified dummy index and, that is +-- important, invalidates all temporary resultant values, which depends +-- on that dummy index. */ + +void assign_dummy_index +( MPL *mpl, + DOMAIN_SLOT *slot, /* modified */ + SYMBOL *value /* not changed */ +) +{ CODE *leaf, *code; + xassert(slot != NULL); + xassert(value != NULL); + /* delete the current value assigned to the dummy index */ + if (slot->value != NULL) + { /* if the current value and the new one are identical, actual + assignment is not needed */ + if (compare_symbols(mpl, slot->value, value) == 0) goto done; + /* delete a symbol, which is the current value */ + delete_symbol(mpl, slot->value), slot->value = NULL; + } + /* now walk through all the pseudo-codes with op = O_INDEX, which + refer to the dummy index to be changed (these pseudo-codes are + leaves in the forest of *all* expressions in the database) */ + for (leaf = slot->list; leaf != NULL; leaf = leaf->arg.index. + next) + { xassert(leaf->op == O_INDEX); + /* invalidate all resultant values, which depend on the dummy + index, walking from the current leaf toward the root of the + corresponding expression tree */ + for (code = leaf; code != NULL; code = code->up) + { if (code->valid) + { /* invalidate and delete resultant value */ + code->valid = 0; + delete_value(mpl, code->type, &code->value); + } + } + } + /* assign new value to the dummy index */ + slot->value = copy_symbol(mpl, value); +done: return; +} + +/*---------------------------------------------------------------------- +-- update_dummy_indices - update current values of dummy indices. +-- +-- This routine assigns components of "backup" n-tuple to dummy indices +-- of specified domain block. If no "backup" n-tuple is defined for the +-- domain block, values of the dummy indices remain untouched. */ + +void update_dummy_indices +( MPL *mpl, + DOMAIN_BLOCK *block /* not changed */ +) +{ DOMAIN_SLOT *slot; + TUPLE *temp; + if (block->backup != NULL) + { for (slot = block->list, temp = block->backup; slot != NULL; + slot = slot->next, temp = temp->next) + { xassert(temp != NULL); + xassert(temp->sym != NULL); + assign_dummy_index(mpl, slot, temp->sym); + } + } + return; +} + +/*---------------------------------------------------------------------- +-- enter_domain_block - enter domain block. +-- +-- Let specified domain block have the form: +-- +-- { ..., (j1, j2, ..., jn) in J, ... } +-- +-- where j1, j2, ..., jn are dummy indices, J is a basic set. +-- +-- This routine does the following: +-- +-- 1. Checks if the given n-tuple is a member of the basic set J. Note +-- that J being *out of the scope* of the domain block cannot depend +-- on the dummy indices in the same and inner domain blocks, so it +-- can be computed before the dummy indices are assigned new values. +-- If this check fails, the routine returns with non-zero code. +-- +-- 2. Saves current values of the dummy indices j1, j2, ..., jn. +-- +-- 3. Assigns new values, which are components of the given n-tuple, to +-- the dummy indices j1, j2, ..., jn. If dimension of the n-tuple is +-- larger than n, its extra components n+1, n+2, ... are not used. +-- +-- 4. Calls the formal routine func which either enters the next domain +-- block or evaluates some code within the domain scope. +-- +-- 5. Restores former values of the dummy indices j1, j2, ..., jn. +-- +-- Since current values assigned to the dummy indices on entry to this +-- routine are restored on exit, the formal routine func is allowed to +-- call this routine recursively. */ + +int enter_domain_block +( MPL *mpl, + DOMAIN_BLOCK *block, /* not changed */ + TUPLE *tuple, /* not changed */ + void *info, void (*func)(MPL *mpl, void *info) +) +{ TUPLE *backup; + int ret = 0; + /* check if the given n-tuple is a member of the basic set */ + xassert(block->code != NULL); + if (!is_member(mpl, block->code, tuple)) + { ret = 1; + goto done; + } + /* save reference to "backup" n-tuple, which was used to assign + current values of the dummy indices (it is sufficient to save + reference, not value, because that n-tuple is defined in some + outer level of recursion and therefore cannot be changed on + this and deeper recursive calls) */ + backup = block->backup; + /* set up new "backup" n-tuple, which defines new values of the + dummy indices */ + block->backup = tuple; + /* assign new values to the dummy indices */ + update_dummy_indices(mpl, block); + /* call the formal routine that does the rest part of the job */ + func(mpl, info); + /* restore reference to the former "backup" n-tuple */ + block->backup = backup; + /* restore former values of the dummy indices; note that if the + domain block just escaped has no other active instances which + may exist due to recursion (it is indicated by a null pointer + to the former n-tuple), former values of the dummy indices are + undefined; therefore in this case the routine keeps currently + assigned values of the dummy indices that involves keeping all + dependent temporary results and thereby, if this domain block + is not used recursively, allows improving efficiency */ + update_dummy_indices(mpl, block); +done: return ret; +} + +/*---------------------------------------------------------------------- +-- eval_within_domain - perform evaluation within domain scope. +-- +-- This routine assigns new values (symbols) to all dummy indices of +-- specified domain and calls the formal routine func, which is used to +-- evaluate some code in the domain scope. Each free dummy index in the +-- domain is assigned a value specified in the corresponding component +-- of given n-tuple. Non-free dummy indices are assigned values, which +-- are computed by this routine. +-- +-- Number of components in the given n-tuple must be the same as number +-- of free indices in the domain. +-- +-- If the given n-tuple is not a member of the domain set, the routine +-- func is not called, and non-zero code is returned. +-- +-- For the sake of convenience it is allowed to specify domain as NULL +-- (then n-tuple also must be 0-tuple, i.e. empty), in which case this +-- routine just calls the routine func and returns zero. +-- +-- This routine allows recursive calls from the routine func providing +-- correct values of dummy indices for each instance. +-- +-- NOTE: The n-tuple passed to this routine must not be changed by any +-- other routines called from the formal routine func until this +-- routine has returned. */ + +struct eval_domain_info +{ /* working info used by the routine eval_within_domain */ + DOMAIN *domain; + /* domain, which has to be entered */ + DOMAIN_BLOCK *block; + /* domain block, which is currently processed */ + TUPLE *tuple; + /* tail of original n-tuple, whose components have to be assigned + to free dummy indices in the current domain block */ + void *info; + /* transit pointer passed to the formal routine func */ + void (*func)(MPL *mpl, void *info); + /* routine, which has to be executed in the domain scope */ + int failure; + /* this flag indicates that given n-tuple is not a member of the + domain set */ +}; + +static void eval_domain_func(MPL *mpl, void *_my_info) +{ /* this routine recursively enters into the domain scope and then + calls the routine func */ + struct eval_domain_info *my_info = _my_info; + if (my_info->block != NULL) + { /* the current domain block to be entered exists */ + DOMAIN_BLOCK *block; + DOMAIN_SLOT *slot; + TUPLE *tuple = NULL, *temp = NULL; + /* save pointer to the current domain block */ + block = my_info->block; + /* and get ready to enter the next block (if it exists) */ + my_info->block = block->next; + /* construct temporary n-tuple, whose components correspond to + dummy indices (slots) of the current domain; components of + the temporary n-tuple that correspond to free dummy indices + are assigned references (not values!) to symbols specified + in the corresponding components of the given n-tuple, while + other components that correspond to non-free dummy indices + are assigned symbolic values computed here */ + for (slot = block->list; slot != NULL; slot = slot->next) + { /* create component that corresponds to the current slot */ + if (tuple == NULL) + tuple = temp = dmp_get_atom(mpl->tuples, sizeof(TUPLE)); + else +temp = (temp->next = dmp_get_atom(mpl->tuples, sizeof(TUPLE))); + if (slot->code == NULL) + { /* dummy index is free; take reference to symbol, which + is specified in the corresponding component of given + n-tuple */ + xassert(my_info->tuple != NULL); + temp->sym = my_info->tuple->sym; + xassert(temp->sym != NULL); + my_info->tuple = my_info->tuple->next; + } + else + { /* dummy index is non-free; compute symbolic value to be + temporarily assigned to the dummy index */ + temp->sym = eval_symbolic(mpl, slot->code); + } + } + temp->next = NULL; + /* enter the current domain block */ + if (enter_domain_block(mpl, block, tuple, my_info, + eval_domain_func)) my_info->failure = 1; + /* delete temporary n-tuple as well as symbols that correspond + to non-free dummy indices (they were computed here) */ + for (slot = block->list; slot != NULL; slot = slot->next) + { xassert(tuple != NULL); + temp = tuple; + tuple = tuple->next; + if (slot->code != NULL) + { /* dummy index is non-free; delete symbolic value */ + delete_symbol(mpl, temp->sym); + } + /* delete component that corresponds to the current slot */ + dmp_free_atom(mpl->tuples, temp, sizeof(TUPLE)); + } + } + else + { /* there are no more domain blocks, i.e. we have reached the + domain scope */ + xassert(my_info->tuple == NULL); + /* check optional predicate specified for the domain */ + if (my_info->domain->code != NULL && !eval_logical(mpl, + my_info->domain->code)) + { /* the predicate is false */ + my_info->failure = 2; + } + else + { /* the predicate is true; do the job */ + my_info->func(mpl, my_info->info); + } + } + return; +} + +int eval_within_domain +( MPL *mpl, + DOMAIN *domain, /* not changed */ + TUPLE *tuple, /* not changed */ + void *info, void (*func)(MPL *mpl, void *info) +) +{ /* this routine performs evaluation within domain scope */ + struct eval_domain_info _my_info, *my_info = &_my_info; + if (domain == NULL) + { xassert(tuple == NULL); + func(mpl, info); + my_info->failure = 0; + } + else + { xassert(tuple != NULL); + my_info->domain = domain; + my_info->block = domain->list; + my_info->tuple = tuple; + my_info->info = info; + my_info->func = func; + my_info->failure = 0; + /* enter the very first domain block */ + eval_domain_func(mpl, my_info); + } + return my_info->failure; +} + +/*---------------------------------------------------------------------- +-- loop_within_domain - perform iterations within domain scope. +-- +-- This routine iteratively assigns new values (symbols) to the dummy +-- indices of specified domain by enumerating all n-tuples, which are +-- members of the domain set, and for every n-tuple it calls the formal +-- routine func to evaluate some code within the domain scope. +-- +-- If the routine func returns non-zero, enumeration within the domain +-- is prematurely terminated. +-- +-- For the sake of convenience it is allowed to specify domain as NULL, +-- in which case this routine just calls the routine func only once and +-- returns zero. +-- +-- This routine allows recursive calls from the routine func providing +-- correct values of dummy indices for each instance. */ + +struct loop_domain_info +{ /* working info used by the routine loop_within_domain */ + DOMAIN *domain; + /* domain, which has to be entered */ + DOMAIN_BLOCK *block; + /* domain block, which is currently processed */ + int looping; + /* clearing this flag leads to terminating enumeration */ + void *info; + /* transit pointer passed to the formal routine func */ + int (*func)(MPL *mpl, void *info); + /* routine, which needs to be executed in the domain scope */ +}; + +static void loop_domain_func(MPL *mpl, void *_my_info) +{ /* this routine enumerates all n-tuples in the basic set of the + current domain block, enters recursively into the domain scope + for every n-tuple, and then calls the routine func */ + struct loop_domain_info *my_info = _my_info; + if (my_info->block != NULL) + { /* the current domain block to be entered exists */ + DOMAIN_BLOCK *block; + DOMAIN_SLOT *slot; + TUPLE *bound; + /* save pointer to the current domain block */ + block = my_info->block; + /* and get ready to enter the next block (if it exists) */ + my_info->block = block->next; + /* compute symbolic values, at which non-free dummy indices of + the current domain block are bound; since that values don't + depend on free dummy indices of the current block, they can + be computed once out of the enumeration loop */ + bound = create_tuple(mpl); + for (slot = block->list; slot != NULL; slot = slot->next) + { if (slot->code != NULL) + bound = expand_tuple(mpl, bound, eval_symbolic(mpl, + slot->code)); + } + /* start enumeration */ + xassert(block->code != NULL); + if (block->code->op == O_DOTS) + { /* the basic set is "arithmetic", in which case it doesn't + need to be computed explicitly */ + TUPLE *tuple; + int n, j; + double t0, tf, dt; + /* compute "parameters" of the basic set */ + t0 = eval_numeric(mpl, block->code->arg.arg.x); + tf = eval_numeric(mpl, block->code->arg.arg.y); + if (block->code->arg.arg.z == NULL) + dt = 1.0; + else + dt = eval_numeric(mpl, block->code->arg.arg.z); + /* determine cardinality of the basic set */ + n = arelset_size(mpl, t0, tf, dt); + /* create dummy 1-tuple for members of the basic set */ + tuple = expand_tuple(mpl, create_tuple(mpl), + create_symbol_num(mpl, 0.0)); + /* in case of "arithmetic" set there is exactly one dummy + index, which cannot be non-free */ + xassert(bound == NULL); + /* walk through 1-tuples of the basic set */ + for (j = 1; j <= n && my_info->looping; j++) + { /* construct dummy 1-tuple for the current member */ + tuple->sym->num = arelset_member(mpl, t0, tf, dt, j); + /* enter the current domain block */ + enter_domain_block(mpl, block, tuple, my_info, + loop_domain_func); + } + /* delete dummy 1-tuple */ + delete_tuple(mpl, tuple); + } + else + { /* the basic set is of general kind, in which case it needs + to be explicitly computed */ + ELEMSET *set; + MEMBER *memb; + TUPLE *temp1, *temp2; + /* compute the basic set */ + set = eval_elemset(mpl, block->code); + /* walk through all n-tuples of the basic set */ + for (memb = set->head; memb != NULL && my_info->looping; + memb = memb->next) + { /* all components of the current n-tuple that correspond + to non-free dummy indices must be feasible; otherwise + the n-tuple is not in the basic set */ + temp1 = memb->tuple; + temp2 = bound; + for (slot = block->list; slot != NULL; slot = slot->next) + { xassert(temp1 != NULL); + if (slot->code != NULL) + { /* non-free dummy index */ + xassert(temp2 != NULL); + if (compare_symbols(mpl, temp1->sym, temp2->sym) + != 0) + { /* the n-tuple is not in the basic set */ + goto skip; + } + temp2 = temp2->next; + } + temp1 = temp1->next; + } + xassert(temp1 == NULL); + xassert(temp2 == NULL); + /* enter the current domain block */ + enter_domain_block(mpl, block, memb->tuple, my_info, + loop_domain_func); +skip: ; + } + /* delete the basic set */ + delete_elemset(mpl, set); + } + /* delete symbolic values binding non-free dummy indices */ + delete_tuple(mpl, bound); + /* restore pointer to the current domain block */ + my_info->block = block; + } + else + { /* there are no more domain blocks, i.e. we have reached the + domain scope */ + /* check optional predicate specified for the domain */ + if (my_info->domain->code != NULL && !eval_logical(mpl, + my_info->domain->code)) + { /* the predicate is false */ + /* nop */; + } + else + { /* the predicate is true; do the job */ + my_info->looping = !my_info->func(mpl, my_info->info); + } + } + return; +} + +void loop_within_domain +( MPL *mpl, + DOMAIN *domain, /* not changed */ + void *info, int (*func)(MPL *mpl, void *info) +) +{ /* this routine performs iterations within domain scope */ + struct loop_domain_info _my_info, *my_info = &_my_info; + if (domain == NULL) + func(mpl, info); + else + { my_info->domain = domain; + my_info->block = domain->list; + my_info->looping = 1; + my_info->info = info; + my_info->func = func; + /* enter the very first domain block */ + loop_domain_func(mpl, my_info); + } + return; +} + +/*---------------------------------------------------------------------- +-- out_of_domain - raise domain exception. +-- +-- This routine is called when a reference is made to a member of some +-- model object, but its n-tuple is out of the object domain. */ + +void out_of_domain +( MPL *mpl, + char *name, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ xassert(name != NULL); + xassert(tuple != NULL); + error(mpl, "%s%s out of domain", name, format_tuple(mpl, '[', + tuple)); + /* no return */ +} + +/*---------------------------------------------------------------------- +-- get_domain_tuple - obtain current n-tuple from domain. +-- +-- This routine constructs n-tuple, whose components are current values +-- assigned to *free* dummy indices of specified domain. +-- +-- For the sake of convenience it is allowed to specify domain as NULL, +-- in which case this routine returns 0-tuple. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +TUPLE *get_domain_tuple +( MPL *mpl, + DOMAIN *domain /* not changed */ +) +{ DOMAIN_BLOCK *block; + DOMAIN_SLOT *slot; + TUPLE *tuple; + tuple = create_tuple(mpl); + if (domain != NULL) + { for (block = domain->list; block != NULL; block = block->next) + { for (slot = block->list; slot != NULL; slot = slot->next) + { if (slot->code == NULL) + { xassert(slot->value != NULL); + tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, + slot->value)); + } + } + } + } + return tuple; +} + +/*---------------------------------------------------------------------- +-- clean_domain - clean domain. +-- +-- This routine cleans specified domain that assumes deleting all stuff +-- dynamically allocated during the generation phase. */ + +void clean_domain(MPL *mpl, DOMAIN *domain) +{ DOMAIN_BLOCK *block; + DOMAIN_SLOT *slot; + /* if no domain is specified, do nothing */ + if (domain == NULL) goto done; + /* clean all domain blocks */ + for (block = domain->list; block != NULL; block = block->next) + { /* clean all domain slots */ + for (slot = block->list; slot != NULL; slot = slot->next) + { /* clean pseudo-code for computing bound value */ + clean_code(mpl, slot->code); + /* delete symbolic value assigned to dummy index */ + if (slot->value != NULL) + delete_symbol(mpl, slot->value), slot->value = NULL; + } + /* clean pseudo-code for computing basic set */ + clean_code(mpl, block->code); + } + /* clean pseudo-code for computing domain predicate */ + clean_code(mpl, domain->code); +done: return; +} + +/**********************************************************************/ +/* * * MODEL SETS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- check_elem_set - check elemental set assigned to set member. +-- +-- This routine checks if given elemental set being assigned to member +-- of specified model set satisfies to all restrictions. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +void check_elem_set +( MPL *mpl, + SET *set, /* not changed */ + TUPLE *tuple, /* not changed */ + ELEMSET *refer /* not changed */ +) +{ WITHIN *within; + MEMBER *memb; + int eqno; + /* elemental set must be within all specified supersets */ + for (within = set->within, eqno = 1; within != NULL; within = + within->next, eqno++) + { xassert(within->code != NULL); + for (memb = refer->head; memb != NULL; memb = memb->next) + { if (!is_member(mpl, within->code, memb->tuple)) + { char buf[255+1]; + strcpy(buf, format_tuple(mpl, '(', memb->tuple)); + xassert(strlen(buf) < sizeof(buf)); + error(mpl, "%s%s contains %s which not within specified " + "set; see (%d)", set->name, format_tuple(mpl, '[', + tuple), buf, eqno); + } + } + } + return; +} + +/*---------------------------------------------------------------------- +-- take_member_set - obtain elemental set assigned to set member. +-- +-- This routine obtains a reference to elemental set assigned to given +-- member of specified model set and returns it on exit. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +ELEMSET *take_member_set /* returns reference, not value */ +( MPL *mpl, + SET *set, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ MEMBER *memb; + ELEMSET *refer; + /* find member in the set array */ + memb = find_member(mpl, set->array, tuple); + if (memb != NULL) + { /* member exists, so just take the reference */ + refer = memb->value.set; + } + else if (set->assign != NULL) + { /* compute value using assignment expression */ + refer = eval_elemset(mpl, set->assign); +add: /* check that the elemental set satisfies to all restrictions, + assign it to new member, and add the member to the array */ + check_elem_set(mpl, set, tuple, refer); + memb = add_member(mpl, set->array, copy_tuple(mpl, tuple)); + memb->value.set = refer; + } + else if (set->option != NULL) + { /* compute default elemental set */ + refer = eval_elemset(mpl, set->option); + goto add; + } + else + { /* no value (elemental set) is provided */ + error(mpl, "no value for %s%s", set->name, format_tuple(mpl, + '[', tuple)); + } + return refer; +} + +/*---------------------------------------------------------------------- +-- eval_member_set - evaluate elemental set assigned to set member. +-- +-- This routine evaluates a reference to elemental set assigned to given +-- member of specified model set and returns it on exit. */ + +struct eval_set_info +{ /* working info used by the routine eval_member_set */ + SET *set; + /* model set */ + TUPLE *tuple; + /* n-tuple, which defines set member */ + MEMBER *memb; + /* normally this pointer is NULL; the routine uses this pointer + to check data provided in the data section, in which case it + points to a member currently checked; this check is performed + automatically only once when a reference to any member occurs + for the first time */ + ELEMSET *refer; + /* evaluated reference to elemental set */ +}; + +static void eval_set_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine to work within domain scope */ + struct eval_set_info *info = _info; + if (info->memb != NULL) + { /* checking call; check elemental set being assigned */ + check_elem_set(mpl, info->set, info->memb->tuple, + info->memb->value.set); + } + else + { /* normal call; evaluate member, which has given n-tuple */ + info->refer = take_member_set(mpl, info->set, info->tuple); + } + return; +} + +#if 1 /* 12/XII-2008 */ +static void saturate_set(MPL *mpl, SET *set) +{ GADGET *gadget = set->gadget; + ELEMSET *data; + MEMBER *elem, *memb; + TUPLE *tuple, *work[20]; + int i; + xprintf("Generating %s...\n", set->name); + eval_whole_set(mpl, gadget->set); + /* gadget set must have exactly one member */ + xassert(gadget->set->array != NULL); + xassert(gadget->set->array->head != NULL); + xassert(gadget->set->array->head == gadget->set->array->tail); + data = gadget->set->array->head->value.set; + xassert(data->type == A_NONE); + xassert(data->dim == gadget->set->dimen); + /* walk thru all elements of the plain set */ + for (elem = data->head; elem != NULL; elem = elem->next) + { /* create a copy of n-tuple */ + tuple = copy_tuple(mpl, elem->tuple); + /* rearrange component of the n-tuple */ + for (i = 0; i < gadget->set->dimen; i++) + work[i] = NULL; + for (i = 0; tuple != NULL; tuple = tuple->next) + work[gadget->ind[i++]-1] = tuple; + xassert(i == gadget->set->dimen); + for (i = 0; i < gadget->set->dimen; i++) + { xassert(work[i] != NULL); + work[i]->next = work[i+1]; + } + /* construct subscript list from first set->dim components */ + if (set->dim == 0) + tuple = NULL; + else + tuple = work[0], work[set->dim-1]->next = NULL; + /* find corresponding member of the set to be initialized */ + memb = find_member(mpl, set->array, tuple); + if (memb == NULL) + { /* not found; add new member to the set and assign it empty + elemental set */ + memb = add_member(mpl, set->array, tuple); + memb->value.set = create_elemset(mpl, set->dimen); + } + else + { /* found; free subscript list */ + delete_tuple(mpl, tuple); + } + /* construct new n-tuple from rest set->dimen components */ + tuple = work[set->dim]; + xassert(set->dim + set->dimen == gadget->set->dimen); + work[gadget->set->dimen-1]->next = NULL; + /* and add it to the elemental set assigned to the member + (no check for duplicates is needed) */ + add_tuple(mpl, memb->value.set, tuple); + } + /* the set has been saturated with data */ + set->data = 1; + return; +} +#endif + +ELEMSET *eval_member_set /* returns reference, not value */ +( MPL *mpl, + SET *set, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ /* this routine evaluates set member */ + struct eval_set_info _info, *info = &_info; + xassert(set->dim == tuple_dimen(mpl, tuple)); + info->set = set; + info->tuple = tuple; +#if 1 /* 12/XII-2008 */ + if (set->gadget != NULL && set->data == 0) + { /* initialize the set with data from a plain set */ + saturate_set(mpl, set); + } +#endif + if (set->data == 1) + { /* check data, which are provided in the data section, but not + checked yet */ + /* save pointer to the last array member; note that during the + check new members may be added beyond the last member due to + references to the same parameter from default expression as + well as from expressions that define restricting supersets; + however, values assigned to the new members will be checked + by other routine, so we don't need to check them here */ + MEMBER *tail = set->array->tail; + /* change the data status to prevent infinite recursive loop + due to references to the same set during the check */ + set->data = 2; + /* check elemental sets assigned to array members in the data + section until the marked member has been reached */ + for (info->memb = set->array->head; info->memb != NULL; + info->memb = info->memb->next) + { if (eval_within_domain(mpl, set->domain, info->memb->tuple, + info, eval_set_func)) + out_of_domain(mpl, set->name, info->memb->tuple); + if (info->memb == tail) break; + } + /* the check has been finished */ + } + /* evaluate member, which has given n-tuple */ + info->memb = NULL; + if (eval_within_domain(mpl, info->set->domain, info->tuple, info, + eval_set_func)) + out_of_domain(mpl, set->name, info->tuple); + /* bring evaluated reference to the calling program */ + return info->refer; +} + +/*---------------------------------------------------------------------- +-- eval_whole_set - evaluate model set over entire domain. +-- +-- This routine evaluates all members of specified model set over entire +-- domain. */ + +static int whole_set_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + SET *set = (SET *)info; + TUPLE *tuple = get_domain_tuple(mpl, set->domain); + eval_member_set(mpl, set, tuple); + delete_tuple(mpl, tuple); + return 0; +} + +void eval_whole_set(MPL *mpl, SET *set) +{ loop_within_domain(mpl, set->domain, set, whole_set_func); + return; +} + +/*---------------------------------------------------------------------- +-- clean set - clean model set. +-- +-- This routine cleans specified model set that assumes deleting all +-- stuff dynamically allocated during the generation phase. */ + +void clean_set(MPL *mpl, SET *set) +{ WITHIN *within; + MEMBER *memb; + /* clean subscript domain */ + clean_domain(mpl, set->domain); + /* clean pseudo-code for computing supersets */ + for (within = set->within; within != NULL; within = within->next) + clean_code(mpl, within->code); + /* clean pseudo-code for computing assigned value */ + clean_code(mpl, set->assign); + /* clean pseudo-code for computing default value */ + clean_code(mpl, set->option); + /* reset data status flag */ + set->data = 0; + /* delete content array */ + for (memb = set->array->head; memb != NULL; memb = memb->next) + delete_value(mpl, set->array->type, &memb->value); + delete_array(mpl, set->array), set->array = NULL; + return; +} + +/**********************************************************************/ +/* * * MODEL PARAMETERS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- check_value_num - check numeric value assigned to parameter member. +-- +-- This routine checks if numeric value being assigned to some member +-- of specified numeric model parameter satisfies to all restrictions. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +void check_value_num +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple, /* not changed */ + double value +) +{ CONDITION *cond; + WITHIN *in; + int eqno; + /* the value must satisfy to the parameter type */ + switch (par->type) + { case A_NUMERIC: + break; + case A_INTEGER: + if (value != floor(value)) + error(mpl, "%s%s = %.*g not integer", par->name, + format_tuple(mpl, '[', tuple), DBL_DIG, value); + break; + case A_BINARY: + if (!(value == 0.0 || value == 1.0)) + error(mpl, "%s%s = %.*g not binary", par->name, + format_tuple(mpl, '[', tuple), DBL_DIG, value); + break; + default: + xassert(par != par); + } + /* the value must satisfy to all specified conditions */ + for (cond = par->cond, eqno = 1; cond != NULL; cond = cond->next, + eqno++) + { double bound; + char *rho; + xassert(cond->code != NULL); + bound = eval_numeric(mpl, cond->code); + switch (cond->rho) + { case O_LT: + if (!(value < bound)) + { rho = "<"; +err: error(mpl, "%s%s = %.*g not %s %.*g; see (%d)", + par->name, format_tuple(mpl, '[', tuple), DBL_DIG, + value, rho, DBL_DIG, bound, eqno); + } + break; + case O_LE: + if (!(value <= bound)) { rho = "<="; goto err; } + break; + case O_EQ: + if (!(value == bound)) { rho = "="; goto err; } + break; + case O_GE: + if (!(value >= bound)) { rho = ">="; goto err; } + break; + case O_GT: + if (!(value > bound)) { rho = ">"; goto err; } + break; + case O_NE: + if (!(value != bound)) { rho = "<>"; goto err; } + break; + default: + xassert(cond != cond); + } + } + /* the value must be in all specified supersets */ + for (in = par->in, eqno = 1; in != NULL; in = in->next, eqno++) + { TUPLE *dummy; + xassert(in->code != NULL); + xassert(in->code->dim == 1); + dummy = expand_tuple(mpl, create_tuple(mpl), + create_symbol_num(mpl, value)); + if (!is_member(mpl, in->code, dummy)) + error(mpl, "%s%s = %.*g not in specified set; see (%d)", + par->name, format_tuple(mpl, '[', tuple), DBL_DIG, + value, eqno); + delete_tuple(mpl, dummy); + } + return; +} + +/*---------------------------------------------------------------------- +-- take_member_num - obtain num. value assigned to parameter member. +-- +-- This routine obtains a numeric value assigned to member of specified +-- numeric model parameter and returns it on exit. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +double take_member_num +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ MEMBER *memb; + double value; + /* find member in the parameter array */ + memb = find_member(mpl, par->array, tuple); + if (memb != NULL) + { /* member exists, so just take its value */ + value = memb->value.num; + } + else if (par->assign != NULL) + { /* compute value using assignment expression */ + value = eval_numeric(mpl, par->assign); +add: /* check that the value satisfies to all restrictions, assign + it to new member, and add the member to the array */ + check_value_num(mpl, par, tuple, value); + memb = add_member(mpl, par->array, copy_tuple(mpl, tuple)); + memb->value.num = value; + } + else if (par->option != NULL) + { /* compute default value */ + value = eval_numeric(mpl, par->option); + goto add; + } + else if (par->defval != NULL) + { /* take default value provided in the data section */ + if (par->defval->str != NULL) + error(mpl, "cannot convert %s to floating-point number", + format_symbol(mpl, par->defval)); + value = par->defval->num; + goto add; + } + else + { /* no value is provided */ + error(mpl, "no value for %s%s", par->name, format_tuple(mpl, + '[', tuple)); + } + return value; +} + +/*---------------------------------------------------------------------- +-- eval_member_num - evaluate num. value assigned to parameter member. +-- +-- This routine evaluates a numeric value assigned to given member of +-- specified numeric model parameter and returns it on exit. */ + +struct eval_num_info +{ /* working info used by the routine eval_member_num */ + PARAMETER *par; + /* model parameter */ + TUPLE *tuple; + /* n-tuple, which defines parameter member */ + MEMBER *memb; + /* normally this pointer is NULL; the routine uses this pointer + to check data provided in the data section, in which case it + points to a member currently checked; this check is performed + automatically only once when a reference to any member occurs + for the first time */ + double value; + /* evaluated numeric value */ +}; + +static void eval_num_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine to work within domain scope */ + struct eval_num_info *info = _info; + if (info->memb != NULL) + { /* checking call; check numeric value being assigned */ + check_value_num(mpl, info->par, info->memb->tuple, + info->memb->value.num); + } + else + { /* normal call; evaluate member, which has given n-tuple */ + info->value = take_member_num(mpl, info->par, info->tuple); + } + return; +} + +double eval_member_num +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ /* this routine evaluates numeric parameter member */ + struct eval_num_info _info, *info = &_info; + xassert(par->type == A_NUMERIC || par->type == A_INTEGER || + par->type == A_BINARY); + xassert(par->dim == tuple_dimen(mpl, tuple)); + info->par = par; + info->tuple = tuple; + if (par->data == 1) + { /* check data, which are provided in the data section, but not + checked yet */ + /* save pointer to the last array member; note that during the + check new members may be added beyond the last member due to + references to the same parameter from default expression as + well as from expressions that define restricting conditions; + however, values assigned to the new members will be checked + by other routine, so we don't need to check them here */ + MEMBER *tail = par->array->tail; + /* change the data status to prevent infinite recursive loop + due to references to the same parameter during the check */ + par->data = 2; + /* check values assigned to array members in the data section + until the marked member has been reached */ + for (info->memb = par->array->head; info->memb != NULL; + info->memb = info->memb->next) + { if (eval_within_domain(mpl, par->domain, info->memb->tuple, + info, eval_num_func)) + out_of_domain(mpl, par->name, info->memb->tuple); + if (info->memb == tail) break; + } + /* the check has been finished */ + } + /* evaluate member, which has given n-tuple */ + info->memb = NULL; + if (eval_within_domain(mpl, info->par->domain, info->tuple, info, + eval_num_func)) + out_of_domain(mpl, par->name, info->tuple); + /* bring evaluated value to the calling program */ + return info->value; +} + +/*---------------------------------------------------------------------- +-- check_value_sym - check symbolic value assigned to parameter member. +-- +-- This routine checks if symbolic value being assigned to some member +-- of specified symbolic model parameter satisfies to all restrictions. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +void check_value_sym +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple, /* not changed */ + SYMBOL *value /* not changed */ +) +{ CONDITION *cond; + WITHIN *in; + int eqno; + /* the value must satisfy to all specified conditions */ + for (cond = par->cond, eqno = 1; cond != NULL; cond = cond->next, + eqno++) + { SYMBOL *bound; + char buf[255+1]; + xassert(cond->code != NULL); + bound = eval_symbolic(mpl, cond->code); + switch (cond->rho) + { +#if 1 /* 13/VIII-2008 */ + case O_LT: + if (!(compare_symbols(mpl, value, bound) < 0)) + { strcpy(buf, format_symbol(mpl, bound)); + xassert(strlen(buf) < sizeof(buf)); + error(mpl, "%s%s = %s not < %s", + par->name, format_tuple(mpl, '[', tuple), + format_symbol(mpl, value), buf, eqno); + } + break; + case O_LE: + if (!(compare_symbols(mpl, value, bound) <= 0)) + { strcpy(buf, format_symbol(mpl, bound)); + xassert(strlen(buf) < sizeof(buf)); + error(mpl, "%s%s = %s not <= %s", + par->name, format_tuple(mpl, '[', tuple), + format_symbol(mpl, value), buf, eqno); + } + break; +#endif + case O_EQ: + if (!(compare_symbols(mpl, value, bound) == 0)) + { strcpy(buf, format_symbol(mpl, bound)); + xassert(strlen(buf) < sizeof(buf)); + error(mpl, "%s%s = %s not = %s", + par->name, format_tuple(mpl, '[', tuple), + format_symbol(mpl, value), buf, eqno); + } + break; +#if 1 /* 13/VIII-2008 */ + case O_GE: + if (!(compare_symbols(mpl, value, bound) >= 0)) + { strcpy(buf, format_symbol(mpl, bound)); + xassert(strlen(buf) < sizeof(buf)); + error(mpl, "%s%s = %s not >= %s", + par->name, format_tuple(mpl, '[', tuple), + format_symbol(mpl, value), buf, eqno); + } + break; + case O_GT: + if (!(compare_symbols(mpl, value, bound) > 0)) + { strcpy(buf, format_symbol(mpl, bound)); + xassert(strlen(buf) < sizeof(buf)); + error(mpl, "%s%s = %s not > %s", + par->name, format_tuple(mpl, '[', tuple), + format_symbol(mpl, value), buf, eqno); + } + break; +#endif + case O_NE: + if (!(compare_symbols(mpl, value, bound) != 0)) + { strcpy(buf, format_symbol(mpl, bound)); + xassert(strlen(buf) < sizeof(buf)); + error(mpl, "%s%s = %s not <> %s", + par->name, format_tuple(mpl, '[', tuple), + format_symbol(mpl, value), buf, eqno); + } + break; + default: + xassert(cond != cond); + } + delete_symbol(mpl, bound); + } + /* the value must be in all specified supersets */ + for (in = par->in, eqno = 1; in != NULL; in = in->next, eqno++) + { TUPLE *dummy; + xassert(in->code != NULL); + xassert(in->code->dim == 1); + dummy = expand_tuple(mpl, create_tuple(mpl), copy_symbol(mpl, + value)); + if (!is_member(mpl, in->code, dummy)) + error(mpl, "%s%s = %s not in specified set; see (%d)", + par->name, format_tuple(mpl, '[', tuple), + format_symbol(mpl, value), eqno); + delete_tuple(mpl, dummy); + } + return; +} + +/*---------------------------------------------------------------------- +-- take_member_sym - obtain symb. value assigned to parameter member. +-- +-- This routine obtains a symbolic value assigned to member of specified +-- symbolic model parameter and returns it on exit. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +SYMBOL *take_member_sym /* returns value, not reference */ +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ MEMBER *memb; + SYMBOL *value; + /* find member in the parameter array */ + memb = find_member(mpl, par->array, tuple); + if (memb != NULL) + { /* member exists, so just take its value */ + value = copy_symbol(mpl, memb->value.sym); + } + else if (par->assign != NULL) + { /* compute value using assignment expression */ + value = eval_symbolic(mpl, par->assign); +add: /* check that the value satisfies to all restrictions, assign + it to new member, and add the member to the array */ + check_value_sym(mpl, par, tuple, value); + memb = add_member(mpl, par->array, copy_tuple(mpl, tuple)); + memb->value.sym = copy_symbol(mpl, value); + } + else if (par->option != NULL) + { /* compute default value */ + value = eval_symbolic(mpl, par->option); + goto add; + } + else if (par->defval != NULL) + { /* take default value provided in the data section */ + value = copy_symbol(mpl, par->defval); + goto add; + } + else + { /* no value is provided */ + error(mpl, "no value for %s%s", par->name, format_tuple(mpl, + '[', tuple)); + } + return value; +} + +/*---------------------------------------------------------------------- +-- eval_member_sym - evaluate symb. value assigned to parameter member. +-- +-- This routine evaluates a symbolic value assigned to given member of +-- specified symbolic model parameter and returns it on exit. */ + +struct eval_sym_info +{ /* working info used by the routine eval_member_sym */ + PARAMETER *par; + /* model parameter */ + TUPLE *tuple; + /* n-tuple, which defines parameter member */ + MEMBER *memb; + /* normally this pointer is NULL; the routine uses this pointer + to check data provided in the data section, in which case it + points to a member currently checked; this check is performed + automatically only once when a reference to any member occurs + for the first time */ + SYMBOL *value; + /* evaluated symbolic value */ +}; + +static void eval_sym_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine to work within domain scope */ + struct eval_sym_info *info = _info; + if (info->memb != NULL) + { /* checking call; check symbolic value being assigned */ + check_value_sym(mpl, info->par, info->memb->tuple, + info->memb->value.sym); + } + else + { /* normal call; evaluate member, which has given n-tuple */ + info->value = take_member_sym(mpl, info->par, info->tuple); + } + return; +} + +SYMBOL *eval_member_sym /* returns value, not reference */ +( MPL *mpl, + PARAMETER *par, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ /* this routine evaluates symbolic parameter member */ + struct eval_sym_info _info, *info = &_info; + xassert(par->type == A_SYMBOLIC); + xassert(par->dim == tuple_dimen(mpl, tuple)); + info->par = par; + info->tuple = tuple; + if (par->data == 1) + { /* check data, which are provided in the data section, but not + checked yet */ + /* save pointer to the last array member; note that during the + check new members may be added beyond the last member due to + references to the same parameter from default expression as + well as from expressions that define restricting conditions; + however, values assigned to the new members will be checked + by other routine, so we don't need to check them here */ + MEMBER *tail = par->array->tail; + /* change the data status to prevent infinite recursive loop + due to references to the same parameter during the check */ + par->data = 2; + /* check values assigned to array members in the data section + until the marked member has been reached */ + for (info->memb = par->array->head; info->memb != NULL; + info->memb = info->memb->next) + { if (eval_within_domain(mpl, par->domain, info->memb->tuple, + info, eval_sym_func)) + out_of_domain(mpl, par->name, info->memb->tuple); + if (info->memb == tail) break; + } + /* the check has been finished */ + } + /* evaluate member, which has given n-tuple */ + info->memb = NULL; + if (eval_within_domain(mpl, info->par->domain, info->tuple, info, + eval_sym_func)) + out_of_domain(mpl, par->name, info->tuple); + /* bring evaluated value to the calling program */ + return info->value; +} + +/*---------------------------------------------------------------------- +-- eval_whole_par - evaluate model parameter over entire domain. +-- +-- This routine evaluates all members of specified model parameter over +-- entire domain. */ + +static int whole_par_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + PARAMETER *par = (PARAMETER *)info; + TUPLE *tuple = get_domain_tuple(mpl, par->domain); + switch (par->type) + { case A_NUMERIC: + case A_INTEGER: + case A_BINARY: + eval_member_num(mpl, par, tuple); + break; + case A_SYMBOLIC: + delete_symbol(mpl, eval_member_sym(mpl, par, tuple)); + break; + default: + xassert(par != par); + } + delete_tuple(mpl, tuple); + return 0; +} + +void eval_whole_par(MPL *mpl, PARAMETER *par) +{ loop_within_domain(mpl, par->domain, par, whole_par_func); + return; +} + +/*---------------------------------------------------------------------- +-- clean_parameter - clean model parameter. +-- +-- This routine cleans specified model parameter that assumes deleting +-- all stuff dynamically allocated during the generation phase. */ + +void clean_parameter(MPL *mpl, PARAMETER *par) +{ CONDITION *cond; + WITHIN *in; + MEMBER *memb; + /* clean subscript domain */ + clean_domain(mpl, par->domain); + /* clean pseudo-code for computing restricting conditions */ + for (cond = par->cond; cond != NULL; cond = cond->next) + clean_code(mpl, cond->code); + /* clean pseudo-code for computing restricting supersets */ + for (in = par->in; in != NULL; in = in->next) + clean_code(mpl, in->code); + /* clean pseudo-code for computing assigned value */ + clean_code(mpl, par->assign); + /* clean pseudo-code for computing default value */ + clean_code(mpl, par->option); + /* reset data status flag */ + par->data = 0; + /* delete default symbolic value */ + if (par->defval != NULL) + delete_symbol(mpl, par->defval), par->defval = NULL; + /* delete content array */ + for (memb = par->array->head; memb != NULL; memb = memb->next) + delete_value(mpl, par->array->type, &memb->value); + delete_array(mpl, par->array), par->array = NULL; + return; +} + +/**********************************************************************/ +/* * * MODEL VARIABLES * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- take_member_var - obtain reference to elemental variable. +-- +-- This routine obtains a reference to elemental variable assigned to +-- given member of specified model variable and returns it on exit. If +-- necessary, new elemental variable is created. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +ELEMVAR *take_member_var /* returns reference */ +( MPL *mpl, + VARIABLE *var, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ MEMBER *memb; + ELEMVAR *refer; + /* find member in the variable array */ + memb = find_member(mpl, var->array, tuple); + if (memb != NULL) + { /* member exists, so just take the reference */ + refer = memb->value.var; + } + else + { /* member is referenced for the first time and therefore does + not exist; create new elemental variable, assign it to new + member, and add the member to the variable array */ + memb = add_member(mpl, var->array, copy_tuple(mpl, tuple)); + refer = (memb->value.var = + dmp_get_atom(mpl->elemvars, sizeof(ELEMVAR))); + refer->j = 0; + refer->var = var; + refer->memb = memb; + /* compute lower bound */ + if (var->lbnd == NULL) + refer->lbnd = 0.0; + else + refer->lbnd = eval_numeric(mpl, var->lbnd); + /* compute upper bound */ + if (var->ubnd == NULL) + refer->ubnd = 0.0; + else if (var->ubnd == var->lbnd) + refer->ubnd = refer->lbnd; + else + refer->ubnd = eval_numeric(mpl, var->ubnd); + /* nullify working quantity */ + refer->temp = 0.0; +#if 1 /* 15/V-2010 */ + /* solution has not been obtained by the solver yet */ + refer->stat = 0; + refer->prim = refer->dual = 0.0; +#endif + } + return refer; +} + +/*---------------------------------------------------------------------- +-- eval_member_var - evaluate reference to elemental variable. +-- +-- This routine evaluates a reference to elemental variable assigned to +-- member of specified model variable and returns it on exit. */ + +struct eval_var_info +{ /* working info used by the routine eval_member_var */ + VARIABLE *var; + /* model variable */ + TUPLE *tuple; + /* n-tuple, which defines variable member */ + ELEMVAR *refer; + /* evaluated reference to elemental variable */ +}; + +static void eval_var_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine to work within domain scope */ + struct eval_var_info *info = _info; + info->refer = take_member_var(mpl, info->var, info->tuple); + return; +} + +ELEMVAR *eval_member_var /* returns reference */ +( MPL *mpl, + VARIABLE *var, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ /* this routine evaluates variable member */ + struct eval_var_info _info, *info = &_info; + xassert(var->dim == tuple_dimen(mpl, tuple)); + info->var = var; + info->tuple = tuple; + /* evaluate member, which has given n-tuple */ + if (eval_within_domain(mpl, info->var->domain, info->tuple, info, + eval_var_func)) + out_of_domain(mpl, var->name, info->tuple); + /* bring evaluated reference to the calling program */ + return info->refer; +} + +/*---------------------------------------------------------------------- +-- eval_whole_var - evaluate model variable over entire domain. +-- +-- This routine evaluates all members of specified model variable over +-- entire domain. */ + +static int whole_var_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + VARIABLE *var = (VARIABLE *)info; + TUPLE *tuple = get_domain_tuple(mpl, var->domain); + eval_member_var(mpl, var, tuple); + delete_tuple(mpl, tuple); + return 0; +} + +void eval_whole_var(MPL *mpl, VARIABLE *var) +{ loop_within_domain(mpl, var->domain, var, whole_var_func); + return; +} + +/*---------------------------------------------------------------------- +-- clean_variable - clean model variable. +-- +-- This routine cleans specified model variable that assumes deleting +-- all stuff dynamically allocated during the generation phase. */ + +void clean_variable(MPL *mpl, VARIABLE *var) +{ MEMBER *memb; + /* clean subscript domain */ + clean_domain(mpl, var->domain); + /* clean code for computing lower bound */ + clean_code(mpl, var->lbnd); + /* clean code for computing upper bound */ + if (var->ubnd != var->lbnd) clean_code(mpl, var->ubnd); + /* delete content array */ + for (memb = var->array->head; memb != NULL; memb = memb->next) + dmp_free_atom(mpl->elemvars, memb->value.var, sizeof(ELEMVAR)); + delete_array(mpl, var->array), var->array = NULL; + return; +} + +/**********************************************************************/ +/* * * MODEL CONSTRAINTS AND OBJECTIVES * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- take_member_con - obtain reference to elemental constraint. +-- +-- This routine obtains a reference to elemental constraint assigned +-- to given member of specified model constraint and returns it on exit. +-- If necessary, new elemental constraint is created. +-- +-- NOTE: This routine must not be called out of domain scope. */ + +ELEMCON *take_member_con /* returns reference */ +( MPL *mpl, + CONSTRAINT *con, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ MEMBER *memb; + ELEMCON *refer; + /* find member in the constraint array */ + memb = find_member(mpl, con->array, tuple); + if (memb != NULL) + { /* member exists, so just take the reference */ + refer = memb->value.con; + } + else + { /* member is referenced for the first time and therefore does + not exist; create new elemental constraint, assign it to new + member, and add the member to the constraint array */ + memb = add_member(mpl, con->array, copy_tuple(mpl, tuple)); + refer = (memb->value.con = + dmp_get_atom(mpl->elemcons, sizeof(ELEMCON))); + refer->i = 0; + refer->con = con; + refer->memb = memb; + /* compute linear form */ + xassert(con->code != NULL); + refer->form = eval_formula(mpl, con->code); + /* compute lower and upper bounds */ + if (con->lbnd == NULL && con->ubnd == NULL) + { /* objective has no bounds */ + double temp; + xassert(con->type == A_MINIMIZE || con->type == A_MAXIMIZE); + /* carry the constant term to the right-hand side */ + refer->form = remove_constant(mpl, refer->form, &temp); + refer->lbnd = refer->ubnd = - temp; + } + else if (con->lbnd != NULL && con->ubnd == NULL) + { /* constraint a * x + b >= c * y + d is transformed to the + standard form a * x - c * y >= d - b */ + double temp; + xassert(con->type == A_CONSTRAINT); + refer->form = linear_comb(mpl, + +1.0, refer->form, + -1.0, eval_formula(mpl, con->lbnd)); + refer->form = remove_constant(mpl, refer->form, &temp); + refer->lbnd = - temp; + refer->ubnd = 0.0; + } + else if (con->lbnd == NULL && con->ubnd != NULL) + { /* constraint a * x + b <= c * y + d is transformed to the + standard form a * x - c * y <= d - b */ + double temp; + xassert(con->type == A_CONSTRAINT); + refer->form = linear_comb(mpl, + +1.0, refer->form, + -1.0, eval_formula(mpl, con->ubnd)); + refer->form = remove_constant(mpl, refer->form, &temp); + refer->lbnd = 0.0; + refer->ubnd = - temp; + } + else if (con->lbnd == con->ubnd) + { /* constraint a * x + b = c * y + d is transformed to the + standard form a * x - c * y = d - b */ + double temp; + xassert(con->type == A_CONSTRAINT); + refer->form = linear_comb(mpl, + +1.0, refer->form, + -1.0, eval_formula(mpl, con->lbnd)); + refer->form = remove_constant(mpl, refer->form, &temp); + refer->lbnd = refer->ubnd = - temp; + } + else + { /* ranged constraint c <= a * x + b <= d is transformed to + the standard form c - b <= a * x <= d - b */ + double temp, temp1, temp2; + xassert(con->type == A_CONSTRAINT); + refer->form = remove_constant(mpl, refer->form, &temp); + xassert(remove_constant(mpl, eval_formula(mpl, con->lbnd), + &temp1) == NULL); + xassert(remove_constant(mpl, eval_formula(mpl, con->ubnd), + &temp2) == NULL); + refer->lbnd = fp_sub(mpl, temp1, temp); + refer->ubnd = fp_sub(mpl, temp2, temp); + } +#if 1 /* 15/V-2010 */ + /* solution has not been obtained by the solver yet */ + refer->stat = 0; + refer->prim = refer->dual = 0.0; +#endif + } + return refer; +} + +/*---------------------------------------------------------------------- +-- eval_member_con - evaluate reference to elemental constraint. +-- +-- This routine evaluates a reference to elemental constraint assigned +-- to member of specified model constraint and returns it on exit. */ + +struct eval_con_info +{ /* working info used by the routine eval_member_con */ + CONSTRAINT *con; + /* model constraint */ + TUPLE *tuple; + /* n-tuple, which defines constraint member */ + ELEMCON *refer; + /* evaluated reference to elemental constraint */ +}; + +static void eval_con_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine to work within domain scope */ + struct eval_con_info *info = _info; + info->refer = take_member_con(mpl, info->con, info->tuple); + return; +} + +ELEMCON *eval_member_con /* returns reference */ +( MPL *mpl, + CONSTRAINT *con, /* not changed */ + TUPLE *tuple /* not changed */ +) +{ /* this routine evaluates constraint member */ + struct eval_con_info _info, *info = &_info; + xassert(con->dim == tuple_dimen(mpl, tuple)); + info->con = con; + info->tuple = tuple; + /* evaluate member, which has given n-tuple */ + if (eval_within_domain(mpl, info->con->domain, info->tuple, info, + eval_con_func)) + out_of_domain(mpl, con->name, info->tuple); + /* bring evaluated reference to the calling program */ + return info->refer; +} + +/*---------------------------------------------------------------------- +-- eval_whole_con - evaluate model constraint over entire domain. +-- +-- This routine evaluates all members of specified model constraint over +-- entire domain. */ + +static int whole_con_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + CONSTRAINT *con = (CONSTRAINT *)info; + TUPLE *tuple = get_domain_tuple(mpl, con->domain); + eval_member_con(mpl, con, tuple); + delete_tuple(mpl, tuple); + return 0; +} + +void eval_whole_con(MPL *mpl, CONSTRAINT *con) +{ loop_within_domain(mpl, con->domain, con, whole_con_func); + return; +} + +/*---------------------------------------------------------------------- +-- clean_constraint - clean model constraint. +-- +-- This routine cleans specified model constraint that assumes deleting +-- all stuff dynamically allocated during the generation phase. */ + +void clean_constraint(MPL *mpl, CONSTRAINT *con) +{ MEMBER *memb; + /* clean subscript domain */ + clean_domain(mpl, con->domain); + /* clean code for computing main linear form */ + clean_code(mpl, con->code); + /* clean code for computing lower bound */ + clean_code(mpl, con->lbnd); + /* clean code for computing upper bound */ + if (con->ubnd != con->lbnd) clean_code(mpl, con->ubnd); + /* delete content array */ + for (memb = con->array->head; memb != NULL; memb = memb->next) + { delete_formula(mpl, memb->value.con->form); + dmp_free_atom(mpl->elemcons, memb->value.con, sizeof(ELEMCON)); + } + delete_array(mpl, con->array), con->array = NULL; + return; +} + +/**********************************************************************/ +/* * * PSEUDO-CODE * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- eval_numeric - evaluate pseudo-code to determine numeric value. +-- +-- This routine evaluates specified pseudo-code to determine resultant +-- numeric value, which is returned on exit. */ + +struct iter_num_info +{ /* working info used by the routine iter_num_func */ + CODE *code; + /* pseudo-code for iterated operation to be performed */ + double value; + /* resultant value */ +}; + +static int iter_num_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine used to perform iterated operation + on numeric "integrand" within domain scope */ + struct iter_num_info *info = _info; + double temp; + temp = eval_numeric(mpl, info->code->arg.loop.x); + switch (info->code->op) + { case O_SUM: + /* summation over domain */ + info->value = fp_add(mpl, info->value, temp); + break; + case O_PROD: + /* multiplication over domain */ + info->value = fp_mul(mpl, info->value, temp); + break; + case O_MINIMUM: + /* minimum over domain */ + if (info->value > temp) info->value = temp; + break; + case O_MAXIMUM: + /* maximum over domain */ + if (info->value < temp) info->value = temp; + break; + default: + xassert(info != info); + } + return 0; +} + +double eval_numeric(MPL *mpl, CODE *code) +{ double value; + xassert(code != NULL); + xassert(code->type == A_NUMERIC); + xassert(code->dim == 0); + /* if the operation has a side effect, invalidate and delete the + resultant value */ + if (code->vflag && code->valid) + { code->valid = 0; + delete_value(mpl, code->type, &code->value); + } + /* if resultant value is valid, no evaluation is needed */ + if (code->valid) + { value = code->value.num; + goto done; + } + /* evaluate pseudo-code recursively */ + switch (code->op) + { case O_NUMBER: + /* take floating-point number */ + value = code->arg.num; + break; + case O_MEMNUM: + /* take member of numeric parameter */ + { TUPLE *tuple; + ARG_LIST *e; + tuple = create_tuple(mpl); + for (e = code->arg.par.list; e != NULL; e = e->next) + tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, + e->x)); + value = eval_member_num(mpl, code->arg.par.par, tuple); + delete_tuple(mpl, tuple); + } + break; + case O_MEMVAR: + /* take computed value of elemental variable */ + { TUPLE *tuple; + ARG_LIST *e; +#if 1 /* 15/V-2010 */ + ELEMVAR *var; +#endif + tuple = create_tuple(mpl); + for (e = code->arg.var.list; e != NULL; e = e->next) + tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, + e->x)); +#if 0 /* 15/V-2010 */ + value = eval_member_var(mpl, code->arg.var.var, tuple) + ->value; +#else + var = eval_member_var(mpl, code->arg.var.var, tuple); + switch (code->arg.var.suff) + { case DOT_LB: + if (var->var->lbnd == NULL) + value = -DBL_MAX; + else + value = var->lbnd; + break; + case DOT_UB: + if (var->var->ubnd == NULL) + value = +DBL_MAX; + else + value = var->ubnd; + break; + case DOT_STATUS: + value = var->stat; + break; + case DOT_VAL: + value = var->prim; + break; + case DOT_DUAL: + value = var->dual; + break; + default: + xassert(code != code); + } +#endif + delete_tuple(mpl, tuple); + } + break; +#if 1 /* 15/V-2010 */ + case O_MEMCON: + /* take computed value of elemental constraint */ + { TUPLE *tuple; + ARG_LIST *e; + ELEMCON *con; + tuple = create_tuple(mpl); + for (e = code->arg.con.list; e != NULL; e = e->next) + tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, + e->x)); + con = eval_member_con(mpl, code->arg.con.con, tuple); + switch (code->arg.con.suff) + { case DOT_LB: + if (con->con->lbnd == NULL) + value = -DBL_MAX; + else + value = con->lbnd; + break; + case DOT_UB: + if (con->con->ubnd == NULL) + value = +DBL_MAX; + else + value = con->ubnd; + break; + case DOT_STATUS: + value = con->stat; + break; + case DOT_VAL: + value = con->prim; + break; + case DOT_DUAL: + value = con->dual; + break; + default: + xassert(code != code); + } + delete_tuple(mpl, tuple); + } + break; +#endif + case O_IRAND224: + /* pseudo-random in [0, 2^24-1] */ + value = fp_irand224(mpl); + break; + case O_UNIFORM01: + /* pseudo-random in [0, 1) */ + value = fp_uniform01(mpl); + break; + case O_NORMAL01: + /* gaussian random, mu = 0, sigma = 1 */ + value = fp_normal01(mpl); + break; + case O_GMTIME: + /* current calendar time */ + value = fn_gmtime(mpl); + break; + case O_CVTNUM: + /* conversion to numeric */ + { SYMBOL *sym; + sym = eval_symbolic(mpl, code->arg.arg.x); +#if 0 /* 23/XI-2008 */ + if (sym->str != NULL) + error(mpl, "cannot convert %s to floating-point numbe" + "r", format_symbol(mpl, sym)); + value = sym->num; +#else + if (sym->str == NULL) + value = sym->num; + else + { if (str2num(sym->str, &value)) + error(mpl, "cannot convert %s to floating-point nu" + "mber", format_symbol(mpl, sym)); + } +#endif + delete_symbol(mpl, sym); + } + break; + case O_PLUS: + /* unary plus */ + value = + eval_numeric(mpl, code->arg.arg.x); + break; + case O_MINUS: + /* unary minus */ + value = - eval_numeric(mpl, code->arg.arg.x); + break; + case O_ABS: + /* absolute value */ + value = fabs(eval_numeric(mpl, code->arg.arg.x)); + break; + case O_CEIL: + /* round upward ("ceiling of x") */ + value = ceil(eval_numeric(mpl, code->arg.arg.x)); + break; + case O_FLOOR: + /* round downward ("floor of x") */ + value = floor(eval_numeric(mpl, code->arg.arg.x)); + break; + case O_EXP: + /* base-e exponential */ + value = fp_exp(mpl, eval_numeric(mpl, code->arg.arg.x)); + break; + case O_LOG: + /* natural logarithm */ + value = fp_log(mpl, eval_numeric(mpl, code->arg.arg.x)); + break; + case O_LOG10: + /* common (decimal) logarithm */ + value = fp_log10(mpl, eval_numeric(mpl, code->arg.arg.x)); + break; + case O_SQRT: + /* square root */ + value = fp_sqrt(mpl, eval_numeric(mpl, code->arg.arg.x)); + break; + case O_SIN: + /* trigonometric sine */ + value = fp_sin(mpl, eval_numeric(mpl, code->arg.arg.x)); + break; + case O_COS: + /* trigonometric cosine */ + value = fp_cos(mpl, eval_numeric(mpl, code->arg.arg.x)); + break; + case O_ATAN: + /* trigonometric arctangent (one argument) */ + value = fp_atan(mpl, eval_numeric(mpl, code->arg.arg.x)); + break; + case O_ATAN2: + /* trigonometric arctangent (two arguments) */ + value = fp_atan2(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_ROUND: + /* round to nearest integer */ + value = fp_round(mpl, + eval_numeric(mpl, code->arg.arg.x), 0.0); + break; + case O_ROUND2: + /* round to n fractional digits */ + value = fp_round(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_TRUNC: + /* truncate to nearest integer */ + value = fp_trunc(mpl, + eval_numeric(mpl, code->arg.arg.x), 0.0); + break; + case O_TRUNC2: + /* truncate to n fractional digits */ + value = fp_trunc(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_ADD: + /* addition */ + value = fp_add(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_SUB: + /* subtraction */ + value = fp_sub(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_LESS: + /* non-negative subtraction */ + value = fp_less(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_MUL: + /* multiplication */ + value = fp_mul(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_DIV: + /* division */ + value = fp_div(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_IDIV: + /* quotient of exact division */ + value = fp_idiv(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_MOD: + /* remainder of exact division */ + value = fp_mod(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_POWER: + /* exponentiation (raise to power) */ + value = fp_power(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_UNIFORM: + /* pseudo-random in [a, b) */ + value = fp_uniform(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_NORMAL: + /* gaussian random, given mu and sigma */ + value = fp_normal(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y)); + break; + case O_CARD: + { ELEMSET *set; + set = eval_elemset(mpl, code->arg.arg.x); + value = set->size; + delete_array(mpl, set); + } + break; + case O_LENGTH: + { SYMBOL *sym; + char str[MAX_LENGTH+1]; + sym = eval_symbolic(mpl, code->arg.arg.x); + if (sym->str == NULL) + sprintf(str, "%.*g", DBL_DIG, sym->num); + else + fetch_string(mpl, sym->str, str); + delete_symbol(mpl, sym); + value = strlen(str); + } + break; + case O_STR2TIME: + { SYMBOL *sym; + char str[MAX_LENGTH+1], fmt[MAX_LENGTH+1]; + sym = eval_symbolic(mpl, code->arg.arg.x); + if (sym->str == NULL) + sprintf(str, "%.*g", DBL_DIG, sym->num); + else + fetch_string(mpl, sym->str, str); + delete_symbol(mpl, sym); + sym = eval_symbolic(mpl, code->arg.arg.y); + if (sym->str == NULL) + sprintf(fmt, "%.*g", DBL_DIG, sym->num); + else + fetch_string(mpl, sym->str, fmt); + delete_symbol(mpl, sym); + value = fn_str2time(mpl, str, fmt); + } + break; + case O_FORK: + /* if-then-else */ + if (eval_logical(mpl, code->arg.arg.x)) + value = eval_numeric(mpl, code->arg.arg.y); + else if (code->arg.arg.z == NULL) + value = 0.0; + else + value = eval_numeric(mpl, code->arg.arg.z); + break; + case O_MIN: + /* minimal value (n-ary) */ + { ARG_LIST *e; + double temp; + value = +DBL_MAX; + for (e = code->arg.list; e != NULL; e = e->next) + { temp = eval_numeric(mpl, e->x); + if (value > temp) value = temp; + } + } + break; + case O_MAX: + /* maximal value (n-ary) */ + { ARG_LIST *e; + double temp; + value = -DBL_MAX; + for (e = code->arg.list; e != NULL; e = e->next) + { temp = eval_numeric(mpl, e->x); + if (value < temp) value = temp; + } + } + break; + case O_SUM: + /* summation over domain */ + { struct iter_num_info _info, *info = &_info; + info->code = code; + info->value = 0.0; + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_num_func); + value = info->value; + } + break; + case O_PROD: + /* multiplication over domain */ + { struct iter_num_info _info, *info = &_info; + info->code = code; + info->value = 1.0; + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_num_func); + value = info->value; + } + break; + case O_MINIMUM: + /* minimum over domain */ + { struct iter_num_info _info, *info = &_info; + info->code = code; + info->value = +DBL_MAX; + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_num_func); + if (info->value == +DBL_MAX) + error(mpl, "min{} over empty set; result undefined"); + value = info->value; + } + break; + case O_MAXIMUM: + /* maximum over domain */ + { struct iter_num_info _info, *info = &_info; + info->code = code; + info->value = -DBL_MAX; + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_num_func); + if (info->value == -DBL_MAX) + error(mpl, "max{} over empty set; result undefined"); + value = info->value; + } + break; + default: + xassert(code != code); + } + /* save resultant value */ + xassert(!code->valid); + code->valid = 1; + code->value.num = value; +done: return value; +} + +/*---------------------------------------------------------------------- +-- eval_symbolic - evaluate pseudo-code to determine symbolic value. +-- +-- This routine evaluates specified pseudo-code to determine resultant +-- symbolic value, which is returned on exit. */ + +SYMBOL *eval_symbolic(MPL *mpl, CODE *code) +{ SYMBOL *value; + xassert(code != NULL); + xassert(code->type == A_SYMBOLIC); + xassert(code->dim == 0); + /* if the operation has a side effect, invalidate and delete the + resultant value */ + if (code->vflag && code->valid) + { code->valid = 0; + delete_value(mpl, code->type, &code->value); + } + /* if resultant value is valid, no evaluation is needed */ + if (code->valid) + { value = copy_symbol(mpl, code->value.sym); + goto done; + } + /* evaluate pseudo-code recursively */ + switch (code->op) + { case O_STRING: + /* take character string */ + value = create_symbol_str(mpl, create_string(mpl, + code->arg.str)); + break; + case O_INDEX: + /* take dummy index */ + xassert(code->arg.index.slot->value != NULL); + value = copy_symbol(mpl, code->arg.index.slot->value); + break; + case O_MEMSYM: + /* take member of symbolic parameter */ + { TUPLE *tuple; + ARG_LIST *e; + tuple = create_tuple(mpl); + for (e = code->arg.par.list; e != NULL; e = e->next) + tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, + e->x)); + value = eval_member_sym(mpl, code->arg.par.par, tuple); + delete_tuple(mpl, tuple); + } + break; + case O_CVTSYM: + /* conversion to symbolic */ + value = create_symbol_num(mpl, eval_numeric(mpl, + code->arg.arg.x)); + break; + case O_CONCAT: + /* concatenation */ + value = concat_symbols(mpl, + eval_symbolic(mpl, code->arg.arg.x), + eval_symbolic(mpl, code->arg.arg.y)); + break; + case O_FORK: + /* if-then-else */ + if (eval_logical(mpl, code->arg.arg.x)) + value = eval_symbolic(mpl, code->arg.arg.y); + else if (code->arg.arg.z == NULL) + value = create_symbol_num(mpl, 0.0); + else + value = eval_symbolic(mpl, code->arg.arg.z); + break; + case O_SUBSTR: + case O_SUBSTR3: + { double pos, len; + char str[MAX_LENGTH+1]; + value = eval_symbolic(mpl, code->arg.arg.x); + if (value->str == NULL) + sprintf(str, "%.*g", DBL_DIG, value->num); + else + fetch_string(mpl, value->str, str); + delete_symbol(mpl, value); + if (code->op == O_SUBSTR) + { pos = eval_numeric(mpl, code->arg.arg.y); + if (pos != floor(pos)) + error(mpl, "substr('...', %.*g); non-integer secon" + "d argument", DBL_DIG, pos); + if (pos < 1 || pos > strlen(str) + 1) + error(mpl, "substr('...', %.*g); substring out of " + "range", DBL_DIG, pos); + } + else + { pos = eval_numeric(mpl, code->arg.arg.y); + len = eval_numeric(mpl, code->arg.arg.z); + if (pos != floor(pos) || len != floor(len)) + error(mpl, "substr('...', %.*g, %.*g); non-integer" + " second and/or third argument", DBL_DIG, pos, + DBL_DIG, len); + if (pos < 1 || len < 0 || pos + len > strlen(str) + 1) + error(mpl, "substr('...', %.*g, %.*g); substring o" + "ut of range", DBL_DIG, pos, DBL_DIG, len); + str[(int)pos + (int)len - 1] = '\0'; + } + value = create_symbol_str(mpl, create_string(mpl, str + + (int)pos - 1)); + } + break; + case O_TIME2STR: + { double num; + SYMBOL *sym; + char str[MAX_LENGTH+1], fmt[MAX_LENGTH+1]; + num = eval_numeric(mpl, code->arg.arg.x); + sym = eval_symbolic(mpl, code->arg.arg.y); + if (sym->str == NULL) + sprintf(fmt, "%.*g", DBL_DIG, sym->num); + else + fetch_string(mpl, sym->str, fmt); + delete_symbol(mpl, sym); + fn_time2str(mpl, str, num, fmt); + value = create_symbol_str(mpl, create_string(mpl, str)); + } + break; + default: + xassert(code != code); + } + /* save resultant value */ + xassert(!code->valid); + code->valid = 1; + code->value.sym = copy_symbol(mpl, value); +done: return value; +} + +/*---------------------------------------------------------------------- +-- eval_logical - evaluate pseudo-code to determine logical value. +-- +-- This routine evaluates specified pseudo-code to determine resultant +-- logical value, which is returned on exit. */ + +struct iter_log_info +{ /* working info used by the routine iter_log_func */ + CODE *code; + /* pseudo-code for iterated operation to be performed */ + int value; + /* resultant value */ +}; + +static int iter_log_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine used to perform iterated operation + on logical "integrand" within domain scope */ + struct iter_log_info *info = _info; + int ret = 0; + switch (info->code->op) + { case O_FORALL: + /* conjunction over domain */ + info->value &= eval_logical(mpl, info->code->arg.loop.x); + if (!info->value) ret = 1; + break; + case O_EXISTS: + /* disjunction over domain */ + info->value |= eval_logical(mpl, info->code->arg.loop.x); + if (info->value) ret = 1; + break; + default: + xassert(info != info); + } + return ret; +} + +int eval_logical(MPL *mpl, CODE *code) +{ int value; + xassert(code->type == A_LOGICAL); + xassert(code->dim == 0); + /* if the operation has a side effect, invalidate and delete the + resultant value */ + if (code->vflag && code->valid) + { code->valid = 0; + delete_value(mpl, code->type, &code->value); + } + /* if resultant value is valid, no evaluation is needed */ + if (code->valid) + { value = code->value.bit; + goto done; + } + /* evaluate pseudo-code recursively */ + switch (code->op) + { case O_CVTLOG: + /* conversion to logical */ + value = (eval_numeric(mpl, code->arg.arg.x) != 0.0); + break; + case O_NOT: + /* negation (logical "not") */ + value = !eval_logical(mpl, code->arg.arg.x); + break; + case O_LT: + /* comparison on 'less than' */ +#if 0 /* 02/VIII-2008 */ + value = (eval_numeric(mpl, code->arg.arg.x) < + eval_numeric(mpl, code->arg.arg.y)); +#else + xassert(code->arg.arg.x != NULL); + if (code->arg.arg.x->type == A_NUMERIC) + value = (eval_numeric(mpl, code->arg.arg.x) < + eval_numeric(mpl, code->arg.arg.y)); + else + { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); + SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); + value = (compare_symbols(mpl, sym1, sym2) < 0); + delete_symbol(mpl, sym1); + delete_symbol(mpl, sym2); + } +#endif + break; + case O_LE: + /* comparison on 'not greater than' */ +#if 0 /* 02/VIII-2008 */ + value = (eval_numeric(mpl, code->arg.arg.x) <= + eval_numeric(mpl, code->arg.arg.y)); +#else + xassert(code->arg.arg.x != NULL); + if (code->arg.arg.x->type == A_NUMERIC) + value = (eval_numeric(mpl, code->arg.arg.x) <= + eval_numeric(mpl, code->arg.arg.y)); + else + { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); + SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); + value = (compare_symbols(mpl, sym1, sym2) <= 0); + delete_symbol(mpl, sym1); + delete_symbol(mpl, sym2); + } +#endif + break; + case O_EQ: + /* comparison on 'equal to' */ + xassert(code->arg.arg.x != NULL); + if (code->arg.arg.x->type == A_NUMERIC) + value = (eval_numeric(mpl, code->arg.arg.x) == + eval_numeric(mpl, code->arg.arg.y)); + else + { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); + SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); + value = (compare_symbols(mpl, sym1, sym2) == 0); + delete_symbol(mpl, sym1); + delete_symbol(mpl, sym2); + } + break; + case O_GE: + /* comparison on 'not less than' */ +#if 0 /* 02/VIII-2008 */ + value = (eval_numeric(mpl, code->arg.arg.x) >= + eval_numeric(mpl, code->arg.arg.y)); +#else + xassert(code->arg.arg.x != NULL); + if (code->arg.arg.x->type == A_NUMERIC) + value = (eval_numeric(mpl, code->arg.arg.x) >= + eval_numeric(mpl, code->arg.arg.y)); + else + { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); + SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); + value = (compare_symbols(mpl, sym1, sym2) >= 0); + delete_symbol(mpl, sym1); + delete_symbol(mpl, sym2); + } +#endif + break; + case O_GT: + /* comparison on 'greater than' */ +#if 0 /* 02/VIII-2008 */ + value = (eval_numeric(mpl, code->arg.arg.x) > + eval_numeric(mpl, code->arg.arg.y)); +#else + xassert(code->arg.arg.x != NULL); + if (code->arg.arg.x->type == A_NUMERIC) + value = (eval_numeric(mpl, code->arg.arg.x) > + eval_numeric(mpl, code->arg.arg.y)); + else + { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); + SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); + value = (compare_symbols(mpl, sym1, sym2) > 0); + delete_symbol(mpl, sym1); + delete_symbol(mpl, sym2); + } +#endif + break; + case O_NE: + /* comparison on 'not equal to' */ + xassert(code->arg.arg.x != NULL); + if (code->arg.arg.x->type == A_NUMERIC) + value = (eval_numeric(mpl, code->arg.arg.x) != + eval_numeric(mpl, code->arg.arg.y)); + else + { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); + SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); + value = (compare_symbols(mpl, sym1, sym2) != 0); + delete_symbol(mpl, sym1); + delete_symbol(mpl, sym2); + } + break; + case O_AND: + /* conjunction (logical "and") */ + value = eval_logical(mpl, code->arg.arg.x) && + eval_logical(mpl, code->arg.arg.y); + break; + case O_OR: + /* disjunction (logical "or") */ + value = eval_logical(mpl, code->arg.arg.x) || + eval_logical(mpl, code->arg.arg.y); + break; + case O_IN: + /* test on 'x in Y' */ + { TUPLE *tuple; + tuple = eval_tuple(mpl, code->arg.arg.x); + value = is_member(mpl, code->arg.arg.y, tuple); + delete_tuple(mpl, tuple); + } + break; + case O_NOTIN: + /* test on 'x not in Y' */ + { TUPLE *tuple; + tuple = eval_tuple(mpl, code->arg.arg.x); + value = !is_member(mpl, code->arg.arg.y, tuple); + delete_tuple(mpl, tuple); + } + break; + case O_WITHIN: + /* test on 'X within Y' */ + { ELEMSET *set; + MEMBER *memb; + set = eval_elemset(mpl, code->arg.arg.x); + value = 1; + for (memb = set->head; memb != NULL; memb = memb->next) + { if (!is_member(mpl, code->arg.arg.y, memb->tuple)) + { value = 0; + break; + } + } + delete_elemset(mpl, set); + } + break; + case O_NOTWITHIN: + /* test on 'X not within Y' */ + { ELEMSET *set; + MEMBER *memb; + set = eval_elemset(mpl, code->arg.arg.x); + value = 1; + for (memb = set->head; memb != NULL; memb = memb->next) + { if (is_member(mpl, code->arg.arg.y, memb->tuple)) + { value = 0; + break; + } + } + delete_elemset(mpl, set); + } + break; + case O_FORALL: + /* conjunction (A-quantification) */ + { struct iter_log_info _info, *info = &_info; + info->code = code; + info->value = 1; + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_log_func); + value = info->value; + } + break; + case O_EXISTS: + /* disjunction (E-quantification) */ + { struct iter_log_info _info, *info = &_info; + info->code = code; + info->value = 0; + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_log_func); + value = info->value; + } + break; + default: + xassert(code != code); + } + /* save resultant value */ + xassert(!code->valid); + code->valid = 1; + code->value.bit = value; +done: return value; +} + +/*---------------------------------------------------------------------- +-- eval_tuple - evaluate pseudo-code to construct n-tuple. +-- +-- This routine evaluates specified pseudo-code to construct resultant +-- n-tuple, which is returned on exit. */ + +TUPLE *eval_tuple(MPL *mpl, CODE *code) +{ TUPLE *value; + xassert(code != NULL); + xassert(code->type == A_TUPLE); + xassert(code->dim > 0); + /* if the operation has a side effect, invalidate and delete the + resultant value */ + if (code->vflag && code->valid) + { code->valid = 0; + delete_value(mpl, code->type, &code->value); + } + /* if resultant value is valid, no evaluation is needed */ + if (code->valid) + { value = copy_tuple(mpl, code->value.tuple); + goto done; + } + /* evaluate pseudo-code recursively */ + switch (code->op) + { case O_TUPLE: + /* make n-tuple */ + { ARG_LIST *e; + value = create_tuple(mpl); + for (e = code->arg.list; e != NULL; e = e->next) + value = expand_tuple(mpl, value, eval_symbolic(mpl, + e->x)); + } + break; + case O_CVTTUP: + /* convert to 1-tuple */ + value = expand_tuple(mpl, create_tuple(mpl), + eval_symbolic(mpl, code->arg.arg.x)); + break; + default: + xassert(code != code); + } + /* save resultant value */ + xassert(!code->valid); + code->valid = 1; + code->value.tuple = copy_tuple(mpl, value); +done: return value; +} + +/*---------------------------------------------------------------------- +-- eval_elemset - evaluate pseudo-code to construct elemental set. +-- +-- This routine evaluates specified pseudo-code to construct resultant +-- elemental set, which is returned on exit. */ + +struct iter_set_info +{ /* working info used by the routine iter_set_func */ + CODE *code; + /* pseudo-code for iterated operation to be performed */ + ELEMSET *value; + /* resultant value */ +}; + +static int iter_set_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine used to perform iterated operation + on n-tuple "integrand" within domain scope */ + struct iter_set_info *info = _info; + TUPLE *tuple; + switch (info->code->op) + { case O_SETOF: + /* compute next n-tuple and add it to the set; in this case + duplicate n-tuples are silently ignored */ + tuple = eval_tuple(mpl, info->code->arg.loop.x); + if (find_tuple(mpl, info->value, tuple) == NULL) + add_tuple(mpl, info->value, tuple); + else + delete_tuple(mpl, tuple); + break; + case O_BUILD: + /* construct next n-tuple using current values assigned to + *free* dummy indices as its components and add it to the + set; in this case duplicate n-tuples cannot appear */ + add_tuple(mpl, info->value, get_domain_tuple(mpl, + info->code->arg.loop.domain)); + break; + default: + xassert(info != info); + } + return 0; +} + +ELEMSET *eval_elemset(MPL *mpl, CODE *code) +{ ELEMSET *value; + xassert(code != NULL); + xassert(code->type == A_ELEMSET); + xassert(code->dim > 0); + /* if the operation has a side effect, invalidate and delete the + resultant value */ + if (code->vflag && code->valid) + { code->valid = 0; + delete_value(mpl, code->type, &code->value); + } + /* if resultant value is valid, no evaluation is needed */ + if (code->valid) + { value = copy_elemset(mpl, code->value.set); + goto done; + } + /* evaluate pseudo-code recursively */ + switch (code->op) + { case O_MEMSET: + /* take member of set */ + { TUPLE *tuple; + ARG_LIST *e; + tuple = create_tuple(mpl); + for (e = code->arg.set.list; e != NULL; e = e->next) + tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, + e->x)); + value = copy_elemset(mpl, + eval_member_set(mpl, code->arg.set.set, tuple)); + delete_tuple(mpl, tuple); + } + break; + case O_MAKE: + /* make elemental set of n-tuples */ + { ARG_LIST *e; + value = create_elemset(mpl, code->dim); + for (e = code->arg.list; e != NULL; e = e->next) + check_then_add(mpl, value, eval_tuple(mpl, e->x)); + } + break; + case O_UNION: + /* union of two elemental sets */ + value = set_union(mpl, + eval_elemset(mpl, code->arg.arg.x), + eval_elemset(mpl, code->arg.arg.y)); + break; + case O_DIFF: + /* difference between two elemental sets */ + value = set_diff(mpl, + eval_elemset(mpl, code->arg.arg.x), + eval_elemset(mpl, code->arg.arg.y)); + break; + case O_SYMDIFF: + /* symmetric difference between two elemental sets */ + value = set_symdiff(mpl, + eval_elemset(mpl, code->arg.arg.x), + eval_elemset(mpl, code->arg.arg.y)); + break; + case O_INTER: + /* intersection of two elemental sets */ + value = set_inter(mpl, + eval_elemset(mpl, code->arg.arg.x), + eval_elemset(mpl, code->arg.arg.y)); + break; + case O_CROSS: + /* cross (Cartesian) product of two elemental sets */ + value = set_cross(mpl, + eval_elemset(mpl, code->arg.arg.x), + eval_elemset(mpl, code->arg.arg.y)); + break; + case O_DOTS: + /* build "arithmetic" elemental set */ + value = create_arelset(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_numeric(mpl, code->arg.arg.y), + code->arg.arg.z == NULL ? 1.0 : eval_numeric(mpl, + code->arg.arg.z)); + break; + case O_FORK: + /* if-then-else */ + if (eval_logical(mpl, code->arg.arg.x)) + value = eval_elemset(mpl, code->arg.arg.y); + else + value = eval_elemset(mpl, code->arg.arg.z); + break; + case O_SETOF: + /* compute elemental set */ + { struct iter_set_info _info, *info = &_info; + info->code = code; + info->value = create_elemset(mpl, code->dim); + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_set_func); + value = info->value; + } + break; + case O_BUILD: + /* build elemental set identical to domain set */ + { struct iter_set_info _info, *info = &_info; + info->code = code; + info->value = create_elemset(mpl, code->dim); + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_set_func); + value = info->value; + } + break; + default: + xassert(code != code); + } + /* save resultant value */ + xassert(!code->valid); + code->valid = 1; + code->value.set = copy_elemset(mpl, value); +done: return value; +} + +/*---------------------------------------------------------------------- +-- is_member - check if n-tuple is in set specified by pseudo-code. +-- +-- This routine checks if given n-tuple is a member of elemental set +-- specified in the form of pseudo-code (i.e. by expression). +-- +-- The n-tuple may have more components that dimension of the elemental +-- set, in which case the extra components are ignored. */ + +static void null_func(MPL *mpl, void *info) +{ /* this is dummy routine used to enter the domain scope */ + xassert(mpl == mpl); + xassert(info == NULL); + return; +} + +int is_member(MPL *mpl, CODE *code, TUPLE *tuple) +{ int value; + xassert(code != NULL); + xassert(code->type == A_ELEMSET); + xassert(code->dim > 0); + xassert(tuple != NULL); + switch (code->op) + { case O_MEMSET: + /* check if given n-tuple is member of elemental set, which + is assigned to member of model set */ + { ARG_LIST *e; + TUPLE *temp; + ELEMSET *set; + /* evaluate reference to elemental set */ + temp = create_tuple(mpl); + for (e = code->arg.set.list; e != NULL; e = e->next) + temp = expand_tuple(mpl, temp, eval_symbolic(mpl, + e->x)); + set = eval_member_set(mpl, code->arg.set.set, temp); + delete_tuple(mpl, temp); + /* check if the n-tuple is contained in the set array */ + temp = build_subtuple(mpl, tuple, set->dim); + value = (find_tuple(mpl, set, temp) != NULL); + delete_tuple(mpl, temp); + } + break; + case O_MAKE: + /* check if given n-tuple is member of literal set */ + { ARG_LIST *e; + TUPLE *temp, *that; + value = 0; + temp = build_subtuple(mpl, tuple, code->dim); + for (e = code->arg.list; e != NULL; e = e->next) + { that = eval_tuple(mpl, e->x); + value = (compare_tuples(mpl, temp, that) == 0); + delete_tuple(mpl, that); + if (value) break; + } + delete_tuple(mpl, temp); + } + break; + case O_UNION: + value = is_member(mpl, code->arg.arg.x, tuple) || + is_member(mpl, code->arg.arg.y, tuple); + break; + case O_DIFF: + value = is_member(mpl, code->arg.arg.x, tuple) && + !is_member(mpl, code->arg.arg.y, tuple); + break; + case O_SYMDIFF: + { int in1 = is_member(mpl, code->arg.arg.x, tuple); + int in2 = is_member(mpl, code->arg.arg.y, tuple); + value = (in1 && !in2) || (!in1 && in2); + } + break; + case O_INTER: + value = is_member(mpl, code->arg.arg.x, tuple) && + is_member(mpl, code->arg.arg.y, tuple); + break; + case O_CROSS: + { int j; + value = is_member(mpl, code->arg.arg.x, tuple); + if (value) + { for (j = 1; j <= code->arg.arg.x->dim; j++) + { xassert(tuple != NULL); + tuple = tuple->next; + } + value = is_member(mpl, code->arg.arg.y, tuple); + } + } + break; + case O_DOTS: + /* check if given 1-tuple is member of "arithmetic" set */ + { int j; + double x, t0, tf, dt; + xassert(code->dim == 1); + /* compute "parameters" of the "arithmetic" set */ + t0 = eval_numeric(mpl, code->arg.arg.x); + tf = eval_numeric(mpl, code->arg.arg.y); + if (code->arg.arg.z == NULL) + dt = 1.0; + else + dt = eval_numeric(mpl, code->arg.arg.z); + /* make sure the parameters are correct */ + arelset_size(mpl, t0, tf, dt); + /* if component of 1-tuple is symbolic, not numeric, the + 1-tuple cannot be member of "arithmetic" set */ + xassert(tuple->sym != NULL); + if (tuple->sym->str != NULL) + { value = 0; + break; + } + /* determine numeric value of the component */ + x = tuple->sym->num; + /* if the component value is out of the set range, the + 1-tuple is not in the set */ + if (dt > 0.0 && !(t0 <= x && x <= tf) || + dt < 0.0 && !(tf <= x && x <= t0)) + { value = 0; + break; + } + /* estimate ordinal number of the 1-tuple in the set */ + j = (int)(((x - t0) / dt) + 0.5) + 1; + /* perform the main check */ + value = (arelset_member(mpl, t0, tf, dt, j) == x); + } + break; + case O_FORK: + /* check if given n-tuple is member of conditional set */ + if (eval_logical(mpl, code->arg.arg.x)) + value = is_member(mpl, code->arg.arg.y, tuple); + else + value = is_member(mpl, code->arg.arg.z, tuple); + break; + case O_SETOF: + /* check if given n-tuple is member of computed set */ + /* it is not clear how to efficiently perform the check not + computing the entire elemental set :+( */ + error(mpl, "implementation restriction; in/within setof{} n" + "ot allowed"); + break; + case O_BUILD: + /* check if given n-tuple is member of domain set */ + { TUPLE *temp; + temp = build_subtuple(mpl, tuple, code->dim); + /* try to enter the domain scope; if it is successful, + the n-tuple is in the domain set */ + value = (eval_within_domain(mpl, code->arg.loop.domain, + temp, NULL, null_func) == 0); + delete_tuple(mpl, temp); + } + break; + default: + xassert(code != code); + } + return value; +} + +/*---------------------------------------------------------------------- +-- eval_formula - evaluate pseudo-code to construct linear form. +-- +-- This routine evaluates specified pseudo-code to construct resultant +-- linear form, which is returned on exit. */ + +struct iter_form_info +{ /* working info used by the routine iter_form_func */ + CODE *code; + /* pseudo-code for iterated operation to be performed */ + FORMULA *value; + /* resultant value */ + FORMULA *tail; + /* pointer to the last term */ +}; + +static int iter_form_func(MPL *mpl, void *_info) +{ /* this is auxiliary routine used to perform iterated operation + on linear form "integrand" within domain scope */ + struct iter_form_info *info = _info; + switch (info->code->op) + { case O_SUM: + /* summation over domain */ +#if 0 + info->value = + linear_comb(mpl, + +1.0, info->value, + +1.0, eval_formula(mpl, info->code->arg.loop.x)); +#else + /* the routine linear_comb needs to look through all terms + of both linear forms to reduce identical terms, so using + it here is not a good idea (for example, evaluation of + sum{i in 1..n} x[i] required quadratic time); the better + idea is to gather all terms of the integrand in one list + and reduce identical terms only once after all terms of + the resultant linear form have been evaluated */ + { FORMULA *form, *term; + form = eval_formula(mpl, info->code->arg.loop.x); + if (info->value == NULL) + { xassert(info->tail == NULL); + info->value = form; + } + else + { xassert(info->tail != NULL); + info->tail->next = form; + } + for (term = form; term != NULL; term = term->next) + info->tail = term; + } +#endif + break; + default: + xassert(info != info); + } + return 0; +} + +FORMULA *eval_formula(MPL *mpl, CODE *code) +{ FORMULA *value; + xassert(code != NULL); + xassert(code->type == A_FORMULA); + xassert(code->dim == 0); + /* if the operation has a side effect, invalidate and delete the + resultant value */ + if (code->vflag && code->valid) + { code->valid = 0; + delete_value(mpl, code->type, &code->value); + } + /* if resultant value is valid, no evaluation is needed */ + if (code->valid) + { value = copy_formula(mpl, code->value.form); + goto done; + } + /* evaluate pseudo-code recursively */ + switch (code->op) + { case O_MEMVAR: + /* take member of variable */ + { TUPLE *tuple; + ARG_LIST *e; + tuple = create_tuple(mpl); + for (e = code->arg.var.list; e != NULL; e = e->next) + tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, + e->x)); +#if 1 /* 15/V-2010 */ + xassert(code->arg.var.suff == DOT_NONE); +#endif + value = single_variable(mpl, + eval_member_var(mpl, code->arg.var.var, tuple)); + delete_tuple(mpl, tuple); + } + break; + case O_CVTLFM: + /* convert to linear form */ + value = constant_term(mpl, eval_numeric(mpl, + code->arg.arg.x)); + break; + case O_PLUS: + /* unary plus */ + value = linear_comb(mpl, + 0.0, constant_term(mpl, 0.0), + +1.0, eval_formula(mpl, code->arg.arg.x)); + break; + case O_MINUS: + /* unary minus */ + value = linear_comb(mpl, + 0.0, constant_term(mpl, 0.0), + -1.0, eval_formula(mpl, code->arg.arg.x)); + break; + case O_ADD: + /* addition */ + value = linear_comb(mpl, + +1.0, eval_formula(mpl, code->arg.arg.x), + +1.0, eval_formula(mpl, code->arg.arg.y)); + break; + case O_SUB: + /* subtraction */ + value = linear_comb(mpl, + +1.0, eval_formula(mpl, code->arg.arg.x), + -1.0, eval_formula(mpl, code->arg.arg.y)); + break; + case O_MUL: + /* multiplication */ + xassert(code->arg.arg.x != NULL); + xassert(code->arg.arg.y != NULL); + if (code->arg.arg.x->type == A_NUMERIC) + { xassert(code->arg.arg.y->type == A_FORMULA); + value = linear_comb(mpl, + eval_numeric(mpl, code->arg.arg.x), + eval_formula(mpl, code->arg.arg.y), + 0.0, constant_term(mpl, 0.0)); + } + else + { xassert(code->arg.arg.x->type == A_FORMULA); + xassert(code->arg.arg.y->type == A_NUMERIC); + value = linear_comb(mpl, + eval_numeric(mpl, code->arg.arg.y), + eval_formula(mpl, code->arg.arg.x), + 0.0, constant_term(mpl, 0.0)); + } + break; + case O_DIV: + /* division */ + value = linear_comb(mpl, + fp_div(mpl, 1.0, eval_numeric(mpl, code->arg.arg.y)), + eval_formula(mpl, code->arg.arg.x), + 0.0, constant_term(mpl, 0.0)); + break; + case O_FORK: + /* if-then-else */ + if (eval_logical(mpl, code->arg.arg.x)) + value = eval_formula(mpl, code->arg.arg.y); + else if (code->arg.arg.z == NULL) + value = constant_term(mpl, 0.0); + else + value = eval_formula(mpl, code->arg.arg.z); + break; + case O_SUM: + /* summation over domain */ + { struct iter_form_info _info, *info = &_info; + info->code = code; + info->value = constant_term(mpl, 0.0); + info->tail = NULL; + loop_within_domain(mpl, code->arg.loop.domain, info, + iter_form_func); + value = reduce_terms(mpl, info->value); + } + break; + default: + xassert(code != code); + } + /* save resultant value */ + xassert(!code->valid); + code->valid = 1; + code->value.form = copy_formula(mpl, value); +done: return value; +} + +/*---------------------------------------------------------------------- +-- clean_code - clean pseudo-code. +-- +-- This routine recursively cleans specified pseudo-code that assumes +-- deleting all temporary resultant values. */ + +void clean_code(MPL *mpl, CODE *code) +{ ARG_LIST *e; + /* if no pseudo-code is specified, do nothing */ + if (code == NULL) goto done; + /* if resultant value is valid (exists), delete it */ + if (code->valid) + { code->valid = 0; + delete_value(mpl, code->type, &code->value); + } + /* recursively clean pseudo-code for operands */ + switch (code->op) + { case O_NUMBER: + case O_STRING: + case O_INDEX: + break; + case O_MEMNUM: + case O_MEMSYM: + for (e = code->arg.par.list; e != NULL; e = e->next) + clean_code(mpl, e->x); + break; + case O_MEMSET: + for (e = code->arg.set.list; e != NULL; e = e->next) + clean_code(mpl, e->x); + break; + case O_MEMVAR: + for (e = code->arg.var.list; e != NULL; e = e->next) + clean_code(mpl, e->x); + break; +#if 1 /* 15/V-2010 */ + case O_MEMCON: + for (e = code->arg.con.list; e != NULL; e = e->next) + clean_code(mpl, e->x); + break; +#endif + case O_TUPLE: + case O_MAKE: + for (e = code->arg.list; e != NULL; e = e->next) + clean_code(mpl, e->x); + break; + case O_SLICE: + xassert(code != code); + case O_IRAND224: + case O_UNIFORM01: + case O_NORMAL01: + case O_GMTIME: + break; + case O_CVTNUM: + case O_CVTSYM: + case O_CVTLOG: + case O_CVTTUP: + case O_CVTLFM: + case O_PLUS: + case O_MINUS: + case O_NOT: + case O_ABS: + case O_CEIL: + case O_FLOOR: + case O_EXP: + case O_LOG: + case O_LOG10: + case O_SQRT: + case O_SIN: + case O_COS: + case O_ATAN: + case O_ROUND: + case O_TRUNC: + case O_CARD: + case O_LENGTH: + /* unary operation */ + clean_code(mpl, code->arg.arg.x); + break; + case O_ADD: + case O_SUB: + case O_LESS: + case O_MUL: + case O_DIV: + case O_IDIV: + case O_MOD: + case O_POWER: + case O_ATAN2: + case O_ROUND2: + case O_TRUNC2: + case O_UNIFORM: + case O_NORMAL: + case O_CONCAT: + case O_LT: + case O_LE: + case O_EQ: + case O_GE: + case O_GT: + case O_NE: + case O_AND: + case O_OR: + case O_UNION: + case O_DIFF: + case O_SYMDIFF: + case O_INTER: + case O_CROSS: + case O_IN: + case O_NOTIN: + case O_WITHIN: + case O_NOTWITHIN: + case O_SUBSTR: + case O_STR2TIME: + case O_TIME2STR: + /* binary operation */ + clean_code(mpl, code->arg.arg.x); + clean_code(mpl, code->arg.arg.y); + break; + case O_DOTS: + case O_FORK: + case O_SUBSTR3: + /* ternary operation */ + clean_code(mpl, code->arg.arg.x); + clean_code(mpl, code->arg.arg.y); + clean_code(mpl, code->arg.arg.z); + break; + case O_MIN: + case O_MAX: + /* n-ary operation */ + for (e = code->arg.list; e != NULL; e = e->next) + clean_code(mpl, e->x); + break; + case O_SUM: + case O_PROD: + case O_MINIMUM: + case O_MAXIMUM: + case O_FORALL: + case O_EXISTS: + case O_SETOF: + case O_BUILD: + /* iterated operation */ + clean_domain(mpl, code->arg.loop.domain); + clean_code(mpl, code->arg.loop.x); + break; + default: + xassert(code->op != code->op); + } +done: return; +} + +#if 1 /* 11/II-2008 */ +/**********************************************************************/ +/* * * DATA TABLES * * */ +/**********************************************************************/ + +int mpl_tab_num_args(TABDCA *dca) +{ /* returns the number of arguments */ + return dca->na; +} + +const char *mpl_tab_get_arg(TABDCA *dca, int k) +{ /* returns pointer to k-th argument */ + xassert(1 <= k && k <= dca->na); + return dca->arg[k]; +} + +int mpl_tab_num_flds(TABDCA *dca) +{ /* returns the number of fields */ + return dca->nf; +} + +const char *mpl_tab_get_name(TABDCA *dca, int k) +{ /* returns pointer to name of k-th field */ + xassert(1 <= k && k <= dca->nf); + return dca->name[k]; +} + +int mpl_tab_get_type(TABDCA *dca, int k) +{ /* returns type of k-th field */ + xassert(1 <= k && k <= dca->nf); + return dca->type[k]; +} + +double mpl_tab_get_num(TABDCA *dca, int k) +{ /* returns numeric value of k-th field */ + xassert(1 <= k && k <= dca->nf); + xassert(dca->type[k] == 'N'); + return dca->num[k]; +} + +const char *mpl_tab_get_str(TABDCA *dca, int k) +{ /* returns pointer to string value of k-th field */ + xassert(1 <= k && k <= dca->nf); + xassert(dca->type[k] == 'S'); + xassert(dca->str[k] != NULL); + return dca->str[k]; +} + +void mpl_tab_set_num(TABDCA *dca, int k, double num) +{ /* assign numeric value to k-th field */ + xassert(1 <= k && k <= dca->nf); + xassert(dca->type[k] == '?'); + dca->type[k] = 'N'; + dca->num[k] = num; + return; +} + +void mpl_tab_set_str(TABDCA *dca, int k, const char *str) +{ /* assign string value to k-th field */ + xassert(1 <= k && k <= dca->nf); + xassert(dca->type[k] == '?'); + xassert(strlen(str) <= MAX_LENGTH); + xassert(dca->str[k] != NULL); + dca->type[k] = 'S'; + strcpy(dca->str[k], str); + return; +} + +static int write_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + TABLE *tab = info; + TABDCA *dca = mpl->dca; + TABOUT *out; + SYMBOL *sym; + int k; + char buf[MAX_LENGTH+1]; + /* evaluate field values */ + k = 0; + for (out = tab->u.out.list; out != NULL; out = out->next) + { k++; + switch (out->code->type) + { case A_NUMERIC: + dca->type[k] = 'N'; + dca->num[k] = eval_numeric(mpl, out->code); + dca->str[k][0] = '\0'; + break; + case A_SYMBOLIC: + sym = eval_symbolic(mpl, out->code); + if (sym->str == NULL) + { dca->type[k] = 'N'; + dca->num[k] = sym->num; + dca->str[k][0] = '\0'; + } + else + { dca->type[k] = 'S'; + dca->num[k] = 0.0; + fetch_string(mpl, sym->str, buf); + strcpy(dca->str[k], buf); + } + delete_symbol(mpl, sym); + break; + default: + xassert(out != out); + } + } + /* write record to output table */ + mpl_tab_drv_write(mpl); + return 0; +} + +void execute_table(MPL *mpl, TABLE *tab) +{ /* execute table statement */ + TABARG *arg; + TABFLD *fld; + TABIN *in; + TABOUT *out; + TABDCA *dca; + SET *set; + int k; + char buf[MAX_LENGTH+1]; + /* allocate table driver communication area */ + xassert(mpl->dca == NULL); + mpl->dca = dca = xmalloc(sizeof(TABDCA)); + dca->id = 0; + dca->link = NULL; + dca->na = 0; + dca->arg = NULL; + dca->nf = 0; + dca->name = NULL; + dca->type = NULL; + dca->num = NULL; + dca->str = NULL; + /* allocate arguments */ + xassert(dca->na == 0); + for (arg = tab->arg; arg != NULL; arg = arg->next) + dca->na++; + dca->arg = xcalloc(1+dca->na, sizeof(char *)); +#if 1 /* 28/IX-2008 */ + for (k = 1; k <= dca->na; k++) dca->arg[k] = NULL; +#endif + /* evaluate argument values */ + k = 0; + for (arg = tab->arg; arg != NULL; arg = arg->next) + { SYMBOL *sym; + k++; + xassert(arg->code->type == A_SYMBOLIC); + sym = eval_symbolic(mpl, arg->code); + if (sym->str == NULL) + sprintf(buf, "%.*g", DBL_DIG, sym->num); + else + fetch_string(mpl, sym->str, buf); + delete_symbol(mpl, sym); + dca->arg[k] = xmalloc(strlen(buf)+1); + strcpy(dca->arg[k], buf); + } + /* perform table input/output */ + switch (tab->type) + { case A_INPUT: goto read_table; + case A_OUTPUT: goto write_table; + default: xassert(tab != tab); + } +read_table: + /* read data from input table */ + /* add the only member to the control set and assign it empty + elemental set */ + set = tab->u.in.set; + if (set != NULL) + { if (set->data) + error(mpl, "%s already provided with data", set->name); + xassert(set->array->head == NULL); + add_member(mpl, set->array, NULL)->value.set = + create_elemset(mpl, set->dimen); + set->data = 1; + } + /* check parameters specified in the input list */ + for (in = tab->u.in.list; in != NULL; in = in->next) + { if (in->par->data) + error(mpl, "%s already provided with data", in->par->name); + in->par->data = 1; + } + /* allocate and initialize fields */ + xassert(dca->nf == 0); + for (fld = tab->u.in.fld; fld != NULL; fld = fld->next) + dca->nf++; + for (in = tab->u.in.list; in != NULL; in = in->next) + dca->nf++; + dca->name = xcalloc(1+dca->nf, sizeof(char *)); + dca->type = xcalloc(1+dca->nf, sizeof(int)); + dca->num = xcalloc(1+dca->nf, sizeof(double)); + dca->str = xcalloc(1+dca->nf, sizeof(char *)); + k = 0; + for (fld = tab->u.in.fld; fld != NULL; fld = fld->next) + { k++; + dca->name[k] = fld->name; + dca->type[k] = '?'; + dca->num[k] = 0.0; + dca->str[k] = xmalloc(MAX_LENGTH+1); + dca->str[k][0] = '\0'; + } + for (in = tab->u.in.list; in != NULL; in = in->next) + { k++; + dca->name[k] = in->name; + dca->type[k] = '?'; + dca->num[k] = 0.0; + dca->str[k] = xmalloc(MAX_LENGTH+1); + dca->str[k][0] = '\0'; + } + /* open input table */ + mpl_tab_drv_open(mpl, 'R'); + /* read and process records */ + for (;;) + { TUPLE *tup; + /* reset field types */ + for (k = 1; k <= dca->nf; k++) + dca->type[k] = '?'; + /* read next record */ + if (mpl_tab_drv_read(mpl)) break; + /* all fields must be set by the driver */ + for (k = 1; k <= dca->nf; k++) + { if (dca->type[k] == '?') + error(mpl, "field %s missing in input table", + dca->name[k]); + } + /* construct n-tuple */ + tup = create_tuple(mpl); + k = 0; + for (fld = tab->u.in.fld; fld != NULL; fld = fld->next) + { k++; + xassert(k <= dca->nf); + switch (dca->type[k]) + { case 'N': + tup = expand_tuple(mpl, tup, create_symbol_num(mpl, + dca->num[k])); + break; + case 'S': + xassert(strlen(dca->str[k]) <= MAX_LENGTH); + tup = expand_tuple(mpl, tup, create_symbol_str(mpl, + create_string(mpl, dca->str[k]))); + break; + default: + xassert(dca != dca); + } + } + /* add n-tuple just read to the control set */ + if (tab->u.in.set != NULL) + check_then_add(mpl, tab->u.in.set->array->head->value.set, + copy_tuple(mpl, tup)); + /* assign values to the parameters in the input list */ + for (in = tab->u.in.list; in != NULL; in = in->next) + { MEMBER *memb; + k++; + xassert(k <= dca->nf); + /* there must be no member with the same n-tuple */ + if (find_member(mpl, in->par->array, tup) != NULL) + error(mpl, "%s%s already defined", in->par->name, + format_tuple(mpl, '[', tup)); + /* create new parameter member with given n-tuple */ + memb = add_member(mpl, in->par->array, copy_tuple(mpl, tup)) + ; + /* assign value to the parameter member */ + switch (in->par->type) + { case A_NUMERIC: + case A_INTEGER: + case A_BINARY: + if (dca->type[k] != 'N') + error(mpl, "%s requires numeric data", + in->par->name); + memb->value.num = dca->num[k]; + break; + case A_SYMBOLIC: + switch (dca->type[k]) + { case 'N': + memb->value.sym = create_symbol_num(mpl, + dca->num[k]); + break; + case 'S': + xassert(strlen(dca->str[k]) <= MAX_LENGTH); + memb->value.sym = create_symbol_str(mpl, + create_string(mpl,dca->str[k])); + break; + default: + xassert(dca != dca); + } + break; + default: + xassert(in != in); + } + } + /* n-tuple is no more needed */ + delete_tuple(mpl, tup); + } + /* close input table */ + mpl_tab_drv_close(mpl); + goto done; +write_table: + /* write data to output table */ + /* allocate and initialize fields */ + xassert(dca->nf == 0); + for (out = tab->u.out.list; out != NULL; out = out->next) + dca->nf++; + dca->name = xcalloc(1+dca->nf, sizeof(char *)); + dca->type = xcalloc(1+dca->nf, sizeof(int)); + dca->num = xcalloc(1+dca->nf, sizeof(double)); + dca->str = xcalloc(1+dca->nf, sizeof(char *)); + k = 0; + for (out = tab->u.out.list; out != NULL; out = out->next) + { k++; + dca->name[k] = out->name; + dca->type[k] = '?'; + dca->num[k] = 0.0; + dca->str[k] = xmalloc(MAX_LENGTH+1); + dca->str[k][0] = '\0'; + } + /* open output table */ + mpl_tab_drv_open(mpl, 'W'); + /* evaluate fields and write records */ + loop_within_domain(mpl, tab->u.out.domain, tab, write_func); + /* close output table */ + mpl_tab_drv_close(mpl); +done: /* free table driver communication area */ + free_dca(mpl); + return; +} + +void free_dca(MPL *mpl) +{ /* free table driver communucation area */ + TABDCA *dca = mpl->dca; + int k; + if (dca != NULL) + { if (dca->link != NULL) + mpl_tab_drv_close(mpl); + if (dca->arg != NULL) + { for (k = 1; k <= dca->na; k++) +#if 1 /* 28/IX-2008 */ + if (dca->arg[k] != NULL) +#endif + xfree(dca->arg[k]); + xfree(dca->arg); + } + if (dca->name != NULL) xfree(dca->name); + if (dca->type != NULL) xfree(dca->type); + if (dca->num != NULL) xfree(dca->num); + if (dca->str != NULL) + { for (k = 1; k <= dca->nf; k++) + xfree(dca->str[k]); + xfree(dca->str); + } + xfree(dca), mpl->dca = NULL; + } + return; +} + +void clean_table(MPL *mpl, TABLE *tab) +{ /* clean table statement */ + TABARG *arg; + TABOUT *out; + /* clean string list */ + for (arg = tab->arg; arg != NULL; arg = arg->next) + clean_code(mpl, arg->code); + switch (tab->type) + { case A_INPUT: + break; + case A_OUTPUT: + /* clean subscript domain */ + clean_domain(mpl, tab->u.out.domain); + /* clean output list */ + for (out = tab->u.out.list; out != NULL; out = out->next) + clean_code(mpl, out->code); + break; + default: + xassert(tab != tab); + } + return; +} +#endif + +/**********************************************************************/ +/* * * MODEL STATEMENTS * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- execute_check - execute check statement. +-- +-- This routine executes specified check statement. */ + +static int check_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + CHECK *chk = (CHECK *)info; + if (!eval_logical(mpl, chk->code)) + error(mpl, "check%s failed", format_tuple(mpl, '[', + get_domain_tuple(mpl, chk->domain))); + return 0; +} + +void execute_check(MPL *mpl, CHECK *chk) +{ loop_within_domain(mpl, chk->domain, chk, check_func); + return; +} + +/*---------------------------------------------------------------------- +-- clean_check - clean check statement. +-- +-- This routine cleans specified check statement that assumes deleting +-- all stuff dynamically allocated on generating/postsolving phase. */ + +void clean_check(MPL *mpl, CHECK *chk) +{ /* clean subscript domain */ + clean_domain(mpl, chk->domain); + /* clean pseudo-code for computing predicate */ + clean_code(mpl, chk->code); + return; +} + +/*---------------------------------------------------------------------- +-- execute_display - execute display statement. +-- +-- This routine executes specified display statement. */ + +static void display_set(MPL *mpl, SET *set, MEMBER *memb) +{ /* display member of model set */ + ELEMSET *s = memb->value.set; + MEMBER *m; + write_text(mpl, "%s%s%s\n", set->name, + format_tuple(mpl, '[', memb->tuple), + s->head == NULL ? " is empty" : ":"); + for (m = s->head; m != NULL; m = m->next) + write_text(mpl, " %s\n", format_tuple(mpl, '(', m->tuple)); + return; +} + +static void display_par(MPL *mpl, PARAMETER *par, MEMBER *memb) +{ /* display member of model parameter */ + switch (par->type) + { case A_NUMERIC: + case A_INTEGER: + case A_BINARY: + write_text(mpl, "%s%s = %.*g\n", par->name, + format_tuple(mpl, '[', memb->tuple), + DBL_DIG, memb->value.num); + break; + case A_SYMBOLIC: + write_text(mpl, "%s%s = %s\n", par->name, + format_tuple(mpl, '[', memb->tuple), + format_symbol(mpl, memb->value.sym)); + break; + default: + xassert(par != par); + } + return; +} + +#if 1 /* 15/V-2010 */ +static void display_var(MPL *mpl, VARIABLE *var, MEMBER *memb, + int suff) +{ /* display member of model variable */ + if (suff == DOT_NONE || suff == DOT_VAL) + write_text(mpl, "%s%s.val = %.*g\n", var->name, + format_tuple(mpl, '[', memb->tuple), DBL_DIG, + memb->value.var->prim); + else if (suff == DOT_LB) + write_text(mpl, "%s%s.lb = %.*g\n", var->name, + format_tuple(mpl, '[', memb->tuple), DBL_DIG, + memb->value.var->var->lbnd == NULL ? -DBL_MAX : + memb->value.var->lbnd); + else if (suff == DOT_UB) + write_text(mpl, "%s%s.ub = %.*g\n", var->name, + format_tuple(mpl, '[', memb->tuple), DBL_DIG, + memb->value.var->var->ubnd == NULL ? +DBL_MAX : + memb->value.var->ubnd); + else if (suff == DOT_STATUS) + write_text(mpl, "%s%s.status = %d\n", var->name, format_tuple + (mpl, '[', memb->tuple), memb->value.var->stat); + else if (suff == DOT_DUAL) + write_text(mpl, "%s%s.dual = %.*g\n", var->name, + format_tuple(mpl, '[', memb->tuple), DBL_DIG, + memb->value.var->dual); + else + xassert(suff != suff); + return; +} +#endif + +#if 1 /* 15/V-2010 */ +static void display_con(MPL *mpl, CONSTRAINT *con, MEMBER *memb, + int suff) +{ /* display member of model constraint */ + if (suff == DOT_NONE || suff == DOT_VAL) + write_text(mpl, "%s%s.val = %.*g\n", con->name, + format_tuple(mpl, '[', memb->tuple), DBL_DIG, + memb->value.con->prim); + else if (suff == DOT_LB) + write_text(mpl, "%s%s.lb = %.*g\n", con->name, + format_tuple(mpl, '[', memb->tuple), DBL_DIG, + memb->value.con->con->lbnd == NULL ? -DBL_MAX : + memb->value.con->lbnd); + else if (suff == DOT_UB) + write_text(mpl, "%s%s.ub = %.*g\n", con->name, + format_tuple(mpl, '[', memb->tuple), DBL_DIG, + memb->value.con->con->ubnd == NULL ? +DBL_MAX : + memb->value.con->ubnd); + else if (suff == DOT_STATUS) + write_text(mpl, "%s%s.status = %d\n", con->name, format_tuple + (mpl, '[', memb->tuple), memb->value.con->stat); + else if (suff == DOT_DUAL) + write_text(mpl, "%s%s.dual = %.*g\n", con->name, + format_tuple(mpl, '[', memb->tuple), DBL_DIG, + memb->value.con->dual); + else + xassert(suff != suff); + return; +} +#endif + +static void display_memb(MPL *mpl, CODE *code) +{ /* display member specified by pseudo-code */ + MEMBER memb; + ARG_LIST *e; + xassert(code->op == O_MEMNUM || code->op == O_MEMSYM + || code->op == O_MEMSET || code->op == O_MEMVAR + || code->op == O_MEMCON); + memb.tuple = create_tuple(mpl); + for (e = code->arg.par.list; e != NULL; e = e->next) + memb.tuple = expand_tuple(mpl, memb.tuple, eval_symbolic(mpl, + e->x)); + switch (code->op) + { case O_MEMNUM: + memb.value.num = eval_member_num(mpl, code->arg.par.par, + memb.tuple); + display_par(mpl, code->arg.par.par, &memb); + break; + case O_MEMSYM: + memb.value.sym = eval_member_sym(mpl, code->arg.par.par, + memb.tuple); + display_par(mpl, code->arg.par.par, &memb); + delete_symbol(mpl, memb.value.sym); + break; + case O_MEMSET: + memb.value.set = eval_member_set(mpl, code->arg.set.set, + memb.tuple); + display_set(mpl, code->arg.set.set, &memb); + break; + case O_MEMVAR: + memb.value.var = eval_member_var(mpl, code->arg.var.var, + memb.tuple); + display_var + (mpl, code->arg.var.var, &memb, code->arg.var.suff); + break; + case O_MEMCON: + memb.value.con = eval_member_con(mpl, code->arg.con.con, + memb.tuple); + display_con + (mpl, code->arg.con.con, &memb, code->arg.con.suff); + break; + default: + xassert(code != code); + } + delete_tuple(mpl, memb.tuple); + return; +} + +static void display_code(MPL *mpl, CODE *code) +{ /* display value of expression */ + switch (code->type) + { case A_NUMERIC: + /* numeric value */ + { double num; + num = eval_numeric(mpl, code); + write_text(mpl, "%.*g\n", DBL_DIG, num); + } + break; + case A_SYMBOLIC: + /* symbolic value */ + { SYMBOL *sym; + sym = eval_symbolic(mpl, code); + write_text(mpl, "%s\n", format_symbol(mpl, sym)); + delete_symbol(mpl, sym); + } + break; + case A_LOGICAL: + /* logical value */ + { int bit; + bit = eval_logical(mpl, code); + write_text(mpl, "%s\n", bit ? "true" : "false"); + } + break; + case A_TUPLE: + /* n-tuple */ + { TUPLE *tuple; + tuple = eval_tuple(mpl, code); + write_text(mpl, "%s\n", format_tuple(mpl, '(', tuple)); + delete_tuple(mpl, tuple); + } + break; + case A_ELEMSET: + /* elemental set */ + { ELEMSET *set; + MEMBER *memb; + set = eval_elemset(mpl, code); + if (set->head == 0) + write_text(mpl, "set is empty\n"); + for (memb = set->head; memb != NULL; memb = memb->next) + write_text(mpl, " %s\n", format_tuple(mpl, '(', + memb->tuple)); + delete_elemset(mpl, set); + } + break; + case A_FORMULA: + /* linear form */ + { FORMULA *form, *term; + form = eval_formula(mpl, code); + if (form == NULL) + write_text(mpl, "linear form is empty\n"); + for (term = form; term != NULL; term = term->next) + { if (term->var == NULL) + write_text(mpl, " %.*g\n", term->coef); + else + write_text(mpl, " %.*g %s%s\n", DBL_DIG, + term->coef, term->var->var->name, + format_tuple(mpl, '[', term->var->memb->tuple)); + } + delete_formula(mpl, form); + } + break; + default: + xassert(code != code); + } + return; +} + +static int display_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + DISPLAY *dpy = (DISPLAY *)info; + DISPLAY1 *entry; + for (entry = dpy->list; entry != NULL; entry = entry->next) + { if (entry->type == A_INDEX) + { /* dummy index */ + DOMAIN_SLOT *slot = entry->u.slot; + write_text(mpl, "%s = %s\n", slot->name, + format_symbol(mpl, slot->value)); + } + else if (entry->type == A_SET) + { /* model set */ + SET *set = entry->u.set; + MEMBER *memb; + if (set->assign != NULL) + { /* the set has assignment expression; evaluate all its + members over entire domain */ + eval_whole_set(mpl, set); + } + else + { /* the set has no assignment expression; refer to its + any existing member ignoring resultant value to check + the data provided the data section */ +#if 1 /* 12/XII-2008 */ + if (set->gadget != NULL && set->data == 0) + { /* initialize the set with data from a plain set */ + saturate_set(mpl, set); + } +#endif + if (set->array->head != NULL) + eval_member_set(mpl, set, set->array->head->tuple); + } + /* display all members of the set array */ + if (set->array->head == NULL) + write_text(mpl, "%s has empty content\n", set->name); + for (memb = set->array->head; memb != NULL; memb = + memb->next) display_set(mpl, set, memb); + } + else if (entry->type == A_PARAMETER) + { /* model parameter */ + PARAMETER *par = entry->u.par; + MEMBER *memb; + if (par->assign != NULL) + { /* the parameter has an assignment expression; evaluate + all its member over entire domain */ + eval_whole_par(mpl, par); + } + else + { /* the parameter has no assignment expression; refer to + its any existing member ignoring resultant value to + check the data provided in the data section */ + if (par->array->head != NULL) + { if (par->type != A_SYMBOLIC) + eval_member_num(mpl, par, par->array->head->tuple); + else + delete_symbol(mpl, eval_member_sym(mpl, par, + par->array->head->tuple)); + } + } + /* display all members of the parameter array */ + if (par->array->head == NULL) + write_text(mpl, "%s has empty content\n", par->name); + for (memb = par->array->head; memb != NULL; memb = + memb->next) display_par(mpl, par, memb); + } + else if (entry->type == A_VARIABLE) + { /* model variable */ + VARIABLE *var = entry->u.var; + MEMBER *memb; + xassert(mpl->flag_p); + /* display all members of the variable array */ + if (var->array->head == NULL) + write_text(mpl, "%s has empty content\n", var->name); + for (memb = var->array->head; memb != NULL; memb = + memb->next) display_var(mpl, var, memb, DOT_NONE); + } + else if (entry->type == A_CONSTRAINT) + { /* model constraint */ + CONSTRAINT *con = entry->u.con; + MEMBER *memb; + xassert(mpl->flag_p); + /* display all members of the constraint array */ + if (con->array->head == NULL) + write_text(mpl, "%s has empty content\n", con->name); + for (memb = con->array->head; memb != NULL; memb = + memb->next) display_con(mpl, con, memb, DOT_NONE); + } + else if (entry->type == A_EXPRESSION) + { /* expression */ + CODE *code = entry->u.code; + if (code->op == O_MEMNUM || code->op == O_MEMSYM || + code->op == O_MEMSET || code->op == O_MEMVAR || + code->op == O_MEMCON) + display_memb(mpl, code); + else + display_code(mpl, code); + } + else + xassert(entry != entry); + } + return 0; +} + +void execute_display(MPL *mpl, DISPLAY *dpy) +{ loop_within_domain(mpl, dpy->domain, dpy, display_func); + return; +} + +/*---------------------------------------------------------------------- +-- clean_display - clean display statement. +-- +-- This routine cleans specified display statement that assumes deleting +-- all stuff dynamically allocated on generating/postsolving phase. */ + +void clean_display(MPL *mpl, DISPLAY *dpy) +{ DISPLAY1 *d; +#if 0 /* 15/V-2010 */ + ARG_LIST *e; +#endif + /* clean subscript domain */ + clean_domain(mpl, dpy->domain); + /* clean display list */ + for (d = dpy->list; d != NULL; d = d->next) + { /* clean pseudo-code for computing expression */ + if (d->type == A_EXPRESSION) + clean_code(mpl, d->u.code); +#if 0 /* 15/V-2010 */ + /* clean pseudo-code for computing subscripts */ + for (e = d->list; e != NULL; e = e->next) + clean_code(mpl, e->x); +#endif + } + return; +} + +/*---------------------------------------------------------------------- +-- execute_printf - execute printf statement. +-- +-- This routine executes specified printf statement. */ + +#if 1 /* 14/VII-2006 */ +static void print_char(MPL *mpl, int c) +{ if (mpl->prt_fp == NULL) + write_char(mpl, c); + else + xfputc(c, mpl->prt_fp); + return; +} + +static void print_text(MPL *mpl, char *fmt, ...) +{ va_list arg; + char buf[OUTBUF_SIZE], *c; + va_start(arg, fmt); + vsprintf(buf, fmt, arg); + xassert(strlen(buf) < sizeof(buf)); + va_end(arg); + for (c = buf; *c != '\0'; c++) print_char(mpl, *c); + return; +} +#endif + +static int printf_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + PRINTF *prt = (PRINTF *)info; + PRINTF1 *entry; + SYMBOL *sym; + char fmt[MAX_LENGTH+1], *c, *from, save; + /* evaluate format control string */ + sym = eval_symbolic(mpl, prt->fmt); + if (sym->str == NULL) + sprintf(fmt, "%.*g", DBL_DIG, sym->num); + else + fetch_string(mpl, sym->str, fmt); + delete_symbol(mpl, sym); + /* scan format control string and perform formatting output */ + entry = prt->list; + for (c = fmt; *c != '\0'; c++) + { if (*c == '%') + { /* scan format specifier */ + from = c++; + if (*c == '%') + { print_char(mpl, '%'); + continue; + } + if (entry == NULL) break; + /* scan optional flags */ + while (*c == '-' || *c == '+' || *c == ' ' || *c == '#' || + *c == '0') c++; + /* scan optional minimum field width */ + while (isdigit((unsigned char)*c)) c++; + /* scan optional precision */ + if (*c == '.') + { c++; + while (isdigit((unsigned char)*c)) c++; + } + /* scan conversion specifier and perform formatting */ + save = *(c+1), *(c+1) = '\0'; + if (*c == 'd' || *c == 'i' || *c == 'e' || *c == 'E' || + *c == 'f' || *c == 'F' || *c == 'g' || *c == 'G') + { /* the specifier requires numeric value */ + double value; + xassert(entry != NULL); + switch (entry->code->type) + { case A_NUMERIC: + value = eval_numeric(mpl, entry->code); + break; + case A_SYMBOLIC: + sym = eval_symbolic(mpl, entry->code); + if (sym->str != NULL) + error(mpl, "cannot convert %s to floating-point" + " number", format_symbol(mpl, sym)); + value = sym->num; + delete_symbol(mpl, sym); + break; + case A_LOGICAL: + if (eval_logical(mpl, entry->code)) + value = 1.0; + else + value = 0.0; + break; + default: + xassert(entry != entry); + } + if (*c == 'd' || *c == 'i') + { double int_max = (double)INT_MAX; + if (!(-int_max <= value && value <= +int_max)) + error(mpl, "cannot convert %.*g to integer", + DBL_DIG, value); + print_text(mpl, from, (int)floor(value + 0.5)); + } + else + print_text(mpl, from, value); + } + else if (*c == 's') + { /* the specifier requires symbolic value */ + char value[MAX_LENGTH+1]; + switch (entry->code->type) + { case A_NUMERIC: + sprintf(value, "%.*g", DBL_DIG, eval_numeric(mpl, + entry->code)); + break; + case A_LOGICAL: + if (eval_logical(mpl, entry->code)) + strcpy(value, "T"); + else + strcpy(value, "F"); + break; + case A_SYMBOLIC: + sym = eval_symbolic(mpl, entry->code); + if (sym->str == NULL) + sprintf(value, "%.*g", DBL_DIG, sym->num); + else + fetch_string(mpl, sym->str, value); + delete_symbol(mpl, sym); + break; + default: + xassert(entry != entry); + } + print_text(mpl, from, value); + } + else + error(mpl, "format specifier missing or invalid"); + *(c+1) = save; + entry = entry->next; + } + else if (*c == '\\') + { /* write some control character */ + c++; + if (*c == 't') + print_char(mpl, '\t'); + else if (*c == 'n') + print_char(mpl, '\n'); +#if 1 /* 28/X-2010 */ + else if (*c == '\0') + { /* format string ends with backslash */ + error(mpl, "invalid use of escape character \\ in format" + " control string"); + } +#endif + else + print_char(mpl, *c); + } + else + { /* write character without formatting */ + print_char(mpl, *c); + } + } + return 0; +} + +#if 0 /* 14/VII-2006 */ +void execute_printf(MPL *mpl, PRINTF *prt) +{ loop_within_domain(mpl, prt->domain, prt, printf_func); + return; +} +#else +void execute_printf(MPL *mpl, PRINTF *prt) +{ if (prt->fname == NULL) + { /* switch to the standard output */ + if (mpl->prt_fp != NULL) + { xfclose(mpl->prt_fp), mpl->prt_fp = NULL; + xfree(mpl->prt_file), mpl->prt_file = NULL; + } + } + else + { /* evaluate file name string */ + SYMBOL *sym; + char fname[MAX_LENGTH+1]; + sym = eval_symbolic(mpl, prt->fname); + if (sym->str == NULL) + sprintf(fname, "%.*g", DBL_DIG, sym->num); + else + fetch_string(mpl, sym->str, fname); + delete_symbol(mpl, sym); + /* close the current print file, if necessary */ + if (mpl->prt_fp != NULL && + (!prt->app || strcmp(mpl->prt_file, fname) != 0)) + { xfclose(mpl->prt_fp), mpl->prt_fp = NULL; + xfree(mpl->prt_file), mpl->prt_file = NULL; + } + /* open the specified print file, if necessary */ + if (mpl->prt_fp == NULL) + { mpl->prt_fp = xfopen(fname, prt->app ? "a" : "w"); + if (mpl->prt_fp == NULL) + error(mpl, "unable to open `%s' for writing - %s", + fname, xerrmsg()); + mpl->prt_file = xmalloc(strlen(fname)+1); + strcpy(mpl->prt_file, fname); + } + } + loop_within_domain(mpl, prt->domain, prt, printf_func); + if (mpl->prt_fp != NULL) + { xfflush(mpl->prt_fp); + if (xferror(mpl->prt_fp)) + error(mpl, "writing error to `%s' - %s", mpl->prt_file, + xerrmsg()); + } + return; +} +#endif + +/*---------------------------------------------------------------------- +-- clean_printf - clean printf statement. +-- +-- This routine cleans specified printf statement that assumes deleting +-- all stuff dynamically allocated on generating/postsolving phase. */ + +void clean_printf(MPL *mpl, PRINTF *prt) +{ PRINTF1 *p; + /* clean subscript domain */ + clean_domain(mpl, prt->domain); + /* clean pseudo-code for computing format string */ + clean_code(mpl, prt->fmt); + /* clean printf list */ + for (p = prt->list; p != NULL; p = p->next) + { /* clean pseudo-code for computing value to be printed */ + clean_code(mpl, p->code); + } +#if 1 /* 14/VII-2006 */ + /* clean pseudo-code for computing file name string */ + clean_code(mpl, prt->fname); +#endif + return; +} + +/*---------------------------------------------------------------------- +-- execute_for - execute for statement. +-- +-- This routine executes specified for statement. */ + +static int for_func(MPL *mpl, void *info) +{ /* this is auxiliary routine to work within domain scope */ + FOR *fur = (FOR *)info; + STATEMENT *stmt, *save; + save = mpl->stmt; + for (stmt = fur->list; stmt != NULL; stmt = stmt->next) + execute_statement(mpl, stmt); + mpl->stmt = save; + return 0; +} + +void execute_for(MPL *mpl, FOR *fur) +{ loop_within_domain(mpl, fur->domain, fur, for_func); + return; +} + +/*---------------------------------------------------------------------- +-- clean_for - clean for statement. +-- +-- This routine cleans specified for statement that assumes deleting all +-- stuff dynamically allocated on generating/postsolving phase. */ + +void clean_for(MPL *mpl, FOR *fur) +{ STATEMENT *stmt; + /* clean subscript domain */ + clean_domain(mpl, fur->domain); + /* clean all sub-statements */ + for (stmt = fur->list; stmt != NULL; stmt = stmt->next) + clean_statement(mpl, stmt); + return; +} + +/*---------------------------------------------------------------------- +-- execute_statement - execute specified model statement. +-- +-- This routine executes specified model statement. */ + +void execute_statement(MPL *mpl, STATEMENT *stmt) +{ mpl->stmt = stmt; + switch (stmt->type) + { case A_SET: + case A_PARAMETER: + case A_VARIABLE: + break; + case A_CONSTRAINT: + xprintf("Generating %s...\n", stmt->u.con->name); + eval_whole_con(mpl, stmt->u.con); + break; + case A_TABLE: + switch (stmt->u.tab->type) + { case A_INPUT: + xprintf("Reading %s...\n", stmt->u.tab->name); + break; + case A_OUTPUT: + xprintf("Writing %s...\n", stmt->u.tab->name); + break; + default: + xassert(stmt != stmt); + } + execute_table(mpl, stmt->u.tab); + break; + case A_SOLVE: + break; + case A_CHECK: + xprintf("Checking (line %d)...\n", stmt->line); + execute_check(mpl, stmt->u.chk); + break; + case A_DISPLAY: + write_text(mpl, "Display statement at line %d\n", + stmt->line); + execute_display(mpl, stmt->u.dpy); + break; + case A_PRINTF: + execute_printf(mpl, stmt->u.prt); + break; + case A_FOR: + execute_for(mpl, stmt->u.fur); + break; + default: + xassert(stmt != stmt); + } + return; +} + +/*---------------------------------------------------------------------- +-- clean_statement - clean specified model statement. +-- +-- This routine cleans specified model statement that assumes deleting +-- all stuff dynamically allocated on generating/postsolving phase. */ + +void clean_statement(MPL *mpl, STATEMENT *stmt) +{ switch(stmt->type) + { case A_SET: + clean_set(mpl, stmt->u.set); break; + case A_PARAMETER: + clean_parameter(mpl, stmt->u.par); break; + case A_VARIABLE: + clean_variable(mpl, stmt->u.var); break; + case A_CONSTRAINT: + clean_constraint(mpl, stmt->u.con); break; +#if 1 /* 11/II-2008 */ + case A_TABLE: + clean_table(mpl, stmt->u.tab); break; +#endif + case A_SOLVE: + break; + case A_CHECK: + clean_check(mpl, stmt->u.chk); break; + case A_DISPLAY: + clean_display(mpl, stmt->u.dpy); break; + case A_PRINTF: + clean_printf(mpl, stmt->u.prt); break; + case A_FOR: + clean_for(mpl, stmt->u.fur); break; + default: + xassert(stmt != stmt); + } + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmpl04.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmpl04.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1424 @@ +/* glpmpl04.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_ERRNO +#define _GLPSTD_STDIO +#include "glpmpl.h" +#define xfault xerror +#define dmp_create_poolx(size) dmp_create_pool() + +/**********************************************************************/ +/* * * GENERATING AND POSTSOLVING MODEL * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- alloc_content - allocate content arrays for all model objects. +-- +-- This routine allocates content arrays for all existing model objects +-- and thereby finalizes creating model. +-- +-- This routine must be called immediately after reading model section, +-- i.e. before reading data section or generating model. */ + +void alloc_content(MPL *mpl) +{ STATEMENT *stmt; + /* walk through all model statements */ + for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) + { switch (stmt->type) + { case A_SET: + /* model set */ + xassert(stmt->u.set->array == NULL); + stmt->u.set->array = create_array(mpl, A_ELEMSET, + stmt->u.set->dim); + break; + case A_PARAMETER: + /* model parameter */ + xassert(stmt->u.par->array == NULL); + switch (stmt->u.par->type) + { case A_NUMERIC: + case A_INTEGER: + case A_BINARY: + stmt->u.par->array = create_array(mpl, A_NUMERIC, + stmt->u.par->dim); + break; + case A_SYMBOLIC: + stmt->u.par->array = create_array(mpl, A_SYMBOLIC, + stmt->u.par->dim); + break; + default: + xassert(stmt != stmt); + } + break; + case A_VARIABLE: + /* model variable */ + xassert(stmt->u.var->array == NULL); + stmt->u.var->array = create_array(mpl, A_ELEMVAR, + stmt->u.var->dim); + break; + case A_CONSTRAINT: + /* model constraint/objective */ + xassert(stmt->u.con->array == NULL); + stmt->u.con->array = create_array(mpl, A_ELEMCON, + stmt->u.con->dim); + break; +#if 1 /* 11/II-2008 */ + case A_TABLE: +#endif + case A_SOLVE: + case A_CHECK: + case A_DISPLAY: + case A_PRINTF: + case A_FOR: + /* functional statements have no content array */ + break; + default: + xassert(stmt != stmt); + } + } + return; +} + +/*---------------------------------------------------------------------- +-- generate_model - generate model. +-- +-- This routine executes the model statements which precede the solve +-- statement. */ + +void generate_model(MPL *mpl) +{ STATEMENT *stmt; + xassert(!mpl->flag_p); + for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) + { execute_statement(mpl, stmt); + if (mpl->stmt->type == A_SOLVE) break; + } + mpl->stmt = stmt; + return; +} + +/*---------------------------------------------------------------------- +-- build_problem - build problem instance. +-- +-- This routine builds lists of rows and columns for problem instance, +-- which corresponds to the generated model. */ + +void build_problem(MPL *mpl) +{ STATEMENT *stmt; + MEMBER *memb; + VARIABLE *v; + CONSTRAINT *c; + FORMULA *t; + int i, j; + xassert(mpl->m == 0); + xassert(mpl->n == 0); + xassert(mpl->row == NULL); + xassert(mpl->col == NULL); + /* check that all elemental variables has zero column numbers */ + for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) + { if (stmt->type == A_VARIABLE) + { v = stmt->u.var; + for (memb = v->array->head; memb != NULL; memb = memb->next) + xassert(memb->value.var->j == 0); + } + } + /* assign row numbers to elemental constraints and objectives */ + for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) + { if (stmt->type == A_CONSTRAINT) + { c = stmt->u.con; + for (memb = c->array->head; memb != NULL; memb = memb->next) + { xassert(memb->value.con->i == 0); + memb->value.con->i = ++mpl->m; + /* walk through linear form and mark elemental variables, + which are referenced at least once */ + for (t = memb->value.con->form; t != NULL; t = t->next) + { xassert(t->var != NULL); + t->var->memb->value.var->j = -1; + } + } + } + } + /* assign column numbers to marked elemental variables */ + for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) + { if (stmt->type == A_VARIABLE) + { v = stmt->u.var; + for (memb = v->array->head; memb != NULL; memb = memb->next) + if (memb->value.var->j != 0) memb->value.var->j = + ++mpl->n; + } + } + /* build list of rows */ + mpl->row = xcalloc(1+mpl->m, sizeof(ELEMCON *)); + for (i = 1; i <= mpl->m; i++) mpl->row[i] = NULL; + for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) + { if (stmt->type == A_CONSTRAINT) + { c = stmt->u.con; + for (memb = c->array->head; memb != NULL; memb = memb->next) + { i = memb->value.con->i; + xassert(1 <= i && i <= mpl->m); + xassert(mpl->row[i] == NULL); + mpl->row[i] = memb->value.con; + } + } + } + for (i = 1; i <= mpl->m; i++) xassert(mpl->row[i] != NULL); + /* build list of columns */ + mpl->col = xcalloc(1+mpl->n, sizeof(ELEMVAR *)); + for (j = 1; j <= mpl->n; j++) mpl->col[j] = NULL; + for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) + { if (stmt->type == A_VARIABLE) + { v = stmt->u.var; + for (memb = v->array->head; memb != NULL; memb = memb->next) + { j = memb->value.var->j; + if (j == 0) continue; + xassert(1 <= j && j <= mpl->n); + xassert(mpl->col[j] == NULL); + mpl->col[j] = memb->value.var; + } + } + } + for (j = 1; j <= mpl->n; j++) xassert(mpl->col[j] != NULL); + return; +} + +/*---------------------------------------------------------------------- +-- postsolve_model - postsolve model. +-- +-- This routine executes the model statements which follow the solve +-- statement. */ + +void postsolve_model(MPL *mpl) +{ STATEMENT *stmt; + xassert(!mpl->flag_p); + mpl->flag_p = 1; + for (stmt = mpl->stmt; stmt != NULL; stmt = stmt->next) + execute_statement(mpl, stmt); + mpl->stmt = NULL; + return; +} + +/*---------------------------------------------------------------------- +-- clean_model - clean model content. +-- +-- This routine cleans the model content that assumes deleting all stuff +-- dynamically allocated on generating/postsolving phase. +-- +-- Actually cleaning model content is not needed. This function is used +-- mainly to be sure that there were no logical errors on using dynamic +-- memory pools during the generation phase. +-- +-- NOTE: This routine must not be called if any errors were detected on +-- the generation phase. */ + +void clean_model(MPL *mpl) +{ STATEMENT *stmt; + for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) + clean_statement(mpl, stmt); + /* check that all atoms have been returned to their pools */ + if (dmp_in_use(mpl->strings).lo != 0) + error(mpl, "internal logic error: %d string segment(s) were lo" + "st", dmp_in_use(mpl->strings).lo); + if (dmp_in_use(mpl->symbols).lo != 0) + error(mpl, "internal logic error: %d symbol(s) were lost", + dmp_in_use(mpl->symbols).lo); + if (dmp_in_use(mpl->tuples).lo != 0) + error(mpl, "internal logic error: %d n-tuple component(s) were" + " lost", dmp_in_use(mpl->tuples).lo); + if (dmp_in_use(mpl->arrays).lo != 0) + error(mpl, "internal logic error: %d array(s) were lost", + dmp_in_use(mpl->arrays).lo); + if (dmp_in_use(mpl->members).lo != 0) + error(mpl, "internal logic error: %d array member(s) were lost" + , dmp_in_use(mpl->members).lo); + if (dmp_in_use(mpl->elemvars).lo != 0) + error(mpl, "internal logic error: %d elemental variable(s) wer" + "e lost", dmp_in_use(mpl->elemvars).lo); + if (dmp_in_use(mpl->formulae).lo != 0) + error(mpl, "internal logic error: %d linear term(s) were lost", + dmp_in_use(mpl->formulae).lo); + if (dmp_in_use(mpl->elemcons).lo != 0) + error(mpl, "internal logic error: %d elemental constraint(s) w" + "ere lost", dmp_in_use(mpl->elemcons).lo); + return; +} + +/**********************************************************************/ +/* * * INPUT/OUTPUT * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- open_input - open input text file. +-- +-- This routine opens the input text file for scanning. */ + +void open_input(MPL *mpl, char *file) +{ mpl->line = 0; + mpl->c = '\n'; + mpl->token = 0; + mpl->imlen = 0; + mpl->image[0] = '\0'; + mpl->value = 0.0; + mpl->b_token = T_EOF; + mpl->b_imlen = 0; + mpl->b_image[0] = '\0'; + mpl->b_value = 0.0; + mpl->f_dots = 0; + mpl->f_scan = 0; + mpl->f_token = 0; + mpl->f_imlen = 0; + mpl->f_image[0] = '\0'; + mpl->f_value = 0.0; + memset(mpl->context, ' ', CONTEXT_SIZE); + mpl->c_ptr = 0; + xassert(mpl->in_fp == NULL); + mpl->in_fp = xfopen(file, "r"); + if (mpl->in_fp == NULL) + error(mpl, "unable to open %s - %s", file, xerrmsg()); + mpl->in_file = file; + /* scan the very first character */ + get_char(mpl); + /* scan the very first token */ + get_token(mpl); + return; +} + +/*---------------------------------------------------------------------- +-- read_char - read next character from input text file. +-- +-- This routine returns a next ASCII character read from the input text +-- file. If the end of file has been reached, EOF is returned. */ + +int read_char(MPL *mpl) +{ int c; + xassert(mpl->in_fp != NULL); + c = xfgetc(mpl->in_fp); + if (c < 0) + { if (xferror(mpl->in_fp)) + error(mpl, "read error on %s - %s", mpl->in_file, + xerrmsg()); + c = EOF; + } + return c; +} + +/*---------------------------------------------------------------------- +-- close_input - close input text file. +-- +-- This routine closes the input text file. */ + +void close_input(MPL *mpl) +{ xassert(mpl->in_fp != NULL); + xfclose(mpl->in_fp); + mpl->in_fp = NULL; + mpl->in_file = NULL; + return; +} + +/*---------------------------------------------------------------------- +-- open_output - open output text file. +-- +-- This routine opens the output text file for writing data produced by +-- display and printf statements. */ + +void open_output(MPL *mpl, char *file) +{ xassert(mpl->out_fp == NULL); + if (file == NULL) + { file = ""; + mpl->out_fp = (void *)stdout; + } + else + { mpl->out_fp = xfopen(file, "w"); + if (mpl->out_fp == NULL) + error(mpl, "unable to create %s - %s", file, xerrmsg()); + } + mpl->out_file = xmalloc(strlen(file)+1); + strcpy(mpl->out_file, file); + return; +} + +/*---------------------------------------------------------------------- +-- write_char - write next character to output text file. +-- +-- This routine writes an ASCII character to the output text file. */ + +void write_char(MPL *mpl, int c) +{ xassert(mpl->out_fp != NULL); + if (mpl->out_fp == (void *)stdout) + xprintf("%c", c); + else + xfprintf(mpl->out_fp, "%c", c); + return; +} + +/*---------------------------------------------------------------------- +-- write_text - format and write text to output text file. +-- +-- This routine formats a text using the format control string and then +-- writes this text to the output text file. */ + +void write_text(MPL *mpl, char *fmt, ...) +{ va_list arg; + char buf[OUTBUF_SIZE], *c; + va_start(arg, fmt); + vsprintf(buf, fmt, arg); + xassert(strlen(buf) < sizeof(buf)); + va_end(arg); + for (c = buf; *c != '\0'; c++) write_char(mpl, *c); + return; +} + +/*---------------------------------------------------------------------- +-- flush_output - finalize writing data to output text file. +-- +-- This routine finalizes writing data to the output text file. */ + +void flush_output(MPL *mpl) +{ xassert(mpl->out_fp != NULL); + if (mpl->out_fp != (void *)stdout) + { xfflush(mpl->out_fp); + if (xferror(mpl->out_fp)) + error(mpl, "write error on %s - %s", mpl->out_file, + xerrmsg()); + } + return; +} + +/**********************************************************************/ +/* * * SOLVER INTERFACE * * */ +/**********************************************************************/ + +/*---------------------------------------------------------------------- +-- error - print error message and terminate model processing. +-- +-- This routine formats and prints an error message and then terminates +-- model processing. */ + +void error(MPL *mpl, char *fmt, ...) +{ va_list arg; + char msg[4095+1]; + va_start(arg, fmt); + vsprintf(msg, fmt, arg); + xassert(strlen(msg) < sizeof(msg)); + va_end(arg); + switch (mpl->phase) + { case 1: + case 2: + /* translation phase */ + xprintf("%s:%d: %s\n", + mpl->in_file == NULL ? "(unknown)" : mpl->in_file, + mpl->line, msg); + print_context(mpl); + break; + case 3: + /* generation/postsolve phase */ + xprintf("%s:%d: %s\n", + mpl->mod_file == NULL ? "(unknown)" : mpl->mod_file, + mpl->stmt == NULL ? 0 : mpl->stmt->line, msg); + break; + default: + xassert(mpl != mpl); + } + mpl->phase = 4; + longjmp(mpl->jump, 1); + /* no return */ +} + +/*---------------------------------------------------------------------- +-- warning - print warning message and continue model processing. +-- +-- This routine formats and prints a warning message and returns to the +-- calling program. */ + +void warning(MPL *mpl, char *fmt, ...) +{ va_list arg; + char msg[4095+1]; + va_start(arg, fmt); + vsprintf(msg, fmt, arg); + xassert(strlen(msg) < sizeof(msg)); + va_end(arg); + switch (mpl->phase) + { case 1: + case 2: + /* translation phase */ + xprintf("%s:%d: warning: %s\n", + mpl->in_file == NULL ? "(unknown)" : mpl->in_file, + mpl->line, msg); + break; + case 3: + /* generation/postsolve phase */ + xprintf("%s:%d: warning: %s\n", + mpl->mod_file == NULL ? "(unknown)" : mpl->mod_file, + mpl->stmt == NULL ? 0 : mpl->stmt->line, msg); + break; + default: + xassert(mpl != mpl); + } + return; +} + +/*---------------------------------------------------------------------- +-- mpl_initialize - create and initialize translator database. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- MPL *mpl_initialize(void); +-- +-- *Description* +-- +-- The routine mpl_initialize creates and initializes the database used +-- by the GNU MathProg translator. +-- +-- *Returns* +-- +-- The routine returns a pointer to the database created. */ + +MPL *mpl_initialize(void) +{ MPL *mpl; + mpl = xmalloc(sizeof(MPL)); + /* scanning segment */ + mpl->line = 0; + mpl->c = 0; + mpl->token = 0; + mpl->imlen = 0; + mpl->image = xcalloc(MAX_LENGTH+1, sizeof(char)); + mpl->image[0] = '\0'; + mpl->value = 0.0; + mpl->b_token = 0; + mpl->b_imlen = 0; + mpl->b_image = xcalloc(MAX_LENGTH+1, sizeof(char)); + mpl->b_image[0] = '\0'; + mpl->b_value = 0.0; + mpl->f_dots = 0; + mpl->f_scan = 0; + mpl->f_token = 0; + mpl->f_imlen = 0; + mpl->f_image = xcalloc(MAX_LENGTH+1, sizeof(char)); + mpl->f_image[0] = '\0'; + mpl->f_value = 0.0; + mpl->context = xcalloc(CONTEXT_SIZE, sizeof(char)); + memset(mpl->context, ' ', CONTEXT_SIZE); + mpl->c_ptr = 0; + mpl->flag_d = 0; + /* translating segment */ + mpl->pool = dmp_create_poolx(0); + mpl->tree = avl_create_tree(avl_strcmp, NULL); + mpl->model = NULL; + mpl->flag_x = 0; + mpl->as_within = 0; + mpl->as_in = 0; + mpl->as_binary = 0; + mpl->flag_s = 0; + /* common segment */ + mpl->strings = dmp_create_poolx(sizeof(STRING)); + mpl->symbols = dmp_create_poolx(sizeof(SYMBOL)); + mpl->tuples = dmp_create_poolx(sizeof(TUPLE)); + mpl->arrays = dmp_create_poolx(sizeof(ARRAY)); + mpl->members = dmp_create_poolx(sizeof(MEMBER)); + mpl->elemvars = dmp_create_poolx(sizeof(ELEMVAR)); + mpl->formulae = dmp_create_poolx(sizeof(FORMULA)); + mpl->elemcons = dmp_create_poolx(sizeof(ELEMCON)); + mpl->a_list = NULL; + mpl->sym_buf = xcalloc(255+1, sizeof(char)); + mpl->sym_buf[0] = '\0'; + mpl->tup_buf = xcalloc(255+1, sizeof(char)); + mpl->tup_buf[0] = '\0'; + /* generating/postsolving segment */ + mpl->rand = rng_create_rand(); + mpl->flag_p = 0; + mpl->stmt = NULL; +#if 1 /* 11/II-2008 */ + mpl->dca = NULL; +#endif + mpl->m = 0; + mpl->n = 0; + mpl->row = NULL; + mpl->col = NULL; + /* input/output segment */ + mpl->in_fp = NULL; + mpl->in_file = NULL; + mpl->out_fp = NULL; + mpl->out_file = NULL; + mpl->prt_fp = NULL; + mpl->prt_file = NULL; + /* solver interface segment */ + if (setjmp(mpl->jump)) xassert(mpl != mpl); + mpl->phase = 0; + mpl->mod_file = NULL; + mpl->mpl_buf = xcalloc(255+1, sizeof(char)); + mpl->mpl_buf[0] = '\0'; + return mpl; +} + +/*---------------------------------------------------------------------- +-- mpl_read_model - read model section and optional data section. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_read_model(MPL *mpl, char *file, int skip_data); +-- +-- *Description* +-- +-- The routine mpl_read_model reads model section and optionally data +-- section, which may follow the model section, from the text file, +-- whose name is the character string file, performs translating model +-- statements and data blocks, and stores all the information in the +-- translator database. +-- +-- The parameter skip_data is a flag. If the input file contains the +-- data section and this flag is set, the data section is not read as +-- if there were no data section and a warning message is issued. This +-- allows reading the data section from another input file. +-- +-- This routine should be called once after the routine mpl_initialize +-- and before other API routines. +-- +-- *Returns* +-- +-- The routine mpl_read_model returns one the following codes: +-- +-- 1 - translation successful. The input text file contains only model +-- section. In this case the calling program may call the routine +-- mpl_read_data to read data section from another file. +-- 2 - translation successful. The input text file contains both model +-- and data section. +-- 4 - processing failed due to some errors. In this case the calling +-- program should call the routine mpl_terminate to terminate model +-- processing. */ + +int mpl_read_model(MPL *mpl, char *file, int skip_data) +{ if (mpl->phase != 0) + xfault("mpl_read_model: invalid call sequence\n"); + if (file == NULL) + xfault("mpl_read_model: no input filename specified\n"); + /* set up error handler */ + if (setjmp(mpl->jump)) goto done; + /* translate model section */ + mpl->phase = 1; + xprintf("Reading model section from %s...\n", file); + open_input(mpl, file); + model_section(mpl); + if (mpl->model == NULL) + error(mpl, "empty model section not allowed"); + /* save name of the input text file containing model section for + error diagnostics during the generation phase */ + mpl->mod_file = xcalloc(strlen(file)+1, sizeof(char)); + strcpy(mpl->mod_file, mpl->in_file); + /* allocate content arrays for all model objects */ + alloc_content(mpl); + /* optional data section may begin with the keyword 'data' */ + if (is_keyword(mpl, "data")) + { if (skip_data) + { warning(mpl, "data section ignored"); + goto skip; + } + mpl->flag_d = 1; + get_token(mpl /* data */); + if (mpl->token != T_SEMICOLON) + error(mpl, "semicolon missing where expected"); + get_token(mpl /* ; */); + /* translate data section */ + mpl->phase = 2; + xprintf("Reading data section from %s...\n", file); + data_section(mpl); + } + /* process end statement */ + end_statement(mpl); +skip: xprintf("%d line%s were read\n", + mpl->line, mpl->line == 1 ? "" : "s"); + close_input(mpl); +done: /* return to the calling program */ + return mpl->phase; +} + +/*---------------------------------------------------------------------- +-- mpl_read_data - read data section. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_read_data(MPL *mpl, char *file); +-- +-- *Description* +-- +-- The routine mpl_read_data reads data section from the text file, +-- whose name is the character string file, performs translating data +-- blocks, and stores the data read in the translator database. +-- +-- If this routine is used, it should be called once after the routine +-- mpl_read_model and if the latter returned the code 1. +-- +-- *Returns* +-- +-- The routine mpl_read_data returns one of the following codes: +-- +-- 2 - data section has been successfully processed. +-- 4 - processing failed due to some errors. In this case the calling +-- program should call the routine mpl_terminate to terminate model +-- processing. */ + +int mpl_read_data(MPL *mpl, char *file) +#if 0 /* 02/X-2008 */ +{ if (mpl->phase != 1) +#else +{ if (!(mpl->phase == 1 || mpl->phase == 2)) +#endif + xfault("mpl_read_data: invalid call sequence\n"); + if (file == NULL) + xfault("mpl_read_data: no input filename specified\n"); + /* set up error handler */ + if (setjmp(mpl->jump)) goto done; + /* process data section */ + mpl->phase = 2; + xprintf("Reading data section from %s...\n", file); + mpl->flag_d = 1; + open_input(mpl, file); + /* in this case the keyword 'data' is optional */ + if (is_literal(mpl, "data")) + { get_token(mpl /* data */); + if (mpl->token != T_SEMICOLON) + error(mpl, "semicolon missing where expected"); + get_token(mpl /* ; */); + } + data_section(mpl); + /* process end statement */ + end_statement(mpl); + xprintf("%d line%s were read\n", + mpl->line, mpl->line == 1 ? "" : "s"); + close_input(mpl); +done: /* return to the calling program */ + return mpl->phase; +} + +/*---------------------------------------------------------------------- +-- mpl_generate - generate model. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_generate(MPL *mpl, char *file); +-- +-- *Description* +-- +-- The routine mpl_generate generates the model using its description +-- stored in the translator database. This phase means generating all +-- variables, constraints, and objectives, executing check and display +-- statements, which precede the solve statement (if it is presented), +-- and building the problem instance. +-- +-- The character string file specifies the name of output text file, to +-- which output produced by display statements should be written. It is +-- allowed to specify NULL, in which case the output goes to stdout via +-- the routine print. +-- +-- This routine should be called once after the routine mpl_read_model +-- or mpl_read_data and if one of the latters returned the code 2. +-- +-- *Returns* +-- +-- The routine mpl_generate returns one of the following codes: +-- +-- 3 - model has been successfully generated. In this case the calling +-- program may call other api routines to obtain components of the +-- problem instance from the translator database. +-- 4 - processing failed due to some errors. In this case the calling +-- program should call the routine mpl_terminate to terminate model +-- processing. */ + +int mpl_generate(MPL *mpl, char *file) +{ if (!(mpl->phase == 1 || mpl->phase == 2)) + xfault("mpl_generate: invalid call sequence\n"); + /* set up error handler */ + if (setjmp(mpl->jump)) goto done; + /* generate model */ + mpl->phase = 3; + open_output(mpl, file); + generate_model(mpl); + flush_output(mpl); + /* build problem instance */ + build_problem(mpl); + /* generation phase has been finished */ + xprintf("Model has been successfully generated\n"); +done: /* return to the calling program */ + return mpl->phase; +} + +/*---------------------------------------------------------------------- +-- mpl_get_prob_name - obtain problem (model) name. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- char *mpl_get_prob_name(MPL *mpl); +-- +-- *Returns* +-- +-- The routine mpl_get_prob_name returns a pointer to internal buffer, +-- which contains symbolic name of the problem (model). +-- +-- *Note* +-- +-- Currently MathProg has no feature to assign a symbolic name to the +-- model. Therefore the routine mpl_get_prob_name tries to construct +-- such name using the name of input text file containing model section, +-- although this is not a good idea (due to portability problems). */ + +char *mpl_get_prob_name(MPL *mpl) +{ char *name = mpl->mpl_buf; + char *file = mpl->mod_file; + int k; + if (mpl->phase != 3) + xfault("mpl_get_prob_name: invalid call sequence\n"); + for (;;) + { if (strchr(file, '/') != NULL) + file = strchr(file, '/') + 1; + else if (strchr(file, '\\') != NULL) + file = strchr(file, '\\') + 1; + else if (strchr(file, ':') != NULL) + file = strchr(file, ':') + 1; + else + break; + } + for (k = 0; ; k++) + { if (k == 255) break; + if (!(isalnum((unsigned char)*file) || *file == '_')) break; + name[k] = *file++; + } + if (k == 0) + strcpy(name, "Unknown"); + else + name[k] = '\0'; + xassert(strlen(name) <= 255); + return name; +} + +/*---------------------------------------------------------------------- +-- mpl_get_num_rows - determine number of rows. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_get_num_rows(MPL *mpl); +-- +-- *Returns* +-- +-- The routine mpl_get_num_rows returns total number of rows in the +-- problem, where each row is an individual constraint or objective. */ + +int mpl_get_num_rows(MPL *mpl) +{ if (mpl->phase != 3) + xfault("mpl_get_num_rows: invalid call sequence\n"); + return mpl->m; +} + +/*---------------------------------------------------------------------- +-- mpl_get_num_cols - determine number of columns. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_get_num_cols(MPL *mpl); +-- +-- *Returns* +-- +-- The routine mpl_get_num_cols returns total number of columns in the +-- problem, where each column is an individual variable. */ + +int mpl_get_num_cols(MPL *mpl) +{ if (mpl->phase != 3) + xfault("mpl_get_num_cols: invalid call sequence\n"); + return mpl->n; +} + +/*---------------------------------------------------------------------- +-- mpl_get_row_name - obtain row name. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- char *mpl_get_row_name(MPL *mpl, int i); +-- +-- *Returns* +-- +-- The routine mpl_get_row_name returns a pointer to internal buffer, +-- which contains symbolic name of i-th row of the problem. */ + +char *mpl_get_row_name(MPL *mpl, int i) +{ char *name = mpl->mpl_buf, *t; + int len; + if (mpl->phase != 3) + xfault("mpl_get_row_name: invalid call sequence\n"); + if (!(1 <= i && i <= mpl->m)) + xfault("mpl_get_row_name: i = %d; row number out of range\n", + i); + strcpy(name, mpl->row[i]->con->name); + len = strlen(name); + xassert(len <= 255); + t = format_tuple(mpl, '[', mpl->row[i]->memb->tuple); + while (*t) + { if (len == 255) break; + name[len++] = *t++; + } + name[len] = '\0'; + if (len == 255) strcpy(name+252, "..."); + xassert(strlen(name) <= 255); + return name; +} + +/*---------------------------------------------------------------------- +-- mpl_get_row_kind - determine row kind. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_get_row_kind(MPL *mpl, int i); +-- +-- *Returns* +-- +-- The routine mpl_get_row_kind returns the kind of i-th row, which can +-- be one of the following: +-- +-- MPL_ST - non-free (constraint) row; +-- MPL_MIN - free (objective) row to be minimized; +-- MPL_MAX - free (objective) row to be maximized. */ + +int mpl_get_row_kind(MPL *mpl, int i) +{ int kind; + if (mpl->phase != 3) + xfault("mpl_get_row_kind: invalid call sequence\n"); + if (!(1 <= i && i <= mpl->m)) + xfault("mpl_get_row_kind: i = %d; row number out of range\n", + i); + switch (mpl->row[i]->con->type) + { case A_CONSTRAINT: + kind = MPL_ST; break; + case A_MINIMIZE: + kind = MPL_MIN; break; + case A_MAXIMIZE: + kind = MPL_MAX; break; + default: + xassert(mpl != mpl); + } + return kind; +} + +/*---------------------------------------------------------------------- +-- mpl_get_row_bnds - obtain row bounds. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_get_row_bnds(MPL *mpl, int i, double *lb, double *ub); +-- +-- *Description* +-- +-- The routine mpl_get_row_bnds stores lower and upper bounds of i-th +-- row of the problem to the locations, which the parameters lb and ub +-- point to, respectively. Besides the routine returns the type of the +-- i-th row. +-- +-- If some of the parameters lb and ub is NULL, the corresponding bound +-- value is not stored. +-- +-- Types and bounds have the following meaning: +-- +-- Type Bounds Note +-- ----------------------------------------------------------- +-- MPL_FR -inf < f(x) < +inf Free linear form +-- MPL_LO lb <= f(x) < +inf Inequality f(x) >= lb +-- MPL_UP -inf < f(x) <= ub Inequality f(x) <= ub +-- MPL_DB lb <= f(x) <= ub Inequality lb <= f(x) <= ub +-- MPL_FX f(x) = lb Equality f(x) = lb +-- +-- where f(x) is the corresponding linear form of the i-th row. +-- +-- If the row has no lower bound, *lb is set to zero; if the row has +-- no upper bound, *ub is set to zero; and if the row is of fixed type, +-- both *lb and *ub are set to the same value. +-- +-- *Returns* +-- +-- The routine returns the type of the i-th row as it is stated in the +-- table above. */ + +int mpl_get_row_bnds(MPL *mpl, int i, double *_lb, double *_ub) +{ ELEMCON *con; + int type; + double lb, ub; + if (mpl->phase != 3) + xfault("mpl_get_row_bnds: invalid call sequence\n"); + if (!(1 <= i && i <= mpl->m)) + xfault("mpl_get_row_bnds: i = %d; row number out of range\n", + i); + con = mpl->row[i]; +#if 0 /* 21/VII-2006 */ + if (con->con->lbnd == NULL && con->con->ubnd == NULL) + type = MPL_FR, lb = ub = 0.0; + else if (con->con->ubnd == NULL) + type = MPL_LO, lb = con->lbnd, ub = 0.0; + else if (con->con->lbnd == NULL) + type = MPL_UP, lb = 0.0, ub = con->ubnd; + else if (con->con->lbnd != con->con->ubnd) + type = MPL_DB, lb = con->lbnd, ub = con->ubnd; + else + type = MPL_FX, lb = ub = con->lbnd; +#else + lb = (con->con->lbnd == NULL ? -DBL_MAX : con->lbnd); + ub = (con->con->ubnd == NULL ? +DBL_MAX : con->ubnd); + if (lb == -DBL_MAX && ub == +DBL_MAX) + type = MPL_FR, lb = ub = 0.0; + else if (ub == +DBL_MAX) + type = MPL_LO, ub = 0.0; + else if (lb == -DBL_MAX) + type = MPL_UP, lb = 0.0; + else if (con->con->lbnd != con->con->ubnd) + type = MPL_DB; + else + type = MPL_FX; +#endif + if (_lb != NULL) *_lb = lb; + if (_ub != NULL) *_ub = ub; + return type; +} + +/*---------------------------------------------------------------------- +-- mpl_get_mat_row - obtain row of the constraint matrix. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_get_mat_row(MPL *mpl, int i, int ndx[], double val[]); +-- +-- *Description* +-- +-- The routine mpl_get_mat_row stores column indices and numeric values +-- of constraint coefficients for the i-th row to locations ndx[1], ..., +-- ndx[len] and val[1], ..., val[len], respectively, where 0 <= len <= n +-- is number of (structural) non-zero constraint coefficients, and n is +-- number of columns in the problem. +-- +-- If the parameter ndx is NULL, column indices are not stored. If the +-- parameter val is NULL, numeric values are not stored. +-- +-- Note that free rows may have constant terms, which are not part of +-- the constraint matrix and therefore not reported by this routine. The +-- constant term of a particular row can be obtained, if necessary, via +-- the routine mpl_get_row_c0. +-- +-- *Returns* +-- +-- The routine mpl_get_mat_row returns len, which is length of i-th row +-- of the constraint matrix (i.e. number of non-zero coefficients). */ + +int mpl_get_mat_row(MPL *mpl, int i, int ndx[], double val[]) +{ FORMULA *term; + int len = 0; + if (mpl->phase != 3) + xfault("mpl_get_mat_row: invalid call sequence\n"); + if (!(1 <= i && i <= mpl->m)) + xfault("mpl_get_mat_row: i = %d; row number out of range\n", + i); + for (term = mpl->row[i]->form; term != NULL; term = term->next) + { xassert(term->var != NULL); + len++; + xassert(len <= mpl->n); + if (ndx != NULL) ndx[len] = term->var->j; + if (val != NULL) val[len] = term->coef; + } + return len; +} + +/*---------------------------------------------------------------------- +-- mpl_get_row_c0 - obtain constant term of free row. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- double mpl_get_row_c0(MPL *mpl, int i); +-- +-- *Returns* +-- +-- The routine mpl_get_row_c0 returns numeric value of constant term of +-- i-th row. +-- +-- Note that only free rows may have non-zero constant terms. Therefore +-- if i-th row is not free, the routine returns zero. */ + +double mpl_get_row_c0(MPL *mpl, int i) +{ ELEMCON *con; + double c0; + if (mpl->phase != 3) + xfault("mpl_get_row_c0: invalid call sequence\n"); + if (!(1 <= i && i <= mpl->m)) + xfault("mpl_get_row_c0: i = %d; row number out of range\n", + i); + con = mpl->row[i]; + if (con->con->lbnd == NULL && con->con->ubnd == NULL) + c0 = - con->lbnd; + else + c0 = 0.0; + return c0; +} + +/*---------------------------------------------------------------------- +-- mpl_get_col_name - obtain column name. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- char *mpl_get_col_name(MPL *mpl, int j); +-- +-- *Returns* +-- +-- The routine mpl_get_col_name returns a pointer to internal buffer, +-- which contains symbolic name of j-th column of the problem. */ + +char *mpl_get_col_name(MPL *mpl, int j) +{ char *name = mpl->mpl_buf, *t; + int len; + if (mpl->phase != 3) + xfault("mpl_get_col_name: invalid call sequence\n"); + if (!(1 <= j && j <= mpl->n)) + xfault("mpl_get_col_name: j = %d; column number out of range\n" + , j); + strcpy(name, mpl->col[j]->var->name); + len = strlen(name); + xassert(len <= 255); + t = format_tuple(mpl, '[', mpl->col[j]->memb->tuple); + while (*t) + { if (len == 255) break; + name[len++] = *t++; + } + name[len] = '\0'; + if (len == 255) strcpy(name+252, "..."); + xassert(strlen(name) <= 255); + return name; +} + +/*---------------------------------------------------------------------- +-- mpl_get_col_kind - determine column kind. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_get_col_kind(MPL *mpl, int j); +-- +-- *Returns* +-- +-- The routine mpl_get_col_kind returns the kind of j-th column, which +-- can be one of the following: +-- +-- MPL_NUM - continuous variable; +-- MPL_INT - integer variable; +-- MPL_BIN - binary variable. +-- +-- Note that column kinds are defined independently on type and bounds +-- (reported by the routine mpl_get_col_bnds) of corresponding columns. +-- This means, in particular, that bounds of an integer column may be +-- fractional, or a binary column may have lower and upper bounds that +-- are not 0 and 1 (or it may have no lower/upper bound at all). */ + +int mpl_get_col_kind(MPL *mpl, int j) +{ int kind; + if (mpl->phase != 3) + xfault("mpl_get_col_kind: invalid call sequence\n"); + if (!(1 <= j && j <= mpl->n)) + xfault("mpl_get_col_kind: j = %d; column number out of range\n" + , j); + switch (mpl->col[j]->var->type) + { case A_NUMERIC: + kind = MPL_NUM; break; + case A_INTEGER: + kind = MPL_INT; break; + case A_BINARY: + kind = MPL_BIN; break; + default: + xassert(mpl != mpl); + } + return kind; +} + +/*---------------------------------------------------------------------- +-- mpl_get_col_bnds - obtain column bounds. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_get_col_bnds(MPL *mpl, int j, double *lb, double *ub); +-- +-- *Description* +-- +-- The routine mpl_get_col_bnds stores lower and upper bound of j-th +-- column of the problem to the locations, which the parameters lb and +-- ub point to, respectively. Besides the routine returns the type of +-- the j-th column. +-- +-- If some of the parameters lb and ub is NULL, the corresponding bound +-- value is not stored. +-- +-- Types and bounds have the following meaning: +-- +-- Type Bounds Note +-- ------------------------------------------------------ +-- MPL_FR -inf < x < +inf Free (unbounded) variable +-- MPL_LO lb <= x < +inf Variable with lower bound +-- MPL_UP -inf < x <= ub Variable with upper bound +-- MPL_DB lb <= x <= ub Double-bounded variable +-- MPL_FX x = lb Fixed variable +-- +-- where x is individual variable corresponding to the j-th column. +-- +-- If the column has no lower bound, *lb is set to zero; if the column +-- has no upper bound, *ub is set to zero; and if the column is of fixed +-- type, both *lb and *ub are set to the same value. +-- +-- *Returns* +-- +-- The routine returns the type of the j-th column as it is stated in +-- the table above. */ + +int mpl_get_col_bnds(MPL *mpl, int j, double *_lb, double *_ub) +{ ELEMVAR *var; + int type; + double lb, ub; + if (mpl->phase != 3) + xfault("mpl_get_col_bnds: invalid call sequence\n"); + if (!(1 <= j && j <= mpl->n)) + xfault("mpl_get_col_bnds: j = %d; column number out of range\n" + , j); + var = mpl->col[j]; +#if 0 /* 21/VII-2006 */ + if (var->var->lbnd == NULL && var->var->ubnd == NULL) + type = MPL_FR, lb = ub = 0.0; + else if (var->var->ubnd == NULL) + type = MPL_LO, lb = var->lbnd, ub = 0.0; + else if (var->var->lbnd == NULL) + type = MPL_UP, lb = 0.0, ub = var->ubnd; + else if (var->var->lbnd != var->var->ubnd) + type = MPL_DB, lb = var->lbnd, ub = var->ubnd; + else + type = MPL_FX, lb = ub = var->lbnd; +#else + lb = (var->var->lbnd == NULL ? -DBL_MAX : var->lbnd); + ub = (var->var->ubnd == NULL ? +DBL_MAX : var->ubnd); + if (lb == -DBL_MAX && ub == +DBL_MAX) + type = MPL_FR, lb = ub = 0.0; + else if (ub == +DBL_MAX) + type = MPL_LO, ub = 0.0; + else if (lb == -DBL_MAX) + type = MPL_UP, lb = 0.0; + else if (var->var->lbnd != var->var->ubnd) + type = MPL_DB; + else + type = MPL_FX; +#endif + if (_lb != NULL) *_lb = lb; + if (_ub != NULL) *_ub = ub; + return type; +} + +/*---------------------------------------------------------------------- +-- mpl_has_solve_stmt - check if model has solve statement. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_has_solve_stmt(MPL *mpl); +-- +-- *Returns* +-- +-- If the model has the solve statement, the routine returns non-zero, +-- otherwise zero is returned. */ + +int mpl_has_solve_stmt(MPL *mpl) +{ if (mpl->phase != 3) + xfault("mpl_has_solve_stmt: invalid call sequence\n"); + return mpl->flag_s; +} + +#if 1 /* 15/V-2010 */ +void mpl_put_row_soln(MPL *mpl, int i, int stat, double prim, + double dual) +{ /* store row (constraint/objective) solution components */ + xassert(mpl->phase == 3); + xassert(1 <= i && i <= mpl->m); + mpl->row[i]->stat = stat; + mpl->row[i]->prim = prim; + mpl->row[i]->dual = dual; + return; +} +#endif + +#if 1 /* 15/V-2010 */ +void mpl_put_col_soln(MPL *mpl, int j, int stat, double prim, + double dual) +{ /* store column (variable) solution components */ + xassert(mpl->phase == 3); + xassert(1 <= j && j <= mpl->n); + mpl->col[j]->stat = stat; + mpl->col[j]->prim = prim; + mpl->col[j]->dual = dual; + return; +} +#endif + +#if 0 /* 15/V-2010 */ +/*---------------------------------------------------------------------- +-- mpl_put_col_value - store column value. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- void mpl_put_col_value(MPL *mpl, int j, double val); +-- +-- *Description* +-- +-- The routine mpl_put_col_value stores numeric value of j-th column +-- into the translator database. It is assumed that the column value is +-- provided by the solver. */ + +void mpl_put_col_value(MPL *mpl, int j, double val) +{ if (mpl->phase != 3) + xfault("mpl_put_col_value: invalid call sequence\n"); + if (!(1 <= j && j <= mpl->n)) + xfault( + "mpl_put_col_value: j = %d; column number out of range\n", j); + mpl->col[j]->prim = val; + return; +} +#endif + +/*---------------------------------------------------------------------- +-- mpl_postsolve - postsolve model. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- int mpl_postsolve(MPL *mpl); +-- +-- *Description* +-- +-- The routine mpl_postsolve performs postsolving of the model using +-- its description stored in the translator database. This phase means +-- executing statements, which follow the solve statement. +-- +-- If this routine is used, it should be called once after the routine +-- mpl_generate and if the latter returned the code 3. +-- +-- *Returns* +-- +-- The routine mpl_postsolve returns one of the following codes: +-- +-- 3 - model has been successfully postsolved. +-- 4 - processing failed due to some errors. In this case the calling +-- program should call the routine mpl_terminate to terminate model +-- processing. */ + +int mpl_postsolve(MPL *mpl) +{ if (!(mpl->phase == 3 && !mpl->flag_p)) + xfault("mpl_postsolve: invalid call sequence\n"); + /* set up error handler */ + if (setjmp(mpl->jump)) goto done; + /* perform postsolving */ + postsolve_model(mpl); + flush_output(mpl); + /* postsolving phase has been finished */ + xprintf("Model has been successfully processed\n"); +done: /* return to the calling program */ + return mpl->phase; +} + +/*---------------------------------------------------------------------- +-- mpl_terminate - free all resources used by translator. +-- +-- *Synopsis* +-- +-- #include "glpmpl.h" +-- void mpl_terminate(MPL *mpl); +-- +-- *Description* +-- +-- The routine mpl_terminate frees all the resources used by the GNU +-- MathProg translator. */ + +void mpl_terminate(MPL *mpl) +{ if (setjmp(mpl->jump)) xassert(mpl != mpl); + switch (mpl->phase) + { case 0: + case 1: + case 2: + case 3: + /* there were no errors; clean the model content */ + clean_model(mpl); + xassert(mpl->a_list == NULL); +#if 1 /* 11/II-2008 */ + xassert(mpl->dca == NULL); +#endif + break; + case 4: + /* model processing has been finished due to error; delete + search trees, which may be created for some arrays */ + { ARRAY *a; + for (a = mpl->a_list; a != NULL; a = a->next) + if (a->tree != NULL) avl_delete_tree(a->tree); + } +#if 1 /* 11/II-2008 */ + free_dca(mpl); +#endif + break; + default: + xassert(mpl != mpl); + } + /* delete the translator database */ + xfree(mpl->image); + xfree(mpl->b_image); + xfree(mpl->f_image); + xfree(mpl->context); + dmp_delete_pool(mpl->pool); + avl_delete_tree(mpl->tree); + dmp_delete_pool(mpl->strings); + dmp_delete_pool(mpl->symbols); + dmp_delete_pool(mpl->tuples); + dmp_delete_pool(mpl->arrays); + dmp_delete_pool(mpl->members); + dmp_delete_pool(mpl->elemvars); + dmp_delete_pool(mpl->formulae); + dmp_delete_pool(mpl->elemcons); + xfree(mpl->sym_buf); + xfree(mpl->tup_buf); + rng_delete_rand(mpl->rand); + if (mpl->row != NULL) xfree(mpl->row); + if (mpl->col != NULL) xfree(mpl->col); + if (mpl->in_fp != NULL) xfclose(mpl->in_fp); + if (mpl->out_fp != NULL && mpl->out_fp != (void *)stdout) + xfclose(mpl->out_fp); + if (mpl->out_file != NULL) xfree(mpl->out_file); + if (mpl->prt_fp != NULL) xfclose(mpl->prt_fp); + if (mpl->prt_file != NULL) xfree(mpl->prt_file); + if (mpl->mod_file != NULL) xfree(mpl->mod_file); + xfree(mpl->mpl_buf); + xfree(mpl); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmpl05.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmpl05.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,562 @@ +/* glpmpl05.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Authors: Andrew Makhorin +* Heinrich Schuchardt +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_STDIO +#define _GLPSTD_TIME +#include "glpmpl.h" + +double fn_gmtime(MPL *mpl) +{ /* obtain the current calendar time (UTC) */ + time_t timer; + struct tm *tm; + int j; + time(&timer); + if (timer == (time_t)(-1)) +err: error(mpl, "gmtime(); unable to obtain current calendar time"); + tm = gmtime(&timer); + if (tm == NULL) goto err; + j = jday(tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year); + if (j < 0) goto err; + return (((double)(j - jday(1, 1, 1970)) * 24.0 + + (double)tm->tm_hour) * 60.0 + (double)tm->tm_min) * 60.0 + + (double)tm->tm_sec; +} + +static char *week[] = { "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday" }; + +static char *moon[] = { "January", "February", "March", "April", "May", + "June", "July", "August", "September", "October", "November", + "December" }; + +static void error1(MPL *mpl, const char *str, const char *s, + const char *fmt, const char *f, const char *msg) +{ xprintf("Input string passed to str2time:\n"); + xprintf("%s\n", str); + xprintf("%*s\n", (s - str) + 1, "^"); + xprintf("Format string passed to str2time:\n"); + xprintf("%s\n", fmt); + xprintf("%*s\n", (f - fmt) + 1, "^"); + error(mpl, "%s", msg); + /* no return */ +} + +double fn_str2time(MPL *mpl, const char *str, const char *fmt) +{ /* convert character string to the calendar time */ + int j, year, month, day, hh, mm, ss, zone; + const char *s, *f; + year = month = day = hh = mm = ss = -1, zone = INT_MAX; + s = str; + for (f = fmt; *f != '\0'; f++) + { if (*f == '%') + { f++; + if (*f == 'b' || *f == 'h') + { /* the abbreviated month name */ + int k; + char *name; + if (month >= 0) + error1(mpl, str, s, fmt, f, "month multiply specified" + ); + while (*s == ' ') s++; + for (month = 1; month <= 12; month++) + { name = moon[month-1]; + for (k = 0; k <= 2; k++) + { if (toupper((unsigned char)s[k]) != + toupper((unsigned char)name[k])) goto next; + } + s += 3; + for (k = 3; name[k] != '\0'; k++) + { if (toupper((unsigned char)*s) != + toupper((unsigned char)name[k])) break; + s++; + } + break; +next: ; + } + if (month > 12) + error1(mpl, str, s, fmt, f, "abbreviated month name m" + "issing or invalid"); + } + else if (*f == 'd') + { /* the day of the month as a decimal number (01..31) */ + if (day >= 0) + error1(mpl, str, s, fmt, f, "day multiply specified"); + while (*s == ' ') s++; + if (!('0' <= *s && *s <= '9')) + error1(mpl, str, s, fmt, f, "day missing or invalid"); + day = (*s++) - '0'; + if ('0' <= *s && *s <= '9') + day = 10 * day + ((*s++) - '0'); + if (!(1 <= day && day <= 31)) + error1(mpl, str, s, fmt, f, "day out of range"); + } + else if (*f == 'H') + { /* the hour as a decimal number, using a 24-hour clock + (00..23) */ + if (hh >= 0) + error1(mpl, str, s, fmt, f, "hour multiply specified") + ; + while (*s == ' ') s++; + if (!('0' <= *s && *s <= '9')) + error1(mpl, str, s, fmt, f, "hour missing or invalid") + ; + hh = (*s++) - '0'; + if ('0' <= *s && *s <= '9') + hh = 10 * hh + ((*s++) - '0'); + if (!(0 <= hh && hh <= 23)) + error1(mpl, str, s, fmt, f, "hour out of range"); + } + else if (*f == 'm') + { /* the month as a decimal number (01..12) */ + if (month >= 0) + error1(mpl, str, s, fmt, f, "month multiply specified" + ); + while (*s == ' ') s++; + if (!('0' <= *s && *s <= '9')) + error1(mpl, str, s, fmt, f, "month missing or invalid" + ); + month = (*s++) - '0'; + if ('0' <= *s && *s <= '9') + month = 10 * month + ((*s++) - '0'); + if (!(1 <= month && month <= 12)) + error1(mpl, str, s, fmt, f, "month out of range"); + } + else if (*f == 'M') + { /* the minute as a decimal number (00..59) */ + if (mm >= 0) + error1(mpl, str, s, fmt, f, "minute multiply specifie" + "d"); + while (*s == ' ') s++; + if (!('0' <= *s && *s <= '9')) + error1(mpl, str, s, fmt, f, "minute missing or invali" + "d"); + mm = (*s++) - '0'; + if ('0' <= *s && *s <= '9') + mm = 10 * mm + ((*s++) - '0'); + if (!(0 <= mm && mm <= 59)) + error1(mpl, str, s, fmt, f, "minute out of range"); + } + else if (*f == 'S') + { /* the second as a decimal number (00..60) */ + if (ss >= 0) + error1(mpl, str, s, fmt, f, "second multiply specifie" + "d"); + while (*s == ' ') s++; + if (!('0' <= *s && *s <= '9')) + error1(mpl, str, s, fmt, f, "second missing or invali" + "d"); + ss = (*s++) - '0'; + if ('0' <= *s && *s <= '9') + ss = 10 * ss + ((*s++) - '0'); + if (!(0 <= ss && ss <= 60)) + error1(mpl, str, s, fmt, f, "second out of range"); + } + else if (*f == 'y') + { /* the year without a century as a decimal number + (00..99); the values 00 to 68 mean the years 2000 to + 2068 while the values 69 to 99 mean the years 1969 to + 1999 */ + if (year >= 0) + error1(mpl, str, s, fmt, f, "year multiply specified") + ; + while (*s == ' ') s++; + if (!('0' <= *s && *s <= '9')) + error1(mpl, str, s, fmt, f, "year missing or invalid") + ; + year = (*s++) - '0'; + if ('0' <= *s && *s <= '9') + year = 10 * year + ((*s++) - '0'); + year += (year >= 69 ? 1900 : 2000); + } + else if (*f == 'Y') + { /* the year as a decimal number, using the Gregorian + calendar */ + if (year >= 0) + error1(mpl, str, s, fmt, f, "year multiply specified") + ; + while (*s == ' ') s++; + if (!('0' <= *s && *s <= '9')) + error1(mpl, str, s, fmt, f, "year missing or invalid") + ; + year = 0; + for (j = 1; j <= 4; j++) + { if (!('0' <= *s && *s <= '9')) break; + year = 10 * year + ((*s++) - '0'); + } + if (!(1 <= year && year <= 4000)) + error1(mpl, str, s, fmt, f, "year out of range"); + } + else if (*f == 'z') + { /* time zone offset in the form zhhmm */ + int z, hh, mm; + if (zone != INT_MAX) + error1(mpl, str, s, fmt, f, "time zone offset multipl" + "y specified"); + while (*s == ' ') s++; + if (*s == 'Z') + { z = hh = mm = 0, s++; + goto skip; + } + if (*s == '+') + z = +1, s++; + else if (*s == '-') + z = -1, s++; + else + error1(mpl, str, s, fmt, f, "time zone offset sign mi" + "ssing"); + hh = 0; + for (j = 1; j <= 2; j++) + { if (!('0' <= *s && *s <= '9')) +err1: error1(mpl, str, s, fmt, f, "time zone offset valu" + "e incomplete or invalid"); + hh = 10 * hh + ((*s++) - '0'); + } + if (hh > 23) +err2: error1(mpl, str, s, fmt, f, "time zone offset value o" + "ut of range"); + if (*s == ':') + { s++; + if (!('0' <= *s && *s <= '9')) goto err1; + } + mm = 0; + if (!('0' <= *s && *s <= '9')) goto skip; + for (j = 1; j <= 2; j++) + { if (!('0' <= *s && *s <= '9')) goto err1; + mm = 10 * mm + ((*s++) - '0'); + } + if (mm > 59) goto err2; +skip: zone = z * (60 * hh + mm); + } + else if (*f == '%') + { /* literal % character */ + goto test; + } + else + error1(mpl, str, s, fmt, f, "invalid conversion specifie" + "r"); + } + else if (*f == ' ') + ; + else +test: { /* check a matching character in the input string */ + if (*s != *f) + error1(mpl, str, s, fmt, f, "character mismatch"); + s++; + } + } + if (year < 0) year = 1970; + if (month < 0) month = 1; + if (day < 0) day = 1; + if (hh < 0) hh = 0; + if (mm < 0) mm = 0; + if (ss < 0) ss = 0; + if (zone == INT_MAX) zone = 0; + j = jday(day, month, year); + xassert(j >= 0); + return (((double)(j - jday(1, 1, 1970)) * 24.0 + (double)hh) * + 60.0 + (double)mm) * 60.0 + (double)ss - 60.0 * (double)zone; +} + +static void error2(MPL *mpl, const char *fmt, const char *f, + const char *msg) +{ xprintf("Format string passed to time2str:\n"); + xprintf("%s\n", fmt); + xprintf("%*s\n", (f - fmt) + 1, "^"); + error(mpl, "%s", msg); + /* no return */ +} + +static int weekday(int j) +{ /* determine weekday number (1 = Mon, ..., 7 = Sun) */ + return (j + jday(1, 1, 1970)) % 7 + 1; +} + +static int firstday(int year) +{ /* determine the first day of the first week for a specified year + according to ISO 8601 */ + int j; + /* if 1 January is Monday, Tuesday, Wednesday or Thursday, it is + in week 01; if 1 January is Friday, Saturday or Sunday, it is + in week 52 or 53 of the previous year */ + j = jday(1, 1, year) - jday(1, 1, 1970); + switch (weekday(j)) + { case 1: /* 1 Jan is Mon */ j += 0; break; + case 2: /* 1 Jan is Tue */ j -= 1; break; + case 3: /* 1 Jan is Wed */ j -= 2; break; + case 4: /* 1 Jan is Thu */ j -= 3; break; + case 5: /* 1 Jan is Fri */ j += 3; break; + case 6: /* 1 Jan is Sat */ j += 2; break; + case 7: /* 1 Jan is Sun */ j += 1; break; + default: xassert(j != j); + } + /* the first day of the week must be Monday */ + xassert(weekday(j) == 1); + return j; +} + +void fn_time2str(MPL *mpl, char *str, double t, const char *fmt) +{ /* convert the calendar time to character string */ + int j, year, month, day, hh, mm, ss, len; + double temp; + const char *f; + char buf[MAX_LENGTH+1]; + if (!(-62135596800.0 <= t && t <= 64092211199.0)) + error(mpl, "time2str(%.*g,...); argument out of range", + DBL_DIG, t); + t = floor(t + 0.5); + temp = fabs(t) / 86400.0; + j = (int)floor(temp); + if (t < 0.0) + { if (temp == floor(temp)) + j = - j; + else + j = - (j + 1); + } + xassert(jdate(j + jday(1, 1, 1970), &day, &month, &year) == 0); + ss = (int)(t - 86400.0 * (double)j); + xassert(0 <= ss && ss < 86400); + mm = ss / 60, ss %= 60; + hh = mm / 60, mm %= 60; + len = 0; + for (f = fmt; *f != '\0'; f++) + { if (*f == '%') + { f++; + if (*f == 'a') + { /* the abbreviated weekday name */ + memcpy(buf, week[weekday(j)-1], 3), buf[3] = '\0'; + } + else if (*f == 'A') + { /* the full weekday name */ + strcpy(buf, week[weekday(j)-1]); + } + else if (*f == 'b' || *f == 'h') + { /* the abbreviated month name */ + memcpy(buf, moon[month-1], 3), buf[3] = '\0'; + } + else if (*f == 'B') + { /* the full month name */ + strcpy(buf, moon[month-1]); + } + else if (*f == 'C') + { /* the century of the year */ + sprintf(buf, "%02d", year / 100); + } + else if (*f == 'd') + { /* the day of the month as a decimal number (01..31) */ + sprintf(buf, "%02d", day); + } + else if (*f == 'D') + { /* the date using the format %m/%d/%y */ + sprintf(buf, "%02d/%02d/%02d", month, day, year % 100); + } + else if (*f == 'e') + { /* the day of the month like with %d, but padded with + blank (1..31) */ + sprintf(buf, "%2d", day); + } + else if (*f == 'F') + { /* the date using the format %Y-%m-%d */ + sprintf(buf, "%04d-%02d-%02d", year, month, day); + } + else if (*f == 'g') + { /* the year corresponding to the ISO week number, but + without the century (range 00 through 99); this has + the same format and value as %y, except that if the + ISO week number (see %V) belongs to the previous or + next year, that year is used instead */ + int iso; + if (j < firstday(year)) + iso = year - 1; + else if (j < firstday(year + 1)) + iso = year; + else + iso = year + 1; + sprintf(buf, "%02d", iso % 100); + } + else if (*f == 'G') + { /* the year corresponding to the ISO week number; this + has the same format and value as %Y, excepth that if + the ISO week number (see %V) belongs to the previous + or next year, that year is used instead */ + int iso; + if (j < firstday(year)) + iso = year - 1; + else if (j < firstday(year + 1)) + iso = year; + else + iso = year + 1; + sprintf(buf, "%04d", iso); + } + else if (*f == 'H') + { /* the hour as a decimal number, using a 24-hour clock + (00..23) */ + sprintf(buf, "%02d", hh); + } + else if (*f == 'I') + { /* the hour as a decimal number, using a 12-hour clock + (01..12) */ + sprintf(buf, "%02d", + hh == 0 ? 12 : hh <= 12 ? hh : hh - 12); + } + else if (*f == 'j') + { /* the day of the year as a decimal number (001..366) */ + sprintf(buf, "%03d", + jday(day, month, year) - jday(1, 1, year) + 1); + } + else if (*f == 'k') + { /* the hour as a decimal number, using a 24-hour clock + like %H, but padded with blank (0..23) */ + sprintf(buf, "%2d", hh); + } + else if (*f == 'l') + { /* the hour as a decimal number, using a 12-hour clock + like %I, but padded with blank (1..12) */ + sprintf(buf, "%2d", + hh == 0 ? 12 : hh <= 12 ? hh : hh - 12); + } + else if (*f == 'm') + { /* the month as a decimal number (01..12) */ + sprintf(buf, "%02d", month); + } + else if (*f == 'M') + { /* the minute as a decimal number (00..59) */ + sprintf(buf, "%02d", mm); + } + else if (*f == 'p') + { /* either AM or PM, according to the given time value; + noon is treated as PM and midnight as AM */ + strcpy(buf, hh <= 11 ? "AM" : "PM"); + } + else if (*f == 'P') + { /* either am or pm, according to the given time value; + noon is treated as pm and midnight as am */ + strcpy(buf, hh <= 11 ? "am" : "pm"); + } + else if (*f == 'r') + { /* the calendar time using the format %I:%M:%S %p */ + sprintf(buf, "%02d:%02d:%02d %s", + hh == 0 ? 12 : hh <= 12 ? hh : hh - 12, + mm, ss, hh <= 11 ? "AM" : "PM"); + } + else if (*f == 'R') + { /* the hour and minute using the format %H:%M */ + sprintf(buf, "%02d:%02d", hh, mm); + } + else if (*f == 'S') + { /* the second as a decimal number (00..59) */ + sprintf(buf, "%02d", ss); + } + else if (*f == 'T') + { /* the time of day using the format %H:%M:%S */ + sprintf(buf, "%02d:%02d:%02d", hh, mm, ss); + } + else if (*f == 'u') + { /* the day of the week as a decimal number (1..7), + Monday being 1 */ + sprintf(buf, "%d", weekday(j)); + } + else if (*f == 'U') + { /* the week number of the current year as a decimal + number (range 00 through 53), starting with the first + Sunday as the first day of the first week; days + preceding the first Sunday in the year are considered + to be in week 00 */ +#if 1 /* 09/I-2009 */ +#undef sun +/* causes compilation error in SunOS */ +#endif + int sun; + /* sun = the first Sunday of the year */ + sun = jday(1, 1, year) - jday(1, 1, 1970); + sun += (7 - weekday(sun)); + sprintf(buf, "%02d", (j + 7 - sun) / 7); + } + else if (*f == 'V') + { /* the ISO week number as a decimal number (range 01 + through 53); ISO weeks start with Monday and end with + Sunday; week 01 of a year is the first week which has + the majority of its days in that year; week 01 of + a year can contain days from the previous year; the + week before week 01 of a year is the last week (52 or + 53) of the previous year even if it contains days + from the new year */ + int iso; + if (j < firstday(year)) + iso = j - firstday(year - 1); + else if (j < firstday(year + 1)) + iso = j - firstday(year); + else + iso = j - firstday(year + 1); + sprintf(buf, "%02d", iso / 7 + 1); + } + else if (*f == 'w') + { /* the day of the week as a decimal number (0..6), + Sunday being 0 */ + sprintf(buf, "%d", weekday(j) % 7); + } + else if (*f == 'W') + { /* the week number of the current year as a decimal + number (range 00 through 53), starting with the first + Monday as the first day of the first week; days + preceding the first Monday in the year are considered + to be in week 00 */ + int mon; + /* mon = the first Monday of the year */ + mon = jday(1, 1, year) - jday(1, 1, 1970); + mon += (8 - weekday(mon)) % 7; + sprintf(buf, "%02d", (j + 7 - mon) / 7); + } + else if (*f == 'y') + { /* the year without a century as a decimal number + (00..99) */ + sprintf(buf, "%02d", year % 100); + } + else if (*f == 'Y') + { /* the year as a decimal number, using the Gregorian + calendar */ + sprintf(buf, "%04d", year); + } + else if (*f == '%') + { /* a literal % character */ + buf[0] = '%', buf[1] = '\0'; + } + else + error2(mpl, fmt, f, "invalid conversion specifier"); + } + else + buf[0] = *f, buf[1] = '\0'; + if (len + strlen(buf) > MAX_LENGTH) + error(mpl, "time2str; output string length exceeds %d chara" + "cters", MAX_LENGTH); + memcpy(str+len, buf, strlen(buf)); + len += strlen(buf); + } + str[len] = '\0'; + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmpl06.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmpl06.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1002 @@ +/* glpmpl06.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_ERRNO +#define _GLPSTD_STDIO +#include "glpmpl.h" +#include "glpsql.h" + +/**********************************************************************/ + +#define CSV_FIELD_MAX 50 +/* maximal number of fields in record */ + +#define CSV_FDLEN_MAX 100 +/* maximal field length */ + +struct csv +{ /* comma-separated values file */ + int mode; + /* 'R' = reading; 'W' = writing */ + char *fname; + /* name of csv file */ + FILE *fp; + /* stream assigned to csv file */ + jmp_buf jump; + /* address for non-local go to in case of error */ + int count; + /* record count */ + /*--------------------------------------------------------------*/ + /* used only for input csv file */ + int c; + /* current character or EOF */ + int what; + /* current marker: */ +#define CSV_EOF 0 /* end-of-file */ +#define CSV_EOR 1 /* end-of-record */ +#define CSV_NUM 2 /* floating-point number */ +#define CSV_STR 3 /* character string */ + char field[CSV_FDLEN_MAX+1]; + /* current field just read */ + int nf; + /* number of fields in the csv file */ + int ref[1+CSV_FIELD_MAX]; + /* ref[k] = k', if k-th field of the csv file corresponds to + k'-th field in the table statement; if ref[k] = 0, k-th field + of the csv file is ignored */ +#if 1 /* 01/VI-2010 */ + int nskip; + /* number of comment records preceding the header record */ +#endif +}; + +#undef read_char + +static void read_char(struct csv *csv) +{ /* read character from csv data file */ + int c; + xassert(csv->c != EOF); + if (csv->c == '\n') csv->count++; +loop: c = fgetc(csv->fp); + if (ferror(csv->fp)) + { xprintf("%s:%d: read error - %s\n", csv->fname, csv->count, + strerror(errno)); + longjmp(csv->jump, 0); + } + if (feof(csv->fp)) + { if (csv->c == '\n') + { csv->count--; + c = EOF; + } + else + { xprintf("%s:%d: warning: missing final end-of-line\n", + csv->fname, csv->count); + c = '\n'; + } + } + else if (c == '\r') + goto loop; + else if (c == '\n') + ; + else if (iscntrl(c)) + { xprintf("%s:%d: invalid control character 0x%02X\n", + csv->fname, csv->count, c); + longjmp(csv->jump, 0); + } + csv->c = c; + return; +} + +static void read_field(struct csv *csv) +{ /* read field from csv data file */ + /* check for end of file */ + if (csv->c == EOF) + { csv->what = CSV_EOF; + strcpy(csv->field, "EOF"); + goto done; + } + /* check for end of record */ + if (csv->c == '\n') + { csv->what = CSV_EOR; + strcpy(csv->field, "EOR"); + read_char(csv); + if (csv->c == ',') +err1: { xprintf("%s:%d: empty field not allowed\n", csv->fname, + csv->count); + longjmp(csv->jump, 0); + } + if (csv->c == '\n') + { xprintf("%s:%d: empty record not allowed\n", csv->fname, + csv->count); + longjmp(csv->jump, 0); + } +#if 1 /* 01/VI-2010 */ + /* skip comment records; may appear only before the very first + record containing field names */ + if (csv->c == '#' && csv->count == 1) + { while (csv->c == '#') + { while (csv->c != '\n') + read_char(csv); + read_char(csv); + csv->nskip++; + } + } +#endif + goto done; + } + /* skip comma before next field */ + if (csv->c == ',') + read_char(csv); + /* read field */ + if (csv->c == '\'' || csv->c == '"') + { /* read a field enclosed in quotes */ + int quote = csv->c, len = 0; + csv->what = CSV_STR; + /* skip opening quote */ + read_char(csv); + /* read field characters within quotes */ + for (;;) + { /* check for closing quote and read it */ + if (csv->c == quote) + { read_char(csv); + if (csv->c == quote) + ; + else if (csv->c == ',' || csv->c == '\n') + break; + else + { xprintf("%s:%d: invalid field\n", csv->fname, + csv->count); + longjmp(csv->jump, 0); + } + } + /* check the current field length */ + if (len == CSV_FDLEN_MAX) +err2: { xprintf("%s:%d: field too long\n", csv->fname, + csv->count); + longjmp(csv->jump, 0); + } + /* add the current character to the field */ + csv->field[len++] = (char)csv->c; + /* read the next character */ + read_char(csv); + } + /* the field has been read */ + if (len == 0) goto err1; + csv->field[len] = '\0'; + } + else + { /* read a field not enclosed in quotes */ + int len = 0; + double temp; + csv->what = CSV_NUM; + while (!(csv->c == ',' || csv->c == '\n')) + { /* quotes within the field are not allowed */ + if (csv->c == '\'' || csv->c == '"') + { xprintf("%s:%d: invalid use of single or double quote wi" + "thin field\n", csv->fname, csv->count); + longjmp(csv->jump, 0); + } + /* check the current field length */ + if (len == CSV_FDLEN_MAX) goto err2; + /* add the current character to the field */ + csv->field[len++] = (char)csv->c; + /* read the next character */ + read_char(csv); + } + /* the field has been read */ + if (len == 0) goto err1; + csv->field[len] = '\0'; + /* check the field type */ + if (str2num(csv->field, &temp)) csv->what = CSV_STR; + } +done: return; +} + +static struct csv *csv_open_file(TABDCA *dca, int mode) +{ /* open csv data file */ + struct csv *csv; + /* create control structure */ + csv = xmalloc(sizeof(struct csv)); + csv->mode = mode; + csv->fname = NULL; + csv->fp = NULL; + if (setjmp(csv->jump)) goto fail; + csv->count = 0; + csv->c = '\n'; + csv->what = 0; + csv->field[0] = '\0'; + csv->nf = 0; + /* try to open the csv data file */ + if (mpl_tab_num_args(dca) < 2) + { xprintf("csv_driver: file name not specified\n"); + longjmp(csv->jump, 0); + } + csv->fname = xmalloc(strlen(mpl_tab_get_arg(dca, 2))+1); + strcpy(csv->fname, mpl_tab_get_arg(dca, 2)); + if (mode == 'R') + { /* open the file for reading */ + int k; + csv->fp = fopen(csv->fname, "r"); + if (csv->fp == NULL) + { xprintf("csv_driver: unable to open %s - %s\n", + csv->fname, strerror(errno)); + longjmp(csv->jump, 0); + } +#if 1 /* 01/VI-2010 */ + csv->nskip = 0; +#endif + /* skip fake new-line */ + read_field(csv); + xassert(csv->what == CSV_EOR); + /* read field names */ + xassert(csv->nf == 0); + for (;;) + { read_field(csv); + if (csv->what == CSV_EOR) + break; + if (csv->what != CSV_STR) + { xprintf("%s:%d: invalid field name\n", csv->fname, + csv->count); + longjmp(csv->jump, 0); + } + if (csv->nf == CSV_FIELD_MAX) + { xprintf("%s:%d: too many fields\n", csv->fname, + csv->count); + longjmp(csv->jump, 0); + } + csv->nf++; + /* find corresponding field in the table statement */ + for (k = mpl_tab_num_flds(dca); k >= 1; k--) + { if (strcmp(mpl_tab_get_name(dca, k), csv->field) == 0) + break; + } + csv->ref[csv->nf] = k; + } + /* find dummy RECNO field in the table statement */ + for (k = mpl_tab_num_flds(dca); k >= 1; k--) + if (strcmp(mpl_tab_get_name(dca, k), "RECNO") == 0) break; + csv->ref[0] = k; + } + else if (mode == 'W') + { /* open the file for writing */ + int k, nf; + csv->fp = fopen(csv->fname, "w"); + if (csv->fp == NULL) + { xprintf("csv_driver: unable to create %s - %s\n", + csv->fname, strerror(errno)); + longjmp(csv->jump, 0); + } + /* write field names */ + nf = mpl_tab_num_flds(dca); + for (k = 1; k <= nf; k++) + fprintf(csv->fp, "%s%c", mpl_tab_get_name(dca, k), + k < nf ? ',' : '\n'); + csv->count++; + } + else + xassert(mode != mode); + /* the file has been open */ + return csv; +fail: /* the file cannot be open */ + if (csv->fname != NULL) xfree(csv->fname); + if (csv->fp != NULL) fclose(csv->fp); + xfree(csv); + return NULL; +} + +static int csv_read_record(TABDCA *dca, struct csv *csv) +{ /* read next record from csv data file */ + int k, ret = 0; + xassert(csv->mode == 'R'); + if (setjmp(csv->jump)) + { ret = 1; + goto done; + } + /* read dummy RECNO field */ + if (csv->ref[0] > 0) +#if 0 /* 01/VI-2010 */ + mpl_tab_set_num(dca, csv->ref[0], csv->count-1); +#else + mpl_tab_set_num(dca, csv->ref[0], csv->count-csv->nskip-1); +#endif + /* read fields */ + for (k = 1; k <= csv->nf; k++) + { read_field(csv); + if (csv->what == CSV_EOF) + { /* end-of-file reached */ + xassert(k == 1); + ret = -1; + goto done; + } + else if (csv->what == CSV_EOR) + { /* end-of-record reached */ + int lack = csv->nf - k + 1; + if (lack == 1) + xprintf("%s:%d: one field missing\n", csv->fname, + csv->count); + else + xprintf("%s:%d: %d fields missing\n", csv->fname, + csv->count, lack); + longjmp(csv->jump, 0); + } + else if (csv->what == CSV_NUM) + { /* floating-point number */ + if (csv->ref[k] > 0) + { double num; + xassert(str2num(csv->field, &num) == 0); + mpl_tab_set_num(dca, csv->ref[k], num); + } + } + else if (csv->what == CSV_STR) + { /* character string */ + if (csv->ref[k] > 0) + mpl_tab_set_str(dca, csv->ref[k], csv->field); + } + else + xassert(csv != csv); + } + /* now there must be NL */ + read_field(csv); + xassert(csv->what != CSV_EOF); + if (csv->what != CSV_EOR) + { xprintf("%s:%d: too many fields\n", csv->fname, csv->count); + longjmp(csv->jump, 0); + } +done: return ret; +} + +static int csv_write_record(TABDCA *dca, struct csv *csv) +{ /* write next record to csv data file */ + int k, nf, ret = 0; + const char *c; + xassert(csv->mode == 'W'); + nf = mpl_tab_num_flds(dca); + for (k = 1; k <= nf; k++) + { switch (mpl_tab_get_type(dca, k)) + { case 'N': + fprintf(csv->fp, "%.*g", DBL_DIG, + mpl_tab_get_num(dca, k)); + break; + case 'S': + fputc('"', csv->fp); + for (c = mpl_tab_get_str(dca, k); *c != '\0'; c++) + { if (*c == '"') + fputc('"', csv->fp), fputc('"', csv->fp); + else + fputc(*c, csv->fp); + } + fputc('"', csv->fp); + break; + default: + xassert(dca != dca); + } + fputc(k < nf ? ',' : '\n', csv->fp); + } + csv->count++; + if (ferror(csv->fp)) + { xprintf("%s:%d: write error - %s\n", csv->fname, csv->count, + strerror(errno)); + ret = 1; + } + return ret; +} + +static int csv_close_file(TABDCA *dca, struct csv *csv) +{ /* close csv data file */ + int ret = 0; + xassert(dca == dca); + if (csv->mode == 'W') + { fflush(csv->fp); + if (ferror(csv->fp)) + { xprintf("%s:%d: write error - %s\n", csv->fname, + csv->count, strerror(errno)); + ret = 1; + } + } + xfree(csv->fname); + fclose(csv->fp); + xfree(csv); + return ret; +} + +/**********************************************************************/ + +#define DBF_FIELD_MAX 50 +/* maximal number of fields in record */ + +#define DBF_FDLEN_MAX 100 +/* maximal field length */ + +struct dbf +{ /* xBASE data file */ + int mode; + /* 'R' = reading; 'W' = writing */ + char *fname; + /* name of xBASE file */ + FILE *fp; + /* stream assigned to xBASE file */ + jmp_buf jump; + /* address for non-local go to in case of error */ + int offset; + /* offset of a byte to be read next */ + int count; + /* record count */ + int nf; + /* number of fields */ + int ref[1+DBF_FIELD_MAX]; + /* ref[k] = k', if k-th field of the csv file corresponds to + k'-th field in the table statement; if ref[k] = 0, k-th field + of the csv file is ignored */ + int type[1+DBF_FIELD_MAX]; + /* type[k] is type of k-th field */ + int len[1+DBF_FIELD_MAX]; + /* len[k] is length of k-th field */ + int prec[1+DBF_FIELD_MAX]; + /* prec[k] is precision of k-th field */ +}; + +static int read_byte(struct dbf *dbf) +{ /* read byte from xBASE data file */ + int b; + b = fgetc(dbf->fp); + if (ferror(dbf->fp)) + { xprintf("%s:0x%X: read error - %s\n", dbf->fname, + dbf->offset, strerror(errno)); + longjmp(dbf->jump, 0); + } + if (feof(dbf->fp)) + { xprintf("%s:0x%X: unexpected end of file\n", dbf->fname, + dbf->offset); + longjmp(dbf->jump, 0); + } + xassert(0x00 <= b && b <= 0xFF); + dbf->offset++; + return b; +} + +static void read_header(TABDCA *dca, struct dbf *dbf) +{ /* read xBASE data file header */ + int b, j, k, recl; + char name[10+1]; + /* (ignored) */ + for (j = 1; j <= 10; j++) + read_byte(dbf); + /* length of each record, in bytes */ + recl = read_byte(dbf); + recl += read_byte(dbf) << 8; + /* (ignored) */ + for (j = 1; j <= 20; j++) + read_byte(dbf); + /* field descriptor array */ + xassert(dbf->nf == 0); + for (;;) + { /* check for end of array */ + b = read_byte(dbf); + if (b == 0x0D) break; + if (dbf->nf == DBF_FIELD_MAX) + { xprintf("%s:0x%X: too many fields\n", dbf->fname, + dbf->offset); + longjmp(dbf->jump, 0); + } + dbf->nf++; + /* field name */ + name[0] = (char)b; + for (j = 1; j < 10; j++) + { b = read_byte(dbf); + name[j] = (char)b; + } + name[10] = '\0'; + b = read_byte(dbf); + if (b != 0x00) + { xprintf("%s:0x%X: invalid field name\n", dbf->fname, + dbf->offset); + longjmp(dbf->jump, 0); + } + /* find corresponding field in the table statement */ + for (k = mpl_tab_num_flds(dca); k >= 1; k--) + if (strcmp(mpl_tab_get_name(dca, k), name) == 0) break; + dbf->ref[dbf->nf] = k; + /* field type */ + b = read_byte(dbf); + if (!(b == 'C' || b == 'N')) + { xprintf("%s:0x%X: invalid field type\n", dbf->fname, + dbf->offset); + longjmp(dbf->jump, 0); + } + dbf->type[dbf->nf] = b; + /* (ignored) */ + for (j = 1; j <= 4; j++) + read_byte(dbf); + /* field length */ + b = read_byte(dbf); + if (b == 0) + { xprintf("%s:0x%X: invalid field length\n", dbf->fname, + dbf->offset); + longjmp(dbf->jump, 0); + } + if (b > DBF_FDLEN_MAX) + { xprintf("%s:0x%X: field too long\n", dbf->fname, + dbf->offset); + longjmp(dbf->jump, 0); + } + dbf->len[dbf->nf] = b; + recl -= b; + /* (ignored) */ + for (j = 1; j <= 15; j++) + read_byte(dbf); + } + if (recl != 1) + { xprintf("%s:0x%X: invalid file header\n", dbf->fname, + dbf->offset); + longjmp(dbf->jump, 0); + } + /* find dummy RECNO field in the table statement */ + for (k = mpl_tab_num_flds(dca); k >= 1; k--) + if (strcmp(mpl_tab_get_name(dca, k), "RECNO") == 0) break; + dbf->ref[0] = k; + return; +} + +static void parse_third_arg(TABDCA *dca, struct dbf *dbf) +{ /* parse xBASE file format (third argument) */ + int j, k, temp; + const char *arg; + dbf->nf = mpl_tab_num_flds(dca); + arg = mpl_tab_get_arg(dca, 3), j = 0; + for (k = 1; k <= dbf->nf; k++) + { /* parse specification of k-th field */ + if (arg[j] == '\0') + { xprintf("xBASE driver: field %s: specification missing\n", + mpl_tab_get_name(dca, k)); + longjmp(dbf->jump, 0); + } + /* parse field type */ + if (arg[j] == 'C' || arg[j] == 'N') + dbf->type[k] = arg[j], j++; + else + { xprintf("xBASE driver: field %s: invalid field type\n", + mpl_tab_get_name(dca, k)); + longjmp(dbf->jump, 0); + } + /* check for left parenthesis */ + if (arg[j] == '(') + j++; + else +err: { xprintf("xBASE driver: field %s: invalid field format\n", + mpl_tab_get_name(dca, k)); + longjmp(dbf->jump, 0); + } + /* parse field length */ + temp = 0; + while (isdigit(arg[j])) + { if (temp > DBF_FDLEN_MAX) break; + temp = 10 * temp + (arg[j] - '0'), j++; + } + if (!(1 <= temp && temp <= DBF_FDLEN_MAX)) + { xprintf("xBASE driver: field %s: invalid field length\n", + mpl_tab_get_name(dca, k)); + longjmp(dbf->jump, 0); + } + dbf->len[k] = temp; + /* parse optional field precision */ + if (dbf->type[k] == 'N' && arg[j] == ',') + { j++; + temp = 0; + while (isdigit(arg[j])) + { if (temp > dbf->len[k]) break; + temp = 10 * temp + (arg[j] - '0'), j++; + } + if (temp > dbf->len[k]) + { xprintf("xBASE driver: field %s: invalid field precision" + "\n", mpl_tab_get_name(dca, k)); + longjmp(dbf->jump, 0); + } + dbf->prec[k] = temp; + } + else + dbf->prec[k] = 0; + /* check for right parenthesis */ + if (arg[j] == ')') + j++; + else + goto err; + } + /* ignore other specifications */ + return; +} + +static void write_byte(struct dbf *dbf, int b) +{ /* write byte to xBASE data file */ + fputc(b, dbf->fp); + dbf->offset++; + return; +} + +static void write_header(TABDCA *dca, struct dbf *dbf) +{ /* write xBASE data file header */ + int j, k, temp; + const char *name; + /* version number */ + write_byte(dbf, 0x03 /* file without DBT */); + /* date of last update (YYMMDD) */ + write_byte(dbf, 70 /* 1970 */); + write_byte(dbf, 1 /* January */); + write_byte(dbf, 1 /* 1st */); + /* number of records (unknown so far) */ + for (j = 1; j <= 4; j++) + write_byte(dbf, 0xFF); + /* length of the header, in bytes */ + temp = 32 + dbf->nf * 32 + 1; + write_byte(dbf, temp); + write_byte(dbf, temp >> 8); + /* length of each record, in bytes */ + temp = 1; + for (k = 1; k <= dbf->nf; k++) + temp += dbf->len[k]; + write_byte(dbf, temp); + write_byte(dbf, temp >> 8); + /* (reserved) */ + for (j = 1; j <= 20; j++) + write_byte(dbf, 0x00); + /* field descriptor array */ + for (k = 1; k <= dbf->nf; k++) + { /* field name (terminated by 0x00) */ + name = mpl_tab_get_name(dca, k); + for (j = 0; j < 10 && name[j] != '\0'; j++) + write_byte(dbf, name[j]); + for (j = j; j < 11; j++) + write_byte(dbf, 0x00); + /* field type */ + write_byte(dbf, dbf->type[k]); + /* (reserved) */ + for (j = 1; j <= 4; j++) + write_byte(dbf, 0x00); + /* field length */ + write_byte(dbf, dbf->len[k]); + /* field precision */ + write_byte(dbf, dbf->prec[k]); + /* (reserved) */ + for (j = 1; j <= 14; j++) + write_byte(dbf, 0x00); + } + /* end of header */ + write_byte(dbf, 0x0D); + return; +} + +static struct dbf *dbf_open_file(TABDCA *dca, int mode) +{ /* open xBASE data file */ + struct dbf *dbf; + /* create control structure */ + dbf = xmalloc(sizeof(struct dbf)); + dbf->mode = mode; + dbf->fname = NULL; + dbf->fp = NULL; + if (setjmp(dbf->jump)) goto fail; + dbf->offset = 0; + dbf->count = 0; + dbf->nf = 0; + /* try to open the xBASE data file */ + if (mpl_tab_num_args(dca) < 2) + { xprintf("xBASE driver: file name not specified\n"); + longjmp(dbf->jump, 0); + } + dbf->fname = xmalloc(strlen(mpl_tab_get_arg(dca, 2))+1); + strcpy(dbf->fname, mpl_tab_get_arg(dca, 2)); + if (mode == 'R') + { /* open the file for reading */ + dbf->fp = fopen(dbf->fname, "rb"); + if (dbf->fp == NULL) + { xprintf("xBASE driver: unable to open %s - %s\n", + dbf->fname, strerror(errno)); + longjmp(dbf->jump, 0); + } + read_header(dca, dbf); + } + else if (mode == 'W') + { /* open the file for writing */ + if (mpl_tab_num_args(dca) < 3) + { xprintf("xBASE driver: file format not specified\n"); + longjmp(dbf->jump, 0); + } + parse_third_arg(dca, dbf); + dbf->fp = fopen(dbf->fname, "wb"); + if (dbf->fp == NULL) + { xprintf("xBASE driver: unable to create %s - %s\n", + dbf->fname, strerror(errno)); + longjmp(dbf->jump, 0); + } + write_header(dca, dbf); + } + else + xassert(mode != mode); + /* the file has been open */ + return dbf; +fail: /* the file cannot be open */ + if (dbf->fname != NULL) xfree(dbf->fname); + if (dbf->fp != NULL) fclose(dbf->fp); + xfree(dbf); + return NULL; +} + +static int dbf_read_record(TABDCA *dca, struct dbf *dbf) +{ /* read next record from xBASE data file */ + int b, j, k, ret = 0; + char buf[DBF_FDLEN_MAX+1]; + xassert(dbf->mode == 'R'); + if (setjmp(dbf->jump)) + { ret = 1; + goto done; + } + /* check record flag */ + b = read_byte(dbf); + if (b == 0x1A) + { /* end of data */ + ret = -1; + goto done; + } + if (b != 0x20) + { xprintf("%s:0x%X: invalid record flag\n", dbf->fname, + dbf->offset); + longjmp(dbf->jump, 0); + } + /* read dummy RECNO field */ + if (dbf->ref[0] > 0) + mpl_tab_set_num(dca, dbf->ref[0], dbf->count+1); + /* read fields */ + for (k = 1; k <= dbf->nf; k++) + { /* read k-th field */ + for (j = 0; j < dbf->len[k]; j++) + buf[j] = (char)read_byte(dbf); + buf[dbf->len[k]] = '\0'; + /* set field value */ + if (dbf->type[k] == 'C') + { /* character field */ + if (dbf->ref[k] > 0) + mpl_tab_set_str(dca, dbf->ref[k], strtrim(buf)); + } + else if (dbf->type[k] == 'N') + { /* numeric field */ + if (dbf->ref[k] > 0) + { double num; + strspx(buf); + xassert(str2num(buf, &num) == 0); + mpl_tab_set_num(dca, dbf->ref[k], num); + } + } + else + xassert(dbf != dbf); + } + /* increase record count */ + dbf->count++; +done: return ret; +} + +static int dbf_write_record(TABDCA *dca, struct dbf *dbf) +{ /* write next record to xBASE data file */ + int j, k, ret = 0; + char buf[255+1]; + xassert(dbf->mode == 'W'); + if (setjmp(dbf->jump)) + { ret = 1; + goto done; + } + /* record flag */ + write_byte(dbf, 0x20); + xassert(dbf->nf == mpl_tab_num_flds(dca)); + for (k = 1; k <= dbf->nf; k++) + { if (dbf->type[k] == 'C') + { /* character field */ + const char *str; + if (mpl_tab_get_type(dca, k) == 'N') + { sprintf(buf, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k)); + str = buf; + } + else if (mpl_tab_get_type(dca, k) == 'S') + str = mpl_tab_get_str(dca, k); + else + xassert(dca != dca); + if ((int)strlen(str) > dbf->len[k]) + { xprintf("xBASE driver: field %s: cannot convert %.15s..." + " to field format\n", mpl_tab_get_name(dca, k), str); + longjmp(dbf->jump, 0); + } + for (j = 0; j < dbf->len[k] && str[j] != '\0'; j++) + write_byte(dbf, str[j]); + for (j = j; j < dbf->len[k]; j++) + write_byte(dbf, ' '); + } + else if (dbf->type[k] == 'N') + { /* numeric field */ + double num = mpl_tab_get_num(dca, k); + if (fabs(num) > 1e20) +err: { xprintf("xBASE driver: field %s: cannot convert %g to fi" + "eld format\n", mpl_tab_get_name(dca, k), num); + longjmp(dbf->jump, 0); + } + sprintf(buf, "%*.*f", dbf->len[k], dbf->prec[k], num); + xassert(strlen(buf) < sizeof(buf)); + if ((int)strlen(buf) != dbf->len[k]) goto err; + for (j = 0; j < dbf->len[k]; j++) + write_byte(dbf, buf[j]); + } + else + xassert(dbf != dbf); + } + /* increase record count */ + dbf->count++; +done: return ret; +} + +static int dbf_close_file(TABDCA *dca, struct dbf *dbf) +{ /* close xBASE data file */ + int ret = 0; + xassert(dca == dca); + if (dbf->mode == 'W') + { if (setjmp(dbf->jump)) + { ret = 1; + goto skip; + } + /* end-of-file flag */ + write_byte(dbf, 0x1A); + /* number of records */ + dbf->offset = 4; + if (fseek(dbf->fp, dbf->offset, SEEK_SET)) + { xprintf("%s:0x%X: seek error - %s\n", dbf->fname, + dbf->offset, strerror(errno)); + longjmp(dbf->jump, 0); + } + write_byte(dbf, dbf->count); + write_byte(dbf, dbf->count >> 8); + write_byte(dbf, dbf->count >> 16); + write_byte(dbf, dbf->count >> 24); + fflush(dbf->fp); + if (ferror(dbf->fp)) + { xprintf("%s:0x%X: write error - %s\n", dbf->fname, + dbf->offset, strerror(errno)); + longjmp(dbf->jump, 0); + } +skip: ; + } + xfree(dbf->fname); + fclose(dbf->fp); + xfree(dbf); + return ret; +} + +/**********************************************************************/ + +#define TAB_CSV 1 +#define TAB_XBASE 2 +#define TAB_ODBC 3 +#define TAB_MYSQL 4 + +void mpl_tab_drv_open(MPL *mpl, int mode) +{ TABDCA *dca = mpl->dca; + xassert(dca->id == 0); + xassert(dca->link == NULL); + xassert(dca->na >= 1); + if (strcmp(dca->arg[1], "CSV") == 0) + { dca->id = TAB_CSV; + dca->link = csv_open_file(dca, mode); + } + else if (strcmp(dca->arg[1], "xBASE") == 0) + { dca->id = TAB_XBASE; + dca->link = dbf_open_file(dca, mode); + } + else if (strcmp(dca->arg[1], "ODBC") == 0 || + strcmp(dca->arg[1], "iODBC") == 0) + { dca->id = TAB_ODBC; + dca->link = db_iodbc_open(dca, mode); + } + else if (strcmp(dca->arg[1], "MySQL") == 0) + { dca->id = TAB_MYSQL; + dca->link = db_mysql_open(dca, mode); + } + else + xprintf("Invalid table driver `%s'\n", dca->arg[1]); + if (dca->link == NULL) + error(mpl, "error on opening table %s", + mpl->stmt->u.tab->name); + return; +} + +int mpl_tab_drv_read(MPL *mpl) +{ TABDCA *dca = mpl->dca; + int ret; + switch (dca->id) + { case TAB_CSV: + ret = csv_read_record(dca, dca->link); + break; + case TAB_XBASE: + ret = dbf_read_record(dca, dca->link); + break; + case TAB_ODBC: + ret = db_iodbc_read(dca, dca->link); + break; + case TAB_MYSQL: + ret = db_mysql_read(dca, dca->link); + break; + default: + xassert(dca != dca); + } + if (ret > 0) + error(mpl, "error on reading data from table %s", + mpl->stmt->u.tab->name); + return ret; +} + +void mpl_tab_drv_write(MPL *mpl) +{ TABDCA *dca = mpl->dca; + int ret; + switch (dca->id) + { case TAB_CSV: + ret = csv_write_record(dca, dca->link); + break; + case TAB_XBASE: + ret = dbf_write_record(dca, dca->link); + break; + case TAB_ODBC: + ret = db_iodbc_write(dca, dca->link); + break; + case TAB_MYSQL: + ret = db_mysql_write(dca, dca->link); + break; + default: + xassert(dca != dca); + } + if (ret) + error(mpl, "error on writing data to table %s", + mpl->stmt->u.tab->name); + return; +} + +void mpl_tab_drv_close(MPL *mpl) +{ TABDCA *dca = mpl->dca; + int ret; + switch (dca->id) + { case TAB_CSV: + ret = csv_close_file(dca, dca->link); + break; + case TAB_XBASE: + ret = dbf_close_file(dca, dca->link); + break; + case TAB_ODBC: + ret = db_iodbc_close(dca, dca->link); + break; + case TAB_MYSQL: + ret = db_mysql_close(dca, dca->link); + break; + default: + xassert(dca != dca); + } + dca->id = 0; + dca->link = NULL; + if (ret) + error(mpl, "error on closing table %s", + mpl->stmt->u.tab->name); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpmps.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpmps.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1401 @@ +/* glpmps.c (MPS format routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_init_mpscp - initialize MPS format control parameters +* +* SYNOPSIS +* +* void glp_init_mpscp(glp_mpscp *parm); +* +* DESCRIPTION +* +* The routine glp_init_mpscp initializes control parameters, which are +* used by the MPS input/output routines glp_read_mps and glp_write_mps, +* with default values. +* +* Default values of the control parameters are stored in the glp_mpscp +* structure, which the parameter parm points to. */ + +void glp_init_mpscp(glp_mpscp *parm) +{ parm->blank = '\0'; + parm->obj_name = NULL; + parm->tol_mps = 1e-12; + return; +} + +static void check_parm(const char *func, const glp_mpscp *parm) +{ /* check control parameters */ + if (!(0x00 <= parm->blank && parm->blank <= 0xFF) || + !(parm->blank == '\0' || isprint(parm->blank))) + xerror("%s: blank = 0x%02X; invalid parameter\n", + func, parm->blank); + if (!(parm->obj_name == NULL || strlen(parm->obj_name) <= 255)) + xerror("%s: obj_name = \"%.12s...\"; parameter too long\n", + func, parm->obj_name); + if (!(0.0 <= parm->tol_mps && parm->tol_mps < 1.0)) + xerror("%s: tol_mps = %g; invalid parameter\n", + func, parm->tol_mps); + return; +} + +/*********************************************************************** +* NAME +* +* glp_read_mps - read problem data in MPS format +* +* SYNOPSIS +* +* int glp_read_mps(glp_prob *P, int fmt, const glp_mpscp *parm, +* const char *fname); +* +* DESCRIPTION +* +* The routine glp_read_mps reads problem data in MPS format from a +* text file. +* +* The parameter fmt specifies the version of MPS format: +* +* GLP_MPS_DECK - fixed (ancient) MPS format; +* GLP_MPS_FILE - free (modern) MPS format. +* +* The parameter parm is a pointer to the structure glp_mpscp, which +* specifies control parameters used by the routine. If parm is NULL, +* the routine uses default settings. +* +* The character string fname specifies a name of the text file to be +* read. +* +* Note that before reading data the current content of the problem +* object is completely erased with the routine glp_erase_prob. +* +* RETURNS +* +* If the operation was successful, the routine glp_read_mps returns +* zero. Otherwise, it prints an error message and returns non-zero. */ + +struct csa +{ /* common storage area */ + glp_prob *P; + /* pointer to problem object */ + int deck; + /* MPS format (0 - free, 1 - fixed) */ + const glp_mpscp *parm; + /* pointer to control parameters */ + const char *fname; + /* name of input MPS file */ + XFILE *fp; + /* stream assigned to input MPS file */ + jmp_buf jump; + /* label for go to in case of error */ + int recno; + /* current record (card) number */ + int recpos; + /* current record (card) position */ + int c; + /* current character */ + int fldno; + /* current field number */ + char field[255+1]; + /* current field content */ + int w80; + /* warning 'record must not be longer than 80 chars' issued */ + int wef; + /* warning 'extra fields detected beyond field 6' issued */ + int obj_row; + /* objective row number */ + void *work1, *work2, *work3; + /* working arrays */ +}; + +static void error(struct csa *csa, const char *fmt, ...) +{ /* print error message and terminate processing */ + va_list arg; + xprintf("%s:%d: ", csa->fname, csa->recno); + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + longjmp(csa->jump, 1); + /* no return */ +} + +static void warning(struct csa *csa, const char *fmt, ...) +{ /* print warning message and continue processing */ + va_list arg; + xprintf("%s:%d: warning: ", csa->fname, csa->recno); + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + return; +} + +static void read_char(struct csa *csa) +{ /* read next character */ + int c; + if (csa->c == '\n') + csa->recno++, csa->recpos = 0; + csa->recpos++; +read: c = xfgetc(csa->fp); + if (c < 0) + { if (xferror(csa->fp)) + error(csa, "read error - %s\n", xerrmsg()); + else if (csa->c == '\n') + error(csa, "unexpected end of file\n"); + else + { warning(csa, "missing final end of line\n"); + c = '\n'; + } + } + else if (c == '\n') + ; + else if (csa->c == '\r') + { c = '\r'; + goto badc; + } + else if (csa->deck && c == '\r') + { csa->c = '\r'; + goto read; + } + else if (c == ' ') + ; + else if (isspace(c)) + { if (csa->deck) +badc: error(csa, "in fixed MPS format white-space character 0x%02" + "X is not allowed\n", c); + c = ' '; + } + else if (iscntrl(c)) + error(csa, "invalid control character 0x%02X\n", c); + if (csa->deck && csa->recpos == 81 && c != '\n' && csa->w80 < 1) + { warning(csa, "in fixed MPS format record must not be longer th" + "an 80 characters\n"); + csa->w80++; + } + csa->c = c; + return; +} + +static int indicator(struct csa *csa, int name) +{ /* skip comment records and read possible indicator record */ + int ret; + /* reset current field number */ + csa->fldno = 0; +loop: /* read the very first character of the next record */ + xassert(csa->c == '\n'); + read_char(csa); + if (csa->c == ' ' || csa->c == '\n') + { /* data record */ + ret = 0; + } + else if (csa->c == '*') + { /* comment record */ + while (csa->c != '\n') + read_char(csa); + goto loop; + } + else + { /* indicator record */ + int len = 0; + while (csa->c != ' ' && csa->c != '\n' && len < 12) + { csa->field[len++] = (char)csa->c; + read_char(csa); + } + csa->field[len] = '\0'; + if (!(strcmp(csa->field, "NAME") == 0 || + strcmp(csa->field, "ROWS") == 0 || + strcmp(csa->field, "COLUMNS") == 0 || + strcmp(csa->field, "RHS") == 0 || + strcmp(csa->field, "RANGES") == 0 || + strcmp(csa->field, "BOUNDS") == 0 || + strcmp(csa->field, "ENDATA") == 0)) + error(csa, "invalid indicator record\n"); + if (!name) + { while (csa->c != '\n') + read_char(csa); + } + ret = 1; + } + return ret; +} + +static void read_field(struct csa *csa) +{ /* read next field of the current data record */ + csa->fldno++; + if (csa->deck) + { /* fixed MPS format */ + int beg, end, pos; + /* determine predefined field positions */ + if (csa->fldno == 1) + beg = 2, end = 3; + else if (csa->fldno == 2) + beg = 5, end = 12; + else if (csa->fldno == 3) + beg = 15, end = 22; + else if (csa->fldno == 4) + beg = 25, end = 36; + else if (csa->fldno == 5) + beg = 40, end = 47; + else if (csa->fldno == 6) + beg = 50, end = 61; + else + xassert(csa != csa); + /* skip blanks preceding the current field */ + if (csa->c != '\n') + { pos = csa->recpos; + while (csa->recpos < beg) + { if (csa->c == ' ') + ; + else if (csa->c == '\n') + break; + else + error(csa, "in fixed MPS format positions %d-%d must " + "be blank\n", pos, beg-1); + read_char(csa); + } + } + /* skip possible comment beginning in the field 3 or 5 */ + if ((csa->fldno == 3 || csa->fldno == 5) && csa->c == '$') + { while (csa->c != '\n') + read_char(csa); + } + /* read the current field */ + for (pos = beg; pos <= end; pos++) + { if (csa->c == '\n') break; + csa->field[pos-beg] = (char)csa->c; + read_char(csa); + } + csa->field[pos-beg] = '\0'; + strtrim(csa->field); + /* skip blanks following the last field */ + if (csa->fldno == 6 && csa->c != '\n') + { while (csa->recpos <= 72) + { if (csa->c == ' ') + ; + else if (csa->c == '\n') + break; + else + error(csa, "in fixed MPS format positions 62-72 must " + "be blank\n"); + read_char(csa); + } + while (csa->c != '\n') + read_char(csa); + } + } + else + { /* free MPS format */ + int len; + /* skip blanks preceding the current field */ + while (csa->c == ' ') + read_char(csa); + /* skip possible comment */ + if (csa->c == '$') + { while (csa->c != '\n') + read_char(csa); + } + /* read the current field */ + len = 0; + while (!(csa->c == ' ' || csa->c == '\n')) + { if (len == 255) + error(csa, "length of field %d exceeds 255 characters\n", + csa->fldno++); + csa->field[len++] = (char)csa->c; + read_char(csa); + } + csa->field[len] = '\0'; + /* skip anything following the last field (any extra fields + are considered to be comments) */ + if (csa->fldno == 6) + { while (csa->c == ' ') + read_char(csa); + if (csa->c != '$' && csa->c != '\n' && csa->wef < 1) + { warning(csa, "some extra field(s) detected beyond field " + "6; field(s) ignored\n"); + csa->wef++; + } + while (csa->c != '\n') + read_char(csa); + } + } + return; +} + +static void patch_name(struct csa *csa, char *name) +{ /* process embedded blanks in symbolic name */ + int blank = csa->parm->blank; + if (blank == '\0') + { /* remove emedded blanks */ + strspx(name); + } + else + { /* replace embedded blanks by specified character */ + for (; *name != '\0'; name++) + if (*name == ' ') *name = (char)blank; + } + return; +} + +static double read_number(struct csa *csa) +{ /* read next field and convert it to floating-point number */ + double x; + char *s; + /* read next field */ + read_field(csa); + xassert(csa->fldno == 4 || csa->fldno == 6); + if (csa->field[0] == '\0') + error(csa, "missing numeric value in field %d\n", csa->fldno); + /* skip initial spaces of the field */ + for (s = csa->field; *s == ' '; s++); + /* perform conversion */ + if (str2num(s, &x) != 0) + error(csa, "cannot convert `%s' to floating-point number\n", + s); + return x; +} + +static void skip_field(struct csa *csa) +{ /* read and skip next field (assumed to be blank) */ + read_field(csa); + if (csa->field[0] != '\0') + error(csa, "field %d must be blank\n", csa->fldno); + return; +} + +static void read_name(struct csa *csa) +{ /* read NAME indicator record */ + if (!(indicator(csa, 1) && strcmp(csa->field, "NAME") == 0)) + error(csa, "missing NAME indicator record\n"); + /* this indicator record looks like a data record; simulate that + fields 1 and 2 were read */ + csa->fldno = 2; + /* field 3: model name */ + read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + warning(csa, "missing model name in field 3\n"); + else + glp_set_prob_name(csa->P, csa->field); + /* skip anything following field 3 */ + while (csa->c != '\n') + read_char(csa); + return; +} + +static void read_rows(struct csa *csa) +{ /* read ROWS section */ + int i, type; +loop: if (indicator(csa, 0)) goto done; + /* field 1: row type */ + read_field(csa), strspx(csa->field); + if (strcmp(csa->field, "N") == 0) + type = GLP_FR; + else if (strcmp(csa->field, "G") == 0) + type = GLP_LO; + else if (strcmp(csa->field, "L") == 0) + type = GLP_UP; + else if (strcmp(csa->field, "E") == 0) + type = GLP_FX; + else if (csa->field[0] == '\0') + error(csa, "missing row type in field 1\n"); + else + error(csa, "invalid row type in field 1\n"); + /* field 2: row name */ + read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + error(csa, "missing row name in field 2\n"); + if (glp_find_row(csa->P, csa->field) != 0) + error(csa, "row `%s' multiply specified\n", csa->field); + i = glp_add_rows(csa->P, 1); + glp_set_row_name(csa->P, i, csa->field); + glp_set_row_bnds(csa->P, i, type, 0.0, 0.0); + /* fields 3, 4, 5, and 6 must be blank */ + skip_field(csa); + skip_field(csa); + skip_field(csa); + skip_field(csa); + goto loop; +done: return; +} + +static void read_columns(struct csa *csa) +{ /* read COLUMNS section */ + int i, j, f, len, kind = GLP_CV, *ind; + double aij, *val; + char name[255+1], *flag; + /* allocate working arrays */ + csa->work1 = ind = xcalloc(1+csa->P->m, sizeof(int)); + csa->work2 = val = xcalloc(1+csa->P->m, sizeof(double)); + csa->work3 = flag = xcalloc(1+csa->P->m, sizeof(char)); + memset(&flag[1], 0, csa->P->m); + /* no current column exists */ + j = 0, len = 0; +loop: if (indicator(csa, 0)) goto done; + /* field 1 must be blank */ + if (csa->deck) + { read_field(csa); + if (csa->field[0] != '\0') + error(csa, "field 1 must be blank\n"); + } + else + csa->fldno++; + /* field 2: column or kind name */ + read_field(csa), patch_name(csa, csa->field); + strcpy(name, csa->field); + /* field 3: row name or keyword 'MARKER' */ + read_field(csa), patch_name(csa, csa->field); + if (strcmp(csa->field, "'MARKER'") == 0) + { /* process kind data record */ + /* field 4 must be blank */ + if (csa->deck) + { read_field(csa); + if (csa->field[0] != '\0') + error(csa, "field 4 must be blank\n"); + } + else + csa->fldno++; + /* field 5: keyword 'INTORG' or 'INTEND' */ + read_field(csa), patch_name(csa, csa->field); + if (strcmp(csa->field, "'INTORG'") == 0) + kind = GLP_IV; + else if (strcmp(csa->field, "'INTEND'") == 0) + kind = GLP_CV; + else if (csa->field[0] == '\0') + error(csa, "missing keyword in field 5\n"); + else + error(csa, "invalid keyword in field 5\n"); + /* field 6 must be blank */ + skip_field(csa); + goto loop; + } + /* process column name specified in field 2 */ + if (name[0] == '\0') + { /* the same column as in previous data record */ + if (j == 0) + error(csa, "missing column name in field 2\n"); + } + else if (j != 0 && strcmp(name, csa->P->col[j]->name) == 0) + { /* the same column as in previous data record */ + xassert(j != 0); + } + else + { /* store the current column */ + if (j != 0) + { glp_set_mat_col(csa->P, j, len, ind, val); + while (len > 0) flag[ind[len--]] = 0; + } + /* create new column */ + if (glp_find_col(csa->P, name) != 0) + error(csa, "column `%s' multiply specified\n", name); + j = glp_add_cols(csa->P, 1); + glp_set_col_name(csa->P, j, name); + glp_set_col_kind(csa->P, j, kind); + if (kind == GLP_CV) + glp_set_col_bnds(csa->P, j, GLP_LO, 0.0, 0.0); + else if (kind == GLP_IV) + glp_set_col_bnds(csa->P, j, GLP_DB, 0.0, 1.0); + else + xassert(kind != kind); + } + /* process fields 3-4 and 5-6 */ + for (f = 3; f <= 5; f += 2) + { /* field 3 or 5: row name */ + if (f == 3) + { if (csa->field[0] == '\0') + error(csa, "missing row name in field 3\n"); + } + else + { read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + { /* if field 5 is blank, field 6 also must be blank */ + skip_field(csa); + continue; + } + } + i = glp_find_row(csa->P, csa->field); + if (i == 0) + error(csa, "row `%s' not found\n", csa->field); + if (flag[i]) + error(csa, "duplicate coefficient in row `%s'\n", + csa->field); + /* field 4 or 6: coefficient value */ + aij = read_number(csa); + if (fabs(aij) < csa->parm->tol_mps) aij = 0.0; + len++, ind[len] = i, val[len] = aij, flag[i] = 1; + } + goto loop; +done: /* store the last column */ + if (j != 0) + glp_set_mat_col(csa->P, j, len, ind, val); + /* free working arrays */ + xfree(ind); + xfree(val); + xfree(flag); + csa->work1 = csa->work2 = csa->work3 = NULL; + return; +} + +static void read_rhs(struct csa *csa) +{ /* read RHS section */ + int i, f, v, type; + double rhs; + char name[255+1], *flag; + /* allocate working array */ + csa->work3 = flag = xcalloc(1+csa->P->m, sizeof(char)); + memset(&flag[1], 0, csa->P->m); + /* no current RHS vector exists */ + v = 0; +loop: if (indicator(csa, 0)) goto done; + /* field 1 must be blank */ + if (csa->deck) + { read_field(csa); + if (csa->field[0] != '\0') + error(csa, "field 1 must be blank\n"); + } + else + csa->fldno++; + /* field 2: RHS vector name */ + read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + { /* the same RHS vector as in previous data record */ + if (v == 0) + { warning(csa, "missing RHS vector name in field 2\n"); + goto blnk; + } + } + else if (v != 0 && strcmp(csa->field, name) == 0) + { /* the same RHS vector as in previous data record */ + xassert(v != 0); + } + else +blnk: { /* new RHS vector */ + if (v != 0) + error(csa, "multiple RHS vectors not supported\n"); + v++; + strcpy(name, csa->field); + } + /* process fields 3-4 and 5-6 */ + for (f = 3; f <= 5; f += 2) + { /* field 3 or 5: row name */ + read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + { if (f == 3) + error(csa, "missing row name in field 3\n"); + else + { /* if field 5 is blank, field 6 also must be blank */ + skip_field(csa); + continue; + } + } + i = glp_find_row(csa->P, csa->field); + if (i == 0) + error(csa, "row `%s' not found\n", csa->field); + if (flag[i]) + error(csa, "duplicate right-hand side for row `%s'\n", + csa->field); + /* field 4 or 6: right-hand side value */ + rhs = read_number(csa); + if (fabs(rhs) < csa->parm->tol_mps) rhs = 0.0; + type = csa->P->row[i]->type; + if (type == GLP_FR) + { if (i == csa->obj_row) + glp_set_obj_coef(csa->P, 0, rhs); + else if (rhs != 0.0) + warning(csa, "non-zero right-hand side for free row `%s'" + " ignored\n", csa->P->row[i]->name); + } + else + glp_set_row_bnds(csa->P, i, type, rhs, rhs); + flag[i] = 1; + } + goto loop; +done: /* free working array */ + xfree(flag); + csa->work3 = NULL; + return; +} + +static void read_ranges(struct csa *csa) +{ /* read RANGES section */ + int i, f, v, type; + double rhs, rng; + char name[255+1], *flag; + /* allocate working array */ + csa->work3 = flag = xcalloc(1+csa->P->m, sizeof(char)); + memset(&flag[1], 0, csa->P->m); + /* no current RANGES vector exists */ + v = 0; +loop: if (indicator(csa, 0)) goto done; + /* field 1 must be blank */ + if (csa->deck) + { read_field(csa); + if (csa->field[0] != '\0') + error(csa, "field 1 must be blank\n"); + } + else + csa->fldno++; + /* field 2: RANGES vector name */ + read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + { /* the same RANGES vector as in previous data record */ + if (v == 0) + { warning(csa, "missing RANGES vector name in field 2\n"); + goto blnk; + } + } + else if (v != 0 && strcmp(csa->field, name) == 0) + { /* the same RANGES vector as in previous data record */ + xassert(v != 0); + } + else +blnk: { /* new RANGES vector */ + if (v != 0) + error(csa, "multiple RANGES vectors not supported\n"); + v++; + strcpy(name, csa->field); + } + /* process fields 3-4 and 5-6 */ + for (f = 3; f <= 5; f += 2) + { /* field 3 or 5: row name */ + read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + { if (f == 3) + error(csa, "missing row name in field 3\n"); + else + { /* if field 5 is blank, field 6 also must be blank */ + skip_field(csa); + continue; + } + } + i = glp_find_row(csa->P, csa->field); + if (i == 0) + error(csa, "row `%s' not found\n", csa->field); + if (flag[i]) + error(csa, "duplicate range for row `%s'\n", csa->field); + /* field 4 or 6: range value */ + rng = read_number(csa); + if (fabs(rng) < csa->parm->tol_mps) rng = 0.0; + type = csa->P->row[i]->type; + if (type == GLP_FR) + warning(csa, "range for free row `%s' ignored\n", + csa->P->row[i]->name); + else if (type == GLP_LO) + { rhs = csa->P->row[i]->lb; + glp_set_row_bnds(csa->P, i, rhs == 0.0 ? GLP_FX : GLP_DB, + rhs, rhs + fabs(rng)); + } + else if (type == GLP_UP) + { rhs = csa->P->row[i]->ub; + glp_set_row_bnds(csa->P, i, rhs == 0.0 ? GLP_FX : GLP_DB, + rhs - fabs(rng), rhs); + } + else if (type == GLP_FX) + { rhs = csa->P->row[i]->lb; + if (rng > 0.0) + glp_set_row_bnds(csa->P, i, GLP_DB, rhs, rhs + rng); + else if (rng < 0.0) + glp_set_row_bnds(csa->P, i, GLP_DB, rhs + rng, rhs); + } + else + xassert(type != type); + flag[i] = 1; + } + goto loop; +done: /* free working array */ + xfree(flag); + csa->work3 = NULL; + return; +} + +static void read_bounds(struct csa *csa) +{ /* read BOUNDS section */ + GLPCOL *col; + int j, v, mask, data; + double bnd, lb, ub; + char type[2+1], name[255+1], *flag; + /* allocate working array */ + csa->work3 = flag = xcalloc(1+csa->P->n, sizeof(char)); + memset(&flag[1], 0, csa->P->n); + /* no current BOUNDS vector exists */ + v = 0; +loop: if (indicator(csa, 0)) goto done; + /* field 1: bound type */ + read_field(csa); + if (strcmp(csa->field, "LO") == 0) + mask = 0x01, data = 1; + else if (strcmp(csa->field, "UP") == 0) + mask = 0x10, data = 1; + else if (strcmp(csa->field, "FX") == 0) + mask = 0x11, data = 1; + else if (strcmp(csa->field, "FR") == 0) + mask = 0x11, data = 0; + else if (strcmp(csa->field, "MI") == 0) + mask = 0x01, data = 0; + else if (strcmp(csa->field, "PL") == 0) + mask = 0x10, data = 0; + else if (strcmp(csa->field, "LI") == 0) + mask = 0x01, data = 1; + else if (strcmp(csa->field, "UI") == 0) + mask = 0x10, data = 1; + else if (strcmp(csa->field, "BV") == 0) + mask = 0x11, data = 0; + else if (csa->field[0] == '\0') + error(csa, "missing bound type in field 1\n"); + else + error(csa, "invalid bound type in field 1\n"); + strcpy(type, csa->field); + /* field 2: BOUNDS vector name */ + read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + { /* the same BOUNDS vector as in previous data record */ + if (v == 0) + { warning(csa, "missing BOUNDS vector name in field 2\n"); + goto blnk; + } + } + else if (v != 0 && strcmp(csa->field, name) == 0) + { /* the same BOUNDS vector as in previous data record */ + xassert(v != 0); + } + else +blnk: { /* new BOUNDS vector */ + if (v != 0) + error(csa, "multiple BOUNDS vectors not supported\n"); + v++; + strcpy(name, csa->field); + } + /* field 3: column name */ + read_field(csa), patch_name(csa, csa->field); + if (csa->field[0] == '\0') + error(csa, "missing column name in field 3\n"); + j = glp_find_col(csa->P, csa->field); + if (j == 0) + error(csa, "column `%s' not found\n", csa->field); + if ((flag[j] & mask) == 0x01) + error(csa, "duplicate lower bound for column `%s'\n", + csa->field); + if ((flag[j] & mask) == 0x10) + error(csa, "duplicate upper bound for column `%s'\n", + csa->field); + xassert((flag[j] & mask) == 0x00); + /* field 4: bound value */ + if (data) + { bnd = read_number(csa); + if (fabs(bnd) < csa->parm->tol_mps) bnd = 0.0; + } + else + read_field(csa), bnd = 0.0; + /* get current column bounds */ + col = csa->P->col[j]; + if (col->type == GLP_FR) + lb = -DBL_MAX, ub = +DBL_MAX; + else if (col->type == GLP_LO) + lb = col->lb, ub = +DBL_MAX; + else if (col->type == GLP_UP) + lb = -DBL_MAX, ub = col->ub; + else if (col->type == GLP_DB) + lb = col->lb, ub = col->ub; + else if (col->type == GLP_FX) + lb = ub = col->lb; + else + xassert(col != col); + /* change column bounds */ + if (strcmp(type, "LO") == 0) + lb = bnd; + else if (strcmp(type, "UP") == 0) + ub = bnd; + else if (strcmp(type, "FX") == 0) + lb = ub = bnd; + else if (strcmp(type, "FR") == 0) + lb = -DBL_MAX, ub = +DBL_MAX; + else if (strcmp(type, "MI") == 0) + lb = -DBL_MAX; + else if (strcmp(type, "PL") == 0) + ub = +DBL_MAX; + else if (strcmp(type, "LI") == 0) + { glp_set_col_kind(csa->P, j, GLP_IV); + lb = ceil(bnd); + } + else if (strcmp(type, "UI") == 0) + { glp_set_col_kind(csa->P, j, GLP_IV); + ub = floor(bnd); + } + else if (strcmp(type, "BV") == 0) + { glp_set_col_kind(csa->P, j, GLP_IV); + lb = 0.0, ub = 1.0; + } + else + xassert(type != type); + /* set new column bounds */ + if (lb == -DBL_MAX && ub == +DBL_MAX) + glp_set_col_bnds(csa->P, j, GLP_FR, lb, ub); + else if (ub == +DBL_MAX) + glp_set_col_bnds(csa->P, j, GLP_LO, lb, ub); + else if (lb == -DBL_MAX) + glp_set_col_bnds(csa->P, j, GLP_UP, lb, ub); + else if (lb != ub) + glp_set_col_bnds(csa->P, j, GLP_DB, lb, ub); + else + glp_set_col_bnds(csa->P, j, GLP_FX, lb, ub); + flag[j] |= (char)mask; + /* fields 5 and 6 must be blank */ + skip_field(csa); + skip_field(csa); + goto loop; +done: /* free working array */ + xfree(flag); + csa->work3 = NULL; + return; +} + +int glp_read_mps(glp_prob *P, int fmt, const glp_mpscp *parm, + const char *fname) +{ /* read problem data in MPS format */ + glp_mpscp _parm; + struct csa _csa, *csa = &_csa; + int ret; + xprintf("Reading problem data from `%s'...\n", fname); + if (!(fmt == GLP_MPS_DECK || fmt == GLP_MPS_FILE)) + xerror("glp_read_mps: fmt = %d; invalid parameter\n", fmt); + if (parm == NULL) + glp_init_mpscp(&_parm), parm = &_parm; + /* check control parameters */ + check_parm("glp_read_mps", parm); + /* initialize common storage area */ + csa->P = P; + csa->deck = (fmt == GLP_MPS_DECK); + csa->parm = parm; + csa->fname = fname; + csa->fp = NULL; + if (setjmp(csa->jump)) + { ret = 1; + goto done; + } + csa->recno = csa->recpos = 0; + csa->c = '\n'; + csa->fldno = 0; + csa->field[0] = '\0'; + csa->w80 = csa->wef = 0; + csa->obj_row = 0; + csa->work1 = csa->work2 = csa->work3 = NULL; + /* erase problem object */ + glp_erase_prob(P); + glp_create_index(P); + /* open input MPS file */ + csa->fp = xfopen(fname, "r"); + if (csa->fp == NULL) + { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* read NAME indicator record */ + read_name(csa); + if (P->name != NULL) + xprintf("Problem: %s\n", P->name); + /* read ROWS section */ + if (!(indicator(csa, 0) && strcmp(csa->field, "ROWS") == 0)) + error(csa, "missing ROWS indicator record\n"); + read_rows(csa); + /* determine objective row */ + if (parm->obj_name == NULL || parm->obj_name[0] == '\0') + { /* use the first row of N type */ + int i; + for (i = 1; i <= P->m; i++) + { if (P->row[i]->type == GLP_FR) + { csa->obj_row = i; + break; + } + } + if (csa->obj_row == 0) + warning(csa, "unable to determine objective row\n"); + } + else + { /* use a row with specified name */ + int i; + for (i = 1; i <= P->m; i++) + { xassert(P->row[i]->name != NULL); + if (strcmp(parm->obj_name, P->row[i]->name) == 0) + { csa->obj_row = i; + break; + } + } + if (csa->obj_row == 0) + error(csa, "objective row `%s' not found\n", + parm->obj_name); + } + if (csa->obj_row != 0) + { glp_set_obj_name(P, P->row[csa->obj_row]->name); + xprintf("Objective: %s\n", P->obj); + } + /* read COLUMNS section */ + if (strcmp(csa->field, "COLUMNS") != 0) + error(csa, "missing COLUMNS indicator record\n"); + read_columns(csa); + /* set objective coefficients */ + if (csa->obj_row != 0) + { GLPAIJ *aij; + for (aij = P->row[csa->obj_row]->ptr; aij != NULL; aij = + aij->r_next) glp_set_obj_coef(P, aij->col->j, aij->val); + } + /* read optional RHS section */ + if (strcmp(csa->field, "RHS") == 0) + read_rhs(csa); + /* read optional RANGES section */ + if (strcmp(csa->field, "RANGES") == 0) + read_ranges(csa); + /* read optional BOUNDS section */ + if (strcmp(csa->field, "BOUNDS") == 0) + read_bounds(csa); + /* read ENDATA indicator record */ + if (strcmp(csa->field, "ENDATA") != 0) + error(csa, "invalid use of %s indicator record\n", + csa->field); + /* print some statistics */ + xprintf("%d row%s, %d column%s, %d non-zero%s\n", + P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", + P->nnz, P->nnz == 1 ? "" : "s"); + if (glp_get_num_int(P) > 0) + { int ni = glp_get_num_int(P); + int nb = glp_get_num_bin(P); + if (ni == 1) + { if (nb == 0) + xprintf("One variable is integer\n"); + else + xprintf("One variable is binary\n"); + } + else + { xprintf("%d integer variables, ", ni); + if (nb == 0) + xprintf("none"); + else if (nb == 1) + xprintf("one"); + else if (nb == ni) + xprintf("all"); + else + xprintf("%d", nb); + xprintf(" of which %s binary\n", nb == 1 ? "is" : "are"); + } + } + xprintf("%d records were read\n", csa->recno); + /* problem data has been successfully read */ + glp_delete_index(P); + glp_sort_matrix(P); + ret = 0; +done: if (csa->fp != NULL) xfclose(csa->fp); + if (csa->work1 != NULL) xfree(csa->work1); + if (csa->work2 != NULL) xfree(csa->work2); + if (csa->work3 != NULL) xfree(csa->work3); + if (ret != 0) glp_erase_prob(P); + return ret; +} + +/*********************************************************************** +* NAME +* +* glp_write_mps - write problem data in MPS format +* +* SYNOPSIS +* +* int glp_write_mps(glp_prob *P, int fmt, const glp_mpscp *parm, +* const char *fname); +* +* DESCRIPTION +* +* The routine glp_write_mps writes problem data in MPS format to a +* text file. +* +* The parameter fmt specifies the version of MPS format: +* +* GLP_MPS_DECK - fixed (ancient) MPS format; +* GLP_MPS_FILE - free (modern) MPS format. +* +* The parameter parm is a pointer to the structure glp_mpscp, which +* specifies control parameters used by the routine. If parm is NULL, +* the routine uses default settings. +* +* The character string fname specifies a name of the text file to be +* written. +* +* RETURNS +* +* If the operation was successful, the routine glp_read_mps returns +* zero. Otherwise, it prints an error message and returns non-zero. */ + +#define csa csa1 + +struct csa +{ /* common storage area */ + glp_prob *P; + /* pointer to problem object */ + int deck; + /* MPS format (0 - free, 1 - fixed) */ + const glp_mpscp *parm; + /* pointer to control parameters */ + char field[255+1]; + /* field buffer */ +}; + +static char *mps_name(struct csa *csa) +{ /* make problem name */ + char *f; + if (csa->P->name == NULL) + csa->field[0] = '\0'; + else if (csa->deck) + { strncpy(csa->field, csa->P->name, 8); + csa->field[8] = '\0'; + } + else + strcpy(csa->field, csa->P->name); + for (f = csa->field; *f != '\0'; f++) + if (*f == ' ') *f = '_'; + return csa->field; +} + +static char *row_name(struct csa *csa, int i) +{ /* make i-th row name */ + char *f; + xassert(0 <= i && i <= csa->P->m); + if (i == 0 || csa->P->row[i]->name == NULL || + csa->deck && strlen(csa->P->row[i]->name) > 8) + sprintf(csa->field, "R%07d", i); + else + { strcpy(csa->field, csa->P->row[i]->name); + for (f = csa->field; *f != '\0'; f++) + if (*f == ' ') *f = '_'; + } + return csa->field; +} + +static char *col_name(struct csa *csa, int j) +{ /* make j-th column name */ + char *f; + xassert(1 <= j && j <= csa->P->n); + if (csa->P->col[j]->name == NULL || + csa->deck && strlen(csa->P->col[j]->name) > 8) + sprintf(csa->field, "C%07d", j); + else + { strcpy(csa->field, csa->P->col[j]->name); + for (f = csa->field; *f != '\0'; f++) + if (*f == ' ') *f = '_'; + } + return csa->field; +} + +static char *mps_numb(struct csa *csa, double val) +{ /* format floating-point number */ + int dig; + char *exp; + for (dig = 12; dig >= 6; dig--) + { if (val != 0.0 && fabs(val) < 0.002) + sprintf(csa->field, "%.*E", dig-1, val); + else + sprintf(csa->field, "%.*G", dig, val); + exp = strchr(csa->field, 'E'); + if (exp != NULL) + sprintf(exp+1, "%d", atoi(exp+1)); + if (strlen(csa->field) <= 12) break; + } + xassert(strlen(csa->field) <= 12); + return csa->field; +} + +int glp_write_mps(glp_prob *P, int fmt, const glp_mpscp *parm, + const char *fname) +{ /* write problem data in MPS format */ + glp_mpscp _parm; + struct csa _csa, *csa = &_csa; + XFILE *fp; + int out_obj, one_col = 0, empty = 0; + int i, j, recno, marker, count, gap, ret; + xprintf("Writing problem data to `%s'...\n", fname); + if (!(fmt == GLP_MPS_DECK || fmt == GLP_MPS_FILE)) + xerror("glp_write_mps: fmt = %d; invalid parameter\n", fmt); + if (parm == NULL) + glp_init_mpscp(&_parm), parm = &_parm; + /* check control parameters */ + check_parm("glp_write_mps", parm); + /* initialize common storage area */ + csa->P = P; + csa->deck = (fmt == GLP_MPS_DECK); + csa->parm = parm; + /* create output MPS file */ + fp = xfopen(fname, "w"), recno = 0; + if (fp == NULL) + { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* write comment records */ + xfprintf(fp, "* %-*s%s\n", P->name == NULL ? 1 : 12, "Problem:", + P->name == NULL ? "" : P->name), recno++; + xfprintf(fp, "* %-12s%s\n", "Class:", glp_get_num_int(P) == 0 ? + "LP" : "MIP"), recno++; + xfprintf(fp, "* %-12s%d\n", "Rows:", P->m), recno++; + if (glp_get_num_int(P) == 0) + xfprintf(fp, "* %-12s%d\n", "Columns:", P->n), recno++; + else + xfprintf(fp, "* %-12s%d (%d integer, %d binary)\n", + "Columns:", P->n, glp_get_num_int(P), glp_get_num_bin(P)), + recno++; + xfprintf(fp, "* %-12s%d\n", "Non-zeros:", P->nnz), recno++; + xfprintf(fp, "* %-12s%s\n", "Format:", csa->deck ? "Fixed MPS" : + "Free MPS"), recno++; + xfprintf(fp, "*\n", recno++); + /* write NAME indicator record */ + xfprintf(fp, "NAME%*s%s\n", + P->name == NULL ? 0 : csa->deck ? 10 : 1, "", mps_name(csa)), + recno++; +#if 1 + /* determine whether to write the objective row */ + out_obj = 1; + for (i = 1; i <= P->m; i++) + { if (P->row[i]->type == GLP_FR) + { out_obj = 0; + break; + } + } +#endif + /* write ROWS section */ + xfprintf(fp, "ROWS\n"), recno++; + for (i = (out_obj ? 0 : 1); i <= P->m; i++) + { int type; + type = (i == 0 ? GLP_FR : P->row[i]->type); + if (type == GLP_FR) + type = 'N'; + else if (type == GLP_LO) + type = 'G'; + else if (type == GLP_UP) + type = 'L'; + else if (type == GLP_DB || type == GLP_FX) + type = 'E'; + else + xassert(type != type); + xfprintf(fp, " %c%*s%s\n", type, csa->deck ? 2 : 1, "", + row_name(csa, i)), recno++; + } + /* write COLUMNS section */ + xfprintf(fp, "COLUMNS\n"), recno++; + marker = 0; + for (j = 1; j <= P->n; j++) + { GLPAIJ cj, *aij; + int kind; + kind = P->col[j]->kind; + if (kind == GLP_CV) + { if (marker % 2 == 1) + { /* close current integer block */ + marker++; + xfprintf(fp, "%*sM%07d%*s'MARKER'%*s'INTEND'\n", + csa->deck ? 4 : 1, "", marker, csa->deck ? 2 : 1, "", + csa->deck ? 17 : 1, ""), recno++; + } + } + else if (kind == GLP_IV) + { if (marker % 2 == 0) + { /* open new integer block */ + marker++; + xfprintf(fp, "%*sM%07d%*s'MARKER'%*s'INTORG'\n", + csa->deck ? 4 : 1, "", marker, csa->deck ? 2 : 1, "", + csa->deck ? 17 : 1, ""), recno++; + } + } + else + xassert(kind != kind); + if (out_obj && P->col[j]->coef != 0.0) + { /* make fake objective coefficient */ + aij = &cj; + aij->row = NULL; + aij->val = P->col[j]->coef; + aij->c_next = P->col[j]->ptr; + } + else + aij = P->col[j]->ptr; +#if 1 /* FIXME */ + if (aij == NULL) + { /* empty column */ + empty++; + xfprintf(fp, "%*s%-*s", csa->deck ? 4 : 1, "", + csa->deck ? 8 : 1, col_name(csa, j)); + /* we need a row */ + xassert(P->m > 0); + xfprintf(fp, "%*s%-*s", + csa->deck ? 2 : 1, "", csa->deck ? 8 : 1, + row_name(csa, 1)); + xfprintf(fp, "%*s0%*s$ empty column\n", + csa->deck ? 13 : 1, "", csa->deck ? 3 : 1, ""), recno++; + } +#endif + count = 0; + for (aij = aij; aij != NULL; aij = aij->c_next) + { if (one_col || count % 2 == 0) + xfprintf(fp, "%*s%-*s", csa->deck ? 4 : 1, "", + csa->deck ? 8 : 1, col_name(csa, j)); + gap = (one_col || count % 2 == 0 ? 2 : 3); + xfprintf(fp, "%*s%-*s", + csa->deck ? gap : 1, "", csa->deck ? 8 : 1, + row_name(csa, aij->row == NULL ? 0 : aij->row->i)); + xfprintf(fp, "%*s%*s", + csa->deck ? 2 : 1, "", csa->deck ? 12 : 1, + mps_numb(csa, aij->val)), count++; + if (one_col || count % 2 == 0) + xfprintf(fp, "\n"), recno++; + } + if (!(one_col || count % 2 == 0)) + xfprintf(fp, "\n"), recno++; + } + if (marker % 2 == 1) + { /* close last integer block */ + marker++; + xfprintf(fp, "%*sM%07d%*s'MARKER'%*s'INTEND'\n", + csa->deck ? 4 : 1, "", marker, csa->deck ? 2 : 1, "", + csa->deck ? 17 : 1, ""), recno++; + } +#if 1 + if (empty > 0) + xprintf("Warning: problem has %d empty column(s)\n", empty); +#endif + /* write RHS section */ + xfprintf(fp, "RHS\n"), recno++; + count = 0; + for (i = (out_obj ? 0 : 1); i <= P->m; i++) + { int type; + double rhs; + if (i == 0) + rhs = P->c0; + else + { type = P->row[i]->type; + if (type == GLP_FR) + rhs = 0.0; + else if (type == GLP_LO) + rhs = P->row[i]->lb; + else if (type == GLP_UP) + rhs = P->row[i]->ub; + else if (type == GLP_DB || type == GLP_FX) + rhs = P->row[i]->lb; + else + xassert(type != type); + } + if (rhs != 0.0) + { if (one_col || count % 2 == 0) + xfprintf(fp, "%*s%-*s", csa->deck ? 4 : 1, "", + csa->deck ? 8 : 1, "RHS1"); + gap = (one_col || count % 2 == 0 ? 2 : 3); + xfprintf(fp, "%*s%-*s", + csa->deck ? gap : 1, "", csa->deck ? 8 : 1, + row_name(csa, i)); + xfprintf(fp, "%*s%*s", + csa->deck ? 2 : 1, "", csa->deck ? 12 : 1, + mps_numb(csa, rhs)), count++; + if (one_col || count % 2 == 0) + xfprintf(fp, "\n"), recno++; + } + } + if (!(one_col || count % 2 == 0)) + xfprintf(fp, "\n"), recno++; + /* write RANGES section */ + for (i = P->m; i >= 1; i--) + if (P->row[i]->type == GLP_DB) break; + if (i == 0) goto bnds; + xfprintf(fp, "RANGES\n"), recno++; + count = 0; + for (i = 1; i <= P->m; i++) + { if (P->row[i]->type == GLP_DB) + { if (one_col || count % 2 == 0) + xfprintf(fp, "%*s%-*s", csa->deck ? 4 : 1, "", + csa->deck ? 8 : 1, "RNG1"); + gap = (one_col || count % 2 == 0 ? 2 : 3); + xfprintf(fp, "%*s%-*s", + csa->deck ? gap : 1, "", csa->deck ? 8 : 1, + row_name(csa, i)); + xfprintf(fp, "%*s%*s", + csa->deck ? 2 : 1, "", csa->deck ? 12 : 1, + mps_numb(csa, P->row[i]->ub - P->row[i]->lb)), count++; + if (one_col || count % 2 == 0) + xfprintf(fp, "\n"), recno++; + } + } + if (!(one_col || count % 2 == 0)) + xfprintf(fp, "\n"), recno++; +bnds: /* write BOUNDS section */ + for (j = P->n; j >= 1; j--) + if (!(P->col[j]->type == GLP_LO && P->col[j]->lb == 0.0)) + break; + if (j == 0) goto endt; + xfprintf(fp, "BOUNDS\n"), recno++; + for (j = 1; j <= P->n; j++) + { int type, data[2]; + double bnd[2]; + char *spec[2]; + spec[0] = spec[1] = NULL; + type = P->col[j]->type; + if (type == GLP_FR) + spec[0] = "FR", data[0] = 0; + else if (type == GLP_LO) + { if (P->col[j]->lb != 0.0) + spec[0] = "LO", data[0] = 1, bnd[0] = P->col[j]->lb; + if (P->col[j]->kind == GLP_IV) + spec[1] = "PL", data[1] = 0; + } + else if (type == GLP_UP) + { spec[0] = "MI", data[0] = 0; + spec[1] = "UP", data[1] = 1, bnd[1] = P->col[j]->ub; + } + else if (type == GLP_DB) + { if (P->col[j]->lb != 0.0) + spec[0] = "LO", data[0] = 1, bnd[0] = P->col[j]->lb; + spec[1] = "UP", data[1] = 1, bnd[1] = P->col[j]->ub; + } + else if (type == GLP_FX) + spec[0] = "FX", data[0] = 1, bnd[0] = P->col[j]->lb; + else + xassert(type != type); + for (i = 0; i <= 1; i++) + { if (spec[i] != NULL) + { xfprintf(fp, " %s %-*s%*s%-*s", spec[i], + csa->deck ? 8 : 1, "BND1", csa->deck ? 2 : 1, "", + csa->deck ? 8 : 1, col_name(csa, j)); + if (data[i]) + xfprintf(fp, "%*s%*s", csa->deck ? 2 : 1, "", + csa->deck ? 12 : 1, mps_numb(csa, bnd[i])); + xfprintf(fp, "\n"), recno++; + } + } + } +endt: /* write ENDATA indicator record */ + xfprintf(fp, "ENDATA\n"), recno++; + xfflush(fp); + if (xferror(fp)) + { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); + ret = 1; + goto done; + } + /* problem data has been successfully written */ + xprintf("%d records were written\n", recno); + ret = 0; +done: if (fp != NULL) xfclose(fp); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,60 @@ +/* glpnet.h (graph and network algorithms) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPNET_H +#define GLPNET_H + +#define mc21a _glp_mc21a +int mc21a(int n, const int icn[], const int ip[], const int lenr[], + int iperm[], int pr[], int arp[], int cv[], int out[]); +/* permutations for zero-free diagonal */ + +#define mc13d _glp_mc13d +int mc13d(int n, const int icn[], const int ip[], const int lenr[], + int ior[], int ib[], int lowl[], int numb[], int prev[]); +/* permutations to block triangular form */ + +#define okalg _glp_okalg +int okalg(int nv, int na, const int tail[], const int head[], + const int low[], const int cap[], const int cost[], int x[], + int pi[]); +/* out-of-kilter algorithm */ + +#define ffalg _glp_ffalg +void ffalg(int nv, int na, const int tail[], const int head[], + int s, int t, const int cap[], int x[], char cut[]); +/* Ford-Fulkerson algorithm */ + +#define wclique _glp_wclique +int wclique(int n, const int w[], const unsigned char a[], int ind[]); +/* find maximum weight clique with Ostergard's algorithm */ + +#define kellerman _glp_kellerman +int kellerman(int n, int (*func)(void *info, int i, int ind[]), + void *info, void /* glp_graph */ *H); +/* cover edges by cliques with Kellerman's heuristic */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,301 @@ +/* glpnet01.c (permutations for zero-free diagonal) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* This code is the result of translation of the Fortran subroutines +* MC21A and MC21B associated with the following paper: +* +* I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM +* Trans. on Math. Softw. 7 (1981), 387-390. +* +* Use of ACM Algorithms is subject to the ACM Software Copyright and +* License Agreement. See . +* +* The translation was made by Andrew Makhorin . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpnet.h" + +/*********************************************************************** +* NAME +* +* mc21a - permutations for zero-free diagonal +* +* SYNOPSIS +* +* #include "glpnet.h" +* int mc21a(int n, const int icn[], const int ip[], const int lenr[], +* int iperm[], int pr[], int arp[], int cv[], int out[]); +* +* DESCRIPTION +* +* Given the pattern of nonzeros of a sparse matrix, the routine mc21a +* attempts to find a permutation of its rows that makes the matrix have +* no zeros on its diagonal. +* +* INPUT PARAMETERS +* +* n order of matrix. +* +* icn array containing the column indices of the non-zeros. Those +* belonging to a single row must be contiguous but the ordering +* of column indices within each row is unimportant and wasted +* space between rows is permitted. +* +* ip ip[i], i = 1,2,...,n, is the position in array icn of the +* first column index of a non-zero in row i. +* +* lenr lenr[i], i = 1,2,...,n, is the number of non-zeros in row i. +* +* OUTPUT PARAMETER +* +* iperm contains permutation to make diagonal have the smallest +* number of zeros on it. Elements (iperm[i], i), i = 1,2,...,n, +* are non-zero at the end of the algorithm unless the matrix is +* structurally singular. In this case, (iperm[i], i) will be +* zero for n - numnz entries. +* +* WORKING ARRAYS +* +* pr working array of length [1+n], where pr[0] is not used. +* pr[i] is the previous row to i in the depth first search. +* +* arp working array of length [1+n], where arp[0] is not used. +* arp[i] is one less than the number of non-zeros in row i which +* have not been scanned when looking for a cheap assignment. +* +* cv working array of length [1+n], where cv[0] is not used. +* cv[i] is the most recent row extension at which column i was +* visited. +* +* out working array of length [1+n], where out[0] is not used. +* out[i] is one less than the number of non-zeros in row i +* which have not been scanned during one pass through the main +* loop. +* +* RETURNS +* +* The routine mc21a returns numnz, the number of non-zeros on diagonal +* of permuted matrix. */ + +int mc21a(int n, const int icn[], const int ip[], const int lenr[], + int iperm[], int pr[], int arp[], int cv[], int out[]) +{ int i, ii, in1, in2, j, j1, jord, k, kk, numnz; + /* Initialization of arrays. */ + for (i = 1; i <= n; i++) + { arp[i] = lenr[i] - 1; + cv[i] = iperm[i] = 0; + } + numnz = 0; + /* Main loop. */ + /* Each pass round this loop either results in a new assignment + or gives a row with no assignment. */ + for (jord = 1; jord <= n; jord++) + { j = jord; + pr[j] = -1; + for (k = 1; k <= jord; k++) + { /* Look for a cheap assignment. */ + in1 = arp[j]; + if (in1 >= 0) + { in2 = ip[j] + lenr[j] - 1; + in1 = in2 - in1; + for (ii = in1; ii <= in2; ii++) + { i = icn[ii]; + if (iperm[i] == 0) goto L110; + } + /* No cheap assignment in row. */ + arp[j] = -1; + } + /* Begin looking for assignment chain starting with row j.*/ + out[j] = lenr[j] - 1; + /* Inner loop. Extends chain by one or backtracks. */ + for (kk = 1; kk <= jord; kk++) + { in1 = out[j]; + if (in1 >= 0) + { in2 = ip[j] + lenr[j] - 1; + in1 = in2 - in1; + /* Forward scan. */ + for (ii = in1; ii <= in2; ii++) + { i = icn[ii]; + if (cv[i] != jord) + { /* Column i has not yet been accessed during + this pass. */ + j1 = j; + j = iperm[i]; + cv[i] = jord; + pr[j] = j1; + out[j1] = in2 - ii - 1; + goto L100; + } + } + } + /* Backtracking step. */ + j = pr[j]; + if (j == -1) goto L130; + } +L100: ; + } +L110: /* New assignment is made. */ + iperm[i] = j; + arp[j] = in2 - ii - 1; + numnz++; + for (k = 1; k <= jord; k++) + { j = pr[j]; + if (j == -1) break; + ii = ip[j] + lenr[j] - out[j] - 2; + i = icn[ii]; + iperm[i] = j; + } +L130: ; + } + /* If matrix is structurally singular, we now complete the + permutation iperm. */ + if (numnz < n) + { for (i = 1; i <= n; i++) + arp[i] = 0; + k = 0; + for (i = 1; i <= n; i++) + { if (iperm[i] == 0) + out[++k] = i; + else + arp[iperm[i]] = i; + } + k = 0; + for (i = 1; i <= n; i++) + { if (arp[i] == 0) + iperm[out[++k]] = i; + } + } + return numnz; +} + +/**********************************************************************/ + +#if 0 +#include "glplib.h" + +int sing; + +void ranmat(int m, int n, int icn[], int iptr[], int nnnp1, int *knum, + int iw[]); + +void fa01bs(int max, int *nrand); + +int main(void) +{ /* test program for the routine mc21a */ + /* these runs on random matrices cause all possible statements in + mc21a to be executed */ + int i, iold, j, j1, j2, jj, knum, l, licn, n, nov4, num, numnz; + int ip[1+21], icn[1+1000], iperm[1+20], lenr[1+20], iw1[1+80]; + licn = 1000; + /* run on random matrices of orders 1 through 20 */ + for (n = 1; n <= 20; n++) + { nov4 = n / 4; + if (nov4 < 1) nov4 = 1; +L10: fa01bs(nov4, &l); + knum = l * n; + /* knum is requested number of non-zeros in random matrix */ + if (knum > licn) goto L10; + /* if sing is false, matrix is guaranteed structurally + non-singular */ + sing = ((n / 2) * 2 == n); + /* call to subroutine to generate random matrix */ + ranmat(n, n, icn, ip, n+1, &knum, iw1); + /* knum is now actual number of non-zeros in random matrix */ + if (knum > licn) goto L10; + xprintf("n = %2d; nz = %4d; sing = %d\n", n, knum, sing); + /* set up array of row lengths */ + for (i = 1; i <= n; i++) + lenr[i] = ip[i+1] - ip[i]; + /* call to mc21a */ + numnz = mc21a(n, icn, ip, lenr, iperm, &iw1[0], &iw1[n], + &iw1[n+n], &iw1[n+n+n]); + /* testing to see if there are numnz non-zeros on the diagonal + of the permuted matrix. */ + num = 0; + for (i = 1; i <= n; i++) + { iold = iperm[i]; + j1 = ip[iold]; + j2 = j1 + lenr[iold] - 1; + if (j2 < j1) continue; + for (jj = j1; jj <= j2; jj++) + { j = icn[jj]; + if (j == i) + { num++; + break; + } + } + } + if (num != numnz) + xprintf("Failure in mc21a, numnz = %d instead of %d\n", + numnz, num); + } + return 0; +} + +void ranmat(int m, int n, int icn[], int iptr[], int nnnp1, int *knum, + int iw[]) +{ /* subroutine to generate random matrix */ + int i, ii, inum, j, lrow, matnum; + inum = (*knum / n) * 2; + if (inum > n-1) inum = n-1; + matnum = 1; + /* each pass through this loop generates a row of the matrix */ + for (j = 1; j <= m; j++) + { iptr[j] = matnum; + if (!(sing || j > n)) + icn[matnum++] = j; + if (n == 1) continue; + for (i = 1; i <= n; i++) iw[i] = 0; + if (!sing) iw[j] = 1; + fa01bs(inum, &lrow); + lrow--; + if (lrow == 0) continue; + /* lrow off-diagonal non-zeros in row j of the matrix */ + for (ii = 1; ii <= lrow; ii++) + { for (;;) + { fa01bs(n, &i); + if (iw[i] != 1) break; + } + iw[i] = 1; + icn[matnum++] = i; + } + } + for (i = m+1; i <= nnnp1; i++) + iptr[i] = matnum; + *knum = matnum - 1; + return; +} + +double g = 1431655765.0; + +double fa01as(int i) +{ /* random number generator */ + g = fmod(g * 9228907.0, 4294967296.0); + if (i >= 0) + return g / 4294967296.0; + else + return 2.0 * g / 4294967296.0 - 1.0; +} + +void fa01bs(int max, int *nrand) +{ *nrand = (int)(fa01as(1) * (double)max) + 1; + return; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,314 @@ +/* glpnet02.c (permutations to block triangular form) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* This code is the result of translation of the Fortran subroutines +* MC13D and MC13E associated with the following paper: +* +* I.S.Duff, J.K.Reid, Algorithm 529: Permutations to block triangular +* form, ACM Trans. on Math. Softw. 4 (1978), 189-192. +* +* Use of ACM Algorithms is subject to the ACM Software Copyright and +* License Agreement. See . +* +* The translation was made by Andrew Makhorin . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpnet.h" + +/*********************************************************************** +* NAME +* +* mc13d - permutations to block triangular form +* +* SYNOPSIS +* +* #include "glpnet.h" +* int mc13d(int n, const int icn[], const int ip[], const int lenr[], +* int ior[], int ib[], int lowl[], int numb[], int prev[]); +* +* DESCRIPTION +* +* Given the column numbers of the nonzeros in each row of the sparse +* matrix, the routine mc13d finds a symmetric permutation that makes +* the matrix block lower triangular. +* +* INPUT PARAMETERS +* +* n order of the matrix. +* +* icn array containing the column indices of the non-zeros. Those +* belonging to a single row must be contiguous but the ordering +* of column indices within each row is unimportant and wasted +* space between rows is permitted. +* +* ip ip[i], i = 1,2,...,n, is the position in array icn of the +* first column index of a non-zero in row i. +* +* lenr lenr[i], i = 1,2,...,n, is the number of non-zeros in row i. +* +* OUTPUT PARAMETERS +* +* ior ior[i], i = 1,2,...,n, gives the position on the original +* ordering of the row or column which is in position i in the +* permuted form. +* +* ib ib[i], i = 1,2,...,num, is the row number in the permuted +* matrix of the beginning of block i, 1 <= num <= n. +* +* WORKING ARRAYS +* +* arp working array of length [1+n], where arp[0] is not used. +* arp[i] is one less than the number of unsearched edges leaving +* node i. At the end of the algorithm it is set to a permutation +* which puts the matrix in block lower triangular form. +* +* ib working array of length [1+n], where ib[0] is not used. +* ib[i] is the position in the ordering of the start of the ith +* block. ib[n+1-i] holds the node number of the ith node on the +* stack. +* +* lowl working array of length [1+n], where lowl[0] is not used. +* lowl[i] is the smallest stack position of any node to which a +* path from node i has been found. It is set to n+1 when node i +* is removed from the stack. +* +* numb working array of length [1+n], where numb[0] is not used. +* numb[i] is the position of node i in the stack if it is on it, +* is the permuted order of node i for those nodes whose final +* position has been found and is otherwise zero. +* +* prev working array of length [1+n], where prev[0] is not used. +* prev[i] is the node at the end of the path when node i was +* placed on the stack. +* +* RETURNS +* +* The routine mc13d returns num, the number of blocks found. */ + +int mc13d(int n, const int icn[], const int ip[], const int lenr[], + int ior[], int ib[], int lowl[], int numb[], int prev[]) +{ int *arp = ior; + int dummy, i, i1, i2, icnt, ii, isn, ist, ist1, iv, iw, j, lcnt, + nnm1, num, stp; + /* icnt is the number of nodes whose positions in final ordering + have been found. */ + icnt = 0; + /* num is the number of blocks that have been found. */ + num = 0; + nnm1 = n + n - 1; + /* Initialization of arrays. */ + for (j = 1; j <= n; j++) + { numb[j] = 0; + arp[j] = lenr[j] - 1; + } + for (isn = 1; isn <= n; isn++) + { /* Look for a starting node. */ + if (numb[isn] != 0) continue; + iv = isn; + /* ist is the number of nodes on the stack ... it is the stack + pointer. */ + ist = 1; + /* Put node iv at beginning of stack. */ + lowl[iv] = numb[iv] = 1; + ib[n] = iv; + /* The body of this loop puts a new node on the stack or + backtracks. */ + for (dummy = 1; dummy <= nnm1; dummy++) + { i1 = arp[iv]; + /* Have all edges leaving node iv been searched? */ + if (i1 >= 0) + { i2 = ip[iv] + lenr[iv] - 1; + i1 = i2 - i1; + /* Look at edges leaving node iv until one enters a new + node or all edges are exhausted. */ + for (ii = i1; ii <= i2; ii++) + { iw = icn[ii]; + /* Has node iw been on stack already? */ + if (numb[iw] == 0) goto L70; + /* Update value of lowl[iv] if necessary. */ + if (lowl[iw] < lowl[iv]) lowl[iv] = lowl[iw]; + } + /* There are no more edges leaving node iv. */ + arp[iv] = -1; + } + /* Is node iv the root of a block? */ + if (lowl[iv] < numb[iv]) goto L60; + /* Order nodes in a block. */ + num++; + ist1 = n + 1 - ist; + lcnt = icnt + 1; + /* Peel block off the top of the stack starting at the top + and working down to the root of the block. */ + for (stp = ist1; stp <= n; stp++) + { iw = ib[stp]; + lowl[iw] = n + 1; + numb[iw] = ++icnt; + if (iw == iv) break; + } + ist = n - stp; + ib[num] = lcnt; + /* Are there any nodes left on the stack? */ + if (ist != 0) goto L60; + /* Have all the nodes been ordered? */ + if (icnt < n) break; + goto L100; +L60: /* Backtrack to previous node on path. */ + iw = iv; + iv = prev[iv]; + /* Update value of lowl[iv] if necessary. */ + if (lowl[iw] < lowl[iv]) lowl[iv] = lowl[iw]; + continue; +L70: /* Put new node on the stack. */ + arp[iv] = i2 - ii - 1; + prev[iw] = iv; + iv = iw; + lowl[iv] = numb[iv] = ++ist; + ib[n+1-ist] = iv; + } + } +L100: /* Put permutation in the required form. */ + for (i = 1; i <= n; i++) + arp[numb[i]] = i; + return num; +} + +/**********************************************************************/ + +#if 0 +#include "glplib.h" + +void test(int n, int ipp); + +int main(void) +{ /* test program for routine mc13d */ + test( 1, 0); + test( 2, 1); + test( 2, 2); + test( 3, 3); + test( 4, 4); + test( 5, 10); + test(10, 10); + test(10, 20); + test(20, 20); + test(20, 50); + test(50, 50); + test(50, 200); + return 0; +} + +void fa01bs(int max, int *nrand); + +void setup(int n, char a[1+50][1+50], int ip[], int icn[], int lenr[]); + +void test(int n, int ipp) +{ int ip[1+50], icn[1+1000], ior[1+50], ib[1+51], iw[1+150], + lenr[1+50]; + char a[1+50][1+50], hold[1+100]; + int i, ii, iblock, ij, index, j, jblock, jj, k9, num; + xprintf("\n\n\nMatrix is of order %d and has %d off-diagonal non-" + "zeros\n", n, ipp); + for (j = 1; j <= n; j++) + { for (i = 1; i <= n; i++) + a[i][j] = 0; + a[j][j] = 1; + } + for (k9 = 1; k9 <= ipp; k9++) + { /* these statements should be replaced by calls to your + favorite random number generator to place two pseudo-random + numbers between 1 and n in the variables i and j */ + for (;;) + { fa01bs(n, &i); + fa01bs(n, &j); + if (!a[i][j]) break; + } + a[i][j] = 1; + } + /* setup converts matrix a[i,j] to required sparsity-oriented + storage format */ + setup(n, a, ip, icn, lenr); + num = mc13d(n, icn, ip, lenr, ior, ib, &iw[0], &iw[n], &iw[n+n]); + /* output reordered matrix with blocking to improve clarity */ + xprintf("\nThe reordered matrix which has %d block%s is of the fo" + "rm\n", num, num == 1 ? "" : "s"); + ib[num+1] = n + 1; + index = 100; + iblock = 1; + for (i = 1; i <= n; i++) + { for (ij = 1; ij <= index; ij++) + hold[ij] = ' '; + if (i == ib[iblock]) + { xprintf("\n"); + iblock++; + } + jblock = 1; + index = 0; + for (j = 1; j <= n; j++) + { if (j == ib[jblock]) + { hold[++index] = ' '; + jblock++; + } + ii = ior[i]; + jj = ior[j]; + hold[++index] = (char)(a[ii][jj] ? 'X' : '0'); + } + xprintf("%.*s\n", index, &hold[1]); + } + xprintf("\nThe starting point for each block is given by\n"); + for (i = 1; i <= num; i++) + { if ((i - 1) % 12 == 0) xprintf("\n"); + xprintf(" %4d", ib[i]); + } + xprintf("\n"); + return; +} + +void setup(int n, char a[1+50][1+50], int ip[], int icn[], int lenr[]) +{ int i, j, ind; + for (i = 1; i <= n; i++) + lenr[i] = 0; + ind = 1; + for (i = 1; i <= n; i++) + { ip[i] = ind; + for (j = 1; j <= n; j++) + { if (a[i][j]) + { lenr[i]++; + icn[ind++] = j; + } + } + } + return; +} + +double g = 1431655765.0; + +double fa01as(int i) +{ /* random number generator */ + g = fmod(g * 9228907.0, 4294967296.0); + if (i >= 0) + return g / 4294967296.0; + else + return 2.0 * g / 4294967296.0 - 1.0; +} + +void fa01bs(int max, int *nrand) +{ *nrand = (int)(fa01as(1) * (double)max) + 1; + return; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet03.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet03.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,776 @@ +/* glpnet03.c (Klingman's network problem generator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* This code is the result of translation of the Fortran program NETGEN +* developed by Dr. Darwin Klingman, which is publically available from +* NETLIB at . +* +* The translation was made by Andrew Makhorin . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_netgen - Klingman's network problem generator +* +* SYNOPSIS +* +* int glp_netgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, +* const int parm[1+15]); +* +* DESCRIPTION +* +* The routine glp_netgen is a network problem generator developed by +* Dr. Darwin Klingman. It can create capacitated and uncapacitated +* minimum cost flow (or transshipment), transportation, and assignment +* problems. +* +* The parameter G specifies the graph object, to which the generated +* problem data have to be stored. Note that on entry the graph object +* is erased with the routine glp_erase_graph. +* +* The parameter v_rhs specifies an offset of the field of type double +* in the vertex data block, to which the routine stores the supply or +* demand value. If v_rhs < 0, the value is not stored. +* +* The parameter a_cap specifies an offset of the field of type double +* in the arc data block, to which the routine stores the arc capacity. +* If a_cap < 0, the capacity is not stored. +* +* The parameter a_cost specifies an offset of the field of type double +* in the arc data block, to which the routine stores the per-unit cost +* if the arc flow. If a_cost < 0, the cost is not stored. +* +* The array parm contains description of the network to be generated: +* +* parm[0] not used +* parm[1] (iseed) 8-digit positive random number seed +* parm[2] (nprob) 8-digit problem id number +* parm[3] (nodes) total number of nodes +* parm[4] (nsorc) total number of source nodes (including +* transshipment nodes) +* parm[5] (nsink) total number of sink nodes (including +* transshipment nodes) +* parm[6] (iarcs) number of arcs +* parm[7] (mincst) minimum cost for arcs +* parm[8] (maxcst) maximum cost for arcs +* parm[9] (itsup) total supply +* parm[10] (ntsorc) number of transshipment source nodes +* parm[11] (ntsink) number of transshipment sink nodes +* parm[12] (iphic) percentage of skeleton arcs to be given +* the maximum cost +* parm[13] (ipcap) percentage of arcs to be capacitated +* parm[14] (mincap) minimum upper bound for capacitated arcs +* parm[15] (maxcap) maximum upper bound for capacitated arcs +* +* The routine generates a transportation problem if: +* +* nsorc + nsink = nodes, ntsorc = 0, and ntsink = 0. +* +* The routine generates an assignment problem if the requirements for +* a transportation problem are met and: +* +* nsorc = nsink and itsup = nsorc. +* +* RETURNS +* +* If the instance was successfully generated, the routine glp_netgen +* returns zero; otherwise, if specified parameters are inconsistent, +* the routine returns a non-zero error code. +* +* REFERENCES +* +* D.Klingman, A.Napier, and J.Stutz. NETGEN: A program for generating +* large scale capacitated assignment, transportation, and minimum cost +* flow networks. Management Science 20 (1974), 814-20. */ + +struct csa +{ /* common storage area */ + glp_graph *G; + int v_rhs, a_cap, a_cost; + int nodes, iarcs, mincst, maxcst, itsup, nsorc, nsink, nonsor, + nfsink, narcs, nsort, nftsor, ipcap, mincap, maxcap, ktl, + nodlft, *ipred, *ihead, *itail, *iflag, *isup, *lsinks, mult, + modul, i15, i16, jran; +}; + +#define G (csa->G) +#define v_rhs (csa->v_rhs) +#define a_cap (csa->a_cap) +#define a_cost (csa->a_cost) +#define nodes (csa->nodes) +#define iarcs (csa->iarcs) +#define mincst (csa->mincst) +#define maxcst (csa->maxcst) +#define itsup (csa->itsup) +#define nsorc (csa->nsorc) +#define nsink (csa->nsink) +#define nonsor (csa->nonsor) +#define nfsink (csa->nfsink) +#define narcs (csa->narcs) +#define nsort (csa->nsort) +#define nftsor (csa->nftsor) +#define ipcap (csa->ipcap) +#define mincap (csa->mincap) +#define maxcap (csa->maxcap) +#define ktl (csa->ktl) +#define nodlft (csa->nodlft) +#if 0 +/* spent a day to find out this bug */ +#define ist (csa->ist) +#else +#define ist (ipred[0]) +#endif +#define ipred (csa->ipred) +#define ihead (csa->ihead) +#define itail (csa->itail) +#define iflag (csa->iflag) +#define isup (csa->isup) +#define lsinks (csa->lsinks) +#define mult (csa->mult) +#define modul (csa->modul) +#define i15 (csa->i15) +#define i16 (csa->i16) +#define jran (csa->jran) + +static void cresup(struct csa *csa); +static void chain(struct csa *csa, int lpick, int lsorc); +static void chnarc(struct csa *csa, int lsorc); +static void sort(struct csa *csa); +static void pickj(struct csa *csa, int it); +static void assign(struct csa *csa); +static void setran(struct csa *csa, int iseed); +static int iran(struct csa *csa, int ilow, int ihigh); + +int glp_netgen(glp_graph *G_, int _v_rhs, int _a_cap, int _a_cost, + const int parm[1+15]) +{ struct csa _csa, *csa = &_csa; + int iseed, nprob, ntsorc, ntsink, iphic, i, nskel, nltr, ltsink, + ntrans, npsink, nftr, npsorc, ntravl, ntrrem, lsorc, lpick, + nsksr, nsrchn, j, item, l, ks, k, ksp, li, n, ii, it, ih, icap, + jcap, icost, jcost, ret; + G = G_; + v_rhs = _v_rhs; + a_cap = _a_cap; + a_cost = _a_cost; + if (G != NULL) + { if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) + xerror("glp_netgen: v_rhs = %d; invalid offset\n", v_rhs); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_netgen: a_cap = %d; invalid offset\n", a_cap); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_netgen: a_cost = %d; invalid offset\n", a_cost); + } + /* Input the user's random number seed and fix it if + non-positive. */ + iseed = parm[1]; + nprob = parm[2]; + if (iseed <= 0) iseed = 13502460; + setran(csa, iseed); + /* Input the user's problem characteristics. */ + nodes = parm[3]; + nsorc = parm[4]; + nsink = parm[5]; + iarcs = parm[6]; + mincst = parm[7]; + maxcst = parm[8]; + itsup = parm[9]; + ntsorc = parm[10]; + ntsink = parm[11]; + iphic = parm[12]; + ipcap = parm[13]; + mincap = parm[14]; + maxcap = parm[15]; + /* Check the size of the problem. */ + if (!(10 <= nodes && nodes <= 100000)) + { ret = 1; + goto done; + } + /* Check user supplied parameters for consistency. */ + if (!(nsorc >= 0 && nsink >= 0 && nsorc + nsink <= nodes)) + { ret = 2; + goto done; + } + if (iarcs < 0) + { ret = 3; + goto done; + } + if (mincst > maxcst) + { ret = 4; + goto done; + } + if (itsup < 0) + { ret = 5; + goto done; + } + if (!(0 <= ntsorc && ntsorc <= nsorc)) + { ret = 6; + goto done; + } + if (!(0 <= ntsink && ntsink <= nsink)) + { ret = 7; + goto done; + } + if (!(0 <= iphic && iphic <= 100)) + { ret = 8; + goto done; + } + if (!(0 <= ipcap && ipcap <= 100)) + { ret = 9; + goto done; + } + if (mincap > maxcap) + { ret = 10; + goto done; + } + /* Initailize the graph object. */ + if (G != NULL) + { glp_erase_graph(G, G->v_size, G->a_size); + glp_add_vertices(G, nodes); + if (v_rhs >= 0) + { double zero = 0.0; + for (i = 1; i <= nodes; i++) + { glp_vertex *v = G->v[i]; + memcpy((char *)v->data + v_rhs, &zero, sizeof(double)); + } + } + } + /* Allocate working arrays. */ + ipred = xcalloc(1+nodes, sizeof(int)); + ihead = xcalloc(1+nodes, sizeof(int)); + itail = xcalloc(1+nodes, sizeof(int)); + iflag = xcalloc(1+nodes, sizeof(int)); + isup = xcalloc(1+nodes, sizeof(int)); + lsinks = xcalloc(1+nodes, sizeof(int)); + /* Print the problem documentation records. */ + if (G == NULL) + { xprintf("BEGIN\n"); + xprintf("NETGEN PROBLEM%8d%10s%10d NODES AND%10d ARCS\n", + nprob, "", nodes, iarcs); + xprintf("USER:%11d%11d%11d%11d%11d%11d\nDATA:%11d%11d%11d%11d%" + "11d%11d\n", iseed, nsorc, nsink, mincst, + maxcst, itsup, ntsorc, ntsink, iphic, ipcap, + mincap, maxcap); + } + else + glp_set_graph_name(G, "NETGEN"); + /* Set various constants used in the program. */ + narcs = 0; + nskel = 0; + nltr = nodes - nsink; + ltsink = nltr + ntsink; + ntrans = nltr - nsorc; + nfsink = nltr + 1; + nonsor = nodes - nsorc + ntsorc; + npsink = nsink - ntsink; + nodlft = nodes - nsink + ntsink; + nftr = nsorc + 1; + nftsor = nsorc - ntsorc + 1; + npsorc = nsorc - ntsorc; + /* Randomly distribute the supply among the source nodes. */ + if (npsorc + npsink == nodes && npsorc == npsink && + itsup == nsorc) + { assign(csa); + nskel = nsorc; + goto L390; + } + cresup(csa); + /* Print the supply records. */ + if (G == NULL) + { xprintf("SUPPLY\n"); + for (i = 1; i <= nsorc; i++) + xprintf("%6s%6d%18s%10d\n", "", i, "", isup[i]); + xprintf("ARCS\n"); + } + else + { if (v_rhs >= 0) + { for (i = 1; i <= nsorc; i++) + { double temp = (double)isup[i]; + glp_vertex *v = G->v[i]; + memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); + } + } + } + /* Make the sources point to themselves in ipred array. */ + for (i = 1; i <= nsorc; i++) + ipred[i] = i; + if (ntrans == 0) goto L170; + /* Chain the transshipment nodes together in the ipred array. */ + ist = nftr; + ipred[nltr] = 0; + for (i = nftr; i < nltr; i++) + ipred[i] = i+1; + /* Form even length chains for 60 percent of the transshipments.*/ + ntravl = 6 * ntrans / 10; + ntrrem = ntrans - ntravl; +L140: lsorc = 1; + while (ntravl != 0) + { lpick = iran(csa, 1, ntravl + ntrrem); + ntravl--; + chain(csa, lpick, lsorc); + if (lsorc == nsorc) goto L140; + lsorc++; + } + /* Add the remaining transshipments to the chains. */ + while (ntrrem != 0) + { + lpick = iran(csa, 1, ntrrem); + ntrrem--; + lsorc = iran(csa, 1, nsorc); + chain(csa, lpick, lsorc); + } +L170: /* Set all demands equal to zero. */ + for (i = nfsink; i <= nodes; i++) + ipred[i] = 0; + /* The following loop takes one chain at a time (through the use + of logic contained in the loop and calls to other routines) and + creates the remaining network arcs. */ + for (lsorc = 1; lsorc <= nsorc; lsorc++) + { chnarc(csa, lsorc); + for (i = nfsink; i <= nodes; i++) + iflag[i] = 0; + /* Choose the number of sinks to be hooked up to the current + chain. */ + if (ntrans != 0) + nsksr = (nsort * 2 * nsink) / ntrans; + else + nsksr = nsink / nsorc + 1; + if (nsksr < 2) nsksr = 2; + if (nsksr > nsink) nsksr = nsink; + nsrchn = nsort; + /* Randomly pick nsksr sinks and put their names in lsinks. */ + ktl = nsink; + for (j = 1; j <= nsksr; j++) + { item = iran(csa, 1, ktl); + ktl--; + for (l = nfsink; l <= nodes; l++) + { if (iflag[l] != 1) + { item--; + if (item == 0) goto L230; + } + } + break; +L230: lsinks[j] = l; + iflag[l] = 1; + } + /* If last source chain, add all sinks with zero demand to + lsinks list. */ + if (lsorc == nsorc) + { for (j = nfsink; j <= nodes; j++) + { if (ipred[j] == 0 && iflag[j] != 1) + { nsksr++; + lsinks[nsksr] = j; + iflag[j] = 1; + } + } + } + /* Create demands for group of sinks in lsinks. */ + ks = isup[lsorc] / nsksr; + k = ipred[lsorc]; + for (i = 1; i <= nsksr; i++) + { nsort++; + ksp = iran(csa, 1, ks); + j = iran(csa, 1, nsksr); + itail[nsort] = k; + li = lsinks[i]; + ihead[nsort] = li; + ipred[li] += ksp; + li = lsinks[j]; + ipred[li] += ks - ksp; + n = iran(csa, 1, nsrchn); + k = lsorc; + for (ii = 1; ii <= n; ii++) + k = ipred[k]; + } + li = lsinks[1]; + ipred[li] += isup[lsorc] - ks * nsksr; + nskel += nsort; + /* Sort the arcs in the chain from source lsorc using itail as + sort key. */ + sort(csa); + /* Print this part of skeleton and create the arcs for these + nodes. */ + i = 1; + itail[nsort+1] = 0; +L300: for (j = nftsor; j <= nodes; j++) + iflag[j] = 0; + ktl = nonsor - 1; + it = itail[i]; + iflag[it] = 1; +L320: ih = ihead[i]; + iflag[ih] = 1; + narcs++; + ktl--; + /* Determine if this skeleton arc should be capacitated. */ + icap = itsup; + jcap = iran(csa, 1, 100); + if (jcap <= ipcap) + { icap = isup[lsorc]; + if (mincap > icap) icap = mincap; + } + /* Determine if this skeleton arc should have the maximum + cost. */ + icost = maxcst; + jcost = iran(csa, 1, 100); + if (jcost > iphic) + icost = iran(csa, mincst, maxcst); + if (G == NULL) + xprintf("%6s%6d%6d%2s%10d%10d\n", "", it, ih, "", icost, + icap); + else + { glp_arc *a = glp_add_arc(G, it, ih); + if (a_cap >= 0) + { double temp = (double)icap; + memcpy((char *)a->data + a_cap, &temp, sizeof(double)); + } + if (a_cost >= 0) + { double temp = (double)icost; + memcpy((char *)a->data + a_cost, &temp, sizeof(double)); + } + } + i++; + if (itail[i] == it) goto L320; + pickj(csa, it); + if (i <= nsort) goto L300; + } + /* Create arcs from the transshipment sinks. */ + if (ntsink != 0) + { for (i = nfsink; i <= ltsink; i++) + { for (j = nftsor; j <= nodes; j++) + iflag[j] = 0; + ktl = nonsor - 1; + iflag[i] = 1; + pickj(csa, i); + } + } +L390: /* Print the demand records and end record. */ + if (G == NULL) + { xprintf("DEMAND\n"); + for (i = nfsink; i <= nodes; i++) + xprintf("%6s%6d%18s%10d\n", "", i, "", ipred[i]); + xprintf("END\n"); + } + else + { if (v_rhs >= 0) + { for (i = nfsink; i <= nodes; i++) + { double temp = - (double)ipred[i]; + glp_vertex *v = G->v[i]; + memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); + } + } + } + /* Free working arrays. */ + xfree(ipred); + xfree(ihead); + xfree(itail); + xfree(iflag); + xfree(isup); + xfree(lsinks); + /* The instance has been successfully generated. */ + ret = 0; +done: return ret; +} + +/*********************************************************************** +* The routine cresup randomly distributes the total supply among the +* source nodes. */ + +static void cresup(struct csa *csa) +{ int i, j, ks, ksp; + xassert(itsup > nsorc); + ks = itsup / nsorc; + for (i = 1; i <= nsorc; i++) + isup[i] = 0; + for (i = 1; i <= nsorc; i++) + { ksp = iran(csa, 1, ks); + j = iran(csa, 1, nsorc); + isup[i] += ksp; + isup[j] += ks - ksp; + } + j = iran(csa, 1, nsorc); + isup[j] += itsup - ks * nsorc; + return; +} + +/*********************************************************************** +* The routine chain adds node lpick to the end of the chain with source +* node lsorc. */ + +static void chain(struct csa *csa, int lpick, int lsorc) +{ int i, j, k, l, m; + k = 0; + m = ist; + for (i = 1; i <= lpick; i++) + { l = k; + k = m; + m = ipred[k]; + } + ipred[l] = m; + j = ipred[lsorc]; + ipred[k] = j; + ipred[lsorc] = k; + return; +} + +/*********************************************************************** +* The routine chnarc puts the arcs in the chain from source lsorc into +* the ihead and itail arrays for sorting. */ + +static void chnarc(struct csa *csa, int lsorc) +{ int ito, ifrom; + nsort = 0; + ito = ipred[lsorc]; +L10: if (ito == lsorc) return; + nsort++; + ifrom = ipred[ito]; + ihead[nsort] = ito; + itail[nsort] = ifrom; + ito = ifrom; + goto L10; +} + +/*********************************************************************** +* The routine sort sorts the nsort arcs in the ihead and itail arrays. +* ihead is used as the sort key (i.e. forward star sort order). */ + +static void sort(struct csa *csa) +{ int i, j, k, l, m, n, it; + n = nsort; + m = n; +L10: m /= 2; + if (m == 0) return; + k = n - m; + j = 1; +L20: i = j; +L30: l = i + m; + if (itail[i] <= itail[l]) goto L40; + it = itail[i]; + itail[i] = itail[l]; + itail[l] = it; + it = ihead[i]; + ihead[i] = ihead[l]; + ihead[l] = it; + i -= m; + if (i >= 1) goto L30; +L40: j++; + if (j <= k) goto L20; + goto L10; +} + +/*********************************************************************** +* The routine pickj creates a random number of arcs out of node 'it'. +* Various parameters are dynamically adjusted in an attempt to ensure +* that the generated network has the correct number of arcs. */ + +static void pickj(struct csa *csa, int it) +{ int j, k, l, nn, nupbnd, icap, jcap, icost; + if ((nodlft - 1) * 2 > iarcs - narcs - 1) + { nodlft--; + return; + } + if ((iarcs - narcs + nonsor - ktl - 1) / nodlft - nonsor + 1 >= 0) + k = nonsor; + else + { nupbnd = (iarcs - narcs - nodlft) / nodlft * 2; +L40: k = iran(csa, 1, nupbnd); + if (nodlft == 1) k = iarcs - narcs; + if ((nodlft - 1) * (nonsor - 1) < iarcs - narcs - k) goto L40; + } + nodlft--; + for (j = 1; j <= k; j++) + { nn = iran(csa, 1, ktl); + ktl--; + for (l = nftsor; l <= nodes; l++) + { if (iflag[l] != 1) + { nn--; + if (nn == 0) goto L70; + } + } + return; +L70: iflag[l] = 1; + icap = itsup; + jcap = iran(csa, 1, 100); + if (jcap <= ipcap) + icap = iran(csa, mincap, maxcap); + icost = iran(csa, mincst, maxcst); + if (G == NULL) + xprintf("%6s%6d%6d%2s%10d%10d\n", "", it, l, "", icost, + icap); + else + { glp_arc *a = glp_add_arc(G, it, l); + if (a_cap >= 0) + { double temp = (double)icap; + memcpy((char *)a->data + a_cap, &temp, sizeof(double)); + } + if (a_cost >= 0) + { double temp = (double)icost; + memcpy((char *)a->data + a_cost, &temp, sizeof(double)); + } + } + narcs++; + } + return; +} + +/*********************************************************************** +* The routine assign generate assignment problems. It defines the unit +* supplies, builds a skeleton, then calls pickj to create the arcs. */ + +static void assign(struct csa *csa) +{ int i, it, nn, l, ll, icost; + if (G == NULL) + xprintf("SUPPLY\n"); + for (i = 1; i <= nsorc; i++) + { isup[i] = 1; + iflag[i] = 0; + if (G == NULL) + xprintf("%6s%6d%18s%10d\n", "", i, "", isup[i]); + else + { if (v_rhs >= 0) + { double temp = (double)isup[i]; + glp_vertex *v = G->v[i]; + memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); + } + } + } + if (G == NULL) + xprintf("ARCS\n"); + for (i = nfsink; i <= nodes; i++) + ipred[i] = 1; + for (it = 1; it <= nsorc; it++) + { for (i = nfsink; i <= nodes; i++) + iflag[i] = 0; + ktl = nsink - 1; + nn = iran(csa, 1, nsink - it + 1); + for (l = 1; l <= nsorc; l++) + { if (iflag[l] != 1) + { nn--; + if (nn == 0) break; + } + } + narcs++; + ll = nsorc + l; + icost = iran(csa, mincst, maxcst); + if (G == NULL) + xprintf("%6s%6d%6d%2s%10d%10d\n", "", it, ll, "", icost, + isup[1]); + else + { glp_arc *a = glp_add_arc(G, it, ll); + if (a_cap >= 0) + { double temp = (double)isup[1]; + memcpy((char *)a->data + a_cap, &temp, sizeof(double)); + } + if (a_cost >= 0) + { double temp = (double)icost; + memcpy((char *)a->data + a_cost, &temp, sizeof(double)); + } + } + iflag[l] = 1; + iflag[ll] = 1; + pickj(csa, it); + } + return; +} + +/*********************************************************************** +* Portable congruential (uniform) random number generator: +* +* next_value = ((7**5) * previous_value) modulo ((2**31)-1) +* +* This generator consists of three routines: +* +* (1) setran - initializes constants and seed +* (2) iran - generates an integer random number +* (3) rran - generates a real random number +* +* The generator requires a machine with at least 32 bits of precision. +* The seed (iseed) must be in the range [1,(2**31)-1]. */ + +static void setran(struct csa *csa, int iseed) +{ xassert(iseed >= 1); + mult = 16807; + modul = 2147483647; + i15 = 1 << 15; + i16 = 1 << 16; + jran = iseed; + return; +} + +/*********************************************************************** +* The routine iran generates an integer random number between ilow and +* ihigh. If ilow > ihigh then iran returns ihigh. */ + +static int iran(struct csa *csa, int ilow, int ihigh) +{ int ixhi, ixlo, ixalo, leftlo, ixahi, ifulhi, irtlo, iover, + irthi, j; + ixhi = jran / i16; + ixlo = jran - ixhi * i16; + ixalo = ixlo * mult; + leftlo = ixalo / i16; + ixahi = ixhi * mult; + ifulhi = ixahi + leftlo; + irtlo = ixalo - leftlo * i16; + iover = ifulhi / i15; + irthi = ifulhi - iover * i15; + jran = ((irtlo - modul) + irthi * i16) + iover; + if (jran < 0) jran += modul; + j = ihigh - ilow + 1; + if (j > 0) + return jran % j + ilow; + else + return ihigh; +} + +/**********************************************************************/ + +#if 0 +static int scan(char card[80+1], int pos, int len) +{ char buf[10+1]; + memcpy(buf, &card[pos-1], len); + buf[len] = '\0'; + return atoi(buf); +} + +int main(void) +{ int parm[1+15]; + char card[80+1]; + xassert(fgets(card, sizeof(card), stdin) == card); + parm[1] = scan(card, 1, 8); + parm[2] = scan(card, 9, 8); + xassert(fgets(card, sizeof(card), stdin) == card); + parm[3] = scan(card, 1, 5); + parm[4] = scan(card, 6, 5); + parm[5] = scan(card, 11, 5); + parm[6] = scan(card, 16, 5); + parm[7] = scan(card, 21, 5); + parm[8] = scan(card, 26, 5); + parm[9] = scan(card, 31, 10); + parm[10] = scan(card, 41, 5); + parm[11] = scan(card, 46, 5); + parm[12] = scan(card, 51, 5); + parm[13] = scan(card, 56, 5); + parm[14] = scan(card, 61, 10); + parm[15] = scan(card, 71, 10); + glp_netgen(NULL, 0, 0, 0, parm); + return 0; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet04.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet04.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,768 @@ +/* glpnet04.c (grid-like network problem generator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* This code is a modified version of the program GRIDGEN, a grid-like +* network problem generator developed by Yusin Lee and Jim Orlin. +* The original code is publically available on the DIMACS ftp site at: +* . +* +* All changes concern only the program interface, so this modified +* version produces exactly the same instances as the original version. +* +* Changes were made by Andrew Makhorin . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* NAME +* +* glp_gridgen - grid-like network problem generator +* +* SYNOPSIS +* +* int glp_gridgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, +* const int parm[1+14]); +* +* DESCRIPTION +* +* The routine glp_gridgen is a grid-like network problem generator +* developed by Yusin Lee and Jim Orlin. +* +* The parameter G specifies the graph object, to which the generated +* problem data have to be stored. Note that on entry the graph object +* is erased with the routine glp_erase_graph. +* +* The parameter v_rhs specifies an offset of the field of type double +* in the vertex data block, to which the routine stores the supply or +* demand value. If v_rhs < 0, the value is not stored. +* +* The parameter a_cap specifies an offset of the field of type double +* in the arc data block, to which the routine stores the arc capacity. +* If a_cap < 0, the capacity is not stored. +* +* The parameter a_cost specifies an offset of the field of type double +* in the arc data block, to which the routine stores the per-unit cost +* if the arc flow. If a_cost < 0, the cost is not stored. +* +* The array parm contains description of the network to be generated: +* +* parm[0] not used +* parm[1] two-ways arcs indicator: +* 1 - if links in both direction should be generated +* 0 - otherwise +* parm[2] random number seed (a positive integer) +* parm[3] number of nodes (the number of nodes generated might be +* slightly different to make the network a grid) +* parm[4] grid width +* parm[5] number of sources +* parm[6] number of sinks +* parm[7] average degree +* parm[8] total flow +* parm[9] distribution of arc costs: +* 1 - uniform +* 2 - exponential +* parm[10] lower bound for arc cost (uniform) +* 100 * lambda (exponential) +* parm[11] upper bound for arc cost (uniform) +* not used (exponential) +* parm[12] distribution of arc capacities: +* 1 - uniform +* 2 - exponential +* parm[13] lower bound for arc capacity (uniform) +* 100 * lambda (exponential) +* parm[14] upper bound for arc capacity (uniform) +* not used (exponential) +* +* RETURNS +* +* If the instance was successfully generated, the routine glp_gridgen +* returns zero; otherwise, if specified parameters are inconsistent, +* the routine returns a non-zero error code. +* +* COMMENTS +* +* This network generator generates a grid-like network plus a super +* node. In additional to the arcs connecting the nodes in the grid, +* there is an arc from each supply node to the super node and from the +* super node to each demand node to guarantee feasiblity. These arcs +* have very high costs and very big capacities. +* +* The idea of this network generator is as follows: First, a grid of +* n1 * n2 is generated. For example, 5 * 3. The nodes are numbered as +* 1 to 15, and the supernode is numbered as n1*n2+1. Then arcs between +* adjacent nodes are generated. For these arcs, the user is allowed to +* specify either to generate two-way arcs or one-way arcs. If two-way +* arcs are to be generated, two arcs, one in each direction, will be +* generated between each adjacent node pairs. Otherwise, only one arc +* will be generated. If this is the case, the arcs will be generated +* in alterntive directions as shown below. +* +* 1 ---> 2 ---> 3 ---> 4 ---> 5 +* | ^ | ^ | +* | | | | | +* V | V | V +* 6 <--- 7 <--- 8 <--- 9 <--- 10 +* | ^ | ^ | +* | | | | | +* V | V | V +* 11 --->12 --->13 --->14 ---> 15 +* +* Then the arcs between the super node and the source/sink nodes are +* added as mentioned before. If the number of arcs still doesn't reach +* the requirement, additional arcs will be added by uniformly picking +* random node pairs. There is no checking to prevent multiple arcs +* between any pair of nodes. However, there will be no self-arcs (arcs +* that poins back to its tail node) in the network. +* +* The source and sink nodes are selected uniformly in the network, and +* the imbalances of each source/sink node are also assigned by uniform +* distribution. */ + +struct stat_para +{ /* structure for statistical distributions */ + int distribution; + /* the distribution: */ +#define UNIFORM 1 /* uniform distribution */ +#define EXPONENTIAL 2 /* exponential distribution */ + double parameter[5]; + /* the parameters of the distribution */ +}; + +struct arcs +{ int from; + /* the FROM node of that arc */ + int to; + /* the TO node of that arc */ + int cost; + /* original cost of that arc */ + int u; + /* capacity of the arc */ +}; + +struct imbalance +{ int node; + /* Node ID */ + int supply; + /* Supply of that node */ +}; + +struct csa +{ /* common storage area */ + glp_graph *G; + int v_rhs, a_cap, a_cost; + int seed; + /* random number seed */ + int seed_original; + /* the original seed from input */ + int two_way; + /* 0: generate arcs in both direction for the basic grid, except + for the arcs to/from the super node. 1: o/w */ + int n_node; + /* total number of nodes in the network, numbered 1 to n_node, + including the super node, which is the last one */ + int n_arc; + /* total number of arcs in the network, counting EVERY arc. */ + int n_grid_arc; + /* number of arcs in the basic grid, including the arcs to/from + the super node */ + int n_source, n_sink; + /* number of source and sink nodes */ + int avg_degree; + /* average degree, arcs to and from the super node are counted */ + int t_supply; + /* total supply in the network */ + int n1, n2; + /* the two edges of the network grid. n1 >= n2 */ + struct imbalance *source_list, *sink_list; + /* head of the array of source/sink nodes */ + struct stat_para arc_costs; + /* the distribution of arc costs */ + struct stat_para capacities; + /* distribution of the capacities of the arcs */ + struct arcs *arc_list; + /* head of the arc list array. Arcs in this array are in the + order of grid_arcs, arcs to/from super node, and other arcs */ +}; + +#define G (csa->G) +#define v_rhs (csa->v_rhs) +#define a_cap (csa->a_cap) +#define a_cost (csa->a_cost) +#define seed (csa->seed) +#define seed_original (csa->seed_original) +#define two_way (csa->two_way) +#define n_node (csa->n_node) +#define n_arc (csa->n_arc) +#define n_grid_arc (csa->n_grid_arc) +#define n_source (csa->n_source) +#define n_sink (csa->n_sink) +#define avg_degree (csa->avg_degree) +#define t_supply (csa->t_supply) +#define n1 (csa->n1) +#define n2 (csa->n2) +#define source_list (csa->source_list) +#define sink_list (csa->sink_list) +#define arc_costs (csa->arc_costs) +#define capacities (csa->capacities) +#define arc_list (csa->arc_list) + +static void assign_capacities(struct csa *csa); +static void assign_costs(struct csa *csa); +static void assign_imbalance(struct csa *csa); +static int exponential(struct csa *csa, double lambda[1]); +static struct arcs *gen_additional_arcs(struct csa *csa, struct arcs + *arc_ptr); +static struct arcs *gen_basic_grid(struct csa *csa, struct arcs + *arc_ptr); +static void gen_more_arcs(struct csa *csa, struct arcs *arc_ptr); +static void generate(struct csa *csa); +static void output(struct csa *csa); +static double randy(struct csa *csa); +static void select_source_sinks(struct csa *csa); +static int uniform(struct csa *csa, double a[2]); + +int glp_gridgen(glp_graph *G_, int _v_rhs, int _a_cap, int _a_cost, + const int parm[1+14]) +{ struct csa _csa, *csa = &_csa; + int n, ret; + G = G_; + v_rhs = _v_rhs; + a_cap = _a_cap; + a_cost = _a_cost; + if (G != NULL) + { if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) + xerror("glp_gridgen: v_rhs = %d; invalid offset\n", v_rhs); + if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_gridgen: a_cap = %d; invalid offset\n", a_cap); + if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) + xerror("glp_gridgen: a_cost = %d; invalid offset\n", a_cost) + ; + } + /* Check the parameters for consistency. */ + if (!(parm[1] == 0 || parm[1] == 1)) + { ret = 1; + goto done; + } + if (parm[2] < 1) + { ret = 2; + goto done; + } + if (!(10 <= parm[3] && parm[3] <= 40000)) + { ret = 3; + goto done; + } + if (!(1 <= parm[4] && parm[4] <= 40000)) + { ret = 4; + goto done; + } + if (!(parm[5] >= 0 && parm[6] >= 0 && parm[5] + parm[6] <= + parm[3])) + { ret = 5; + goto done; + } + if (!(1 <= parm[7] && parm[7] <= parm[3])) + { ret = 6; + goto done; + } + if (parm[8] < 0) + { ret = 7; + goto done; + } + if (!(parm[9] == 1 || parm[9] == 2)) + { ret = 8; + goto done; + } + if (parm[9] == 1 && parm[10] > parm[11] || + parm[9] == 2 && parm[10] < 1) + { ret = 9; + goto done; + } + if (!(parm[12] == 1 || parm[12] == 2)) + { ret = 10; + goto done; + } + if (parm[12] == 1 && !(0 <= parm[13] && parm[13] <= parm[14]) || + parm[12] == 2 && parm[13] < 1) + { ret = 11; + goto done; + } + /* Initialize the graph object. */ + if (G != NULL) + { glp_erase_graph(G, G->v_size, G->a_size); + glp_set_graph_name(G, "GRIDGEN"); + } + /* Copy the generator parameters. */ + two_way = parm[1]; + seed_original = seed = parm[2]; + n_node = parm[3]; + n = parm[4]; + n_source = parm[5]; + n_sink = parm[6]; + avg_degree = parm[7]; + t_supply = parm[8]; + arc_costs.distribution = parm[9]; + if (parm[9] == 1) + { arc_costs.parameter[0] = parm[10]; + arc_costs.parameter[1] = parm[11]; + } + else + { arc_costs.parameter[0] = (double)parm[10] / 100.0; + arc_costs.parameter[1] = 0.0; + } + capacities.distribution = parm[12]; + if (parm[12] == 1) + { capacities.parameter[0] = parm[13]; + capacities.parameter[1] = parm[14]; + } + else + { capacities.parameter[0] = (double)parm[13] / 100.0; + capacities.parameter[1] = 0.0; + } + /* Calculate the edge lengths of the grid according to the + input. */ + if (n * n >= n_node) + { n1 = n; + n2 = (int)((double)n_node / (double)n + 0.5); + } + else + { n2 = n; + n1 = (int)((double)n_node / (double)n + 0.5); + } + /* Recalculate the total number of nodes and plus 1 for the super + node. */ + n_node = n1 * n2 + 1; + n_arc = n_node * avg_degree; + n_grid_arc = (two_way + 1) * ((n1 - 1) * n2 + (n2 - 1) * n1) + + n_source + n_sink; + if (n_grid_arc > n_arc) n_arc = n_grid_arc; + arc_list = xcalloc(n_arc, sizeof(struct arcs)); + source_list = xcalloc(n_source, sizeof(struct imbalance)); + sink_list = xcalloc(n_sink, sizeof(struct imbalance)); + /* Generate a random network. */ + generate(csa); + /* Output the network. */ + output(csa); + /* Free all allocated memory. */ + xfree(arc_list); + xfree(source_list); + xfree(sink_list); + /* The instance has been successfully generated. */ + ret = 0; +done: return ret; +} + +#undef random + +static void assign_capacities(struct csa *csa) +{ /* Assign a capacity to each arc. */ + struct arcs *arc_ptr = arc_list; + int (*random)(struct csa *csa, double *); + int i; + /* Determine the random number generator to use. */ + switch (arc_costs.distribution) + { case UNIFORM: + random = uniform; + break; + case EXPONENTIAL: + random = exponential; + break; + default: + xassert(csa != csa); + } + /* Assign capacities to grid arcs. */ + for (i = n_source + n_sink; i < n_grid_arc; i++, arc_ptr++) + arc_ptr->u = random(csa, capacities.parameter); + i = i - n_source - n_sink; + /* Assign capacities to arcs to/from supernode. */ + for (; i < n_grid_arc; i++, arc_ptr++) + arc_ptr->u = t_supply; + /* Assign capacities to all other arcs. */ + for (; i < n_arc; i++, arc_ptr++) + arc_ptr->u = random(csa, capacities.parameter); + return; +} + +static void assign_costs(struct csa *csa) +{ /* Assign a cost to each arc. */ + struct arcs *arc_ptr = arc_list; + int (*random)(struct csa *csa, double *); + int i; + /* A high cost assigned to arcs to/from the supernode. */ + int high_cost; + /* The maximum cost assigned to arcs in the base grid. */ + int max_cost = 0; + /* Determine the random number generator to use. */ + switch (arc_costs.distribution) + { case UNIFORM: + random = uniform; + break; + case EXPONENTIAL: + random = exponential; + break; + default: + xassert(csa != csa); + } + /* Assign costs to arcs in the base grid. */ + for (i = n_source + n_sink; i < n_grid_arc; i++, arc_ptr++) + { arc_ptr->cost = random(csa, arc_costs.parameter); + if (max_cost < arc_ptr->cost) max_cost = arc_ptr->cost; + } + i = i - n_source - n_sink; + /* Assign costs to arcs to/from the super node. */ + high_cost = max_cost * 2; + for (; i < n_grid_arc; i++, arc_ptr++) + arc_ptr->cost = high_cost; + /* Assign costs to all other arcs. */ + for (; i < n_arc; i++, arc_ptr++) + arc_ptr->cost = random(csa, arc_costs.parameter); + return; +} + +static void assign_imbalance(struct csa *csa) +{ /* Assign an imbalance to each node. */ + int total, i; + double avg; + struct imbalance *ptr; + /* assign the supply nodes */ + avg = 2.0 * t_supply / n_source; + do + { for (i = 1, total = t_supply, ptr = source_list + 1; + i < n_source; i++, ptr++) + { ptr->supply = (int)(randy(csa) * avg + 0.5); + total -= ptr->supply; + } + source_list->supply = total; + } + /* redo all if the assignment "overshooted" */ + while (total <= 0); + /* assign the demand nodes */ + avg = -2.0 * t_supply / n_sink; + do + { for (i = 1, total = t_supply, ptr = sink_list + 1; + i < n_sink; i++, ptr++) + { ptr->supply = (int)(randy(csa) * avg - 0.5); + total += ptr->supply; + } + sink_list->supply = - total; + } + while (total <= 0); + return; +} + +static int exponential(struct csa *csa, double lambda[1]) +{ /* Returns an "exponentially distributed" integer with parameter + lambda. */ + return ((int)(- lambda[0] * log((double)randy(csa)) + 0.5)); +} + +static struct arcs *gen_additional_arcs(struct csa *csa, struct arcs + *arc_ptr) +{ /* Generate an arc from each source to the supernode and from + supernode to each sink. */ + int i; + for (i = 0; i < n_source; i++, arc_ptr++) + { arc_ptr->from = source_list[i].node; + arc_ptr->to = n_node; + } + for (i = 0; i < n_sink; i++, arc_ptr++) + { arc_ptr->to = sink_list[i].node; + arc_ptr->from = n_node; + } + return arc_ptr; +} + +static struct arcs *gen_basic_grid(struct csa *csa, struct arcs + *arc_ptr) +{ /* Generate the basic grid. */ + int direction = 1, i, j, k; + if (two_way) + { /* Generate an arc in each direction. */ + for (i = 1; i < n_node; i += n1) + { for (j = i, k = j + n1 - 1; j < k; j++) + { arc_ptr->from = j; + arc_ptr->to = j + 1; + arc_ptr++; + arc_ptr->from = j + 1; + arc_ptr->to = j; + arc_ptr++; + } + } + for (i = 1; i <= n1; i++) + { for (j = i + n1; j < n_node; j += n1) + { arc_ptr->from = j; + arc_ptr->to = j - n1; + arc_ptr++; + arc_ptr->from = j - n1; + arc_ptr->to = j; + arc_ptr++; + } + } + } + else + { /* Generate one arc in each direction. */ + for (i = 1; i < n_node; i += n1) + { if (direction == 1) + j = i; + else + j = i + 1; + for (k = j + n1 - 1; j < k; j++) + { arc_ptr->from = j; + arc_ptr->to = j + direction; + arc_ptr++; + } + direction = - direction; + } + for (i = 1; i <= n1; i++) + { j = i + n1; + if (direction == 1) + { for (; j < n_node; j += n1) + { arc_ptr->from = j - n1; + arc_ptr->to = j; + arc_ptr++; + } + } + else + { for (; j < n_node; j += n1) + { arc_ptr->from = j - n1; + arc_ptr->to = j; + arc_ptr++; + } + } + direction = - direction; + } + } + return arc_ptr; +} + +static void gen_more_arcs(struct csa *csa, struct arcs *arc_ptr) +{ /* Generate random arcs to meet the specified density. */ + int i; + double ab[2]; + ab[0] = 0.9; + ab[1] = n_node - 0.99; /* upper limit is n_node-1 because the + supernode cannot be selected */ + for (i = n_grid_arc; i < n_arc; i++, arc_ptr++) + { arc_ptr->from = uniform(csa, ab); + arc_ptr->to = uniform(csa, ab); + if (arc_ptr->from == arc_ptr->to) + { arc_ptr--; + i--; + } + } + return; +} + +static void generate(struct csa *csa) +{ /* Generate a random network. */ + struct arcs *arc_ptr = arc_list; + arc_ptr = gen_basic_grid(csa, arc_ptr); + select_source_sinks(csa); + arc_ptr = gen_additional_arcs(csa, arc_ptr); + gen_more_arcs(csa, arc_ptr); + assign_costs(csa); + assign_capacities(csa); + assign_imbalance(csa); + return; +} + +static void output(struct csa *csa) +{ /* Output the network in DIMACS format. */ + struct arcs *arc_ptr; + struct imbalance *imb_ptr; + int i; + if (G != NULL) goto skip; + /* Output "c", "p" records. */ + xprintf("c generated by GRIDGEN\n"); + xprintf("c seed %d\n", seed_original); + xprintf("c nodes %d\n", n_node); + xprintf("c grid size %d X %d\n", n1, n2); + xprintf("c sources %d sinks %d\n", n_source, n_sink); + xprintf("c avg. degree %d\n", avg_degree); + xprintf("c supply %d\n", t_supply); + switch (arc_costs.distribution) + { case UNIFORM: + xprintf("c arc costs: UNIFORM distr. min %d max %d\n", + (int)arc_costs.parameter[0], + (int)arc_costs.parameter[1]); + break; + case EXPONENTIAL: + xprintf("c arc costs: EXPONENTIAL distr. lambda %d\n", + (int)arc_costs.parameter[0]); + break; + default: + xassert(csa != csa); + } + switch (capacities.distribution) + { case UNIFORM: + xprintf("c arc caps : UNIFORM distr. min %d max %d\n", + (int)capacities.parameter[0], + (int)capacities.parameter[1]); + break; + case EXPONENTIAL: + xprintf("c arc caps : EXPONENTIAL distr. %d lambda %d\n", + (int)capacities.parameter[0]); + break; + default: + xassert(csa != csa); + } +skip: if (G == NULL) + xprintf("p min %d %d\n", n_node, n_arc); + else + { glp_add_vertices(G, n_node); + if (v_rhs >= 0) + { double zero = 0.0; + for (i = 1; i <= n_node; i++) + { glp_vertex *v = G->v[i]; + memcpy((char *)v->data + v_rhs, &zero, sizeof(double)); + } + } + } + /* Output "n node supply". */ + for (i = 0, imb_ptr = source_list; i < n_source; i++, imb_ptr++) + { if (G == NULL) + xprintf("n %d %d\n", imb_ptr->node, imb_ptr->supply); + else + { if (v_rhs >= 0) + { double temp = (double)imb_ptr->supply; + glp_vertex *v = G->v[imb_ptr->node]; + memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); + } + } + } + for (i = 0, imb_ptr = sink_list; i < n_sink; i++, imb_ptr++) + { if (G == NULL) + xprintf("n %d %d\n", imb_ptr->node, imb_ptr->supply); + else + { if (v_rhs >= 0) + { double temp = (double)imb_ptr->supply; + glp_vertex *v = G->v[imb_ptr->node]; + memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); + } + } + } + /* Output "a from to lowcap=0 hicap cost". */ + for (i = 0, arc_ptr = arc_list; i < n_arc; i++, arc_ptr++) + { if (G == NULL) + xprintf("a %d %d 0 %d %d\n", arc_ptr->from, arc_ptr->to, + arc_ptr->u, arc_ptr->cost); + else + { glp_arc *a = glp_add_arc(G, arc_ptr->from, arc_ptr->to); + if (a_cap >= 0) + { double temp = (double)arc_ptr->u; + memcpy((char *)a->data + a_cap, &temp, sizeof(double)); + } + if (a_cost >= 0) + { double temp = (double)arc_ptr->cost; + memcpy((char *)a->data + a_cost, &temp, sizeof(double)); + } + } + } + return; +} + +static double randy(struct csa *csa) +{ /* Returns a random number between 0.0 and 1.0. + See Ward Cheney & David Kincaid, "Numerical Mathematics and + Computing," 2Ed, pp. 335. */ + seed = 16807 * seed % 2147483647; + if (seed < 0) seed = - seed; + return seed * 4.6566128752459e-10; +} + +static void select_source_sinks(struct csa *csa) +{ /* Randomly select the source nodes and sink nodes. */ + int i, *int_ptr; + int *temp_list; /* a temporary list of nodes */ + struct imbalance *ptr; + double ab[2]; /* parameter for random number generator */ + ab[0] = 0.9; + ab[1] = n_node - 0.99; /* upper limit is n_node-1 because the + supernode cannot be selected */ + temp_list = xcalloc(n_node, sizeof(int)); + for (i = 0, int_ptr = temp_list; i < n_node; i++, int_ptr++) + *int_ptr = 0; + /* Select the source nodes. */ + for (i = 0, ptr = source_list; i < n_source; i++, ptr++) + { ptr->node = uniform(csa, ab); + if (temp_list[ptr->node] == 1) /* check for duplicates */ + { ptr--; + i--; + } + else + temp_list[ptr->node] = 1; + } + /* Select the sink nodes. */ + for (i = 0, ptr = sink_list; i < n_sink; i++, ptr++) + { ptr->node = uniform(csa, ab); + if (temp_list[ptr->node] == 1) + { ptr--; + i--; + } + else + temp_list[ptr->node] = 1; + } + xfree(temp_list); + return; +} + +int uniform(struct csa *csa, double a[2]) +{ /* Generates an integer uniformly selected from [a[0],a[1]]. */ + return (int)((a[1] - a[0]) * randy(csa) + a[0] + 0.5); +} + +/**********************************************************************/ + +#if 0 +int main(void) +{ int parm[1+14]; + double temp; + scanf("%d", &parm[1]); + scanf("%d", &parm[2]); + scanf("%d", &parm[3]); + scanf("%d", &parm[4]); + scanf("%d", &parm[5]); + scanf("%d", &parm[6]); + scanf("%d", &parm[7]); + scanf("%d", &parm[8]); + scanf("%d", &parm[9]); + if (parm[9] == 1) + { scanf("%d", &parm[10]); + scanf("%d", &parm[11]); + } + else + { scanf("%le", &temp); + parm[10] = (int)(100.0 * temp + .5); + parm[11] = 0; + } + scanf("%d", &parm[12]); + if (parm[12] == 1) + { scanf("%d", &parm[13]); + scanf("%d", &parm[14]); + } + else + { scanf("%le", &temp); + parm[13] = (int)(100.0 * temp + .5); + parm[14] = 0; + } + glp_gridgen(NULL, 0, 0, 0, parm); + return 0; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet05.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet05.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,367 @@ +/* glpnet05.c (Goldfarb's maximum flow problem generator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* This code is a modified version of the program RMFGEN, a maxflow +* problem generator developed by D.Goldfarb and M.Grigoriadis, and +* originally implemented by Tamas Badics . +* The original code is publically available on the DIMACS ftp site at: +* . +* +* All changes concern only the program interface, so this modified +* version produces exactly the same instances as the original version. +* +* Changes were made by Andrew Makhorin . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" +#include "glprng.h" + +/*********************************************************************** +* NAME +* +* glp_rmfgen - Goldfarb's maximum flow problem generator +* +* SYNOPSIS +* +* int glp_rmfgen(glp_graph *G, int *s, int *t, int a_cap, +* const int parm[1+5]); +* +* DESCRIPTION +* +* The routine glp_rmfgen is a maximum flow problem generator developed +* by D.Goldfarb and M.Grigoriadis. +* +* The parameter G specifies the graph object, to which the generated +* problem data have to be stored. Note that on entry the graph object +* is erased with the routine glp_erase_graph. +* +* The pointer s specifies a location, to which the routine stores the +* source node number. If s is NULL, the node number is not stored. +* +* The pointer t specifies a location, to which the routine stores the +* sink node number. If t is NULL, the node number is not stored. +* +* The parameter a_cap specifies an offset of the field of type double +* in the arc data block, to which the routine stores the arc capacity. +* If a_cap < 0, the capacity is not stored. +* +* The array parm contains description of the network to be generated: +* +* parm[0] not used +* parm[1] (seed) random number seed (a positive integer) +* parm[2] (a) frame size +* parm[3] (b) depth +* parm[4] (c1) minimal arc capacity +* parm[5] (c2) maximal arc capacity +* +* RETURNS +* +* If the instance was successfully generated, the routine glp_netgen +* returns zero; otherwise, if specified parameters are inconsistent, +* the routine returns a non-zero error code. +* +* COMMENTS +* +* The generated network is as follows. It has b pieces of frames of +* size a * a. (So alltogether the number of vertices is a * a * b) +* +* In each frame all the vertices are connected with their neighbours +* (forth and back). In addition the vertices of a frame are connected +* one to one with the vertices of next frame using a random permutation +* of those vertices. +* +* The source is the lower left vertex of the first frame, the sink is +* the upper right vertex of the b'th frame. +* +* t +* +-------+ +* | .| +* | . | +* / | / | +* +-------+/ -+ b +* | | |/. +* a | -v- |/ +* | | |/ +* +-------+ 1 +* s a +* +* The capacities are randomly chosen integers from the range of [c1,c2] +* in the case of interconnecting edges, and c2 * a * a for the in-frame +* edges. +* +* REFERENCES +* +* D.Goldfarb and M.D.Grigoriadis, "A computational comparison of the +* Dinic and network simplex methods for maximum flow." Annals of Op. +* Res. 13 (1988), pp. 83-123. +* +* U.Derigs and W.Meier, "Implementing Goldberg's max-flow algorithm: +* A computational investigation." Zeitschrift fuer Operations Research +* 33 (1989), pp. 383-403. */ + +typedef struct VERTEX +{ struct EDGE **edgelist; + /* Pointer to the list of pointers to the adjacent edges. + (No matter that to or from edges) */ + struct EDGE **current; + /* Pointer to the current edge */ + int degree; + /* Number of adjacent edges (both direction) */ + int index; +} vertex; + +typedef struct EDGE +{ int from; + int to; + int cap; + /* Capacity */ +} edge; + +typedef struct NETWORK +{ struct NETWORK *next, *prev; + int vertnum; + int edgenum; + vertex *verts; + /* Vertex array[1..vertnum] */ + edge *edges; + /* Edge array[1..edgenum] */ + int source; + /* Pointer to the source */ + int sink; + /* Pointer to the sink */ +} network; + +struct csa +{ /* common storage area */ + glp_graph *G; + int *s, *t, a_cap; + RNG *rand; + network *N; + int *Parr; + int A, AA, C2AA, Ec; +}; + +#define G (csa->G) +#define s (csa->s) +#define t (csa->t) +#define a_cap (csa->a_cap) +#define N (csa->N) +#define Parr (csa->Parr) +#define A (csa->A) +#define AA (csa->AA) +#define C2AA (csa->C2AA) +#define Ec (csa->Ec) + +#undef random +#define random(A) (int)(rng_unif_01(csa->rand) * (double)(A)) +#define RANDOM(A, B) (int)(random((B) - (A) + 1) + (A)) +#define sgn(A) (((A) > 0) ? 1 : ((A) == 0) ? 0 : -1) + +static void make_edge(struct csa *csa, int from, int to, int c1, int c2) +{ Ec++; + N->edges[Ec].from = from; + N->edges[Ec].to = to; + N->edges[Ec].cap = RANDOM(c1, c2); + return; +} + +static void permute(struct csa *csa) +{ int i, j, tmp; + for (i = 1; i < AA; i++) + { j = RANDOM(i, AA); + tmp = Parr[i]; + Parr[i] = Parr[j]; + Parr[j] = tmp; + } + return; +} + +static void connect(struct csa *csa, int offset, int cv, int x1, int y1) +{ int cv1; + cv1 = offset + (x1 - 1) * A + y1; + Ec++; + N->edges[Ec].from = cv; + N->edges[Ec].to = cv1; + N->edges[Ec].cap = C2AA; + return; +} + +static network *gen_rmf(struct csa *csa, int a, int b, int c1, int c2) +{ /* generates a network with a*a*b nodes and 6a*a*b-4ab-2a*a edges + random_frame network: + Derigs & Meier, Methods & Models of OR (1989), 33:383-403 */ + int x, y, z, offset, cv; + A = a; + AA = a * a; + C2AA = c2 * AA; + Ec = 0; + N = (network *)xmalloc(sizeof(network)); + N->vertnum = AA * b; + N->edgenum = 5 * AA * b - 4 * A * b - AA; + N->edges = (edge *)xcalloc(N->edgenum + 1, sizeof(edge)); + N->source = 1; + N->sink = N->vertnum; + Parr = (int *)xcalloc(AA + 1, sizeof(int)); + for (x = 1; x <= AA; x++) + Parr[x] = x; + for (z = 1; z <= b; z++) + { offset = AA * (z - 1); + if (z != b) + permute(csa); + for (x = 1; x <= A; x++) + { for (y = 1; y <= A; y++) + { cv = offset + (x - 1) * A + y; + if (z != b) + make_edge(csa, cv, offset + AA + Parr[cv - offset], + c1, c2); /* the intermediate edges */ + if (y < A) + connect(csa, offset, cv, x, y + 1); + if (y > 1) + connect(csa, offset, cv, x, y - 1); + if (x < A) + connect(csa, offset, cv, x + 1, y); + if (x > 1) + connect(csa, offset, cv, x - 1, y); + } + } + } + xfree(Parr); + return N; +} + +static void print_max_format(struct csa *csa, network *n, char *comm[], + int dim) +{ /* prints a network heading with dim lines of comments (no \n + needs at the ends) */ + int i, vnum, e_num; + edge *e; + vnum = n->vertnum; + e_num = n->edgenum; + if (G == NULL) + { for (i = 0; i < dim; i++) + xprintf("c %s\n", comm[i]); + xprintf("p max %7d %10d\n", vnum, e_num); + xprintf("n %7d s\n", n->source); + xprintf("n %7d t\n", n->sink); + } + else + { glp_add_vertices(G, vnum); + if (s != NULL) *s = n->source; + if (t != NULL) *t = n->sink; + } + for (i = 1; i <= e_num; i++) + { e = &n->edges[i]; + if (G == NULL) + xprintf("a %7d %7d %10d\n", e->from, e->to, (int)e->cap); + else + { glp_arc *a = glp_add_arc(G, e->from, e->to); + if (a_cap >= 0) + { double temp = (double)e->cap; + memcpy((char *)a->data + a_cap, &temp, sizeof(double)); + } + } + } + return; +} + +static void gen_free_net(network *n) +{ xfree(n->edges); + xfree(n); + return; +} + +int glp_rmfgen(glp_graph *G_, int *_s, int *_t, int _a_cap, + const int parm[1+5]) +{ struct csa _csa, *csa = &_csa; + network *n; + char comm[10][80], *com1[10]; + int seed, a, b, c1, c2, ret; + G = G_; + s = _s; + t = _t; + a_cap = _a_cap; + if (G != NULL) + { if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) + xerror("glp_rmfgen: a_cap = %d; invalid offset\n", a_cap); + } + seed = parm[1]; + a = parm[2]; + b = parm[3]; + c1 = parm[4]; + c2 = parm[5]; + if (!(seed > 0 && 1 <= a && a <= 1000 && 1 <= b && b <= 1000 && + 0 <= c1 && c1 <= c2 && c2 <= 1000)) + { ret = 1; + goto done; + } + if (G != NULL) + { glp_erase_graph(G, G->v_size, G->a_size); + glp_set_graph_name(G, "RMFGEN"); + } + csa->rand = rng_create_rand(); + rng_init_rand(csa->rand, seed); + n = gen_rmf(csa, a, b, c1, c2); + sprintf(comm[0], "This file was generated by genrmf."); + sprintf(comm[1], "The parameters are: a: %d b: %d c1: %d c2: %d", + a, b, c1, c2); + com1[0] = comm[0]; + com1[1] = comm[1]; + print_max_format(csa, n, com1, 2); + gen_free_net(n); + rng_delete_rand(csa->rand); + ret = 0; +done: return ret; +} + +/**********************************************************************/ + +#if 0 +int main(int argc, char *argv[]) +{ int seed, a, b, c1, c2, i, parm[1+5]; + seed = 123; + a = b = c1 = c2 = -1; + for (i = 1; i < argc; i++) + { if (strcmp(argv[i], "-seed") == 0) + seed = atoi(argv[++i]); + else if (strcmp(argv[i], "-a") == 0) + a = atoi(argv[++i]); + else if (strcmp(argv[i], "-b") == 0) + b = atoi(argv[++i]); + else if (strcmp(argv[i], "-c1") == 0) + c1 = atoi(argv[++i]); + else if (strcmp(argv[i], "-c2") == 0) + c2 = atoi(argv[++i]); + } + if (a < 0 || b < 0 || c1 < 0 || c2 < 0) + { xprintf("Usage:\n"); + xprintf("genrmf [-seed seed] -a frame_size -b depth\n"); + xprintf(" -c1 cap_range1 -c2 cap_range2\n"); + } + else + { parm[1] = seed; + parm[2] = a; + parm[3] = b; + parm[4] = c1; + parm[5] = c2; + glp_rmfgen(NULL, NULL, NULL, 0, parm); + } + return 0; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet06.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet06.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,383 @@ +/* glpnet06.c (out-of-kilter algorithm) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glpnet.h" + +/*********************************************************************** +* NAME +* +* okalg - out-of-kilter algorithm +* +* SYNOPSIS +* +* #include "glpnet.h" +* int okalg(int nv, int na, const int tail[], const int head[], +* const int low[], const int cap[], const int cost[], int x[], +* int pi[]); +* +* DESCRIPTION +* +* The routine okalg implements the out-of-kilter algorithm to find a +* minimal-cost circulation in the specified flow network. +* +* INPUT PARAMETERS +* +* nv is the number of nodes, nv >= 0. +* +* na is the number of arcs, na >= 0. +* +* tail[a], a = 1,...,na, is the index of tail node of arc a. +* +* head[a], a = 1,...,na, is the index of head node of arc a. +* +* low[a], a = 1,...,na, is an lower bound to the flow through arc a. +* +* cap[a], a = 1,...,na, is an upper bound to the flow through arc a, +* which is the capacity of the arc. +* +* cost[a], a = 1,...,na, is a per-unit cost of the flow through arc a. +* +* NOTES +* +* 1. Multiple arcs are allowed, but self-loops are not allowed. +* +* 2. It is required that 0 <= low[a] <= cap[a] for all arcs. +* +* 3. Arc costs may have any sign. +* +* OUTPUT PARAMETERS +* +* x[a], a = 1,...,na, is optimal value of the flow through arc a. +* +* pi[i], i = 1,...,nv, is Lagrange multiplier for flow conservation +* equality constraint corresponding to node i (the node potential). +* +* RETURNS +* +* 0 optimal circulation found; +* +* 1 there is no feasible circulation; +* +* 2 integer overflow occured; +* +* 3 optimality test failed (logic error). +* +* REFERENCES +* +* L.R.Ford, Jr., and D.R.Fulkerson, "Flows in Networks," The RAND +* Corp., Report R-375-PR (August 1962), Chap. III "Minimal Cost Flow +* Problems," pp.113-26. */ + +static int overflow(int u, int v) +{ /* check for integer overflow on computing u + v */ + if (u > 0 && v > 0 && u + v < 0) return 1; + if (u < 0 && v < 0 && u + v > 0) return 1; + return 0; +} + +int okalg(int nv, int na, const int tail[], const int head[], + const int low[], const int cap[], const int cost[], int x[], + int pi[]) +{ int a, aok, delta, i, j, k, lambda, pos1, pos2, s, t, temp, ret, + *ptr, *arc, *link, *list; + /* sanity checks */ + xassert(nv >= 0); + xassert(na >= 0); + for (a = 1; a <= na; a++) + { i = tail[a], j = head[a]; + xassert(1 <= i && i <= nv); + xassert(1 <= j && j <= nv); + xassert(i != j); + xassert(0 <= low[a] && low[a] <= cap[a]); + } + /* allocate working arrays */ + ptr = xcalloc(1+nv+1, sizeof(int)); + arc = xcalloc(1+na+na, sizeof(int)); + link = xcalloc(1+nv, sizeof(int)); + list = xcalloc(1+nv, sizeof(int)); + /* ptr[i] := (degree of node i) */ + for (i = 1; i <= nv; i++) + ptr[i] = 0; + for (a = 1; a <= na; a++) + { ptr[tail[a]]++; + ptr[head[a]]++; + } + /* initialize arc pointers */ + ptr[1]++; + for (i = 1; i < nv; i++) + ptr[i+1] += ptr[i]; + ptr[nv+1] = ptr[nv]; + /* build arc lists */ + for (a = 1; a <= na; a++) + { arc[--ptr[tail[a]]] = a; + arc[--ptr[head[a]]] = a; + } + xassert(ptr[1] == 1); + xassert(ptr[nv+1] == na+na+1); + /* now the indices of arcs incident to node i are stored in + locations arc[ptr[i]], arc[ptr[i]+1], ..., arc[ptr[i+1]-1] */ + /* initialize arc flows and node potentials */ + for (a = 1; a <= na; a++) + x[a] = 0; + for (i = 1; i <= nv; i++) + pi[i] = 0; +loop: /* main loop starts here */ + /* find out-of-kilter arc */ + aok = 0; + for (a = 1; a <= na; a++) + { i = tail[a], j = head[a]; + if (overflow(cost[a], pi[i] - pi[j])) + { ret = 2; + goto done; + } + lambda = cost[a] + (pi[i] - pi[j]); + if (x[a] < low[a] || lambda < 0 && x[a] < cap[a]) + { /* arc a = i->j is out of kilter, and we need to increase + the flow through this arc */ + aok = a, s = j, t = i; + break; + } + if (x[a] > cap[a] || lambda > 0 && x[a] > low[a]) + { /* arc a = i->j is out of kilter, and we need to decrease + the flow through this arc */ + aok = a, s = i, t = j; + break; + } + } + if (aok == 0) + { /* all arcs are in kilter */ + /* check for feasibility */ + for (a = 1; a <= na; a++) + { if (!(low[a] <= x[a] && x[a] <= cap[a])) + { ret = 3; + goto done; + } + } + for (i = 1; i <= nv; i++) + { temp = 0; + for (k = ptr[i]; k < ptr[i+1]; k++) + { a = arc[k]; + if (tail[a] == i) + { /* a is outgoing arc */ + temp += x[a]; + } + else if (head[a] == i) + { /* a is incoming arc */ + temp -= x[a]; + } + else + xassert(a != a); + } + if (temp != 0) + { ret = 3; + goto done; + } + } + /* check for optimality */ + for (a = 1; a <= na; a++) + { i = tail[a], j = head[a]; + lambda = cost[a] + (pi[i] - pi[j]); + if (lambda > 0 && x[a] != low[a] || + lambda < 0 && x[a] != cap[a]) + { ret = 3; + goto done; + } + } + /* current circulation is optimal */ + ret = 0; + goto done; + } + /* now we need to find a cycle (t, a, s, ..., t), which allows + increasing the flow along it, where a is the out-of-kilter arc + just found */ + /* link[i] = 0 means that node i is not labelled yet; + link[i] = a means that arc a immediately precedes node i */ + /* initially only node s is labelled */ + for (i = 1; i <= nv; i++) + link[i] = 0; + link[s] = aok, list[1] = s, pos1 = pos2 = 1; + /* breadth first search */ + while (pos1 <= pos2) + { /* dequeue node i */ + i = list[pos1++]; + /* consider all arcs incident to node i */ + for (k = ptr[i]; k < ptr[i+1]; k++) + { a = arc[k]; + if (tail[a] == i) + { /* a = i->j is a forward arc from s to t */ + j = head[a]; + /* if node j has been labelled, skip the arc */ + if (link[j] != 0) continue; + /* if the arc does not allow increasing the flow through + it, skip the arc */ + if (x[a] >= cap[a]) continue; + if (overflow(cost[a], pi[i] - pi[j])) + { ret = 2; + goto done; + } + lambda = cost[a] + (pi[i] - pi[j]); + if (lambda > 0 && x[a] >= low[a]) continue; + } + else if (head[a] == i) + { /* a = i<-j is a backward arc from s to t */ + j = tail[a]; + /* if node j has been labelled, skip the arc */ + if (link[j] != 0) continue; + /* if the arc does not allow decreasing the flow through + it, skip the arc */ + if (x[a] <= low[a]) continue; + if (overflow(cost[a], pi[j] - pi[i])) + { ret = 2; + goto done; + } + lambda = cost[a] + (pi[j] - pi[i]); + if (lambda < 0 && x[a] <= cap[a]) continue; + } + else + xassert(a != a); + /* label node j and enqueue it */ + link[j] = a, list[++pos2] = j; + /* check for breakthrough */ + if (j == t) goto brkt; + } + } + /* NONBREAKTHROUGH */ + /* consider all arcs, whose one endpoint is labelled and other is + not, and determine maximal change of node potentials */ + delta = 0; + for (a = 1; a <= na; a++) + { i = tail[a], j = head[a]; + if (link[i] != 0 && link[j] == 0) + { /* a = i->j, where node i is labelled, node j is not */ + if (overflow(cost[a], pi[i] - pi[j])) + { ret = 2; + goto done; + } + lambda = cost[a] + (pi[i] - pi[j]); + if (x[a] <= cap[a] && lambda > 0) + if (delta == 0 || delta > + lambda) delta = + lambda; + } + else if (link[i] == 0 && link[j] != 0) + { /* a = j<-i, where node j is labelled, node i is not */ + if (overflow(cost[a], pi[i] - pi[j])) + { ret = 2; + goto done; + } + lambda = cost[a] + (pi[i] - pi[j]); + if (x[a] >= low[a] && lambda < 0) + if (delta == 0 || delta > - lambda) delta = - lambda; + } + } + if (delta == 0) + { /* there is no feasible circulation */ + ret = 1; + goto done; + } + /* increase potentials of all unlabelled nodes */ + for (i = 1; i <= nv; i++) + { if (link[i] == 0) + { if (overflow(pi[i], delta)) + { ret = 2; + goto done; + } + pi[i] += delta; + } + } + goto loop; +brkt: /* BREAKTHROUGH */ + /* walk through arcs of the cycle (t, a, s, ..., t) found in the + reverse order and determine maximal change of the flow */ + delta = 0; + for (j = t;; j = i) + { /* arc a immediately precedes node j in the cycle */ + a = link[j]; + if (head[a] == j) + { /* a = i->j is a forward arc of the cycle */ + i = tail[a]; + lambda = cost[a] + (pi[i] - pi[j]); + if (lambda > 0 && x[a] < low[a]) + { /* x[a] may be increased until its lower bound */ + temp = low[a] - x[a]; + } + else if (lambda <= 0 && x[a] < cap[a]) + { /* x[a] may be increased until its upper bound */ + temp = cap[a] - x[a]; + } + else + xassert(a != a); + } + else if (tail[a] == j) + { /* a = i<-j is a backward arc of the cycle */ + i = head[a]; + lambda = cost[a] + (pi[j] - pi[i]); + if (lambda < 0 && x[a] > cap[a]) + { /* x[a] may be decreased until its upper bound */ + temp = x[a] - cap[a]; + } + else if (lambda >= 0 && x[a] > low[a]) + { /* x[a] may be decreased until its lower bound */ + temp = x[a] - low[a]; + } + else + xassert(a != a); + } + else + xassert(a != a); + if (delta == 0 || delta > temp) delta = temp; + /* check for end of the cycle */ + if (i == t) break; + } + xassert(delta > 0); + /* increase the flow along the cycle */ + for (j = t;; j = i) + { /* arc a immediately precedes node j in the cycle */ + a = link[j]; + if (head[a] == j) + { /* a = i->j is a forward arc of the cycle */ + i = tail[a]; + /* overflow cannot occur */ + x[a] += delta; + } + else if (tail[a] == j) + { /* a = i<-j is a backward arc of the cycle */ + i = head[a]; + /* overflow cannot occur */ + x[a] -= delta; + } + else + xassert(a != a); + /* check for end of the cycle */ + if (i == t) break; + } + goto loop; +done: /* free working arrays */ + xfree(ptr); + xfree(arc); + xfree(link); + xfree(list); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet07.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet07.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,222 @@ +/* glpnet07.c (Ford-Fulkerson algorithm) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glpnet.h" + +/*********************************************************************** +* NAME +* +* ffalg - Ford-Fulkerson algorithm +* +* SYNOPSIS +* +* #include "glpnet.h" +* void ffalg(int nv, int na, const int tail[], const int head[], +* int s, int t, const int cap[], int x[], char cut[]); +* +* DESCRIPTION +* +* The routine ffalg implements the Ford-Fulkerson algorithm to find a +* maximal flow in the specified flow network. +* +* INPUT PARAMETERS +* +* nv is the number of nodes, nv >= 2. +* +* na is the number of arcs, na >= 0. +* +* tail[a], a = 1,...,na, is the index of tail node of arc a. +* +* head[a], a = 1,...,na, is the index of head node of arc a. +* +* s is the source node index, 1 <= s <= nv. +* +* t is the sink node index, 1 <= t <= nv, t != s. +* +* cap[a], a = 1,...,na, is the capacity of arc a, cap[a] >= 0. +* +* NOTE: Multiple arcs are allowed, but self-loops are not allowed. +* +* OUTPUT PARAMETERS +* +* x[a], a = 1,...,na, is optimal value of the flow through arc a. +* +* cut[i], i = 1,...,nv, is 1 if node i is labelled, and 0 otherwise. +* The set of arcs, whose one endpoint is labelled and other is not, +* defines the minimal cut corresponding to the maximal flow found. +* If the parameter cut is NULL, the cut information are not stored. +* +* REFERENCES +* +* L.R.Ford, Jr., and D.R.Fulkerson, "Flows in Networks," The RAND +* Corp., Report R-375-PR (August 1962), Chap. I "Static Maximal Flow," +* pp.30-33. */ + +void ffalg(int nv, int na, const int tail[], const int head[], + int s, int t, const int cap[], int x[], char cut[]) +{ int a, delta, i, j, k, pos1, pos2, temp, + *ptr, *arc, *link, *list; + /* sanity checks */ + xassert(nv >= 2); + xassert(na >= 0); + xassert(1 <= s && s <= nv); + xassert(1 <= t && t <= nv); + xassert(s != t); + for (a = 1; a <= na; a++) + { i = tail[a], j = head[a]; + xassert(1 <= i && i <= nv); + xassert(1 <= j && j <= nv); + xassert(i != j); + xassert(cap[a] >= 0); + } + /* allocate working arrays */ + ptr = xcalloc(1+nv+1, sizeof(int)); + arc = xcalloc(1+na+na, sizeof(int)); + link = xcalloc(1+nv, sizeof(int)); + list = xcalloc(1+nv, sizeof(int)); + /* ptr[i] := (degree of node i) */ + for (i = 1; i <= nv; i++) + ptr[i] = 0; + for (a = 1; a <= na; a++) + { ptr[tail[a]]++; + ptr[head[a]]++; + } + /* initialize arc pointers */ + ptr[1]++; + for (i = 1; i < nv; i++) + ptr[i+1] += ptr[i]; + ptr[nv+1] = ptr[nv]; + /* build arc lists */ + for (a = 1; a <= na; a++) + { arc[--ptr[tail[a]]] = a; + arc[--ptr[head[a]]] = a; + } + xassert(ptr[1] == 1); + xassert(ptr[nv+1] == na+na+1); + /* now the indices of arcs incident to node i are stored in + locations arc[ptr[i]], arc[ptr[i]+1], ..., arc[ptr[i+1]-1] */ + /* initialize arc flows */ + for (a = 1; a <= na; a++) + x[a] = 0; +loop: /* main loop starts here */ + /* build augmenting tree rooted at s */ + /* link[i] = 0 means that node i is not labelled yet; + link[i] = a means that arc a immediately precedes node i */ + /* initially node s is labelled as the root */ + for (i = 1; i <= nv; i++) + link[i] = 0; + link[s] = -1, list[1] = s, pos1 = pos2 = 1; + /* breadth first search */ + while (pos1 <= pos2) + { /* dequeue node i */ + i = list[pos1++]; + /* consider all arcs incident to node i */ + for (k = ptr[i]; k < ptr[i+1]; k++) + { a = arc[k]; + if (tail[a] == i) + { /* a = i->j is a forward arc from s to t */ + j = head[a]; + /* if node j has been labelled, skip the arc */ + if (link[j] != 0) continue; + /* if the arc does not allow increasing the flow through + it, skip the arc */ + if (x[a] == cap[a]) continue; + } + else if (head[a] == i) + { /* a = i<-j is a backward arc from s to t */ + j = tail[a]; + /* if node j has been labelled, skip the arc */ + if (link[j] != 0) continue; + /* if the arc does not allow decreasing the flow through + it, skip the arc */ + if (x[a] == 0) continue; + } + else + xassert(a != a); + /* label node j and enqueue it */ + link[j] = a, list[++pos2] = j; + /* check for breakthrough */ + if (j == t) goto brkt; + } + } + /* NONBREAKTHROUGH */ + /* no augmenting path exists; current flow is maximal */ + /* store minimal cut information, if necessary */ + if (cut != NULL) + { for (i = 1; i <= nv; i++) + cut[i] = (char)(link[i] != 0); + } + goto done; +brkt: /* BREAKTHROUGH */ + /* walk through arcs of the augmenting path (s, ..., t) found in + the reverse order and determine maximal change of the flow */ + delta = 0; + for (j = t; j != s; j = i) + { /* arc a immediately precedes node j in the path */ + a = link[j]; + if (head[a] == j) + { /* a = i->j is a forward arc of the cycle */ + i = tail[a]; + /* x[a] may be increased until its upper bound */ + temp = cap[a] - x[a]; + } + else if (tail[a] == j) + { /* a = i<-j is a backward arc of the cycle */ + i = head[a]; + /* x[a] may be decreased until its lower bound */ + temp = x[a]; + } + else + xassert(a != a); + if (delta == 0 || delta > temp) delta = temp; + } + xassert(delta > 0); + /* increase the flow along the path */ + for (j = t; j != s; j = i) + { /* arc a immediately precedes node j in the path */ + a = link[j]; + if (head[a] == j) + { /* a = i->j is a forward arc of the cycle */ + i = tail[a]; + x[a] += delta; + } + else if (tail[a] == j) + { /* a = i<-j is a backward arc of the cycle */ + i = head[a]; + x[a] -= delta; + } + else + xassert(a != a); + } + goto loop; +done: /* free working arrays */ + xfree(ptr); + xfree(arc); + xfree(link); + xfree(list); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet08.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet08.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,241 @@ +/* glpnet08.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Two subroutines sub() and wclique() below are intended to find a +* maximum weight clique in a given undirected graph. These subroutines +* are slightly modified version of the program WCLIQUE developed by +* Patric Ostergard and based +* on ideas from the article "P. R. J. Ostergard, A new algorithm for +* the maximum-weight clique problem, submitted for publication", which +* in turn is a generalization of the algorithm for unweighted graphs +* presented in "P. R. J. Ostergard, A fast algorithm for the maximum +* clique problem, submitted for publication". +* +* USED WITH PERMISSION OF THE AUTHOR OF THE ORIGINAL CODE. +* +* Changes were made by Andrew Makhorin . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glpnet.h" + +/*********************************************************************** +* NAME +* +* wclique - find maximum weight clique with Ostergard's algorithm +* +* SYNOPSIS +* +* int wclique(int n, const int w[], const unsigned char a[], +* int ind[]); +* +* DESCRIPTION +* +* The routine wclique finds a maximum weight clique in an undirected +* graph with Ostergard's algorithm. +* +* INPUT PARAMETERS +* +* n is the number of vertices, n > 0. +* +* w[i], i = 1,...,n, is a weight of vertex i. +* +* a[*] is the strict (without main diagonal) lower triangle of the +* graph adjacency matrix in packed format. +* +* OUTPUT PARAMETER +* +* ind[k], k = 1,...,size, is the number of a vertex included in the +* clique found, 1 <= ind[k] <= n, where size is the number of vertices +* in the clique returned on exit. +* +* RETURNS +* +* The routine returns the clique size, i.e. the number of vertices in +* the clique. */ + +struct csa +{ /* common storage area */ + int n; + /* number of vertices */ + const int *wt; /* int wt[0:n-1]; */ + /* weights */ + const unsigned char *a; + /* adjacency matrix (packed lower triangle without main diag.) */ + int record; + /* weight of best clique */ + int rec_level; + /* number of vertices in best clique */ + int *rec; /* int rec[0:n-1]; */ + /* best clique so far */ + int *clique; /* int clique[0:n-1]; */ + /* table for pruning */ + int *set; /* int set[0:n-1]; */ + /* current clique */ +}; + +#define n (csa->n) +#define wt (csa->wt) +#define a (csa->a) +#define record (csa->record) +#define rec_level (csa->rec_level) +#define rec (csa->rec) +#define clique (csa->clique) +#define set (csa->set) + +#if 0 +static int is_edge(struct csa *csa, int i, int j) +{ /* if there is arc (i,j), the routine returns true; otherwise + false; 0 <= i, j < n */ + int k; + xassert(0 <= i && i < n); + xassert(0 <= j && j < n); + if (i == j) return 0; + if (i < j) k = i, i = j, j = k; + k = (i * (i - 1)) / 2 + j; + return a[k / CHAR_BIT] & + (unsigned char)(1 << ((CHAR_BIT - 1) - k % CHAR_BIT)); +} +#else +#define is_edge(csa, i, j) ((i) == (j) ? 0 : \ + (i) > (j) ? is_edge1(i, j) : is_edge1(j, i)) +#define is_edge1(i, j) is_edge2(((i) * ((i) - 1)) / 2 + (j)) +#define is_edge2(k) (a[(k) / CHAR_BIT] & \ + (unsigned char)(1 << ((CHAR_BIT - 1) - (k) % CHAR_BIT))) +#endif + +static void sub(struct csa *csa, int ct, int table[], int level, + int weight, int l_weight) +{ int i, j, k, curr_weight, left_weight, *p1, *p2, *newtable; + newtable = xcalloc(n, sizeof(int)); + if (ct <= 0) + { /* 0 or 1 elements left; include these */ + if (ct == 0) + { set[level++] = table[0]; + weight += l_weight; + } + if (weight > record) + { record = weight; + rec_level = level; + for (i = 0; i < level; i++) rec[i] = set[i]; + } + goto done; + } + for (i = ct; i >= 0; i--) + { if ((level == 0) && (i < ct)) goto done; + k = table[i]; + if ((level > 0) && (clique[k] <= (record - weight))) + goto done; /* prune */ + set[level] = k; + curr_weight = weight + wt[k]; + l_weight -= wt[k]; + if (l_weight <= (record - curr_weight)) + goto done; /* prune */ + p1 = newtable; + p2 = table; + left_weight = 0; + while (p2 < table + i) + { j = *p2++; + if (is_edge(csa, j, k)) + { *p1++ = j; + left_weight += wt[j]; + } + } + if (left_weight <= (record - curr_weight)) continue; + sub(csa, p1 - newtable - 1, newtable, level + 1, curr_weight, + left_weight); + } +done: xfree(newtable); + return; +} + +int wclique(int _n, const int w[], const unsigned char _a[], int ind[]) +{ struct csa _csa, *csa = &_csa; + int i, j, p, max_wt, max_nwt, wth, *used, *nwt, *pos; + glp_long timer; + n = _n; + xassert(n > 0); + wt = &w[1]; + a = _a; + record = 0; + rec_level = 0; + rec = &ind[1]; + clique = xcalloc(n, sizeof(int)); + set = xcalloc(n, sizeof(int)); + used = xcalloc(n, sizeof(int)); + nwt = xcalloc(n, sizeof(int)); + pos = xcalloc(n, sizeof(int)); + /* start timer */ + timer = xtime(); + /* order vertices */ + for (i = 0; i < n; i++) + { nwt[i] = 0; + for (j = 0; j < n; j++) + if (is_edge(csa, i, j)) nwt[i] += wt[j]; + } + for (i = 0; i < n; i++) + used[i] = 0; + for (i = n-1; i >= 0; i--) + { max_wt = -1; + max_nwt = -1; + for (j = 0; j < n; j++) + { if ((!used[j]) && ((wt[j] > max_wt) || (wt[j] == max_wt + && nwt[j] > max_nwt))) + { max_wt = wt[j]; + max_nwt = nwt[j]; + p = j; + } + } + pos[i] = p; + used[p] = 1; + for (j = 0; j < n; j++) + if ((!used[j]) && (j != p) && (is_edge(csa, p, j))) + nwt[j] -= wt[p]; + } + /* main routine */ + wth = 0; + for (i = 0; i < n; i++) + { wth += wt[pos[i]]; + sub(csa, i, pos, 0, 0, wth); + clique[pos[i]] = record; + if (xdifftime(xtime(), timer) >= 5.0 - 0.001) + { /* print current record and reset timer */ + xprintf("level = %d (%d); best = %d\n", i+1, n, record); + timer = xtime(); + } + } + xfree(clique); + xfree(set); + xfree(used); + xfree(nwt); + xfree(pos); + /* return the solution found */ + for (i = 1; i <= rec_level; i++) ind[i]++; + return rec_level; +} + +#undef n +#undef wt +#undef a +#undef record +#undef rec_level +#undef rec +#undef clique +#undef set + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnet09.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnet09.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,235 @@ +/* glpnet09.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" +#include "glpnet.h" + +/*********************************************************************** +* NAME +* +* kellerman - cover edges by cliques with Kellerman's heuristic +* +* SYNOPSIS +* +* #include "glpnet.h" +* int kellerman(int n, int (*func)(void *info, int i, int ind[]), +* void *info, glp_graph *H); +* +* DESCRIPTION +* +* The routine kellerman implements Kellerman's heuristic algorithm +* to find a minimal set of cliques which cover all edges of specified +* graph G = (V, E). +* +* The parameter n specifies the number of vertices |V|, n >= 0. +* +* Formal routine func specifies the set of edges E in the following +* way. Running the routine kellerman calls the routine func and passes +* to it parameter i, which is the number of some vertex, 1 <= i <= n. +* In response the routine func should store numbers of all vertices +* adjacent to vertex i to locations ind[1], ind[2], ..., ind[len] and +* return the value of len, which is the number of adjacent vertices, +* 0 <= len <= n. Self-loops are allowed, but ignored. Multiple edges +* are not allowed. +* +* The parameter info is a transit pointer (magic cookie) passed to the +* formal routine func as its first parameter. +* +* The result provided by the routine kellerman is the bipartite graph +* H = (V union C, F), which defines the covering found. (The program +* object of type glp_graph specified by the parameter H should be +* previously created with the routine glp_create_graph. On entry the +* routine kellerman erases the content of this object with the routine +* glp_erase_graph.) Vertices of first part V correspond to vertices of +* the graph G and have the same ordinal numbers 1, 2, ..., n. Vertices +* of second part C correspond to cliques and have ordinal numbers +* n+1, n+2, ..., n+k, where k is the total number of cliques in the +* edge covering found. Every edge f in F in the program object H is +* represented as arc f = (i->j), where i in V and j in C, which means +* that vertex i of the graph G is in clique C[j], 1 <= j <= k. (Thus, +* if two vertices of the graph G are in the same clique, these vertices +* are adjacent in G, and corresponding edge is covered by that clique.) +* +* RETURNS +* +* The routine Kellerman returns k, the total number of cliques in the +* edge covering found. +* +* REFERENCE +* +* For more details see: glpk/doc/notes/keller.pdf (in Russian). */ + +struct set +{ /* set of vertices */ + int size; + /* size (cardinality) of the set, 0 <= card <= n */ + int *list; /* int list[1+n]; */ + /* the set contains vertices list[1,...,size] */ + int *pos; /* int pos[1+n]; */ + /* pos[i] > 0 means that vertex i is in the set and + list[pos[i]] = i; pos[i] = 0 means that vertex i is not in + the set */ +}; + +int kellerman(int n, int (*func)(void *info, int i, int ind[]), + void *info, void /* glp_graph */ *H_) +{ glp_graph *H = H_; + struct set W_, *W = &W_, V_, *V = &V_; + glp_arc *a; + int i, j, k, m, t, len, card, best; + xassert(n >= 0); + /* H := (V, 0; 0), where V is the set of vertices of graph G */ + glp_erase_graph(H, H->v_size, H->a_size); + glp_add_vertices(H, n); + /* W := 0 */ + W->size = 0; + W->list = xcalloc(1+n, sizeof(int)); + W->pos = xcalloc(1+n, sizeof(int)); + memset(&W->pos[1], 0, sizeof(int) * n); + /* V := 0 */ + V->size = 0; + V->list = xcalloc(1+n, sizeof(int)); + V->pos = xcalloc(1+n, sizeof(int)); + memset(&V->pos[1], 0, sizeof(int) * n); + /* main loop */ + for (i = 1; i <= n; i++) + { /* W must be empty */ + xassert(W->size == 0); + /* W := { j : i > j and (i,j) in E } */ + len = func(info, i, W->list); + xassert(0 <= len && len <= n); + for (t = 1; t <= len; t++) + { j = W->list[t]; + xassert(1 <= j && j <= n); + if (j >= i) continue; + xassert(W->pos[j] == 0); + W->list[++W->size] = j, W->pos[j] = W->size; + } + /* on i-th iteration we need to cover edges (i,j) for all + j in W */ + /* if W is empty, it is a special case */ + if (W->size == 0) + { /* set k := k + 1 and create new clique C[k] = { i } */ + k = glp_add_vertices(H, 1) - n; + glp_add_arc(H, i, n + k); + continue; + } + /* try to include vertex i into existing cliques */ + /* V must be empty */ + xassert(V->size == 0); + /* k is the number of cliques found so far */ + k = H->nv - n; + for (m = 1; m <= k; m++) + { /* do while V != W; since here V is within W, we can use + equivalent condition: do while |V| < |W| */ + if (V->size == W->size) break; + /* check if C[m] is within W */ + for (a = H->v[n + m]->in; a != NULL; a = a->h_next) + { j = a->tail->i; + if (W->pos[j] == 0) break; + } + if (a != NULL) continue; + /* C[m] is within W, expand clique C[m] with vertex i */ + /* C[m] := C[m] union {i} */ + glp_add_arc(H, i, n + m); + /* V is a set of vertices whose incident edges are already + covered by existing cliques */ + /* V := V union C[m] */ + for (a = H->v[n + m]->in; a != NULL; a = a->h_next) + { j = a->tail->i; + if (V->pos[j] == 0) + V->list[++V->size] = j, V->pos[j] = V->size; + } + } + /* remove from set W the vertices whose incident edges are + already covered by existing cliques */ + /* W := W \ V, V := 0 */ + for (t = 1; t <= V->size; t++) + { j = V->list[t], V->pos[j] = 0; + if (W->pos[j] != 0) + { /* remove vertex j from W */ + if (W->pos[j] != W->size) + { int jj = W->list[W->size]; + W->list[W->pos[j]] = jj; + W->pos[jj] = W->pos[j]; + } + W->size--, W->pos[j] = 0; + } + } + V->size = 0; + /* now set W contains only vertices whose incident edges are + still not covered by existing cliques; create new cliques + to cover remaining edges until set W becomes empty */ + while (W->size > 0) + { /* find clique C[m], 1 <= m <= k, which shares maximal + number of vertices with W; to break ties choose clique + having smallest number m */ + m = 0, best = -1; + k = H->nv - n; + for (t = 1; t <= k; t++) + { /* compute cardinality of intersection of W and C[t] */ + card = 0; + for (a = H->v[n + t]->in; a != NULL; a = a->h_next) + { j = a->tail->i; + if (W->pos[j] != 0) card++; + } + if (best < card) + m = t, best = card; + } + xassert(m > 0); + /* set k := k + 1 and create new clique: + C[k] := (W intersect C[m]) union { i }, which covers all + edges incident to vertices from (W intersect C[m]) */ + k = glp_add_vertices(H, 1) - n; + for (a = H->v[n + m]->in; a != NULL; a = a->h_next) + { j = a->tail->i; + if (W->pos[j] != 0) + { /* vertex j is in both W and C[m]; include it in new + clique C[k] */ + glp_add_arc(H, j, n + k); + /* remove vertex j from W, since edge (i,j) will be + covered by new clique C[k] */ + if (W->pos[j] != W->size) + { int jj = W->list[W->size]; + W->list[W->pos[j]] = jj; + W->pos[jj] = W->pos[j]; + } + W->size--, W->pos[j] = 0; + } + } + /* include vertex i to new clique C[k] to cover edges (i,j) + incident to all vertices j just removed from W */ + glp_add_arc(H, i, n + k); + } + } + /* free working arrays */ + xfree(W->list); + xfree(W->pos); + xfree(V->list); + xfree(V->pos); + /* return the number of cliques in the edge covering found */ + return H->nv - n; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnpp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnpp.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,520 @@ +/* glpnpp.h (LP/MIP preprocessor) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPNPP_H +#define GLPNPP_H + +#include "glpapi.h" + +typedef struct NPP NPP; +typedef struct NPPROW NPPROW; +typedef struct NPPCOL NPPCOL; +typedef struct NPPAIJ NPPAIJ; +typedef struct NPPTSE NPPTSE; +typedef struct NPPLFE NPPLFE; + +struct NPP +{ /* LP/MIP preprocessor workspace */ + /*--------------------------------------------------------------*/ + /* original problem segment */ + int orig_dir; + /* optimization direction flag: + GLP_MIN - minimization + GLP_MAX - maximization */ + int orig_m; + /* number of rows */ + int orig_n; + /* number of columns */ + int orig_nnz; + /* number of non-zero constraint coefficients */ + /*--------------------------------------------------------------*/ + /* transformed problem segment (always minimization) */ + DMP *pool; + /* memory pool to store problem components */ + char *name; + /* problem name (1 to 255 chars); NULL means no name is assigned + to the problem */ + char *obj; + /* objective function name (1 to 255 chars); NULL means no name + is assigned to the objective function */ + double c0; + /* constant term of the objective function */ + int nrows; + /* number of rows introduced into the problem; this count + increases by one every time a new row is added and never + decreases; thus, actual number of rows may be less than nrows + due to row deletions */ + int ncols; + /* number of columns introduced into the problem; this count + increases by one every time a new column is added and never + decreases; thus, actual number of column may be less than + ncols due to column deletions */ + NPPROW *r_head; + /* pointer to the beginning of the row list */ + NPPROW *r_tail; + /* pointer to the end of the row list */ + NPPCOL *c_head; + /* pointer to the beginning of the column list */ + NPPCOL *c_tail; + /* pointer to the end of the column list */ + /*--------------------------------------------------------------*/ + /* transformation history */ + DMP *stack; + /* memory pool to store transformation entries */ + NPPTSE *top; + /* pointer to most recent transformation entry */ +#if 0 /* 16/XII-2009 */ + int count[1+25]; + /* transformation statistics */ +#endif + /*--------------------------------------------------------------*/ + /* resultant (preprocessed) problem segment */ + int m; + /* number of rows */ + int n; + /* number of columns */ + int nnz; + /* number of non-zero constraint coefficients */ + int *row_ref; /* int row_ref[1+m]; */ + /* row_ref[i], 1 <= i <= m, is the reference number assigned to + a row, which is i-th row of the resultant problem */ + int *col_ref; /* int col_ref[1+n]; */ + /* col_ref[j], 1 <= j <= n, is the reference number assigned to + a column, which is j-th column of the resultant problem */ + /*--------------------------------------------------------------*/ + /* recovered solution segment */ + int sol; + /* solution indicator: + GLP_SOL - basic solution + GLP_IPT - interior-point solution + GLP_MIP - mixed integer solution */ + int scaling; + /* scaling option: + GLP_OFF - scaling is disabled + GLP_ON - scaling is enabled */ + int p_stat; + /* status of primal basic solution: + GLP_UNDEF - primal solution is undefined + GLP_FEAS - primal solution is feasible + GLP_INFEAS - primal solution is infeasible + GLP_NOFEAS - no primal feasible solution exists */ + int d_stat; + /* status of dual basic solution: + GLP_UNDEF - dual solution is undefined + GLP_FEAS - dual solution is feasible + GLP_INFEAS - dual solution is infeasible + GLP_NOFEAS - no dual feasible solution exists */ + int t_stat; + /* status of interior-point solution: + GLP_UNDEF - interior solution is undefined + GLP_OPT - interior solution is optimal */ + int i_stat; + /* status of mixed integer solution: + GLP_UNDEF - integer solution is undefined + GLP_OPT - integer solution is optimal + GLP_FEAS - integer solution is feasible + GLP_NOFEAS - no integer solution exists */ + char *r_stat; /* char r_stat[1+nrows]; */ + /* r_stat[i], 1 <= i <= nrows, is status of i-th row: + GLP_BS - inactive constraint + GLP_NL - active constraint on lower bound + GLP_NU - active constraint on upper bound + GLP_NF - active free row + GLP_NS - active equality constraint */ + char *c_stat; /* char c_stat[1+nrows]; */ + /* c_stat[j], 1 <= j <= nrows, is status of j-th column: + GLP_BS - basic variable + GLP_NL - non-basic variable on lower bound + GLP_NU - non-basic variable on upper bound + GLP_NF - non-basic free variable + GLP_NS - non-basic fixed variable */ + double *r_pi; /* double r_pi[1+nrows]; */ + /* r_pi[i], 1 <= i <= nrows, is Lagrange multiplier (dual value) + for i-th row (constraint) */ + double *c_value; /* double c_value[1+ncols]; */ + /* c_value[j], 1 <= j <= ncols, is primal value of j-th column + (structural variable) */ +}; + +struct NPPROW +{ /* row (constraint) */ + int i; + /* reference number assigned to the row, 1 <= i <= nrows */ + char *name; + /* row name (1 to 255 chars); NULL means no name is assigned to + the row */ + double lb; + /* lower bound; -DBL_MAX means the row has no lower bound */ + double ub; + /* upper bound; +DBL_MAX means the row has no upper bound */ + NPPAIJ *ptr; + /* pointer to the linked list of constraint coefficients */ + int temp; + /* working field used by preprocessor routines */ + NPPROW *prev; + /* pointer to previous row in the row list */ + NPPROW *next; + /* pointer to next row in the row list */ +}; + +struct NPPCOL +{ /* column (variable) */ + int j; + /* reference number assigned to the column, 1 <= j <= ncols */ + char *name; + /* column name (1 to 255 chars); NULL means no name is assigned + to the column */ + char is_int; + /* 0 means continuous variable; 1 means integer variable */ + double lb; + /* lower bound; -DBL_MAX means the column has no lower bound */ + double ub; + /* upper bound; +DBL_MAX means the column has no upper bound */ + double coef; + /* objective coefficient */ + NPPAIJ *ptr; + /* pointer to the linked list of constraint coefficients */ + int temp; + /* working field used by preprocessor routines */ +#if 1 /* 28/XII-2009 */ + union + { double ll; + /* implied column lower bound */ + int pos; + /* vertex ordinal number corresponding to this binary column + in the conflict graph (0, if the vertex does not exist) */ + } ll; + union + { double uu; + /* implied column upper bound */ + int neg; + /* vertex ordinal number corresponding to complement of this + binary column in the conflict graph (0, if the vertex does + not exist) */ + } uu; +#endif + NPPCOL *prev; + /* pointer to previous column in the column list */ + NPPCOL *next; + /* pointer to next column in the column list */ +}; + +struct NPPAIJ +{ /* constraint coefficient */ + NPPROW *row; + /* pointer to corresponding row */ + NPPCOL *col; + /* pointer to corresponding column */ + double val; + /* (non-zero) coefficient value */ + NPPAIJ *r_prev; + /* pointer to previous coefficient in the same row */ + NPPAIJ *r_next; + /* pointer to next coefficient in the same row */ + NPPAIJ *c_prev; + /* pointer to previous coefficient in the same column */ + NPPAIJ *c_next; + /* pointer to next coefficient in the same column */ +}; + +struct NPPTSE +{ /* transformation stack entry */ + int (*func)(NPP *npp, void *info); + /* pointer to routine performing back transformation */ + void *info; + /* pointer to specific info (depends on the transformation) */ + NPPTSE *link; + /* pointer to another entry created *before* this entry */ +}; + +struct NPPLFE +{ /* linear form element */ + int ref; + /* row/column reference number */ + double val; + /* (non-zero) coefficient value */ + NPPLFE *next; + /* pointer to another element */ +}; + +#define npp_create_wksp _glp_npp_create_wksp +NPP *npp_create_wksp(void); +/* create LP/MIP preprocessor workspace */ + +#define npp_insert_row _glp_npp_insert_row +void npp_insert_row(NPP *npp, NPPROW *row, int where); +/* insert row to the row list */ + +#define npp_remove_row _glp_npp_remove_row +void npp_remove_row(NPP *npp, NPPROW *row); +/* remove row from the row list */ + +#define npp_activate_row _glp_npp_activate_row +void npp_activate_row(NPP *npp, NPPROW *row); +/* make row active */ + +#define npp_deactivate_row _glp_npp_deactivate_row +void npp_deactivate_row(NPP *npp, NPPROW *row); +/* make row inactive */ + +#define npp_insert_col _glp_npp_insert_col +void npp_insert_col(NPP *npp, NPPCOL *col, int where); +/* insert column to the column list */ + +#define npp_remove_col _glp_npp_remove_col +void npp_remove_col(NPP *npp, NPPCOL *col); +/* remove column from the column list */ + +#define npp_activate_col _glp_npp_activate_col +void npp_activate_col(NPP *npp, NPPCOL *col); +/* make column active */ + +#define npp_deactivate_col _glp_npp_deactivate_col +void npp_deactivate_col(NPP *npp, NPPCOL *col); +/* make column inactive */ + +#define npp_add_row _glp_npp_add_row +NPPROW *npp_add_row(NPP *npp); +/* add new row to the current problem */ + +#define npp_add_col _glp_npp_add_col +NPPCOL *npp_add_col(NPP *npp); +/* add new column to the current problem */ + +#define npp_add_aij _glp_npp_add_aij +NPPAIJ *npp_add_aij(NPP *npp, NPPROW *row, NPPCOL *col, double val); +/* add new element to the constraint matrix */ + +#define npp_row_nnz _glp_npp_row_nnz +int npp_row_nnz(NPP *npp, NPPROW *row); +/* count number of non-zero coefficients in row */ + +#define npp_col_nnz _glp_npp_col_nnz +int npp_col_nnz(NPP *npp, NPPCOL *col); +/* count number of non-zero coefficients in column */ + +#define npp_push_tse _glp_npp_push_tse +void *npp_push_tse(NPP *npp, int (*func)(NPP *npp, void *info), + int size); +/* push new entry to the transformation stack */ + +#define npp_erase_row _glp_npp_erase_row +void npp_erase_row(NPP *npp, NPPROW *row); +/* erase row content to make it empty */ + +#define npp_del_row _glp_npp_del_row +void npp_del_row(NPP *npp, NPPROW *row); +/* remove row from the current problem */ + +#define npp_del_col _glp_npp_del_col +void npp_del_col(NPP *npp, NPPCOL *col); +/* remove column from the current problem */ + +#define npp_del_aij _glp_npp_del_aij +void npp_del_aij(NPP *npp, NPPAIJ *aij); +/* remove element from the constraint matrix */ + +#define npp_load_prob _glp_npp_load_prob +void npp_load_prob(NPP *npp, glp_prob *orig, int names, int sol, + int scaling); +/* load original problem into the preprocessor workspace */ + +#define npp_build_prob _glp_npp_build_prob +void npp_build_prob(NPP *npp, glp_prob *prob); +/* build resultant (preprocessed) problem */ + +#define npp_postprocess _glp_npp_postprocess +void npp_postprocess(NPP *npp, glp_prob *prob); +/* postprocess solution from the resultant problem */ + +#define npp_unload_sol _glp_npp_unload_sol +void npp_unload_sol(NPP *npp, glp_prob *orig); +/* store solution to the original problem */ + +#define npp_delete_wksp _glp_npp_delete_wksp +void npp_delete_wksp(NPP *npp); +/* delete LP/MIP preprocessor workspace */ + +#define npp_error() + +#define npp_free_row _glp_npp_free_row +void npp_free_row(NPP *npp, NPPROW *p); +/* process free (unbounded) row */ + +#define npp_geq_row _glp_npp_geq_row +void npp_geq_row(NPP *npp, NPPROW *p); +/* process row of 'not less than' type */ + +#define npp_leq_row _glp_npp_leq_row +void npp_leq_row(NPP *npp, NPPROW *p); +/* process row of 'not greater than' type */ + +#define npp_free_col _glp_npp_free_col +void npp_free_col(NPP *npp, NPPCOL *q); +/* process free (unbounded) column */ + +#define npp_lbnd_col _glp_npp_lbnd_col +void npp_lbnd_col(NPP *npp, NPPCOL *q); +/* process column with (non-zero) lower bound */ + +#define npp_ubnd_col _glp_npp_ubnd_col +void npp_ubnd_col(NPP *npp, NPPCOL *q); +/* process column with upper bound */ + +#define npp_dbnd_col _glp_npp_dbnd_col +void npp_dbnd_col(NPP *npp, NPPCOL *q); +/* process non-negative column with upper bound */ + +#define npp_fixed_col _glp_npp_fixed_col +void npp_fixed_col(NPP *npp, NPPCOL *q); +/* process fixed column */ + +#define npp_make_equality _glp_npp_make_equality +int npp_make_equality(NPP *npp, NPPROW *p); +/* process row with almost identical bounds */ + +#define npp_make_fixed _glp_npp_make_fixed +int npp_make_fixed(NPP *npp, NPPCOL *q); +/* process column with almost identical bounds */ + +#define npp_empty_row _glp_npp_empty_row +int npp_empty_row(NPP *npp, NPPROW *p); +/* process empty row */ + +#define npp_empty_col _glp_npp_empty_col +int npp_empty_col(NPP *npp, NPPCOL *q); +/* process empty column */ + +#define npp_implied_value _glp_npp_implied_value +int npp_implied_value(NPP *npp, NPPCOL *q, double s); +/* process implied column value */ + +#define npp_eq_singlet _glp_npp_eq_singlet +int npp_eq_singlet(NPP *npp, NPPROW *p); +/* process row singleton (equality constraint) */ + +#define npp_implied_lower _glp_npp_implied_lower +int npp_implied_lower(NPP *npp, NPPCOL *q, double l); +/* process implied column lower bound */ + +#define npp_implied_upper _glp_npp_implied_upper +int npp_implied_upper(NPP *npp, NPPCOL *q, double u); +/* process implied upper bound of column */ + +#define npp_ineq_singlet _glp_npp_ineq_singlet +int npp_ineq_singlet(NPP *npp, NPPROW *p); +/* process row singleton (inequality constraint) */ + +#define npp_implied_slack _glp_npp_implied_slack +void npp_implied_slack(NPP *npp, NPPCOL *q); +/* process column singleton (implied slack variable) */ + +#define npp_implied_free _glp_npp_implied_free +int npp_implied_free(NPP *npp, NPPCOL *q); +/* process column singleton (implied free variable) */ + +#define npp_eq_doublet _glp_npp_eq_doublet +NPPCOL *npp_eq_doublet(NPP *npp, NPPROW *p); +/* process row doubleton (equality constraint) */ + +#define npp_forcing_row _glp_npp_forcing_row +int npp_forcing_row(NPP *npp, NPPROW *p, int at); +/* process forcing row */ + +#define npp_analyze_row _glp_npp_analyze_row +int npp_analyze_row(NPP *npp, NPPROW *p); +/* perform general row analysis */ + +#define npp_inactive_bound _glp_npp_inactive_bound +void npp_inactive_bound(NPP *npp, NPPROW *p, int which); +/* remove row lower/upper inactive bound */ + +#define npp_implied_bounds _glp_npp_implied_bounds +void npp_implied_bounds(NPP *npp, NPPROW *p); +/* determine implied column bounds */ + +#define npp_binarize_prob _glp_npp_binarize_prob +int npp_binarize_prob(NPP *npp); +/* binarize MIP problem */ + +#define npp_is_packing _glp_npp_is_packing +int npp_is_packing(NPP *npp, NPPROW *row); +/* test if constraint is packing inequality */ + +#define npp_hidden_packing _glp_npp_hidden_packing +int npp_hidden_packing(NPP *npp, NPPROW *row); +/* identify hidden packing inequality */ + +#define npp_implied_packing _glp_npp_implied_packing +int npp_implied_packing(NPP *npp, NPPROW *row, int which, + NPPCOL *var[], char set[]); +/* identify implied packing inequality */ + +#define npp_is_covering _glp_npp_is_covering +int npp_is_covering(NPP *npp, NPPROW *row); +/* test if constraint is covering inequality */ + +#define npp_hidden_covering _glp_npp_hidden_covering +int npp_hidden_covering(NPP *npp, NPPROW *row); +/* identify hidden covering inequality */ + +#define npp_is_partitioning _glp_npp_is_partitioning +int npp_is_partitioning(NPP *npp, NPPROW *row); +/* test if constraint is partitioning equality */ + +#define npp_reduce_ineq_coef _glp_npp_reduce_ineq_coef +int npp_reduce_ineq_coef(NPP *npp, NPPROW *row); +/* reduce inequality constraint coefficients */ + +#define npp_clean_prob _glp_npp_clean_prob +void npp_clean_prob(NPP *npp); +/* perform initial LP/MIP processing */ + +#define npp_process_row _glp_npp_process_row +int npp_process_row(NPP *npp, NPPROW *row, int hard); +/* perform basic row processing */ + +#define npp_improve_bounds _glp_npp_improve_bounds +int npp_improve_bounds(NPP *npp, NPPROW *row, int flag); +/* improve current column bounds */ + +#define npp_process_col _glp_npp_process_col +int npp_process_col(NPP *npp, NPPCOL *col); +/* perform basic column processing */ + +#define npp_process_prob _glp_npp_process_prob +int npp_process_prob(NPP *npp, int hard); +/* perform basic LP/MIP processing */ + +#define npp_simplex _glp_npp_simplex +int npp_simplex(NPP *npp, const glp_smcp *parm); +/* process LP prior to applying primal/dual simplex method */ + +#define npp_integer _glp_npp_integer +int npp_integer(NPP *npp, const glp_iocp *parm); +/* process MIP prior to applying branch-and-bound method */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnpp01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnpp01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,927 @@ +/* glpnpp01.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpnpp.h" + +NPP *npp_create_wksp(void) +{ /* create LP/MIP preprocessor workspace */ + NPP *npp; + npp = xmalloc(sizeof(NPP)); + npp->orig_dir = 0; + npp->orig_m = npp->orig_n = npp->orig_nnz = 0; + npp->pool = dmp_create_pool(); + npp->name = npp->obj = NULL; + npp->c0 = 0.0; + npp->nrows = npp->ncols = 0; + npp->r_head = npp->r_tail = NULL; + npp->c_head = npp->c_tail = NULL; + npp->stack = dmp_create_pool(); + npp->top = NULL; +#if 0 /* 16/XII-2009 */ + memset(&npp->count, 0, sizeof(npp->count)); +#endif + npp->m = npp->n = npp->nnz = 0; + npp->row_ref = npp->col_ref = NULL; + npp->sol = npp->scaling = 0; + npp->p_stat = npp->d_stat = npp->t_stat = npp->i_stat = 0; + npp->r_stat = NULL; + /*npp->r_prim =*/ npp->r_pi = NULL; + npp->c_stat = NULL; + npp->c_value = /*npp->c_dual =*/ NULL; + return npp; +} + +void npp_insert_row(NPP *npp, NPPROW *row, int where) +{ /* insert row to the row list */ + if (where == 0) + { /* insert row to the beginning of the row list */ + row->prev = NULL; + row->next = npp->r_head; + if (row->next == NULL) + npp->r_tail = row; + else + row->next->prev = row; + npp->r_head = row; + } + else + { /* insert row to the end of the row list */ + row->prev = npp->r_tail; + row->next = NULL; + if (row->prev == NULL) + npp->r_head = row; + else + row->prev->next = row; + npp->r_tail = row; + } + return; +} + +void npp_remove_row(NPP *npp, NPPROW *row) +{ /* remove row from the row list */ + if (row->prev == NULL) + npp->r_head = row->next; + else + row->prev->next = row->next; + if (row->next == NULL) + npp->r_tail = row->prev; + else + row->next->prev = row->prev; + return; +} + +void npp_activate_row(NPP *npp, NPPROW *row) +{ /* make row active */ + if (!row->temp) + { row->temp = 1; + /* move the row to the beginning of the row list */ + npp_remove_row(npp, row); + npp_insert_row(npp, row, 0); + } + return; +} + +void npp_deactivate_row(NPP *npp, NPPROW *row) +{ /* make row inactive */ + if (row->temp) + { row->temp = 0; + /* move the row to the end of the row list */ + npp_remove_row(npp, row); + npp_insert_row(npp, row, 1); + } + return; +} + +void npp_insert_col(NPP *npp, NPPCOL *col, int where) +{ /* insert column to the column list */ + if (where == 0) + { /* insert column to the beginning of the column list */ + col->prev = NULL; + col->next = npp->c_head; + if (col->next == NULL) + npp->c_tail = col; + else + col->next->prev = col; + npp->c_head = col; + } + else + { /* insert column to the end of the column list */ + col->prev = npp->c_tail; + col->next = NULL; + if (col->prev == NULL) + npp->c_head = col; + else + col->prev->next = col; + npp->c_tail = col; + } + return; +} + +void npp_remove_col(NPP *npp, NPPCOL *col) +{ /* remove column from the column list */ + if (col->prev == NULL) + npp->c_head = col->next; + else + col->prev->next = col->next; + if (col->next == NULL) + npp->c_tail = col->prev; + else + col->next->prev = col->prev; + return; +} + +void npp_activate_col(NPP *npp, NPPCOL *col) +{ /* make column active */ + if (!col->temp) + { col->temp = 1; + /* move the column to the beginning of the column list */ + npp_remove_col(npp, col); + npp_insert_col(npp, col, 0); + } + return; +} + +void npp_deactivate_col(NPP *npp, NPPCOL *col) +{ /* make column inactive */ + if (col->temp) + { col->temp = 0; + /* move the column to the end of the column list */ + npp_remove_col(npp, col); + npp_insert_col(npp, col, 1); + } + return; +} + +NPPROW *npp_add_row(NPP *npp) +{ /* add new row to the current problem */ + NPPROW *row; + row = dmp_get_atom(npp->pool, sizeof(NPPROW)); + row->i = ++(npp->nrows); + row->name = NULL; + row->lb = -DBL_MAX, row->ub = +DBL_MAX; + row->ptr = NULL; + row->temp = 0; + npp_insert_row(npp, row, 1); + return row; +} + +NPPCOL *npp_add_col(NPP *npp) +{ /* add new column to the current problem */ + NPPCOL *col; + col = dmp_get_atom(npp->pool, sizeof(NPPCOL)); + col->j = ++(npp->ncols); + col->name = NULL; +#if 0 + col->kind = GLP_CV; +#else + col->is_int = 0; +#endif + col->lb = col->ub = col->coef = 0.0; + col->ptr = NULL; + col->temp = 0; + npp_insert_col(npp, col, 1); + return col; +} + +NPPAIJ *npp_add_aij(NPP *npp, NPPROW *row, NPPCOL *col, double val) +{ /* add new element to the constraint matrix */ + NPPAIJ *aij; + aij = dmp_get_atom(npp->pool, sizeof(NPPAIJ)); + aij->row = row; + aij->col = col; + aij->val = val; + aij->r_prev = NULL; + aij->r_next = row->ptr; + aij->c_prev = NULL; + aij->c_next = col->ptr; + if (aij->r_next != NULL) + aij->r_next->r_prev = aij; + if (aij->c_next != NULL) + aij->c_next->c_prev = aij; + row->ptr = col->ptr = aij; + return aij; +} + +int npp_row_nnz(NPP *npp, NPPROW *row) +{ /* count number of non-zero coefficients in row */ + NPPAIJ *aij; + int nnz; + xassert(npp == npp); + nnz = 0; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + nnz++; + return nnz; +} + +int npp_col_nnz(NPP *npp, NPPCOL *col) +{ /* count number of non-zero coefficients in column */ + NPPAIJ *aij; + int nnz; + xassert(npp == npp); + nnz = 0; + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + nnz++; + return nnz; +} + +void *npp_push_tse(NPP *npp, int (*func)(NPP *npp, void *info), + int size) +{ /* push new entry to the transformation stack */ + NPPTSE *tse; + tse = dmp_get_atom(npp->stack, sizeof(NPPTSE)); + tse->func = func; + tse->info = dmp_get_atom(npp->stack, size); + tse->link = npp->top; + npp->top = tse; + return tse->info; +} + +#if 1 /* 23/XII-2009 */ +void npp_erase_row(NPP *npp, NPPROW *row) +{ /* erase row content to make it empty */ + NPPAIJ *aij; + while (row->ptr != NULL) + { aij = row->ptr; + row->ptr = aij->r_next; + if (aij->c_prev == NULL) + aij->col->ptr = aij->c_next; + else + aij->c_prev->c_next = aij->c_next; + if (aij->c_next == NULL) + ; + else + aij->c_next->c_prev = aij->c_prev; + dmp_free_atom(npp->pool, aij, sizeof(NPPAIJ)); + } + return; +} +#endif + +void npp_del_row(NPP *npp, NPPROW *row) +{ /* remove row from the current problem */ +#if 0 /* 23/XII-2009 */ + NPPAIJ *aij; +#endif + if (row->name != NULL) + dmp_free_atom(npp->pool, row->name, strlen(row->name)+1); +#if 0 /* 23/XII-2009 */ + while (row->ptr != NULL) + { aij = row->ptr; + row->ptr = aij->r_next; + if (aij->c_prev == NULL) + aij->col->ptr = aij->c_next; + else + aij->c_prev->c_next = aij->c_next; + if (aij->c_next == NULL) + ; + else + aij->c_next->c_prev = aij->c_prev; + dmp_free_atom(npp->pool, aij, sizeof(NPPAIJ)); + } +#else + npp_erase_row(npp, row); +#endif + npp_remove_row(npp, row); + dmp_free_atom(npp->pool, row, sizeof(NPPROW)); + return; +} + +void npp_del_col(NPP *npp, NPPCOL *col) +{ /* remove column from the current problem */ + NPPAIJ *aij; + if (col->name != NULL) + dmp_free_atom(npp->pool, col->name, strlen(col->name)+1); + while (col->ptr != NULL) + { aij = col->ptr; + col->ptr = aij->c_next; + if (aij->r_prev == NULL) + aij->row->ptr = aij->r_next; + else + aij->r_prev->r_next = aij->r_next; + if (aij->r_next == NULL) + ; + else + aij->r_next->r_prev = aij->r_prev; + dmp_free_atom(npp->pool, aij, sizeof(NPPAIJ)); + } + npp_remove_col(npp, col); + dmp_free_atom(npp->pool, col, sizeof(NPPCOL)); + return; +} + +void npp_del_aij(NPP *npp, NPPAIJ *aij) +{ /* remove element from the constraint matrix */ + if (aij->r_prev == NULL) + aij->row->ptr = aij->r_next; + else + aij->r_prev->r_next = aij->r_next; + if (aij->r_next == NULL) + ; + else + aij->r_next->r_prev = aij->r_prev; + if (aij->c_prev == NULL) + aij->col->ptr = aij->c_next; + else + aij->c_prev->c_next = aij->c_next; + if (aij->c_next == NULL) + ; + else + aij->c_next->c_prev = aij->c_prev; + dmp_free_atom(npp->pool, aij, sizeof(NPPAIJ)); + return; +} + +void npp_load_prob(NPP *npp, glp_prob *orig, int names, int sol, + int scaling) +{ /* load original problem into the preprocessor workspace */ + int m = orig->m; + int n = orig->n; + NPPROW **link; + int i, j; + double dir; + xassert(names == GLP_OFF || names == GLP_ON); + xassert(sol == GLP_SOL || sol == GLP_IPT || sol == GLP_MIP); + xassert(scaling == GLP_OFF || scaling == GLP_ON); + if (sol == GLP_MIP) xassert(!scaling); + npp->orig_dir = orig->dir; + if (npp->orig_dir == GLP_MIN) + dir = +1.0; + else if (npp->orig_dir == GLP_MAX) + dir = -1.0; + else + xassert(npp != npp); + npp->orig_m = m; + npp->orig_n = n; + npp->orig_nnz = orig->nnz; + if (names && orig->name != NULL) + { npp->name = dmp_get_atom(npp->pool, strlen(orig->name)+1); + strcpy(npp->name, orig->name); + } + if (names && orig->obj != NULL) + { npp->obj = dmp_get_atom(npp->pool, strlen(orig->obj)+1); + strcpy(npp->obj, orig->obj); + } + npp->c0 = dir * orig->c0; + /* load rows */ + link = xcalloc(1+m, sizeof(NPPROW *)); + for (i = 1; i <= m; i++) + { GLPROW *rrr = orig->row[i]; + NPPROW *row; + link[i] = row = npp_add_row(npp); + xassert(row->i == i); + if (names && rrr->name != NULL) + { row->name = dmp_get_atom(npp->pool, strlen(rrr->name)+1); + strcpy(row->name, rrr->name); + } + if (!scaling) + { if (rrr->type == GLP_FR) + row->lb = -DBL_MAX, row->ub = +DBL_MAX; + else if (rrr->type == GLP_LO) + row->lb = rrr->lb, row->ub = +DBL_MAX; + else if (rrr->type == GLP_UP) + row->lb = -DBL_MAX, row->ub = rrr->ub; + else if (rrr->type == GLP_DB) + row->lb = rrr->lb, row->ub = rrr->ub; + else if (rrr->type == GLP_FX) + row->lb = row->ub = rrr->lb; + else + xassert(rrr != rrr); + } + else + { double rii = rrr->rii; + if (rrr->type == GLP_FR) + row->lb = -DBL_MAX, row->ub = +DBL_MAX; + else if (rrr->type == GLP_LO) + row->lb = rrr->lb * rii, row->ub = +DBL_MAX; + else if (rrr->type == GLP_UP) + row->lb = -DBL_MAX, row->ub = rrr->ub * rii; + else if (rrr->type == GLP_DB) + row->lb = rrr->lb * rii, row->ub = rrr->ub * rii; + else if (rrr->type == GLP_FX) + row->lb = row->ub = rrr->lb * rii; + else + xassert(rrr != rrr); + } + } + /* load columns and constraint coefficients */ + for (j = 1; j <= n; j++) + { GLPCOL *ccc = orig->col[j]; + GLPAIJ *aaa; + NPPCOL *col; + col = npp_add_col(npp); + xassert(col->j == j); + if (names && ccc->name != NULL) + { col->name = dmp_get_atom(npp->pool, strlen(ccc->name)+1); + strcpy(col->name, ccc->name); + } + if (sol == GLP_MIP) +#if 0 + col->kind = ccc->kind; +#else + col->is_int = (char)(ccc->kind == GLP_IV); +#endif + if (!scaling) + { if (ccc->type == GLP_FR) + col->lb = -DBL_MAX, col->ub = +DBL_MAX; + else if (ccc->type == GLP_LO) + col->lb = ccc->lb, col->ub = +DBL_MAX; + else if (ccc->type == GLP_UP) + col->lb = -DBL_MAX, col->ub = ccc->ub; + else if (ccc->type == GLP_DB) + col->lb = ccc->lb, col->ub = ccc->ub; + else if (ccc->type == GLP_FX) + col->lb = col->ub = ccc->lb; + else + xassert(ccc != ccc); + col->coef = dir * ccc->coef; + for (aaa = ccc->ptr; aaa != NULL; aaa = aaa->c_next) + npp_add_aij(npp, link[aaa->row->i], col, aaa->val); + } + else + { double sjj = ccc->sjj; + if (ccc->type == GLP_FR) + col->lb = -DBL_MAX, col->ub = +DBL_MAX; + else if (ccc->type == GLP_LO) + col->lb = ccc->lb / sjj, col->ub = +DBL_MAX; + else if (ccc->type == GLP_UP) + col->lb = -DBL_MAX, col->ub = ccc->ub / sjj; + else if (ccc->type == GLP_DB) + col->lb = ccc->lb / sjj, col->ub = ccc->ub / sjj; + else if (ccc->type == GLP_FX) + col->lb = col->ub = ccc->lb / sjj; + else + xassert(ccc != ccc); + col->coef = dir * ccc->coef * sjj; + for (aaa = ccc->ptr; aaa != NULL; aaa = aaa->c_next) + npp_add_aij(npp, link[aaa->row->i], col, + aaa->row->rii * aaa->val * sjj); + } + } + xfree(link); + /* keep solution indicator and scaling option */ + npp->sol = sol; + npp->scaling = scaling; + return; +} + +void npp_build_prob(NPP *npp, glp_prob *prob) +{ /* build resultant (preprocessed) problem */ + NPPROW *row; + NPPCOL *col; + NPPAIJ *aij; + int i, j, type, len, *ind; + double dir, *val; + glp_erase_prob(prob); + glp_set_prob_name(prob, npp->name); + glp_set_obj_name(prob, npp->obj); + glp_set_obj_dir(prob, npp->orig_dir); + if (npp->orig_dir == GLP_MIN) + dir = +1.0; + else if (npp->orig_dir == GLP_MAX) + dir = -1.0; + else + xassert(npp != npp); + glp_set_obj_coef(prob, 0, dir * npp->c0); + /* build rows */ + for (row = npp->r_head; row != NULL; row = row->next) + { row->temp = i = glp_add_rows(prob, 1); + glp_set_row_name(prob, i, row->name); + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) + type = GLP_FR; + else if (row->ub == +DBL_MAX) + type = GLP_LO; + else if (row->lb == -DBL_MAX) + type = GLP_UP; + else if (row->lb != row->ub) + type = GLP_DB; + else + type = GLP_FX; + glp_set_row_bnds(prob, i, type, row->lb, row->ub); + } + /* build columns and the constraint matrix */ + ind = xcalloc(1+prob->m, sizeof(int)); + val = xcalloc(1+prob->m, sizeof(double)); + for (col = npp->c_head; col != NULL; col = col->next) + { j = glp_add_cols(prob, 1); + glp_set_col_name(prob, j, col->name); +#if 0 + glp_set_col_kind(prob, j, col->kind); +#else + glp_set_col_kind(prob, j, col->is_int ? GLP_IV : GLP_CV); +#endif + if (col->lb == -DBL_MAX && col->ub == +DBL_MAX) + type = GLP_FR; + else if (col->ub == +DBL_MAX) + type = GLP_LO; + else if (col->lb == -DBL_MAX) + type = GLP_UP; + else if (col->lb != col->ub) + type = GLP_DB; + else + type = GLP_FX; + glp_set_col_bnds(prob, j, type, col->lb, col->ub); + glp_set_obj_coef(prob, j, dir * col->coef); + len = 0; + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + { len++; + ind[len] = aij->row->temp; + val[len] = aij->val; + } + glp_set_mat_col(prob, j, len, ind, val); + } + xfree(ind); + xfree(val); + /* resultant problem has been built */ + npp->m = prob->m; + npp->n = prob->n; + npp->nnz = prob->nnz; + npp->row_ref = xcalloc(1+npp->m, sizeof(int)); + npp->col_ref = xcalloc(1+npp->n, sizeof(int)); + for (row = npp->r_head, i = 0; row != NULL; row = row->next) + npp->row_ref[++i] = row->i; + for (col = npp->c_head, j = 0; col != NULL; col = col->next) + npp->col_ref[++j] = col->j; + /* transformed problem segment is no longer needed */ + dmp_delete_pool(npp->pool), npp->pool = NULL; + npp->name = npp->obj = NULL; + npp->c0 = 0.0; + npp->r_head = npp->r_tail = NULL; + npp->c_head = npp->c_tail = NULL; + return; +} + +void npp_postprocess(NPP *npp, glp_prob *prob) +{ /* postprocess solution from the resultant problem */ + GLPROW *row; + GLPCOL *col; + NPPTSE *tse; + int i, j, k; + double dir; + xassert(npp->orig_dir == prob->dir); + if (npp->orig_dir == GLP_MIN) + dir = +1.0; + else if (npp->orig_dir == GLP_MAX) + dir = -1.0; + else + xassert(npp != npp); + xassert(npp->m == prob->m); + xassert(npp->n == prob->n); + xassert(npp->nnz == prob->nnz); + /* copy solution status */ + if (npp->sol == GLP_SOL) + { npp->p_stat = prob->pbs_stat; + npp->d_stat = prob->dbs_stat; + } + else if (npp->sol == GLP_IPT) + npp->t_stat = prob->ipt_stat; + else if (npp->sol == GLP_MIP) + npp->i_stat = prob->mip_stat; + else + xassert(npp != npp); + /* allocate solution arrays */ + if (npp->sol == GLP_SOL) + { if (npp->r_stat == NULL) + npp->r_stat = xcalloc(1+npp->nrows, sizeof(char)); + for (i = 1; i <= npp->nrows; i++) + npp->r_stat[i] = 0; + if (npp->c_stat == NULL) + npp->c_stat = xcalloc(1+npp->ncols, sizeof(char)); + for (j = 1; j <= npp->ncols; j++) + npp->c_stat[j] = 0; + } +#if 0 + if (npp->r_prim == NULL) + npp->r_prim = xcalloc(1+npp->nrows, sizeof(double)); + for (i = 1; i <= npp->nrows; i++) + npp->r_prim[i] = DBL_MAX; +#endif + if (npp->c_value == NULL) + npp->c_value = xcalloc(1+npp->ncols, sizeof(double)); + for (j = 1; j <= npp->ncols; j++) + npp->c_value[j] = DBL_MAX; + if (npp->sol != GLP_MIP) + { if (npp->r_pi == NULL) + npp->r_pi = xcalloc(1+npp->nrows, sizeof(double)); + for (i = 1; i <= npp->nrows; i++) + npp->r_pi[i] = DBL_MAX; +#if 0 + if (npp->c_dual == NULL) + npp->c_dual = xcalloc(1+npp->ncols, sizeof(double)); + for (j = 1; j <= npp->ncols; j++) + npp->c_dual[j] = DBL_MAX; +#endif + } + /* copy solution components from the resultant problem */ + if (npp->sol == GLP_SOL) + { for (i = 1; i <= npp->m; i++) + { row = prob->row[i]; + k = npp->row_ref[i]; + npp->r_stat[k] = (char)row->stat; + /*npp->r_prim[k] = row->prim;*/ + npp->r_pi[k] = dir * row->dual; + } + for (j = 1; j <= npp->n; j++) + { col = prob->col[j]; + k = npp->col_ref[j]; + npp->c_stat[k] = (char)col->stat; + npp->c_value[k] = col->prim; + /*npp->c_dual[k] = dir * col->dual;*/ + } + } + else if (npp->sol == GLP_IPT) + { for (i = 1; i <= npp->m; i++) + { row = prob->row[i]; + k = npp->row_ref[i]; + /*npp->r_prim[k] = row->pval;*/ + npp->r_pi[k] = dir * row->dval; + } + for (j = 1; j <= npp->n; j++) + { col = prob->col[j]; + k = npp->col_ref[j]; + npp->c_value[k] = col->pval; + /*npp->c_dual[k] = dir * col->dval;*/ + } + } + else if (npp->sol == GLP_MIP) + { +#if 0 + for (i = 1; i <= npp->m; i++) + { row = prob->row[i]; + k = npp->row_ref[i]; + /*npp->r_prim[k] = row->mipx;*/ + } +#endif + for (j = 1; j <= npp->n; j++) + { col = prob->col[j]; + k = npp->col_ref[j]; + npp->c_value[k] = col->mipx; + } + } + else + xassert(npp != npp); + /* perform postprocessing to construct solution to the original + problem */ + for (tse = npp->top; tse != NULL; tse = tse->link) + { xassert(tse->func != NULL); + xassert(tse->func(npp, tse->info) == 0); + } + return; +} + +void npp_unload_sol(NPP *npp, glp_prob *orig) +{ /* store solution to the original problem */ + GLPROW *row; + GLPCOL *col; + int i, j; + double dir; + xassert(npp->orig_dir == orig->dir); + if (npp->orig_dir == GLP_MIN) + dir = +1.0; + else if (npp->orig_dir == GLP_MAX) + dir = -1.0; + else + xassert(npp != npp); + xassert(npp->orig_m == orig->m); + xassert(npp->orig_n == orig->n); + xassert(npp->orig_nnz == orig->nnz); + if (npp->sol == GLP_SOL) + { /* store basic solution */ + orig->valid = 0; + orig->pbs_stat = npp->p_stat; + orig->dbs_stat = npp->d_stat; + orig->obj_val = orig->c0; + orig->some = 0; + for (i = 1; i <= orig->m; i++) + { row = orig->row[i]; + row->stat = npp->r_stat[i]; + if (!npp->scaling) + { /*row->prim = npp->r_prim[i];*/ + row->dual = dir * npp->r_pi[i]; + } + else + { /*row->prim = npp->r_prim[i] / row->rii;*/ + row->dual = dir * npp->r_pi[i] * row->rii; + } + if (row->stat == GLP_BS) + row->dual = 0.0; + else if (row->stat == GLP_NL) + { xassert(row->type == GLP_LO || row->type == GLP_DB); + row->prim = row->lb; + } + else if (row->stat == GLP_NU) + { xassert(row->type == GLP_UP || row->type == GLP_DB); + row->prim = row->ub; + } + else if (row->stat == GLP_NF) + { xassert(row->type == GLP_FR); + row->prim = 0.0; + } + else if (row->stat == GLP_NS) + { xassert(row->type == GLP_FX); + row->prim = row->lb; + } + else + xassert(row != row); + } + for (j = 1; j <= orig->n; j++) + { col = orig->col[j]; + col->stat = npp->c_stat[j]; + if (!npp->scaling) + { col->prim = npp->c_value[j]; + /*col->dual = dir * npp->c_dual[j];*/ + } + else + { col->prim = npp->c_value[j] * col->sjj; + /*col->dual = dir * npp->c_dual[j] / col->sjj;*/ + } + if (col->stat == GLP_BS) + col->dual = 0.0; +#if 1 + else if (col->stat == GLP_NL) + { xassert(col->type == GLP_LO || col->type == GLP_DB); + col->prim = col->lb; + } + else if (col->stat == GLP_NU) + { xassert(col->type == GLP_UP || col->type == GLP_DB); + col->prim = col->ub; + } + else if (col->stat == GLP_NF) + { xassert(col->type == GLP_FR); + col->prim = 0.0; + } + else if (col->stat == GLP_NS) + { xassert(col->type == GLP_FX); + col->prim = col->lb; + } + else + xassert(col != col); +#endif + orig->obj_val += col->coef * col->prim; + } +#if 1 + /* compute primal values of inactive rows */ + for (i = 1; i <= orig->m; i++) + { row = orig->row[i]; + if (row->stat == GLP_BS) + { GLPAIJ *aij; + double temp; + temp = 0.0; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + temp += aij->val * aij->col->prim; + row->prim = temp; + } + } + /* compute reduced costs of active columns */ + for (j = 1; j <= orig->n; j++) + { col = orig->col[j]; + if (col->stat != GLP_BS) + { GLPAIJ *aij; + double temp; + temp = col->coef; + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + temp -= aij->val * aij->row->dual; + col->dual = temp; + } + } +#endif + } + else if (npp->sol == GLP_IPT) + { /* store interior-point solution */ + orig->ipt_stat = npp->t_stat; + orig->ipt_obj = orig->c0; + for (i = 1; i <= orig->m; i++) + { row = orig->row[i]; + if (!npp->scaling) + { /*row->pval = npp->r_prim[i];*/ + row->dval = dir * npp->r_pi[i]; + } + else + { /*row->pval = npp->r_prim[i] / row->rii;*/ + row->dval = dir * npp->r_pi[i] * row->rii; + } + } + for (j = 1; j <= orig->n; j++) + { col = orig->col[j]; + if (!npp->scaling) + { col->pval = npp->c_value[j]; + /*col->dval = dir * npp->c_dual[j];*/ + } + else + { col->pval = npp->c_value[j] * col->sjj; + /*col->dval = dir * npp->c_dual[j] / col->sjj;*/ + } + orig->ipt_obj += col->coef * col->pval; + } +#if 1 + /* compute row primal values */ + for (i = 1; i <= orig->m; i++) + { row = orig->row[i]; + { GLPAIJ *aij; + double temp; + temp = 0.0; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + temp += aij->val * aij->col->pval; + row->pval = temp; + } + } + /* compute column dual values */ + for (j = 1; j <= orig->n; j++) + { col = orig->col[j]; + { GLPAIJ *aij; + double temp; + temp = col->coef; + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + temp -= aij->val * aij->row->dval; + col->dval = temp; + } + } +#endif + } + else if (npp->sol == GLP_MIP) + { /* store MIP solution */ + xassert(!npp->scaling); + orig->mip_stat = npp->i_stat; + orig->mip_obj = orig->c0; +#if 0 + for (i = 1; i <= orig->m; i++) + { row = orig->row[i]; + /*row->mipx = npp->r_prim[i];*/ + } +#endif + for (j = 1; j <= orig->n; j++) + { col = orig->col[j]; + col->mipx = npp->c_value[j]; + if (col->kind == GLP_IV) + xassert(col->mipx == floor(col->mipx)); + orig->mip_obj += col->coef * col->mipx; + } +#if 1 + /* compute row primal values */ + for (i = 1; i <= orig->m; i++) + { row = orig->row[i]; + { GLPAIJ *aij; + double temp; + temp = 0.0; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + temp += aij->val * aij->col->mipx; + row->mipx = temp; + } + } +#endif + } + else + xassert(npp != npp); + return; +} + +void npp_delete_wksp(NPP *npp) +{ /* delete LP/MIP preprocessor workspace */ + if (npp->pool != NULL) + dmp_delete_pool(npp->pool); + if (npp->stack != NULL) + dmp_delete_pool(npp->stack); + if (npp->row_ref != NULL) + xfree(npp->row_ref); + if (npp->col_ref != NULL) + xfree(npp->col_ref); + if (npp->r_stat != NULL) + xfree(npp->r_stat); +#if 0 + if (npp->r_prim != NULL) + xfree(npp->r_prim); +#endif + if (npp->r_pi != NULL) + xfree(npp->r_pi); + if (npp->c_stat != NULL) + xfree(npp->c_stat); + if (npp->c_value != NULL) + xfree(npp->c_value); +#if 0 + if (npp->c_dual != NULL) + xfree(npp->c_dual); +#endif + xfree(npp); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnpp02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnpp02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1433 @@ +/* glpnpp02.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpnpp.h" + +/*********************************************************************** +* NAME +* +* npp_free_row - process free (unbounded) row +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_free_row(NPP *npp, NPPROW *p); +* +* DESCRIPTION +* +* The routine npp_free_row processes row p, which is free (i.e. has +* no finite bounds): +* +* -inf < sum a[p,j] x[j] < +inf. (1) +* j +* +* PROBLEM TRANSFORMATION +* +* Constraint (1) cannot be active, so it is redundant and can be +* removed from the original problem. +* +* Removing row p leads to removing a column of multiplier pi[p] for +* this row in the dual system. Since row p has no bounds, pi[p] = 0, +* so removing the column does not affect the dual solution. +* +* RECOVERING BASIC SOLUTION +* +* In solution to the original problem row p is inactive constraint, +* so it is assigned status GLP_BS, and multiplier pi[p] is assigned +* zero value. +* +* RECOVERING INTERIOR-POINT SOLUTION +* +* In solution to the original problem row p is inactive constraint, +* so its multiplier pi[p] is assigned zero value. +* +* RECOVERING MIP SOLUTION +* +* None needed. */ + +struct free_row +{ /* free (unbounded) row */ + int p; + /* row reference number */ +}; + +static int rcv_free_row(NPP *npp, void *info); + +void npp_free_row(NPP *npp, NPPROW *p) +{ /* process free (unbounded) row */ + struct free_row *info; + /* the row must be free */ + xassert(p->lb == -DBL_MAX && p->ub == +DBL_MAX); + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_free_row, sizeof(struct free_row)); + info->p = p->i; + /* remove the row from the problem */ + npp_del_row(npp, p); + return; +} + +static int rcv_free_row(NPP *npp, void *_info) +{ /* recover free (unbounded) row */ + struct free_row *info = _info; + if (npp->sol == GLP_SOL) + npp->r_stat[info->p] = GLP_BS; + if (npp->sol != GLP_MIP) + npp->r_pi[info->p] = 0.0; + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_geq_row - process row of 'not less than' type +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_geq_row(NPP *npp, NPPROW *p); +* +* DESCRIPTION +* +* The routine npp_geq_row processes row p, which is 'not less than' +* inequality constraint: +* +* L[p] <= sum a[p,j] x[j] (<= U[p]), (1) +* j +* +* where L[p] < U[p], and upper bound may not exist (U[p] = +oo). +* +* PROBLEM TRANSFORMATION +* +* Constraint (1) can be replaced by equality constraint: +* +* sum a[p,j] x[j] - s = L[p], (2) +* j +* +* where +* +* 0 <= s (<= U[p] - L[p]) (3) +* +* is a non-negative surplus variable. +* +* Since in the primal system there appears column s having the only +* non-zero coefficient in row p, in the dual system there appears a +* new row: +* +* (-1) pi[p] + lambda = 0, (4) +* +* where (-1) is coefficient of column s in row p, pi[p] is multiplier +* of row p, lambda is multiplier of column q, 0 is coefficient of +* column s in the objective row. +* +* RECOVERING BASIC SOLUTION +* +* Status of row p in solution to the original problem is determined +* by its status and status of column q in solution to the transformed +* problem as follows: +* +* +--------------------------------------+------------------+ +* | Transformed problem | Original problem | +* +-----------------+--------------------+------------------+ +* | Status of row p | Status of column s | Status of row p | +* +-----------------+--------------------+------------------+ +* | GLP_BS | GLP_BS | N/A | +* | GLP_BS | GLP_NL | GLP_BS | +* | GLP_BS | GLP_NU | GLP_BS | +* | GLP_NS | GLP_BS | GLP_BS | +* | GLP_NS | GLP_NL | GLP_NL | +* | GLP_NS | GLP_NU | GLP_NU | +* +-----------------+--------------------+------------------+ +* +* Value of row multiplier pi[p] in solution to the original problem +* is the same as in solution to the transformed problem. +* +* 1. In solution to the transformed problem row p and column q cannot +* be basic at the same time; otherwise the basis matrix would have +* two linear dependent columns: unity column of auxiliary variable +* of row p and unity column of variable s. +* +* 2. Though in the transformed problem row p is equality constraint, +* it may be basic due to primal degenerate solution. +* +* 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 ineq_row +{ /* inequality constraint row */ + int p; + /* row reference number */ + int s; + /* column reference number for slack/surplus variable */ +}; + +static int rcv_geq_row(NPP *npp, void *info); + +void npp_geq_row(NPP *npp, NPPROW *p) +{ /* process row of 'not less than' type */ + struct ineq_row *info; + NPPCOL *s; + /* the row must have lower bound */ + xassert(p->lb != -DBL_MAX); + xassert(p->lb < p->ub); + /* create column for surplus variable */ + s = npp_add_col(npp); + s->lb = 0.0; + s->ub = (p->ub == +DBL_MAX ? +DBL_MAX : p->ub - p->lb); + /* and add it to the transformed problem */ + npp_add_aij(npp, p, s, -1.0); + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_geq_row, sizeof(struct ineq_row)); + info->p = p->i; + info->s = s->j; + /* replace the row by equality constraint */ + p->ub = p->lb; + return; +} + +static int rcv_geq_row(NPP *npp, void *_info) +{ /* recover row of 'not less than' type */ + struct ineq_row *info = _info; + if (npp->sol == GLP_SOL) + { if (npp->r_stat[info->p] == GLP_BS) + { if (npp->c_stat[info->s] == GLP_BS) + { npp_error(); + return 1; + } + else if (npp->c_stat[info->s] == GLP_NL || + npp->c_stat[info->s] == GLP_NU) + npp->r_stat[info->p] = GLP_BS; + else + { npp_error(); + return 1; + } + } + else if (npp->r_stat[info->p] == GLP_NS) + { if (npp->c_stat[info->s] == GLP_BS) + npp->r_stat[info->p] = GLP_BS; + else if (npp->c_stat[info->s] == GLP_NL) + npp->r_stat[info->p] = GLP_NL; + else if (npp->c_stat[info->s] == GLP_NU) + npp->r_stat[info->p] = GLP_NU; + else + { npp_error(); + return 1; + } + } + else + { npp_error(); + return 1; + } + } + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_leq_row - process row of 'not greater than' type +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_leq_row(NPP *npp, NPPROW *p); +* +* DESCRIPTION +* +* The routine npp_leq_row processes row p, which is 'not greater than' +* inequality constraint: +* +* (L[p] <=) sum a[p,j] x[j] <= U[p], (1) +* j +* +* where L[p] < U[p], and lower bound may not exist (L[p] = +oo). +* +* PROBLEM TRANSFORMATION +* +* Constraint (1) can be replaced by equality constraint: +* +* sum a[p,j] x[j] + s = L[p], (2) +* j +* +* where +* +* 0 <= s (<= U[p] - L[p]) (3) +* +* is a non-negative slack variable. +* +* Since in the primal system there appears column s having the only +* non-zero coefficient in row p, in the dual system there appears a +* new row: +* +* (+1) pi[p] + lambda = 0, (4) +* +* where (+1) is coefficient of column s in row p, pi[p] is multiplier +* of row p, lambda is multiplier of column q, 0 is coefficient of +* column s in the objective row. +* +* RECOVERING BASIC SOLUTION +* +* Status of row p in solution to the original problem is determined +* by its status and status of column q in solution to the transformed +* problem as follows: +* +* +--------------------------------------+------------------+ +* | Transformed problem | Original problem | +* +-----------------+--------------------+------------------+ +* | Status of row p | Status of column s | Status of row p | +* +-----------------+--------------------+------------------+ +* | GLP_BS | GLP_BS | N/A | +* | GLP_BS | GLP_NL | GLP_BS | +* | GLP_BS | GLP_NU | GLP_BS | +* | GLP_NS | GLP_BS | GLP_BS | +* | GLP_NS | GLP_NL | GLP_NU | +* | GLP_NS | GLP_NU | GLP_NL | +* +-----------------+--------------------+------------------+ +* +* Value of row multiplier pi[p] in solution to the original problem +* is the same as in solution to the transformed problem. +* +* 1. In solution to the transformed problem row p and column q cannot +* be basic at the same time; otherwise the basis matrix would have +* two linear dependent columns: unity column of auxiliary variable +* of row p and unity column of variable s. +* +* 2. Though in the transformed problem row p is equality constraint, +* it may be basic due to primal degeneracy. +* +* 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. */ + +static int rcv_leq_row(NPP *npp, void *info); + +void npp_leq_row(NPP *npp, NPPROW *p) +{ /* process row of 'not greater than' type */ + struct ineq_row *info; + NPPCOL *s; + /* the row must have upper bound */ + xassert(p->ub != +DBL_MAX); + xassert(p->lb < p->ub); + /* create column for slack variable */ + s = npp_add_col(npp); + s->lb = 0.0; + s->ub = (p->lb == -DBL_MAX ? +DBL_MAX : p->ub - p->lb); + /* and add it to the transformed problem */ + npp_add_aij(npp, p, s, +1.0); + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_leq_row, sizeof(struct ineq_row)); + info->p = p->i; + info->s = s->j; + /* replace the row by equality constraint */ + p->lb = p->ub; + return; +} + +static int rcv_leq_row(NPP *npp, void *_info) +{ /* recover row of 'not greater than' type */ + struct ineq_row *info = _info; + if (npp->sol == GLP_SOL) + { if (npp->r_stat[info->p] == GLP_BS) + { if (npp->c_stat[info->s] == GLP_BS) + { npp_error(); + return 1; + } + else if (npp->c_stat[info->s] == GLP_NL || + npp->c_stat[info->s] == GLP_NU) + npp->r_stat[info->p] = GLP_BS; + else + { npp_error(); + return 1; + } + } + else if (npp->r_stat[info->p] == GLP_NS) + { if (npp->c_stat[info->s] == GLP_BS) + npp->r_stat[info->p] = GLP_BS; + else if (npp->c_stat[info->s] == GLP_NL) + npp->r_stat[info->p] = GLP_NU; + else if (npp->c_stat[info->s] == GLP_NU) + npp->r_stat[info->p] = GLP_NL; + else + { npp_error(); + return 1; + } + } + else + { npp_error(); + return 1; + } + } + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_free_col - process free (unbounded) column +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_free_col(NPP *npp, NPPCOL *q); +* +* DESCRIPTION +* +* The routine npp_free_col processes column q, which is free (i.e. has +* no finite bounds): +* +* -oo < x[q] < +oo. (1) +* +* PROBLEM TRANSFORMATION +* +* Free (unbounded) variable can be replaced by the difference of two +* non-negative variables: +* +* x[q] = s' - s'', s', s'' >= 0. (2) +* +* Assuming that in the transformed problem x[q] becomes s', +* transformation (2) causes new column s'' to appear, which differs +* from column s' only in the sign of coefficients in constraint and +* objective rows. Thus, if in the dual system the following row +* corresponds to column s': +* +* sum a[i,q] pi[i] + lambda' = c[q], (3) +* i +* +* the row which corresponds to column s'' is the following: +* +* sum (-a[i,q]) pi[i] + lambda'' = -c[q]. (4) +* i +* +* Then from (3) and (4) it follows that: +* +* lambda' + lambda'' = 0 => lambda' = lmabda'' = 0, (5) +* +* where lambda' and lambda'' are multipliers for columns s' and s'', +* resp. +* +* RECOVERING BASIC SOLUTION +* +* With respect to (5) status of column q in solution to the original +* problem is determined by statuses of columns s' and s'' in solution +* to the transformed problem as follows: +* +* +--------------------------------------+------------------+ +* | Transformed problem | Original problem | +* +------------------+-------------------+------------------+ +* | Status of col s' | Status of col s'' | Status of col q | +* +------------------+-------------------+------------------+ +* | GLP_BS | GLP_BS | N/A | +* | GLP_BS | GLP_NL | GLP_BS | +* | GLP_NL | GLP_BS | GLP_BS | +* | GLP_NL | GLP_NL | GLP_NF | +* +------------------+-------------------+------------------+ +* +* Value of column q is computed with formula (2). +* +* 1. In solution to the transformed problem columns s' and s'' cannot +* be basic at the same time, because they differ only in the sign, +* hence, are linear dependent. +* +* 2. Though column q is free, it can be non-basic due to dual +* degeneracy. +* +* 3. If column q is integral, columns s' and s'' are also integral. +* +* RECOVERING INTERIOR-POINT SOLUTION +* +* Value of column q is computed with formula (2). +* +* RECOVERING MIP SOLUTION +* +* Value of column q is computed with formula (2). */ + +struct free_col +{ /* free (unbounded) column */ + int q; + /* column reference number for variables x[q] and s' */ + int s; + /* column reference number for variable s'' */ +}; + +static int rcv_free_col(NPP *npp, void *info); + +void npp_free_col(NPP *npp, NPPCOL *q) +{ /* process free (unbounded) column */ + struct free_col *info; + NPPCOL *s; + NPPAIJ *aij; + /* the column must be free */ + xassert(q->lb == -DBL_MAX && q->ub == +DBL_MAX); + /* variable x[q] becomes s' */ + q->lb = 0.0, q->ub = +DBL_MAX; + /* create variable s'' */ + s = npp_add_col(npp); + s->is_int = q->is_int; + s->lb = 0.0, s->ub = +DBL_MAX; + /* duplicate objective coefficient */ + s->coef = -q->coef; + /* duplicate column of the constraint matrix */ + for (aij = q->ptr; aij != NULL; aij = aij->c_next) + npp_add_aij(npp, aij->row, s, -aij->val); + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_free_col, sizeof(struct free_col)); + info->q = q->j; + info->s = s->j; + return; +} + +static int rcv_free_col(NPP *npp, void *_info) +{ /* recover free (unbounded) column */ + struct free_col *info = _info; + if (npp->sol == GLP_SOL) + { if (npp->c_stat[info->q] == GLP_BS) + { if (npp->c_stat[info->s] == GLP_BS) + { npp_error(); + return 1; + } + else if (npp->c_stat[info->s] == GLP_NL) + npp->c_stat[info->q] = GLP_BS; + else + { npp_error(); + return -1; + } + } + else if (npp->c_stat[info->q] == GLP_NL) + { if (npp->c_stat[info->s] == GLP_BS) + npp->c_stat[info->q] = GLP_BS; + else if (npp->c_stat[info->s] == GLP_NL) + npp->c_stat[info->q] = GLP_NF; + else + { npp_error(); + return -1; + } + } + else + { npp_error(); + return -1; + } + } + /* compute value of x[q] with formula (2) */ + npp->c_value[info->q] -= npp->c_value[info->s]; + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_lbnd_col - process column with (non-zero) lower bound +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_lbnd_col(NPP *npp, NPPCOL *q); +* +* DESCRIPTION +* +* The routine npp_lbnd_col processes column q, which has (non-zero) +* lower bound: +* +* l[q] <= x[q] (<= u[q]), (1) +* +* where l[q] < u[q], and upper bound may not exist (u[q] = +oo). +* +* PROBLEM TRANSFORMATION +* +* Column q can be replaced as follows: +* +* x[q] = l[q] + s, (2) +* +* where +* +* 0 <= s (<= u[q] - l[q]) (3) +* +* is a non-negative variable. +* +* Substituting x[q] from (2) into the objective row, we have: +* +* z = sum c[j] x[j] + c0 = +* j +* +* = sum c[j] x[j] + c[q] x[q] + c0 = +* j!=q +* +* = sum c[j] x[j] + c[q] (l[q] + s) + c0 = +* j!=q +* +* = sum c[j] x[j] + c[q] s + c~0, +* +* where +* +* c~0 = c0 + c[q] l[q] (4) +* +* is the constant term of the objective in the transformed problem. +* Similarly, substituting x[q] into constraint row i, we have: +* +* L[i] <= sum a[i,j] x[j] <= U[i] ==> +* j +* +* L[i] <= sum a[i,j] x[j] + a[i,q] x[q] <= U[i] ==> +* j!=q +* +* L[i] <= sum a[i,j] x[j] + a[i,q] (l[q] + s) <= U[i] ==> +* j!=q +* +* L~[i] <= sum a[i,j] x[j] + a[i,q] s <= U~[i], +* j!=q +* +* where +* +* L~[i] = L[i] - a[i,q] l[q], U~[i] = U[i] - a[i,q] l[q] (5) +* +* are lower and upper bounds of row i in the transformed problem, +* resp. +* +* Transformation (2) does not affect the dual system. +* +* RECOVERING BASIC SOLUTION +* +* Status of column q in solution to the original problem is the same +* as in solution to the transformed problem (GLP_BS, GLP_NL or GLP_NU). +* Value of column q is computed with formula (2). +* +* RECOVERING INTERIOR-POINT SOLUTION +* +* Value of column q is computed with formula (2). +* +* RECOVERING MIP SOLUTION +* +* Value of column q is computed with formula (2). */ + +struct bnd_col +{ /* bounded column */ + int q; + /* column reference number for variables x[q] and s */ + double bnd; + /* lower/upper bound l[q] or u[q] */ +}; + +static int rcv_lbnd_col(NPP *npp, void *info); + +void npp_lbnd_col(NPP *npp, NPPCOL *q) +{ /* process column with (non-zero) lower bound */ + struct bnd_col *info; + NPPROW *i; + NPPAIJ *aij; + /* the column must have non-zero lower bound */ + xassert(q->lb != 0.0); + xassert(q->lb != -DBL_MAX); + xassert(q->lb < q->ub); + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_lbnd_col, sizeof(struct bnd_col)); + info->q = q->j; + info->bnd = q->lb; + /* substitute x[q] into objective row */ + npp->c0 += q->coef * q->lb; + /* substitute x[q] into constraint rows */ + for (aij = q->ptr; aij != NULL; aij = aij->c_next) + { i = aij->row; + if (i->lb == i->ub) + i->ub = (i->lb -= aij->val * q->lb); + else + { if (i->lb != -DBL_MAX) + i->lb -= aij->val * q->lb; + if (i->ub != +DBL_MAX) + i->ub -= aij->val * q->lb; + } + } + /* column x[q] becomes column s */ + if (q->ub != +DBL_MAX) + q->ub -= q->lb; + q->lb = 0.0; + return; +} + +static int rcv_lbnd_col(NPP *npp, void *_info) +{ /* recover column with (non-zero) lower bound */ + struct bnd_col *info = _info; + if (npp->sol == GLP_SOL) + { if (npp->c_stat[info->q] == GLP_BS || + npp->c_stat[info->q] == GLP_NL || + npp->c_stat[info->q] == GLP_NU) + npp->c_stat[info->q] = npp->c_stat[info->q]; + else + { npp_error(); + return 1; + } + } + /* compute value of x[q] with formula (2) */ + npp->c_value[info->q] = info->bnd + npp->c_value[info->q]; + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_ubnd_col - process column with upper bound +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_ubnd_col(NPP *npp, NPPCOL *q); +* +* DESCRIPTION +* +* The routine npp_ubnd_col processes column q, which has upper bound: +* +* (l[q] <=) x[q] <= u[q], (1) +* +* where l[q] < u[q], and lower bound may not exist (l[q] = -oo). +* +* PROBLEM TRANSFORMATION +* +* Column q can be replaced as follows: +* +* x[q] = u[q] - s, (2) +* +* where +* +* 0 <= s (<= u[q] - l[q]) (3) +* +* is a non-negative variable. +* +* Substituting x[q] from (2) into the objective row, we have: +* +* z = sum c[j] x[j] + c0 = +* j +* +* = sum c[j] x[j] + c[q] x[q] + c0 = +* j!=q +* +* = sum c[j] x[j] + c[q] (u[q] - s) + c0 = +* j!=q +* +* = sum c[j] x[j] - c[q] s + c~0, +* +* where +* +* c~0 = c0 + c[q] u[q] (4) +* +* is the constant term of the objective in the transformed problem. +* Similarly, substituting x[q] into constraint row i, we have: +* +* L[i] <= sum a[i,j] x[j] <= U[i] ==> +* j +* +* L[i] <= sum a[i,j] x[j] + a[i,q] x[q] <= U[i] ==> +* j!=q +* +* L[i] <= sum a[i,j] x[j] + a[i,q] (u[q] - s) <= U[i] ==> +* j!=q +* +* L~[i] <= sum a[i,j] x[j] - a[i,q] s <= U~[i], +* j!=q +* +* where +* +* L~[i] = L[i] - a[i,q] u[q], U~[i] = U[i] - a[i,q] u[q] (5) +* +* are lower and upper bounds of row i in the transformed problem, +* resp. +* +* Note that in the transformed problem coefficients c[q] and a[i,q] +* change their sign. Thus, the row of the dual system corresponding to +* column q: +* +* sum a[i,q] pi[i] + lambda[q] = c[q] (6) +* i +* +* in the transformed problem becomes the following: +* +* sum (-a[i,q]) pi[i] + lambda[s] = -c[q]. (7) +* i +* +* Therefore: +* +* lambda[q] = - lambda[s], (8) +* +* where lambda[q] is multiplier for column q, lambda[s] is multiplier +* for column s. +* +* RECOVERING BASIC SOLUTION +* +* With respect to (8) status of column q in solution to the original +* problem is determined by status of column s in solution to the +* transformed problem as follows: +* +* +-----------------------+--------------------+ +* | Status of column s | Status of column q | +* | (transformed problem) | (original problem) | +* +-----------------------+--------------------+ +* | GLP_BS | GLP_BS | +* | GLP_NL | GLP_NU | +* | GLP_NU | GLP_NL | +* +-----------------------+--------------------+ +* +* Value of column q is computed with formula (2). +* +* RECOVERING INTERIOR-POINT SOLUTION +* +* Value of column q is computed with formula (2). +* +* RECOVERING MIP SOLUTION +* +* Value of column q is computed with formula (2). */ + +static int rcv_ubnd_col(NPP *npp, void *info); + +void npp_ubnd_col(NPP *npp, NPPCOL *q) +{ /* process column with upper bound */ + struct bnd_col *info; + NPPROW *i; + NPPAIJ *aij; + /* the column must have upper bound */ + xassert(q->ub != +DBL_MAX); + xassert(q->lb < q->ub); + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_ubnd_col, sizeof(struct bnd_col)); + info->q = q->j; + info->bnd = q->ub; + /* substitute x[q] into objective row */ + npp->c0 += q->coef * q->ub; + q->coef = -q->coef; + /* substitute x[q] into constraint rows */ + for (aij = q->ptr; aij != NULL; aij = aij->c_next) + { i = aij->row; + if (i->lb == i->ub) + i->ub = (i->lb -= aij->val * q->ub); + else + { if (i->lb != -DBL_MAX) + i->lb -= aij->val * q->ub; + if (i->ub != +DBL_MAX) + i->ub -= aij->val * q->ub; + } + aij->val = -aij->val; + } + /* column x[q] becomes column s */ + if (q->lb != -DBL_MAX) + q->ub -= q->lb; + else + q->ub = +DBL_MAX; + q->lb = 0.0; + return; +} + +static int rcv_ubnd_col(NPP *npp, void *_info) +{ /* recover column with upper bound */ + struct bnd_col *info = _info; + if (npp->sol == GLP_BS) + { if (npp->c_stat[info->q] == GLP_BS) + npp->c_stat[info->q] = GLP_BS; + else if (npp->c_stat[info->q] == GLP_NL) + npp->c_stat[info->q] = GLP_NU; + else if (npp->c_stat[info->q] == GLP_NU) + npp->c_stat[info->q] = GLP_NL; + else + { npp_error(); + return 1; + } + } + /* compute value of x[q] with formula (2) */ + npp->c_value[info->q] = info->bnd - npp->c_value[info->q]; + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_dbnd_col - process non-negative column with upper bound +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_dbnd_col(NPP *npp, NPPCOL *q); +* +* DESCRIPTION +* +* The routine npp_dbnd_col processes column q, which is non-negative +* and has upper bound: +* +* 0 <= x[q] <= u[q], (1) +* +* where u[q] > 0. +* +* PROBLEM TRANSFORMATION +* +* Upper bound of column q can be replaced by the following equality +* constraint: +* +* x[q] + s = u[q], (2) +* +* where s >= 0 is a non-negative complement variable. +* +* Since in the primal system along with new row (2) there appears a +* new column s having the only non-zero coefficient in this row, in +* the dual system there appears a new row: +* +* (+1)pi + lambda[s] = 0, (3) +* +* where (+1) is coefficient at column s in row (2), pi is multiplier +* for row (2), lambda[s] is multiplier for column s, 0 is coefficient +* at column s in the objective row. +* +* RECOVERING BASIC SOLUTION +* +* Status of column q in solution to the original problem is determined +* by its status and status of column s in solution to the transformed +* problem as follows: +* +* +-----------------------------------+------------------+ +* | Transformed problem | Original problem | +* +-----------------+-----------------+------------------+ +* | Status of col q | Status of col s | Status of col q | +* +-----------------+-----------------+------------------+ +* | GLP_BS | GLP_BS | GLP_BS | +* | GLP_BS | GLP_NL | GLP_NU | +* | GLP_NL | GLP_BS | GLP_NL | +* | GLP_NL | GLP_NL | GLP_NL (*) | +* +-----------------+-----------------+------------------+ +* +* Value of column q in solution to the original problem is the same as +* in solution to the transformed problem. +* +* 1. Formally, in solution to the transformed problem columns q and s +* cannot be non-basic at the same time, since the constraint (2) +* would be violated. However, if u[q] is close to zero, violation +* may be less than a working precision even if both columns q and s +* are non-basic. In this degenerate case row (2) can be only basic, +* i.e. non-active constraint (otherwise corresponding row of the +* basis matrix would be zero). This allows to pivot out auxiliary +* variable and pivot in column s, in which case the row becomes +* active while column s becomes basic. +* +* 2. If column q is integral, column s is also integral. +* +* RECOVERING INTERIOR-POINT SOLUTION +* +* Value of column q in solution to the original problem is the same as +* in solution to the transformed problem. +* +* RECOVERING MIP SOLUTION +* +* Value of column q in solution to the original problem is the same as +* in solution to the transformed problem. */ + +struct dbnd_col +{ /* double-bounded column */ + int q; + /* column reference number for variable x[q] */ + int s; + /* column reference number for complement variable s */ +}; + +static int rcv_dbnd_col(NPP *npp, void *info); + +void npp_dbnd_col(NPP *npp, NPPCOL *q) +{ /* process non-negative column with upper bound */ + struct dbnd_col *info; + NPPROW *p; + NPPCOL *s; + /* the column must be non-negative with upper bound */ + xassert(q->lb == 0.0); + xassert(q->ub > 0.0); + xassert(q->ub != +DBL_MAX); + /* create variable s */ + s = npp_add_col(npp); + s->is_int = q->is_int; + s->lb = 0.0, s->ub = +DBL_MAX; + /* create equality constraint (2) */ + p = npp_add_row(npp); + p->lb = p->ub = q->ub; + npp_add_aij(npp, p, q, +1.0); + npp_add_aij(npp, p, s, +1.0); + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_dbnd_col, sizeof(struct dbnd_col)); + info->q = q->j; + info->s = s->j; + /* remove upper bound of x[q] */ + q->ub = +DBL_MAX; + return; +} + +static int rcv_dbnd_col(NPP *npp, void *_info) +{ /* recover non-negative column with upper bound */ + struct dbnd_col *info = _info; + if (npp->sol == GLP_BS) + { if (npp->c_stat[info->q] == GLP_BS) + { if (npp->c_stat[info->s] == GLP_BS) + npp->c_stat[info->q] = GLP_BS; + else if (npp->c_stat[info->s] == GLP_NL) + npp->c_stat[info->q] = GLP_NU; + else + { npp_error(); + return 1; + } + } + else if (npp->c_stat[info->q] == GLP_NL) + { if (npp->c_stat[info->s] == GLP_BS || + npp->c_stat[info->s] == GLP_NL) + npp->c_stat[info->q] = GLP_NL; + else + { npp_error(); + return 1; + } + } + else + { npp_error(); + return 1; + } + } + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_fixed_col - process fixed column +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_fixed_col(NPP *npp, NPPCOL *q); +* +* DESCRIPTION +* +* The routine npp_fixed_col processes column q, which is fixed: +* +* x[q] = s[q], (1) +* +* where s[q] is a fixed column value. +* +* PROBLEM TRANSFORMATION +* +* The value of a fixed column can be substituted into the objective +* and constraint rows that allows removing the column from the problem. +* +* Substituting x[q] = s[q] into the objective row, we have: +* +* z = sum c[j] x[j] + c0 = +* j +* +* = sum c[j] x[j] + c[q] x[q] + c0 = +* j!=q +* +* = sum c[j] x[j] + c[q] s[q] + c0 = +* j!=q +* +* = sum c[j] x[j] + c~0, +* j!=q +* +* where +* +* c~0 = c0 + c[q] s[q] (2) +* +* is the constant term of the objective in the transformed problem. +* Similarly, substituting x[q] = s[q] into constraint row i, we have: +* +* L[i] <= sum a[i,j] x[j] <= U[i] ==> +* j +* +* L[i] <= sum a[i,j] x[j] + a[i,q] x[q] <= U[i] ==> +* j!=q +* +* L[i] <= sum a[i,j] x[j] + a[i,q] s[q] <= U[i] ==> +* j!=q +* +* L~[i] <= sum a[i,j] x[j] + a[i,q] s <= U~[i], +* j!=q +* +* where +* +* L~[i] = L[i] - a[i,q] s[q], U~[i] = U[i] - a[i,q] s[q] (3) +* +* are lower and upper bounds of row i in the transformed problem, +* resp. +* +* RECOVERING BASIC SOLUTION +* +* Column q is assigned status GLP_NS and its value is assigned s[q]. +* +* RECOVERING INTERIOR-POINT SOLUTION +* +* Value of column q is assigned s[q]. +* +* RECOVERING MIP SOLUTION +* +* Value of column q is assigned s[q]. */ + +struct fixed_col +{ /* fixed column */ + int q; + /* column reference number for variable x[q] */ + double s; + /* value, at which x[q] is fixed */ +}; + +static int rcv_fixed_col(NPP *npp, void *info); + +void npp_fixed_col(NPP *npp, NPPCOL *q) +{ /* process fixed column */ + struct fixed_col *info; + NPPROW *i; + NPPAIJ *aij; + /* the column must be fixed */ + xassert(q->lb == q->ub); + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_fixed_col, sizeof(struct fixed_col)); + info->q = q->j; + info->s = q->lb; + /* substitute x[q] = s[q] into objective row */ + npp->c0 += q->coef * q->lb; + /* substitute x[q] = s[q] into constraint rows */ + for (aij = q->ptr; aij != NULL; aij = aij->c_next) + { i = aij->row; + if (i->lb == i->ub) + i->ub = (i->lb -= aij->val * q->lb); + else + { if (i->lb != -DBL_MAX) + i->lb -= aij->val * q->lb; + if (i->ub != +DBL_MAX) + i->ub -= aij->val * q->lb; + } + } + /* remove the column from the problem */ + npp_del_col(npp, q); + return; +} + +static int rcv_fixed_col(NPP *npp, void *_info) +{ /* recover fixed column */ + struct fixed_col *info = _info; + if (npp->sol == GLP_SOL) + npp->c_stat[info->q] = GLP_NS; + npp->c_value[info->q] = info->s; + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_make_equality - process row with almost identical bounds +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_make_equality(NPP *npp, NPPROW *p); +* +* DESCRIPTION +* +* The routine npp_make_equality processes row p: +* +* L[p] <= sum a[p,j] x[j] <= U[p], (1) +* j +* +* where -oo < L[p] < U[p] < +oo, i.e. which is double-sided inequality +* constraint. +* +* RETURNS +* +* 0 - row bounds have not been changed; +* +* 1 - row has been replaced by equality constraint. +* +* PROBLEM TRANSFORMATION +* +* If bounds of row (1) are very close to each other: +* +* U[p] - L[p] <= eps, (2) +* +* where eps is an absolute tolerance for row value, the row can be +* replaced by the following almost equivalent equiality constraint: +* +* sum a[p,j] x[j] = b, (3) +* j +* +* where b = (L[p] + U[p]) / 2. If the right-hand side in (3) happens +* to be very close to its nearest integer: +* +* |b - floor(b + 0.5)| <= eps, (4) +* +* it is reasonable to use this nearest integer as the right-hand side. +* +* RECOVERING BASIC SOLUTION +* +* Status of row p in solution to the original problem is determined +* by its status and the sign of its multiplier pi[p] in solution to +* the transformed problem as follows: +* +* +-----------------------+---------+--------------------+ +* | Status of row p | Sign of | Status of row p | +* | (transformed problem) | pi[p] | (original problem) | +* +-----------------------+---------+--------------------+ +* | GLP_BS | + / - | GLP_BS | +* | GLP_NS | + | GLP_NL | +* | GLP_NS | - | GLP_NU | +* +-----------------------+---------+--------------------+ +* +* Value of row multiplier pi[p] 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 make_equality +{ /* row with almost identical bounds */ + int p; + /* row reference number */ +}; + +static int rcv_make_equality(NPP *npp, void *info); + +int npp_make_equality(NPP *npp, NPPROW *p) +{ /* process row with almost identical bounds */ + struct make_equality *info; + double b, eps, nint; + /* the row must be double-sided inequality */ + xassert(p->lb != -DBL_MAX); + xassert(p->ub != +DBL_MAX); + xassert(p->lb < p->ub); + /* check row bounds */ + eps = 1e-9 + 1e-12 * fabs(p->lb); + if (p->ub - p->lb > eps) return 0; + /* row bounds are very close to each other */ + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_make_equality, sizeof(struct make_equality)); + info->p = p->i; + /* compute right-hand side */ + b = 0.5 * (p->ub + p->lb); + nint = floor(b + 0.5); + if (fabs(b - nint) <= eps) b = nint; + /* replace row p by almost equivalent equality constraint */ + p->lb = p->ub = b; + return 1; +} + +int rcv_make_equality(NPP *npp, void *_info) +{ /* recover row with almost identical bounds */ + struct make_equality *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) + { if (npp->r_pi[info->p] >= 0.0) + npp->r_stat[info->p] = GLP_NL; + else + npp->r_stat[info->p] = GLP_NU; + } + else + { npp_error(); + return 1; + } + } + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_make_fixed - process column with almost identical bounds +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_make_fixed(NPP *npp, NPPCOL *q); +* +* DESCRIPTION +* +* The routine npp_make_fixed processes column q: +* +* l[q] <= x[q] <= u[q], (1) +* +* where -oo < l[q] < u[q] < +oo, i.e. which has both lower and upper +* bounds. +* +* RETURNS +* +* 0 - column bounds have not been changed; +* +* 1 - column has been fixed. +* +* PROBLEM TRANSFORMATION +* +* If bounds of column (1) are very close to each other: +* +* u[q] - l[q] <= eps, (2) +* +* where eps is an absolute tolerance for column value, the column can +* be fixed: +* +* x[q] = s[q], (3) +* +* where s[q] = (l[q] + u[q]) / 2. And if the fixed column value s[q] +* happens to be very close to its nearest integer: +* +* |s[q] - floor(s[q] + 0.5)| <= eps, (4) +* +* it is reasonable to use this nearest integer as the fixed value. +* +* RECOVERING BASIC SOLUTION +* +* In the dual system of the original (as well as transformed) problem +* column q corresponds to the following row: +* +* sum a[i,q] pi[i] + lambda[q] = c[q]. (5) +* i +* +* Since multipliers pi[i] are known for all rows from solution to the +* transformed problem, formula (5) allows computing value of multiplier +* (reduced cost) for column q: +* +* lambda[q] = c[q] - sum a[i,q] pi[i]. (6) +* i +* +* Status of column q in solution to the original problem is determined +* by its status and the sign of its multiplier lambda[q] in solution to +* the transformed problem as follows: +* +* +-----------------------+-----------+--------------------+ +* | Status of column q | Sign of | Status of column q | +* | (transformed problem) | lambda[q] | (original problem) | +* +-----------------------+-----------+--------------------+ +* | GLP_BS | + / - | GLP_BS | +* | GLP_NS | + | GLP_NL | +* | GLP_NS | - | GLP_NU | +* +-----------------------+-----------+--------------------+ +* +* 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 column q in solution to the original problem is the same as +* in solution to the transformed problem. +* +* RECOVERING MIP SOLUTION +* +* None needed. */ + +struct make_fixed +{ /* column with almost identical bounds */ + int q; + /* column reference number */ + double c; + /* objective coefficient at x[q] */ + NPPLFE *ptr; + /* list of non-zero coefficients a[i,q] */ +}; + +static int rcv_make_fixed(NPP *npp, void *info); + +int npp_make_fixed(NPP *npp, NPPCOL *q) +{ /* process column with almost identical bounds */ + struct make_fixed *info; + NPPAIJ *aij; + NPPLFE *lfe; + double s, eps, nint; + /* the column must be double-bounded */ + xassert(q->lb != -DBL_MAX); + xassert(q->ub != +DBL_MAX); + xassert(q->lb < q->ub); + /* check column bounds */ + eps = 1e-9 + 1e-12 * fabs(q->lb); + if (q->ub - q->lb > eps) return 0; + /* column bounds are very close to each other */ + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_make_fixed, sizeof(struct make_fixed)); + info->q = q->j; + info->c = q->coef; + info->ptr = NULL; + /* save column coefficients a[i,q] (needed for basic solution + only) */ + if (npp->sol == GLP_SOL) + { for (aij = q->ptr; aij != NULL; aij = aij->c_next) + { lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE)); + lfe->ref = aij->row->i; + lfe->val = aij->val; + lfe->next = info->ptr; + info->ptr = lfe; + } + } + /* compute column fixed value */ + s = 0.5 * (q->ub + q->lb); + nint = floor(s + 0.5); + if (fabs(s - nint) <= eps) s = nint; + /* make column q fixed */ + q->lb = q->ub = s; + return 1; +} + +static int rcv_make_fixed(NPP *npp, void *_info) +{ /* recover column with almost identical bounds */ + struct make_fixed *info = _info; + NPPLFE *lfe; + double lambda; + if (npp->sol == GLP_SOL) + { if (npp->c_stat[info->q] == GLP_BS) + npp->c_stat[info->q] = GLP_BS; + else if (npp->c_stat[info->q] == GLP_NS) + { /* compute multiplier for column q with formula (6) */ + lambda = info->c; + for (lfe = info->ptr; lfe != NULL; lfe = lfe->next) + lambda -= lfe->val * npp->r_pi[lfe->ref]; + /* assign status to non-basic column */ + if (lambda >= 0.0) + npp->c_stat[info->q] = GLP_NL; + else + npp->c_stat[info->q] = GLP_NU; + } + else + { npp_error(); + return 1; + } + } + return 0; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnpp03.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnpp03.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,2861 @@ +/* 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: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpnpp.h" + +/*********************************************************************** +* NAME +* +* npp_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 */ diff -r d59bea55db9b -r c445c931472f src/glpnpp04.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnpp04.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1414 @@ +/* glpnpp04.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpnpp.h" + +/*********************************************************************** +* NAME +* +* npp_binarize_prob - binarize MIP problem +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_binarize_prob(NPP *npp); +* +* DESCRIPTION +* +* The routine npp_binarize_prob replaces in the original MIP problem +* every integer variable: +* +* l[q] <= x[q] <= u[q], (1) +* +* where l[q] < u[q], by an equivalent sum of binary variables. +* +* RETURNS +* +* The routine returns the number of integer variables for which the +* transformation failed, because u[q] - l[q] > d_max. +* +* PROBLEM TRANSFORMATION +* +* If variable x[q] has non-zero lower bound, it is first processed +* with the routine npp_lbnd_col. Thus, we can assume that: +* +* 0 <= x[q] <= u[q]. (2) +* +* If u[q] = 1, variable x[q] is already binary, so further processing +* is not needed. Let, therefore, that 2 <= u[q] <= d_max, and n be a +* smallest integer such that u[q] <= 2^n - 1 (n >= 2, since u[q] >= 2). +* Then variable x[q] can be replaced by the following sum: +* +* n-1 +* x[q] = sum 2^k x[k], (3) +* k=0 +* +* where x[k] are binary columns (variables). If u[q] < 2^n - 1, the +* following additional inequality constraint must be also included in +* the transformed problem: +* +* n-1 +* sum 2^k x[k] <= u[q]. (4) +* k=0 +* +* Note: Assuming that in the transformed problem x[q] becomes binary +* variable x[0], this transformation causes new n-1 binary variables +* to appear. +* +* Substituting x[q] from (3) to the objective row gives: +* +* z = sum c[j] x[j] + c[0] = +* j +* +* = sum c[j] x[j] + c[q] x[q] + c[0] = +* j!=q +* n-1 +* = sum c[j] x[j] + c[q] sum 2^k x[k] + c[0] = +* j!=q k=0 +* n-1 +* = sum c[j] x[j] + sum c[k] x[k] + c[0], +* j!=q k=0 +* +* where: +* +* c[k] = 2^k c[q], k = 0, ..., n-1. (5) +* +* And substituting x[q] from (3) to i-th constraint row i gives: +* +* L[i] <= sum a[i,j] x[j] <= U[i] ==> +* j +* +* L[i] <= sum a[i,j] x[j] + a[i,q] x[q] <= U[i] ==> +* j!=q +* n-1 +* L[i] <= sum a[i,j] x[j] + a[i,q] sum 2^k x[k] <= U[i] ==> +* j!=q k=0 +* n-1 +* L[i] <= sum a[i,j] x[j] + sum a[i,k] x[k] <= U[i], +* j!=q k=0 +* +* where: +* +* a[i,k] = 2^k a[i,q], k = 0, ..., n-1. (6) +* +* RECOVERING SOLUTION +* +* Value of variable x[q] is computed with formula (3). */ + +struct binarize +{ int q; + /* column reference number for x[q] = x[0] */ + int j; + /* column reference number for x[1]; x[2] has reference number + j+1, x[3] - j+2, etc. */ + int n; + /* total number of binary variables, n >= 2 */ +}; + +static int rcv_binarize_prob(NPP *npp, void *info); + +int npp_binarize_prob(NPP *npp) +{ /* binarize MIP problem */ + struct binarize *info; + NPPROW *row; + NPPCOL *col, *bin; + NPPAIJ *aij; + int u, n, k, temp, nfails, nvars, nbins, nrows; + /* new variables will be added to the end of the column list, so + we go from the end to beginning of the column list */ + nfails = nvars = nbins = nrows = 0; + for (col = npp->c_tail; col != NULL; col = col->prev) + { /* skip continuous variable */ + if (!col->is_int) continue; + /* skip fixed variable */ + if (col->lb == col->ub) continue; + /* skip binary variable */ + if (col->lb == 0.0 && col->ub == 1.0) continue; + /* check if the transformation is applicable */ + if (col->lb < -1e6 || col->ub > +1e6 || + col->ub - col->lb > 4095.0) + { /* unfortunately, not */ + nfails++; + continue; + } + /* process integer non-binary variable x[q] */ + nvars++; + /* make x[q] non-negative, if its lower bound is non-zero */ + if (col->lb != 0.0) + npp_lbnd_col(npp, col); + /* now 0 <= x[q] <= u[q] */ + xassert(col->lb == 0.0); + u = (int)col->ub; + xassert(col->ub == (double)u); + /* if x[q] is binary, further processing is not needed */ + if (u == 1) continue; + /* determine smallest n such that u <= 2^n - 1 (thus, n is the + number of binary variables needed) */ + n = 2, temp = 4; + while (u >= temp) + n++, temp += temp; + nbins += n; + /* create transformation stack entry */ + info = npp_push_tse(npp, + rcv_binarize_prob, sizeof(struct binarize)); + info->q = col->j; + info->j = 0; /* will be set below */ + info->n = n; + /* if u < 2^n - 1, we need one additional row for (4) */ + if (u < temp - 1) + { row = npp_add_row(npp), nrows++; + row->lb = -DBL_MAX, row->ub = u; + } + else + row = NULL; + /* in the transformed problem variable x[q] becomes binary + variable x[0], so its objective and constraint coefficients + are not changed */ + col->ub = 1.0; + /* include x[0] into constraint (4) */ + if (row != NULL) + npp_add_aij(npp, row, col, 1.0); + /* add other binary variables x[1], ..., x[n-1] */ + for (k = 1, temp = 2; k < n; k++, temp += temp) + { /* add new binary variable x[k] */ + bin = npp_add_col(npp); + bin->is_int = 1; + bin->lb = 0.0, bin->ub = 1.0; + bin->coef = (double)temp * col->coef; + /* store column reference number for x[1] */ + if (info->j == 0) + info->j = bin->j; + else + xassert(info->j + (k-1) == bin->j); + /* duplicate constraint coefficients for x[k]; this also + automatically includes x[k] into constraint (4) */ + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + npp_add_aij(npp, aij->row, bin, (double)temp * aij->val); + } + } + if (nvars > 0) + xprintf("%d integer variable(s) were replaced by %d binary one" + "s\n", nvars, nbins); + if (nrows > 0) + xprintf("%d row(s) were added due to binarization\n", nrows); + if (nfails > 0) + xprintf("Binarization failed for %d integer variable(s)\n", + nfails); + return nfails; +} + +static int rcv_binarize_prob(NPP *npp, void *_info) +{ /* recovery binarized variable */ + struct binarize *info = _info; + int k, temp; + double sum; + /* compute value of x[q]; see formula (3) */ + sum = npp->c_value[info->q]; + for (k = 1, temp = 2; k < info->n; k++, temp += temp) + sum += (double)temp * npp->c_value[info->j + (k-1)]; + npp->c_value[info->q] = sum; + return 0; +} + +/**********************************************************************/ + +struct elem +{ /* linear form element a[j] x[j] */ + double aj; + /* non-zero coefficient value */ + NPPCOL *xj; + /* pointer to variable (column) */ + struct elem *next; + /* pointer to another term */ +}; + +static struct elem *copy_form(NPP *npp, NPPROW *row, double s) +{ /* copy linear form */ + NPPAIJ *aij; + struct elem *ptr, *e; + ptr = NULL; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { e = dmp_get_atom(npp->pool, sizeof(struct elem)); + e->aj = s * aij->val; + e->xj = aij->col; + e->next = ptr; + ptr = e; + } + return ptr; +} + +static void drop_form(NPP *npp, struct elem *ptr) +{ /* drop linear form */ + struct elem *e; + while (ptr != NULL) + { e = ptr; + ptr = e->next; + dmp_free_atom(npp->pool, e, sizeof(struct elem)); + } + return; +} + +/*********************************************************************** +* NAME +* +* npp_is_packing - test if constraint is packing inequality +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_is_packing(NPP *npp, NPPROW *row); +* +* RETURNS +* +* If the specified row (constraint) is packing inequality (see below), +* the routine npp_is_packing returns non-zero. Otherwise, it returns +* zero. +* +* PACKING INEQUALITIES +* +* In canonical format the packing inequality is the following: +* +* sum x[j] <= 1, (1) +* j in J +* +* where all variables x[j] are binary. This inequality expresses the +* condition that in any integer feasible solution at most one variable +* from set J can take non-zero (unity) value while other variables +* must be equal to zero. W.l.o.g. it is assumed that |J| >= 2, because +* if J is empty or |J| = 1, the inequality (1) is redundant. +* +* In general case the packing inequality may include original variables +* x[j] as well as their complements x~[j]: +* +* sum x[j] + sum x~[j] <= 1, (2) +* j in Jp j in Jn +* +* where Jp and Jn are not intersected. Therefore, using substitution +* x~[j] = 1 - x[j] gives the packing inequality in generalized format: +* +* sum x[j] - sum x[j] <= 1 - |Jn|. (3) +* j in Jp j in Jn */ + +int npp_is_packing(NPP *npp, NPPROW *row) +{ /* test if constraint is packing inequality */ + NPPCOL *col; + NPPAIJ *aij; + int b; + xassert(npp == npp); + if (!(row->lb == -DBL_MAX && row->ub != +DBL_MAX)) + return 0; + b = 1; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col = aij->col; + if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) + return 0; + if (aij->val == +1.0) + ; + else if (aij->val == -1.0) + b--; + else + return 0; + } + if (row->ub != (double)b) return 0; + return 1; +} + +/*********************************************************************** +* NAME +* +* npp_hidden_packing - identify hidden packing inequality +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_hidden_packing(NPP *npp, NPPROW *row); +* +* DESCRIPTION +* +* The routine npp_hidden_packing processes specified inequality +* constraint, which includes only binary variables, and the number of +* the variables is not less than two. If the original inequality is +* equivalent to a packing inequality, the routine replaces it by this +* equivalent inequality. If the original constraint is double-sided +* inequality, it is replaced by a pair of single-sided inequalities, +* if necessary. +* +* RETURNS +* +* If the original inequality constraint was replaced by equivalent +* packing inequality, the routine npp_hidden_packing returns non-zero. +* Otherwise, it returns zero. +* +* PROBLEM TRANSFORMATION +* +* Consider an inequality constraint: +* +* sum a[j] x[j] <= b, (1) +* j in J +* +* where all variables x[j] are binary, and |J| >= 2. (In case of '>=' +* inequality it can be transformed to '<=' format by multiplying both +* its sides by -1.) +* +* Let Jp = {j: a[j] > 0}, Jn = {j: a[j] < 0}. Performing substitution +* x[j] = 1 - x~[j] for all j in Jn, we have: +* +* sum a[j] x[j] <= b ==> +* j in J +* +* sum a[j] x[j] + sum a[j] x[j] <= b ==> +* j in Jp j in Jn +* +* sum a[j] x[j] + sum a[j] (1 - x~[j]) <= b ==> +* j in Jp j in Jn +* +* sum a[j] x[j] - sum a[j] x~[j] <= b - sum a[j]. +* j in Jp j in Jn j in Jn +* +* Thus, meaning the transformation above, we can assume that in +* inequality (1) all coefficients a[j] are positive. Moreover, we can +* assume that a[j] <= b. In fact, let a[j] > b; then the following +* three cases are possible: +* +* 1) b < 0. In this case inequality (1) is infeasible, so the problem +* has no feasible solution (see the routine npp_analyze_row); +* +* 2) b = 0. In this case inequality (1) is a forcing inequality on its +* upper bound (see the routine npp_forcing row), from which it +* follows that all variables x[j] should be fixed at zero; +* +* 3) b > 0. In this case inequality (1) defines an implied zero upper +* bound for variable x[j] (see the routine npp_implied_bounds), from +* which it follows that x[j] should be fixed at zero. +* +* It is assumed that all three cases listed above have been recognized +* by the routine npp_process_prob, which performs basic MIP processing +* prior to a call the routine npp_hidden_packing. So, if one of these +* cases occurs, we should just skip processing such constraint. +* +* Thus, let 0 < a[j] <= b. Then it is obvious that constraint (1) is +* equivalent to packing inquality only if: +* +* a[j] + a[k] > b + eps (2) +* +* for all j, k in J, j != k, where eps is an absolute tolerance for +* row (linear form) value. Checking the condition (2) for all j and k, +* j != k, requires time O(|J|^2). However, this time can be reduced to +* O(|J|), if use minimal a[j] and a[k], in which case it is sufficient +* to check the condition (2) only once. +* +* Once the original inequality (1) is replaced by equivalent packing +* inequality, we need to perform back substitution x~[j] = 1 - x[j] for +* all j in Jn (see above). +* +* RECOVERING SOLUTION +* +* None needed. */ + +static int hidden_packing(NPP *npp, struct elem *ptr, double *_b) +{ /* process inequality constraint: sum a[j] x[j] <= b; + 0 - specified row is NOT hidden packing inequality; + 1 - specified row is packing inequality; + 2 - specified row is hidden packing inequality. */ + struct elem *e, *ej, *ek; + int neg; + double b = *_b, eps; + xassert(npp == npp); + /* a[j] must be non-zero, x[j] must be binary, for all j in J */ + for (e = ptr; e != NULL; e = e->next) + { xassert(e->aj != 0.0); + xassert(e->xj->is_int); + xassert(e->xj->lb == 0.0 && e->xj->ub == 1.0); + } + /* check if the specified inequality constraint already has the + form of packing inequality */ + neg = 0; /* neg is |Jn| */ + for (e = ptr; e != NULL; e = e->next) + { if (e->aj == +1.0) + ; + else if (e->aj == -1.0) + neg++; + else + break; + } + if (e == NULL) + { /* all coefficients a[j] are +1 or -1; check rhs b */ + if (b == (double)(1 - neg)) + { /* it is packing inequality; no processing is needed */ + return 1; + } + } + /* substitute x[j] = 1 - x~[j] for all j in Jn to make all a[j] + positive; the result is a~[j] = |a[j]| and new rhs b */ + for (e = ptr; e != NULL; e = e->next) + if (e->aj < 0) b -= e->aj; + /* now a[j] > 0 for all j in J (actually |a[j]| are used) */ + /* if a[j] > b, skip processing--this case must not appear */ + for (e = ptr; e != NULL; e = e->next) + if (fabs(e->aj) > b) return 0; + /* now 0 < a[j] <= b for all j in J */ + /* find two minimal coefficients a[j] and a[k], j != k */ + ej = NULL; + for (e = ptr; e != NULL; e = e->next) + if (ej == NULL || fabs(ej->aj) > fabs(e->aj)) ej = e; + xassert(ej != NULL); + ek = NULL; + for (e = ptr; e != NULL; e = e->next) + if (e != ej) + if (ek == NULL || fabs(ek->aj) > fabs(e->aj)) ek = e; + xassert(ek != NULL); + /* the specified constraint is equivalent to packing inequality + iff a[j] + a[k] > b + eps */ + eps = 1e-3 + 1e-6 * fabs(b); + if (fabs(ej->aj) + fabs(ek->aj) <= b + eps) return 0; + /* perform back substitution x~[j] = 1 - x[j] and construct the + final equivalent packing inequality in generalized format */ + b = 1.0; + for (e = ptr; e != NULL; e = e->next) + { if (e->aj > 0.0) + e->aj = +1.0; + else /* e->aj < 0.0 */ + e->aj = -1.0, b -= 1.0; + } + *_b = b; + return 2; +} + +int npp_hidden_packing(NPP *npp, NPPROW *row) +{ /* identify hidden packing inequality */ + NPPROW *copy; + NPPAIJ *aij; + struct elem *ptr, *e; + int kase, ret, count = 0; + double b; + /* the row must be inequality constraint */ + xassert(row->lb < row->ub); + for (kase = 0; kase <= 1; kase++) + { if (kase == 0) + { /* process row upper bound */ + if (row->ub == +DBL_MAX) continue; + ptr = copy_form(npp, row, +1.0); + b = + row->ub; + } + else + { /* process row lower bound */ + if (row->lb == -DBL_MAX) continue; + ptr = copy_form(npp, row, -1.0); + b = - row->lb; + } + /* now the inequality has the form "sum a[j] x[j] <= b" */ + ret = hidden_packing(npp, ptr, &b); + xassert(0 <= ret && ret <= 2); + if (kase == 1 && ret == 1 || ret == 2) + { /* the original inequality has been identified as hidden + packing inequality */ + count++; +#ifdef GLP_DEBUG + xprintf("Original constraint:\n"); + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + xprintf(" %+g x%d", aij->val, aij->col->j); + if (row->lb != -DBL_MAX) xprintf(", >= %g", row->lb); + if (row->ub != +DBL_MAX) xprintf(", <= %g", row->ub); + xprintf("\n"); + xprintf("Equivalent packing inequality:\n"); + for (e = ptr; e != NULL; e = e->next) + xprintf(" %sx%d", e->aj > 0.0 ? "+" : "-", e->xj->j); + xprintf(", <= %g\n", b); +#endif + if (row->lb == -DBL_MAX || row->ub == +DBL_MAX) + { /* the original row is single-sided inequality; no copy + is needed */ + copy = NULL; + } + else + { /* the original row is double-sided inequality; we need + to create its copy for other bound before replacing it + with the equivalent inequality */ + copy = npp_add_row(npp); + if (kase == 0) + { /* the copy is for lower bound */ + copy->lb = row->lb, copy->ub = +DBL_MAX; + } + else + { /* the copy is for upper bound */ + copy->lb = -DBL_MAX, copy->ub = row->ub; + } + /* copy original row coefficients */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + npp_add_aij(npp, copy, aij->col, aij->val); + } + /* replace the original inequality by equivalent one */ + npp_erase_row(npp, row); + row->lb = -DBL_MAX, row->ub = b; + for (e = ptr; e != NULL; e = e->next) + npp_add_aij(npp, row, e->xj, e->aj); + /* continue processing lower bound for the copy */ + if (copy != NULL) row = copy; + } + drop_form(npp, ptr); + } + return count; +} + +/*********************************************************************** +* NAME +* +* npp_implied_packing - identify implied packing inequality +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_implied_packing(NPP *npp, NPPROW *row, int which, +* NPPCOL *var[], char set[]); +* +* DESCRIPTION +* +* The routine npp_implied_packing processes specified row (constraint) +* of general format: +* +* L <= sum a[j] x[j] <= U. (1) +* j +* +* If which = 0, only lower bound L, which must exist, is considered, +* while upper bound U is ignored. Similarly, if which = 1, only upper +* bound U, which must exist, is considered, while lower bound L is +* ignored. Thus, if the specified row is a double-sided inequality or +* equality constraint, this routine should be called twice for both +* lower and upper bounds. +* +* The routine npp_implied_packing attempts to find a non-trivial (i.e. +* having not less than two binary variables) packing inequality: +* +* sum x[j] - sum x[j] <= 1 - |Jn|, (2) +* j in Jp j in Jn +* +* which is relaxation of the constraint (1) in the sense that any +* solution satisfying to that constraint also satisfies to the packing +* inequality (2). If such relaxation exists, the routine stores +* pointers to descriptors of corresponding binary variables and their +* flags, resp., to locations var[1], var[2], ..., var[len] and set[1], +* set[2], ..., set[len], where set[j] = 0 means that j in Jp and +* set[j] = 1 means that j in Jn. +* +* RETURNS +* +* The routine npp_implied_packing returns len, which is the total +* number of binary variables in the packing inequality found, len >= 2. +* However, if the relaxation does not exist, the routine returns zero. +* +* ALGORITHM +* +* If which = 0, the constraint coefficients (1) are multiplied by -1 +* and b is assigned -L; if which = 1, the constraint coefficients (1) +* are not changed and b is assigned +U. In both cases the specified +* constraint gets the following format: +* +* sum a[j] x[j] <= b. (3) +* j +* +* (Note that (3) is a relaxation of (1), because one of bounds L or U +* is ignored.) +* +* Let J be set of binary variables, Kp be set of non-binary (integer +* or continuous) variables with a[j] > 0, and Kn be set of non-binary +* variables with a[j] < 0. Then the inequality (3) can be written as +* follows: +* +* sum a[j] x[j] <= b - sum a[j] x[j] - sum a[j] x[j]. (4) +* j in J j in Kp j in Kn +* +* To get rid of non-binary variables we can replace the inequality (4) +* by the following relaxed inequality: +* +* sum a[j] x[j] <= b~, (5) +* j in J +* +* where: +* +* b~ = sup(b - sum a[j] x[j] - sum a[j] x[j]) = +* j in Kp j in Kn +* +* = b - inf sum a[j] x[j] - inf sum a[j] x[j] = (6) +* j in Kp j in Kn +* +* = b - sum a[j] l[j] - sum a[j] u[j]. +* j in Kp j in Kn +* +* Note that if lower bound l[j] (if j in Kp) or upper bound u[j] +* (if j in Kn) of some non-binary variable x[j] does not exist, then +* formally b = +oo, in which case further analysis is not performed. +* +* Let Bp = {j in J: a[j] > 0}, Bn = {j in J: a[j] < 0}. To make all +* the inequality coefficients in (5) positive, we replace all x[j] in +* Bn by their complementaries, substituting x[j] = 1 - x~[j] for all +* j in Bn, that gives: +* +* sum a[j] x[j] - sum a[j] x~[j] <= b~ - sum a[j]. (7) +* j in Bp j in Bn j in Bn +* +* This inequality is a relaxation of the original constraint (1), and +* it is a binary knapsack inequality. Writing it in the standard format +* we have: +* +* sum alfa[j] z[j] <= beta, (8) +* j in J +* +* where: +* ( + a[j], if j in Bp, +* alfa[j] = < (9) +* ( - a[j], if j in Bn, +* +* ( x[j], if j in Bp, +* z[j] = < (10) +* ( 1 - x[j], if j in Bn, +* +* beta = b~ - sum a[j]. (11) +* j in Bn +* +* In the inequality (8) all coefficients are positive, therefore, the +* packing relaxation to be found for this inequality is the following: +* +* sum z[j] <= 1. (12) +* j in P +* +* It is obvious that set P within J, which we would like to find, must +* satisfy to the following condition: +* +* alfa[j] + alfa[k] > beta + eps for all j, k in P, j != k, (13) +* +* where eps is an absolute tolerance for value of the linear form. +* Thus, it is natural to take P = {j: alpha[j] > (beta + eps) / 2}. +* Moreover, if in the equality (8) there exist coefficients alfa[k], +* for which alfa[k] <= (beta + eps) / 2, but which, nevertheless, +* satisfies to the condition (13) for all j in P, *one* corresponding +* variable z[k] (having, for example, maximal coefficient alfa[k]) can +* be included in set P, that allows increasing the number of binary +* variables in (12) by one. +* +* Once the set P has been built, for the inequality (12) we need to +* perform back substitution according to (10) in order to express it +* through the original binary variables. As the result of such back +* substitution the relaxed packing inequality get its final format (2), +* where Jp = J intersect Bp, and Jn = J intersect Bn. */ + +int npp_implied_packing(NPP *npp, NPPROW *row, int which, + NPPCOL *var[], char set[]) +{ struct elem *ptr, *e, *i, *k; + int len = 0; + double b, eps; + /* build inequality (3) */ + if (which == 0) + { ptr = copy_form(npp, row, -1.0); + xassert(row->lb != -DBL_MAX); + b = - row->lb; + } + else if (which == 1) + { ptr = copy_form(npp, row, +1.0); + xassert(row->ub != +DBL_MAX); + b = + row->ub; + } + /* remove non-binary variables to build relaxed inequality (5); + compute its right-hand side b~ with formula (6) */ + for (e = ptr; e != NULL; e = e->next) + { if (!(e->xj->is_int && e->xj->lb == 0.0 && e->xj->ub == 1.0)) + { /* x[j] is non-binary variable */ + if (e->aj > 0.0) + { if (e->xj->lb == -DBL_MAX) goto done; + b -= e->aj * e->xj->lb; + } + else /* e->aj < 0.0 */ + { if (e->xj->ub == +DBL_MAX) goto done; + b -= e->aj * e->xj->ub; + } + /* a[j] = 0 means that variable x[j] is removed */ + e->aj = 0.0; + } + } + /* substitute x[j] = 1 - x~[j] to build knapsack inequality (8); + compute its right-hand side beta with formula (11) */ + for (e = ptr; e != NULL; e = e->next) + if (e->aj < 0.0) b -= e->aj; + /* if beta is close to zero, the knapsack inequality is either + infeasible or forcing inequality; this must never happen, so + we skip further analysis */ + if (b < 1e-3) goto done; + /* build set P as well as sets Jp and Jn, and determine x[k] as + explained above in comments to the routine */ + eps = 1e-3 + 1e-6 * b; + i = k = NULL; + for (e = ptr; e != NULL; e = e->next) + { /* note that alfa[j] = |a[j]| */ + if (fabs(e->aj) > 0.5 * (b + eps)) + { /* alfa[j] > (b + eps) / 2; include x[j] in set P, i.e. in + set Jp or Jn */ + var[++len] = e->xj; + set[len] = (char)(e->aj > 0.0 ? 0 : 1); + /* alfa[i] = min alfa[j] over all j included in set P */ + if (i == NULL || fabs(i->aj) > fabs(e->aj)) i = e; + } + else if (fabs(e->aj) >= 1e-3) + { /* alfa[k] = max alfa[j] over all j not included in set P; + we skip coefficient a[j] if it is close to zero to avoid + numerically unreliable results */ + if (k == NULL || fabs(k->aj) < fabs(e->aj)) k = e; + } + } + /* if alfa[k] satisfies to condition (13) for all j in P, include + x[k] in P */ + if (i != NULL && k != NULL && fabs(i->aj) + fabs(k->aj) > b + eps) + { var[++len] = k->xj; + set[len] = (char)(k->aj > 0.0 ? 0 : 1); + } + /* trivial packing inequality being redundant must never appear, + so we just ignore it */ + if (len < 2) len = 0; +done: drop_form(npp, ptr); + return len; +} + +/*********************************************************************** +* NAME +* +* npp_is_covering - test if constraint is covering inequality +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_is_covering(NPP *npp, NPPROW *row); +* +* RETURNS +* +* If the specified row (constraint) is covering inequality (see below), +* the routine npp_is_covering returns non-zero. Otherwise, it returns +* zero. +* +* COVERING INEQUALITIES +* +* In canonical format the covering inequality is the following: +* +* sum x[j] >= 1, (1) +* j in J +* +* where all variables x[j] are binary. This inequality expresses the +* condition that in any integer feasible solution variables in set J +* cannot be all equal to zero at the same time, i.e. at least one +* variable must take non-zero (unity) value. W.l.o.g. it is assumed +* that |J| >= 2, because if J is empty, the inequality (1) is +* infeasible, and if |J| = 1, the inequality (1) is a forcing row. +* +* In general case the covering inequality may include original +* variables x[j] as well as their complements x~[j]: +* +* sum x[j] + sum x~[j] >= 1, (2) +* j in Jp j in Jn +* +* where Jp and Jn are not intersected. Therefore, using substitution +* x~[j] = 1 - x[j] gives the packing inequality in generalized format: +* +* sum x[j] - sum x[j] >= 1 - |Jn|. (3) +* j in Jp j in Jn +* +* (May note that the inequality (3) cuts off infeasible solutions, +* where x[j] = 0 for all j in Jp and x[j] = 1 for all j in Jn.) +* +* NOTE: If |J| = 2, the inequality (3) is equivalent to packing +* inequality (see the routine npp_is_packing). */ + +int npp_is_covering(NPP *npp, NPPROW *row) +{ /* test if constraint is covering inequality */ + NPPCOL *col; + NPPAIJ *aij; + int b; + xassert(npp == npp); + if (!(row->lb != -DBL_MAX && row->ub == +DBL_MAX)) + return 0; + b = 1; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col = aij->col; + if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) + return 0; + if (aij->val == +1.0) + ; + else if (aij->val == -1.0) + b--; + else + return 0; + } + if (row->lb != (double)b) return 0; + return 1; +} + +/*********************************************************************** +* NAME +* +* npp_hidden_covering - identify hidden covering inequality +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_hidden_covering(NPP *npp, NPPROW *row); +* +* DESCRIPTION +* +* The routine npp_hidden_covering processes specified inequality +* constraint, which includes only binary variables, and the number of +* the variables is not less than three. If the original inequality is +* equivalent to a covering inequality (see below), the routine +* replaces it by the equivalent inequality. If the original constraint +* is double-sided inequality, it is replaced by a pair of single-sided +* inequalities, if necessary. +* +* RETURNS +* +* If the original inequality constraint was replaced by equivalent +* covering inequality, the routine npp_hidden_covering returns +* non-zero. Otherwise, it returns zero. +* +* PROBLEM TRANSFORMATION +* +* Consider an inequality constraint: +* +* sum a[j] x[j] >= b, (1) +* j in J +* +* where all variables x[j] are binary, and |J| >= 3. (In case of '<=' +* inequality it can be transformed to '>=' format by multiplying both +* its sides by -1.) +* +* Let Jp = {j: a[j] > 0}, Jn = {j: a[j] < 0}. Performing substitution +* x[j] = 1 - x~[j] for all j in Jn, we have: +* +* sum a[j] x[j] >= b ==> +* j in J +* +* sum a[j] x[j] + sum a[j] x[j] >= b ==> +* j in Jp j in Jn +* +* sum a[j] x[j] + sum a[j] (1 - x~[j]) >= b ==> +* j in Jp j in Jn +* +* sum m a[j] x[j] - sum a[j] x~[j] >= b - sum a[j]. +* j in Jp j in Jn j in Jn +* +* Thus, meaning the transformation above, we can assume that in +* inequality (1) all coefficients a[j] are positive. Moreover, we can +* assume that b > 0, because otherwise the inequality (1) would be +* redundant (see the routine npp_analyze_row). It is then obvious that +* constraint (1) is equivalent to covering inequality only if: +* +* a[j] >= b, (2) +* +* for all j in J. +* +* Once the original inequality (1) is replaced by equivalent covering +* inequality, we need to perform back substitution x~[j] = 1 - x[j] for +* all j in Jn (see above). +* +* RECOVERING SOLUTION +* +* None needed. */ + +static int hidden_covering(NPP *npp, struct elem *ptr, double *_b) +{ /* process inequality constraint: sum a[j] x[j] >= b; + 0 - specified row is NOT hidden covering inequality; + 1 - specified row is covering inequality; + 2 - specified row is hidden covering inequality. */ + struct elem *e; + int neg; + double b = *_b, eps; + xassert(npp == npp); + /* a[j] must be non-zero, x[j] must be binary, for all j in J */ + for (e = ptr; e != NULL; e = e->next) + { xassert(e->aj != 0.0); + xassert(e->xj->is_int); + xassert(e->xj->lb == 0.0 && e->xj->ub == 1.0); + } + /* check if the specified inequality constraint already has the + form of covering inequality */ + neg = 0; /* neg is |Jn| */ + for (e = ptr; e != NULL; e = e->next) + { if (e->aj == +1.0) + ; + else if (e->aj == -1.0) + neg++; + else + break; + } + if (e == NULL) + { /* all coefficients a[j] are +1 or -1; check rhs b */ + if (b == (double)(1 - neg)) + { /* it is covering inequality; no processing is needed */ + return 1; + } + } + /* substitute x[j] = 1 - x~[j] for all j in Jn to make all a[j] + positive; the result is a~[j] = |a[j]| and new rhs b */ + for (e = ptr; e != NULL; e = e->next) + if (e->aj < 0) b -= e->aj; + /* now a[j] > 0 for all j in J (actually |a[j]| are used) */ + /* if b <= 0, skip processing--this case must not appear */ + if (b < 1e-3) return 0; + /* now a[j] > 0 for all j in J, and b > 0 */ + /* the specified constraint is equivalent to covering inequality + iff a[j] >= b for all j in J */ + eps = 1e-9 + 1e-12 * fabs(b); + for (e = ptr; e != NULL; e = e->next) + if (fabs(e->aj) < b - eps) return 0; + /* perform back substitution x~[j] = 1 - x[j] and construct the + final equivalent covering inequality in generalized format */ + b = 1.0; + for (e = ptr; e != NULL; e = e->next) + { if (e->aj > 0.0) + e->aj = +1.0; + else /* e->aj < 0.0 */ + e->aj = -1.0, b -= 1.0; + } + *_b = b; + return 2; +} + +int npp_hidden_covering(NPP *npp, NPPROW *row) +{ /* identify hidden covering inequality */ + NPPROW *copy; + NPPAIJ *aij; + struct elem *ptr, *e; + int kase, ret, count = 0; + double b; + /* the row must be inequality constraint */ + xassert(row->lb < row->ub); + for (kase = 0; kase <= 1; kase++) + { if (kase == 0) + { /* process row lower bound */ + if (row->lb == -DBL_MAX) continue; + ptr = copy_form(npp, row, +1.0); + b = + row->lb; + } + else + { /* process row upper bound */ + if (row->ub == +DBL_MAX) continue; + ptr = copy_form(npp, row, -1.0); + b = - row->ub; + } + /* now the inequality has the form "sum a[j] x[j] >= b" */ + ret = hidden_covering(npp, ptr, &b); + xassert(0 <= ret && ret <= 2); + if (kase == 1 && ret == 1 || ret == 2) + { /* the original inequality has been identified as hidden + covering inequality */ + count++; +#ifdef GLP_DEBUG + xprintf("Original constraint:\n"); + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + xprintf(" %+g x%d", aij->val, aij->col->j); + if (row->lb != -DBL_MAX) xprintf(", >= %g", row->lb); + if (row->ub != +DBL_MAX) xprintf(", <= %g", row->ub); + xprintf("\n"); + xprintf("Equivalent covering inequality:\n"); + for (e = ptr; e != NULL; e = e->next) + xprintf(" %sx%d", e->aj > 0.0 ? "+" : "-", e->xj->j); + xprintf(", >= %g\n", b); +#endif + if (row->lb == -DBL_MAX || row->ub == +DBL_MAX) + { /* the original row is single-sided inequality; no copy + is needed */ + copy = NULL; + } + else + { /* the original row is double-sided inequality; we need + to create its copy for other bound before replacing it + with the equivalent inequality */ + copy = npp_add_row(npp); + if (kase == 0) + { /* the copy is for upper bound */ + copy->lb = -DBL_MAX, copy->ub = row->ub; + } + else + { /* the copy is for lower bound */ + copy->lb = row->lb, copy->ub = +DBL_MAX; + } + /* copy original row coefficients */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + npp_add_aij(npp, copy, aij->col, aij->val); + } + /* replace the original inequality by equivalent one */ + npp_erase_row(npp, row); + row->lb = b, row->ub = +DBL_MAX; + for (e = ptr; e != NULL; e = e->next) + npp_add_aij(npp, row, e->xj, e->aj); + /* continue processing upper bound for the copy */ + if (copy != NULL) row = copy; + } + drop_form(npp, ptr); + } + return count; +} + +/*********************************************************************** +* NAME +* +* npp_is_partitioning - test if constraint is partitioning equality +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_is_partitioning(NPP *npp, NPPROW *row); +* +* RETURNS +* +* If the specified row (constraint) is partitioning equality (see +* below), the routine npp_is_partitioning returns non-zero. Otherwise, +* it returns zero. +* +* PARTITIONING EQUALITIES +* +* In canonical format the partitioning equality is the following: +* +* sum x[j] = 1, (1) +* j in J +* +* where all variables x[j] are binary. This equality expresses the +* condition that in any integer feasible solution exactly one variable +* in set J must take non-zero (unity) value while other variables must +* be equal to zero. W.l.o.g. it is assumed that |J| >= 2, because if +* J is empty, the inequality (1) is infeasible, and if |J| = 1, the +* inequality (1) is a fixing row. +* +* In general case the partitioning equality may include original +* variables x[j] as well as their complements x~[j]: +* +* sum x[j] + sum x~[j] = 1, (2) +* j in Jp j in Jn +* +* where Jp and Jn are not intersected. Therefore, using substitution +* x~[j] = 1 - x[j] leads to the partitioning equality in generalized +* format: +* +* sum x[j] - sum x[j] = 1 - |Jn|. (3) +* j in Jp j in Jn */ + +int npp_is_partitioning(NPP *npp, NPPROW *row) +{ /* test if constraint is partitioning equality */ + NPPCOL *col; + NPPAIJ *aij; + int b; + xassert(npp == npp); + if (row->lb != row->ub) return 0; + b = 1; + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col = aij->col; + if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) + return 0; + if (aij->val == +1.0) + ; + else if (aij->val == -1.0) + b--; + else + return 0; + } + if (row->lb != (double)b) return 0; + return 1; +} + +/*********************************************************************** +* NAME +* +* npp_reduce_ineq_coef - reduce inequality constraint coefficients +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_reduce_ineq_coef(NPP *npp, NPPROW *row); +* +* DESCRIPTION +* +* The routine npp_reduce_ineq_coef processes specified inequality +* constraint attempting to replace it by an equivalent constraint, +* where magnitude of coefficients at binary variables is smaller than +* in the original constraint. If the inequality is double-sided, it is +* replaced by a pair of single-sided inequalities, if necessary. +* +* RETURNS +* +* The routine npp_reduce_ineq_coef returns the number of coefficients +* reduced. +* +* BACKGROUND +* +* Consider an inequality constraint: +* +* sum a[j] x[j] >= b. (1) +* j in J +* +* (In case of '<=' inequality it can be transformed to '>=' format by +* multiplying both its sides by -1.) Let x[k] be a binary variable; +* other variables can be integer as well as continuous. We can write +* constraint (1) as follows: +* +* a[k] x[k] + t[k] >= b, (2) +* +* where: +* +* t[k] = sum a[j] x[j]. (3) +* j in J\{k} +* +* Since x[k] is binary, constraint (2) is equivalent to disjunction of +* the following two constraints: +* +* x[k] = 0, t[k] >= b (4) +* +* OR +* +* x[k] = 1, t[k] >= b - a[k]. (5) +* +* Let also that for the partial sum t[k] be known some its implied +* lower bound inf t[k]. +* +* Case a[k] > 0. Let inf t[k] < b, since otherwise both constraints +* (4) and (5) and therefore constraint (2) are redundant. +* If inf t[k] > b - a[k], only constraint (5) is redundant, in which +* case it can be replaced with the following redundant and therefore +* equivalent constraint: +* +* t[k] >= b - a'[k] = inf t[k], (6) +* +* where: +* +* a'[k] = b - inf t[k]. (7) +* +* Thus, the original constraint (2) is equivalent to the following +* constraint with coefficient at variable x[k] changed: +* +* a'[k] x[k] + t[k] >= b. (8) +* +* From inf t[k] < b it follows that a'[k] > 0, i.e. the coefficient +* at x[k] keeps its sign. And from inf t[k] > b - a[k] it follows that +* a'[k] < a[k], i.e. the coefficient reduces in magnitude. +* +* Case a[k] < 0. Let inf t[k] < b - a[k], since otherwise both +* constraints (4) and (5) and therefore constraint (2) are redundant. +* If inf t[k] > b, only constraint (4) is redundant, in which case it +* can be replaced with the following redundant and therefore equivalent +* constraint: +* +* t[k] >= b' = inf t[k]. (9) +* +* Rewriting constraint (5) as follows: +* +* t[k] >= b - a[k] = b' - a'[k], (10) +* +* where: +* +* a'[k] = a[k] + b' - b = a[k] + inf t[k] - b, (11) +* +* we can see that disjunction of constraint (9) and (10) is equivalent +* to disjunction of constraint (4) and (5), from which it follows that +* the original constraint (2) is equivalent to the following constraint +* with both coefficient at variable x[k] and right-hand side changed: +* +* a'[k] x[k] + t[k] >= b'. (12) +* +* From inf t[k] < b - a[k] it follows that a'[k] < 0, i.e. the +* coefficient at x[k] keeps its sign. And from inf t[k] > b it follows +* that a'[k] > a[k], i.e. the coefficient reduces in magnitude. +* +* PROBLEM TRANSFORMATION +* +* In the routine npp_reduce_ineq_coef the following implied lower +* bound of the partial sum (3) is used: +* +* inf t[k] = sum a[j] l[j] + sum a[j] u[j], (13) +* j in Jp\{k} k in Jn\{k} +* +* where Jp = {j : a[j] > 0}, Jn = {j : a[j] < 0}, l[j] and u[j] are +* lower and upper bounds, resp., of variable x[j]. +* +* In order to compute inf t[k] more efficiently, the following formula, +* which is equivalent to (13), is actually used: +* +* ( h - a[k] l[k] = h, if a[k] > 0, +* inf t[k] = < (14) +* ( h - a[k] u[k] = h - a[k], if a[k] < 0, +* +* where: +* +* h = sum a[j] l[j] + sum a[j] u[j] (15) +* j in Jp j in Jn +* +* is the implied lower bound of row (1). +* +* Reduction of positive coefficient (a[k] > 0) does not change value +* of h, since l[k] = 0. In case of reduction of negative coefficient +* (a[k] < 0) from (11) it follows that: +* +* delta a[k] = a'[k] - a[k] = inf t[k] - b (> 0), (16) +* +* so new value of h (accounting that u[k] = 1) can be computed as +* follows: +* +* h := h + delta a[k] = h + (inf t[k] - b). (17) +* +* RECOVERING SOLUTION +* +* None needed. */ + +static int reduce_ineq_coef(NPP *npp, struct elem *ptr, double *_b) +{ /* process inequality constraint: sum a[j] x[j] >= b */ + /* returns: the number of coefficients reduced */ + struct elem *e; + int count = 0; + double h, inf_t, new_a, b = *_b; + xassert(npp == npp); + /* compute h; see (15) */ + h = 0.0; + for (e = ptr; e != NULL; e = e->next) + { if (e->aj > 0.0) + { if (e->xj->lb == -DBL_MAX) goto done; + h += e->aj * e->xj->lb; + } + else /* e->aj < 0.0 */ + { if (e->xj->ub == +DBL_MAX) goto done; + h += e->aj * e->xj->ub; + } + } + /* perform reduction of coefficients at binary variables */ + for (e = ptr; e != NULL; e = e->next) + { /* skip non-binary variable */ + if (!(e->xj->is_int && e->xj->lb == 0.0 && e->xj->ub == 1.0)) + continue; + if (e->aj > 0.0) + { /* compute inf t[k]; see (14) */ + inf_t = h; + if (b - e->aj < inf_t && inf_t < b) + { /* compute reduced coefficient a'[k]; see (7) */ + new_a = b - inf_t; + if (new_a >= +1e-3 && + e->aj - new_a >= 0.01 * (1.0 + e->aj)) + { /* accept a'[k] */ +#ifdef GLP_DEBUG + xprintf("+"); +#endif + e->aj = new_a; + count++; + } + } + } + else /* e->aj < 0.0 */ + { /* compute inf t[k]; see (14) */ + inf_t = h - e->aj; + if (b < inf_t && inf_t < b - e->aj) + { /* compute reduced coefficient a'[k]; see (11) */ + new_a = e->aj + (inf_t - b); + if (new_a <= -1e-3 && + new_a - e->aj >= 0.01 * (1.0 - e->aj)) + { /* accept a'[k] */ +#ifdef GLP_DEBUG + xprintf("-"); +#endif + e->aj = new_a; + /* update h; see (17) */ + h += (inf_t - b); + /* compute b'; see (9) */ + b = inf_t; + count++; + } + } + } + } + *_b = b; +done: return count; +} + +int npp_reduce_ineq_coef(NPP *npp, NPPROW *row) +{ /* reduce inequality constraint coefficients */ + NPPROW *copy; + NPPAIJ *aij; + struct elem *ptr, *e; + int kase, count[2]; + double b; + /* the row must be inequality constraint */ + xassert(row->lb < row->ub); + count[0] = count[1] = 0; + for (kase = 0; kase <= 1; kase++) + { if (kase == 0) + { /* process row lower bound */ + if (row->lb == -DBL_MAX) continue; +#ifdef GLP_DEBUG + xprintf("L"); +#endif + ptr = copy_form(npp, row, +1.0); + b = + row->lb; + } + else + { /* process row upper bound */ + if (row->ub == +DBL_MAX) continue; +#ifdef GLP_DEBUG + xprintf("U"); +#endif + ptr = copy_form(npp, row, -1.0); + b = - row->ub; + } + /* now the inequality has the form "sum a[j] x[j] >= b" */ + count[kase] = reduce_ineq_coef(npp, ptr, &b); + if (count[kase] > 0) + { /* the original inequality has been replaced by equivalent + one with coefficients reduced */ + if (row->lb == -DBL_MAX || row->ub == +DBL_MAX) + { /* the original row is single-sided inequality; no copy + is needed */ + copy = NULL; + } + else + { /* the original row is double-sided inequality; we need + to create its copy for other bound before replacing it + with the equivalent inequality */ +#ifdef GLP_DEBUG + xprintf("*"); +#endif + copy = npp_add_row(npp); + if (kase == 0) + { /* the copy is for upper bound */ + copy->lb = -DBL_MAX, copy->ub = row->ub; + } + else + { /* the copy is for lower bound */ + copy->lb = row->lb, copy->ub = +DBL_MAX; + } + /* copy original row coefficients */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + npp_add_aij(npp, copy, aij->col, aij->val); + } + /* replace the original inequality by equivalent one */ + npp_erase_row(npp, row); + row->lb = b, row->ub = +DBL_MAX; + for (e = ptr; e != NULL; e = e->next) + npp_add_aij(npp, row, e->xj, e->aj); + /* continue processing upper bound for the copy */ + if (copy != NULL) row = copy; + } + drop_form(npp, ptr); + } + return count[0] + count[1]; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpnpp05.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpnpp05.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,809 @@ +/* glpnpp05.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpnpp.h" + +/*********************************************************************** +* NAME +* +* npp_clean_prob - perform initial LP/MIP processing +* +* SYNOPSIS +* +* #include "glpnpp.h" +* void npp_clean_prob(NPP *npp); +* +* DESCRIPTION +* +* The routine npp_clean_prob performs initial LP/MIP processing that +* currently includes: +* +* 1) removing free rows; +* +* 2) replacing double-sided constraint rows with almost identical +* bounds, by equality constraint rows; +* +* 3) removing fixed columns; +* +* 4) replacing double-bounded columns with almost identical bounds by +* fixed columns and removing those columns; +* +* 5) initial processing constraint coefficients (not implemented); +* +* 6) initial processing objective coefficients (not implemented). */ + +void npp_clean_prob(NPP *npp) +{ /* perform initial LP/MIP processing */ + NPPROW *row, *next_row; + NPPCOL *col, *next_col; + int ret; + xassert(npp == npp); + /* process rows which originally are free */ + for (row = npp->r_head; row != NULL; row = next_row) + { next_row = row->next; + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) + { /* process free row */ +#ifdef GLP_DEBUG + xprintf("1"); +#endif + npp_free_row(npp, row); + /* row was deleted */ + } + } + /* process rows which originally are double-sided inequalities */ + for (row = npp->r_head; row != NULL; row = next_row) + { next_row = row->next; + if (row->lb != -DBL_MAX && row->ub != +DBL_MAX && + row->lb < row->ub) + { ret = npp_make_equality(npp, row); + if (ret == 0) + ; + else if (ret == 1) + { /* row was replaced by equality constraint */ +#ifdef GLP_DEBUG + xprintf("2"); +#endif + } + else + xassert(ret != ret); + } + } + /* process columns which are originally fixed */ + for (col = npp->c_head; col != NULL; col = next_col) + { next_col = col->next; + if (col->lb == col->ub) + { /* process fixed column */ +#ifdef GLP_DEBUG + xprintf("3"); +#endif + npp_fixed_col(npp, col); + /* column was deleted */ + } + } + /* process columns which are originally double-bounded */ + for (col = npp->c_head; col != NULL; col = next_col) + { next_col = col->next; + if (col->lb != -DBL_MAX && col->ub != +DBL_MAX && + col->lb < col->ub) + { ret = npp_make_fixed(npp, col); + if (ret == 0) + ; + else if (ret == 1) + { /* column was replaced by fixed column; process it */ +#ifdef GLP_DEBUG + xprintf("4"); +#endif + npp_fixed_col(npp, col); + /* column was deleted */ + } + } + } + return; +} + +/*********************************************************************** +* NAME +* +* npp_process_row - perform basic row processing +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_process_row(NPP *npp, NPPROW *row, int hard); +* +* DESCRIPTION +* +* The routine npp_process_row performs basic row processing that +* currently includes: +* +* 1) removing empty row; +* +* 2) removing equality constraint row singleton and corresponding +* column; +* +* 3) removing inequality constraint row singleton and corresponding +* column if it was fixed; +* +* 4) performing general row analysis; +* +* 5) removing redundant row bounds; +* +* 6) removing forcing row and corresponding columns; +* +* 7) removing row which becomes free due to redundant bounds; +* +* 8) computing implied bounds for all columns in the row and using +* them to strengthen current column bounds (MIP only, optional, +* performed if the flag hard is on). +* +* Additionally the routine may activate affected rows and/or columns +* for further processing. +* +* RETURNS +* +* 0 success; +* +* GLP_ENOPFS primal/integer infeasibility detected; +* +* GLP_ENODFS dual infeasibility detected. */ + +int npp_process_row(NPP *npp, NPPROW *row, int hard) +{ /* perform basic row processing */ + NPPCOL *col; + NPPAIJ *aij, *next_aij, *aaa; + int ret; + /* row must not be free */ + xassert(!(row->lb == -DBL_MAX && row->ub == +DBL_MAX)); + /* start processing row */ + if (row->ptr == NULL) + { /* empty row */ + ret = npp_empty_row(npp, row); + if (ret == 0) + { /* row was deleted */ +#ifdef GLP_DEBUG + xprintf("A"); +#endif + return 0; + } + else if (ret == 1) + { /* primal infeasibility */ + return GLP_ENOPFS; + } + else + xassert(ret != ret); + } + if (row->ptr->r_next == NULL) + { /* row singleton */ + col = row->ptr->col; + if (row->lb == row->ub) + { /* equality constraint */ + ret = npp_eq_singlet(npp, row); + if (ret == 0) + { /* column was fixed, row was deleted */ +#ifdef GLP_DEBUG + xprintf("B"); +#endif + /* activate rows affected by column */ + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + npp_activate_row(npp, aij->row); + /* process fixed column */ + npp_fixed_col(npp, col); + /* column was deleted */ + return 0; + } + else if (ret == 1 || ret == 2) + { /* primal/integer infeasibility */ + return GLP_ENOPFS; + } + else + xassert(ret != ret); + } + else + { /* inequality constraint */ + ret = npp_ineq_singlet(npp, row); + if (0 <= ret && ret <= 3) + { /* row was deleted */ +#ifdef GLP_DEBUG + xprintf("C"); +#endif + /* activate column, since its length was changed due to + row deletion */ + npp_activate_col(npp, col); + if (ret >= 2) + { /* column bounds changed significantly or column was + fixed */ + /* activate rows affected by column */ + for (aij = col->ptr; aij != NULL; aij = aij->c_next) + npp_activate_row(npp, aij->row); + } + if (ret == 3) + { /* column was fixed; process it */ +#ifdef GLP_DEBUG + xprintf("D"); +#endif + npp_fixed_col(npp, col); + /* column was deleted */ + } + return 0; + } + else if (ret == 4) + { /* primal infeasibility */ + return GLP_ENOPFS; + } + else + xassert(ret != ret); + } + } +#if 0 + /* sometimes this causes too large round-off errors; probably + pivot coefficient should be chosen more carefully */ + if (row->ptr->r_next->r_next == NULL) + { /* row doubleton */ + if (row->lb == row->ub) + { /* equality constraint */ + if (!(row->ptr->col->is_int || + row->ptr->r_next->col->is_int)) + { /* both columns are continuous */ + NPPCOL *q; + q = npp_eq_doublet(npp, row); + if (q != NULL) + { /* column q was eliminated */ +#ifdef GLP_DEBUG + xprintf("E"); +#endif + /* now column q is singleton of type "implied slack + variable"; we process it here to make sure that on + recovering basic solution the row is always active + equality constraint (as required by the routine + rcv_eq_doublet) */ + xassert(npp_process_col(npp, q) == 0); + /* column q was deleted; note that row p also may be + deleted */ + return 0; + } + } + } + } +#endif + /* general row analysis */ + ret = npp_analyze_row(npp, row); + xassert(0x00 <= ret && ret <= 0xFF); + if (ret == 0x33) + { /* row bounds are inconsistent with column bounds */ + return GLP_ENOPFS; + } + if ((ret & 0x0F) == 0x00) + { /* row lower bound does not exist or redundant */ + if (row->lb != -DBL_MAX) + { /* remove redundant row lower bound */ +#ifdef GLP_DEBUG + xprintf("F"); +#endif + npp_inactive_bound(npp, row, 0); + } + } + else if ((ret & 0x0F) == 0x01) + { /* row lower bound can be active */ + /* see below */ + } + else if ((ret & 0x0F) == 0x02) + { /* row lower bound is a forcing bound */ +#ifdef GLP_DEBUG + xprintf("G"); +#endif + /* process forcing row */ + if (npp_forcing_row(npp, row, 0) == 0) +fixup: { /* columns were fixed, row was made free */ + for (aij = row->ptr; aij != NULL; aij = next_aij) + { /* process column fixed by forcing row */ +#ifdef GLP_DEBUG + xprintf("H"); +#endif + col = aij->col; + next_aij = aij->r_next; + /* activate rows affected by column */ + for (aaa = col->ptr; aaa != NULL; aaa = aaa->c_next) + npp_activate_row(npp, aaa->row); + /* process fixed column */ + npp_fixed_col(npp, col); + /* column was deleted */ + } + /* process free row (which now is empty due to deletion of + all its columns) */ + npp_free_row(npp, row); + /* row was deleted */ + return 0; + } + } + else + xassert(ret != ret); + if ((ret & 0xF0) == 0x00) + { /* row upper bound does not exist or redundant */ + if (row->ub != +DBL_MAX) + { /* remove redundant row upper bound */ +#ifdef GLP_DEBUG + xprintf("I"); +#endif + npp_inactive_bound(npp, row, 1); + } + } + else if ((ret & 0xF0) == 0x10) + { /* row upper bound can be active */ + /* see below */ + } + else if ((ret & 0xF0) == 0x20) + { /* row upper bound is a forcing bound */ +#ifdef GLP_DEBUG + xprintf("J"); +#endif + /* process forcing row */ + if (npp_forcing_row(npp, row, 1) == 0) goto fixup; + } + else + xassert(ret != ret); + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) + { /* row became free due to redundant bounds removal */ +#ifdef GLP_DEBUG + xprintf("K"); +#endif + /* activate its columns, since their length will change due + to row deletion */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + npp_activate_col(npp, aij->col); + /* process free row */ + npp_free_row(npp, row); + /* row was deleted */ + return 0; + } +#if 1 /* 23/XII-2009 */ + /* row lower and/or upper bounds can be active */ + if (npp->sol == GLP_MIP && hard) + { /* improve current column bounds (optional) */ + if (npp_improve_bounds(npp, row, 1) < 0) + return GLP_ENOPFS; + } +#endif + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_improve_bounds - improve current column bounds +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_improve_bounds(NPP *npp, NPPROW *row, int flag); +* +* DESCRIPTION +* +* The routine npp_improve_bounds analyzes specified row (inequality +* or equality constraint) to determine implied column bounds and then +* uses these bounds to improve (strengthen) current column bounds. +* +* If the flag is on and current column bounds changed significantly +* or the column was fixed, the routine activate rows affected by the +* column for further processing. (This feature is intended to be used +* in the main loop of the routine npp_process_row.) +* +* NOTE: This operation can be used for MIP problem only. +* +* RETURNS +* +* The routine npp_improve_bounds returns the number of significantly +* changed bounds plus the number of column having been fixed due to +* bound improvements. However, if the routine detects primal/integer +* infeasibility, it returns a negative value. */ + +int npp_improve_bounds(NPP *npp, NPPROW *row, int flag) +{ /* improve current column bounds */ + NPPCOL *col; + NPPAIJ *aij, *next_aij, *aaa; + int kase, ret, count = 0; + double lb, ub; + xassert(npp->sol == GLP_MIP); + /* row must not be free */ + xassert(!(row->lb == -DBL_MAX && row->ub == +DBL_MAX)); + /* determine implied column bounds */ + npp_implied_bounds(npp, row); + /* and use these bounds to strengthen current column bounds */ + for (aij = row->ptr; aij != NULL; aij = next_aij) + { col = aij->col; + next_aij = aij->r_next; + for (kase = 0; kase <= 1; kase++) + { /* save current column bounds */ + lb = col->lb, ub = col->ub; + if (kase == 0) + { /* process implied column lower bound */ + if (col->ll.ll == -DBL_MAX) continue; + ret = npp_implied_lower(npp, col, col->ll.ll); + } + else + { /* process implied column upper bound */ + if (col->uu.uu == +DBL_MAX) continue; + ret = npp_implied_upper(npp, col, col->uu.uu); + } + if (ret == 0 || ret == 1) + { /* current column bounds did not change or changed, but + not significantly; restore current column bounds */ + col->lb = lb, col->ub = ub; + } + else if (ret == 2 || ret == 3) + { /* current column bounds changed significantly or column + was fixed */ +#ifdef GLP_DEBUG + xprintf("L"); +#endif + count++; + /* activate other rows affected by column, if required */ + if (flag) + { for (aaa = col->ptr; aaa != NULL; aaa = aaa->c_next) + { if (aaa->row != row) + npp_activate_row(npp, aaa->row); + } + } + if (ret == 3) + { /* process fixed column */ +#ifdef GLP_DEBUG + xprintf("M"); +#endif + npp_fixed_col(npp, col); + /* column was deleted */ + break; /* for kase */ + } + } + else if (ret == 4) + { /* primal/integer infeasibility */ + return -1; + } + else + xassert(ret != ret); + } + } + return count; +} + +/*********************************************************************** +* NAME +* +* npp_process_col - perform basic column processing +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_process_col(NPP *npp, NPPCOL *col); +* +* DESCRIPTION +* +* The routine npp_process_col performs basic column processing that +* currently includes: +* +* 1) fixing and removing empty column; +* +* 2) removing column singleton, which is implied slack variable, and +* corresponding row if it becomes free; +* +* 3) removing bounds of column, which is implied free variable, and +* replacing corresponding row by equality constraint. +* +* Additionally the routine may activate affected rows and/or columns +* for further processing. +* +* RETURNS +* +* 0 success; +* +* GLP_ENOPFS primal/integer infeasibility detected; +* +* GLP_ENODFS dual infeasibility detected. */ + +int npp_process_col(NPP *npp, NPPCOL *col) +{ /* perform basic column processing */ + NPPROW *row; + NPPAIJ *aij; + int ret; + /* column must not be fixed */ + xassert(col->lb < col->ub); + /* start processing column */ + if (col->ptr == NULL) + { /* empty column */ + ret = npp_empty_col(npp, col); + if (ret == 0) + { /* column was fixed and deleted */ +#ifdef GLP_DEBUG + xprintf("N"); +#endif + return 0; + } + else if (ret == 1) + { /* dual infeasibility */ + return GLP_ENODFS; + } + else + xassert(ret != ret); + } + if (col->ptr->c_next == NULL) + { /* column singleton */ + row = col->ptr->row; + if (row->lb == row->ub) + { /* equality constraint */ + if (!col->is_int) +slack: { /* implied slack variable */ +#ifdef GLP_DEBUG + xprintf("O"); +#endif + npp_implied_slack(npp, col); + /* column was deleted */ + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) + { /* row became free due to implied slack variable */ +#ifdef GLP_DEBUG + xprintf("P"); +#endif + /* activate columns affected by row */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + npp_activate_col(npp, aij->col); + /* process free row */ + npp_free_row(npp, row); + /* row was deleted */ + } + else + { /* row became inequality constraint; activate it + since its length changed due to column deletion */ + npp_activate_row(npp, row); + } + return 0; + } + } + else + { /* inequality constraint */ + if (!col->is_int) + { ret = npp_implied_free(npp, col); + if (ret == 0) + { /* implied free variable */ +#ifdef GLP_DEBUG + xprintf("Q"); +#endif + /* column bounds were removed, row was replaced by + equality constraint */ + goto slack; + } + else if (ret == 1) + { /* column is not implied free variable, because its + lower and/or upper bounds can be active */ + } + else if (ret == 2) + { /* dual infeasibility */ + return GLP_ENODFS; + } + } + } + } + /* column still exists */ + return 0; +} + +/*********************************************************************** +* NAME +* +* npp_process_prob - perform basic LP/MIP processing +* +* SYNOPSIS +* +* #include "glpnpp.h" +* int npp_process_prob(NPP *npp, int hard); +* +* DESCRIPTION +* +* The routine npp_process_prob performs basic LP/MIP processing that +* currently includes: +* +* 1) initial LP/MIP processing (see the routine npp_clean_prob), +* +* 2) basic row processing (see the routine npp_process_row), and +* +* 3) basic column processing (see the routine npp_process_col). +* +* If the flag hard is on, the routine attempts to improve current +* column bounds multiple times within the main processing loop, in +* which case this feature may take a time. Otherwise, if the flag hard +* is off, improving column bounds is performed only once at the end of +* the main loop. (Note that this feature is used for MIP only.) +* +* The routine uses two sets: the set of active rows and the set of +* active columns. Rows/columns are marked by a flag (the field temp in +* NPPROW/NPPCOL). If the flag is non-zero, the row/column is active, +* in which case it is placed in the beginning of the row/column list; +* otherwise, if the flag is zero, the row/column is inactive, in which +* case it is placed in the end of the row/column list. If a row/column +* being currently processed may affect other rows/columns, the latters +* are activated for further processing. +* +* RETURNS +* +* 0 success; +* +* GLP_ENOPFS primal/integer infeasibility detected; +* +* GLP_ENODFS dual infeasibility detected. */ + +int npp_process_prob(NPP *npp, int hard) +{ /* perform basic LP/MIP processing */ + NPPROW *row; + NPPCOL *col; + int processing, ret; + /* perform initial LP/MIP processing */ + npp_clean_prob(npp); + /* activate all remaining rows and columns */ + for (row = npp->r_head; row != NULL; row = row->next) + row->temp = 1; + for (col = npp->c_head; col != NULL; col = col->next) + col->temp = 1; + /* main processing loop */ + processing = 1; + while (processing) + { processing = 0; + /* process all active rows */ + for (;;) + { row = npp->r_head; + if (row == NULL || !row->temp) break; + npp_deactivate_row(npp, row); + ret = npp_process_row(npp, row, hard); + if (ret != 0) goto done; + processing = 1; + } + /* process all active columns */ + for (;;) + { col = npp->c_head; + if (col == NULL || !col->temp) break; + npp_deactivate_col(npp, col); + ret = npp_process_col(npp, col); + if (ret != 0) goto done; + processing = 1; + } + } +#if 1 /* 23/XII-2009 */ + if (npp->sol == GLP_MIP && !hard) + { /* improve current column bounds (optional) */ + for (row = npp->r_head; row != NULL; row = row->next) + { if (npp_improve_bounds(npp, row, 0) < 0) + { ret = GLP_ENOPFS; + goto done; + } + } + } +#endif + /* all seems ok */ + ret = 0; +done: xassert(ret == 0 || ret == GLP_ENOPFS || ret == GLP_ENODFS); +#ifdef GLP_DEBUG + xprintf("\n"); +#endif + return ret; +} + +/**********************************************************************/ + +int npp_simplex(NPP *npp, const glp_smcp *parm) +{ /* process LP prior to applying primal/dual simplex method */ + int ret; + xassert(npp->sol == GLP_SOL); + xassert(parm == parm); + ret = npp_process_prob(npp, 0); + return ret; +} + +/**********************************************************************/ + +int npp_integer(NPP *npp, const glp_iocp *parm) +{ /* process MIP prior to applying branch-and-bound method */ + NPPROW *row, *prev_row; + NPPCOL *col; + NPPAIJ *aij; + int count, ret; + xassert(npp->sol == GLP_MIP); + xassert(parm == parm); + /*==============================================================*/ + /* perform basic MIP processing */ + ret = npp_process_prob(npp, 1); + if (ret != 0) goto done; + /*==============================================================*/ + /* binarize problem, if required */ + if (parm->binarize) + npp_binarize_prob(npp); + /*==============================================================*/ + /* identify hidden packing inequalities */ + count = 0; + /* new rows will be added to the end of the row list, so we go + from the end to beginning of the row list */ + for (row = npp->r_tail; row != NULL; row = prev_row) + { prev_row = row->prev; + /* skip free row */ + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) continue; + /* skip equality constraint */ + if (row->lb == row->ub) continue; + /* skip row having less than two variables */ + if (row->ptr == NULL || row->ptr->r_next == NULL) continue; + /* skip row having non-binary variables */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col = aij->col; + if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) + break; + } + if (aij != NULL) continue; + count += npp_hidden_packing(npp, row); + } + if (count > 0) + xprintf("%d hidden packing inequaliti(es) were detected\n", + count); + /*==============================================================*/ + /* identify hidden covering inequalities */ + count = 0; + /* new rows will be added to the end of the row list, so we go + from the end to beginning of the row list */ + for (row = npp->r_tail; row != NULL; row = prev_row) + { prev_row = row->prev; + /* skip free row */ + if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) continue; + /* skip equality constraint */ + if (row->lb == row->ub) continue; + /* skip row having less than three variables */ + if (row->ptr == NULL || row->ptr->r_next == NULL || + row->ptr->r_next->r_next == NULL) continue; + /* skip row having non-binary variables */ + for (aij = row->ptr; aij != NULL; aij = aij->r_next) + { col = aij->col; + if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) + break; + } + if (aij != NULL) continue; + count += npp_hidden_covering(npp, row); + } + if (count > 0) + xprintf("%d hidden covering inequaliti(es) were detected\n", + count); + /*==============================================================*/ + /* reduce inequality constraint coefficients */ + count = 0; + /* new rows will be added to the end of the row list, so we go + from the end to beginning of the row list */ + for (row = npp->r_tail; row != NULL; row = prev_row) + { prev_row = row->prev; + /* skip equality constraint */ + if (row->lb == row->ub) continue; + count += npp_reduce_ineq_coef(npp, row); + } + if (count > 0) + xprintf("%d constraint coefficient(s) were reduced\n", count); + /*==============================================================*/ +#ifdef GLP_DEBUG + routine(npp); +#endif + /*==============================================================*/ + /* all seems ok */ + ret = 0; +done: return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpqmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpqmd.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,584 @@ +/* glpqmd.c (quotient minimum degree algorithm) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* THIS CODE IS THE RESULT OF TRANSLATION OF THE FORTRAN SUBROUTINES +* GENQMD, QMDRCH, QMDQT, QMDUPD, AND QMDMRG FROM THE BOOK: +* +* ALAN GEORGE, JOSEPH W-H LIU. COMPUTER SOLUTION OF LARGE SPARSE +* POSITIVE DEFINITE SYSTEMS. PRENTICE-HALL, 1981. +* +* THE TRANSLATION HAS BEEN DONE WITH THE PERMISSION OF THE AUTHORS +* OF THE ORIGINAL FORTRAN SUBROUTINES: ALAN GEORGE AND JOSEPH LIU, +* UNIVERSITY OF WATERLOO, WATERLOO, ONTARIO, CANADA. +* +* The translation was made by Andrew Makhorin . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpqmd.h" + +/*********************************************************************** +* NAME +* +* genqmd - GENeral Quotient Minimum Degree algorithm +* +* SYNOPSIS +* +* #include "glpqmd.h" +* void genqmd(int *neqns, int xadj[], int adjncy[], int perm[], +* int invp[], int deg[], int marker[], int rchset[], int nbrhd[], +* int qsize[], int qlink[], int *nofsub); +* +* PURPOSE +* +* This routine implements the minimum degree algorithm. It makes use +* of the implicit representation of the elimination graph by quotient +* graphs, and the notion of indistinguishable nodes. +* +* CAUTION +* +* The adjancy vector adjncy will be destroyed. +* +* INPUT PARAMETERS +* +* neqns - number of equations; +* (xadj, adjncy) - +* the adjancy structure. +* +* OUTPUT PARAMETERS +* +* perm - the minimum degree ordering; +* invp - the inverse of perm. +* +* WORKING PARAMETERS +* +* deg - the degree vector. deg[i] is negative means node i has been +* numbered; +* marker - a marker vector, where marker[i] is negative means node i +* has been merged with another nodeand thus can be ignored; +* rchset - vector used for the reachable set; +* nbrhd - vector used for neighborhood set; +* qsize - vector used to store the size of indistinguishable +* supernodes; +* qlink - vector used to store indistinguishable nodes, i, qlink[i], +* qlink[qlink[i]], ... are the members of the supernode +* represented by i. +* +* PROGRAM SUBROUTINES +* +* qmdrch, qmdqt, qmdupd. +***********************************************************************/ + +void genqmd(int *_neqns, int xadj[], int adjncy[], int perm[], + int invp[], int deg[], int marker[], int rchset[], int nbrhd[], + int qsize[], int qlink[], int *_nofsub) +{ int inode, ip, irch, j, mindeg, ndeg, nhdsze, node, np, num, + nump1, nxnode, rchsze, search, thresh; +# define neqns (*_neqns) +# define nofsub (*_nofsub) + /* Initialize degree vector and other working variables. */ + mindeg = neqns; + nofsub = 0; + for (node = 1; node <= neqns; node++) + { perm[node] = node; + invp[node] = node; + marker[node] = 0; + qsize[node] = 1; + qlink[node] = 0; + ndeg = xadj[node+1] - xadj[node]; + deg[node] = ndeg; + if (ndeg < mindeg) mindeg = ndeg; + } + num = 0; + /* Perform threshold search to get a node of min degree. + Variable search point to where search should start. */ +s200: search = 1; + thresh = mindeg; + mindeg = neqns; +s300: nump1 = num + 1; + if (nump1 > search) search = nump1; + for (j = search; j <= neqns; j++) + { node = perm[j]; + if (marker[node] >= 0) + { ndeg = deg[node]; + if (ndeg <= thresh) goto s500; + if (ndeg < mindeg) mindeg = ndeg; + } + } + goto s200; + /* Node has minimum degree. Find its reachable sets by calling + qmdrch. */ +s500: search = j; + nofsub += deg[node]; + marker[node] = 1; + qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, &nhdsze, + nbrhd); + /* Eliminate all nodes indistinguishable from node. They are given + by node, qlink[node], ... . */ + nxnode = node; +s600: num++; + np = invp[nxnode]; + ip = perm[num]; + perm[np] = ip; + invp[ip] = np; + perm[num] = nxnode; + invp[nxnode] = num; + deg[nxnode] = -1; + nxnode = qlink[nxnode]; + if (nxnode > 0) goto s600; + if (rchsze > 0) + { /* Update the degrees of the nodes in the reachable set and + identify indistinguishable nodes. */ + qmdupd(xadj, adjncy, &rchsze, rchset, deg, qsize, qlink, + marker, &rchset[rchsze+1], &nbrhd[nhdsze+1]); + /* Reset marker value of nodes in reach set. Update threshold + value for cyclic search. Also call qmdqt to form new + quotient graph. */ + marker[node] = 0; + for (irch = 1; irch <= rchsze; irch++) + { inode = rchset[irch]; + if (marker[inode] >= 0) + { marker[inode] = 0; + ndeg = deg[inode]; + if (ndeg < mindeg) mindeg = ndeg; + if (ndeg <= thresh) + { mindeg = thresh; + thresh = ndeg; + search = invp[inode]; + } + } + } + if (nhdsze > 0) + qmdqt(&node, xadj, adjncy, marker, &rchsze, rchset, nbrhd); + } + if (num < neqns) goto s300; + return; +# undef neqns +# undef nofsub +} + +/*********************************************************************** +* NAME +* +* qmdrch - Quotient MD ReaCHable set +* +* SYNOPSIS +* +* #include "glpqmd.h" +* void qmdrch(int *root, int xadj[], int adjncy[], int deg[], +* int marker[], int *rchsze, int rchset[], int *nhdsze, +* int nbrhd[]); +* +* PURPOSE +* +* This subroutine determines the reachable set of a node through a +* given subset. The adjancy structure is assumed to be stored in a +* quotient graph format. +* +* INPUT PARAMETERS +* +* root - the given node not in the subset; +* (xadj, adjncy) - +* the adjancy structure pair; +* deg - the degree vector. deg[i] < 0 means the node belongs to the +* given subset. +* +* OUTPUT PARAMETERS +* +* (rchsze, rchset) - +* the reachable set; +* (nhdsze, nbrhd) - +* the neighborhood set. +* +* UPDATED PARAMETERS +* +* marker - the marker vector for reach and nbrhd sets. > 0 means the +* node is in reach set. < 0 means the node has been merged +* with others in the quotient or it is in nbrhd set. +***********************************************************************/ + +void qmdrch(int *_root, int xadj[], int adjncy[], int deg[], + int marker[], int *_rchsze, int rchset[], int *_nhdsze, + int nbrhd[]) +{ int i, istop, istrt, j, jstop, jstrt, nabor, node; +# define root (*_root) +# define rchsze (*_rchsze) +# define nhdsze (*_nhdsze) + /* Loop through the neighbors of root in the quotient graph. */ + nhdsze = 0; + rchsze = 0; + istrt = xadj[root]; + istop = xadj[root+1] - 1; + if (istop < istrt) return; + for (i = istrt; i <= istop; i++) + { nabor = adjncy[i]; + if (nabor == 0) return; + if (marker[nabor] == 0) + { if (deg[nabor] >= 0) + { /* Include nabor into the reachable set. */ + rchsze++; + rchset[rchsze] = nabor; + marker[nabor] = 1; + goto s600; + } + /* nabor has been eliminated. Find nodes reachable from + it. */ + marker[nabor] = -1; + nhdsze++; + nbrhd[nhdsze] = nabor; +s300: jstrt = xadj[nabor]; + jstop = xadj[nabor+1] - 1; + for (j = jstrt; j <= jstop; j++) + { node = adjncy[j]; + nabor = - node; + if (node < 0) goto s300; + if (node == 0) goto s600; + if (marker[node] == 0) + { rchsze++; + rchset[rchsze] = node; + marker[node] = 1; + } + } + } +s600: ; + } + return; +# undef root +# undef rchsze +# undef nhdsze +} + +/*********************************************************************** +* NAME +* +* qmdqt - Quotient MD Quotient graph Transformation +* +* SYNOPSIS +* +* #include "glpqmd.h" +* void qmdqt(int *root, int xadj[], int adjncy[], int marker[], +* int *rchsze, int rchset[], int nbrhd[]); +* +* PURPOSE +* +* This subroutine performs the quotient graph transformation after a +* node has been eliminated. +* +* INPUT PARAMETERS +* +* root - the node just eliminated. It becomes the representative of +* the new supernode; +* (xadj, adjncy) - +* the adjancy structure; +* (rchsze, rchset) - +* the reachable set of root in the old quotient graph; +* nbrhd - the neighborhood set which will be merged with root to form +* the new supernode; +* marker - the marker vector. +* +* UPDATED PARAMETERS +* +* adjncy - becomes the adjncy of the quotient graph. +***********************************************************************/ + +void qmdqt(int *_root, int xadj[], int adjncy[], int marker[], + int *_rchsze, int rchset[], int nbrhd[]) +{ int inhd, irch, j, jstop, jstrt, link, nabor, node; +# define root (*_root) +# define rchsze (*_rchsze) + irch = 0; + inhd = 0; + node = root; +s100: jstrt = xadj[node]; + jstop = xadj[node+1] - 2; + if (jstop >= jstrt) + { /* Place reach nodes into the adjacent list of node. */ + for (j = jstrt; j <= jstop; j++) + { irch++; + adjncy[j] = rchset[irch]; + if (irch >= rchsze) goto s400; + } + } + /* Link to other space provided by the nbrhd set. */ + link = adjncy[jstop+1]; + node = - link; + if (link >= 0) + { inhd++; + node = nbrhd[inhd]; + adjncy[jstop+1] = - node; + } + goto s100; + /* All reachable nodes have been saved. End the adjacent list. + Add root to the neighborhood list of each node in the reach + set. */ +s400: adjncy[j+1] = 0; + for (irch = 1; irch <= rchsze; irch++) + { node = rchset[irch]; + if (marker[node] >= 0) + { jstrt = xadj[node]; + jstop = xadj[node+1] - 1; + for (j = jstrt; j <= jstop; j++) + { nabor = adjncy[j]; + if (marker[nabor] < 0) + { adjncy[j] = root; + goto s600; + } + } + } +s600: ; + } + return; +# undef root +# undef rchsze +} + +/*********************************************************************** +* NAME +* +* qmdupd - Quotient MD UPDate +* +* SYNOPSIS +* +* #include "glpqmd.h" +* void qmdupd(int xadj[], int adjncy[], int *nlist, int list[], +* int deg[], int qsize[], int qlink[], int marker[], int rchset[], +* int nbrhd[]); +* +* PURPOSE +* +* This routine performs degree update for a set of nodes in the minimum +* degree algorithm. +* +* INPUT PARAMETERS +* +* (xadj, adjncy) - +* the adjancy structure; +* (nlist, list) - +* the list of nodes whose degree has to be updated. +* +* UPDATED PARAMETERS +* +* deg - the degree vector; +* qsize - size of indistinguishable supernodes; +* qlink - linked list for indistinguishable nodes; +* marker - used to mark those nodes in reach/nbrhd sets. +* +* WORKING PARAMETERS +* +* rchset - the reachable set; +* nbrhd - the neighborhood set. +* +* PROGRAM SUBROUTINES +* +* qmdmrg. +***********************************************************************/ + +void qmdupd(int xadj[], int adjncy[], int *_nlist, int list[], + int deg[], int qsize[], int qlink[], int marker[], int rchset[], + int nbrhd[]) +{ int deg0, deg1, il, inhd, inode, irch, j, jstop, jstrt, mark, + nabor, nhdsze, node, rchsze; +# define nlist (*_nlist) + /* Find all eliminated supernodes that are adjacent to some nodes + in the given list. Put them into (nhdsze, nbrhd). deg0 contains + the number of nodes in the list. */ + if (nlist <= 0) return; + deg0 = 0; + nhdsze = 0; + for (il = 1; il <= nlist; il++) + { node = list[il]; + deg0 += qsize[node]; + jstrt = xadj[node]; + jstop = xadj[node+1] - 1; + for (j = jstrt; j <= jstop; j++) + { nabor = adjncy[j]; + if (marker[nabor] == 0 && deg[nabor] < 0) + { marker[nabor] = -1; + nhdsze++; + nbrhd[nhdsze] = nabor; + } + } + } + /* Merge indistinguishable nodes in the list by calling the + subroutine qmdmrg. */ + if (nhdsze > 0) + qmdmrg(xadj, adjncy, deg, qsize, qlink, marker, °0, &nhdsze, + nbrhd, rchset, &nbrhd[nhdsze+1]); + /* Find the new degrees of the nodes that have not been merged. */ + for (il = 1; il <= nlist; il++) + { node = list[il]; + mark = marker[node]; + if (mark == 0 || mark == 1) + { marker[node] = 2; + qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, + &nhdsze, nbrhd); + deg1 = deg0; + if (rchsze > 0) + { for (irch = 1; irch <= rchsze; irch++) + { inode = rchset[irch]; + deg1 += qsize[inode]; + marker[inode] = 0; + } + } + deg[node] = deg1 - 1; + if (nhdsze > 0) + { for (inhd = 1; inhd <= nhdsze; inhd++) + { inode = nbrhd[inhd]; + marker[inode] = 0; + } + } + } + } + return; +# undef nlist +} + +/*********************************************************************** +* NAME +* +* qmdmrg - Quotient MD MeRGe +* +* SYNOPSIS +* +* #include "qmdmrg.h" +* void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[], +* int qlink[], int marker[], int *deg0, int *nhdsze, int nbrhd[], +* int rchset[], int ovrlp[]); +* +* PURPOSE +* +* This routine merges indistinguishable nodes in the minimum degree +* ordering algorithm. It also computes the new degrees of these new +* supernodes. +* +* INPUT PARAMETERS +* +* (xadj, adjncy) - +* the adjancy structure; +* deg0 - the number of nodes in the given set; +* (nhdsze, nbrhd) - +* the set of eliminated supernodes adjacent to some nodes in +* the set. +* +* UPDATED PARAMETERS +* +* deg - the degree vector; +* qsize - size of indistinguishable nodes; +* qlink - linked list for indistinguishable nodes; +* marker - the given set is given by those nodes with marker value set +* to 1. Those nodes with degree updated will have marker value +* set to 2. +* +* WORKING PARAMETERS +* +* rchset - the reachable set; +* ovrlp - temp vector to store the intersection of two reachable sets. +***********************************************************************/ + +void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[], + int qlink[], int marker[], int *_deg0, int *_nhdsze, int nbrhd[], + int rchset[], int ovrlp[]) +{ int deg1, head, inhd, iov, irch, j, jstop, jstrt, link, lnode, + mark, mrgsze, nabor, node, novrlp, rchsze, root; +# define deg0 (*_deg0) +# define nhdsze (*_nhdsze) + /* Initialization. */ + if (nhdsze <= 0) return; + for (inhd = 1; inhd <= nhdsze; inhd++) + { root = nbrhd[inhd]; + marker[root] = 0; + } + /* Loop through each eliminated supernode in the set + (nhdsze, nbrhd). */ + for (inhd = 1; inhd <= nhdsze; inhd++) + { root = nbrhd[inhd]; + marker[root] = -1; + rchsze = 0; + novrlp = 0; + deg1 = 0; +s200: jstrt = xadj[root]; + jstop = xadj[root+1] - 1; + /* Determine the reachable set and its intersection with the + input reachable set. */ + for (j = jstrt; j <= jstop; j++) + { nabor = adjncy[j]; + root = - nabor; + if (nabor < 0) goto s200; + if (nabor == 0) break; + mark = marker[nabor]; + if (mark == 0) + { rchsze++; + rchset[rchsze] = nabor; + deg1 += qsize[nabor]; + marker[nabor] = 1; + } + else if (mark == 1) + { novrlp++; + ovrlp[novrlp] = nabor; + marker[nabor] = 2; + } + } + /* From the overlapped set, determine the nodes that can be + merged together. */ + head = 0; + mrgsze = 0; + for (iov = 1; iov <= novrlp; iov++) + { node = ovrlp[iov]; + jstrt = xadj[node]; + jstop = xadj[node+1] - 1; + for (j = jstrt; j <= jstop; j++) + { nabor = adjncy[j]; + if (marker[nabor] == 0) + { marker[node] = 1; + goto s1100; + } + } + /* Node belongs to the new merged supernode. Update the + vectors qlink and qsize. */ + mrgsze += qsize[node]; + marker[node] = -1; + lnode = node; +s900: link = qlink[lnode]; + if (link > 0) + { lnode = link; + goto s900; + } + qlink[lnode] = head; + head = node; +s1100: ; + } + if (head > 0) + { qsize[head] = mrgsze; + deg[head] = deg0 + deg1 - 1; + marker[head] = 2; + } + /* Reset marker values. */ + root = nbrhd[inhd]; + marker[root] = 0; + if (rchsze > 0) + { for (irch = 1; irch <= rchsze; irch++) + { node = rchset[irch]; + marker[node] = 0; + } + } + } + return; +# undef deg0 +# undef nhdsze +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpqmd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpqmd.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,59 @@ +/* glpqmd.h (quotient minimum degree algorithm) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPQMD_H +#define GLPQMD_H + +#define genqmd _glp_qmd_genqmd +void genqmd(int *neqns, int xadj[], int adjncy[], int perm[], + int invp[], int deg[], int marker[], int rchset[], int nbrhd[], + int qsize[], int qlink[], int *nofsub); +/* GENeral Quotient Minimum Degree algorithm */ + +#define qmdrch _glp_qmd_qmdrch +void qmdrch(int *root, int xadj[], int adjncy[], int deg[], + int marker[], int *rchsze, int rchset[], int *nhdsze, + int nbrhd[]); +/* Quotient MD ReaCHable set */ + +#define qmdqt _glp_qmd_qmdqt +void qmdqt(int *root, int xadj[], int adjncy[], int marker[], + int *rchsze, int rchset[], int nbrhd[]); +/* Quotient MD Quotient graph Transformation */ + +#define qmdupd _glp_qmd_qmdupd +void qmdupd(int xadj[], int adjncy[], int *nlist, int list[], + int deg[], int qsize[], int qlink[], int marker[], int rchset[], + int nbrhd[]); +/* Quotient MD UPDate */ + +#define qmdmrg _glp_qmd_qmdmrg +void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[], + int qlink[], int marker[], int *deg0, int *nhdsze, int nbrhd[], + int rchset[], int ovrlp[]); +/* Quotient MD MeRGe */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glprgr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glprgr.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,165 @@ +/* glprgr.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_ERRNO +#define _GLPSTD_STDIO +#include "glpenv.h" +#include "glprgr.h" +#define xfault xerror + +/*********************************************************************** +* NAME +* +* rgr_write_bmp16 - write 16-color raster image in BMP file format +* +* SYNOPSIS +* +* #include "glprgr.h" +* int rgr_write_bmp16(const char *fname, int m, int n, const char +* map[]); +* +* DESCRIPTION +* +* The routine rgr_write_bmp16 writes 16-color raster image in +* uncompressed BMP file format (Windows bitmap) to a binary file whose +* name is specified by the character string fname. +* +* The parameters m and n specify, respectively, the number of rows and +* the numbers of columns (i.e. height and width) of the raster image. +* +* The character array map has m*n elements. Elements map[0, ..., n-1] +* correspond to the first (top) scanline, elements map[n, ..., 2*n-1] +* correspond to the second scanline, etc. +* +* Each element of the array map specifies a color of the corresponding +* pixel as 8-bit binary number XXXXIRGB, where four high-order bits (X) +* are ignored, I is high intensity bit, R is red color bit, G is green +* color bit, and B is blue color bit. Thus, all 16 possible colors are +* coded as following hexadecimal numbers: +* +* 0x00 = black 0x08 = dark gray +* 0x01 = blue 0x09 = bright blue +* 0x02 = green 0x0A = bright green +* 0x03 = cyan 0x0B = bright cyan +* 0x04 = red 0x0C = bright red +* 0x05 = magenta 0x0D = bright magenta +* 0x06 = brown 0x0E = yellow +* 0x07 = light gray 0x0F = white +* +* RETURNS +* +* If no error occured, the routine returns zero; otherwise, it prints +* an appropriate error message and returns non-zero. */ + +static void put_byte(FILE *fp, int c) +{ fputc(c, fp); + return; +} + +static void put_word(FILE *fp, int w) +{ /* big endian */ + put_byte(fp, w); + put_byte(fp, w >> 8); + return; +} + +static void put_dword(FILE *fp, int d) +{ /* big endian */ + put_word(fp, d); + put_word(fp, d >> 16); + return; +} + +int rgr_write_bmp16(const char *fname, int m, int n, const char map[]) +{ FILE *fp; + int offset, bmsize, i, j, b, ret = 0; + if (!(1 <= m && m <= 32767)) + xfault("rgr_write_bmp16: m = %d; invalid height\n", m); + if (!(1 <= n && n <= 32767)) + xfault("rgr_write_bmp16: n = %d; invalid width\n", n); + fp = fopen(fname, "wb"); + if (fp == NULL) + { xprintf("rgr_write_bmp16: unable to create `%s' - %s\n", + fname, strerror(errno)); + ret = 1; + goto fini; + } + offset = 14 + 40 + 16 * 4; + bmsize = (4 * n + 31) / 32; + /* struct BMPFILEHEADER (14 bytes) */ + /* UINT bfType */ put_byte(fp, 'B'), put_byte(fp, 'M'); + /* DWORD bfSize */ put_dword(fp, offset + bmsize * 4); + /* UINT bfReserved1 */ put_word(fp, 0); + /* UNIT bfReserved2 */ put_word(fp, 0); + /* DWORD bfOffBits */ put_dword(fp, offset); + /* struct BMPINFOHEADER (40 bytes) */ + /* DWORD biSize */ put_dword(fp, 40); + /* LONG biWidth */ put_dword(fp, n); + /* LONG biHeight */ put_dword(fp, m); + /* WORD biPlanes */ put_word(fp, 1); + /* WORD biBitCount */ put_word(fp, 4); + /* DWORD biCompression */ put_dword(fp, 0 /* BI_RGB */); + /* DWORD biSizeImage */ put_dword(fp, 0); + /* LONG biXPelsPerMeter */ put_dword(fp, 2953 /* 75 dpi */); + /* LONG biYPelsPerMeter */ put_dword(fp, 2953 /* 75 dpi */); + /* DWORD biClrUsed */ put_dword(fp, 0); + /* DWORD biClrImportant */ put_dword(fp, 0); + /* struct RGBQUAD (16 * 4 = 64 bytes) */ + /* CGA-compatible colors: */ + /* 0x00 = black */ put_dword(fp, 0x000000); + /* 0x01 = blue */ put_dword(fp, 0x000080); + /* 0x02 = green */ put_dword(fp, 0x008000); + /* 0x03 = cyan */ put_dword(fp, 0x008080); + /* 0x04 = red */ put_dword(fp, 0x800000); + /* 0x05 = magenta */ put_dword(fp, 0x800080); + /* 0x06 = brown */ put_dword(fp, 0x808000); + /* 0x07 = light gray */ put_dword(fp, 0xC0C0C0); + /* 0x08 = dark gray */ put_dword(fp, 0x808080); + /* 0x09 = bright blue */ put_dword(fp, 0x0000FF); + /* 0x0A = bright green */ put_dword(fp, 0x00FF00); + /* 0x0B = bright cyan */ put_dword(fp, 0x00FFFF); + /* 0x0C = bright red */ put_dword(fp, 0xFF0000); + /* 0x0D = bright magenta */ put_dword(fp, 0xFF00FF); + /* 0x0E = yellow */ put_dword(fp, 0xFFFF00); + /* 0x0F = white */ put_dword(fp, 0xFFFFFF); + /* pixel data bits */ + b = 0; + for (i = m - 1; i >= 0; i--) + { for (j = 0; j < ((n + 7) / 8) * 8; j++) + { b <<= 4; + b |= (j < n ? map[i * n + j] & 15 : 0); + if (j & 1) put_byte(fp, b); + } + } + fflush(fp); + if (ferror(fp)) + { xprintf("rgr_write_bmp16: write error on `%s' - %s\n", + fname, strerror(errno)); + ret = 1; + } +fini: if (fp != NULL) fclose(fp); + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glprgr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glprgr.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,34 @@ +/* glprgr.h (raster graphics) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPRGR_H +#define GLPRGR_H + +#define rgr_write_bmp16 _glp_rgr_write_bmp16 +int rgr_write_bmp16(const char *fname, int m, int n, const char map[]); +/* write 16-color raster image in BMP file format */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glprng.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glprng.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,68 @@ +/* glprng.h (pseudo-random number generator) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPRNG_H +#define GLPRNG_H + +typedef struct RNG RNG; + +struct RNG +{ /* Knuth's portable pseudo-random number generator */ + int A[56]; + /* pseudo-random values */ + int *fptr; + /* the next A value to be exported */ +}; + +#define rng_create_rand _glp_rng_create_rand +RNG *rng_create_rand(void); +/* create pseudo-random number generator */ + +#define rng_init_rand _glp_rng_init_rand +void rng_init_rand(RNG *rand, int seed); +/* initialize pseudo-random number generator */ + +#define rng_next_rand _glp_rng_next_rand +int rng_next_rand(RNG *rand); +/* obtain pseudo-random integer in the range [0, 2^31-1] */ + +#define rng_unif_rand _glp_rng_unif_rand +int rng_unif_rand(RNG *rand, int m); +/* obtain pseudo-random integer in the range [0, m-1] */ + +#define rng_delete_rand _glp_rng_delete_rand +void rng_delete_rand(RNG *rand); +/* delete pseudo-random number generator */ + +#define rng_unif_01 _glp_rng_unif_01 +double rng_unif_01(RNG *rand); +/* obtain pseudo-random number in the range [0, 1] */ + +#define rng_uniform _glp_rng_uniform +double rng_uniform(RNG *rand, double a, double b); +/* obtain pseudo-random number in the range [a, b] */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glprng01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glprng01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,227 @@ +/* glprng01.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* This code is a modified version of the module GB_FLIP, a portable +* pseudo-random number generator. The original version of GB_FLIP is +* a part of The Stanford GraphBase developed by Donald E. Knuth (see +* http://www-cs-staff.stanford.edu/~knuth/sgb.html). +* +* Note that all changes concern only external names, so this modified +* version produces exactly the same results as the original version. +* +* Changes were made by Andrew Makhorin . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glprng.h" + +#if 0 +int A[56] = { -1 }; +#else +#define A (rand->A) +#endif +/* pseudo-random values */ + +#if 0 +int *fptr = A; +#else +#define fptr (rand->fptr) +#endif +/* the next A value to be exported */ + +#define mod_diff(x, y) (((x) - (y)) & 0x7FFFFFFF) +/* difference modulo 2^31 */ + +static int flip_cycle(RNG *rand) +{ /* this is an auxiliary routine to do 55 more steps of the basic + recurrence, at high speed, and to reset fptr */ + int *ii, *jj; + for (ii = &A[1], jj = &A[32]; jj <= &A[55]; ii++, jj++) + *ii = mod_diff(*ii, *jj); + for (jj = &A[1]; ii <= &A[55]; ii++, jj++) + *ii = mod_diff(*ii, *jj); + fptr = &A[54]; + return A[55]; +} + +/*********************************************************************** +* NAME +* +* rng_create_rand - create pseudo-random number generator +* +* SYNOPSIS +* +* #include "glprng.h" +* RNG *rng_create_rand(void); +* +* DESCRIPTION +* +* The routine rng_create_rand creates and initializes a pseudo-random +* number generator. +* +* RETURNS +* +* The routine returns a pointer to the generator created. */ + +RNG *rng_create_rand(void) +{ RNG *rand; + int i; + rand = xmalloc(sizeof(RNG)); + A[0] = -1; + for (i = 1; i <= 55; i++) A[i] = 0; + fptr = A; + rng_init_rand(rand, 1); + return rand; +} + +/*********************************************************************** +* NAME +* +* rng_init_rand - initialize pseudo-random number generator +* +* SYNOPSIS +* +* #include "glprng.h" +* void rng_init_rand(RNG *rand, int seed); +* +* DESCRIPTION +* +* The routine rng_init_rand initializes the pseudo-random number +* generator. The parameter seed may be any integer number. Note that +* on creating the generator this routine is called with the parameter +* seed equal to 1. */ + +void rng_init_rand(RNG *rand, int seed) +{ int i; + int prev = seed, next = 1; + seed = prev = mod_diff(prev, 0); + A[55] = prev; + for (i = 21; i; i = (i + 21) % 55) + { A[i] = next; + next = mod_diff(prev, next); + if (seed & 1) + seed = 0x40000000 + (seed >> 1); + else + seed >>= 1; + next = mod_diff(next, seed); + prev = A[i]; + } + flip_cycle(rand); + flip_cycle(rand); + flip_cycle(rand); + flip_cycle(rand); + flip_cycle(rand); + return; +} + +/*********************************************************************** +* NAME +* +* rng_next_rand - obtain pseudo-random integer in the range [0, 2^31-1] +* +* SYNOPSIS +* +* #include "glprng.h" +* int rng_next_rand(RNG *rand); +* +* RETURNS +* +* The routine rng_next_rand returns a next pseudo-random integer which +* is uniformly distributed between 0 and 2^31-1, inclusive. The period +* length of the generated numbers is 2^85 - 2^30. The low order bits of +* the generated numbers are just as random as the high-order bits. */ + +int rng_next_rand(RNG *rand) +{ return + *fptr >= 0 ? *fptr-- : flip_cycle(rand); +} + +/*********************************************************************** +* NAME +* +* rng_unif_rand - obtain pseudo-random integer in the range [0, m-1] +* +* SYNOPSIS +* +* #include "glprng.h" +* int rng_unif_rand(RNG *rand, int m); +* +* RETURNS +* +* The routine rng_unif_rand returns a next pseudo-random integer which +* is uniformly distributed between 0 and m-1, inclusive, where m is any +* positive integer less than 2^31. */ + +#define two_to_the_31 ((unsigned int)0x80000000) + +int rng_unif_rand(RNG *rand, int m) +{ unsigned int t = two_to_the_31 - (two_to_the_31 % m); + int r; + xassert(m > 0); + do { r = rng_next_rand(rand); } while (t <= (unsigned int)r); + return r % m; +} + +/*********************************************************************** +* NAME +* +* rng_delete_rand - delete pseudo-random number generator +* +* SYNOPSIS +* +* #include "glprng.h" +* void rng_delete_rand(RNG *rand); +* +* DESCRIPTION +* +* The routine rng_delete_rand frees all the memory allocated to the +* specified pseudo-random number generator. */ + +void rng_delete_rand(RNG *rand) +{ xfree(rand); + return; +} + +/**********************************************************************/ + +#if 0 +/* To be sure that this modified version produces the same results as + the original version, run this validation program. */ + +int main(void) +{ RNG *rand; + int j; + rand = rng_create_rand(); + rng_init_rand(rand, -314159); + if (rng_next_rand(rand) != 119318998) + { fprintf(stderr, "Failure on the first try!\n"); + return -1; + } + for (j = 1; j <= 133; j++) rng_next_rand(rand); + if (rng_unif_rand(rand, 0x55555555) != 748103812) + { fprintf(stderr, "Failure on the second try!\n"); + return -2; + } + fprintf(stderr, "OK, the random-number generator routines seem to" + " work!\n"); + rng_delete_rand(rand); + return 0; +} +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glprng02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glprng02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,76 @@ +/* glprng02.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glprng.h" +#define xfault xerror + +/*********************************************************************** +* NAME +* +* rng_unif_01 - obtain pseudo-random number in the range [0, 1] +* +* SYNOPSIS +* +* #include "glprng.h" +* double rng_unif_01(RNG *rand); +* +* RETURNS +* +* The routine rng_unif_01 returns a next pseudo-random number which is +* uniformly distributed in the range [0, 1]. */ + +double rng_unif_01(RNG *rand) +{ double x; + x = (double)rng_next_rand(rand) / 2147483647.0; + xassert(0.0 <= x && x <= 1.0); + return x; +} + +/*********************************************************************** +* NAME +* +* rng_uniform - obtain pseudo-random number in the range [a, b] +* +* SYNOPSIS +* +* #include "glprng.h" +* double rng_uniform(RNG *rand, double a, double b); +* +* RETURNS +* +* The routine rng_uniform returns a next pseudo-random number which is +* uniformly distributed in the range [a, b]. */ + +double rng_uniform(RNG *rand, double a, double b) +{ double x; + if (a >= b) + xfault("rng_uniform: a = %g, b = %g; invalid range\n", a, b); + x = rng_unif_01(rand); + x = a * (1.0 - x) + b * x; + xassert(a <= x && x <= b); + return x; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpscf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpscf.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,634 @@ +/* glpscf.c (Schur complement factorization) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glpscf.h" +#define xfault xerror + +#define _GLPSCF_DEBUG 0 + +#define eps 1e-10 + +/*********************************************************************** +* NAME +* +* scf_create_it - create Schur complement factorization +* +* SYNOPSIS +* +* #include "glpscf.h" +* SCF *scf_create_it(int n_max); +* +* DESCRIPTION +* +* The routine scf_create_it creates the factorization of matrix C, +* which initially has no rows and columns. +* +* The parameter n_max specifies the maximal order of matrix C to be +* factorized, 1 <= n_max <= 32767. +* +* RETURNS +* +* The routine scf_create_it returns a pointer to the structure SCF, +* which defines the factorization. */ + +SCF *scf_create_it(int n_max) +{ SCF *scf; +#if _GLPSCF_DEBUG + xprintf("scf_create_it: warning: debug mode enabled\n"); +#endif + if (!(1 <= n_max && n_max <= 32767)) + xfault("scf_create_it: n_max = %d; invalid parameter\n", + n_max); + scf = xmalloc(sizeof(SCF)); + scf->n_max = n_max; + scf->n = 0; + scf->f = xcalloc(1 + n_max * n_max, sizeof(double)); + scf->u = xcalloc(1 + n_max * (n_max + 1) / 2, sizeof(double)); + scf->p = xcalloc(1 + n_max, sizeof(int)); + scf->t_opt = SCF_TBG; + scf->rank = 0; +#if _GLPSCF_DEBUG + scf->c = xcalloc(1 + n_max * n_max, sizeof(double)); +#else + scf->c = NULL; +#endif + scf->w = xcalloc(1 + n_max, sizeof(double)); + return scf; +} + +/*********************************************************************** +* The routine f_loc determines location of matrix element F[i,j] in +* the one-dimensional array f. */ + +static int f_loc(SCF *scf, int i, int j) +{ int n_max = scf->n_max; + int n = scf->n; + xassert(1 <= i && i <= n); + xassert(1 <= j && j <= n); + return (i - 1) * n_max + j; +} + +/*********************************************************************** +* The routine u_loc determines location of matrix element U[i,j] in +* the one-dimensional array u. */ + +static int u_loc(SCF *scf, int i, int j) +{ int n_max = scf->n_max; + int n = scf->n; + xassert(1 <= i && i <= n); + xassert(i <= j && j <= n); + return (i - 1) * n_max + j - i * (i - 1) / 2; +} + +/*********************************************************************** +* The routine bg_transform applies Bartels-Golub version of gaussian +* elimination to restore triangular structure of matrix U. +* +* On entry matrix U has the following structure: +* +* 1 k n +* 1 * * * * * * * * * * +* . * * * * * * * * * +* . . * * * * * * * * +* . . . * * * * * * * +* k . . . . * * * * * * +* . . . . . * * * * * +* . . . . . . * * * * +* . . . . . . . * * * +* . . . . . . . . * * +* n . . . . # # # # # # +* +* where '#' is a row spike to be eliminated. +* +* Elements of n-th row are passed separately in locations un[k], ..., +* un[n]. On exit the content of the array un is destroyed. +* +* REFERENCES +* +* R.H.Bartels, G.H.Golub, "The Simplex Method of Linear Programming +* Using LU-decomposition", Comm. ACM, 12, pp. 266-68, 1969. */ + +static void bg_transform(SCF *scf, int k, double un[]) +{ int n = scf->n; + double *f = scf->f; + double *u = scf->u; + int j, k1, kj, kk, n1, nj; + double t; + xassert(1 <= k && k <= n); + /* main elimination loop */ + for (k = k; k < n; k++) + { /* determine location of U[k,k] */ + kk = u_loc(scf, k, k); + /* determine location of F[k,1] */ + k1 = f_loc(scf, k, 1); + /* determine location of F[n,1] */ + n1 = f_loc(scf, n, 1); + /* if |U[k,k]| < |U[n,k]|, interchange k-th and n-th rows to + provide |U[k,k]| >= |U[n,k]| */ + if (fabs(u[kk]) < fabs(un[k])) + { /* interchange k-th and n-th rows of matrix U */ + for (j = k, kj = kk; j <= n; j++, kj++) + t = u[kj], u[kj] = un[j], un[j] = t; + /* interchange k-th and n-th rows of matrix F to keep the + main equality F * C = U * P */ + for (j = 1, kj = k1, nj = n1; j <= n; j++, kj++, nj++) + t = f[kj], f[kj] = f[nj], f[nj] = t; + } + /* now |U[k,k]| >= |U[n,k]| */ + /* if U[k,k] is too small in the magnitude, replace U[k,k] and + U[n,k] by exact zero */ + if (fabs(u[kk]) < eps) u[kk] = un[k] = 0.0; + /* if U[n,k] is already zero, elimination is not needed */ + if (un[k] == 0.0) continue; + /* compute gaussian multiplier t = U[n,k] / U[k,k] */ + t = un[k] / u[kk]; + /* apply gaussian elimination to nullify U[n,k] */ + /* (n-th row of U) := (n-th row of U) - t * (k-th row of U) */ + for (j = k+1, kj = kk+1; j <= n; j++, kj++) + un[j] -= t * u[kj]; + /* (n-th row of F) := (n-th row of F) - t * (k-th row of F) + to keep the main equality F * C = U * P */ + for (j = 1, kj = k1, nj = n1; j <= n; j++, kj++, nj++) + f[nj] -= t * f[kj]; + } + /* if U[n,n] is too small in the magnitude, replace it by exact + zero */ + if (fabs(un[n]) < eps) un[n] = 0.0; + /* store U[n,n] in a proper location */ + u[u_loc(scf, n, n)] = un[n]; + return; +} + +/*********************************************************************** +* The routine givens computes the parameters of Givens plane rotation +* c = cos(teta) and s = sin(teta) such that: +* +* ( c -s ) ( a ) ( r ) +* ( ) ( ) = ( ) , +* ( s c ) ( b ) ( 0 ) +* +* where a and b are given scalars. +* +* REFERENCES +* +* G.H.Golub, C.F.Van Loan, "Matrix Computations", 2nd ed. */ + +static void givens(double a, double b, double *c, double *s) +{ double t; + if (b == 0.0) + (*c) = 1.0, (*s) = 0.0; + else if (fabs(a) <= fabs(b)) + t = - a / b, (*s) = 1.0 / sqrt(1.0 + t * t), (*c) = (*s) * t; + else + t = - b / a, (*c) = 1.0 / sqrt(1.0 + t * t), (*s) = (*c) * t; + return; +} + +/*---------------------------------------------------------------------- +* The routine gr_transform applies Givens plane rotations to restore +* triangular structure of matrix U. +* +* On entry matrix U has the following structure: +* +* 1 k n +* 1 * * * * * * * * * * +* . * * * * * * * * * +* . . * * * * * * * * +* . . . * * * * * * * +* k . . . . * * * * * * +* . . . . . * * * * * +* . . . . . . * * * * +* . . . . . . . * * * +* . . . . . . . . * * +* n . . . . # # # # # # +* +* where '#' is a row spike to be eliminated. +* +* Elements of n-th row are passed separately in locations un[k], ..., +* un[n]. On exit the content of the array un is destroyed. +* +* REFERENCES +* +* R.H.Bartels, G.H.Golub, "The Simplex Method of Linear Programming +* Using LU-decomposition", Comm. ACM, 12, pp. 266-68, 1969. */ + +static void gr_transform(SCF *scf, int k, double un[]) +{ int n = scf->n; + double *f = scf->f; + double *u = scf->u; + int j, k1, kj, kk, n1, nj; + double c, s; + xassert(1 <= k && k <= n); + /* main elimination loop */ + for (k = k; k < n; k++) + { /* determine location of U[k,k] */ + kk = u_loc(scf, k, k); + /* determine location of F[k,1] */ + k1 = f_loc(scf, k, 1); + /* determine location of F[n,1] */ + n1 = f_loc(scf, n, 1); + /* if both U[k,k] and U[n,k] are too small in the magnitude, + replace them by exact zero */ + if (fabs(u[kk]) < eps && fabs(un[k]) < eps) + u[kk] = un[k] = 0.0; + /* if U[n,k] is already zero, elimination is not needed */ + if (un[k] == 0.0) continue; + /* compute the parameters of Givens plane rotation */ + givens(u[kk], un[k], &c, &s); + /* apply Givens rotation to k-th and n-th rows of matrix U */ + for (j = k, kj = kk; j <= n; j++, kj++) + { double ukj = u[kj], unj = un[j]; + u[kj] = c * ukj - s * unj; + un[j] = s * ukj + c * unj; + } + /* apply Givens rotation to k-th and n-th rows of matrix F + to keep the main equality F * C = U * P */ + for (j = 1, kj = k1, nj = n1; j <= n; j++, kj++, nj++) + { double fkj = f[kj], fnj = f[nj]; + f[kj] = c * fkj - s * fnj; + f[nj] = s * fkj + c * fnj; + } + } + /* if U[n,n] is too small in the magnitude, replace it by exact + zero */ + if (fabs(un[n]) < eps) un[n] = 0.0; + /* store U[n,n] in a proper location */ + u[u_loc(scf, n, n)] = un[n]; + return; +} + +/*********************************************************************** +* The routine transform restores triangular structure of matrix U. +* It is a driver to the routines bg_transform and gr_transform (see +* comments to these routines above). */ + +static void transform(SCF *scf, int k, double un[]) +{ switch (scf->t_opt) + { case SCF_TBG: + bg_transform(scf, k, un); + break; + case SCF_TGR: + gr_transform(scf, k, un); + break; + default: + xassert(scf != scf); + } + return; +} + +/*********************************************************************** +* The routine estimate_rank estimates the rank of matrix C. +* +* Since all transformations applied to matrix F are non-singular, +* and F is assumed to be well conditioned, from the main equaility +* F * C = U * P it follows that rank(C) = rank(U), where rank(U) is +* estimated as the number of non-zero diagonal elements of U. */ + +static int estimate_rank(SCF *scf) +{ int n_max = scf->n_max; + int n = scf->n; + double *u = scf->u; + int i, ii, inc, rank = 0; + for (i = 1, ii = u_loc(scf, i, i), inc = n_max; i <= n; + i++, ii += inc, inc--) + if (u[ii] != 0.0) rank++; + return rank; +} + +#if _GLPSCF_DEBUG +/*********************************************************************** +* The routine check_error computes the maximal relative error between +* left- and right-hand sides of the main equality F * C = U * P. (This +* routine is intended only for debugging.) */ + +static void check_error(SCF *scf, const char *func) +{ int n = scf->n; + double *f = scf->f; + double *u = scf->u; + int *p = scf->p; + double *c = scf->c; + int i, j, k; + double d, dmax = 0.0, s, t; + xassert(c != NULL); + for (i = 1; i <= n; i++) + { for (j = 1; j <= n; j++) + { /* compute element (i,j) of product F * C */ + s = 0.0; + for (k = 1; k <= n; k++) + s += f[f_loc(scf, i, k)] * c[f_loc(scf, k, j)]; + /* compute element (i,j) of product U * P */ + k = p[j]; + t = (i <= k ? u[u_loc(scf, i, k)] : 0.0); + /* compute the maximal relative error */ + d = fabs(s - t) / (1.0 + fabs(t)); + if (dmax < d) dmax = d; + } + } + if (dmax > 1e-8) + xprintf("%s: dmax = %g; relative error too large\n", func, + dmax); + return; +} +#endif + +/*********************************************************************** +* NAME +* +* scf_update_exp - update factorization on expanding C +* +* SYNOPSIS +* +* #include "glpscf.h" +* int scf_update_exp(SCF *scf, const double x[], const double y[], +* double z); +* +* DESCRIPTION +* +* The routine scf_update_exp updates the factorization of matrix C on +* expanding it by adding a new row and column as follows: +* +* ( C x ) +* new C = ( ) +* ( y' z ) +* +* where x[1,...,n] is a new column, y[1,...,n] is a new row, and z is +* a new diagonal element. +* +* If on entry the factorization is empty, the parameters x and y can +* be specified as NULL. +* +* RETURNS +* +* 0 The factorization has been successfully updated. +* +* SCF_ESING +* The factorization has been successfully updated, however, new +* matrix C is singular within working precision. Note that the new +* factorization remains valid. +* +* SCF_ELIMIT +* There is not enough room to expand the factorization, because +* n = n_max. The factorization remains unchanged. +* +* ALGORITHM +* +* We can see that: +* +* ( F 0 ) ( C x ) ( FC Fx ) ( UP Fx ) +* ( ) ( ) = ( ) = ( ) = +* ( 0 1 ) ( y' z ) ( y' z ) ( y' z ) +* +* ( U Fx ) ( P 0 ) +* = ( ) ( ), +* ( y'P' z ) ( 0 1 ) +* +* therefore to keep the main equality F * C = U * P we can take: +* +* ( F 0 ) ( U Fx ) ( P 0 ) +* new F = ( ), new U = ( ), new P = ( ), +* ( 0 1 ) ( y'P' z ) ( 0 1 ) +* +* and eliminate the row spike y'P' in the last row of new U to restore +* its upper triangular structure. */ + +int scf_update_exp(SCF *scf, const double x[], const double y[], + double z) +{ int n_max = scf->n_max; + int n = scf->n; + double *f = scf->f; + double *u = scf->u; + int *p = scf->p; +#if _GLPSCF_DEBUG + double *c = scf->c; +#endif + double *un = scf->w; + int i, ij, in, j, k, nj, ret = 0; + double t; + /* check if the factorization can be expanded */ + if (n == n_max) + { /* there is not enough room */ + ret = SCF_ELIMIT; + goto done; + } + /* increase the order of the factorization */ + scf->n = ++n; + /* fill new zero column of matrix F */ + for (i = 1, in = f_loc(scf, i, n); i < n; i++, in += n_max) + f[in] = 0.0; + /* fill new zero row of matrix F */ + for (j = 1, nj = f_loc(scf, n, j); j < n; j++, nj++) + f[nj] = 0.0; + /* fill new unity diagonal element of matrix F */ + f[f_loc(scf, n, n)] = 1.0; + /* compute new column of matrix U, which is (old F) * x */ + for (i = 1; i < n; i++) + { /* u[i,n] := (i-th row of old F) * x */ + t = 0.0; + for (j = 1, ij = f_loc(scf, i, 1); j < n; j++, ij++) + t += f[ij] * x[j]; + u[u_loc(scf, i, n)] = t; + } + /* compute new (spiked) row of matrix U, which is (old P) * y */ + for (j = 1; j < n; j++) un[j] = y[p[j]]; + /* store new diagonal element of matrix U, which is z */ + un[n] = z; + /* expand matrix P */ + p[n] = n; +#if _GLPSCF_DEBUG + /* expand matrix C */ + /* fill its new column, which is x */ + for (i = 1, in = f_loc(scf, i, n); i < n; i++, in += n_max) + c[in] = x[i]; + /* fill its new row, which is y */ + for (j = 1, nj = f_loc(scf, n, j); j < n; j++, nj++) + c[nj] = y[j]; + /* fill its new diagonal element, which is z */ + c[f_loc(scf, n, n)] = z; +#endif + /* restore upper triangular structure of matrix U */ + for (k = 1; k < n; k++) + if (un[k] != 0.0) break; + transform(scf, k, un); + /* estimate the rank of matrices C and U */ + scf->rank = estimate_rank(scf); + if (scf->rank != n) ret = SCF_ESING; +#if _GLPSCF_DEBUG + /* check that the factorization is accurate enough */ + check_error(scf, "scf_update_exp"); +#endif +done: return ret; +} + +/*********************************************************************** +* The routine solve solves the system C * x = b. +* +* From the main equation F * C = U * P it follows that: +* +* C * x = b => F * C * x = F * b => U * P * x = F * b => +* +* P * x = inv(U) * F * b => x = P' * inv(U) * F * b. +* +* On entry the array x contains right-hand side vector b. On exit this +* array contains solution vector x. */ + +static void solve(SCF *scf, double x[]) +{ int n = scf->n; + double *f = scf->f; + double *u = scf->u; + int *p = scf->p; + double *y = scf->w; + int i, j, ij; + double t; + /* y := F * b */ + for (i = 1; i <= n; i++) + { /* y[i] = (i-th row of F) * b */ + t = 0.0; + for (j = 1, ij = f_loc(scf, i, 1); j <= n; j++, ij++) + t += f[ij] * x[j]; + y[i] = t; + } + /* y := inv(U) * y */ + for (i = n; i >= 1; i--) + { t = y[i]; + for (j = n, ij = u_loc(scf, i, n); j > i; j--, ij--) + t -= u[ij] * y[j]; + y[i] = t / u[ij]; + } + /* x := P' * y */ + for (i = 1; i <= n; i++) x[p[i]] = y[i]; + return; +} + +/*********************************************************************** +* The routine tsolve solves the transposed system C' * x = b. +* +* From the main equation F * C = U * P it follows that: +* +* C' * F' = P' * U', +* +* therefore: +* +* C' * x = b => C' * F' * inv(F') * x = b => +* +* P' * U' * inv(F') * x = b => U' * inv(F') * x = P * b => +* +* inv(F') * x = inv(U') * P * b => x = F' * inv(U') * P * b. +* +* On entry the array x contains right-hand side vector b. On exit this +* array contains solution vector x. */ + +static void tsolve(SCF *scf, double x[]) +{ int n = scf->n; + double *f = scf->f; + double *u = scf->u; + int *p = scf->p; + double *y = scf->w; + int i, j, ij; + double t; + /* y := P * b */ + for (i = 1; i <= n; i++) y[i] = x[p[i]]; + /* y := inv(U') * y */ + for (i = 1; i <= n; i++) + { /* compute y[i] */ + ij = u_loc(scf, i, i); + t = (y[i] /= u[ij]); + /* substitute y[i] in other equations */ + for (j = i+1, ij++; j <= n; j++, ij++) + y[j] -= u[ij] * t; + } + /* x := F' * y (computed as linear combination of rows of F) */ + for (j = 1; j <= n; j++) x[j] = 0.0; + for (i = 1; i <= n; i++) + { t = y[i]; /* coefficient of linear combination */ + for (j = 1, ij = f_loc(scf, i, 1); j <= n; j++, ij++) + x[j] += f[ij] * t; + } + return; +} + +/*********************************************************************** +* NAME +* +* scf_solve_it - solve either system C * x = b or C' * x = b +* +* SYNOPSIS +* +* #include "glpscf.h" +* void scf_solve_it(SCF *scf, int tr, double x[]); +* +* DESCRIPTION +* +* The routine scf_solve_it solves either the system C * x = b (if tr +* is zero) or the system C' * x = b, where C' is a matrix transposed +* to C (if tr is non-zero). C is assumed to be non-singular. +* +* On entry the array x should contain the right-hand side vector b in +* locations x[1], ..., x[n], where n is the order of matrix C. On exit +* the array x contains the solution vector x in the same locations. */ + +void scf_solve_it(SCF *scf, int tr, double x[]) +{ if (scf->rank < scf->n) + xfault("scf_solve_it: singular matrix\n"); + if (!tr) + solve(scf, x); + else + tsolve(scf, x); + return; +} + +void scf_reset_it(SCF *scf) +{ /* reset factorization for empty matrix C */ + scf->n = scf->rank = 0; + return; +} + +/*********************************************************************** +* NAME +* +* scf_delete_it - delete Schur complement factorization +* +* SYNOPSIS +* +* #include "glpscf.h" +* void scf_delete_it(SCF *scf); +* +* DESCRIPTION +* +* The routine scf_delete_it deletes the specified factorization and +* frees all the memory allocated to this object. */ + +void scf_delete_it(SCF *scf) +{ xfree(scf->f); + xfree(scf->u); + xfree(scf->p); +#if _GLPSCF_DEBUG + xfree(scf->c); +#endif + xfree(scf->w); + xfree(scf); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpscf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpscf.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,126 @@ +/* glpscf.h (Schur complement factorization) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPSCF_H +#define GLPSCF_H + +/*********************************************************************** +* The structure SCF defines the following factorization of a square +* nxn matrix C (which is the Schur complement): +* +* F * C = U * P, +* +* where F is a square transforming matrix, U is an upper triangular +* matrix, P is a permutation matrix. +* +* It is assumed that matrix C is small and dense, so matrices F and U +* are stored in the dense format by rows as follows: +* +* 1 n n_max 1 n n_max +* 1 * * * * * * x x x x 1 * * * * * * x x x x +* * * * * * * x x x x . * * * * * x x x x +* * * * * * * x x x x . . * * * * x x x x +* * * * * * * x x x x . . . * * * x x x x +* * * * * * * x x x x . . . . * * x x x x +* n * * * * * * x x x x n . . . . . * x x x x +* x x x x x x x x x x . . . . . . x x x x +* x x x x x x x x x x . . . . . . . x x x +* x x x x x x x x x x . . . . . . . . x x +* n_max x x x x x x x x x x n_max . . . . . . . . . x +* +* matrix F matrix U +* +* where '*' are matrix elements, 'x' are reserved locations. +* +* Permutation matrix P is stored in row-like format. +* +* Matrix C normally is not stored. +* +* REFERENCES +* +* 1. M.A.Saunders, "LUSOL: A basis package for constrained optimiza- +* tion," SCCM, Stanford University, 2006. +* +* 2. M.A.Saunders, "Notes 5: Basis Updates," CME 318, Stanford Univer- +* sity, Spring 2006. +* +* 3. M.A.Saunders, "Notes 6: LUSOL---a Basis Factorization Package," +* ibid. */ + +typedef struct SCF SCF; + +struct SCF +{ /* Schur complement factorization */ + int n_max; + /* maximal order of matrices C, F, U, P; n_max >= 1 */ + int n; + /* current order of matrices C, F, U, P; n >= 0 */ + double *f; /* double f[1+n_max*n_max]; */ + /* matrix F stored by rows */ + double *u; /* double u[1+n_max*(n_max+1)/2]; */ + /* upper triangle of matrix U stored by rows */ + int *p; /* int p[1+n_max]; */ + /* matrix P; p[i] = j means that P[i,j] = 1 */ + int t_opt; + /* type of transformation used to restore triangular structure of + matrix U: */ +#define SCF_TBG 1 /* Bartels-Golub elimination */ +#define SCF_TGR 2 /* Givens plane rotation */ + int rank; + /* estimated rank of matrices C and U */ + double *c; /* double c[1+n_max*n_max]; */ + /* matrix C stored in the same format as matrix F and used only + for debugging; normally this array is not allocated */ + double *w; /* double w[1+n_max]; */ + /* working array */ +}; + +/* return codes: */ +#define SCF_ESING 1 /* singular matrix */ +#define SCF_ELIMIT 2 /* update limit reached */ + +#define scf_create_it _glp_scf_create_it +SCF *scf_create_it(int n_max); +/* create Schur complement factorization */ + +#define scf_update_exp _glp_scf_update_exp +int scf_update_exp(SCF *scf, const double x[], const double y[], + double z); +/* update factorization on expanding C */ + +#define scf_solve_it _glp_scf_solve_it +void scf_solve_it(SCF *scf, int tr, double x[]); +/* solve either system C * x = b or C' * x = b */ + +#define scf_reset_it _glp_scf_reset_it +void scf_reset_it(SCF *scf); +/* reset factorization for empty matrix C */ + +#define scf_delete_it _glp_scf_delete_it +void scf_delete_it(SCF *scf); +/* delete Schur complement factorization */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpscl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpscl.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,476 @@ +/* glpscl.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpapi.h" + +/*********************************************************************** +* min_row_aij - determine minimal |a[i,j]| in i-th row +* +* This routine returns minimal magnitude of (non-zero) constraint +* coefficients in i-th row of the constraint matrix. +* +* If the parameter scaled is zero, the original constraint matrix A is +* assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. +* +* If i-th row of the matrix is empty, the routine returns 1. */ + +static double min_row_aij(glp_prob *lp, int i, int scaled) +{ GLPAIJ *aij; + double min_aij, temp; + xassert(1 <= i && i <= lp->m); + min_aij = 1.0; + for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) + { temp = fabs(aij->val); + if (scaled) temp *= (aij->row->rii * aij->col->sjj); + if (aij->r_prev == NULL || min_aij > temp) + min_aij = temp; + } + return min_aij; +} + +/*********************************************************************** +* max_row_aij - determine maximal |a[i,j]| in i-th row +* +* This routine returns maximal magnitude of (non-zero) constraint +* coefficients in i-th row of the constraint matrix. +* +* If the parameter scaled is zero, the original constraint matrix A is +* assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. +* +* If i-th row of the matrix is empty, the routine returns 1. */ + +static double max_row_aij(glp_prob *lp, int i, int scaled) +{ GLPAIJ *aij; + double max_aij, temp; + xassert(1 <= i && i <= lp->m); + max_aij = 1.0; + for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) + { temp = fabs(aij->val); + if (scaled) temp *= (aij->row->rii * aij->col->sjj); + if (aij->r_prev == NULL || max_aij < temp) + max_aij = temp; + } + return max_aij; +} + +/*********************************************************************** +* min_col_aij - determine minimal |a[i,j]| in j-th column +* +* This routine returns minimal magnitude of (non-zero) constraint +* coefficients in j-th column of the constraint matrix. +* +* If the parameter scaled is zero, the original constraint matrix A is +* assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. +* +* If j-th column of the matrix is empty, the routine returns 1. */ + +static double min_col_aij(glp_prob *lp, int j, int scaled) +{ GLPAIJ *aij; + double min_aij, temp; + xassert(1 <= j && j <= lp->n); + min_aij = 1.0; + for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) + { temp = fabs(aij->val); + if (scaled) temp *= (aij->row->rii * aij->col->sjj); + if (aij->c_prev == NULL || min_aij > temp) + min_aij = temp; + } + return min_aij; +} + +/*********************************************************************** +* max_col_aij - determine maximal |a[i,j]| in j-th column +* +* This routine returns maximal magnitude of (non-zero) constraint +* coefficients in j-th column of the constraint matrix. +* +* If the parameter scaled is zero, the original constraint matrix A is +* assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. +* +* If j-th column of the matrix is empty, the routine returns 1. */ + +static double max_col_aij(glp_prob *lp, int j, int scaled) +{ GLPAIJ *aij; + double max_aij, temp; + xassert(1 <= j && j <= lp->n); + max_aij = 1.0; + for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) + { temp = fabs(aij->val); + if (scaled) temp *= (aij->row->rii * aij->col->sjj); + if (aij->c_prev == NULL || max_aij < temp) + max_aij = temp; + } + return max_aij; +} + +/*********************************************************************** +* min_mat_aij - determine minimal |a[i,j]| in constraint matrix +* +* This routine returns minimal magnitude of (non-zero) constraint +* coefficients in the constraint matrix. +* +* If the parameter scaled is zero, the original constraint matrix A is +* assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. +* +* If the matrix is empty, the routine returns 1. */ + +static double min_mat_aij(glp_prob *lp, int scaled) +{ int i; + double min_aij, temp; + min_aij = 1.0; + for (i = 1; i <= lp->m; i++) + { temp = min_row_aij(lp, i, scaled); + if (i == 1 || min_aij > temp) + min_aij = temp; + } + return min_aij; +} + +/*********************************************************************** +* max_mat_aij - determine maximal |a[i,j]| in constraint matrix +* +* This routine returns maximal magnitude of (non-zero) constraint +* coefficients in the constraint matrix. +* +* If the parameter scaled is zero, the original constraint matrix A is +* assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. +* +* If the matrix is empty, the routine returns 1. */ + +static double max_mat_aij(glp_prob *lp, int scaled) +{ int i; + double max_aij, temp; + max_aij = 1.0; + for (i = 1; i <= lp->m; i++) + { temp = max_row_aij(lp, i, scaled); + if (i == 1 || max_aij < temp) + max_aij = temp; + } + return max_aij; +} + +/*********************************************************************** +* eq_scaling - perform equilibration scaling +* +* This routine performs equilibration scaling of rows and columns of +* the constraint matrix. +* +* If the parameter flag is zero, the routine scales rows at first and +* then columns. Otherwise, the routine scales columns and then rows. +* +* Rows are scaled as follows: +* +* n +* a'[i,j] = a[i,j] / max |a[i,j]|, i = 1,...,m. +* j=1 +* +* This makes the infinity (maximum) norm of each row of the matrix +* equal to 1. +* +* Columns are scaled as follows: +* +* n +* a'[i,j] = a[i,j] / max |a[i,j]|, j = 1,...,n. +* i=1 +* +* This makes the infinity (maximum) norm of each column of the matrix +* equal to 1. */ + +static void eq_scaling(glp_prob *lp, int flag) +{ int i, j, pass; + double temp; + xassert(flag == 0 || flag == 1); + for (pass = 0; pass <= 1; pass++) + { if (pass == flag) + { /* scale rows */ + for (i = 1; i <= lp->m; i++) + { temp = max_row_aij(lp, i, 1); + glp_set_rii(lp, i, glp_get_rii(lp, i) / temp); + } + } + else + { /* scale columns */ + for (j = 1; j <= lp->n; j++) + { temp = max_col_aij(lp, j, 1); + glp_set_sjj(lp, j, glp_get_sjj(lp, j) / temp); + } + } + } + return; +} + +/*********************************************************************** +* gm_scaling - perform geometric mean scaling +* +* This routine performs geometric mean scaling of rows and columns of +* the constraint matrix. +* +* If the parameter flag is zero, the routine scales rows at first and +* then columns. Otherwise, the routine scales columns and then rows. +* +* Rows are scaled as follows: +* +* a'[i,j] = a[i,j] / sqrt(alfa[i] * beta[i]), i = 1,...,m, +* +* where: +* n n +* alfa[i] = min |a[i,j]|, beta[i] = max |a[i,j]|. +* j=1 j=1 +* +* This allows decreasing the ratio beta[i] / alfa[i] for each row of +* the matrix. +* +* Columns are scaled as follows: +* +* a'[i,j] = a[i,j] / sqrt(alfa[j] * beta[j]), j = 1,...,n, +* +* where: +* m m +* alfa[j] = min |a[i,j]|, beta[j] = max |a[i,j]|. +* i=1 i=1 +* +* This allows decreasing the ratio beta[j] / alfa[j] for each column +* of the matrix. */ + +static void gm_scaling(glp_prob *lp, int flag) +{ int i, j, pass; + double temp; + xassert(flag == 0 || flag == 1); + for (pass = 0; pass <= 1; pass++) + { if (pass == flag) + { /* scale rows */ + for (i = 1; i <= lp->m; i++) + { temp = min_row_aij(lp, i, 1) * max_row_aij(lp, i, 1); + glp_set_rii(lp, i, glp_get_rii(lp, i) / sqrt(temp)); + } + } + else + { /* scale columns */ + for (j = 1; j <= lp->n; j++) + { temp = min_col_aij(lp, j, 1) * max_col_aij(lp, j, 1); + glp_set_sjj(lp, j, glp_get_sjj(lp, j) / sqrt(temp)); + } + } + } + return; +} + +/*********************************************************************** +* max_row_ratio - determine worst scaling "quality" for rows +* +* This routine returns the worst scaling "quality" for rows of the +* currently scaled constraint matrix: +* +* m +* ratio = max ratio[i], +* i=1 +* where: +* n n +* ratio[i] = max |a[i,j]| / min |a[i,j]|, 1 <= i <= m, +* j=1 j=1 +* +* is the scaling "quality" of i-th row. */ + +static double max_row_ratio(glp_prob *lp) +{ int i; + double ratio, temp; + ratio = 1.0; + for (i = 1; i <= lp->m; i++) + { temp = max_row_aij(lp, i, 1) / min_row_aij(lp, i, 1); + if (i == 1 || ratio < temp) ratio = temp; + } + return ratio; +} + +/*********************************************************************** +* max_col_ratio - determine worst scaling "quality" for columns +* +* This routine returns the worst scaling "quality" for columns of the +* currently scaled constraint matrix: +* +* n +* ratio = max ratio[j], +* j=1 +* where: +* m m +* ratio[j] = max |a[i,j]| / min |a[i,j]|, 1 <= j <= n, +* i=1 i=1 +* +* is the scaling "quality" of j-th column. */ + +static double max_col_ratio(glp_prob *lp) +{ int j; + double ratio, temp; + ratio = 1.0; + for (j = 1; j <= lp->n; j++) + { temp = max_col_aij(lp, j, 1) / min_col_aij(lp, j, 1); + if (j == 1 || ratio < temp) ratio = temp; + } + return ratio; +} + +/*********************************************************************** +* gm_iterate - perform iterative geometric mean scaling +* +* This routine performs iterative geometric mean scaling of rows and +* columns of the constraint matrix. +* +* The parameter it_max specifies the maximal number of iterations. +* Recommended value of it_max is 15. +* +* The parameter tau specifies a minimal improvement of the scaling +* "quality" on each iteration, 0 < tau < 1. It means than the scaling +* process continues while the following condition is satisfied: +* +* ratio[k] <= tau * ratio[k-1], +* +* where ratio = max |a[i,j]| / min |a[i,j]| is the scaling "quality" +* to be minimized, k is the iteration number. Recommended value of tau +* is 0.90. */ + +static void gm_iterate(glp_prob *lp, int it_max, double tau) +{ int k, flag; + double ratio = 0.0, r_old; + /* if the scaling "quality" for rows is better than for columns, + the rows are scaled first; otherwise, the columns are scaled + first */ + flag = (max_row_ratio(lp) > max_col_ratio(lp)); + for (k = 1; k <= it_max; k++) + { /* save the scaling "quality" from previous iteration */ + r_old = ratio; + /* determine the current scaling "quality" */ + ratio = max_mat_aij(lp, 1) / min_mat_aij(lp, 1); +#if 0 + xprintf("k = %d; ratio = %g\n", k, ratio); +#endif + /* if improvement is not enough, terminate scaling */ + if (k > 1 && ratio > tau * r_old) break; + /* otherwise, perform another iteration */ + gm_scaling(lp, flag); + } + return; +} + +/*********************************************************************** +* NAME +* +* scale_prob - scale problem data +* +* SYNOPSIS +* +* #include "glpscl.h" +* void scale_prob(glp_prob *lp, int flags); +* +* DESCRIPTION +* +* The routine scale_prob performs automatic scaling of problem data +* for the specified problem object. */ + +static void scale_prob(glp_prob *lp, int flags) +{ static const char *fmt = + "%s: min|aij| = %10.3e max|aij| = %10.3e ratio = %10.3e\n"; + double min_aij, max_aij, ratio; + xprintf("Scaling...\n"); + /* cancel the current scaling effect */ + glp_unscale_prob(lp); + /* report original scaling "quality" */ + min_aij = min_mat_aij(lp, 1); + max_aij = max_mat_aij(lp, 1); + ratio = max_aij / min_aij; + xprintf(fmt, " A", min_aij, max_aij, ratio); + /* check if the problem is well scaled */ + if (min_aij >= 0.10 && max_aij <= 10.0) + { xprintf("Problem data seem to be well scaled\n"); + /* skip scaling, if required */ + if (flags & GLP_SF_SKIP) goto done; + } + /* perform iterative geometric mean scaling, if required */ + if (flags & GLP_SF_GM) + { gm_iterate(lp, 15, 0.90); + min_aij = min_mat_aij(lp, 1); + max_aij = max_mat_aij(lp, 1); + ratio = max_aij / min_aij; + xprintf(fmt, "GM", min_aij, max_aij, ratio); + } + /* perform equilibration scaling, if required */ + if (flags & GLP_SF_EQ) + { eq_scaling(lp, max_row_ratio(lp) > max_col_ratio(lp)); + min_aij = min_mat_aij(lp, 1); + max_aij = max_mat_aij(lp, 1); + ratio = max_aij / min_aij; + xprintf(fmt, "EQ", min_aij, max_aij, ratio); + } + /* round scale factors to nearest power of two, if required */ + if (flags & GLP_SF_2N) + { int i, j; + for (i = 1; i <= lp->m; i++) + glp_set_rii(lp, i, round2n(glp_get_rii(lp, i))); + for (j = 1; j <= lp->n; j++) + glp_set_sjj(lp, j, round2n(glp_get_sjj(lp, j))); + min_aij = min_mat_aij(lp, 1); + max_aij = max_mat_aij(lp, 1); + ratio = max_aij / min_aij; + xprintf(fmt, "2N", min_aij, max_aij, ratio); + } +done: return; +} + +/*********************************************************************** +* NAME +* +* glp_scale_prob - scale problem data +* +* SYNOPSIS +* +* void glp_scale_prob(glp_prob *lp, int flags); +* +* DESCRIPTION +* +* The routine glp_scale_prob performs automatic scaling of problem +* data for the specified problem object. +* +* The parameter flags specifies scaling options used by the routine. +* Options can be combined with the bitwise OR operator and may be the +* following: +* +* GLP_SF_GM perform geometric mean scaling; +* GLP_SF_EQ perform equilibration scaling; +* GLP_SF_2N round scale factors to nearest power of two; +* GLP_SF_SKIP skip scaling, if the problem is well scaled. +* +* The parameter flags may be specified as GLP_SF_AUTO, in which case +* the routine chooses scaling options automatically. */ + +void glp_scale_prob(glp_prob *lp, int flags) +{ if (flags & ~(GLP_SF_GM | GLP_SF_EQ | GLP_SF_2N | GLP_SF_SKIP | + GLP_SF_AUTO)) + xerror("glp_scale_prob: flags = 0x%02X; invalid scaling option" + "s\n", flags); + if (flags & GLP_SF_AUTO) + flags = (GLP_SF_GM | GLP_SF_EQ | GLP_SF_SKIP); + scale_prob(lp, flags); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpsdf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpsdf.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,262 @@ +/* glpsdf.c (plain data file reading routines) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define GLPSDF_H + +#define GLP_DATA_DEFINED +typedef struct glp_data glp_data; + +#include "glpapi.h" + +struct glp_data +{ /* plain data file */ + char *fname; + /* name of data file */ + XFILE *fp; + /* stream assigned to data file */ + void *jump; /* jmp_buf jump; */ + /* label for go to in case of error */ + int count; + /* line count */ + int c; + /* current character of XEOF */ + char item[255+1]; + /* current data item */ +}; + +static void next_char(glp_data *data); + +glp_data *glp_sdf_open_file(const char *fname) +{ /* open plain data file */ + glp_data *data = NULL; + XFILE *fp; + jmp_buf jump; + fp = xfopen(fname, "r"); + if (fp == NULL) + { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); + goto done; + } + data = xmalloc(sizeof(glp_data)); + data->fname = xmalloc(strlen(fname)+1); + strcpy(data->fname, fname); + data->fp = fp; + data->jump = NULL; + data->count = 0; + data->c = '\n'; + data->item[0] = '\0'; + /* read the very first character */ + if (setjmp(jump)) + { glp_sdf_close_file(data); + data = NULL; + goto done; + } + data->jump = jump; + next_char(data); + data->jump = NULL; +done: return data; +} + +void glp_sdf_set_jump(glp_data *data, void *jump) +{ /* set up error handling */ + data->jump = jump; + return; +} + +void glp_sdf_error(glp_data *data, const char *fmt, ...) +{ /* print error message */ + va_list arg; + xprintf("%s:%d: ", data->fname, data->count); + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + if (data->jump == NULL) + xerror(""); + else + longjmp(data->jump, 1); + /* no return */ +} + +void glp_sdf_warning(glp_data *data, const char *fmt, ...) +{ /* print warning message */ + va_list arg; + xprintf("%s:%d: warning: ", data->fname, data->count); + va_start(arg, fmt); + xvprintf(fmt, arg); + va_end(arg); + return; +} + +static void next_char(glp_data *data) +{ /* read next character */ + int c; + if (data->c == XEOF) + glp_sdf_error(data, "unexpected end of file\n"); + else if (data->c == '\n') + data->count++; + c = xfgetc(data->fp); + if (c < 0) + { if (xferror(data->fp)) + glp_sdf_error(data, "read error - %s\n", xerrmsg()); + else if (data->c == '\n') + c = XEOF; + else + { glp_sdf_warning(data, "missing final end of line\n"); + c = '\n'; + } + } + else if (c == '\n') + ; + else if (isspace(c)) + c = ' '; + else if (iscntrl(c)) + glp_sdf_error(data, "invalid control character 0x%02X\n", c); + data->c = c; + return; +} + +static void skip_pad(glp_data *data) +{ /* skip uninteresting characters and comments */ +loop: while (data->c == ' ' || data->c == '\n') + next_char(data); + if (data->c == '/') + { next_char(data); + if (data->c != '*') + glp_sdf_error(data, "invalid use of slash\n"); + next_char(data); + for (;;) + { if (data->c == '*') + { next_char(data); + if (data->c == '/') + { next_char(data); + break; + } + } + next_char(data); + } + goto loop; + } + return; +} + +static void next_item(glp_data *data) +{ /* read next item */ + int len; + skip_pad(data); + len = 0; + while (!(data->c == ' ' || data->c == '\n')) + { data->item[len++] = (char)data->c; + if (len == sizeof(data->item)) + glp_sdf_error(data, "data item `%.31s...' too long\n", + data->item); + next_char(data); + } + data->item[len] = '\0'; + return; +} + +int glp_sdf_read_int(glp_data *data) +{ /* read integer number */ + int x; + next_item(data); + switch (str2int(data->item, &x)) + { case 0: + break; + case 1: + glp_sdf_error(data, "integer `%s' out of range\n", + data->item); + case 2: + glp_sdf_error(data, "cannot convert `%s' to integer\n", + data->item); + default: + xassert(data != data); + } + return x; +} + +double glp_sdf_read_num(glp_data *data) +{ /* read floating-point number */ + double x; + next_item(data); + switch (str2num(data->item, &x)) + { case 0: + break; + case 1: + glp_sdf_error(data, "number `%s' out of range\n", + data->item); + case 2: + glp_sdf_error(data, "cannot convert `%s' to number\n", + data->item); + default: + xassert(data != data); + } + return x; +} + +const char *glp_sdf_read_item(glp_data *data) +{ /* read data item */ + next_item(data); + return data->item; +} + +const char *glp_sdf_read_text(glp_data *data) +{ /* read text until end of line */ + int c, len = 0; + for (;;) + { c = data->c; + next_char(data); + if (c == ' ') + { /* ignore initial spaces */ + if (len == 0) continue; + /* and multiple ones */ + if (data->item[len-1] == ' ') continue; + } + else if (c == '\n') + { /* remove trailing space */ + if (len > 0 && data->item[len-1] == ' ') len--; + /* and stop reading */ + break; + } + /* add current character to the buffer */ + data->item[len++] = (char)c; + if (len == sizeof(data->item)) + glp_sdf_error(data, "line too long\n", data->item); + } + data->item[len] = '\0'; + return data->item; +} + +int glp_sdf_line(glp_data *data) +{ /* determine current line number */ + return data->count; +} + +void glp_sdf_close_file(glp_data *data) +{ /* close plain data file */ + xfclose(data->fp); + xfree(data->fname); + xfree(data); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpspm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpspm.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,846 @@ +/* glpspm.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glphbm.h" +#include "glprgr.h" +#include "glpspm.h" + +/*********************************************************************** +* NAME +* +* spm_create_mat - create general sparse matrix +* +* SYNOPSIS +* +* #include "glpspm.h" +* SPM *spm_create_mat(int m, int n); +* +* DESCRIPTION +* +* The routine spm_create_mat creates a general sparse matrix having +* m rows and n columns. Being created the matrix is zero (empty), i.e. +* has no elements. +* +* RETURNS +* +* The routine returns a pointer to the matrix created. */ + +SPM *spm_create_mat(int m, int n) +{ SPM *A; + xassert(0 <= m && m < INT_MAX); + xassert(0 <= n && n < INT_MAX); + A = xmalloc(sizeof(SPM)); + A->m = m; + A->n = n; + if (m == 0 || n == 0) + { A->pool = NULL; + A->row = NULL; + A->col = NULL; + } + else + { int i, j; + A->pool = dmp_create_pool(); + A->row = xcalloc(1+m, sizeof(SPME *)); + for (i = 1; i <= m; i++) A->row[i] = NULL; + A->col = xcalloc(1+n, sizeof(SPME *)); + for (j = 1; j <= n; j++) A->col[j] = NULL; + } + return A; +} + +/*********************************************************************** +* NAME +* +* spm_new_elem - add new element to sparse matrix +* +* SYNOPSIS +* +* #include "glpspm.h" +* SPME *spm_new_elem(SPM *A, int i, int j, double val); +* +* DESCRIPTION +* +* The routine spm_new_elem adds a new element to the specified sparse +* matrix. Parameters i, j, and val specify the row number, the column +* number, and a numerical value of the element, respectively. +* +* RETURNS +* +* The routine returns a pointer to the new element added. */ + +SPME *spm_new_elem(SPM *A, int i, int j, double val) +{ SPME *e; + xassert(1 <= i && i <= A->m); + xassert(1 <= j && j <= A->n); + e = dmp_get_atom(A->pool, sizeof(SPME)); + e->i = i; + e->j = j; + e->val = val; + e->r_prev = NULL; + e->r_next = A->row[i]; + if (e->r_next != NULL) e->r_next->r_prev = e; + e->c_prev = NULL; + e->c_next = A->col[j]; + if (e->c_next != NULL) e->c_next->c_prev = e; + A->row[i] = A->col[j] = e; + return e; +} + +/*********************************************************************** +* NAME +* +* spm_delete_mat - delete general sparse matrix +* +* SYNOPSIS +* +* #include "glpspm.h" +* void spm_delete_mat(SPM *A); +* +* DESCRIPTION +* +* The routine deletes the specified general sparse matrix freeing all +* the memory allocated to this object. */ + +void spm_delete_mat(SPM *A) +{ /* delete sparse matrix */ + if (A->pool != NULL) dmp_delete_pool(A->pool); + if (A->row != NULL) xfree(A->row); + if (A->col != NULL) xfree(A->col); + xfree(A); + return; +} + +/*********************************************************************** +* NAME +* +* spm_test_mat_e - create test sparse matrix of E(n,c) class +* +* SYNOPSIS +* +* #include "glpspm.h" +* SPM *spm_test_mat_e(int n, int c); +* +* DESCRIPTION +* +* The routine spm_test_mat_e creates a test sparse matrix of E(n,c) +* class as described in the book: Ole 0sterby, Zahari Zlatev. Direct +* Methods for Sparse Matrices. Springer-Verlag, 1983. +* +* Matrix of E(n,c) class is a symmetric positive definite matrix of +* the order n. It has the number 4 on its main diagonal and the number +* -1 on its four co-diagonals, two of which are neighbour to the main +* diagonal and two others are shifted from the main diagonal on the +* distance c. +* +* It is necessary that n >= 3 and 2 <= c <= n-1. +* +* RETURNS +* +* The routine returns a pointer to the matrix created. */ + +SPM *spm_test_mat_e(int n, int c) +{ SPM *A; + int i; + xassert(n >= 3 && 2 <= c && c <= n-1); + A = spm_create_mat(n, n); + for (i = 1; i <= n; i++) + spm_new_elem(A, i, i, 4.0); + for (i = 1; i <= n-1; i++) + { spm_new_elem(A, i, i+1, -1.0); + spm_new_elem(A, i+1, i, -1.0); + } + for (i = 1; i <= n-c; i++) + { spm_new_elem(A, i, i+c, -1.0); + spm_new_elem(A, i+c, i, -1.0); + } + return A; +} + +/*********************************************************************** +* NAME +* +* spm_test_mat_d - create test sparse matrix of D(n,c) class +* +* SYNOPSIS +* +* #include "glpspm.h" +* SPM *spm_test_mat_d(int n, int c); +* +* DESCRIPTION +* +* The routine spm_test_mat_d creates a test sparse matrix of D(n,c) +* class as described in the book: Ole 0sterby, Zahari Zlatev. Direct +* Methods for Sparse Matrices. Springer-Verlag, 1983. +* +* Matrix of D(n,c) class is a non-singular matrix of the order n. It +* has unity main diagonal, three co-diagonals above the main diagonal +* on the distance c, which are cyclically continued below the main +* diagonal, and a triangle block of the size 10x10 in the upper right +* corner. +* +* It is necessary that n >= 14 and 1 <= c <= n-13. +* +* RETURNS +* +* The routine returns a pointer to the matrix created. */ + +SPM *spm_test_mat_d(int n, int c) +{ SPM *A; + int i, j; + xassert(n >= 14 && 1 <= c && c <= n-13); + A = spm_create_mat(n, n); + for (i = 1; i <= n; i++) + spm_new_elem(A, i, i, 1.0); + for (i = 1; i <= n-c; i++) + spm_new_elem(A, i, i+c, (double)(i+1)); + for (i = n-c+1; i <= n; i++) + spm_new_elem(A, i, i-n+c, (double)(i+1)); + for (i = 1; i <= n-c-1; i++) + spm_new_elem(A, i, i+c+1, (double)(-i)); + for (i = n-c; i <= n; i++) + spm_new_elem(A, i, i-n+c+1, (double)(-i)); + for (i = 1; i <= n-c-2; i++) + spm_new_elem(A, i, i+c+2, 16.0); + for (i = n-c-1; i <= n; i++) + spm_new_elem(A, i, i-n+c+2, 16.0); + for (j = 1; j <= 10; j++) + for (i = 1; i <= 11-j; i++) + spm_new_elem(A, i, n-11+i+j, 100.0 * (double)j); + return A; +} + +/*********************************************************************** +* NAME +* +* spm_show_mat - write sparse matrix pattern in BMP file format +* +* SYNOPSIS +* +* #include "glpspm.h" +* int spm_show_mat(const SPM *A, const char *fname); +* +* DESCRIPTION +* +* The routine spm_show_mat writes pattern of the specified sparse +* matrix in uncompressed BMP file format (Windows bitmap) to a binary +* file whose name is specified by the character string fname. +* +* Each pixel corresponds to one matrix element. The pixel colors have +* the following meaning: +* +* Black structurally zero element +* White positive element +* Cyan negative element +* Green zero element +* Red duplicate element +* +* RETURNS +* +* If no error occured, the routine returns zero. Otherwise, it prints +* an appropriate error message and returns non-zero. */ + +int spm_show_mat(const SPM *A, const char *fname) +{ int m = A->m; + int n = A->n; + int i, j, k, ret; + char *map; + xprintf("spm_show_mat: writing matrix pattern to `%s'...\n", + fname); + xassert(1 <= m && m <= 32767); + xassert(1 <= n && n <= 32767); + map = xmalloc(m * n); + memset(map, 0x08, m * n); + for (i = 1; i <= m; i++) + { SPME *e; + for (e = A->row[i]; e != NULL; e = e->r_next) + { j = e->j; + xassert(1 <= j && j <= n); + k = n * (i - 1) + (j - 1); + if (map[k] != 0x08) + map[k] = 0x0C; + else if (e->val > 0.0) + map[k] = 0x0F; + else if (e->val < 0.0) + map[k] = 0x0B; + else + map[k] = 0x0A; + } + } + ret = rgr_write_bmp16(fname, m, n, map); + xfree(map); + return ret; +} + +/*********************************************************************** +* NAME +* +* spm_read_hbm - read sparse matrix in Harwell-Boeing format +* +* SYNOPSIS +* +* #include "glpspm.h" +* SPM *spm_read_hbm(const char *fname); +* +* DESCRIPTION +* +* The routine spm_read_hbm reads a sparse matrix in the Harwell-Boeing +* format from a text file whose name is the character string fname. +* +* Detailed description of the Harwell-Boeing format recognised by this +* routine can be found in the following report: +* +* I.S.Duff, R.G.Grimes, J.G.Lewis. User's Guide for the Harwell-Boeing +* Sparse Matrix Collection (Release I), TR/PA/92/86, October 1992. +* +* NOTE +* +* The routine spm_read_hbm reads the matrix "as is", due to which zero +* and/or duplicate elements can appear in the matrix. +* +* RETURNS +* +* If no error occured, the routine returns a pointer to the matrix +* created. Otherwise, the routine prints an appropriate error message +* and returns NULL. */ + +SPM *spm_read_hbm(const char *fname) +{ SPM *A = NULL; + HBM *hbm; + int nrow, ncol, nnzero, i, j, beg, end, ptr, *colptr, *rowind; + double val, *values; + char *mxtype; + hbm = hbm_read_mat(fname); + if (hbm == NULL) + { xprintf("spm_read_hbm: unable to read matrix\n"); + goto fini; + } + mxtype = hbm->mxtype; + nrow = hbm->nrow; + ncol = hbm->ncol; + nnzero = hbm->nnzero; + colptr = hbm->colptr; + rowind = hbm->rowind; + values = hbm->values; + if (!(strcmp(mxtype, "RSA") == 0 || strcmp(mxtype, "PSA") == 0 || + strcmp(mxtype, "RUA") == 0 || strcmp(mxtype, "PUA") == 0 || + strcmp(mxtype, "RRA") == 0 || strcmp(mxtype, "PRA") == 0)) + { xprintf("spm_read_hbm: matrix type `%s' not supported\n", + mxtype); + goto fini; + } + A = spm_create_mat(nrow, ncol); + if (mxtype[1] == 'S' || mxtype[1] == 'U') + xassert(nrow == ncol); + for (j = 1; j <= ncol; j++) + { beg = colptr[j]; + end = colptr[j+1]; + xassert(1 <= beg && beg <= end && end <= nnzero + 1); + for (ptr = beg; ptr < end; ptr++) + { i = rowind[ptr]; + xassert(1 <= i && i <= nrow); + if (mxtype[0] == 'R') + val = values[ptr]; + else + val = 1.0; + spm_new_elem(A, i, j, val); + if (mxtype[1] == 'S' && i != j) + spm_new_elem(A, j, i, val); + } + } +fini: if (hbm != NULL) hbm_free_mat(hbm); + return A; +} + +/*********************************************************************** +* NAME +* +* spm_count_nnz - determine number of non-zeros in sparse matrix +* +* SYNOPSIS +* +* #include "glpspm.h" +* int spm_count_nnz(const SPM *A); +* +* RETURNS +* +* The routine spm_count_nnz returns the number of structural non-zero +* elements in the specified sparse matrix. */ + +int spm_count_nnz(const SPM *A) +{ SPME *e; + int i, nnz = 0; + for (i = 1; i <= A->m; i++) + for (e = A->row[i]; e != NULL; e = e->r_next) nnz++; + return nnz; +} + +/*********************************************************************** +* NAME +* +* spm_drop_zeros - remove zero elements from sparse matrix +* +* SYNOPSIS +* +* #include "glpspm.h" +* int spm_drop_zeros(SPM *A, double eps); +* +* DESCRIPTION +* +* The routine spm_drop_zeros removes all elements from the specified +* sparse matrix, whose absolute value is less than eps. +* +* If the parameter eps is 0, only zero elements are removed from the +* matrix. +* +* RETURNS +* +* The routine returns the number of elements removed. */ + +int spm_drop_zeros(SPM *A, double eps) +{ SPME *e, *next; + int i, count = 0; + for (i = 1; i <= A->m; i++) + { for (e = A->row[i]; e != NULL; e = next) + { next = e->r_next; + if (e->val == 0.0 || fabs(e->val) < eps) + { /* remove element from the row list */ + if (e->r_prev == NULL) + A->row[e->i] = e->r_next; + else + e->r_prev->r_next = e->r_next; + if (e->r_next == NULL) + ; + else + e->r_next->r_prev = e->r_prev; + /* remove element from the column list */ + if (e->c_prev == NULL) + A->col[e->j] = e->c_next; + else + e->c_prev->c_next = e->c_next; + if (e->c_next == NULL) + ; + else + e->c_next->c_prev = e->c_prev; + /* return element to the memory pool */ + dmp_free_atom(A->pool, e, sizeof(SPME)); + count++; + } + } + } + return count; +} + +/*********************************************************************** +* NAME +* +* spm_read_mat - read sparse matrix from text file +* +* SYNOPSIS +* +* #include "glpspm.h" +* SPM *spm_read_mat(const char *fname); +* +* DESCRIPTION +* +* The routine reads a sparse matrix from a text file whose name is +* specified by the parameter fname. +* +* For the file format see description of the routine spm_write_mat. +* +* RETURNS +* +* On success the routine returns a pointer to the matrix created, +* otherwise NULL. */ + +#if 1 +SPM *spm_read_mat(const char *fname) +{ xassert(fname != fname); + return NULL; +} +#else +SPM *spm_read_mat(const char *fname) +{ SPM *A = NULL; + PDS *pds; + jmp_buf jump; + int i, j, k, m, n, nnz, fail = 0; + double val; + xprintf("spm_read_mat: reading matrix from `%s'...\n", fname); + pds = pds_open_file(fname); + if (pds == NULL) + { xprintf("spm_read_mat: unable to open `%s' - %s\n", fname, + strerror(errno)); + fail = 1; + goto done; + } + if (setjmp(jump)) + { fail = 1; + goto done; + } + pds_set_jump(pds, jump); + /* number of rows, number of columns, number of non-zeros */ + m = pds_scan_int(pds); + if (m < 0) + pds_error(pds, "invalid number of rows\n"); + n = pds_scan_int(pds); + if (n < 0) + pds_error(pds, "invalid number of columns\n"); + nnz = pds_scan_int(pds); + if (nnz < 0) + pds_error(pds, "invalid number of non-zeros\n"); + /* create matrix */ + xprintf("spm_read_mat: %d rows, %d columns, %d non-zeros\n", + m, n, nnz); + A = spm_create_mat(m, n); + /* read matrix elements */ + for (k = 1; k <= nnz; k++) + { /* row index, column index, element value */ + i = pds_scan_int(pds); + if (!(1 <= i && i <= m)) + pds_error(pds, "row index out of range\n"); + j = pds_scan_int(pds); + if (!(1 <= j && j <= n)) + pds_error(pds, "column index out of range\n"); + val = pds_scan_num(pds); + /* add new element to the matrix */ + spm_new_elem(A, i, j, val); + } + xprintf("spm_read_mat: %d lines were read\n", pds->count); +done: if (pds != NULL) pds_close_file(pds); + if (fail && A != NULL) spm_delete_mat(A), A = NULL; + return A; +} +#endif + +/*********************************************************************** +* NAME +* +* spm_write_mat - write sparse matrix to text file +* +* SYNOPSIS +* +* #include "glpspm.h" +* int spm_write_mat(const SPM *A, const char *fname); +* +* DESCRIPTION +* +* The routine spm_write_mat writes the specified sparse matrix to a +* text file whose name is specified by the parameter fname. This file +* can be read back with the routine spm_read_mat. +* +* RETURNS +* +* On success the routine returns zero, otherwise non-zero. +* +* FILE FORMAT +* +* The file created by the routine spm_write_mat is a plain text file, +* which contains the following information: +* +* m n nnz +* row[1] col[1] val[1] +* row[2] col[2] val[2] +* . . . +* row[nnz] col[nnz] val[nnz] +* +* where: +* m is the number of rows; +* n is the number of columns; +* nnz is the number of non-zeros; +* row[k], k = 1,...,nnz, are row indices; +* col[k], k = 1,...,nnz, are column indices; +* val[k], k = 1,...,nnz, are element values. */ + +#if 1 +int spm_write_mat(const SPM *A, const char *fname) +{ xassert(A != A); + xassert(fname != fname); + return 0; +} +#else +int spm_write_mat(const SPM *A, const char *fname) +{ FILE *fp; + int i, nnz, ret = 0; + xprintf("spm_write_mat: writing matrix to `%s'...\n", fname); + fp = fopen(fname, "w"); + if (fp == NULL) + { xprintf("spm_write_mat: unable to create `%s' - %s\n", fname, + strerror(errno)); + ret = 1; + goto done; + } + /* number of rows, number of columns, number of non-zeros */ + nnz = spm_count_nnz(A); + fprintf(fp, "%d %d %d\n", A->m, A->n, nnz); + /* walk through rows of the matrix */ + for (i = 1; i <= A->m; i++) + { SPME *e; + /* walk through elements of i-th row */ + for (e = A->row[i]; e != NULL; e = e->r_next) + { /* row index, column index, element value */ + fprintf(fp, "%d %d %.*g\n", e->i, e->j, DBL_DIG, e->val); + } + } + fflush(fp); + if (ferror(fp)) + { xprintf("spm_write_mat: writing error on `%s' - %s\n", fname, + strerror(errno)); + ret = 1; + goto done; + } + xprintf("spm_write_mat: %d lines were written\n", 1 + nnz); +done: if (fp != NULL) fclose(fp); + return ret; +} +#endif + +/*********************************************************************** +* NAME +* +* spm_transpose - transpose sparse matrix +* +* SYNOPSIS +* +* #include "glpspm.h" +* SPM *spm_transpose(const SPM *A); +* +* RETURNS +* +* The routine computes and returns sparse matrix B, which is a matrix +* transposed to sparse matrix A. */ + +SPM *spm_transpose(const SPM *A) +{ SPM *B; + int i; + B = spm_create_mat(A->n, A->m); + for (i = 1; i <= A->m; i++) + { SPME *e; + for (e = A->row[i]; e != NULL; e = e->r_next) + spm_new_elem(B, e->j, i, e->val); + } + return B; +} + +SPM *spm_add_sym(const SPM *A, const SPM *B) +{ /* add two sparse matrices (symbolic phase) */ + SPM *C; + int i, j, *flag; + xassert(A->m == B->m); + xassert(A->n == B->n); + /* create resultant matrix */ + C = spm_create_mat(A->m, A->n); + /* allocate and clear the flag array */ + flag = xcalloc(1+C->n, sizeof(int)); + for (j = 1; j <= C->n; j++) + flag[j] = 0; + /* compute pattern of C = A + B */ + for (i = 1; i <= C->m; i++) + { SPME *e; + /* at the beginning i-th row of C is empty */ + /* (i-th row of C) := (i-th row of C) union (i-th row of A) */ + for (e = A->row[i]; e != NULL; e = e->r_next) + { /* (note that i-th row of A may have duplicate elements) */ + j = e->j; + if (!flag[j]) + { spm_new_elem(C, i, j, 0.0); + flag[j] = 1; + } + } + /* (i-th row of C) := (i-th row of C) union (i-th row of B) */ + for (e = B->row[i]; e != NULL; e = e->r_next) + { /* (note that i-th row of B may have duplicate elements) */ + j = e->j; + if (!flag[j]) + { spm_new_elem(C, i, j, 0.0); + flag[j] = 1; + } + } + /* reset the flag array */ + for (e = C->row[i]; e != NULL; e = e->r_next) + flag[e->j] = 0; + } + /* check and deallocate the flag array */ + for (j = 1; j <= C->n; j++) + xassert(!flag[j]); + xfree(flag); + return C; +} + +void spm_add_num(SPM *C, double alfa, const SPM *A, double beta, + const SPM *B) +{ /* add two sparse matrices (numeric phase) */ + int i, j; + double *work; + /* allocate and clear the working array */ + work = xcalloc(1+C->n, sizeof(double)); + for (j = 1; j <= C->n; j++) + work[j] = 0.0; + /* compute matrix C = alfa * A + beta * B */ + for (i = 1; i <= C->n; i++) + { SPME *e; + /* work := alfa * (i-th row of A) + beta * (i-th row of B) */ + /* (note that A and/or B may have duplicate elements) */ + for (e = A->row[i]; e != NULL; e = e->r_next) + work[e->j] += alfa * e->val; + for (e = B->row[i]; e != NULL; e = e->r_next) + work[e->j] += beta * e->val; + /* (i-th row of C) := work, work := 0 */ + for (e = C->row[i]; e != NULL; e = e->r_next) + { j = e->j; + e->val = work[j]; + work[j] = 0.0; + } + } + /* check and deallocate the working array */ + for (j = 1; j <= C->n; j++) + xassert(work[j] == 0.0); + xfree(work); + return; +} + +SPM *spm_add_mat(double alfa, const SPM *A, double beta, const SPM *B) +{ /* add two sparse matrices (driver routine) */ + SPM *C; + C = spm_add_sym(A, B); + spm_add_num(C, alfa, A, beta, B); + return C; +} + +SPM *spm_mul_sym(const SPM *A, const SPM *B) +{ /* multiply two sparse matrices (symbolic phase) */ + int i, j, k, *flag; + SPM *C; + xassert(A->n == B->m); + /* create resultant matrix */ + C = spm_create_mat(A->m, B->n); + /* allocate and clear the flag array */ + flag = xcalloc(1+C->n, sizeof(int)); + for (j = 1; j <= C->n; j++) + flag[j] = 0; + /* compute pattern of C = A * B */ + for (i = 1; i <= C->m; i++) + { SPME *e, *ee; + /* compute pattern of i-th row of C */ + for (e = A->row[i]; e != NULL; e = e->r_next) + { k = e->j; + for (ee = B->row[k]; ee != NULL; ee = ee->r_next) + { j = ee->j; + /* if a[i,k] != 0 and b[k,j] != 0 then c[i,j] != 0 */ + if (!flag[j]) + { /* c[i,j] does not exist, so create it */ + spm_new_elem(C, i, j, 0.0); + flag[j] = 1; + } + } + } + /* reset the flag array */ + for (e = C->row[i]; e != NULL; e = e->r_next) + flag[e->j] = 0; + } + /* check and deallocate the flag array */ + for (j = 1; j <= C->n; j++) + xassert(!flag[j]); + xfree(flag); + return C; +} + +void spm_mul_num(SPM *C, const SPM *A, const SPM *B) +{ /* multiply two sparse matrices (numeric phase) */ + int i, j; + double *work; + /* allocate and clear the working array */ + work = xcalloc(1+A->n, sizeof(double)); + for (j = 1; j <= A->n; j++) + work[j] = 0.0; + /* compute matrix C = A * B */ + for (i = 1; i <= C->m; i++) + { SPME *e, *ee; + double temp; + /* work := (i-th row of A) */ + /* (note that A may have duplicate elements) */ + for (e = A->row[i]; e != NULL; e = e->r_next) + work[e->j] += e->val; + /* compute i-th row of C */ + for (e = C->row[i]; e != NULL; e = e->r_next) + { j = e->j; + /* c[i,j] := work * (j-th column of B) */ + temp = 0.0; + for (ee = B->col[j]; ee != NULL; ee = ee->c_next) + temp += work[ee->i] * ee->val; + e->val = temp; + } + /* reset the working array */ + for (e = A->row[i]; e != NULL; e = e->r_next) + work[e->j] = 0.0; + } + /* check and deallocate the working array */ + for (j = 1; j <= A->n; j++) + xassert(work[j] == 0.0); + xfree(work); + return; +} + +SPM *spm_mul_mat(const SPM *A, const SPM *B) +{ /* multiply two sparse matrices (driver routine) */ + SPM *C; + C = spm_mul_sym(A, B); + spm_mul_num(C, A, B); + return C; +} + +PER *spm_create_per(int n) +{ /* create permutation matrix */ + PER *P; + int k; + xassert(n >= 0); + P = xmalloc(sizeof(PER)); + P->n = n; + P->row = xcalloc(1+n, sizeof(int)); + P->col = xcalloc(1+n, sizeof(int)); + /* initially it is identity matrix */ + for (k = 1; k <= n; k++) + P->row[k] = P->col[k] = k; + return P; +} + +void spm_check_per(PER *P) +{ /* check permutation matrix for correctness */ + int i, j; + xassert(P->n >= 0); + for (i = 1; i <= P->n; i++) + { j = P->row[i]; + xassert(1 <= j && j <= P->n); + xassert(P->col[j] == i); + } + return; +} + +void spm_delete_per(PER *P) +{ /* delete permutation matrix */ + xfree(P->row); + xfree(P->col); + xfree(P); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpspm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpspm.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,165 @@ +/* glpspm.h (general sparse matrix) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPSPM_H +#define GLPSPM_H + +#include "glpdmp.h" + +typedef struct SPM SPM; +typedef struct SPME SPME; + +struct SPM +{ /* general sparse matrix */ + int m; + /* number of rows, m >= 0 */ + int n; + /* number of columns, n >= 0 */ + DMP *pool; + /* memory pool to store matrix elements */ + SPME **row; /* SPME *row[1+m]; */ + /* row[i], 1 <= i <= m, is a pointer to i-th row list */ + SPME **col; /* SPME *col[1+n]; */ + /* col[j], 1 <= j <= n, is a pointer to j-th column list */ +}; + +struct SPME +{ /* sparse matrix element */ + int i; + /* row number */ + int j; + /* column number */ + double val; + /* element value */ + SPME *r_prev; + /* pointer to previous element in the same row */ + SPME *r_next; + /* pointer to next element in the same row */ + SPME *c_prev; + /* pointer to previous element in the same column */ + SPME *c_next; + /* pointer to next element in the same column */ +}; + +typedef struct PER PER; + +struct PER +{ /* permutation matrix */ + int n; + /* matrix order, n >= 0 */ + int *row; /* int row[1+n]; */ + /* row[i] = j means p[i,j] = 1 */ + int *col; /* int col[1+n]; */ + /* col[j] = i means p[i,j] = 1 */ +}; + +#define spm_create_mat _glp_spm_create_mat +SPM *spm_create_mat(int m, int n); +/* create general sparse matrix */ + +#define spm_new_elem _glp_spm_new_elem +SPME *spm_new_elem(SPM *A, int i, int j, double val); +/* add new element to sparse matrix */ + +#define spm_delete_mat _glp_spm_delete_mat +void spm_delete_mat(SPM *A); +/* delete general sparse matrix */ + +#define spm_test_mat_e _glp_spm_test_mat_e +SPM *spm_test_mat_e(int n, int c); +/* create test sparse matrix of E(n,c) class */ + +#define spm_test_mat_d _glp_spm_test_mat_d +SPM *spm_test_mat_d(int n, int c); +/* create test sparse matrix of D(n,c) class */ + +#define spm_show_mat _glp_spm_show_mat +int spm_show_mat(const SPM *A, const char *fname); +/* write sparse matrix pattern in BMP file format */ + +#define spm_read_hbm _glp_spm_read_hbm +SPM *spm_read_hbm(const char *fname); +/* read sparse matrix in Harwell-Boeing format */ + +#define spm_count_nnz _glp_spm_count_nnz +int spm_count_nnz(const SPM *A); +/* determine number of non-zeros in sparse matrix */ + +#define spm_drop_zeros _glp_spm_drop_zeros +int spm_drop_zeros(SPM *A, double eps); +/* remove zero elements from sparse matrix */ + +#define spm_read_mat _glp_spm_read_mat +SPM *spm_read_mat(const char *fname); +/* read sparse matrix from text file */ + +#define spm_write_mat _glp_spm_write_mat +int spm_write_mat(const SPM *A, const char *fname); +/* write sparse matrix to text file */ + +#define spm_transpose _glp_spm_transpose +SPM *spm_transpose(const SPM *A); +/* transpose sparse matrix */ + +#define spm_add_sym _glp_spm_add_sym +SPM *spm_add_sym(const SPM *A, const SPM *B); +/* add two sparse matrices (symbolic phase) */ + +#define spm_add_num _glp_spm_add_num +void spm_add_num(SPM *C, double alfa, const SPM *A, double beta, + const SPM *B); +/* add two sparse matrices (numeric phase) */ + +#define spm_add_mat _glp_spm_add_mat +SPM *spm_add_mat(double alfa, const SPM *A, double beta, + const SPM *B); +/* add two sparse matrices (driver routine) */ + +#define spm_mul_sym _glp_spm_mul_sym +SPM *spm_mul_sym(const SPM *A, const SPM *B); +/* multiply two sparse matrices (symbolic phase) */ + +#define spm_mul_num _glp_spm_mul_num +void spm_mul_num(SPM *C, const SPM *A, const SPM *B); +/* multiply two sparse matrices (numeric phase) */ + +#define spm_mul_mat _glp_spm_mul_mat +SPM *spm_mul_mat(const SPM *A, const SPM *B); +/* multiply two sparse matrices (driver routine) */ + +#define spm_create_per _glp_spm_create_per +PER *spm_create_per(int n); +/* create permutation matrix */ + +#define spm_check_per _glp_spm_check_per +void spm_check_per(PER *P); +/* check permutation matrix for correctness */ + +#define spm_delete_per _glp_spm_delete_per +void spm_delete_per(PER *P); +/* delete permutation matrix */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpspx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpspx.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,40 @@ +/* glpspx.h (core simplex solvers) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPSPX_H +#define GLPSPX_H + +#include "glpapi.h" + +#define spx_primal _glp_spx_primal +int spx_primal(glp_prob *lp, const glp_smcp *parm); +/* core LP solver based on the primal simplex method */ + +#define spx_dual _glp_spx_dual +int spx_dual(glp_prob *lp, const glp_smcp *parm); +/* core LP solver based on the dual simplex method */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpspx01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpspx01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,2954 @@ +/* glpspx01.c (primal simplex method) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpspx.h" + +struct csa +{ /* common storage area */ + /*--------------------------------------------------------------*/ + /* LP data */ + int m; + /* number of rows (auxiliary variables), m > 0 */ + int n; + /* number of columns (structural variables), n > 0 */ + char *type; /* char type[1+m+n]; */ + /* type[0] is not used; + type[k], 1 <= k <= m+n, is the type of variable x[k]: + GLP_FR - free variable + GLP_LO - variable with lower bound + GLP_UP - variable with upper bound + GLP_DB - double-bounded variable + GLP_FX - fixed variable */ + double *lb; /* double lb[1+m+n]; */ + /* lb[0] is not used; + lb[k], 1 <= k <= m+n, is an lower bound of variable x[k]; + if x[k] has no lower bound, lb[k] is zero */ + double *ub; /* double ub[1+m+n]; */ + /* ub[0] is not used; + ub[k], 1 <= k <= m+n, is an upper bound of variable x[k]; + if x[k] has no upper bound, ub[k] is zero; + if x[k] is of fixed type, ub[k] is the same as lb[k] */ + double *coef; /* double coef[1+m+n]; */ + /* coef[0] is not used; + coef[k], 1 <= k <= m+n, is an objective coefficient at + variable x[k] (note that on phase I auxiliary variables also + may have non-zero objective coefficients) */ + /*--------------------------------------------------------------*/ + /* original objective function */ + double *obj; /* double obj[1+n]; */ + /* obj[0] is a constant term of the original objective function; + obj[j], 1 <= j <= n, is an original objective coefficient at + structural variable x[m+j] */ + double zeta; + /* factor used to scale original objective coefficients; its + sign defines original optimization direction: zeta > 0 means + minimization, zeta < 0 means maximization */ + /*--------------------------------------------------------------*/ + /* constraint matrix A; it has m rows and n columns and is stored + by columns */ + int *A_ptr; /* int A_ptr[1+n+1]; */ + /* A_ptr[0] is not used; + A_ptr[j], 1 <= j <= n, is starting position of j-th column in + arrays A_ind and A_val; note that A_ptr[1] is always 1; + A_ptr[n+1] indicates the position after the last element in + arrays A_ind and A_val */ + int *A_ind; /* int A_ind[A_ptr[n+1]]; */ + /* row indices */ + double *A_val; /* double A_val[A_ptr[n+1]]; */ + /* non-zero element values */ + /*--------------------------------------------------------------*/ + /* basis header */ + int *head; /* int head[1+m+n]; */ + /* head[0] is not used; + head[i], 1 <= i <= m, is the ordinal number of basic variable + xB[i]; head[i] = k means that xB[i] = x[k] and i-th column of + matrix B is k-th column of matrix (I|-A); + head[m+j], 1 <= j <= n, is the ordinal number of non-basic + variable xN[j]; head[m+j] = k means that xN[j] = x[k] and j-th + column of matrix N is k-th column of matrix (I|-A) */ + char *stat; /* char stat[1+n]; */ + /* stat[0] is not used; + stat[j], 1 <= j <= n, is the status of non-basic variable + xN[j], which defines its active bound: + GLP_NL - lower bound is active + GLP_NU - upper bound is active + GLP_NF - free variable + GLP_NS - fixed variable */ + /*--------------------------------------------------------------*/ + /* matrix B is the basis matrix; it is composed from columns of + the augmented constraint matrix (I|-A) corresponding to basic + variables and stored in a factorized (invertable) form */ + int valid; + /* factorization is valid only if this flag is set */ + BFD *bfd; /* BFD bfd[1:m,1:m]; */ + /* factorized (invertable) form of the basis matrix */ + /*--------------------------------------------------------------*/ + /* matrix N is a matrix composed from columns of the augmented + constraint matrix (I|-A) corresponding to non-basic variables + except fixed ones; it is stored by rows and changes every time + the basis changes */ + int *N_ptr; /* int N_ptr[1+m+1]; */ + /* N_ptr[0] is not used; + N_ptr[i], 1 <= i <= m, is starting position of i-th row in + arrays N_ind and N_val; note that N_ptr[1] is always 1; + N_ptr[m+1] indicates the position after the last element in + arrays N_ind and N_val */ + int *N_len; /* int N_len[1+m]; */ + /* N_len[0] is not used; + N_len[i], 1 <= i <= m, is length of i-th row (0 to n) */ + int *N_ind; /* int N_ind[N_ptr[m+1]]; */ + /* column indices */ + double *N_val; /* double N_val[N_ptr[m+1]]; */ + /* non-zero element values */ + /*--------------------------------------------------------------*/ + /* working parameters */ + int phase; + /* search phase: + 0 - not determined yet + 1 - search for primal feasible solution + 2 - search for optimal solution */ + glp_long tm_beg; + /* time value at the beginning of the search */ + int it_beg; + /* simplex iteration count at the beginning of the search */ + int it_cnt; + /* simplex iteration count; it increases by one every time the + basis changes (including the case when a non-basic variable + jumps to its opposite bound) */ + int it_dpy; + /* simplex iteration count at the most recent display output */ + /*--------------------------------------------------------------*/ + /* basic solution components */ + double *bbar; /* double bbar[1+m]; */ + /* bbar[0] is not used; + bbar[i], 1 <= i <= m, is primal value of basic variable xB[i] + (if xB[i] is free, its primal value is not updated) */ + double *cbar; /* double cbar[1+n]; */ + /* cbar[0] is not used; + cbar[j], 1 <= j <= n, is reduced cost of non-basic variable + xN[j] (if xN[j] is fixed, its reduced cost is not updated) */ + /*--------------------------------------------------------------*/ + /* the following pricing technique options may be used: + GLP_PT_STD - standard ("textbook") pricing; + GLP_PT_PSE - projected steepest edge; + GLP_PT_DVX - Devex pricing (not implemented yet); + in case of GLP_PT_STD the reference space is not used, and all + steepest edge coefficients are set to 1 */ + int refct; + /* this count is set to an initial value when the reference space + is defined and decreases by one every time the basis changes; + once this count reaches zero, the reference space is redefined + again */ + char *refsp; /* char refsp[1+m+n]; */ + /* refsp[0] is not used; + refsp[k], 1 <= k <= m+n, is the flag which means that variable + x[k] belongs to the current reference space */ + double *gamma; /* double gamma[1+n]; */ + /* gamma[0] is not used; + gamma[j], 1 <= j <= n, is the steepest edge coefficient for + non-basic variable xN[j]; if xN[j] is fixed, gamma[j] is not + used and just set to 1 */ + /*--------------------------------------------------------------*/ + /* non-basic variable xN[q] chosen to enter the basis */ + int q; + /* index of the non-basic variable xN[q] chosen, 1 <= q <= n; + if the set of eligible non-basic variables is empty and thus + no variable has been chosen, q is set to 0 */ + /*--------------------------------------------------------------*/ + /* pivot column of the simplex table corresponding to non-basic + variable xN[q] chosen is the following vector: + T * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], + where B is the current basis matrix, N[q] is a column of the + matrix (I|-A) corresponding to xN[q] */ + int tcol_nnz; + /* number of non-zero components, 0 <= nnz <= m */ + int *tcol_ind; /* int tcol_ind[1+m]; */ + /* tcol_ind[0] is not used; + tcol_ind[t], 1 <= t <= nnz, is an index of non-zero component, + i.e. tcol_ind[t] = i means that tcol_vec[i] != 0 */ + double *tcol_vec; /* double tcol_vec[1+m]; */ + /* tcol_vec[0] is not used; + tcol_vec[i], 1 <= i <= m, is a numeric value of i-th component + of the column */ + double tcol_max; + /* infinity (maximum) norm of the column (max |tcol_vec[i]|) */ + int tcol_num; + /* number of significant non-zero components, which means that: + |tcol_vec[i]| >= eps for i in tcol_ind[1,...,num], + |tcol_vec[i]| < eps for i in tcol_ind[num+1,...,nnz], + where eps is a pivot tolerance */ + /*--------------------------------------------------------------*/ + /* basic variable xB[p] chosen to leave the basis */ + int p; + /* index of the basic variable xB[p] chosen, 1 <= p <= m; + p = 0 means that no basic variable reaches its bound; + p < 0 means that non-basic variable xN[q] reaches its opposite + bound before any basic variable */ + int p_stat; + /* new status (GLP_NL, GLP_NU, or GLP_NS) to be assigned to xB[p] + once it has left the basis */ + double teta; + /* change of non-basic variable xN[q] (see above), on which xB[p] + (or, if p < 0, xN[q] itself) reaches its bound */ + /*--------------------------------------------------------------*/ + /* pivot row of the simplex table corresponding to basic variable + xB[p] chosen is the following vector: + T' * e[p] = - N' * inv(B') * e[p] = - N' * rho, + where B' is a matrix transposed to the current basis matrix, + N' is a matrix, whose rows are columns of the matrix (I|-A) + corresponding to non-basic non-fixed variables */ + int trow_nnz; + /* number of non-zero components, 0 <= nnz <= n */ + int *trow_ind; /* int trow_ind[1+n]; */ + /* trow_ind[0] is not used; + trow_ind[t], 1 <= t <= nnz, is an index of non-zero component, + i.e. trow_ind[t] = j means that trow_vec[j] != 0 */ + double *trow_vec; /* int trow_vec[1+n]; */ + /* trow_vec[0] is not used; + trow_vec[j], 1 <= j <= n, is a numeric value of j-th component + of the row */ + /*--------------------------------------------------------------*/ + /* working arrays */ + double *work1; /* double work1[1+m]; */ + double *work2; /* double work2[1+m]; */ + double *work3; /* double work3[1+m]; */ + double *work4; /* double work4[1+m]; */ +}; + +static const double kappa = 0.10; + +/*********************************************************************** +* alloc_csa - allocate common storage area +* +* This routine allocates all arrays in the common storage area (CSA) +* and returns a pointer to the CSA. */ + +static struct csa *alloc_csa(glp_prob *lp) +{ struct csa *csa; + int m = lp->m; + int n = lp->n; + int nnz = lp->nnz; + csa = xmalloc(sizeof(struct csa)); + xassert(m > 0 && n > 0); + csa->m = m; + csa->n = n; + csa->type = xcalloc(1+m+n, sizeof(char)); + csa->lb = xcalloc(1+m+n, sizeof(double)); + csa->ub = xcalloc(1+m+n, sizeof(double)); + csa->coef = xcalloc(1+m+n, sizeof(double)); + csa->obj = xcalloc(1+n, sizeof(double)); + csa->A_ptr = xcalloc(1+n+1, sizeof(int)); + csa->A_ind = xcalloc(1+nnz, sizeof(int)); + csa->A_val = xcalloc(1+nnz, sizeof(double)); + csa->head = xcalloc(1+m+n, sizeof(int)); + csa->stat = xcalloc(1+n, sizeof(char)); + csa->N_ptr = xcalloc(1+m+1, sizeof(int)); + csa->N_len = xcalloc(1+m, sizeof(int)); + csa->N_ind = NULL; /* will be allocated later */ + csa->N_val = NULL; /* will be allocated later */ + csa->bbar = xcalloc(1+m, sizeof(double)); + csa->cbar = xcalloc(1+n, sizeof(double)); + csa->refsp = xcalloc(1+m+n, sizeof(char)); + csa->gamma = xcalloc(1+n, sizeof(double)); + csa->tcol_ind = xcalloc(1+m, sizeof(int)); + csa->tcol_vec = xcalloc(1+m, sizeof(double)); + csa->trow_ind = xcalloc(1+n, sizeof(int)); + csa->trow_vec = xcalloc(1+n, sizeof(double)); + csa->work1 = xcalloc(1+m, sizeof(double)); + csa->work2 = xcalloc(1+m, sizeof(double)); + csa->work3 = xcalloc(1+m, sizeof(double)); + csa->work4 = xcalloc(1+m, sizeof(double)); + return csa; +} + +/*********************************************************************** +* init_csa - initialize common storage area +* +* This routine initializes all data structures in the common storage +* area (CSA). */ + +static void alloc_N(struct csa *csa); +static void build_N(struct csa *csa); + +static void init_csa(struct csa *csa, glp_prob *lp) +{ int m = csa->m; + int n = csa->n; + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + double *coef = csa->coef; + double *obj = csa->obj; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + char *stat = csa->stat; + char *refsp = csa->refsp; + double *gamma = csa->gamma; + int i, j, k, loc; + double cmax; + /* auxiliary variables */ + for (i = 1; i <= m; i++) + { GLPROW *row = lp->row[i]; + type[i] = (char)row->type; + lb[i] = row->lb * row->rii; + ub[i] = row->ub * row->rii; + coef[i] = 0.0; + } + /* structural variables */ + for (j = 1; j <= n; j++) + { GLPCOL *col = lp->col[j]; + type[m+j] = (char)col->type; + lb[m+j] = col->lb / col->sjj; + ub[m+j] = col->ub / col->sjj; + coef[m+j] = col->coef * col->sjj; + } + /* original objective function */ + obj[0] = lp->c0; + memcpy(&obj[1], &coef[m+1], n * sizeof(double)); + /* factor used to scale original objective coefficients */ + cmax = 0.0; + for (j = 1; j <= n; j++) + if (cmax < fabs(obj[j])) cmax = fabs(obj[j]); + if (cmax == 0.0) cmax = 1.0; + switch (lp->dir) + { case GLP_MIN: + csa->zeta = + 1.0 / cmax; + break; + case GLP_MAX: + csa->zeta = - 1.0 / cmax; + break; + default: + xassert(lp != lp); + } +#if 1 + if (fabs(csa->zeta) < 1.0) csa->zeta *= 1000.0; +#endif + /* matrix A (by columns) */ + loc = 1; + for (j = 1; j <= n; j++) + { GLPAIJ *aij; + A_ptr[j] = loc; + for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) + { A_ind[loc] = aij->row->i; + A_val[loc] = aij->row->rii * aij->val * aij->col->sjj; + loc++; + } + } + A_ptr[n+1] = loc; + xassert(loc == lp->nnz+1); + /* basis header */ + xassert(lp->valid); + memcpy(&head[1], &lp->head[1], m * sizeof(int)); + k = 0; + for (i = 1; i <= m; i++) + { GLPROW *row = lp->row[i]; + if (row->stat != GLP_BS) + { k++; + xassert(k <= n); + head[m+k] = i; + stat[k] = (char)row->stat; + } + } + for (j = 1; j <= n; j++) + { GLPCOL *col = lp->col[j]; + if (col->stat != GLP_BS) + { k++; + xassert(k <= n); + head[m+k] = m + j; + stat[k] = (char)col->stat; + } + } + xassert(k == n); + /* factorization of matrix B */ + csa->valid = 1, lp->valid = 0; + csa->bfd = lp->bfd, lp->bfd = NULL; + /* matrix N (by rows) */ + alloc_N(csa); + build_N(csa); + /* working parameters */ + csa->phase = 0; + csa->tm_beg = xtime(); + csa->it_beg = csa->it_cnt = lp->it_cnt; + csa->it_dpy = -1; + /* reference space and steepest edge coefficients */ + csa->refct = 0; + memset(&refsp[1], 0, (m+n) * sizeof(char)); + for (j = 1; j <= n; j++) gamma[j] = 1.0; + return; +} + +/*********************************************************************** +* invert_B - compute factorization of the basis matrix +* +* This routine computes factorization of the current basis matrix B. +* +* If the operation is successful, the routine returns zero, otherwise +* non-zero. */ + +static int inv_col(void *info, int i, int ind[], double val[]) +{ /* this auxiliary routine returns row indices and numeric values + of non-zero elements of i-th column of the basis matrix */ + struct csa *csa = info; + int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + int k, len, ptr, t; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); +#endif + k = head[i]; /* B[i] is k-th column of (I|-A) */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { /* B[i] is k-th column of submatrix I */ + len = 1; + ind[1] = k; + val[1] = 1.0; + } + else + { /* B[i] is (k-m)-th column of submatrix (-A) */ + ptr = A_ptr[k-m]; + len = A_ptr[k-m+1] - ptr; + memcpy(&ind[1], &A_ind[ptr], len * sizeof(int)); + memcpy(&val[1], &A_val[ptr], len * sizeof(double)); + for (t = 1; t <= len; t++) val[t] = - val[t]; + } + return len; +} + +static int invert_B(struct csa *csa) +{ int ret; + ret = bfd_factorize(csa->bfd, csa->m, NULL, inv_col, csa); + csa->valid = (ret == 0); + return ret; +} + +/*********************************************************************** +* update_B - update factorization of the basis matrix +* +* This routine replaces i-th column of the basis matrix B by k-th +* column of the augmented constraint matrix (I|-A) and then updates +* the factorization of B. +* +* If the factorization has been successfully updated, the routine +* returns zero, otherwise non-zero. */ + +static int update_B(struct csa *csa, int i, int k) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int ret; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { /* new i-th column of B is k-th column of I */ + int ind[1+1]; + double val[1+1]; + ind[1] = k; + val[1] = 1.0; + xassert(csa->valid); + ret = bfd_update_it(csa->bfd, i, 0, 1, ind, val); + } + else + { /* new i-th column of B is (k-m)-th column of (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + double *val = csa->work1; + int beg, end, ptr, len; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + len = 0; + for (ptr = beg; ptr < end; ptr++) + val[++len] = - A_val[ptr]; + xassert(csa->valid); + ret = bfd_update_it(csa->bfd, i, 0, len, &A_ind[beg-1], val); + } + csa->valid = (ret == 0); + return ret; +} + +/*********************************************************************** +* error_ftran - compute residual vector r = h - B * x +* +* This routine computes the residual vector r = h - B * x, where B is +* the current basis matrix, h is the vector of right-hand sides, x is +* the solution vector. */ + +static void error_ftran(struct csa *csa, double h[], double x[], + double r[]) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + int i, k, beg, end, ptr; + double temp; + /* compute the residual vector: + r = h - B * x = h - B[1] * x[1] - ... - B[m] * x[m], + where B[1], ..., B[m] are columns of matrix B */ + memcpy(&r[1], &h[1], m * sizeof(double)); + for (i = 1; i <= m; i++) + { temp = x[i]; + if (temp == 0.0) continue; + k = head[i]; /* B[i] is k-th column of (I|-A) */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { /* B[i] is k-th column of submatrix I */ + r[k] -= temp; + } + else + { /* B[i] is (k-m)-th column of submatrix (-A) */ + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + r[A_ind[ptr]] += A_val[ptr] * temp; + } + } + return; +} + +/*********************************************************************** +* refine_ftran - refine solution of B * x = h +* +* This routine performs one iteration to refine the solution of +* the system B * x = h, where B is the current basis matrix, h is the +* vector of right-hand sides, x is the solution vector. */ + +static void refine_ftran(struct csa *csa, double h[], double x[]) +{ int m = csa->m; + double *r = csa->work1; + double *d = csa->work1; + int i; + /* compute the residual vector r = h - B * x */ + error_ftran(csa, h, x, r); + /* compute the correction vector d = inv(B) * r */ + xassert(csa->valid); + bfd_ftran(csa->bfd, d); + /* refine the solution vector (new x) = (old x) + d */ + for (i = 1; i <= m; i++) x[i] += d[i]; + return; +} + +/*********************************************************************** +* error_btran - compute residual vector r = h - B'* x +* +* This routine computes the residual vector r = h - B'* x, where B' +* is a matrix transposed to the current basis matrix, h is the vector +* of right-hand sides, x is the solution vector. */ + +static void error_btran(struct csa *csa, double h[], double x[], + double r[]) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + int i, k, beg, end, ptr; + double temp; + /* compute the residual vector r = b - B'* x */ + for (i = 1; i <= m; i++) + { /* r[i] := b[i] - (i-th column of B)'* x */ + k = head[i]; /* B[i] is k-th column of (I|-A) */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + temp = h[i]; + if (k <= m) + { /* B[i] is k-th column of submatrix I */ + temp -= x[k]; + } + else + { /* B[i] is (k-m)-th column of submatrix (-A) */ + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + temp += A_val[ptr] * x[A_ind[ptr]]; + } + r[i] = temp; + } + return; +} + +/*********************************************************************** +* refine_btran - refine solution of B'* x = h +* +* This routine performs one iteration to refine the solution of the +* system B'* x = h, where B' is a matrix transposed to the current +* basis matrix, h is the vector of right-hand sides, x is the solution +* vector. */ + +static void refine_btran(struct csa *csa, double h[], double x[]) +{ int m = csa->m; + double *r = csa->work1; + double *d = csa->work1; + int i; + /* compute the residual vector r = h - B'* x */ + error_btran(csa, h, x, r); + /* compute the correction vector d = inv(B') * r */ + xassert(csa->valid); + bfd_btran(csa->bfd, d); + /* refine the solution vector (new x) = (old x) + d */ + for (i = 1; i <= m; i++) x[i] += d[i]; + return; +} + +/*********************************************************************** +* alloc_N - allocate matrix N +* +* This routine determines maximal row lengths of matrix N, sets its +* row pointers, and then allocates arrays N_ind and N_val. +* +* Note that some fixed structural variables may temporarily become +* double-bounded, so corresponding columns of matrix A should not be +* ignored on calculating maximal row lengths of matrix N. */ + +static void alloc_N(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + int *N_ptr = csa->N_ptr; + int *N_len = csa->N_len; + int i, j, beg, end, ptr; + /* determine number of non-zeros in each row of the augmented + constraint matrix (I|-A) */ + for (i = 1; i <= m; i++) + N_len[i] = 1; + for (j = 1; j <= n; j++) + { beg = A_ptr[j]; + end = A_ptr[j+1]; + for (ptr = beg; ptr < end; ptr++) + N_len[A_ind[ptr]]++; + } + /* determine maximal row lengths of matrix N and set its row + pointers */ + N_ptr[1] = 1; + for (i = 1; i <= m; i++) + { /* row of matrix N cannot have more than n non-zeros */ + if (N_len[i] > n) N_len[i] = n; + N_ptr[i+1] = N_ptr[i] + N_len[i]; + } + /* now maximal number of non-zeros in matrix N is known */ + csa->N_ind = xcalloc(N_ptr[m+1], sizeof(int)); + csa->N_val = xcalloc(N_ptr[m+1], sizeof(double)); + return; +} + +/*********************************************************************** +* add_N_col - add column of matrix (I|-A) to matrix N +* +* This routine adds j-th column to matrix N which is k-th column of +* the augmented constraint matrix (I|-A). (It is assumed that old j-th +* column was previously removed from matrix N.) */ + +static void add_N_col(struct csa *csa, int j, int k) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *N_ptr = csa->N_ptr; + int *N_len = csa->N_len; + int *N_ind = csa->N_ind; + double *N_val = csa->N_val; + int pos; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + pos = N_ptr[k] + (N_len[k]++); +#ifdef GLP_DEBUG + xassert(pos < N_ptr[k+1]); +#endif + N_ind[pos] = j; + N_val[pos] = 1.0; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int i, beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + { i = A_ind[ptr]; /* row number */ + pos = N_ptr[i] + (N_len[i]++); +#ifdef GLP_DEBUG + xassert(pos < N_ptr[i+1]); +#endif + N_ind[pos] = j; + N_val[pos] = - A_val[ptr]; + } + } + return; +} + +/*********************************************************************** +* del_N_col - remove column of matrix (I|-A) from matrix N +* +* This routine removes j-th column from matrix N which is k-th column +* of the augmented constraint matrix (I|-A). */ + +static void del_N_col(struct csa *csa, int j, int k) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *N_ptr = csa->N_ptr; + int *N_len = csa->N_len; + int *N_ind = csa->N_ind; + double *N_val = csa->N_val; + int pos, head, tail; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + /* find element in k-th row of N */ + head = N_ptr[k]; + for (pos = head; N_ind[pos] != j; pos++) /* nop */; + /* and remove it from the row list */ + tail = head + (--N_len[k]); +#ifdef GLP_DEBUG + xassert(pos <= tail); +#endif + N_ind[pos] = N_ind[tail]; + N_val[pos] = N_val[tail]; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + int i, beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + { i = A_ind[ptr]; /* row number */ + /* find element in i-th row of N */ + head = N_ptr[i]; + for (pos = head; N_ind[pos] != j; pos++) /* nop */; + /* and remove it from the row list */ + tail = head + (--N_len[i]); +#ifdef GLP_DEBUG + xassert(pos <= tail); +#endif + N_ind[pos] = N_ind[tail]; + N_val[pos] = N_val[tail]; + } + } + return; +} + +/*********************************************************************** +* build_N - build matrix N for current basis +* +* This routine builds matrix N for the current basis from columns +* of the augmented constraint matrix (I|-A) corresponding to non-basic +* non-fixed variables. */ + +static void build_N(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + int *head = csa->head; + char *stat = csa->stat; + int *N_len = csa->N_len; + int j, k; + /* N := empty matrix */ + memset(&N_len[1], 0, m * sizeof(int)); + /* go through non-basic columns of matrix (I|-A) */ + for (j = 1; j <= n; j++) + { if (stat[j] != GLP_NS) + { /* xN[j] is non-fixed; add j-th column to matrix N which is + k-th column of matrix (I|-A) */ + k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + add_N_col(csa, j, k); + } + } + return; +} + +/*********************************************************************** +* get_xN - determine current value of non-basic variable xN[j] +* +* This routine returns the current value of non-basic variable xN[j], +* which is a value of its active bound. */ + +static double get_xN(struct csa *csa, int j) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + double *lb = csa->lb; + double *ub = csa->ub; + int *head = csa->head; + char *stat = csa->stat; + int k; + double xN; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + switch (stat[j]) + { case GLP_NL: + /* x[k] is on its lower bound */ + xN = lb[k]; break; + case GLP_NU: + /* x[k] is on its upper bound */ + xN = ub[k]; break; + case GLP_NF: + /* x[k] is free non-basic variable */ + xN = 0.0; break; + case GLP_NS: + /* x[k] is fixed non-basic variable */ + xN = lb[k]; break; + default: + xassert(stat != stat); + } + return xN; +} + +/*********************************************************************** +* eval_beta - compute primal values of basic variables +* +* This routine computes current primal values of all basic variables: +* +* beta = - inv(B) * N * xN, +* +* where B is the current basis matrix, N is a matrix built of columns +* of matrix (I|-A) corresponding to non-basic variables, and xN is the +* vector of current values of non-basic variables. */ + +static void eval_beta(struct csa *csa, double beta[]) +{ int m = csa->m; + int n = csa->n; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + double *h = csa->work2; + int i, j, k, beg, end, ptr; + double xN; + /* compute the right-hand side vector: + h := - N * xN = - N[1] * xN[1] - ... - N[n] * xN[n], + where N[1], ..., N[n] are columns of matrix N */ + for (i = 1; i <= m; i++) + h[i] = 0.0; + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* determine current value of xN[j] */ + xN = get_xN(csa, j); + if (xN == 0.0) continue; + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + h[k] -= xN; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + h[A_ind[ptr]] += xN * A_val[ptr]; + } + } + /* solve system B * beta = h */ + memcpy(&beta[1], &h[1], m * sizeof(double)); + xassert(csa->valid); + bfd_ftran(csa->bfd, beta); + /* and refine the solution */ + refine_ftran(csa, h, beta); + return; +} + +/*********************************************************************** +* eval_pi - compute vector of simplex multipliers +* +* This routine computes the vector of current simplex multipliers: +* +* pi = inv(B') * cB, +* +* where B' is a matrix transposed to the current basis matrix, cB is +* a subvector of objective coefficients at basic variables. */ + +static void eval_pi(struct csa *csa, double pi[]) +{ int m = csa->m; + double *c = csa->coef; + int *head = csa->head; + double *cB = csa->work2; + int i; + /* construct the right-hand side vector cB */ + for (i = 1; i <= m; i++) + cB[i] = c[head[i]]; + /* solve system B'* pi = cB */ + memcpy(&pi[1], &cB[1], m * sizeof(double)); + xassert(csa->valid); + bfd_btran(csa->bfd, pi); + /* and refine the solution */ + refine_btran(csa, cB, pi); + return; +} + +/*********************************************************************** +* eval_cost - compute reduced cost of non-basic variable xN[j] +* +* This routine computes the current reduced cost of non-basic variable +* xN[j]: +* +* d[j] = cN[j] - N'[j] * pi, +* +* where cN[j] is the objective coefficient at variable xN[j], N[j] is +* a column of the augmented constraint matrix (I|-A) corresponding to +* xN[j], pi is the vector of simplex multipliers. */ + +static double eval_cost(struct csa *csa, double pi[], int j) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + double *coef = csa->coef; + int *head = csa->head; + int k; + double dj; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + dj = coef[k]; + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + dj -= pi[k]; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + dj += A_val[ptr] * pi[A_ind[ptr]]; + } + return dj; +} + +/*********************************************************************** +* eval_bbar - compute and store primal values of basic variables +* +* This routine computes primal values of all basic variables and then +* stores them in the solution array. */ + +static void eval_bbar(struct csa *csa) +{ eval_beta(csa, csa->bbar); + return; +} + +/*********************************************************************** +* eval_cbar - compute and store reduced costs of non-basic variables +* +* This routine computes reduced costs of all non-basic variables and +* then stores them in the solution array. */ + +static void eval_cbar(struct csa *csa) +{ +#ifdef GLP_DEBUG + int m = csa->m; +#endif + int n = csa->n; +#ifdef GLP_DEBUG + int *head = csa->head; +#endif + double *cbar = csa->cbar; + double *pi = csa->work3; + int j; +#ifdef GLP_DEBUG + int k; +#endif + /* compute simplex multipliers */ + eval_pi(csa, pi); + /* compute and store reduced costs */ + for (j = 1; j <= n; j++) + { +#ifdef GLP_DEBUG + k = head[m+j]; /* x[k] = xN[j] */ + xassert(1 <= k && k <= m+n); +#endif + cbar[j] = eval_cost(csa, pi, j); + } + return; +} + +/*********************************************************************** +* reset_refsp - reset the reference space +* +* This routine resets (redefines) the reference space used in the +* projected steepest edge pricing algorithm. */ + +static void reset_refsp(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + int *head = csa->head; + char *refsp = csa->refsp; + double *gamma = csa->gamma; + int j, k; + xassert(csa->refct == 0); + csa->refct = 1000; + memset(&refsp[1], 0, (m+n) * sizeof(char)); + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ + refsp[k] = 1; + gamma[j] = 1.0; + } + return; +} + +/*********************************************************************** +* eval_gamma - compute steepest edge coefficient +* +* This routine computes the steepest edge coefficient for non-basic +* variable xN[j] using its direct definition: +* +* gamma[j] = delta[j] + sum alfa[i,j]^2, +* i in R +* +* where delta[j] = 1, if xN[j] is in the current reference space, +* and 0 otherwise; R is a set of basic variables xB[i], which are in +* the current reference space; alfa[i,j] are elements of the current +* simplex table. +* +* NOTE: The routine is intended only for debugginig purposes. */ + +static double eval_gamma(struct csa *csa, int j) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *head = csa->head; + char *refsp = csa->refsp; + double *alfa = csa->work3; + double *h = csa->work3; + int i, k; + double gamma; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* construct the right-hand side vector h = - N[j] */ + for (i = 1; i <= m; i++) + h[i] = 0.0; + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + h[k] = -1.0; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + h[A_ind[ptr]] = A_val[ptr]; + } + /* solve system B * alfa = h */ + xassert(csa->valid); + bfd_ftran(csa->bfd, alfa); + /* compute gamma */ + gamma = (refsp[k] ? 1.0 : 0.0); + for (i = 1; i <= m; i++) + { k = head[i]; +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (refsp[k]) gamma += alfa[i] * alfa[i]; + } + return gamma; +} + +/*********************************************************************** +* chuzc - choose non-basic variable (column of the simplex table) +* +* This routine chooses non-basic variable xN[q], which has largest +* weighted reduced cost: +* +* |d[q]| / sqrt(gamma[q]) = max |d[j]| / sqrt(gamma[j]), +* j in J +* +* where J is a subset of eligible non-basic variables xN[j], d[j] is +* reduced cost of xN[j], gamma[j] is the steepest edge coefficient. +* +* The working objective function is always minimized, so the sign of +* d[q] determines direction, in which xN[q] has to change: +* +* if d[q] < 0, xN[q] has to increase; +* +* if d[q] > 0, xN[q] has to decrease. +* +* If |d[j]| <= tol_dj, where tol_dj is a specified tolerance, xN[j] +* is not included in J and therefore ignored. (It is assumed that the +* working objective row is appropriately scaled, i.e. max|c[k]| = 1.) +* +* If J is empty and no variable has been chosen, q is set to 0. */ + +static void chuzc(struct csa *csa, double tol_dj) +{ int n = csa->n; + char *stat = csa->stat; + double *cbar = csa->cbar; + double *gamma = csa->gamma; + int j, q; + double dj, best, temp; + /* nothing is chosen so far */ + q = 0, best = 0.0; + /* look through the list of non-basic variables */ + for (j = 1; j <= n; j++) + { dj = cbar[j]; + switch (stat[j]) + { case GLP_NL: + /* xN[j] can increase */ + if (dj >= - tol_dj) continue; + break; + case GLP_NU: + /* xN[j] can decrease */ + if (dj <= + tol_dj) continue; + break; + case GLP_NF: + /* xN[j] can change in any direction */ + if (- tol_dj <= dj && dj <= + tol_dj) continue; + break; + case GLP_NS: + /* xN[j] cannot change at all */ + continue; + default: + xassert(stat != stat); + } + /* xN[j] is eligible non-basic variable; choose one which has + largest weighted reduced cost */ +#ifdef GLP_DEBUG + xassert(gamma[j] > 0.0); +#endif + temp = (dj * dj) / gamma[j]; + if (best < temp) + q = j, best = temp; + } + /* store the index of non-basic variable xN[q] chosen */ + csa->q = q; + return; +} + +/*********************************************************************** +* eval_tcol - compute pivot column of the simplex table +* +* This routine computes the pivot column of the simplex table, which +* corresponds to non-basic variable xN[q] chosen. +* +* The pivot column is the following vector: +* +* tcol = T * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], +* +* where B is the current basis matrix, N[q] is a column of the matrix +* (I|-A) corresponding to variable xN[q]. */ + +static void eval_tcol(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *head = csa->head; + int q = csa->q; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + double *h = csa->tcol_vec; + int i, k, nnz; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + k = head[m+q]; /* x[k] = xN[q] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* construct the right-hand side vector h = - N[q] */ + for (i = 1; i <= m; i++) + h[i] = 0.0; + if (k <= m) + { /* N[q] is k-th column of submatrix I */ + h[k] = -1.0; + } + else + { /* N[q] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + h[A_ind[ptr]] = A_val[ptr]; + } + /* solve system B * tcol = h */ + xassert(csa->valid); + bfd_ftran(csa->bfd, tcol_vec); + /* construct sparse pattern of the pivot column */ + nnz = 0; + for (i = 1; i <= m; i++) + { if (tcol_vec[i] != 0.0) + tcol_ind[++nnz] = i; + } + csa->tcol_nnz = nnz; + return; +} + +/*********************************************************************** +* refine_tcol - refine pivot column of the simplex table +* +* This routine refines the pivot column of the simplex table assuming +* that it was previously computed by the routine eval_tcol. */ + +static void refine_tcol(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *head = csa->head; + int q = csa->q; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + double *h = csa->work3; + int i, k, nnz; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + k = head[m+q]; /* x[k] = xN[q] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* construct the right-hand side vector h = - N[q] */ + for (i = 1; i <= m; i++) + h[i] = 0.0; + if (k <= m) + { /* N[q] is k-th column of submatrix I */ + h[k] = -1.0; + } + else + { /* N[q] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + h[A_ind[ptr]] = A_val[ptr]; + } + /* refine solution of B * tcol = h */ + refine_ftran(csa, h, tcol_vec); + /* construct sparse pattern of the pivot column */ + nnz = 0; + for (i = 1; i <= m; i++) + { if (tcol_vec[i] != 0.0) + tcol_ind[++nnz] = i; + } + csa->tcol_nnz = nnz; + return; +} + +/*********************************************************************** +* sort_tcol - sort pivot column of the simplex table +* +* This routine reorders the list of non-zero elements of the pivot +* column to put significant elements, whose magnitude is not less than +* a specified tolerance, in front of the list, and stores the number +* of significant elements in tcol_num. */ + +static void sort_tcol(struct csa *csa, double tol_piv) +{ +#ifdef GLP_DEBUG + int m = csa->m; +#endif + int nnz = csa->tcol_nnz; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + int i, num, pos; + double big, eps, temp; + /* compute infinity (maximum) norm of the column */ + big = 0.0; + for (pos = 1; pos <= nnz; pos++) + { +#ifdef GLP_DEBUG + i = tcol_ind[pos]; + xassert(1 <= i && i <= m); +#endif + temp = fabs(tcol_vec[tcol_ind[pos]]); + if (big < temp) big = temp; + } + csa->tcol_max = big; + /* determine absolute pivot tolerance */ + eps = tol_piv * (1.0 + 0.01 * big); + /* move significant column components to front of the list */ + for (num = 0; num < nnz; ) + { i = tcol_ind[nnz]; + if (fabs(tcol_vec[i]) < eps) + nnz--; + else + { num++; + tcol_ind[nnz] = tcol_ind[num]; + tcol_ind[num] = i; + } + } + csa->tcol_num = num; + return; +} + +/*********************************************************************** +* chuzr - choose basic variable (row of the simplex table) +* +* This routine chooses basic variable xB[p], which reaches its bound +* first on changing non-basic variable xN[q] in valid direction. +* +* The parameter rtol is a relative tolerance used to relax bounds of +* basic variables. If rtol = 0, the routine implements the standard +* ratio test. Otherwise, if rtol > 0, the routine implements Harris' +* two-pass ratio test. In the latter case rtol should be about three +* times less than a tolerance used to check primal feasibility. */ + +static void chuzr(struct csa *csa, double rtol) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + double *coef = csa->coef; + int *head = csa->head; + int phase = csa->phase; + double *bbar = csa->bbar; + double *cbar = csa->cbar; + int q = csa->q; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + int tcol_num = csa->tcol_num; + int i, i_stat, k, p, p_stat, pos; + double alfa, big, delta, s, t, teta, tmax; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + /* s := - sign(d[q]), where d[q] is reduced cost of xN[q] */ +#ifdef GLP_DEBUG + xassert(cbar[q] != 0.0); +#endif + s = (cbar[q] > 0.0 ? -1.0 : +1.0); + /*** FIRST PASS ***/ + k = head[m+q]; /* x[k] = xN[q] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (type[k] == GLP_DB) + { /* xN[q] has both lower and upper bounds */ + p = -1, p_stat = 0, teta = ub[k] - lb[k], big = 1.0; + } + else + { /* xN[q] has no opposite bound */ + p = 0, p_stat = 0, teta = DBL_MAX, big = 0.0; + } + /* walk through significant elements of the pivot column */ + for (pos = 1; pos <= tcol_num; pos++) + { i = tcol_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); +#endif + k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + alfa = s * tcol_vec[i]; +#ifdef GLP_DEBUG + xassert(alfa != 0.0); +#endif + /* xB[i] = ... + alfa * xN[q] + ..., and due to s we need to + consider the only case when xN[q] is increasing */ + if (alfa > 0.0) + { /* xB[i] is increasing */ + if (phase == 1 && coef[k] < 0.0) + { /* xB[i] violates its lower bound, which plays the role + of an upper bound on phase I */ + delta = rtol * (1.0 + kappa * fabs(lb[k])); + t = ((lb[k] + delta) - bbar[i]) / alfa; + i_stat = GLP_NL; + } + else if (phase == 1 && coef[k] > 0.0) + { /* xB[i] violates its upper bound, which plays the role + of an lower bound on phase I */ + continue; + } + else if (type[k] == GLP_UP || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* xB[i] is within its bounds and has an upper bound */ + delta = rtol * (1.0 + kappa * fabs(ub[k])); + t = ((ub[k] + delta) - bbar[i]) / alfa; + i_stat = GLP_NU; + } + else + { /* xB[i] is within its bounds and has no upper bound */ + continue; + } + } + else + { /* xB[i] is decreasing */ + if (phase == 1 && coef[k] > 0.0) + { /* xB[i] violates its upper bound, which plays the role + of an lower bound on phase I */ + delta = rtol * (1.0 + kappa * fabs(ub[k])); + t = ((ub[k] - delta) - bbar[i]) / alfa; + i_stat = GLP_NU; + } + else if (phase == 1 && coef[k] < 0.0) + { /* xB[i] violates its lower bound, which plays the role + of an upper bound on phase I */ + continue; + } + else if (type[k] == GLP_LO || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* xB[i] is within its bounds and has an lower bound */ + delta = rtol * (1.0 + kappa * fabs(lb[k])); + t = ((lb[k] - delta) - bbar[i]) / alfa; + i_stat = GLP_NL; + } + else + { /* xB[i] is within its bounds and has no lower bound */ + continue; + } + } + /* t is a change of xN[q], on which xB[i] reaches its bound + (possibly relaxed); since the basic solution is assumed to + be primal feasible (or pseudo feasible on phase I), t has + to be non-negative by definition; however, it may happen + that xB[i] slightly (i.e. within a tolerance) violates its + bound, that leads to negative t; in the latter case, if + xB[i] is chosen, negative t means that xN[q] changes in + wrong direction; if pivot alfa[i,q] is close to zero, even + small bound violation of xB[i] may lead to a large change + of xN[q] in wrong direction; let, for example, xB[i] >= 0 + and in the current basis its value be -5e-9; let also xN[q] + be on its zero bound and should increase; from the ratio + test rule it follows that the pivot alfa[i,q] < 0; however, + if alfa[i,q] is, say, -1e-9, the change of xN[q] in wrong + direction is 5e-9 / (-1e-9) = -5, and using it for updating + values of other basic variables will give absolutely wrong + results; therefore, if t is negative, we should replace it + by exact zero assuming that xB[i] is exactly on its bound, + and the violation appears due to round-off errors */ + if (t < 0.0) t = 0.0; + /* apply minimal ratio test */ + if (teta > t || teta == t && big < fabs(alfa)) + p = i, p_stat = i_stat, teta = t, big = fabs(alfa); + } + /* the second pass is skipped in the following cases: */ + /* if the standard ratio test is used */ + if (rtol == 0.0) goto done; + /* if xN[q] reaches its opposite bound or if no basic variable + has been chosen on the first pass */ + if (p <= 0) goto done; + /* if xB[p] is a blocking variable, i.e. if it prevents xN[q] + from any change */ + if (teta == 0.0) goto done; + /*** SECOND PASS ***/ + /* here tmax is a maximal change of xN[q], on which the solution + remains primal feasible (or pseudo feasible on phase I) within + a tolerance */ +#if 0 + tmax = (1.0 + 10.0 * DBL_EPSILON) * teta; +#else + tmax = teta; +#endif + /* nothing is chosen so far */ + p = 0, p_stat = 0, teta = DBL_MAX, big = 0.0; + /* walk through significant elements of the pivot column */ + for (pos = 1; pos <= tcol_num; pos++) + { i = tcol_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); +#endif + k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + alfa = s * tcol_vec[i]; +#ifdef GLP_DEBUG + xassert(alfa != 0.0); +#endif + /* xB[i] = ... + alfa * xN[q] + ..., and due to s we need to + consider the only case when xN[q] is increasing */ + if (alfa > 0.0) + { /* xB[i] is increasing */ + if (phase == 1 && coef[k] < 0.0) + { /* xB[i] violates its lower bound, which plays the role + of an upper bound on phase I */ + t = (lb[k] - bbar[i]) / alfa; + i_stat = GLP_NL; + } + else if (phase == 1 && coef[k] > 0.0) + { /* xB[i] violates its upper bound, which plays the role + of an lower bound on phase I */ + continue; + } + else if (type[k] == GLP_UP || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* xB[i] is within its bounds and has an upper bound */ + t = (ub[k] - bbar[i]) / alfa; + i_stat = GLP_NU; + } + else + { /* xB[i] is within its bounds and has no upper bound */ + continue; + } + } + else + { /* xB[i] is decreasing */ + if (phase == 1 && coef[k] > 0.0) + { /* xB[i] violates its upper bound, which plays the role + of an lower bound on phase I */ + t = (ub[k] - bbar[i]) / alfa; + i_stat = GLP_NU; + } + else if (phase == 1 && coef[k] < 0.0) + { /* xB[i] violates its lower bound, which plays the role + of an upper bound on phase I */ + continue; + } + else if (type[k] == GLP_LO || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* xB[i] is within its bounds and has an lower bound */ + t = (lb[k] - bbar[i]) / alfa; + i_stat = GLP_NL; + } + else + { /* xB[i] is within its bounds and has no lower bound */ + continue; + } + } + /* (see comments for the first pass) */ + if (t < 0.0) t = 0.0; + /* t is a change of xN[q], on which xB[i] reaches its bound; + if t <= tmax, all basic variables can violate their bounds + only within relaxation tolerance delta; we can use this + freedom and choose basic variable having largest influence + coefficient to avoid possible numeric instability */ + if (t <= tmax && big < fabs(alfa)) + p = i, p_stat = i_stat, teta = t, big = fabs(alfa); + } + /* something must be chosen on the second pass */ + xassert(p != 0); +done: /* store the index and status of basic variable xB[p] chosen */ + csa->p = p; + if (p > 0 && type[head[p]] == GLP_FX) + csa->p_stat = GLP_NS; + else + csa->p_stat = p_stat; + /* store corresponding change of non-basic variable xN[q] */ +#ifdef GLP_DEBUG + xassert(teta >= 0.0); +#endif + csa->teta = s * teta; + return; +} + +/*********************************************************************** +* eval_rho - compute pivot row of the inverse +* +* This routine computes the pivot (p-th) row of the inverse inv(B), +* which corresponds to basic variable xB[p] chosen: +* +* rho = inv(B') * e[p], +* +* where B' is a matrix transposed to the current basis matrix, e[p] +* is unity vector. */ + +static void eval_rho(struct csa *csa, double rho[]) +{ int m = csa->m; + int p = csa->p; + double *e = rho; + int i; +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); +#endif + /* construct the right-hand side vector e[p] */ + for (i = 1; i <= m; i++) + e[i] = 0.0; + e[p] = 1.0; + /* solve system B'* rho = e[p] */ + xassert(csa->valid); + bfd_btran(csa->bfd, rho); + return; +} + +/*********************************************************************** +* refine_rho - refine pivot row of the inverse +* +* This routine refines the pivot row of the inverse inv(B) assuming +* that it was previously computed by the routine eval_rho. */ + +static void refine_rho(struct csa *csa, double rho[]) +{ int m = csa->m; + int p = csa->p; + double *e = csa->work3; + int i; +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); +#endif + /* construct the right-hand side vector e[p] */ + for (i = 1; i <= m; i++) + e[i] = 0.0; + e[p] = 1.0; + /* refine solution of B'* rho = e[p] */ + refine_btran(csa, e, rho); + return; +} + +/*********************************************************************** +* eval_trow - compute pivot row of the simplex table +* +* This routine computes the pivot row of the simplex table, which +* corresponds to basic variable xB[p] chosen. +* +* The pivot row is the following vector: +* +* trow = T'* e[p] = - N'* inv(B') * e[p] = - N' * rho, +* +* where rho is the pivot row of the inverse inv(B) previously computed +* by the routine eval_rho. +* +* Note that elements of the pivot row corresponding to fixed non-basic +* variables are not computed. */ + +static void eval_trow(struct csa *csa, double rho[]) +{ int m = csa->m; + int n = csa->n; +#ifdef GLP_DEBUG + char *stat = csa->stat; +#endif + int *N_ptr = csa->N_ptr; + int *N_len = csa->N_len; + int *N_ind = csa->N_ind; + double *N_val = csa->N_val; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int i, j, beg, end, ptr, nnz; + double temp; + /* clear the pivot row */ + for (j = 1; j <= n; j++) + trow_vec[j] = 0.0; + /* compute the pivot row as a linear combination of rows of the + matrix N: trow = - rho[1] * N'[1] - ... - rho[m] * N'[m] */ + for (i = 1; i <= m; i++) + { temp = rho[i]; + if (temp == 0.0) continue; + /* trow := trow - rho[i] * N'[i] */ + beg = N_ptr[i]; + end = beg + N_len[i]; + for (ptr = beg; ptr < end; ptr++) + { +#ifdef GLP_DEBUG + j = N_ind[ptr]; + xassert(1 <= j && j <= n); + xassert(stat[j] != GLP_NS); +#endif + trow_vec[N_ind[ptr]] -= temp * N_val[ptr]; + } + } + /* construct sparse pattern of the pivot row */ + nnz = 0; + for (j = 1; j <= n; j++) + { if (trow_vec[j] != 0.0) + trow_ind[++nnz] = j; + } + csa->trow_nnz = nnz; + return; +} + +/*********************************************************************** +* update_bbar - update values of basic variables +* +* This routine updates values of all basic variables for the adjacent +* basis. */ + +static void update_bbar(struct csa *csa) +{ +#ifdef GLP_DEBUG + int m = csa->m; + int n = csa->n; +#endif + double *bbar = csa->bbar; + int q = csa->q; + int tcol_nnz = csa->tcol_nnz; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + int p = csa->p; + double teta = csa->teta; + int i, pos; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); + xassert(p < 0 || 1 <= p && p <= m); +#endif + /* if xN[q] leaves the basis, compute its value in the adjacent + basis, where it will replace xB[p] */ + if (p > 0) + bbar[p] = get_xN(csa, q) + teta; + /* update values of other basic variables (except xB[p], because + it will be replaced by xN[q]) */ + if (teta == 0.0) goto done; + for (pos = 1; pos <= tcol_nnz; pos++) + { i = tcol_ind[pos]; + /* skip xB[p] */ + if (i == p) continue; + /* (change of xB[i]) = alfa[i,q] * (change of xN[q]) */ + bbar[i] += tcol_vec[i] * teta; + } +done: return; +} + +/*********************************************************************** +* reeval_cost - recompute reduced cost of non-basic variable xN[q] +* +* This routine recomputes reduced cost of non-basic variable xN[q] for +* the current basis more accurately using its direct definition: +* +* d[q] = cN[q] - N'[q] * pi = +* +* = cN[q] - N'[q] * (inv(B') * cB) = +* +* = cN[q] - (cB' * inv(B) * N[q]) = +* +* = cN[q] + cB' * (pivot column). +* +* It is assumed that the pivot column of the simplex table is already +* computed. */ + +static double reeval_cost(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + double *coef = csa->coef; + int *head = csa->head; + int q = csa->q; + int tcol_nnz = csa->tcol_nnz; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + int i, pos; + double dq; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + dq = coef[head[m+q]]; + for (pos = 1; pos <= tcol_nnz; pos++) + { i = tcol_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); +#endif + dq += coef[head[i]] * tcol_vec[i]; + } + return dq; +} + +/*********************************************************************** +* update_cbar - update reduced costs of non-basic variables +* +* This routine updates reduced costs of all (except fixed) non-basic +* variables for the adjacent basis. */ + +static void update_cbar(struct csa *csa) +{ +#ifdef GLP_DEBUG + int n = csa->n; +#endif + double *cbar = csa->cbar; + int q = csa->q; + int trow_nnz = csa->trow_nnz; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int j, pos; + double new_dq; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + /* compute reduced cost of xB[p] in the adjacent basis, where it + will replace xN[q] */ +#ifdef GLP_DEBUG + xassert(trow_vec[q] != 0.0); +#endif + new_dq = (cbar[q] /= trow_vec[q]); + /* update reduced costs of other non-basic variables (except + xN[q], because it will be replaced by xB[p]) */ + for (pos = 1; pos <= trow_nnz; pos++) + { j = trow_ind[pos]; + /* skip xN[q] */ + if (j == q) continue; + cbar[j] -= trow_vec[j] * new_dq; + } + return; +} + +/*********************************************************************** +* update_gamma - update steepest edge coefficients +* +* This routine updates steepest-edge coefficients for the adjacent +* basis. */ + +static void update_gamma(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + char *type = csa->type; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + char *refsp = csa->refsp; + double *gamma = csa->gamma; + int q = csa->q; + int tcol_nnz = csa->tcol_nnz; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + int p = csa->p; + int trow_nnz = csa->trow_nnz; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + double *u = csa->work3; + int i, j, k, pos, beg, end, ptr; + double gamma_q, delta_q, pivot, s, t, t1, t2; +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); + xassert(1 <= q && q <= n); +#endif + /* the basis changes, so decrease the count */ + xassert(csa->refct > 0); + csa->refct--; + /* recompute gamma[q] for the current basis more accurately and + compute auxiliary vector u */ + gamma_q = delta_q = (refsp[head[m+q]] ? 1.0 : 0.0); + for (i = 1; i <= m; i++) u[i] = 0.0; + for (pos = 1; pos <= tcol_nnz; pos++) + { i = tcol_ind[pos]; + if (refsp[head[i]]) + { u[i] = t = tcol_vec[i]; + gamma_q += t * t; + } + else + u[i] = 0.0; + } + xassert(csa->valid); + bfd_btran(csa->bfd, u); + /* update gamma[k] for other non-basic variables (except fixed + variables and xN[q], because it will be replaced by xB[p]) */ + pivot = trow_vec[q]; +#ifdef GLP_DEBUG + xassert(pivot != 0.0); +#endif + for (pos = 1; pos <= trow_nnz; pos++) + { j = trow_ind[pos]; + /* skip xN[q] */ + if (j == q) continue; + /* compute t */ + t = trow_vec[j] / pivot; + /* compute inner product s = N'[j] * u */ + k = head[m+j]; /* x[k] = xN[j] */ + if (k <= m) + s = u[k]; + else + { s = 0.0; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + s -= A_val[ptr] * u[A_ind[ptr]]; + } + /* compute gamma[k] for the adjacent basis */ + t1 = gamma[j] + t * t * gamma_q + 2.0 * t * s; + t2 = (refsp[k] ? 1.0 : 0.0) + delta_q * t * t; + gamma[j] = (t1 >= t2 ? t1 : t2); + if (gamma[j] < DBL_EPSILON) gamma[j] = DBL_EPSILON; + } + /* compute gamma[q] for the adjacent basis */ + if (type[head[p]] == GLP_FX) + gamma[q] = 1.0; + else + { gamma[q] = gamma_q / (pivot * pivot); + if (gamma[q] < DBL_EPSILON) gamma[q] = DBL_EPSILON; + } + return; +} + +/*********************************************************************** +* err_in_bbar - compute maximal relative error in primal solution +* +* This routine returns maximal relative error: +* +* max |beta[i] - bbar[i]| / (1 + |beta[i]|), +* +* where beta and bbar are, respectively, directly computed and the +* current (updated) values of basic variables. +* +* NOTE: The routine is intended only for debugginig purposes. */ + +static double err_in_bbar(struct csa *csa) +{ int m = csa->m; + double *bbar = csa->bbar; + int i; + double e, emax, *beta; + beta = xcalloc(1+m, sizeof(double)); + eval_beta(csa, beta); + emax = 0.0; + for (i = 1; i <= m; i++) + { e = fabs(beta[i] - bbar[i]) / (1.0 + fabs(beta[i])); + if (emax < e) emax = e; + } + xfree(beta); + return emax; +} + +/*********************************************************************** +* err_in_cbar - compute maximal relative error in dual solution +* +* This routine returns maximal relative error: +* +* max |cost[j] - cbar[j]| / (1 + |cost[j]|), +* +* where cost and cbar are, respectively, directly computed and the +* current (updated) reduced costs of non-basic non-fixed variables. +* +* NOTE: The routine is intended only for debugginig purposes. */ + +static double err_in_cbar(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + char *stat = csa->stat; + double *cbar = csa->cbar; + int j; + double e, emax, cost, *pi; + pi = xcalloc(1+m, sizeof(double)); + eval_pi(csa, pi); + emax = 0.0; + for (j = 1; j <= n; j++) + { if (stat[j] == GLP_NS) continue; + cost = eval_cost(csa, pi, j); + e = fabs(cost - cbar[j]) / (1.0 + fabs(cost)); + if (emax < e) emax = e; + } + xfree(pi); + return emax; +} + +/*********************************************************************** +* err_in_gamma - compute maximal relative error in steepest edge cff. +* +* This routine returns maximal relative error: +* +* max |gamma'[j] - gamma[j]| / (1 + |gamma'[j]), +* +* where gamma'[j] and gamma[j] are, respectively, directly computed +* and the current (updated) steepest edge coefficients for non-basic +* non-fixed variable x[j]. +* +* NOTE: The routine is intended only for debugginig purposes. */ + +static double err_in_gamma(struct csa *csa) +{ int n = csa->n; + char *stat = csa->stat; + double *gamma = csa->gamma; + int j; + double e, emax, temp; + emax = 0.0; + for (j = 1; j <= n; j++) + { if (stat[j] == GLP_NS) + { xassert(gamma[j] == 1.0); + continue; + } + temp = eval_gamma(csa, j); + e = fabs(temp - gamma[j]) / (1.0 + fabs(temp)); + if (emax < e) emax = e; + } + return emax; +} + +/*********************************************************************** +* change_basis - change basis header +* +* This routine changes the basis header to make it corresponding to +* the adjacent basis. */ + +static void change_basis(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; + char *type = csa->type; +#endif + int *head = csa->head; + char *stat = csa->stat; + int q = csa->q; + int p = csa->p; + int p_stat = csa->p_stat; + int k; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + if (p < 0) + { /* xN[q] goes to its opposite bound */ +#ifdef GLP_DEBUG + k = head[m+q]; /* x[k] = xN[q] */ + xassert(1 <= k && k <= m+n); + xassert(type[k] == GLP_DB); +#endif + switch (stat[q]) + { case GLP_NL: + /* xN[q] increases */ + stat[q] = GLP_NU; + break; + case GLP_NU: + /* xN[q] decreases */ + stat[q] = GLP_NL; + break; + default: + xassert(stat != stat); + } + } + else + { /* xB[p] leaves the basis, xN[q] enters the basis */ +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); + k = head[p]; /* x[k] = xB[p] */ + switch (p_stat) + { case GLP_NL: + /* xB[p] goes to its lower bound */ + xassert(type[k] == GLP_LO || type[k] == GLP_DB); + break; + case GLP_NU: + /* xB[p] goes to its upper bound */ + xassert(type[k] == GLP_UP || type[k] == GLP_DB); + break; + case GLP_NS: + /* xB[p] goes to its fixed value */ + xassert(type[k] == GLP_NS); + break; + default: + xassert(p_stat != p_stat); + } +#endif + /* xB[p] <-> xN[q] */ + k = head[p], head[p] = head[m+q], head[m+q] = k; + stat[q] = (char)p_stat; + } + return; +} + +/*********************************************************************** +* set_aux_obj - construct auxiliary objective function +* +* The auxiliary objective function is a separable piecewise linear +* convex function, which is the sum of primal infeasibilities: +* +* z = t[1] + ... + t[m+n] -> minimize, +* +* where: +* +* / lb[k] - x[k], if x[k] < lb[k] +* | +* t[k] = < 0, if lb[k] <= x[k] <= ub[k] +* | +* \ x[k] - ub[k], if x[k] > ub[k] +* +* This routine computes objective coefficients for the current basis +* and returns the number of non-zero terms t[k]. */ + +static int set_aux_obj(struct csa *csa, double tol_bnd) +{ int m = csa->m; + int n = csa->n; + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + double *coef = csa->coef; + int *head = csa->head; + double *bbar = csa->bbar; + int i, k, cnt = 0; + double eps; + /* use a bit more restrictive tolerance */ + tol_bnd *= 0.90; + /* clear all objective coefficients */ + for (k = 1; k <= m+n; k++) + coef[k] = 0.0; + /* walk through the list of basic variables */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ + if (type[k] == GLP_LO || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* x[k] has lower bound */ + eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); + if (bbar[i] < lb[k] - eps) + { /* and violates it */ + coef[k] = -1.0; + cnt++; + } + } + if (type[k] == GLP_UP || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* x[k] has upper bound */ + eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); + if (bbar[i] > ub[k] + eps) + { /* and violates it */ + coef[k] = +1.0; + cnt++; + } + } + } + return cnt; +} + +/*********************************************************************** +* set_orig_obj - restore original objective function +* +* This routine assigns scaled original objective coefficients to the +* working objective function. */ + +static void set_orig_obj(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + double *coef = csa->coef; + double *obj = csa->obj; + double zeta = csa->zeta; + int i, j; + for (i = 1; i <= m; i++) + coef[i] = 0.0; + for (j = 1; j <= n; j++) + coef[m+j] = zeta * obj[j]; + return; +} + +/*********************************************************************** +* check_stab - check numerical stability of basic solution +* +* If the current basic solution is primal feasible (or pseudo feasible +* on phase I) within a tolerance, this routine returns zero, otherwise +* it returns non-zero. */ + +static int check_stab(struct csa *csa, double tol_bnd) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + double *coef = csa->coef; + int *head = csa->head; + int phase = csa->phase; + double *bbar = csa->bbar; + int i, k; + double eps; + /* walk through the list of basic variables */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (phase == 1 && coef[k] < 0.0) + { /* x[k] must not be greater than its lower bound */ +#ifdef GLP_DEBUG + xassert(type[k] == GLP_LO || type[k] == GLP_DB || + type[k] == GLP_FX); +#endif + eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); + if (bbar[i] > lb[k] + eps) return 1; + } + else if (phase == 1 && coef[k] > 0.0) + { /* x[k] must not be less than its upper bound */ +#ifdef GLP_DEBUG + xassert(type[k] == GLP_UP || type[k] == GLP_DB || + type[k] == GLP_FX); +#endif + eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); + if (bbar[i] < ub[k] - eps) return 1; + } + else + { /* either phase = 1 and coef[k] = 0, or phase = 2 */ + if (type[k] == GLP_LO || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* x[k] must not be less than its lower bound */ + eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); + if (bbar[i] < lb[k] - eps) return 1; + } + if (type[k] == GLP_UP || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* x[k] must not be greater then its upper bound */ + eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); + if (bbar[i] > ub[k] + eps) return 1; + } + } + } + /* basic solution is primal feasible within a tolerance */ + return 0; +} + +/*********************************************************************** +* check_feas - check primal feasibility of basic solution +* +* If the current basic solution is primal feasible within a tolerance, +* this routine returns zero, otherwise it returns non-zero. */ + +static int check_feas(struct csa *csa, double tol_bnd) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; + char *type = csa->type; +#endif + double *lb = csa->lb; + double *ub = csa->ub; + double *coef = csa->coef; + int *head = csa->head; + double *bbar = csa->bbar; + int i, k; + double eps; + xassert(csa->phase == 1); + /* walk through the list of basic variables */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (coef[k] < 0.0) + { /* check if x[k] still violates its lower bound */ +#ifdef GLP_DEBUG + xassert(type[k] == GLP_LO || type[k] == GLP_DB || + type[k] == GLP_FX); +#endif + eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); + if (bbar[i] < lb[k] - eps) return 1; + } + else if (coef[k] > 0.0) + { /* check if x[k] still violates its upper bound */ +#ifdef GLP_DEBUG + xassert(type[k] == GLP_UP || type[k] == GLP_DB || + type[k] == GLP_FX); +#endif + eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); + if (bbar[i] > ub[k] + eps) return 1; + } + } + /* basic solution is primal feasible within a tolerance */ + return 0; +} + +/*********************************************************************** +* eval_obj - compute original objective function +* +* This routine computes the current value of the original objective +* function. */ + +static double eval_obj(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + double *obj = csa->obj; + int *head = csa->head; + double *bbar = csa->bbar; + int i, j, k; + double sum; + sum = obj[0]; + /* walk through the list of basic variables */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k > m) + sum += obj[k-m] * bbar[i]; + } + /* walk through the list of non-basic variables */ + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k > m) + sum += obj[k-m] * get_xN(csa, j); + } + return sum; +} + +/*********************************************************************** +* display - display the search progress +* +* This routine displays some information about the search progress +* that includes: +* +* the search phase; +* +* the number of simplex iterations performed by the solver; +* +* the original objective value; +* +* the sum of (scaled) primal infeasibilities; +* +* the number of basic fixed variables. */ + +static void display(struct csa *csa, const glp_smcp *parm, int spec) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + int phase = csa->phase; + int *head = csa->head; + double *bbar = csa->bbar; + int i, k, cnt; + double sum; + if (parm->msg_lev < GLP_MSG_ON) goto skip; + if (parm->out_dly > 0 && + 1000.0 * xdifftime(xtime(), csa->tm_beg) < parm->out_dly) + goto skip; + if (csa->it_cnt == csa->it_dpy) goto skip; + if (!spec && csa->it_cnt % parm->out_frq != 0) goto skip; + /* compute the sum of primal infeasibilities and determine the + number of basic fixed variables */ + sum = 0.0, cnt = 0; + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (type[k] == GLP_LO || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* x[k] has lower bound */ + if (bbar[i] < lb[k]) + sum += (lb[k] - bbar[i]); + } + if (type[k] == GLP_UP || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* x[k] has upper bound */ + if (bbar[i] > ub[k]) + sum += (bbar[i] - ub[k]); + } + if (type[k] == GLP_FX) cnt++; + } + xprintf("%c%6d: obj = %17.9e infeas = %10.3e (%d)\n", + phase == 1 ? ' ' : '*', csa->it_cnt, eval_obj(csa), sum, cnt); + csa->it_dpy = csa->it_cnt; +skip: return; +} + +/*********************************************************************** +* store_sol - store basic solution back to the problem object +* +* This routine stores basic solution components back to the problem +* object. */ + +static void store_sol(struct csa *csa, glp_prob *lp, int p_stat, + int d_stat, int ray) +{ int m = csa->m; + int n = csa->n; + double zeta = csa->zeta; + int *head = csa->head; + char *stat = csa->stat; + double *bbar = csa->bbar; + double *cbar = csa->cbar; + int i, j, k; +#ifdef GLP_DEBUG + xassert(lp->m == m); + xassert(lp->n == n); +#endif + /* basis factorization */ +#ifdef GLP_DEBUG + xassert(!lp->valid && lp->bfd == NULL); + xassert(csa->valid && csa->bfd != NULL); +#endif + lp->valid = 1, csa->valid = 0; + lp->bfd = csa->bfd, csa->bfd = NULL; + memcpy(&lp->head[1], &head[1], m * sizeof(int)); + /* basic solution status */ + lp->pbs_stat = p_stat; + lp->dbs_stat = d_stat; + /* objective function value */ + lp->obj_val = eval_obj(csa); + /* simplex iteration count */ + lp->it_cnt = csa->it_cnt; + /* unbounded ray */ + lp->some = ray; + /* basic variables */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { GLPROW *row = lp->row[k]; + row->stat = GLP_BS; + row->bind = i; + row->prim = bbar[i] / row->rii; + row->dual = 0.0; + } + else + { GLPCOL *col = lp->col[k-m]; + col->stat = GLP_BS; + col->bind = i; + col->prim = bbar[i] * col->sjj; + col->dual = 0.0; + } + } + /* non-basic variables */ + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { GLPROW *row = lp->row[k]; + row->stat = stat[j]; + row->bind = 0; +#if 0 + row->prim = get_xN(csa, j) / row->rii; +#else + switch (stat[j]) + { case GLP_NL: + row->prim = row->lb; break; + case GLP_NU: + row->prim = row->ub; break; + case GLP_NF: + row->prim = 0.0; break; + case GLP_NS: + row->prim = row->lb; break; + default: + xassert(stat != stat); + } +#endif + row->dual = (cbar[j] * row->rii) / zeta; + } + else + { GLPCOL *col = lp->col[k-m]; + col->stat = stat[j]; + col->bind = 0; +#if 0 + col->prim = get_xN(csa, j) * col->sjj; +#else + switch (stat[j]) + { case GLP_NL: + col->prim = col->lb; break; + case GLP_NU: + col->prim = col->ub; break; + case GLP_NF: + col->prim = 0.0; break; + case GLP_NS: + col->prim = col->lb; break; + default: + xassert(stat != stat); + } +#endif + col->dual = (cbar[j] / col->sjj) / zeta; + } + } + return; +} + +/*********************************************************************** +* free_csa - deallocate common storage area +* +* This routine frees all the memory allocated to arrays in the common +* storage area (CSA). */ + +static void free_csa(struct csa *csa) +{ xfree(csa->type); + xfree(csa->lb); + xfree(csa->ub); + xfree(csa->coef); + xfree(csa->obj); + xfree(csa->A_ptr); + xfree(csa->A_ind); + xfree(csa->A_val); + xfree(csa->head); + xfree(csa->stat); + xfree(csa->N_ptr); + xfree(csa->N_len); + xfree(csa->N_ind); + xfree(csa->N_val); + xfree(csa->bbar); + xfree(csa->cbar); + xfree(csa->refsp); + xfree(csa->gamma); + xfree(csa->tcol_ind); + xfree(csa->tcol_vec); + xfree(csa->trow_ind); + xfree(csa->trow_vec); + xfree(csa->work1); + xfree(csa->work2); + xfree(csa->work3); + xfree(csa->work4); + xfree(csa); + return; +} + +/*********************************************************************** +* spx_primal - core LP solver based on the primal simplex method +* +* SYNOPSIS +* +* #include "glpspx.h" +* int spx_primal(glp_prob *lp, const glp_smcp *parm); +* +* DESCRIPTION +* +* The routine spx_primal is a core LP solver based on the two-phase +* primal simplex method. +* +* RETURNS +* +* 0 LP instance has been successfully solved. +* +* GLP_EITLIM +* Iteration limit has been exhausted. +* +* GLP_ETMLIM +* Time limit has been exhausted. +* +* GLP_EFAIL +* The solver failed to solve LP instance. */ + +int spx_primal(glp_prob *lp, const glp_smcp *parm) +{ struct csa *csa; + int binv_st = 2; + /* status of basis matrix factorization: + 0 - invalid; 1 - just computed; 2 - updated */ + int bbar_st = 0; + /* status of primal values of basic variables: + 0 - invalid; 1 - just computed; 2 - updated */ + int cbar_st = 0; + /* status of reduced costs of non-basic variables: + 0 - invalid; 1 - just computed; 2 - updated */ + int rigorous = 0; + /* rigorous mode flag; this flag is used to enable iterative + refinement on computing pivot rows and columns of the simplex + table */ + int check = 0; + int p_stat, d_stat, ret; + /* allocate and initialize the common storage area */ + csa = alloc_csa(lp); + init_csa(csa, lp); + if (parm->msg_lev >= GLP_MSG_DBG) + xprintf("Objective scale factor = %g\n", csa->zeta); +loop: /* main loop starts here */ + /* compute factorization of the basis matrix */ + if (binv_st == 0) + { ret = invert_B(csa); + if (ret != 0) + { if (parm->msg_lev >= GLP_MSG_ERR) + { xprintf("Error: unable to factorize the basis matrix (%d" + ")\n", ret); + xprintf("Sorry, basis recovery procedure not implemented" + " yet\n"); + } + xassert(!lp->valid && lp->bfd == NULL); + lp->bfd = csa->bfd, csa->bfd = NULL; + lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; + lp->obj_val = 0.0; + lp->it_cnt = csa->it_cnt; + lp->some = 0; + ret = GLP_EFAIL; + goto done; + } + csa->valid = 1; + binv_st = 1; /* just computed */ + /* invalidate basic solution components */ + bbar_st = cbar_st = 0; + } + /* compute primal values of basic variables */ + if (bbar_st == 0) + { eval_bbar(csa); + bbar_st = 1; /* just computed */ + /* determine the search phase, if not determined yet */ + if (csa->phase == 0) + { if (set_aux_obj(csa, parm->tol_bnd) > 0) + { /* current basic solution is primal infeasible */ + /* start to minimize the sum of infeasibilities */ + csa->phase = 1; + } + else + { /* current basic solution is primal feasible */ + /* start to minimize the original objective function */ + set_orig_obj(csa); + csa->phase = 2; + } + xassert(check_stab(csa, parm->tol_bnd) == 0); + /* working objective coefficients have been changed, so + invalidate reduced costs */ + cbar_st = 0; + display(csa, parm, 1); + } + /* make sure that the current basic solution remains primal + feasible (or pseudo feasible on phase I) */ + if (check_stab(csa, parm->tol_bnd)) + { /* there are excessive bound violations due to round-off + errors */ + if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: numerical instability (primal simplex," + " phase %s)\n", csa->phase == 1 ? "I" : "II"); + /* restart the search */ + csa->phase = 0; + binv_st = 0; + rigorous = 5; + goto loop; + } + } + xassert(csa->phase == 1 || csa->phase == 2); + /* on phase I we do not need to wait until the current basic + solution becomes dual feasible; it is sufficient to make sure + that no basic variable violates its bounds */ + if (csa->phase == 1 && !check_feas(csa, parm->tol_bnd)) + { /* the current basis is primal feasible; switch to phase II */ + csa->phase = 2; + set_orig_obj(csa); + cbar_st = 0; + display(csa, parm, 1); + } + /* compute reduced costs of non-basic variables */ + if (cbar_st == 0) + { eval_cbar(csa); + cbar_st = 1; /* just computed */ + } + /* redefine the reference space, if required */ + switch (parm->pricing) + { case GLP_PT_STD: + break; + case GLP_PT_PSE: + if (csa->refct == 0) reset_refsp(csa); + break; + default: + xassert(parm != parm); + } + /* at this point the basis factorization and all basic solution + components are valid */ + xassert(binv_st && bbar_st && cbar_st); + /* check accuracy of current basic solution components (only for + debugging) */ + if (check) + { double e_bbar = err_in_bbar(csa); + double e_cbar = err_in_cbar(csa); + double e_gamma = + (parm->pricing == GLP_PT_PSE ? err_in_gamma(csa) : 0.0); + xprintf("e_bbar = %10.3e; e_cbar = %10.3e; e_gamma = %10.3e\n", + e_bbar, e_cbar, e_gamma); + xassert(e_bbar <= 1e-5 && e_cbar <= 1e-5 && e_gamma <= 1e-3); + } + /* check if the iteration limit has been exhausted */ + if (parm->it_lim < INT_MAX && + csa->it_cnt - csa->it_beg >= parm->it_lim) + { if (bbar_st != 1 || csa->phase == 2 && cbar_st != 1) + { if (bbar_st != 1) bbar_st = 0; + if (csa->phase == 2 && cbar_st != 1) cbar_st = 0; + goto loop; + } + display(csa, parm, 1); + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED\n"); + switch (csa->phase) + { case 1: + p_stat = GLP_INFEAS; + set_orig_obj(csa); + eval_cbar(csa); + break; + case 2: + p_stat = GLP_FEAS; + break; + default: + xassert(csa != csa); + } + chuzc(csa, parm->tol_dj); + d_stat = (csa->q == 0 ? GLP_FEAS : GLP_INFEAS); + store_sol(csa, lp, p_stat, d_stat, 0); + ret = GLP_EITLIM; + goto done; + } + /* check if the time limit has been exhausted */ + if (parm->tm_lim < INT_MAX && + 1000.0 * xdifftime(xtime(), csa->tm_beg) >= parm->tm_lim) + { if (bbar_st != 1 || csa->phase == 2 && cbar_st != 1) + { if (bbar_st != 1) bbar_st = 0; + if (csa->phase == 2 && cbar_st != 1) cbar_st = 0; + goto loop; + } + display(csa, parm, 1); + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); + switch (csa->phase) + { case 1: + p_stat = GLP_INFEAS; + set_orig_obj(csa); + eval_cbar(csa); + break; + case 2: + p_stat = GLP_FEAS; + break; + default: + xassert(csa != csa); + } + chuzc(csa, parm->tol_dj); + d_stat = (csa->q == 0 ? GLP_FEAS : GLP_INFEAS); + store_sol(csa, lp, p_stat, d_stat, 0); + ret = GLP_ETMLIM; + goto done; + } + /* display the search progress */ + display(csa, parm, 0); + /* choose non-basic variable xN[q] */ + chuzc(csa, parm->tol_dj); + if (csa->q == 0) + { if (bbar_st != 1 || cbar_st != 1) + { if (bbar_st != 1) bbar_st = 0; + if (cbar_st != 1) cbar_st = 0; + goto loop; + } + display(csa, parm, 1); + switch (csa->phase) + { case 1: + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n"); + p_stat = GLP_NOFEAS; + set_orig_obj(csa); + eval_cbar(csa); + chuzc(csa, parm->tol_dj); + d_stat = (csa->q == 0 ? GLP_FEAS : GLP_INFEAS); + break; + case 2: + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("OPTIMAL SOLUTION FOUND\n"); + p_stat = d_stat = GLP_FEAS; + break; + default: + xassert(csa != csa); + } + store_sol(csa, lp, p_stat, d_stat, 0); + ret = 0; + goto done; + } + /* compute pivot column of the simplex table */ + eval_tcol(csa); + if (rigorous) refine_tcol(csa); + sort_tcol(csa, parm->tol_piv); + /* check accuracy of the reduced cost of xN[q] */ + { double d1 = csa->cbar[csa->q]; /* less accurate */ + double d2 = reeval_cost(csa); /* more accurate */ + xassert(d1 != 0.0); + if (fabs(d1 - d2) > 1e-5 * (1.0 + fabs(d2)) || + !(d1 < 0.0 && d2 < 0.0 || d1 > 0.0 && d2 > 0.0)) + { if (parm->msg_lev >= GLP_MSG_DBG) + xprintf("d1 = %.12g; d2 = %.12g\n", d1, d2); + if (cbar_st != 1 || !rigorous) + { if (cbar_st != 1) cbar_st = 0; + rigorous = 5; + goto loop; + } + } + /* replace cbar[q] by more accurate value keeping its sign */ + if (d1 > 0.0) + csa->cbar[csa->q] = (d2 > 0.0 ? d2 : +DBL_EPSILON); + else + csa->cbar[csa->q] = (d2 < 0.0 ? d2 : -DBL_EPSILON); + } + /* choose basic variable xB[p] */ + switch (parm->r_test) + { case GLP_RT_STD: + chuzr(csa, 0.0); + break; + case GLP_RT_HAR: + chuzr(csa, 0.30 * parm->tol_bnd); + break; + default: + xassert(parm != parm); + } + if (csa->p == 0) + { if (bbar_st != 1 || cbar_st != 1 || !rigorous) + { if (bbar_st != 1) bbar_st = 0; + if (cbar_st != 1) cbar_st = 0; + rigorous = 1; + goto loop; + } + display(csa, parm, 1); + switch (csa->phase) + { case 1: + if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("Error: unable to choose basic variable on ph" + "ase I\n"); + xassert(!lp->valid && lp->bfd == NULL); + lp->bfd = csa->bfd, csa->bfd = NULL; + lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; + lp->obj_val = 0.0; + lp->it_cnt = csa->it_cnt; + lp->some = 0; + ret = GLP_EFAIL; + break; + case 2: + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS UNBOUNDED SOLUTION\n"); + store_sol(csa, lp, GLP_FEAS, GLP_NOFEAS, + csa->head[csa->m+csa->q]); + ret = 0; + break; + default: + xassert(csa != csa); + } + goto done; + } + /* check if the pivot element is acceptable */ + if (csa->p > 0) + { double piv = csa->tcol_vec[csa->p]; + double eps = 1e-5 * (1.0 + 0.01 * csa->tcol_max); + if (fabs(piv) < eps) + { if (parm->msg_lev >= GLP_MSG_DBG) + xprintf("piv = %.12g; eps = %g\n", piv, eps); + if (!rigorous) + { rigorous = 5; + goto loop; + } + } + } + /* now xN[q] and xB[p] have been chosen anyhow */ + /* compute pivot row of the simplex table */ + if (csa->p > 0) + { double *rho = csa->work4; + eval_rho(csa, rho); + if (rigorous) refine_rho(csa, rho); + eval_trow(csa, rho); + } + /* accuracy check based on the pivot element */ + if (csa->p > 0) + { double piv1 = csa->tcol_vec[csa->p]; /* more accurate */ + double piv2 = csa->trow_vec[csa->q]; /* less accurate */ + xassert(piv1 != 0.0); + if (fabs(piv1 - piv2) > 1e-8 * (1.0 + fabs(piv1)) || + !(piv1 > 0.0 && piv2 > 0.0 || piv1 < 0.0 && piv2 < 0.0)) + { if (parm->msg_lev >= GLP_MSG_DBG) + xprintf("piv1 = %.12g; piv2 = %.12g\n", piv1, piv2); + if (binv_st != 1 || !rigorous) + { if (binv_st != 1) binv_st = 0; + rigorous = 5; + goto loop; + } + /* use more accurate version in the pivot row */ + if (csa->trow_vec[csa->q] == 0.0) + { csa->trow_nnz++; + xassert(csa->trow_nnz <= csa->n); + csa->trow_ind[csa->trow_nnz] = csa->q; + } + csa->trow_vec[csa->q] = piv1; + } + } + /* update primal values of basic variables */ + update_bbar(csa); + bbar_st = 2; /* updated */ + /* update reduced costs of non-basic variables */ + if (csa->p > 0) + { update_cbar(csa); + cbar_st = 2; /* updated */ + /* on phase I objective coefficient of xB[p] in the adjacent + basis becomes zero */ + if (csa->phase == 1) + { int k = csa->head[csa->p]; /* x[k] = xB[p] -> xN[q] */ + csa->cbar[csa->q] -= csa->coef[k]; + csa->coef[k] = 0.0; + } + } + /* update steepest edge coefficients */ + if (csa->p > 0) + { switch (parm->pricing) + { case GLP_PT_STD: + break; + case GLP_PT_PSE: + if (csa->refct > 0) update_gamma(csa); + break; + default: + xassert(parm != parm); + } + } + /* update factorization of the basis matrix */ + if (csa->p > 0) + { ret = update_B(csa, csa->p, csa->head[csa->m+csa->q]); + if (ret == 0) + binv_st = 2; /* updated */ + else + { csa->valid = 0; + binv_st = 0; /* invalid */ + } + } + /* update matrix N */ + if (csa->p > 0) + { del_N_col(csa, csa->q, csa->head[csa->m+csa->q]); + if (csa->type[csa->head[csa->p]] != GLP_FX) + add_N_col(csa, csa->q, csa->head[csa->p]); + } + /* change the basis header */ + change_basis(csa); + /* iteration complete */ + csa->it_cnt++; + if (rigorous > 0) rigorous--; + goto loop; +done: /* deallocate the common storage area */ + free_csa(csa); + /* return to the calling program */ + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpspx02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpspx02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,3077 @@ +/* glpspx02.c (dual simplex method) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpspx.h" + +#define GLP_DEBUG 1 + +#if 0 +#define GLP_LONG_STEP 1 +#endif + +struct csa +{ /* common storage area */ + /*--------------------------------------------------------------*/ + /* LP data */ + int m; + /* number of rows (auxiliary variables), m > 0 */ + int n; + /* number of columns (structural variables), n > 0 */ + char *type; /* char type[1+m+n]; */ + /* type[0] is not used; + type[k], 1 <= k <= m+n, is the type of variable x[k]: + GLP_FR - free variable + GLP_LO - variable with lower bound + GLP_UP - variable with upper bound + GLP_DB - double-bounded variable + GLP_FX - fixed variable */ + double *lb; /* double lb[1+m+n]; */ + /* lb[0] is not used; + lb[k], 1 <= k <= m+n, is an lower bound of variable x[k]; + if x[k] has no lower bound, lb[k] is zero */ + double *ub; /* double ub[1+m+n]; */ + /* ub[0] is not used; + ub[k], 1 <= k <= m+n, is an upper bound of variable x[k]; + if x[k] has no upper bound, ub[k] is zero; + if x[k] is of fixed type, ub[k] is the same as lb[k] */ + double *coef; /* double coef[1+m+n]; */ + /* coef[0] is not used; + coef[k], 1 <= k <= m+n, is an objective coefficient at + variable x[k] */ + /*--------------------------------------------------------------*/ + /* original bounds of variables */ + char *orig_type; /* char orig_type[1+m+n]; */ + double *orig_lb; /* double orig_lb[1+m+n]; */ + double *orig_ub; /* double orig_ub[1+m+n]; */ + /*--------------------------------------------------------------*/ + /* original objective function */ + double *obj; /* double obj[1+n]; */ + /* obj[0] is a constant term of the original objective function; + obj[j], 1 <= j <= n, is an original objective coefficient at + structural variable x[m+j] */ + double zeta; + /* factor used to scale original objective coefficients; its + sign defines original optimization direction: zeta > 0 means + minimization, zeta < 0 means maximization */ + /*--------------------------------------------------------------*/ + /* constraint matrix A; it has m rows and n columns and is stored + by columns */ + int *A_ptr; /* int A_ptr[1+n+1]; */ + /* A_ptr[0] is not used; + A_ptr[j], 1 <= j <= n, is starting position of j-th column in + arrays A_ind and A_val; note that A_ptr[1] is always 1; + A_ptr[n+1] indicates the position after the last element in + arrays A_ind and A_val */ + int *A_ind; /* int A_ind[A_ptr[n+1]]; */ + /* row indices */ + double *A_val; /* double A_val[A_ptr[n+1]]; */ + /* non-zero element values */ +#if 1 /* 06/IV-2009 */ + /* constraint matrix A stored by rows */ + int *AT_ptr; /* int AT_ptr[1+m+1]; + /* AT_ptr[0] is not used; + AT_ptr[i], 1 <= i <= m, is starting position of i-th row in + arrays AT_ind and AT_val; note that AT_ptr[1] is always 1; + AT_ptr[m+1] indicates the position after the last element in + arrays AT_ind and AT_val */ + int *AT_ind; /* int AT_ind[AT_ptr[m+1]]; */ + /* column indices */ + double *AT_val; /* double AT_val[AT_ptr[m+1]]; */ + /* non-zero element values */ +#endif + /*--------------------------------------------------------------*/ + /* basis header */ + int *head; /* int head[1+m+n]; */ + /* head[0] is not used; + head[i], 1 <= i <= m, is the ordinal number of basic variable + xB[i]; head[i] = k means that xB[i] = x[k] and i-th column of + matrix B is k-th column of matrix (I|-A); + head[m+j], 1 <= j <= n, is the ordinal number of non-basic + variable xN[j]; head[m+j] = k means that xN[j] = x[k] and j-th + column of matrix N is k-th column of matrix (I|-A) */ +#if 1 /* 06/IV-2009 */ + int *bind; /* int bind[1+m+n]; */ + /* bind[0] is not used; + bind[k], 1 <= k <= m+n, is the position of k-th column of the + matrix (I|-A) in the matrix (B|N); that is, bind[k] = k' means + that head[k'] = k */ +#endif + char *stat; /* char stat[1+n]; */ + /* stat[0] is not used; + stat[j], 1 <= j <= n, is the status of non-basic variable + xN[j], which defines its active bound: + GLP_NL - lower bound is active + GLP_NU - upper bound is active + GLP_NF - free variable + GLP_NS - fixed variable */ + /*--------------------------------------------------------------*/ + /* matrix B is the basis matrix; it is composed from columns of + the augmented constraint matrix (I|-A) corresponding to basic + variables and stored in a factorized (invertable) form */ + int valid; + /* factorization is valid only if this flag is set */ + BFD *bfd; /* BFD bfd[1:m,1:m]; */ + /* factorized (invertable) form of the basis matrix */ +#if 0 /* 06/IV-2009 */ + /*--------------------------------------------------------------*/ + /* matrix N is a matrix composed from columns of the augmented + constraint matrix (I|-A) corresponding to non-basic variables + except fixed ones; it is stored by rows and changes every time + the basis changes */ + int *N_ptr; /* int N_ptr[1+m+1]; */ + /* N_ptr[0] is not used; + N_ptr[i], 1 <= i <= m, is starting position of i-th row in + arrays N_ind and N_val; note that N_ptr[1] is always 1; + N_ptr[m+1] indicates the position after the last element in + arrays N_ind and N_val */ + int *N_len; /* int N_len[1+m]; */ + /* N_len[0] is not used; + N_len[i], 1 <= i <= m, is length of i-th row (0 to n) */ + int *N_ind; /* int N_ind[N_ptr[m+1]]; */ + /* column indices */ + double *N_val; /* double N_val[N_ptr[m+1]]; */ + /* non-zero element values */ +#endif + /*--------------------------------------------------------------*/ + /* working parameters */ + int phase; + /* search phase: + 0 - not determined yet + 1 - search for dual feasible solution + 2 - search for optimal solution */ + glp_long tm_beg; + /* time value at the beginning of the search */ + int it_beg; + /* simplex iteration count at the beginning of the search */ + int it_cnt; + /* simplex iteration count; it increases by one every time the + basis changes */ + int it_dpy; + /* simplex iteration count at the most recent display output */ + /*--------------------------------------------------------------*/ + /* basic solution components */ + double *bbar; /* double bbar[1+m]; */ + /* bbar[0] is not used on phase I; on phase II it is the current + value of the original objective function; + bbar[i], 1 <= i <= m, is primal value of basic variable xB[i] + (if xB[i] is free, its primal value is not updated) */ + double *cbar; /* double cbar[1+n]; */ + /* cbar[0] is not used; + cbar[j], 1 <= j <= n, is reduced cost of non-basic variable + xN[j] (if xN[j] is fixed, its reduced cost is not updated) */ + /*--------------------------------------------------------------*/ + /* the following pricing technique options may be used: + GLP_PT_STD - standard ("textbook") pricing; + GLP_PT_PSE - projected steepest edge; + GLP_PT_DVX - Devex pricing (not implemented yet); + in case of GLP_PT_STD the reference space is not used, and all + steepest edge coefficients are set to 1 */ + int refct; + /* this count is set to an initial value when the reference space + is defined and decreases by one every time the basis changes; + once this count reaches zero, the reference space is redefined + again */ + char *refsp; /* char refsp[1+m+n]; */ + /* refsp[0] is not used; + refsp[k], 1 <= k <= m+n, is the flag which means that variable + x[k] belongs to the current reference space */ + double *gamma; /* double gamma[1+m]; */ + /* gamma[0] is not used; + gamma[i], 1 <= i <= n, is the steepest edge coefficient for + basic variable xB[i]; if xB[i] is free, gamma[i] is not used + and just set to 1 */ + /*--------------------------------------------------------------*/ + /* basic variable xB[p] chosen to leave the basis */ + int p; + /* index of the basic variable xB[p] chosen, 1 <= p <= m; + if the set of eligible basic variables is empty (i.e. if the + current basic solution is primal feasible within a tolerance) + and thus no variable has been chosen, p is set to 0 */ + double delta; + /* change of xB[p] in the adjacent basis; + delta > 0 means that xB[p] violates its lower bound and will + increase to achieve it in the adjacent basis; + delta < 0 means that xB[p] violates its upper bound and will + decrease to achieve it in the adjacent basis */ + /*--------------------------------------------------------------*/ + /* pivot row of the simplex table corresponding to basic variable + xB[p] chosen is the following vector: + T' * e[p] = - N' * inv(B') * e[p] = - N' * rho, + where B' is a matrix transposed to the current basis matrix, + N' is a matrix, whose rows are columns of the matrix (I|-A) + corresponding to non-basic non-fixed variables */ + int trow_nnz; + /* number of non-zero components, 0 <= nnz <= n */ + int *trow_ind; /* int trow_ind[1+n]; */ + /* trow_ind[0] is not used; + trow_ind[t], 1 <= t <= nnz, is an index of non-zero component, + i.e. trow_ind[t] = j means that trow_vec[j] != 0 */ + double *trow_vec; /* int trow_vec[1+n]; */ + /* trow_vec[0] is not used; + trow_vec[j], 1 <= j <= n, is a numeric value of j-th component + of the row */ + double trow_max; + /* infinity (maximum) norm of the row (max |trow_vec[j]|) */ + int trow_num; + /* number of significant non-zero components, which means that: + |trow_vec[j]| >= eps for j in trow_ind[1,...,num], + |tcol_vec[j]| < eps for j in trow_ind[num+1,...,nnz], + where eps is a pivot tolerance */ + /*--------------------------------------------------------------*/ +#ifdef GLP_LONG_STEP /* 07/IV-2009 */ + int nbps; + /* number of breakpoints, 0 <= nbps <= n */ + struct bkpt + { int j; + /* index of non-basic variable xN[j], 1 <= j <= n */ + double t; + /* value of dual ray parameter at breakpoint, t >= 0 */ + double dz; + /* dz = zeta(t = t[k]) - zeta(t = 0) */ + } *bkpt; /* struct bkpt bkpt[1+n]; */ + /* bkpt[0] is not used; + bkpt[k], 1 <= k <= nbps, is k-th breakpoint of the dual + objective */ +#endif + /*--------------------------------------------------------------*/ + /* non-basic variable xN[q] chosen to enter the basis */ + int q; + /* index of the non-basic variable xN[q] chosen, 1 <= q <= n; + if no variable has been chosen, q is set to 0 */ + double new_dq; + /* reduced cost of xN[q] in the adjacent basis (it is the change + of lambdaB[p]) */ + /*--------------------------------------------------------------*/ + /* pivot column of the simplex table corresponding to non-basic + variable xN[q] chosen is the following vector: + T * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], + where B is the current basis matrix, N[q] is a column of the + matrix (I|-A) corresponding to xN[q] */ + int tcol_nnz; + /* number of non-zero components, 0 <= nnz <= m */ + int *tcol_ind; /* int tcol_ind[1+m]; */ + /* tcol_ind[0] is not used; + tcol_ind[t], 1 <= t <= nnz, is an index of non-zero component, + i.e. tcol_ind[t] = i means that tcol_vec[i] != 0 */ + double *tcol_vec; /* double tcol_vec[1+m]; */ + /* tcol_vec[0] is not used; + tcol_vec[i], 1 <= i <= m, is a numeric value of i-th component + of the column */ + /*--------------------------------------------------------------*/ + /* working arrays */ + double *work1; /* double work1[1+m]; */ + double *work2; /* double work2[1+m]; */ + double *work3; /* double work3[1+m]; */ + double *work4; /* double work4[1+m]; */ +}; + +static const double kappa = 0.10; + +/*********************************************************************** +* alloc_csa - allocate common storage area +* +* This routine allocates all arrays in the common storage area (CSA) +* and returns a pointer to the CSA. */ + +static struct csa *alloc_csa(glp_prob *lp) +{ struct csa *csa; + int m = lp->m; + int n = lp->n; + int nnz = lp->nnz; + csa = xmalloc(sizeof(struct csa)); + xassert(m > 0 && n > 0); + csa->m = m; + csa->n = n; + csa->type = xcalloc(1+m+n, sizeof(char)); + csa->lb = xcalloc(1+m+n, sizeof(double)); + csa->ub = xcalloc(1+m+n, sizeof(double)); + csa->coef = xcalloc(1+m+n, sizeof(double)); + csa->orig_type = xcalloc(1+m+n, sizeof(char)); + csa->orig_lb = xcalloc(1+m+n, sizeof(double)); + csa->orig_ub = xcalloc(1+m+n, sizeof(double)); + csa->obj = xcalloc(1+n, sizeof(double)); + csa->A_ptr = xcalloc(1+n+1, sizeof(int)); + csa->A_ind = xcalloc(1+nnz, sizeof(int)); + csa->A_val = xcalloc(1+nnz, sizeof(double)); +#if 1 /* 06/IV-2009 */ + csa->AT_ptr = xcalloc(1+m+1, sizeof(int)); + csa->AT_ind = xcalloc(1+nnz, sizeof(int)); + csa->AT_val = xcalloc(1+nnz, sizeof(double)); +#endif + csa->head = xcalloc(1+m+n, sizeof(int)); +#if 1 /* 06/IV-2009 */ + csa->bind = xcalloc(1+m+n, sizeof(int)); +#endif + csa->stat = xcalloc(1+n, sizeof(char)); +#if 0 /* 06/IV-2009 */ + csa->N_ptr = xcalloc(1+m+1, sizeof(int)); + csa->N_len = xcalloc(1+m, sizeof(int)); + csa->N_ind = NULL; /* will be allocated later */ + csa->N_val = NULL; /* will be allocated later */ +#endif + csa->bbar = xcalloc(1+m, sizeof(double)); + csa->cbar = xcalloc(1+n, sizeof(double)); + csa->refsp = xcalloc(1+m+n, sizeof(char)); + csa->gamma = xcalloc(1+m, sizeof(double)); + csa->trow_ind = xcalloc(1+n, sizeof(int)); + csa->trow_vec = xcalloc(1+n, sizeof(double)); +#ifdef GLP_LONG_STEP /* 07/IV-2009 */ + csa->bkpt = xcalloc(1+n, sizeof(struct bkpt)); +#endif + csa->tcol_ind = xcalloc(1+m, sizeof(int)); + csa->tcol_vec = xcalloc(1+m, sizeof(double)); + csa->work1 = xcalloc(1+m, sizeof(double)); + csa->work2 = xcalloc(1+m, sizeof(double)); + csa->work3 = xcalloc(1+m, sizeof(double)); + csa->work4 = xcalloc(1+m, sizeof(double)); + return csa; +} + +/*********************************************************************** +* init_csa - initialize common storage area +* +* This routine initializes all data structures in the common storage +* area (CSA). */ + +static void init_csa(struct csa *csa, glp_prob *lp) +{ int m = csa->m; + int n = csa->n; + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + double *coef = csa->coef; + char *orig_type = csa->orig_type; + double *orig_lb = csa->orig_lb; + double *orig_ub = csa->orig_ub; + double *obj = csa->obj; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; +#if 1 /* 06/IV-2009 */ + int *AT_ptr = csa->AT_ptr; + int *AT_ind = csa->AT_ind; + double *AT_val = csa->AT_val; +#endif + int *head = csa->head; +#if 1 /* 06/IV-2009 */ + int *bind = csa->bind; +#endif + char *stat = csa->stat; + char *refsp = csa->refsp; + double *gamma = csa->gamma; + int i, j, k, loc; + double cmax; + /* auxiliary variables */ + for (i = 1; i <= m; i++) + { GLPROW *row = lp->row[i]; + type[i] = (char)row->type; + lb[i] = row->lb * row->rii; + ub[i] = row->ub * row->rii; + coef[i] = 0.0; + } + /* structural variables */ + for (j = 1; j <= n; j++) + { GLPCOL *col = lp->col[j]; + type[m+j] = (char)col->type; + lb[m+j] = col->lb / col->sjj; + ub[m+j] = col->ub / col->sjj; + coef[m+j] = col->coef * col->sjj; + } + /* original bounds of variables */ + memcpy(&orig_type[1], &type[1], (m+n) * sizeof(char)); + memcpy(&orig_lb[1], &lb[1], (m+n) * sizeof(double)); + memcpy(&orig_ub[1], &ub[1], (m+n) * sizeof(double)); + /* original objective function */ + obj[0] = lp->c0; + memcpy(&obj[1], &coef[m+1], n * sizeof(double)); + /* factor used to scale original objective coefficients */ + cmax = 0.0; + for (j = 1; j <= n; j++) + if (cmax < fabs(obj[j])) cmax = fabs(obj[j]); + if (cmax == 0.0) cmax = 1.0; + switch (lp->dir) + { case GLP_MIN: + csa->zeta = + 1.0 / cmax; + break; + case GLP_MAX: + csa->zeta = - 1.0 / cmax; + break; + default: + xassert(lp != lp); + } +#if 1 + if (fabs(csa->zeta) < 1.0) csa->zeta *= 1000.0; +#endif + /* scale working objective coefficients */ + for (j = 1; j <= n; j++) coef[m+j] *= csa->zeta; + /* matrix A (by columns) */ + loc = 1; + for (j = 1; j <= n; j++) + { GLPAIJ *aij; + A_ptr[j] = loc; + for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) + { A_ind[loc] = aij->row->i; + A_val[loc] = aij->row->rii * aij->val * aij->col->sjj; + loc++; + } + } + A_ptr[n+1] = loc; + xassert(loc-1 == lp->nnz); +#if 1 /* 06/IV-2009 */ + /* matrix A (by rows) */ + loc = 1; + for (i = 1; i <= m; i++) + { GLPAIJ *aij; + AT_ptr[i] = loc; + for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) + { AT_ind[loc] = aij->col->j; + AT_val[loc] = aij->row->rii * aij->val * aij->col->sjj; + loc++; + } + } + AT_ptr[m+1] = loc; + xassert(loc-1 == lp->nnz); +#endif + /* basis header */ + xassert(lp->valid); + memcpy(&head[1], &lp->head[1], m * sizeof(int)); + k = 0; + for (i = 1; i <= m; i++) + { GLPROW *row = lp->row[i]; + if (row->stat != GLP_BS) + { k++; + xassert(k <= n); + head[m+k] = i; + stat[k] = (char)row->stat; + } + } + for (j = 1; j <= n; j++) + { GLPCOL *col = lp->col[j]; + if (col->stat != GLP_BS) + { k++; + xassert(k <= n); + head[m+k] = m + j; + stat[k] = (char)col->stat; + } + } + xassert(k == n); +#if 1 /* 06/IV-2009 */ + for (k = 1; k <= m+n; k++) + bind[head[k]] = k; +#endif + /* factorization of matrix B */ + csa->valid = 1, lp->valid = 0; + csa->bfd = lp->bfd, lp->bfd = NULL; +#if 0 /* 06/IV-2009 */ + /* matrix N (by rows) */ + alloc_N(csa); + build_N(csa); +#endif + /* working parameters */ + csa->phase = 0; + csa->tm_beg = xtime(); + csa->it_beg = csa->it_cnt = lp->it_cnt; + csa->it_dpy = -1; + /* reference space and steepest edge coefficients */ + csa->refct = 0; + memset(&refsp[1], 0, (m+n) * sizeof(char)); + for (i = 1; i <= m; i++) gamma[i] = 1.0; + return; +} + +#if 1 /* copied from primal */ +/*********************************************************************** +* invert_B - compute factorization of the basis matrix +* +* This routine computes factorization of the current basis matrix B. +* +* If the operation is successful, the routine returns zero, otherwise +* non-zero. */ + +static int inv_col(void *info, int i, int ind[], double val[]) +{ /* this auxiliary routine returns row indices and numeric values + of non-zero elements of i-th column of the basis matrix */ + struct csa *csa = info; + int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + int k, len, ptr, t; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); +#endif + k = head[i]; /* B[i] is k-th column of (I|-A) */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { /* B[i] is k-th column of submatrix I */ + len = 1; + ind[1] = k; + val[1] = 1.0; + } + else + { /* B[i] is (k-m)-th column of submatrix (-A) */ + ptr = A_ptr[k-m]; + len = A_ptr[k-m+1] - ptr; + memcpy(&ind[1], &A_ind[ptr], len * sizeof(int)); + memcpy(&val[1], &A_val[ptr], len * sizeof(double)); + for (t = 1; t <= len; t++) val[t] = - val[t]; + } + return len; +} + +static int invert_B(struct csa *csa) +{ int ret; + ret = bfd_factorize(csa->bfd, csa->m, NULL, inv_col, csa); + csa->valid = (ret == 0); + return ret; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* update_B - update factorization of the basis matrix +* +* This routine replaces i-th column of the basis matrix B by k-th +* column of the augmented constraint matrix (I|-A) and then updates +* the factorization of B. +* +* If the factorization has been successfully updated, the routine +* returns zero, otherwise non-zero. */ + +static int update_B(struct csa *csa, int i, int k) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int ret; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { /* new i-th column of B is k-th column of I */ + int ind[1+1]; + double val[1+1]; + ind[1] = k; + val[1] = 1.0; + xassert(csa->valid); + ret = bfd_update_it(csa->bfd, i, 0, 1, ind, val); + } + else + { /* new i-th column of B is (k-m)-th column of (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + double *val = csa->work1; + int beg, end, ptr, len; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + len = 0; + for (ptr = beg; ptr < end; ptr++) + val[++len] = - A_val[ptr]; + xassert(csa->valid); + ret = bfd_update_it(csa->bfd, i, 0, len, &A_ind[beg-1], val); + } + csa->valid = (ret == 0); + return ret; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* error_ftran - compute residual vector r = h - B * x +* +* This routine computes the residual vector r = h - B * x, where B is +* the current basis matrix, h is the vector of right-hand sides, x is +* the solution vector. */ + +static void error_ftran(struct csa *csa, double h[], double x[], + double r[]) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + int i, k, beg, end, ptr; + double temp; + /* compute the residual vector: + r = h - B * x = h - B[1] * x[1] - ... - B[m] * x[m], + where B[1], ..., B[m] are columns of matrix B */ + memcpy(&r[1], &h[1], m * sizeof(double)); + for (i = 1; i <= m; i++) + { temp = x[i]; + if (temp == 0.0) continue; + k = head[i]; /* B[i] is k-th column of (I|-A) */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { /* B[i] is k-th column of submatrix I */ + r[k] -= temp; + } + else + { /* B[i] is (k-m)-th column of submatrix (-A) */ + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + r[A_ind[ptr]] += A_val[ptr] * temp; + } + } + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* refine_ftran - refine solution of B * x = h +* +* This routine performs one iteration to refine the solution of +* the system B * x = h, where B is the current basis matrix, h is the +* vector of right-hand sides, x is the solution vector. */ + +static void refine_ftran(struct csa *csa, double h[], double x[]) +{ int m = csa->m; + double *r = csa->work1; + double *d = csa->work1; + int i; + /* compute the residual vector r = h - B * x */ + error_ftran(csa, h, x, r); + /* compute the correction vector d = inv(B) * r */ + xassert(csa->valid); + bfd_ftran(csa->bfd, d); + /* refine the solution vector (new x) = (old x) + d */ + for (i = 1; i <= m; i++) x[i] += d[i]; + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* error_btran - compute residual vector r = h - B'* x +* +* This routine computes the residual vector r = h - B'* x, where B' +* is a matrix transposed to the current basis matrix, h is the vector +* of right-hand sides, x is the solution vector. */ + +static void error_btran(struct csa *csa, double h[], double x[], + double r[]) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + int i, k, beg, end, ptr; + double temp; + /* compute the residual vector r = b - B'* x */ + for (i = 1; i <= m; i++) + { /* r[i] := b[i] - (i-th column of B)'* x */ + k = head[i]; /* B[i] is k-th column of (I|-A) */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + temp = h[i]; + if (k <= m) + { /* B[i] is k-th column of submatrix I */ + temp -= x[k]; + } + else + { /* B[i] is (k-m)-th column of submatrix (-A) */ + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + temp += A_val[ptr] * x[A_ind[ptr]]; + } + r[i] = temp; + } + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* refine_btran - refine solution of B'* x = h +* +* This routine performs one iteration to refine the solution of the +* system B'* x = h, where B' is a matrix transposed to the current +* basis matrix, h is the vector of right-hand sides, x is the solution +* vector. */ + +static void refine_btran(struct csa *csa, double h[], double x[]) +{ int m = csa->m; + double *r = csa->work1; + double *d = csa->work1; + int i; + /* compute the residual vector r = h - B'* x */ + error_btran(csa, h, x, r); + /* compute the correction vector d = inv(B') * r */ + xassert(csa->valid); + bfd_btran(csa->bfd, d); + /* refine the solution vector (new x) = (old x) + d */ + for (i = 1; i <= m; i++) x[i] += d[i]; + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* get_xN - determine current value of non-basic variable xN[j] +* +* This routine returns the current value of non-basic variable xN[j], +* which is a value of its active bound. */ + +static double get_xN(struct csa *csa, int j) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + double *lb = csa->lb; + double *ub = csa->ub; + int *head = csa->head; + char *stat = csa->stat; + int k; + double xN; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + switch (stat[j]) + { case GLP_NL: + /* x[k] is on its lower bound */ + xN = lb[k]; break; + case GLP_NU: + /* x[k] is on its upper bound */ + xN = ub[k]; break; + case GLP_NF: + /* x[k] is free non-basic variable */ + xN = 0.0; break; + case GLP_NS: + /* x[k] is fixed non-basic variable */ + xN = lb[k]; break; + default: + xassert(stat != stat); + } + return xN; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* eval_beta - compute primal values of basic variables +* +* This routine computes current primal values of all basic variables: +* +* beta = - inv(B) * N * xN, +* +* where B is the current basis matrix, N is a matrix built of columns +* of matrix (I|-A) corresponding to non-basic variables, and xN is the +* vector of current values of non-basic variables. */ + +static void eval_beta(struct csa *csa, double beta[]) +{ int m = csa->m; + int n = csa->n; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + double *h = csa->work2; + int i, j, k, beg, end, ptr; + double xN; + /* compute the right-hand side vector: + h := - N * xN = - N[1] * xN[1] - ... - N[n] * xN[n], + where N[1], ..., N[n] are columns of matrix N */ + for (i = 1; i <= m; i++) + h[i] = 0.0; + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* determine current value of xN[j] */ + xN = get_xN(csa, j); + if (xN == 0.0) continue; + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + h[k] -= xN; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + h[A_ind[ptr]] += xN * A_val[ptr]; + } + } + /* solve system B * beta = h */ + memcpy(&beta[1], &h[1], m * sizeof(double)); + xassert(csa->valid); + bfd_ftran(csa->bfd, beta); + /* and refine the solution */ + refine_ftran(csa, h, beta); + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* eval_pi - compute vector of simplex multipliers +* +* This routine computes the vector of current simplex multipliers: +* +* pi = inv(B') * cB, +* +* where B' is a matrix transposed to the current basis matrix, cB is +* a subvector of objective coefficients at basic variables. */ + +static void eval_pi(struct csa *csa, double pi[]) +{ int m = csa->m; + double *c = csa->coef; + int *head = csa->head; + double *cB = csa->work2; + int i; + /* construct the right-hand side vector cB */ + for (i = 1; i <= m; i++) + cB[i] = c[head[i]]; + /* solve system B'* pi = cB */ + memcpy(&pi[1], &cB[1], m * sizeof(double)); + xassert(csa->valid); + bfd_btran(csa->bfd, pi); + /* and refine the solution */ + refine_btran(csa, cB, pi); + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* eval_cost - compute reduced cost of non-basic variable xN[j] +* +* This routine computes the current reduced cost of non-basic variable +* xN[j]: +* +* d[j] = cN[j] - N'[j] * pi, +* +* where cN[j] is the objective coefficient at variable xN[j], N[j] is +* a column of the augmented constraint matrix (I|-A) corresponding to +* xN[j], pi is the vector of simplex multipliers. */ + +static double eval_cost(struct csa *csa, double pi[], int j) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + double *coef = csa->coef; + int *head = csa->head; + int k; + double dj; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + dj = coef[k]; + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + dj -= pi[k]; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + dj += A_val[ptr] * pi[A_ind[ptr]]; + } + return dj; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* eval_bbar - compute and store primal values of basic variables +* +* This routine computes primal values of all basic variables and then +* stores them in the solution array. */ + +static void eval_bbar(struct csa *csa) +{ eval_beta(csa, csa->bbar); + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* eval_cbar - compute and store reduced costs of non-basic variables +* +* This routine computes reduced costs of all non-basic variables and +* then stores them in the solution array. */ + +static void eval_cbar(struct csa *csa) +{ +#ifdef GLP_DEBUG + int m = csa->m; +#endif + int n = csa->n; +#ifdef GLP_DEBUG + int *head = csa->head; +#endif + double *cbar = csa->cbar; + double *pi = csa->work3; + int j; +#ifdef GLP_DEBUG + int k; +#endif + /* compute simplex multipliers */ + eval_pi(csa, pi); + /* compute and store reduced costs */ + for (j = 1; j <= n; j++) + { +#ifdef GLP_DEBUG + k = head[m+j]; /* x[k] = xN[j] */ + xassert(1 <= k && k <= m+n); +#endif + cbar[j] = eval_cost(csa, pi, j); + } + return; +} +#endif + +/*********************************************************************** +* reset_refsp - reset the reference space +* +* This routine resets (redefines) the reference space used in the +* projected steepest edge pricing algorithm. */ + +static void reset_refsp(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + int *head = csa->head; + char *refsp = csa->refsp; + double *gamma = csa->gamma; + int i, k; + xassert(csa->refct == 0); + csa->refct = 1000; + memset(&refsp[1], 0, (m+n) * sizeof(char)); + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ + refsp[k] = 1; + gamma[i] = 1.0; + } + return; +} + +/*********************************************************************** +* eval_gamma - compute steepest edge coefficients +* +* This routine computes the vector of steepest edge coefficients for +* all basic variables (except free ones) using its direct definition: +* +* gamma[i] = eta[i] + sum alfa[i,j]^2, i = 1,...,m, +* j in C +* +* where eta[i] = 1 means that xB[i] is in the current reference space, +* and 0 otherwise; C is a set of non-basic non-fixed variables xN[j], +* which are in the current reference space; alfa[i,j] are elements of +* the current simplex table. +* +* NOTE: The routine is intended only for debugginig purposes. */ + +static void eval_gamma(struct csa *csa, double gamma[]) +{ int m = csa->m; + int n = csa->n; + char *type = csa->type; + int *head = csa->head; + char *refsp = csa->refsp; + double *alfa = csa->work3; + double *h = csa->work3; + int i, j, k; + /* gamma[i] := eta[i] (or 1, if xB[i] is free) */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (type[k] == GLP_FR) + gamma[i] = 1.0; + else + gamma[i] = (refsp[k] ? 1.0 : 0.0); + } + /* compute columns of the current simplex table */ + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* skip column, if xN[j] is not in C */ + if (!refsp[k]) continue; +#ifdef GLP_DEBUG + /* set C must not contain fixed variables */ + xassert(type[k] != GLP_FX); +#endif + /* construct the right-hand side vector h = - N[j] */ + for (i = 1; i <= m; i++) + h[i] = 0.0; + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + h[k] = -1.0; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + h[A_ind[ptr]] = A_val[ptr]; + } + /* solve system B * alfa = h */ + xassert(csa->valid); + bfd_ftran(csa->bfd, alfa); + /* gamma[i] := gamma[i] + alfa[i,j]^2 */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ + if (type[k] != GLP_FR) + gamma[i] += alfa[i] * alfa[i]; + } + } + return; +} + +/*********************************************************************** +* chuzr - choose basic variable (row of the simplex table) +* +* This routine chooses basic variable xB[p] having largest weighted +* bound violation: +* +* |r[p]| / sqrt(gamma[p]) = max |r[i]| / sqrt(gamma[i]), +* i in I +* +* / lB[i] - beta[i], if beta[i] < lB[i] +* | +* r[i] = < 0, if lB[i] <= beta[i] <= uB[i] +* | +* \ uB[i] - beta[i], if beta[i] > uB[i] +* +* where beta[i] is primal value of xB[i] in the current basis, lB[i] +* and uB[i] are lower and upper bounds of xB[i], I is a subset of +* eligible basic variables, which significantly violates their bounds, +* gamma[i] is the steepest edge coefficient. +* +* If |r[i]| is less than a specified tolerance, xB[i] is not included +* in I and therefore ignored. +* +* If I is empty and no variable has been chosen, p is set to 0. */ + +static void chuzr(struct csa *csa, double tol_bnd) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + int *head = csa->head; + double *bbar = csa->bbar; + double *gamma = csa->gamma; + int i, k, p; + double delta, best, eps, ri, temp; + /* nothing is chosen so far */ + p = 0, delta = 0.0, best = 0.0; + /* look through the list of basic variables */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* determine bound violation ri[i] */ + ri = 0.0; + if (type[k] == GLP_LO || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* xB[i] has lower bound */ + eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); + if (bbar[i] < lb[k] - eps) + { /* and significantly violates it */ + ri = lb[k] - bbar[i]; + } + } + if (type[k] == GLP_UP || type[k] == GLP_DB || + type[k] == GLP_FX) + { /* xB[i] has upper bound */ + eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); + if (bbar[i] > ub[k] + eps) + { /* and significantly violates it */ + ri = ub[k] - bbar[i]; + } + } + /* if xB[i] is not eligible, skip it */ + if (ri == 0.0) continue; + /* xB[i] is eligible basic variable; choose one with largest + weighted bound violation */ +#ifdef GLP_DEBUG + xassert(gamma[i] >= 0.0); +#endif + temp = gamma[i]; + if (temp < DBL_EPSILON) temp = DBL_EPSILON; + temp = (ri * ri) / temp; + if (best < temp) + p = i, delta = ri, best = temp; + } + /* store the index of basic variable xB[p] chosen and its change + in the adjacent basis */ + csa->p = p; + csa->delta = delta; + return; +} + +#if 1 /* copied from primal */ +/*********************************************************************** +* eval_rho - compute pivot row of the inverse +* +* This routine computes the pivot (p-th) row of the inverse inv(B), +* which corresponds to basic variable xB[p] chosen: +* +* rho = inv(B') * e[p], +* +* where B' is a matrix transposed to the current basis matrix, e[p] +* is unity vector. */ + +static void eval_rho(struct csa *csa, double rho[]) +{ int m = csa->m; + int p = csa->p; + double *e = rho; + int i; +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); +#endif + /* construct the right-hand side vector e[p] */ + for (i = 1; i <= m; i++) + e[i] = 0.0; + e[p] = 1.0; + /* solve system B'* rho = e[p] */ + xassert(csa->valid); + bfd_btran(csa->bfd, rho); + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* refine_rho - refine pivot row of the inverse +* +* This routine refines the pivot row of the inverse inv(B) assuming +* that it was previously computed by the routine eval_rho. */ + +static void refine_rho(struct csa *csa, double rho[]) +{ int m = csa->m; + int p = csa->p; + double *e = csa->work3; + int i; +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); +#endif + /* construct the right-hand side vector e[p] */ + for (i = 1; i <= m; i++) + e[i] = 0.0; + e[p] = 1.0; + /* refine solution of B'* rho = e[p] */ + refine_btran(csa, e, rho); + return; +} +#endif + +#if 1 /* 06/IV-2009 */ +/*********************************************************************** +* eval_trow - compute pivot row of the simplex table +* +* This routine computes the pivot row of the simplex table, which +* corresponds to basic variable xB[p] chosen. +* +* The pivot row is the following vector: +* +* trow = T'* e[p] = - N'* inv(B') * e[p] = - N' * rho, +* +* where rho is the pivot row of the inverse inv(B) previously computed +* by the routine eval_rho. +* +* Note that elements of the pivot row corresponding to fixed non-basic +* variables are not computed. +* +* NOTES +* +* Computing pivot row of the simplex table is one of the most time +* consuming operations, and for some instances it may take more than +* 50% of the total solution time. +* +* In the current implementation there are two routines to compute the +* pivot row. The routine eval_trow1 computes elements of the pivot row +* as inner products of columns of the matrix N and the vector rho; it +* is used when the vector rho is relatively dense. The routine +* eval_trow2 computes the pivot row as a linear combination of rows of +* the matrix N; it is used when the vector rho is relatively sparse. */ + +static void eval_trow1(struct csa *csa, double rho[]) +{ int m = csa->m; + int n = csa->n; + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int *head = csa->head; + char *stat = csa->stat; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int j, k, beg, end, ptr, nnz; + double temp; + /* compute the pivot row as inner products of columns of the + matrix N and vector rho: trow[j] = - rho * N[j] */ + nnz = 0; + for (j = 1; j <= n; j++) + { if (stat[j] == GLP_NS) + { /* xN[j] is fixed */ + trow_vec[j] = 0.0; + continue; + } + k = head[m+j]; /* x[k] = xN[j] */ + if (k <= m) + { /* N[j] is k-th column of submatrix I */ + temp = - rho[k]; + } + else + { /* N[j] is (k-m)-th column of submatrix (-A) */ + beg = A_ptr[k-m], end = A_ptr[k-m+1]; + temp = 0.0; + for (ptr = beg; ptr < end; ptr++) + temp += rho[A_ind[ptr]] * A_val[ptr]; + } + if (temp != 0.0) + trow_ind[++nnz] = j; + trow_vec[j] = temp; + } + csa->trow_nnz = nnz; + return; +} + +static void eval_trow2(struct csa *csa, double rho[]) +{ int m = csa->m; + int n = csa->n; + int *AT_ptr = csa->AT_ptr; + int *AT_ind = csa->AT_ind; + double *AT_val = csa->AT_val; + int *bind = csa->bind; + char *stat = csa->stat; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int i, j, beg, end, ptr, nnz; + double temp; + /* clear the pivot row */ + for (j = 1; j <= n; j++) + trow_vec[j] = 0.0; + /* compute the pivot row as a linear combination of rows of the + matrix N: trow = - rho[1] * N'[1] - ... - rho[m] * N'[m] */ + for (i = 1; i <= m; i++) + { temp = rho[i]; + if (temp == 0.0) continue; + /* trow := trow - rho[i] * N'[i] */ + j = bind[i] - m; /* x[i] = xN[j] */ + if (j >= 1 && stat[j] != GLP_NS) + trow_vec[j] -= temp; + beg = AT_ptr[i], end = AT_ptr[i+1]; + for (ptr = beg; ptr < end; ptr++) + { j = bind[m + AT_ind[ptr]] - m; /* x[k] = xN[j] */ + if (j >= 1 && stat[j] != GLP_NS) + trow_vec[j] += temp * AT_val[ptr]; + } + } + /* construct sparse pattern of the pivot row */ + nnz = 0; + for (j = 1; j <= n; j++) + { if (trow_vec[j] != 0.0) + trow_ind[++nnz] = j; + } + csa->trow_nnz = nnz; + return; +} + +static void eval_trow(struct csa *csa, double rho[]) +{ int m = csa->m; + int i, nnz; + double dens; + /* determine the density of the vector rho */ + nnz = 0; + for (i = 1; i <= m; i++) + if (rho[i] != 0.0) nnz++; + dens = (double)nnz / (double)m; + if (dens >= 0.20) + { /* rho is relatively dense */ + eval_trow1(csa, rho); + } + else + { /* rho is relatively sparse */ + eval_trow2(csa, rho); + } + return; +} +#endif + +/*********************************************************************** +* sort_trow - sort pivot row of the simplex table +* +* This routine reorders the list of non-zero elements of the pivot +* row to put significant elements, whose magnitude is not less than +* a specified tolerance, in front of the list, and stores the number +* of significant elements in trow_num. */ + +static void sort_trow(struct csa *csa, double tol_piv) +{ +#ifdef GLP_DEBUG + int n = csa->n; + char *stat = csa->stat; +#endif + int nnz = csa->trow_nnz; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int j, num, pos; + double big, eps, temp; + /* compute infinity (maximum) norm of the row */ + big = 0.0; + for (pos = 1; pos <= nnz; pos++) + { +#ifdef GLP_DEBUG + j = trow_ind[pos]; + xassert(1 <= j && j <= n); + xassert(stat[j] != GLP_NS); +#endif + temp = fabs(trow_vec[trow_ind[pos]]); + if (big < temp) big = temp; + } + csa->trow_max = big; + /* determine absolute pivot tolerance */ + eps = tol_piv * (1.0 + 0.01 * big); + /* move significant row components to the front of the list */ + for (num = 0; num < nnz; ) + { j = trow_ind[nnz]; + if (fabs(trow_vec[j]) < eps) + nnz--; + else + { num++; + trow_ind[nnz] = trow_ind[num]; + trow_ind[num] = j; + } + } + csa->trow_num = num; + return; +} + +#ifdef GLP_LONG_STEP /* 07/IV-2009 */ +static int ls_func(const void *p1_, const void *p2_) +{ const struct bkpt *p1 = p1_, *p2 = p2_; + if (p1->t < p2->t) return -1; + if (p1->t > p2->t) return +1; + return 0; +} + +static int ls_func1(const void *p1_, const void *p2_) +{ const struct bkpt *p1 = p1_, *p2 = p2_; + if (p1->dz < p2->dz) return -1; + if (p1->dz > p2->dz) return +1; + return 0; +} + +static void long_step(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + int *head = csa->head; + char *stat = csa->stat; + double *cbar = csa->cbar; + double delta = csa->delta; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int trow_num = csa->trow_num; + struct bkpt *bkpt = csa->bkpt; + int j, k, kk, nbps, pos; + double alfa, s, slope, dzmax; + /* delta > 0 means that xB[p] violates its lower bound, so to + increase the dual objective lambdaB[p] must increase; + delta < 0 means that xB[p] violates its upper bound, so to + increase the dual objective lambdaB[p] must decrease */ + /* s := sign(delta) */ + s = (delta > 0.0 ? +1.0 : -1.0); + /* determine breakpoints of the dual objective */ + nbps = 0; + for (pos = 1; pos <= trow_num; pos++) + { j = trow_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); + xassert(stat[j] != GLP_NS); +#endif + /* if there is free non-basic variable, switch to the standard + ratio test */ + if (stat[j] == GLP_NF) + { nbps = 0; + goto done; + } + /* lambdaN[j] = ... - alfa * t - ..., where t = s * lambdaB[i] + is the dual ray parameter, t >= 0 */ + alfa = s * trow_vec[j]; +#ifdef GLP_DEBUG + xassert(alfa != 0.0); + xassert(stat[j] == GLP_NL || stat[j] == GLP_NU); +#endif + if (alfa > 0.0 && stat[j] == GLP_NL || + alfa < 0.0 && stat[j] == GLP_NU) + { /* either lambdaN[j] >= 0 (if stat = GLP_NL) and decreases + or lambdaN[j] <= 0 (if stat = GLP_NU) and increases; in + both cases we have a breakpoint */ + nbps++; +#ifdef GLP_DEBUG + xassert(nbps <= n); +#endif + bkpt[nbps].j = j; + bkpt[nbps].t = cbar[j] / alfa; +/* +if (stat[j] == GLP_NL && cbar[j] < 0.0 || + stat[j] == GLP_NU && cbar[j] > 0.0) +xprintf("%d %g\n", stat[j], cbar[j]); +*/ + /* if t is negative, replace it by exact zero (see comments + in the routine chuzc) */ + if (bkpt[nbps].t < 0.0) bkpt[nbps].t = 0.0; + } + } + /* if there are less than two breakpoints, switch to the standard + ratio test */ + if (nbps < 2) + { nbps = 0; + goto done; + } + /* sort breakpoints by ascending the dual ray parameter, t */ + qsort(&bkpt[1], nbps, sizeof(struct bkpt), ls_func); + /* determine last breakpoint, at which the dual objective still + greater than at t = 0 */ + dzmax = 0.0; + slope = fabs(delta); /* initial slope */ + for (kk = 1; kk <= nbps; kk++) + { if (kk == 1) + bkpt[kk].dz = + 0.0 + slope * (bkpt[kk].t - 0.0); + else + bkpt[kk].dz = + bkpt[kk-1].dz + slope * (bkpt[kk].t - bkpt[kk-1].t); + if (dzmax < bkpt[kk].dz) + dzmax = bkpt[kk].dz; + else if (bkpt[kk].dz < 0.05 * (1.0 + dzmax)) + { nbps = kk - 1; + break; + } + j = bkpt[kk].j; + k = head[m+j]; /* x[k] = xN[j] */ + if (type[k] == GLP_DB) + slope -= fabs(trow_vec[j]) * (ub[k] - lb[k]); + else + { nbps = kk; + break; + } + } + /* if there are less than two breakpoints, switch to the standard + ratio test */ + if (nbps < 2) + { nbps = 0; + goto done; + } + /* sort breakpoints by ascending the dual change, dz */ + qsort(&bkpt[1], nbps, sizeof(struct bkpt), ls_func1); +/* +for (kk = 1; kk <= nbps; kk++) +xprintf("%d; t = %g; dz = %g\n", kk, bkpt[kk].t, bkpt[kk].dz); +*/ +done: csa->nbps = nbps; + return; +} +#endif + +/*********************************************************************** +* chuzc - choose non-basic variable (column of the simplex table) +* +* This routine chooses non-basic variable xN[q], which being entered +* in the basis keeps dual feasibility of the basic solution. +* +* The parameter rtol is a relative tolerance used to relax zero bounds +* of reduced costs of non-basic variables. If rtol = 0, the routine +* implements the standard ratio test. Otherwise, if rtol > 0, the +* routine implements Harris' two-pass ratio test. In the latter case +* rtol should be about three times less than a tolerance used to check +* dual feasibility. */ + +static void chuzc(struct csa *csa, double rtol) +{ +#ifdef GLP_DEBUG + int m = csa->m; + int n = csa->n; +#endif + char *stat = csa->stat; + double *cbar = csa->cbar; +#ifdef GLP_DEBUG + int p = csa->p; +#endif + double delta = csa->delta; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int trow_num = csa->trow_num; + int j, pos, q; + double alfa, big, s, t, teta, tmax; +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); +#endif + /* delta > 0 means that xB[p] violates its lower bound and goes + to it in the adjacent basis, so lambdaB[p] is increasing from + its lower zero bound; + delta < 0 means that xB[p] violates its upper bound and goes + to it in the adjacent basis, so lambdaB[p] is decreasing from + its upper zero bound */ +#ifdef GLP_DEBUG + xassert(delta != 0.0); +#endif + /* s := sign(delta) */ + s = (delta > 0.0 ? +1.0 : -1.0); + /*** FIRST PASS ***/ + /* nothing is chosen so far */ + q = 0, teta = DBL_MAX, big = 0.0; + /* walk through significant elements of the pivot row */ + for (pos = 1; pos <= trow_num; pos++) + { j = trow_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + alfa = s * trow_vec[j]; +#ifdef GLP_DEBUG + xassert(alfa != 0.0); +#endif + /* lambdaN[j] = ... - alfa * lambdaB[p] - ..., and due to s we + need to consider only increasing lambdaB[p] */ + if (alfa > 0.0) + { /* lambdaN[j] is decreasing */ + if (stat[j] == GLP_NL || stat[j] == GLP_NF) + { /* lambdaN[j] has zero lower bound */ + t = (cbar[j] + rtol) / alfa; + } + else + { /* lambdaN[j] has no lower bound */ + continue; + } + } + else + { /* lambdaN[j] is increasing */ + if (stat[j] == GLP_NU || stat[j] == GLP_NF) + { /* lambdaN[j] has zero upper bound */ + t = (cbar[j] - rtol) / alfa; + } + else + { /* lambdaN[j] has no upper bound */ + continue; + } + } + /* t is a change of lambdaB[p], on which lambdaN[j] reaches + its zero bound (possibly relaxed); since the basic solution + is assumed to be dual feasible, t has to be non-negative by + definition; however, it may happen that lambdaN[j] slightly + (i.e. within a tolerance) violates its zero bound, that + leads to negative t; in the latter case, if xN[j] is chosen, + negative t means that lambdaB[p] changes in wrong direction + that may cause wrong results on updating reduced costs; + thus, if t is negative, we should replace it by exact zero + assuming that lambdaN[j] is exactly on its zero bound, and + violation appears due to round-off errors */ + if (t < 0.0) t = 0.0; + /* apply minimal ratio test */ + if (teta > t || teta == t && big < fabs(alfa)) + q = j, teta = t, big = fabs(alfa); + } + /* the second pass is skipped in the following cases: */ + /* if the standard ratio test is used */ + if (rtol == 0.0) goto done; + /* if no non-basic variable has been chosen on the first pass */ + if (q == 0) goto done; + /* if lambdaN[q] prevents lambdaB[p] from any change */ + if (teta == 0.0) goto done; + /*** SECOND PASS ***/ + /* here tmax is a maximal change of lambdaB[p], on which the + solution remains dual feasible within a tolerance */ +#if 0 + tmax = (1.0 + 10.0 * DBL_EPSILON) * teta; +#else + tmax = teta; +#endif + /* nothing is chosen so far */ + q = 0, teta = DBL_MAX, big = 0.0; + /* walk through significant elements of the pivot row */ + for (pos = 1; pos <= trow_num; pos++) + { j = trow_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + alfa = s * trow_vec[j]; +#ifdef GLP_DEBUG + xassert(alfa != 0.0); +#endif + /* lambdaN[j] = ... - alfa * lambdaB[p] - ..., and due to s we + need to consider only increasing lambdaB[p] */ + if (alfa > 0.0) + { /* lambdaN[j] is decreasing */ + if (stat[j] == GLP_NL || stat[j] == GLP_NF) + { /* lambdaN[j] has zero lower bound */ + t = cbar[j] / alfa; + } + else + { /* lambdaN[j] has no lower bound */ + continue; + } + } + else + { /* lambdaN[j] is increasing */ + if (stat[j] == GLP_NU || stat[j] == GLP_NF) + { /* lambdaN[j] has zero upper bound */ + t = cbar[j] / alfa; + } + else + { /* lambdaN[j] has no upper bound */ + continue; + } + } + /* (see comments for the first pass) */ + if (t < 0.0) t = 0.0; + /* t is a change of lambdaB[p], on which lambdaN[j] reaches + its zero (lower or upper) bound; if t <= tmax, all reduced + costs can violate their zero bounds only within relaxation + tolerance rtol, so we can choose non-basic variable having + largest influence coefficient to avoid possible numerical + instability */ + if (t <= tmax && big < fabs(alfa)) + q = j, teta = t, big = fabs(alfa); + } + /* something must be chosen on the second pass */ + xassert(q != 0); +done: /* store the index of non-basic variable xN[q] chosen */ + csa->q = q; + /* store reduced cost of xN[q] in the adjacent basis */ + csa->new_dq = s * teta; + return; +} + +#if 1 /* copied from primal */ +/*********************************************************************** +* eval_tcol - compute pivot column of the simplex table +* +* This routine computes the pivot column of the simplex table, which +* corresponds to non-basic variable xN[q] chosen. +* +* The pivot column is the following vector: +* +* tcol = T * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], +* +* where B is the current basis matrix, N[q] is a column of the matrix +* (I|-A) corresponding to variable xN[q]. */ + +static void eval_tcol(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *head = csa->head; + int q = csa->q; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + double *h = csa->tcol_vec; + int i, k, nnz; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + k = head[m+q]; /* x[k] = xN[q] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* construct the right-hand side vector h = - N[q] */ + for (i = 1; i <= m; i++) + h[i] = 0.0; + if (k <= m) + { /* N[q] is k-th column of submatrix I */ + h[k] = -1.0; + } + else + { /* N[q] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + h[A_ind[ptr]] = A_val[ptr]; + } + /* solve system B * tcol = h */ + xassert(csa->valid); + bfd_ftran(csa->bfd, tcol_vec); + /* construct sparse pattern of the pivot column */ + nnz = 0; + for (i = 1; i <= m; i++) + { if (tcol_vec[i] != 0.0) + tcol_ind[++nnz] = i; + } + csa->tcol_nnz = nnz; + return; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* refine_tcol - refine pivot column of the simplex table +* +* This routine refines the pivot column of the simplex table assuming +* that it was previously computed by the routine eval_tcol. */ + +static void refine_tcol(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + int *head = csa->head; + int q = csa->q; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + double *h = csa->work3; + int i, k, nnz; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + k = head[m+q]; /* x[k] = xN[q] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* construct the right-hand side vector h = - N[q] */ + for (i = 1; i <= m; i++) + h[i] = 0.0; + if (k <= m) + { /* N[q] is k-th column of submatrix I */ + h[k] = -1.0; + } + else + { /* N[q] is (k-m)-th column of submatrix (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + h[A_ind[ptr]] = A_val[ptr]; + } + /* refine solution of B * tcol = h */ + refine_ftran(csa, h, tcol_vec); + /* construct sparse pattern of the pivot column */ + nnz = 0; + for (i = 1; i <= m; i++) + { if (tcol_vec[i] != 0.0) + tcol_ind[++nnz] = i; + } + csa->tcol_nnz = nnz; + return; +} +#endif + +/*********************************************************************** +* update_cbar - update reduced costs of non-basic variables +* +* This routine updates reduced costs of all (except fixed) non-basic +* variables for the adjacent basis. */ + +static void update_cbar(struct csa *csa) +{ +#ifdef GLP_DEBUG + int n = csa->n; +#endif + double *cbar = csa->cbar; + int trow_nnz = csa->trow_nnz; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int q = csa->q; + double new_dq = csa->new_dq; + int j, pos; +#ifdef GLP_DEBUG + xassert(1 <= q && q <= n); +#endif + /* set new reduced cost of xN[q] */ + cbar[q] = new_dq; + /* update reduced costs of other non-basic variables */ + if (new_dq == 0.0) goto done; + for (pos = 1; pos <= trow_nnz; pos++) + { j = trow_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + if (j != q) + cbar[j] -= trow_vec[j] * new_dq; + } +done: return; +} + +/*********************************************************************** +* update_bbar - update values of basic variables +* +* This routine updates values of all basic variables for the adjacent +* basis. */ + +static void update_bbar(struct csa *csa) +{ +#ifdef GLP_DEBUG + int m = csa->m; + int n = csa->n; +#endif + double *bbar = csa->bbar; + int p = csa->p; + double delta = csa->delta; + int q = csa->q; + int tcol_nnz = csa->tcol_nnz; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + int i, pos; + double teta; +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); + xassert(1 <= q && q <= n); +#endif + /* determine the change of xN[q] in the adjacent basis */ +#ifdef GLP_DEBUG + xassert(tcol_vec[p] != 0.0); +#endif + teta = delta / tcol_vec[p]; + /* set new primal value of xN[q] */ + bbar[p] = get_xN(csa, q) + teta; + /* update primal values of other basic variables */ + if (teta == 0.0) goto done; + for (pos = 1; pos <= tcol_nnz; pos++) + { i = tcol_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); +#endif + if (i != p) + bbar[i] += tcol_vec[i] * teta; + } +done: return; +} + +/*********************************************************************** +* update_gamma - update steepest edge coefficients +* +* This routine updates steepest-edge coefficients for the adjacent +* basis. */ + +static void update_gamma(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + char *type = csa->type; + int *head = csa->head; + char *refsp = csa->refsp; + double *gamma = csa->gamma; + int p = csa->p; + int trow_nnz = csa->trow_nnz; + int *trow_ind = csa->trow_ind; + double *trow_vec = csa->trow_vec; + int q = csa->q; + int tcol_nnz = csa->tcol_nnz; + int *tcol_ind = csa->tcol_ind; + double *tcol_vec = csa->tcol_vec; + double *u = csa->work3; + int i, j, k,pos; + double gamma_p, eta_p, pivot, t, t1, t2; +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); + xassert(1 <= q && q <= n); +#endif + /* the basis changes, so decrease the count */ + xassert(csa->refct > 0); + csa->refct--; + /* recompute gamma[p] for the current basis more accurately and + compute auxiliary vector u */ +#ifdef GLP_DEBUG + xassert(type[head[p]] != GLP_FR); +#endif + gamma_p = eta_p = (refsp[head[p]] ? 1.0 : 0.0); + for (i = 1; i <= m; i++) u[i] = 0.0; + for (pos = 1; pos <= trow_nnz; pos++) + { j = trow_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= j && j <= n); +#endif + k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); + xassert(type[k] != GLP_FX); +#endif + if (!refsp[k]) continue; + t = trow_vec[j]; + gamma_p += t * t; + /* u := u + N[j] * delta[j] * trow[j] */ + if (k <= m) + { /* N[k] = k-j stolbec submatrix I */ + u[k] += t; + } + else + { /* N[k] = k-m-k stolbec (-A) */ + int *A_ptr = csa->A_ptr; + int *A_ind = csa->A_ind; + double *A_val = csa->A_val; + int beg, end, ptr; + beg = A_ptr[k-m]; + end = A_ptr[k-m+1]; + for (ptr = beg; ptr < end; ptr++) + u[A_ind[ptr]] -= t * A_val[ptr]; + } + } + xassert(csa->valid); + bfd_ftran(csa->bfd, u); + /* update gamma[i] for other basic variables (except xB[p] and + free variables) */ + pivot = tcol_vec[p]; +#ifdef GLP_DEBUG + xassert(pivot != 0.0); +#endif + for (pos = 1; pos <= tcol_nnz; pos++) + { i = tcol_ind[pos]; +#ifdef GLP_DEBUG + xassert(1 <= i && i <= m); +#endif + k = head[i]; +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + /* skip xB[p] */ + if (i == p) continue; + /* skip free basic variable */ + if (type[head[i]] == GLP_FR) + { +#ifdef GLP_DEBUG + xassert(gamma[i] == 1.0); +#endif + continue; + } + /* compute gamma[i] for the adjacent basis */ + t = tcol_vec[i] / pivot; + t1 = gamma[i] + t * t * gamma_p + 2.0 * t * u[i]; + t2 = (refsp[k] ? 1.0 : 0.0) + eta_p * t * t; + gamma[i] = (t1 >= t2 ? t1 : t2); + /* (though gamma[i] can be exact zero, because the reference + space does not include non-basic fixed variables) */ + if (gamma[i] < DBL_EPSILON) gamma[i] = DBL_EPSILON; + } + /* compute gamma[p] for the adjacent basis */ + if (type[head[m+q]] == GLP_FR) + gamma[p] = 1.0; + else + { gamma[p] = gamma_p / (pivot * pivot); + if (gamma[p] < DBL_EPSILON) gamma[p] = DBL_EPSILON; + } + /* if xB[p], which becomes xN[q] in the adjacent basis, is fixed + and belongs to the reference space, remove it from there, and + change all gamma's appropriately */ + k = head[p]; + if (type[k] == GLP_FX && refsp[k]) + { refsp[k] = 0; + for (pos = 1; pos <= tcol_nnz; pos++) + { i = tcol_ind[pos]; + if (i == p) + { if (type[head[m+q]] == GLP_FR) continue; + t = 1.0 / tcol_vec[p]; + } + else + { if (type[head[i]] == GLP_FR) continue; + t = tcol_vec[i] / tcol_vec[p]; + } + gamma[i] -= t * t; + if (gamma[i] < DBL_EPSILON) gamma[i] = DBL_EPSILON; + } + } + return; +} + +#if 1 /* copied from primal */ +/*********************************************************************** +* err_in_bbar - compute maximal relative error in primal solution +* +* This routine returns maximal relative error: +* +* max |beta[i] - bbar[i]| / (1 + |beta[i]|), +* +* where beta and bbar are, respectively, directly computed and the +* current (updated) values of basic variables. +* +* NOTE: The routine is intended only for debugginig purposes. */ + +static double err_in_bbar(struct csa *csa) +{ int m = csa->m; + double *bbar = csa->bbar; + int i; + double e, emax, *beta; + beta = xcalloc(1+m, sizeof(double)); + eval_beta(csa, beta); + emax = 0.0; + for (i = 1; i <= m; i++) + { e = fabs(beta[i] - bbar[i]) / (1.0 + fabs(beta[i])); + if (emax < e) emax = e; + } + xfree(beta); + return emax; +} +#endif + +#if 1 /* copied from primal */ +/*********************************************************************** +* err_in_cbar - compute maximal relative error in dual solution +* +* This routine returns maximal relative error: +* +* max |cost[j] - cbar[j]| / (1 + |cost[j]|), +* +* where cost and cbar are, respectively, directly computed and the +* current (updated) reduced costs of non-basic non-fixed variables. +* +* NOTE: The routine is intended only for debugginig purposes. */ + +static double err_in_cbar(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + char *stat = csa->stat; + double *cbar = csa->cbar; + int j; + double e, emax, cost, *pi; + pi = xcalloc(1+m, sizeof(double)); + eval_pi(csa, pi); + emax = 0.0; + for (j = 1; j <= n; j++) + { if (stat[j] == GLP_NS) continue; + cost = eval_cost(csa, pi, j); + e = fabs(cost - cbar[j]) / (1.0 + fabs(cost)); + if (emax < e) emax = e; + } + xfree(pi); + return emax; +} +#endif + +/*********************************************************************** +* err_in_gamma - compute maximal relative error in steepest edge cff. +* +* This routine returns maximal relative error: +* +* max |gamma'[j] - gamma[j]| / (1 + |gamma'[j]), +* +* where gamma'[j] and gamma[j] are, respectively, directly computed +* and the current (updated) steepest edge coefficients for non-basic +* non-fixed variable x[j]. +* +* NOTE: The routine is intended only for debugginig purposes. */ + +static double err_in_gamma(struct csa *csa) +{ int m = csa->m; + char *type = csa->type; + int *head = csa->head; + double *gamma = csa->gamma; + double *exact = csa->work4; + int i; + double e, emax, temp; + eval_gamma(csa, exact); + emax = 0.0; + for (i = 1; i <= m; i++) + { if (type[head[i]] == GLP_FR) + { xassert(gamma[i] == 1.0); + xassert(exact[i] == 1.0); + continue; + } + temp = exact[i]; + e = fabs(temp - gamma[i]) / (1.0 + fabs(temp)); + if (emax < e) emax = e; + } + return emax; +} + +/*********************************************************************** +* change_basis - change basis header +* +* This routine changes the basis header to make it corresponding to +* the adjacent basis. */ + +static void change_basis(struct csa *csa) +{ int m = csa->m; +#ifdef GLP_DEBUG + int n = csa->n; +#endif + char *type = csa->type; + int *head = csa->head; +#if 1 /* 06/IV-2009 */ + int *bind = csa->bind; +#endif + char *stat = csa->stat; + int p = csa->p; + double delta = csa->delta; + int q = csa->q; + int k; + /* xB[p] leaves the basis, xN[q] enters the basis */ +#ifdef GLP_DEBUG + xassert(1 <= p && p <= m); + xassert(1 <= q && q <= n); +#endif + /* xB[p] <-> xN[q] */ + k = head[p], head[p] = head[m+q], head[m+q] = k; +#if 1 /* 06/IV-2009 */ + bind[head[p]] = p, bind[head[m+q]] = m + q; +#endif + if (type[k] == GLP_FX) + stat[q] = GLP_NS; + else if (delta > 0.0) + { +#ifdef GLP_DEBUG + xassert(type[k] == GLP_LO || type[k] == GLP_DB); +#endif + stat[q] = GLP_NL; + } + else /* delta < 0.0 */ + { +#ifdef GLP_DEBUG + xassert(type[k] == GLP_UP || type[k] == GLP_DB); +#endif + stat[q] = GLP_NU; + } + return; +} + +/*********************************************************************** +* check_feas - check dual feasibility of basic solution +* +* If the current basic solution is dual feasible within a tolerance, +* this routine returns zero, otherwise it returns non-zero. */ + +static int check_feas(struct csa *csa, double tol_dj) +{ int m = csa->m; + int n = csa->n; + char *orig_type = csa->orig_type; + int *head = csa->head; + double *cbar = csa->cbar; + int j, k; + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (cbar[j] < - tol_dj) + if (orig_type[k] == GLP_LO || orig_type[k] == GLP_FR) + return 1; + if (cbar[j] > + tol_dj) + if (orig_type[k] == GLP_UP || orig_type[k] == GLP_FR) + return 1; + } + return 0; +} + +/*********************************************************************** +* set_aux_bnds - assign auxiliary bounds to variables +* +* This routine assigns auxiliary bounds to variables to construct an +* LP problem solved on phase I. */ + +static void set_aux_bnds(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + char *orig_type = csa->orig_type; + int *head = csa->head; + char *stat = csa->stat; + double *cbar = csa->cbar; + int j, k; + for (k = 1; k <= m+n; k++) + { switch (orig_type[k]) + { case GLP_FR: +#if 0 + type[k] = GLP_DB, lb[k] = -1.0, ub[k] = +1.0; +#else + /* to force free variables to enter the basis */ + type[k] = GLP_DB, lb[k] = -1e3, ub[k] = +1e3; +#endif + break; + case GLP_LO: + type[k] = GLP_DB, lb[k] = 0.0, ub[k] = +1.0; + break; + case GLP_UP: + type[k] = GLP_DB, lb[k] = -1.0, ub[k] = 0.0; + break; + case GLP_DB: + case GLP_FX: + type[k] = GLP_FX, lb[k] = ub[k] = 0.0; + break; + default: + xassert(orig_type != orig_type); + } + } + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (type[k] == GLP_FX) + stat[j] = GLP_NS; + else if (cbar[j] >= 0.0) + stat[j] = GLP_NL; + else + stat[j] = GLP_NU; + } + return; +} + +/*********************************************************************** +* set_orig_bnds - restore original bounds of variables +* +* This routine restores original types and bounds of variables and +* determines statuses of non-basic variables assuming that the current +* basis is dual feasible. */ + +static void set_orig_bnds(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + char *type = csa->type; + double *lb = csa->lb; + double *ub = csa->ub; + char *orig_type = csa->orig_type; + double *orig_lb = csa->orig_lb; + double *orig_ub = csa->orig_ub; + int *head = csa->head; + char *stat = csa->stat; + double *cbar = csa->cbar; + int j, k; + memcpy(&type[1], &orig_type[1], (m+n) * sizeof(char)); + memcpy(&lb[1], &orig_lb[1], (m+n) * sizeof(double)); + memcpy(&ub[1], &orig_ub[1], (m+n) * sizeof(double)); + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + switch (type[k]) + { case GLP_FR: + stat[j] = GLP_NF; + break; + case GLP_LO: + stat[j] = GLP_NL; + break; + case GLP_UP: + stat[j] = GLP_NU; + break; + case GLP_DB: + if (cbar[j] >= +DBL_EPSILON) + stat[j] = GLP_NL; + else if (cbar[j] <= -DBL_EPSILON) + stat[j] = GLP_NU; + else if (fabs(lb[k]) <= fabs(ub[k])) + stat[j] = GLP_NL; + else + stat[j] = GLP_NU; + break; + case GLP_FX: + stat[j] = GLP_NS; + break; + default: + xassert(type != type); + } + } + return; +} + +/*********************************************************************** +* check_stab - check numerical stability of basic solution +* +* If the current basic solution is dual feasible within a tolerance, +* this routine returns zero, otherwise it returns non-zero. */ + +static int check_stab(struct csa *csa, double tol_dj) +{ int n = csa->n; + char *stat = csa->stat; + double *cbar = csa->cbar; + int j; + for (j = 1; j <= n; j++) + { if (cbar[j] < - tol_dj) + if (stat[j] == GLP_NL || stat[j] == GLP_NF) return 1; + if (cbar[j] > + tol_dj) + if (stat[j] == GLP_NU || stat[j] == GLP_NF) return 1; + } + return 0; +} + +#if 1 /* copied from primal */ +/*********************************************************************** +* eval_obj - compute original objective function +* +* This routine computes the current value of the original objective +* function. */ + +static double eval_obj(struct csa *csa) +{ int m = csa->m; + int n = csa->n; + double *obj = csa->obj; + int *head = csa->head; + double *bbar = csa->bbar; + int i, j, k; + double sum; + sum = obj[0]; + /* walk through the list of basic variables */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k > m) + sum += obj[k-m] * bbar[i]; + } + /* walk through the list of non-basic variables */ + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k > m) + sum += obj[k-m] * get_xN(csa, j); + } + return sum; +} +#endif + +/*********************************************************************** +* display - display the search progress +* +* This routine displays some information about the search progress. */ + +static void display(struct csa *csa, const glp_smcp *parm, int spec) +{ int m = csa->m; + int n = csa->n; + double *coef = csa->coef; + char *orig_type = csa->orig_type; + int *head = csa->head; + char *stat = csa->stat; + int phase = csa->phase; + double *bbar = csa->bbar; + double *cbar = csa->cbar; + int i, j, cnt; + double sum; + if (parm->msg_lev < GLP_MSG_ON) goto skip; + if (parm->out_dly > 0 && + 1000.0 * xdifftime(xtime(), csa->tm_beg) < parm->out_dly) + goto skip; + if (csa->it_cnt == csa->it_dpy) goto skip; + if (!spec && csa->it_cnt % parm->out_frq != 0) goto skip; + /* compute the sum of dual infeasibilities */ + sum = 0.0; + if (phase == 1) + { for (i = 1; i <= m; i++) + sum -= coef[head[i]] * bbar[i]; + for (j = 1; j <= n; j++) + sum -= coef[head[m+j]] * get_xN(csa, j); + } + else + { for (j = 1; j <= n; j++) + { if (cbar[j] < 0.0) + if (stat[j] == GLP_NL || stat[j] == GLP_NF) + sum -= cbar[j]; + if (cbar[j] > 0.0) + if (stat[j] == GLP_NU || stat[j] == GLP_NF) + sum += cbar[j]; + } + } + /* determine the number of basic fixed variables */ + cnt = 0; + for (i = 1; i <= m; i++) + if (orig_type[head[i]] == GLP_FX) cnt++; + if (csa->phase == 1) + xprintf(" %6d: %24s infeas = %10.3e (%d)\n", + csa->it_cnt, "", sum, cnt); + else + xprintf("|%6d: obj = %17.9e infeas = %10.3e (%d)\n", + csa->it_cnt, eval_obj(csa), sum, cnt); + csa->it_dpy = csa->it_cnt; +skip: return; +} + +#if 1 /* copied from primal */ +/*********************************************************************** +* store_sol - store basic solution back to the problem object +* +* This routine stores basic solution components back to the problem +* object. */ + +static void store_sol(struct csa *csa, glp_prob *lp, int p_stat, + int d_stat, int ray) +{ int m = csa->m; + int n = csa->n; + double zeta = csa->zeta; + int *head = csa->head; + char *stat = csa->stat; + double *bbar = csa->bbar; + double *cbar = csa->cbar; + int i, j, k; +#ifdef GLP_DEBUG + xassert(lp->m == m); + xassert(lp->n == n); +#endif + /* basis factorization */ +#ifdef GLP_DEBUG + xassert(!lp->valid && lp->bfd == NULL); + xassert(csa->valid && csa->bfd != NULL); +#endif + lp->valid = 1, csa->valid = 0; + lp->bfd = csa->bfd, csa->bfd = NULL; + memcpy(&lp->head[1], &head[1], m * sizeof(int)); + /* basic solution status */ + lp->pbs_stat = p_stat; + lp->dbs_stat = d_stat; + /* objective function value */ + lp->obj_val = eval_obj(csa); + /* simplex iteration count */ + lp->it_cnt = csa->it_cnt; + /* unbounded ray */ + lp->some = ray; + /* basic variables */ + for (i = 1; i <= m; i++) + { k = head[i]; /* x[k] = xB[i] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { GLPROW *row = lp->row[k]; + row->stat = GLP_BS; + row->bind = i; + row->prim = bbar[i] / row->rii; + row->dual = 0.0; + } + else + { GLPCOL *col = lp->col[k-m]; + col->stat = GLP_BS; + col->bind = i; + col->prim = bbar[i] * col->sjj; + col->dual = 0.0; + } + } + /* non-basic variables */ + for (j = 1; j <= n; j++) + { k = head[m+j]; /* x[k] = xN[j] */ +#ifdef GLP_DEBUG + xassert(1 <= k && k <= m+n); +#endif + if (k <= m) + { GLPROW *row = lp->row[k]; + row->stat = stat[j]; + row->bind = 0; +#if 0 + row->prim = get_xN(csa, j) / row->rii; +#else + switch (stat[j]) + { case GLP_NL: + row->prim = row->lb; break; + case GLP_NU: + row->prim = row->ub; break; + case GLP_NF: + row->prim = 0.0; break; + case GLP_NS: + row->prim = row->lb; break; + default: + xassert(stat != stat); + } +#endif + row->dual = (cbar[j] * row->rii) / zeta; + } + else + { GLPCOL *col = lp->col[k-m]; + col->stat = stat[j]; + col->bind = 0; +#if 0 + col->prim = get_xN(csa, j) * col->sjj; +#else + switch (stat[j]) + { case GLP_NL: + col->prim = col->lb; break; + case GLP_NU: + col->prim = col->ub; break; + case GLP_NF: + col->prim = 0.0; break; + case GLP_NS: + col->prim = col->lb; break; + default: + xassert(stat != stat); + } +#endif + col->dual = (cbar[j] / col->sjj) / zeta; + } + } + return; +} +#endif + +/*********************************************************************** +* free_csa - deallocate common storage area +* +* This routine frees all the memory allocated to arrays in the common +* storage area (CSA). */ + +static void free_csa(struct csa *csa) +{ xfree(csa->type); + xfree(csa->lb); + xfree(csa->ub); + xfree(csa->coef); + xfree(csa->orig_type); + xfree(csa->orig_lb); + xfree(csa->orig_ub); + xfree(csa->obj); + xfree(csa->A_ptr); + xfree(csa->A_ind); + xfree(csa->A_val); +#if 1 /* 06/IV-2009 */ + xfree(csa->AT_ptr); + xfree(csa->AT_ind); + xfree(csa->AT_val); +#endif + xfree(csa->head); +#if 1 /* 06/IV-2009 */ + xfree(csa->bind); +#endif + xfree(csa->stat); +#if 0 /* 06/IV-2009 */ + xfree(csa->N_ptr); + xfree(csa->N_len); + xfree(csa->N_ind); + xfree(csa->N_val); +#endif + xfree(csa->bbar); + xfree(csa->cbar); + xfree(csa->refsp); + xfree(csa->gamma); + xfree(csa->trow_ind); + xfree(csa->trow_vec); +#ifdef GLP_LONG_STEP /* 07/IV-2009 */ + xfree(csa->bkpt); +#endif + xfree(csa->tcol_ind); + xfree(csa->tcol_vec); + xfree(csa->work1); + xfree(csa->work2); + xfree(csa->work3); + xfree(csa->work4); + xfree(csa); + return; +} + +/*********************************************************************** +* spx_dual - core LP solver based on the dual simplex method +* +* SYNOPSIS +* +* #include "glpspx.h" +* int spx_dual(glp_prob *lp, const glp_smcp *parm); +* +* DESCRIPTION +* +* The routine spx_dual is a core LP solver based on the two-phase dual +* simplex method. +* +* RETURNS +* +* 0 LP instance has been successfully solved. +* +* GLP_EOBJLL +* Objective lower limit has been reached (maximization). +* +* GLP_EOBJUL +* Objective upper limit has been reached (minimization). +* +* GLP_EITLIM +* Iteration limit has been exhausted. +* +* GLP_ETMLIM +* Time limit has been exhausted. +* +* GLP_EFAIL +* The solver failed to solve LP instance. */ + +int spx_dual(glp_prob *lp, const glp_smcp *parm) +{ struct csa *csa; + int binv_st = 2; + /* status of basis matrix factorization: + 0 - invalid; 1 - just computed; 2 - updated */ + int bbar_st = 0; + /* status of primal values of basic variables: + 0 - invalid; 1 - just computed; 2 - updated */ + int cbar_st = 0; + /* status of reduced costs of non-basic variables: + 0 - invalid; 1 - just computed; 2 - updated */ + int rigorous = 0; + /* rigorous mode flag; this flag is used to enable iterative + refinement on computing pivot rows and columns of the simplex + table */ + int check = 0; + int p_stat, d_stat, ret; + /* allocate and initialize the common storage area */ + csa = alloc_csa(lp); + init_csa(csa, lp); + if (parm->msg_lev >= GLP_MSG_DBG) + xprintf("Objective scale factor = %g\n", csa->zeta); +loop: /* main loop starts here */ + /* compute factorization of the basis matrix */ + if (binv_st == 0) + { ret = invert_B(csa); + if (ret != 0) + { if (parm->msg_lev >= GLP_MSG_ERR) + { xprintf("Error: unable to factorize the basis matrix (%d" + ")\n", ret); + xprintf("Sorry, basis recovery procedure not implemented" + " yet\n"); + } + xassert(!lp->valid && lp->bfd == NULL); + lp->bfd = csa->bfd, csa->bfd = NULL; + lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; + lp->obj_val = 0.0; + lp->it_cnt = csa->it_cnt; + lp->some = 0; + ret = GLP_EFAIL; + goto done; + } + csa->valid = 1; + binv_st = 1; /* just computed */ + /* invalidate basic solution components */ + bbar_st = cbar_st = 0; + } + /* compute reduced costs of non-basic variables */ + if (cbar_st == 0) + { eval_cbar(csa); + cbar_st = 1; /* just computed */ + /* determine the search phase, if not determined yet */ + if (csa->phase == 0) + { if (check_feas(csa, 0.90 * parm->tol_dj) != 0) + { /* current basic solution is dual infeasible */ + /* start searching for dual feasible solution */ + csa->phase = 1; + set_aux_bnds(csa); + } + else + { /* current basic solution is dual feasible */ + /* start searching for optimal solution */ + csa->phase = 2; + set_orig_bnds(csa); + } + xassert(check_stab(csa, parm->tol_dj) == 0); + /* some non-basic double-bounded variables might become + fixed (on phase I) or vice versa (on phase II) */ +#if 0 /* 06/IV-2009 */ + build_N(csa); +#endif + csa->refct = 0; + /* bounds of non-basic variables have been changed, so + invalidate primal values */ + bbar_st = 0; + } + /* make sure that the current basic solution remains dual + feasible */ + if (check_stab(csa, parm->tol_dj) != 0) + { if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: numerical instability (dual simplex, p" + "hase %s)\n", csa->phase == 1 ? "I" : "II"); +#if 1 + if (parm->meth == GLP_DUALP) + { store_sol(csa, lp, GLP_UNDEF, GLP_UNDEF, 0); + ret = GLP_EFAIL; + goto done; + } +#endif + /* restart the search */ + csa->phase = 0; + binv_st = 0; + rigorous = 5; + goto loop; + } + } + xassert(csa->phase == 1 || csa->phase == 2); + /* on phase I we do not need to wait until the current basic + solution becomes primal feasible; it is sufficient to make + sure that all reduced costs have correct signs */ + if (csa->phase == 1 && check_feas(csa, parm->tol_dj) == 0) + { /* the current basis is dual feasible; switch to phase II */ + display(csa, parm, 1); + csa->phase = 2; + if (cbar_st != 1) + { eval_cbar(csa); + cbar_st = 1; + } + set_orig_bnds(csa); +#if 0 /* 06/IV-2009 */ + build_N(csa); +#endif + csa->refct = 0; + bbar_st = 0; + } + /* compute primal values of basic variables */ + if (bbar_st == 0) + { eval_bbar(csa); + if (csa->phase == 2) + csa->bbar[0] = eval_obj(csa); + bbar_st = 1; /* just computed */ + } + /* redefine the reference space, if required */ + switch (parm->pricing) + { case GLP_PT_STD: + break; + case GLP_PT_PSE: + if (csa->refct == 0) reset_refsp(csa); + break; + default: + xassert(parm != parm); + } + /* at this point the basis factorization and all basic solution + components are valid */ + xassert(binv_st && bbar_st && cbar_st); + /* check accuracy of current basic solution components (only for + debugging) */ + if (check) + { double e_bbar = err_in_bbar(csa); + double e_cbar = err_in_cbar(csa); + double e_gamma = + (parm->pricing == GLP_PT_PSE ? err_in_gamma(csa) : 0.0); + xprintf("e_bbar = %10.3e; e_cbar = %10.3e; e_gamma = %10.3e\n", + e_bbar, e_cbar, e_gamma); + xassert(e_bbar <= 1e-5 && e_cbar <= 1e-5 && e_gamma <= 1e-3); + } + /* if the objective has to be maximized, check if it has reached + its lower limit */ + if (csa->phase == 2 && csa->zeta < 0.0 && + parm->obj_ll > -DBL_MAX && csa->bbar[0] <= parm->obj_ll) + { if (bbar_st != 1 || cbar_st != 1) + { if (bbar_st != 1) bbar_st = 0; + if (cbar_st != 1) cbar_st = 0; + goto loop; + } + display(csa, parm, 1); + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("OBJECTIVE LOWER LIMIT REACHED; SEARCH TERMINATED\n" + ); + store_sol(csa, lp, GLP_INFEAS, GLP_FEAS, 0); + ret = GLP_EOBJLL; + goto done; + } + /* if the objective has to be minimized, check if it has reached + its upper limit */ + if (csa->phase == 2 && csa->zeta > 0.0 && + parm->obj_ul < +DBL_MAX && csa->bbar[0] >= parm->obj_ul) + { if (bbar_st != 1 || cbar_st != 1) + { if (bbar_st != 1) bbar_st = 0; + if (cbar_st != 1) cbar_st = 0; + goto loop; + } + display(csa, parm, 1); + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("OBJECTIVE UPPER LIMIT REACHED; SEARCH TERMINATED\n" + ); + store_sol(csa, lp, GLP_INFEAS, GLP_FEAS, 0); + ret = GLP_EOBJUL; + goto done; + } + /* check if the iteration limit has been exhausted */ + if (parm->it_lim < INT_MAX && + csa->it_cnt - csa->it_beg >= parm->it_lim) + { if (csa->phase == 2 && bbar_st != 1 || cbar_st != 1) + { if (csa->phase == 2 && bbar_st != 1) bbar_st = 0; + if (cbar_st != 1) cbar_st = 0; + goto loop; + } + display(csa, parm, 1); + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED\n"); + switch (csa->phase) + { case 1: + d_stat = GLP_INFEAS; + set_orig_bnds(csa); + eval_bbar(csa); + break; + case 2: + d_stat = GLP_FEAS; + break; + default: + xassert(csa != csa); + } + store_sol(csa, lp, GLP_INFEAS, d_stat, 0); + ret = GLP_EITLIM; + goto done; + } + /* check if the time limit has been exhausted */ + if (parm->tm_lim < INT_MAX && + 1000.0 * xdifftime(xtime(), csa->tm_beg) >= parm->tm_lim) + { if (csa->phase == 2 && bbar_st != 1 || cbar_st != 1) + { if (csa->phase == 2 && bbar_st != 1) bbar_st = 0; + if (cbar_st != 1) cbar_st = 0; + goto loop; + } + display(csa, parm, 1); + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); + switch (csa->phase) + { case 1: + d_stat = GLP_INFEAS; + set_orig_bnds(csa); + eval_bbar(csa); + break; + case 2: + d_stat = GLP_FEAS; + break; + default: + xassert(csa != csa); + } + store_sol(csa, lp, GLP_INFEAS, d_stat, 0); + ret = GLP_ETMLIM; + goto done; + } + /* display the search progress */ + display(csa, parm, 0); + /* choose basic variable xB[p] */ + chuzr(csa, parm->tol_bnd); + if (csa->p == 0) + { if (bbar_st != 1 || cbar_st != 1) + { if (bbar_st != 1) bbar_st = 0; + if (cbar_st != 1) cbar_st = 0; + goto loop; + } + display(csa, parm, 1); + switch (csa->phase) + { case 1: + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS NO DUAL FEASIBLE SOLUTION\n"); + set_orig_bnds(csa); + eval_bbar(csa); + p_stat = GLP_INFEAS, d_stat = GLP_NOFEAS; + break; + case 2: + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("OPTIMAL SOLUTION FOUND\n"); + p_stat = d_stat = GLP_FEAS; + break; + default: + xassert(csa != csa); + } + store_sol(csa, lp, p_stat, d_stat, 0); + ret = 0; + goto done; + } + /* compute pivot row of the simplex table */ + { double *rho = csa->work4; + eval_rho(csa, rho); + if (rigorous) refine_rho(csa, rho); + eval_trow(csa, rho); + sort_trow(csa, parm->tol_bnd); + } + /* unlike primal simplex there is no need to check accuracy of + the primal value of xB[p] (which might be computed using the + pivot row), since bbar is a result of FTRAN */ +#ifdef GLP_LONG_STEP /* 07/IV-2009 */ + long_step(csa); + if (csa->nbps > 0) + { csa->q = csa->bkpt[csa->nbps].j; + if (csa->delta > 0.0) + csa->new_dq = + csa->bkpt[csa->nbps].t; + else + csa->new_dq = - csa->bkpt[csa->nbps].t; + } + else +#endif + /* choose non-basic variable xN[q] */ + switch (parm->r_test) + { case GLP_RT_STD: + chuzc(csa, 0.0); + break; + case GLP_RT_HAR: + chuzc(csa, 0.30 * parm->tol_dj); + break; + default: + xassert(parm != parm); + } + if (csa->q == 0) + { if (bbar_st != 1 || cbar_st != 1 || !rigorous) + { if (bbar_st != 1) bbar_st = 0; + if (cbar_st != 1) cbar_st = 0; + rigorous = 1; + goto loop; + } + display(csa, parm, 1); + switch (csa->phase) + { case 1: + if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("Error: unable to choose basic variable on ph" + "ase I\n"); + xassert(!lp->valid && lp->bfd == NULL); + lp->bfd = csa->bfd, csa->bfd = NULL; + lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; + lp->obj_val = 0.0; + lp->it_cnt = csa->it_cnt; + lp->some = 0; + ret = GLP_EFAIL; + break; + case 2: + if (parm->msg_lev >= GLP_MSG_ALL) + xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n"); + store_sol(csa, lp, GLP_NOFEAS, GLP_FEAS, + csa->head[csa->p]); + ret = 0; + break; + default: + xassert(csa != csa); + } + goto done; + } + /* check if the pivot element is acceptable */ + { double piv = csa->trow_vec[csa->q]; + double eps = 1e-5 * (1.0 + 0.01 * csa->trow_max); + if (fabs(piv) < eps) + { if (parm->msg_lev >= GLP_MSG_DBG) + xprintf("piv = %.12g; eps = %g\n", piv, eps); + if (!rigorous) + { rigorous = 5; + goto loop; + } + } + } + /* now xN[q] and xB[p] have been chosen anyhow */ + /* compute pivot column of the simplex table */ + eval_tcol(csa); + if (rigorous) refine_tcol(csa); + /* accuracy check based on the pivot element */ + { double piv1 = csa->tcol_vec[csa->p]; /* more accurate */ + double piv2 = csa->trow_vec[csa->q]; /* less accurate */ + xassert(piv1 != 0.0); + if (fabs(piv1 - piv2) > 1e-8 * (1.0 + fabs(piv1)) || + !(piv1 > 0.0 && piv2 > 0.0 || piv1 < 0.0 && piv2 < 0.0)) + { if (parm->msg_lev >= GLP_MSG_DBG) + xprintf("piv1 = %.12g; piv2 = %.12g\n", piv1, piv2); + if (binv_st != 1 || !rigorous) + { if (binv_st != 1) binv_st = 0; + rigorous = 5; + goto loop; + } + /* (not a good idea; should be revised later) */ + if (csa->tcol_vec[csa->p] == 0.0) + { csa->tcol_nnz++; + xassert(csa->tcol_nnz <= csa->m); + csa->tcol_ind[csa->tcol_nnz] = csa->p; + } + csa->tcol_vec[csa->p] = piv2; + } + } + /* update primal values of basic variables */ +#ifdef GLP_LONG_STEP /* 07/IV-2009 */ + if (csa->nbps > 0) + { int kk, j, k; + for (kk = 1; kk < csa->nbps; kk++) + { if (csa->bkpt[kk].t >= csa->bkpt[csa->nbps].t) continue; + j = csa->bkpt[kk].j; + k = csa->head[csa->m + j]; + xassert(csa->type[k] == GLP_DB); + if (csa->stat[j] == GLP_NL) + csa->stat[j] = GLP_NU; + else + csa->stat[j] = GLP_NL; + } + } + bbar_st = 0; +#else + update_bbar(csa); + if (csa->phase == 2) + csa->bbar[0] += (csa->cbar[csa->q] / csa->zeta) * + (csa->delta / csa->tcol_vec[csa->p]); + bbar_st = 2; /* updated */ +#endif + /* update reduced costs of non-basic variables */ + update_cbar(csa); + cbar_st = 2; /* updated */ + /* update steepest edge coefficients */ + switch (parm->pricing) + { case GLP_PT_STD: + break; + case GLP_PT_PSE: + if (csa->refct > 0) update_gamma(csa); + break; + default: + xassert(parm != parm); + } + /* update factorization of the basis matrix */ + ret = update_B(csa, csa->p, csa->head[csa->m+csa->q]); + if (ret == 0) + binv_st = 2; /* updated */ + else + { csa->valid = 0; + binv_st = 0; /* invalid */ + } +#if 0 /* 06/IV-2009 */ + /* update matrix N */ + del_N_col(csa, csa->q, csa->head[csa->m+csa->q]); + if (csa->type[csa->head[csa->p]] != GLP_FX) + add_N_col(csa, csa->q, csa->head[csa->p]); +#endif + /* change the basis header */ + change_basis(csa); + /* iteration complete */ + csa->it_cnt++; + if (rigorous > 0) rigorous--; + goto loop; +done: /* deallocate the common storage area */ + free_csa(csa); + /* return to the calling program */ + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpsql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpsql.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1627 @@ +/* glpsql.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Author: Heinrich Schuchardt . +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glpmpl.h" +#include "glpsql.h" + +#ifdef ODBC_DLNAME +#define HAVE_ODBC +#define libodbc ODBC_DLNAME +#define h_odbc (get_env_ptr()->h_odbc) +#endif + +#ifdef MYSQL_DLNAME +#define HAVE_MYSQL +#define libmysql MYSQL_DLNAME +#define h_mysql (get_env_ptr()->h_mysql) +#endif + +static void *db_iodbc_open_int(TABDCA *dca, int mode, const char + **sqllines); +static void *db_mysql_open_int(TABDCA *dca, int mode, const char + **sqllines); + +/**********************************************************************/ + +#if defined(HAVE_ODBC) || defined(HAVE_MYSQL) + +#define SQL_FIELD_MAX 100 +/* maximal field count */ + +#define SQL_FDLEN_MAX 255 +/* maximal field length */ + +/*********************************************************************** +* NAME +* +* args_concat - concatenate arguments +* +* SYNOPSIS +* +* static char **args_concat(TABDCA *dca); +* +* DESCRIPTION +* +* The arguments passed in dca are SQL statements. A SQL statement may +* be split over multiple arguments. The last argument of a SQL +* statement will be terminated with a semilocon. Each SQL statement is +* merged into a single zero terminated string. Boundaries between +* arguments are replaced by space. +* +* RETURNS +* +* Buffer with SQL statements */ + +static char **args_concat(TABDCA *dca) +{ + const char *arg; + int i; + int j; + int j0; + int j1; + int len; + int lentot; + int narg; + int nline = 0; + void *ret; + char **sqllines = NULL; + + narg = mpl_tab_num_args(dca); + /* The SQL statements start with argument 3. */ + if (narg < 3) + return NULL; + /* Count the SQL statements */ + for (j = 3; j <= narg; j++) + { + arg = mpl_tab_get_arg(dca, j); + len = strlen(arg); + if (arg[len-1] == ';' || j == narg) + nline ++; + } + /* Allocate string buffer. */ + sqllines = (char **) xmalloc((nline+1) * sizeof(char **)); + /* Join arguments */ + sqllines[0] = NULL; + j0 = 3; + i = 0; + lentot = 0; + for (j = 3; j <= narg; j++) + { + arg = mpl_tab_get_arg(dca, j); + len = strlen(arg); + lentot += len; + if (arg[len-1] == ';' || j == narg) + { /* Join arguments for a single SQL statement */ + sqllines[i] = xmalloc(lentot+1); + sqllines[i+1] = NULL; + sqllines[i][0] = 0x00; + for (j1 = j0; j1 <= j; j1++) + { if(j1>j0) + strcat(sqllines[i], " "); + strcat(sqllines[i], mpl_tab_get_arg(dca, j1)); + } + len = strlen(sqllines[i]); + if (sqllines[i][len-1] == ';') + sqllines[i][len-1] = 0x00; + j0 = j+1; + i++; + lentot = 0; + } + } + return sqllines; +} + +/*********************************************************************** +* NAME +* +* free_buffer - free multiline string buffer +* +* SYNOPSIS +* +* static void free_buffer(char **buf); +* +* DESCRIPTION +* +* buf is a list of strings terminated by NULL. +* The memory for the strings and for the list is released. */ + +static void free_buffer(char **buf) +{ int i; + + for(i = 0; buf[i] != NULL; i++) + xfree(buf[i]); + xfree(buf); +} + +static int db_escaped_string_length(const char* from) +/* length of escaped string */ +{ + int count; + const char *pointer; + + for (pointer = from, count = 0; *pointer != (char) '\0'; pointer++, + count++) + { + switch (*pointer) + { + case '\'': + count++; + break; + } + } + + return count; +} + +static int db_escape_string (char *to, const char *from) +/* escape string*/ +{ + const char *source = from; + char *target = to; + unsigned int remaining; + + remaining = strlen(from); + + if (to == NULL) + to = (char *) (from + remaining); + + while (remaining > 0) + { + switch (*source) + { + case '\'': + *target = '\''; + target++; + *target = '\''; + break; + + default: + *target = *source; + } + source++; + target++; + remaining--; + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} + +static char *db_generate_select_stmt(TABDCA *dca) +/* generate select statement */ +{ + char *arg; + char const *field; + char *query; + int j; + int narg; + int nf; + int total; + + total = 50; + nf = mpl_tab_num_flds(dca); + narg = mpl_tab_num_args(dca); + for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) + { + field = mpl_tab_get_name(dca, j); + total += strlen(field); + total += 2; + } + arg = (char *) mpl_tab_get_arg(dca, narg); + total += strlen(arg); + query = xmalloc( total * sizeof(char)); + strcpy (query, "SELECT "); + for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) + { + field = mpl_tab_get_name(dca, j); + strcat(query, field); + if ( j < nf ) + strcat(query, ", "); + } + strcat(query, " FROM "); + strcat(query, arg); + return query; +} + +static char *db_generate_insert_stmt(TABDCA *dca) +/* generate insert statement */ +{ + char *arg; + char const *field; + char *query; + int j; + int narg; + int nf; + int total; + + total = 50; + nf = mpl_tab_num_flds(dca); + narg = mpl_tab_num_args(dca); + for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) + { + field = mpl_tab_get_name(dca, j); + total += strlen(field); + total += 5; + } + arg = (char *) mpl_tab_get_arg(dca, narg); + total += strlen(arg); + query = xmalloc( (total+1) * sizeof(char)); + strcpy (query, "INSERT INTO "); + strcat(query, arg); + strcat(query, " ( "); + for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) + { + field = mpl_tab_get_name(dca, j); + strcat(query, field); + if ( j < nf ) + strcat(query, ", "); + } + strcat(query, " ) VALUES ( "); + for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) + { + strcat(query, "?"); + if ( j < nf ) + strcat(query, ", "); + } + strcat(query, " )"); + return query; +} + +#endif + +/**********************************************************************/ + +#ifndef HAVE_ODBC + +void *db_iodbc_open(TABDCA *dca, int mode) +{ xassert(dca == dca); + xassert(mode == mode); + xprintf("iODBC table driver not supported\n"); + return NULL; +} + +int db_iodbc_read(TABDCA *dca, void *link) +{ xassert(dca != dca); + xassert(link != link); + return 0; +} + +int db_iodbc_write(TABDCA *dca, void *link) +{ xassert(dca != dca); + xassert(link != link); + return 0; +} + +int db_iodbc_close(TABDCA *dca, void *link) +{ xassert(dca != dca); + xassert(link != link); + return 0; +} + +#else + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__WOE__) +#include +#endif + +#include +#include + +struct db_odbc +{ + int mode; /*'R' = Read, 'W' = Write*/ + SQLHDBC hdbc; /*connection handle*/ + SQLHENV henv; /*environment handle*/ + SQLHSTMT hstmt; /*statement handle*/ + SQLSMALLINT nresultcols; /* columns in result*/ + SQLULEN collen[SQL_FIELD_MAX+1]; + SQLLEN outlen[SQL_FIELD_MAX+1]; + SQLSMALLINT coltype[SQL_FIELD_MAX+1]; + SQLCHAR data[SQL_FIELD_MAX+1][SQL_FDLEN_MAX+1]; + SQLCHAR colname[SQL_FIELD_MAX+1][SQL_FDLEN_MAX+1]; + int isnumeric[SQL_FIELD_MAX+1]; + int nf; + /* number of fields in the csv file */ + int ref[1+SQL_FIELD_MAX]; + /* ref[k] = k', if k-th field of the csv file corresponds to + k'-th field in the table statement; if ref[k] = 0, k-th field + of the csv file is ignored */ + SQLCHAR *query; + /* query generated by db_iodbc_open */ +}; + +SQLRETURN SQL_API dl_SQLAllocHandle ( + SQLSMALLINT HandleType, + SQLHANDLE InputHandle, + SQLHANDLE *OutputHandle) +{ + typedef SQLRETURN SQL_API ep_SQLAllocHandle( + SQLSMALLINT HandleType, + SQLHANDLE InputHandle, + SQLHANDLE *OutputHandle); + + ep_SQLAllocHandle *fn; + fn = (ep_SQLAllocHandle *) xdlsym(h_odbc, "SQLAllocHandle"); + xassert(fn != NULL); + return (*fn)(HandleType, InputHandle, OutputHandle); +} + +SQLRETURN SQL_API dl_SQLBindCol ( + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLSMALLINT TargetType, + SQLPOINTER TargetValue, + SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind) +{ + typedef SQLRETURN SQL_API ep_SQLBindCol( + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLSMALLINT TargetType, + SQLPOINTER TargetValue, + SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind); + ep_SQLBindCol *fn; + fn = (ep_SQLBindCol *) xdlsym(h_odbc, "SQLBindCol"); + xassert(fn != NULL); + return (*fn)(StatementHandle, ColumnNumber, TargetType, + TargetValue, BufferLength, StrLen_or_Ind); +} + +SQLRETURN SQL_API dl_SQLCloseCursor ( + SQLHSTMT StatementHandle) +{ + typedef SQLRETURN SQL_API ep_SQLCloseCursor ( + SQLHSTMT StatementHandle); + + ep_SQLCloseCursor *fn; + fn = (ep_SQLCloseCursor *) xdlsym(h_odbc, "SQLCloseCursor"); + xassert(fn != NULL); + return (*fn)(StatementHandle); +} + + +SQLRETURN SQL_API dl_SQLDisconnect ( + SQLHDBC ConnectionHandle) +{ + typedef SQLRETURN SQL_API ep_SQLDisconnect( + SQLHDBC ConnectionHandle); + + ep_SQLDisconnect *fn; + fn = (ep_SQLDisconnect *) xdlsym(h_odbc, "SQLDisconnect"); + xassert(fn != NULL); + return (*fn)(ConnectionHandle); +} + +SQLRETURN SQL_API dl_SQLDriverConnect ( + SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion) +{ + typedef SQLRETURN SQL_API ep_SQLDriverConnect( + SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR * szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR * szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT * pcbConnStrOut, + SQLUSMALLINT fDriverCompletion); + + ep_SQLDriverConnect *fn; + fn = (ep_SQLDriverConnect *) xdlsym(h_odbc, "SQLDriverConnect"); + xassert(fn != NULL); + return (*fn)(hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, + cbConnStrOutMax, pcbConnStrOut, fDriverCompletion); +} + +SQLRETURN SQL_API dl_SQLEndTran ( + SQLSMALLINT HandleType, + SQLHANDLE Handle, + SQLSMALLINT CompletionType) +{ + typedef SQLRETURN SQL_API ep_SQLEndTran ( + SQLSMALLINT HandleType, + SQLHANDLE Handle, + SQLSMALLINT CompletionType); + + ep_SQLEndTran *fn; + fn = (ep_SQLEndTran *) xdlsym(h_odbc, "SQLEndTran"); + xassert(fn != NULL); + return (*fn)(HandleType, Handle, CompletionType); +} + +SQLRETURN SQL_API dl_SQLExecDirect ( + SQLHSTMT StatementHandle, + SQLCHAR * StatementText, + SQLINTEGER TextLength) +{ + typedef SQLRETURN SQL_API ep_SQLExecDirect ( + SQLHSTMT StatementHandle, + SQLCHAR * StatementText, + SQLINTEGER TextLength); + + ep_SQLExecDirect *fn; + fn = (ep_SQLExecDirect *) xdlsym(h_odbc, "SQLExecDirect"); + xassert(fn != NULL); + return (*fn)(StatementHandle, StatementText, TextLength); +} + +SQLRETURN SQL_API dl_SQLFetch ( + SQLHSTMT StatementHandle) +{ + typedef SQLRETURN SQL_API ep_SQLFetch ( + SQLHSTMT StatementHandle); + + ep_SQLFetch *fn; + fn = (ep_SQLFetch*) xdlsym(h_odbc, "SQLFetch"); + xassert(fn != NULL); + return (*fn)(StatementHandle); +} + +SQLRETURN SQL_API dl_SQLFreeHandle ( + SQLSMALLINT HandleType, + SQLHANDLE Handle) +{ + typedef SQLRETURN SQL_API ep_SQLFreeHandle ( + SQLSMALLINT HandleType, + SQLHANDLE Handle); + + ep_SQLFreeHandle *fn; + fn = (ep_SQLFreeHandle *) xdlsym(h_odbc, "SQLFreeHandle"); + xassert(fn != NULL); + return (*fn)(HandleType, Handle); +} + +SQLRETURN SQL_API dl_SQLDescribeCol ( + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLCHAR * ColumnName, + SQLSMALLINT BufferLength, + SQLSMALLINT * NameLength, + SQLSMALLINT * DataType, + SQLULEN * ColumnSize, + SQLSMALLINT * DecimalDigits, + SQLSMALLINT * Nullable) +{ + typedef SQLRETURN SQL_API ep_SQLDescribeCol ( + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLCHAR *ColumnName, + SQLSMALLINT BufferLength, + SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, + SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, + SQLSMALLINT *Nullable); + + ep_SQLDescribeCol *fn; + fn = (ep_SQLDescribeCol *) xdlsym(h_odbc, "SQLDescribeCol"); + xassert(fn != NULL); + return (*fn)(StatementHandle, ColumnNumber, ColumnName, + BufferLength, NameLength, + DataType, ColumnSize, DecimalDigits, Nullable); +} + +SQLRETURN SQL_API dl_SQLGetDiagRec ( + SQLSMALLINT HandleType, + SQLHANDLE Handle, + SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, + SQLCHAR *MessageText, + SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength) +{ + typedef SQLRETURN SQL_API ep_SQLGetDiagRec ( + SQLSMALLINT HandleType, + SQLHANDLE Handle, + SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, + SQLCHAR *MessageText, + SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength); + + ep_SQLGetDiagRec *fn; + fn = (ep_SQLGetDiagRec *) xdlsym(h_odbc, "SQLGetDiagRec"); + xassert(fn != NULL); + return (*fn)(HandleType, Handle, RecNumber, Sqlstate, + NativeError, MessageText, BufferLength, TextLength); +} + +SQLRETURN SQL_API dl_SQLGetInfo ( + SQLHDBC ConnectionHandle, + SQLUSMALLINT InfoType, + SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength) +{ + typedef SQLRETURN SQL_API ep_SQLGetInfo ( + SQLHDBC ConnectionHandle, + SQLUSMALLINT InfoType, + SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength); + + ep_SQLGetInfo *fn; + fn = (ep_SQLGetInfo *) xdlsym(h_odbc, "SQLGetInfo"); + xassert(fn != NULL); + return (*fn)(ConnectionHandle, InfoType, InfoValue, BufferLength, + StringLength); +} + +SQLRETURN SQL_API dl_SQLNumResultCols ( + SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount) +{ + typedef SQLRETURN SQL_API ep_SQLNumResultCols ( + SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount); + + ep_SQLNumResultCols *fn; + fn = (ep_SQLNumResultCols *) xdlsym(h_odbc, "SQLNumResultCols"); + xassert(fn != NULL); + return (*fn)(StatementHandle, ColumnCount); +} + +SQLRETURN SQL_API dl_SQLSetConnectAttr ( + SQLHDBC ConnectionHandle, + SQLINTEGER Attribute, + SQLPOINTER Value, + SQLINTEGER StringLength) +{ + typedef SQLRETURN SQL_API ep_SQLSetConnectAttr ( + SQLHDBC ConnectionHandle, + SQLINTEGER Attribute, + SQLPOINTER Value, + SQLINTEGER StringLength); + + ep_SQLSetConnectAttr *fn; + fn = (ep_SQLSetConnectAttr *) xdlsym(h_odbc, "SQLSetConnectAttr"); + xassert(fn != NULL); + return (*fn)(ConnectionHandle, Attribute, Value, StringLength); +} + +SQLRETURN SQL_API dl_SQLSetEnvAttr ( + SQLHENV EnvironmentHandle, + SQLINTEGER Attribute, + SQLPOINTER Value, + SQLINTEGER StringLength) +{ + typedef SQLRETURN SQL_API ep_SQLSetEnvAttr ( + SQLHENV EnvironmentHandle, + SQLINTEGER Attribute, + SQLPOINTER Value, + SQLINTEGER StringLength); + + ep_SQLSetEnvAttr *fn; + fn = (ep_SQLSetEnvAttr *) xdlsym(h_odbc, "SQLSetEnvAttr"); + xassert(fn != NULL); + return (*fn)(EnvironmentHandle, Attribute, Value, StringLength); +} + +static void extract_error( + char *fn, + SQLHANDLE handle, + SQLSMALLINT type); + +static int is_numeric( + SQLSMALLINT coltype); + +/*********************************************************************** +* NAME +* +* db_iodbc_open - open connection to ODBC data base +* +* SYNOPSIS +* +* #include "glpsql.h" +* void *db_iodbc_open(TABDCA *dca, int mode); +* +* DESCRIPTION +* +* The routine db_iodbc_open opens a connection to an ODBC data base. +* It then executes the sql statements passed. +* +* In the case of table read the SELECT statement is executed. +* +* In the case of table write the INSERT statement is prepared. +* RETURNS +* +* The routine returns a pointer to data storage area created. */ +void *db_iodbc_open(TABDCA *dca, int mode) +{ void *ret; + char **sqllines; + + sqllines = args_concat(dca); + if (sqllines == NULL) + { xprintf("Missing arguments in table statement.\n" + "Please, supply table driver, dsn, and query.\n"); + return NULL; + } + ret = db_iodbc_open_int(dca, mode, (const char **) sqllines); + free_buffer(sqllines); + return ret; +} + +static void *db_iodbc_open_int(TABDCA *dca, int mode, const char + **sqllines) +{ + struct db_odbc *sql; + SQLRETURN ret; + SQLCHAR FAR *dsn; + SQLCHAR info[256]; + SQLSMALLINT colnamelen; + SQLSMALLINT nullable; + SQLSMALLINT scale; + const char *arg; + int narg; + int i, j; + int total; + + if (libodbc == NULL) + { + xprintf("No loader for shared ODBC library available\n"); + return NULL; + } + + if (h_odbc == NULL) + { + h_odbc = xdlopen(libodbc); + if (h_odbc == NULL) + { xprintf("unable to open library %s\n", libodbc); + xprintf("%s\n", xerrmsg()); + return NULL; + } + } + + sql = (struct db_odbc *) xmalloc(sizeof(struct db_odbc)); + if (sql == NULL) + return NULL; + + sql->mode = mode; + sql->hdbc = NULL; + sql->henv = NULL; + sql->hstmt = NULL; + sql->query = NULL; + narg = mpl_tab_num_args(dca); + + dsn = (SQLCHAR FAR *) mpl_tab_get_arg(dca, 2); + /* allocate an environment handle */ + ret = dl_SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, + &(sql->henv)); + /* set attribute to enable application to run as ODBC 3.0 + application */ + ret = dl_SQLSetEnvAttr(sql->henv, SQL_ATTR_ODBC_VERSION, + (void *) SQL_OV_ODBC3, 0); + /* allocate a connection handle */ + ret = dl_SQLAllocHandle(SQL_HANDLE_DBC, sql->henv, &(sql->hdbc)); + /* connect */ + ret = dl_SQLDriverConnect(sql->hdbc, NULL, dsn, SQL_NTS, NULL, 0, + NULL, SQL_DRIVER_COMPLETE); + if (SQL_SUCCEEDED(ret)) + { /* output information about data base connection */ + xprintf("Connected to "); + dl_SQLGetInfo(sql->hdbc, SQL_DBMS_NAME, (SQLPOINTER)info, + sizeof(info), NULL); + xprintf("%s ", info); + dl_SQLGetInfo(sql->hdbc, SQL_DBMS_VER, (SQLPOINTER)info, + sizeof(info), NULL); + xprintf("%s - ", info); + dl_SQLGetInfo(sql->hdbc, SQL_DATABASE_NAME, (SQLPOINTER)info, + sizeof(info), NULL); + xprintf("%s\n", info); + } + else + { /* describe error */ + xprintf("Failed to connect\n"); + extract_error("SQLDriverConnect", sql->hdbc, SQL_HANDLE_DBC); + dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); + xfree(sql); + return NULL; + } + /* set AUTOCOMMIT on*/ + ret = dl_SQLSetConnectAttr(sql->hdbc, SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0); + /* allocate a statement handle */ + ret = dl_SQLAllocHandle(SQL_HANDLE_STMT, sql->hdbc, &(sql->hstmt)); + + /* initialization queries */ + for(j = 0; sqllines[j+1] != NULL; j++) + { + sql->query = (SQLCHAR *) sqllines[j]; + xprintf("%s\n", sql->query); + ret = dl_SQLExecDirect(sql->hstmt, sql->query, SQL_NTS); + switch (ret) + { + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + case SQL_NO_DATA_FOUND: + break; + default: + xprintf("db_iodbc_open: Query\n\"%s\"\nfailed.\n", + sql->query); + extract_error("SQLExecDirect", sql->hstmt, SQL_HANDLE_STMT); + dl_SQLFreeHandle(SQL_HANDLE_STMT, sql->hstmt); + dl_SQLDisconnect(sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); + xfree(sql); + return NULL; + } + /* commit statement */ + dl_SQLEndTran(SQL_HANDLE_ENV, sql->henv, SQL_COMMIT); + } + + if ( sql->mode == 'R' ) + { sql->nf = mpl_tab_num_flds(dca); + for(j = 0; sqllines[j] != NULL; j++) + arg = sqllines[j]; + total = strlen(arg); + if (total > 7 && 0 == strncmp(arg, "SELECT ", 7)) + { + total = strlen(arg); + sql->query = xmalloc( (total+1) * sizeof(char)); + strcpy (sql->query, arg); + } + else + { + sql->query = db_generate_select_stmt(dca); + } + xprintf("%s\n", sql->query); + if (dl_SQLExecDirect(sql->hstmt, sql->query, SQL_NTS) != + SQL_SUCCESS) + { + xprintf("db_iodbc_open: Query\n\"%s\"\nfailed.\n", sql->query); + extract_error("SQLExecDirect", sql->hstmt, SQL_HANDLE_STMT); + dl_SQLFreeHandle(SQL_HANDLE_STMT, sql->hstmt); + dl_SQLDisconnect(sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); + xfree(sql->query); + xfree(sql); + return NULL; + } + xfree(sql->query); + /* determine number of result columns */ + ret = dl_SQLNumResultCols(sql->hstmt, &sql->nresultcols); + total = sql->nresultcols; + if (total > SQL_FIELD_MAX) + { xprintf("db_iodbc_open: Too many fields (> %d) in query.\n" + "\"%s\"\n", SQL_FIELD_MAX, sql->query); + dl_SQLFreeHandle(SQL_HANDLE_STMT, sql->hstmt); + dl_SQLDisconnect(sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); + xfree(sql->query); + return NULL; + } + for (i = 1; i <= total; i++) + { /* return a set of attributes for a column */ + ret = dl_SQLDescribeCol(sql->hstmt, (SQLSMALLINT) i, + sql->colname[i], SQL_FDLEN_MAX, + &colnamelen, &(sql->coltype[i]), &(sql->collen[i]), &scale, + &nullable); + sql->isnumeric[i] = is_numeric(sql->coltype[i]); + /* bind columns to program vars, converting all types to CHAR*/ + dl_SQLBindCol(sql->hstmt, i, SQL_CHAR, sql->data[i], + SQL_FDLEN_MAX, &(sql->outlen[i])); + for (j = sql->nf; j >= 1; j--) + { if (strcmp(mpl_tab_get_name(dca, j), sql->colname[i]) == 0) + break; + } + sql->ref[i] = j; + } + } + else if ( sql->mode == 'W' ) + { for(j = 0; sqllines[j] != NULL; j++) + arg = sqllines[j]; + if ( NULL != strchr(arg, '?') ) + { + total = strlen(arg); + sql->query = xmalloc( (total+1) * sizeof(char)); + strcpy (sql->query, arg); + } + else + { + sql->query = db_generate_insert_stmt(dca); + } + xprintf("%s\n", sql->query); + } + return sql; +} + +int db_iodbc_read(TABDCA *dca, void *link) +{ + struct db_odbc *sql; + SQLRETURN ret; + char buf[SQL_FDLEN_MAX+1]; + int i; + int len; + double num; + + sql = (struct db_odbc *) link; + + xassert(sql != NULL); + xassert(sql->mode == 'R'); + + ret=dl_SQLFetch(sql->hstmt); + if (ret== SQL_ERROR) + return -1; + if (ret== SQL_NO_DATA_FOUND) + return -1; /*EOF*/ + for (i=1; i <= sql->nresultcols; i++) + { + if (sql->ref[i] > 0) + { + len = sql->outlen[i]; + if (len != SQL_NULL_DATA) + { + if (len > SQL_FDLEN_MAX) + len = SQL_FDLEN_MAX; + else if (len < 0) + len = 0; + strncpy(buf, (const char *) sql->data[i], len); + buf[len] = 0x00; + if (0 != (sql->isnumeric[i])) + { strspx(buf); /* remove spaces*/ + if (str2num(buf, &num) != 0) + { xprintf("'%s' cannot be converted to a number.\n", + buf); + return 1; + } + mpl_tab_set_num(dca, sql->ref[i], num); + } + else + { mpl_tab_set_str(dca, sql->ref[i], strtrim(buf)); + } + } + } + } + return 0; +} + +int db_iodbc_write(TABDCA *dca, void *link) +{ + struct db_odbc *sql; + char *part; + char *query; + char *template; + char num[50]; + int k; + int len; + int nf; + + sql = (struct db_odbc *) link; + xassert(sql != NULL); + xassert(sql->mode == 'W'); + + len = strlen(sql->query); + template = (char *) xmalloc( (len + 1) * sizeof(char) ); + strcpy(template, sql->query); + + nf = mpl_tab_num_flds(dca); + for (k = 1; k <= nf; k++) + { switch (mpl_tab_get_type(dca, k)) + { case 'N': + len += 20; + break; + case 'S': + len += db_escaped_string_length(mpl_tab_get_str(dca, k)); + len += 2; + break; + default: + xassert(dca != dca); + } + } + query = xmalloc( (len + 1 ) * sizeof(char) ); + query[0] = 0x00; + for (k = 1, part = strtok (template, "?"); (part != NULL); + part = strtok (NULL, "?"), k++) + { + if (k > nf) break; + strcat( query, part ); + switch (mpl_tab_get_type(dca, k)) + { case 'N': +#if 0 /* 02/XI-2010 by xypron */ + sprintf(num, "%-18g",mpl_tab_get_num(dca, k)); +#else + sprintf(num, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k)); +#endif + strcat( query, num ); + break; + case 'S': + strcat( query, "'"); + db_escape_string( query + strlen(query), + mpl_tab_get_str(dca, k) ); + strcat( query, "'"); + break; + default: + xassert(dca != dca); + } + } + if (part != NULL) + strcat(query, part); + if (dl_SQLExecDirect(sql->hstmt, (SQLCHAR *) query, SQL_NTS) + != SQL_SUCCESS) + { + xprintf("db_iodbc_write: Query\n\"%s\"\nfailed.\n", query); + extract_error("SQLExecDirect", sql->hdbc, SQL_HANDLE_DBC); + xfree(query); + xfree(template); + return 1; + } + + xfree(query); + xfree(template); + return 0; +} + +int db_iodbc_close(TABDCA *dca, void *link) +{ + struct db_odbc *sql; + + sql = (struct db_odbc *) link; + xassert(sql != NULL); + /* Commit */ + if ( sql->mode == 'W' ) + dl_SQLEndTran(SQL_HANDLE_ENV, sql->henv, SQL_COMMIT); + if ( sql->mode == 'R' ) + dl_SQLCloseCursor(sql->hstmt); + + dl_SQLFreeHandle(SQL_HANDLE_STMT, sql->hstmt); + dl_SQLDisconnect(sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); + dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); + if ( sql->mode == 'W' ) + xfree(sql->query); + xfree(sql); + dca->link = NULL; + return 0; +} + +static void extract_error( + char *fn, + SQLHANDLE handle, + SQLSMALLINT type) +{ + SQLINTEGER i = 0; + SQLINTEGER native; + SQLCHAR state[ 7 ]; + SQLCHAR text[256]; + SQLSMALLINT len; + SQLRETURN ret; + + xprintf("\nThe driver reported the following diagnostics whilst " + "running %s\n", fn); + + do + { + ret = dl_SQLGetDiagRec(type, handle, ++i, state, &native, text, + sizeof(text), &len ); + if (SQL_SUCCEEDED(ret)) + xprintf("%s:%ld:%ld:%s\n", state, i, native, text); + } + while( ret == SQL_SUCCESS ); +} + +static int is_numeric(SQLSMALLINT coltype) +{ + int ret = 0; + switch (coltype) + { + case SQL_DECIMAL: + case SQL_NUMERIC: + case SQL_SMALLINT: + case SQL_INTEGER: + case SQL_REAL: + case SQL_FLOAT: + case SQL_DOUBLE: + case SQL_TINYINT: + case SQL_BIGINT: + ret = 1; + break; + } + return ret; +} + +#endif + +/**********************************************************************/ + +#ifndef HAVE_MYSQL + +void *db_mysql_open(TABDCA *dca, int mode) +{ xassert(dca == dca); + xassert(mode == mode); + xprintf("MySQL table driver not supported\n"); + return NULL; +} + +int db_mysql_read(TABDCA *dca, void *link) +{ xassert(dca != dca); + xassert(link != link); + return 0; +} + +int db_mysql_write(TABDCA *dca, void *link) +{ xassert(dca != dca); + xassert(link != link); + return 0; +} + +int db_mysql_close(TABDCA *dca, void *link) +{ xassert(dca != dca); + xassert(link != link); + return 0; +} + +#else + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__WOE__) +#include +#endif + +#ifdef __CYGWIN__ +#define byte_defined 1 +#endif + +#include +#include +#include + +struct db_mysql +{ + int mode; /*'R' = Read, 'W' = Write*/ + MYSQL *con; /*connection*/ + MYSQL_RES *res; /*result*/ + int nf; + /* number of fields in the csv file */ + int ref[1+SQL_FIELD_MAX]; + /* ref[k] = k', if k-th field of the csv file corresponds to + k'-th field in the table statement; if ref[k] = 0, k-th field + of the csv file is ignored */ + char *query; + /* query generated by db_mysql_open */ +}; + +void STDCALL dl_mysql_close(MYSQL *sock) +{ + typedef void STDCALL ep_mysql_close(MYSQL *sock); + + ep_mysql_close *fn; + fn = (ep_mysql_close *) xdlsym(h_mysql, "mysql_close"); + xassert(fn != NULL); + return (*fn)(sock); +} + +const char * STDCALL dl_mysql_error(MYSQL *mysql) +{ + typedef const char * STDCALL ep_mysql_error(MYSQL *mysql); + + ep_mysql_error *fn; + fn = (ep_mysql_error *) xdlsym(h_mysql, "mysql_error"); + xassert(fn != NULL); + return (*fn)(mysql); +} + +MYSQL_FIELD * STDCALL dl_mysql_fetch_fields(MYSQL_RES *res) +{ + typedef MYSQL_FIELD * STDCALL + ep_mysql_fetch_fields(MYSQL_RES *res); + + ep_mysql_fetch_fields *fn; + fn = (ep_mysql_fetch_fields *) xdlsym(h_mysql, "mysql_fetch_fields"); + xassert(fn != NULL); + return (*fn)(res); +} + +unsigned long * STDCALL dl_mysql_fetch_lengths(MYSQL_RES *result) +{ + typedef unsigned long * STDCALL + ep_mysql_fetch_lengths(MYSQL_RES *result); + + ep_mysql_fetch_lengths *fn; + fn = (ep_mysql_fetch_lengths *) xdlsym(h_mysql, + "mysql_fetch_lengths"); + xassert(fn != NULL); + return (*fn)(result); +} + +MYSQL_ROW STDCALL dl_mysql_fetch_row(MYSQL_RES *result) +{ + typedef MYSQL_ROW STDCALL ep_mysql_fetch_row(MYSQL_RES *result); + + ep_mysql_fetch_row *fn; + fn = (ep_mysql_fetch_row *) xdlsym(h_mysql, "mysql_fetch_row"); + xassert(fn != NULL); + return (*fn)(result); +} + +unsigned int STDCALL dl_mysql_field_count(MYSQL *mysql) +{ + typedef unsigned int STDCALL ep_mysql_field_count(MYSQL *mysql); + + ep_mysql_field_count *fn; + fn = (ep_mysql_field_count *) xdlsym(h_mysql, "mysql_field_count"); + xassert(fn != NULL); + return (*fn)(mysql); +} + +MYSQL * STDCALL dl_mysql_init(MYSQL *mysql) +{ + typedef MYSQL * STDCALL ep_mysql_init(MYSQL *mysql); + + ep_mysql_init *fn; + fn = (ep_mysql_init *) xdlsym(h_mysql, "mysql_init"); + xassert(fn != NULL); + return (*fn)(mysql); +} + +unsigned int STDCALL dl_mysql_num_fields(MYSQL_RES *res) +{ + typedef unsigned int STDCALL ep_mysql_num_fields(MYSQL_RES *res); + + ep_mysql_num_fields *fn; + fn = (ep_mysql_num_fields *) xdlsym(h_mysql, "mysql_num_fields"); + xassert(fn != NULL); + return (*fn)(res); +} + +int STDCALL dl_mysql_query(MYSQL *mysql, const char *q) +{ + typedef int STDCALL ep_mysql_query(MYSQL *mysql, const char *q); + + ep_mysql_query *fn; + fn = (ep_mysql_query *) xdlsym(h_mysql, "mysql_query"); + xassert(fn != NULL); + return (*fn)(mysql, q); +} + +MYSQL * STDCALL dl_mysql_real_connect(MYSQL *mysql, const char *host, + const char *user, + const char *passwd, + const char *db, + unsigned int port, + const char *unix_socket, + unsigned long clientflag) +{ + typedef MYSQL * STDCALL ep_mysql_real_connect(MYSQL *mysql, + const char *host, + const char *user, + const char *passwd, + const char *db, + unsigned int port, + const char *unix_socket, + unsigned long clientflag); + + ep_mysql_real_connect *fn; + fn = (ep_mysql_real_connect *) xdlsym(h_mysql, + "mysql_real_connect"); + xassert(fn != NULL); + return (*fn)(mysql, host, user, passwd, db, port, unix_socket, + clientflag); +} + +MYSQL_RES * STDCALL dl_mysql_use_result(MYSQL *mysql) +{ + typedef MYSQL_RES * STDCALL ep_mysql_use_result(MYSQL *mysql); + ep_mysql_use_result *fn; + fn = (ep_mysql_use_result *) xdlsym(h_mysql, "mysql_use_result"); + xassert(fn != NULL); + return (*fn)(mysql); +} + +/*********************************************************************** +* NAME +* +* db_mysql_open - open connection to ODBC data base +* +* SYNOPSIS +* +* #include "glpsql.h" +* void *db_mysql_open(TABDCA *dca, int mode); +* +* DESCRIPTION +* +* The routine db_mysql_open opens a connection to a MySQL data base. +* It then executes the sql statements passed. +* +* In the case of table read the SELECT statement is executed. +* +* In the case of table write the INSERT statement is prepared. +* RETURNS +* +* The routine returns a pointer to data storage area created. */ + +void *db_mysql_open(TABDCA *dca, int mode) +{ void *ret; + char **sqllines; + + sqllines = args_concat(dca); + if (sqllines == NULL) + { xprintf("Missing arguments in table statement.\n" + "Please, supply table driver, dsn, and query.\n"); + return NULL; + } + ret = db_mysql_open_int(dca, mode, (const char **) sqllines); + free_buffer(sqllines); + return ret; +} + +static void *db_mysql_open_int(TABDCA *dca, int mode, const char + **sqllines) +{ + struct db_mysql *sql = NULL; + char *arg = NULL; + const char *field; + MYSQL_FIELD *fields; + char *keyword; + char *value; + char *query; + char *dsn; +/* "Server=[server_name];Database=[database_name];UID=[username];*/ +/* PWD=[password];Port=[port]"*/ + char *server = NULL; /* Server */ + char *user = NULL; /* UID */ + char *password = NULL; /* PWD */ + char *database = NULL; /* Database */ + unsigned int port = 0; /* Port */ + int narg; + int i, j, total; + + if (libmysql == NULL) + { + xprintf("No loader for shared MySQL library available\n"); + return NULL; + } + + if (h_mysql == NULL) + { + h_mysql = xdlopen(libmysql); + if (h_mysql == NULL) + { xprintf("unable to open library %s\n", libmysql); + xprintf("%s\n", xerrmsg()); + return NULL; + } + } + + sql = (struct db_mysql *) xmalloc(sizeof(struct db_mysql)); + if (sql == NULL) + return NULL; + sql->mode = mode; + sql->res = NULL; + sql->query = NULL; + sql->nf = mpl_tab_num_flds(dca); + + narg = mpl_tab_num_args(dca); + if (narg < 3 ) + xprintf("MySQL driver: string list too short \n"); + + /* get connection string*/ + dsn = (char *) mpl_tab_get_arg(dca, 2); + /* copy connection string*/ + i = strlen(dsn); + i++; + arg = xmalloc(i * sizeof(char)); + strcpy(arg, dsn); + /*tokenize connection string*/ + for (i = 1, keyword = strtok (arg, "="); (keyword != NULL); + keyword = strtok (NULL, "="), i++) + { + value = strtok (NULL, ";"); + if (value==NULL) + { + xprintf("db_mysql_open: Missing value for keyword %s\n", + keyword); + xfree(arg); + xfree(sql); + return NULL; + } + if (0 == strcmp(keyword, "Server")) + server = value; + else if (0 == strcmp(keyword, "Database")) + database = value; + else if (0 == strcmp(keyword, "UID")) + user = value; + else if (0 == strcmp(keyword, "PWD")) + password = value; + else if (0 == strcmp(keyword, "Port")) + port = (unsigned int) atol(value); + } + /* Connect to database */ + sql->con = dl_mysql_init(NULL); + if (!dl_mysql_real_connect(sql->con, server, user, password, database, + port, NULL, 0)) + { + xprintf("db_mysql_open: Connect failed\n"); + xprintf("%s\n", dl_mysql_error(sql->con)); + xfree(arg); + xfree(sql); + return NULL; + } + xfree(arg); + + for(j = 0; sqllines[j+1] != NULL; j++) + { query = (char *) sqllines[j]; + xprintf("%s\n", query); + if (dl_mysql_query(sql->con, query)) + { + xprintf("db_mysql_open: Query\n\"%s\"\nfailed.\n", query); + xprintf("%s\n",dl_mysql_error(sql->con)); + dl_mysql_close(sql->con); + xfree(sql); + return NULL; + } + } + + if ( sql->mode == 'R' ) + { sql->nf = mpl_tab_num_flds(dca); + for(j = 0; sqllines[j] != NULL; j++) + arg = (char *) sqllines[j]; + total = strlen(arg); + if (total > 7 && 0 == strncmp(arg, "SELECT ", 7)) + { + total = strlen(arg); + query = xmalloc( (total+1) * sizeof(char)); + strcpy (query, arg); + } + else + { + query = db_generate_select_stmt(dca); + } + xprintf("%s\n", query); + if (dl_mysql_query(sql->con, query)) + { + xprintf("db_mysql_open: Query\n\"%s\"\nfailed.\n", query); + xprintf("%s\n",dl_mysql_error(sql->con)); + dl_mysql_close(sql->con); + xfree(query); + xfree(sql); + return NULL; + } + xfree(query); + sql->res = dl_mysql_use_result(sql->con); + if (sql->res) + { + /* create references between query results and table fields*/ + total = dl_mysql_num_fields(sql->res); + if (total > SQL_FIELD_MAX) + { xprintf("db_mysql_open: Too many fields (> %d) in query.\n" + "\"%s\"\n", SQL_FIELD_MAX, query); + xprintf("%s\n",dl_mysql_error(sql->con)); + dl_mysql_close(sql->con); + xfree(query); + xfree(sql); + return NULL; + } + fields = dl_mysql_fetch_fields(sql->res); + for (i = 1; i <= total; i++) + { + for (j = sql->nf; j >= 1; j--) + { + if (strcmp(mpl_tab_get_name(dca, j), fields[i-1].name) + == 0) + break; + } + sql->ref[i] = j; + } + } + else + { + if(dl_mysql_field_count(sql->con) == 0) + { + xprintf("db_mysql_open: Query was not a SELECT\n\"%s\"\n", + query); + xprintf("%s\n",dl_mysql_error(sql->con)); + xfree(query); + xfree(sql); + return NULL; + } + else + { + xprintf("db_mysql_open: Query\n\"%s\"\nfailed.\n", query); + xprintf("%s\n",dl_mysql_error(sql->con)); + xfree(query); + xfree(sql); + return NULL; + } + } + } + else if ( sql->mode == 'W' ) + { for(j = 0; sqllines[j] != NULL; j++) + arg = (char *) sqllines[j]; + if ( NULL != strchr(arg, '?') ) + { + total = strlen(arg); + query = xmalloc( (total+1) * sizeof(char)); + strcpy (query, arg); + } + else + query = db_generate_insert_stmt(dca); + sql->query = query; + xprintf("%s\n", query); + } + return sql; +} + +int db_mysql_read(TABDCA *dca, void *link) +{ struct db_mysql *sql; + char buf[255+1]; + char **row; + unsigned long *lengths; + MYSQL_FIELD *fields; + double num; + int len; + unsigned long num_fields; + int i; + + sql = (struct db_mysql *) link; + + xassert(sql != NULL); + xassert(sql->mode == 'R'); + if (NULL == sql->res) + { + xprintf("db_mysql_read: no result set available"); + return 1; + } + if (NULL==(row = (char **)dl_mysql_fetch_row(sql->res))) { + return -1; /*EOF*/ + } + lengths = dl_mysql_fetch_lengths(sql->res); + fields = dl_mysql_fetch_fields(sql->res); + num_fields = dl_mysql_num_fields(sql->res); + for (i=1; i <= num_fields; i++) + { + if (row[i-1] != NULL) + { len = (size_t) lengths[i-1]; + if (len > 255) + len = 255; + strncpy(buf, (const char *) row[i-1], len); + buf[len] = 0x00; + if (0 != (fields[i-1].flags & NUM_FLAG)) + { strspx(buf); /* remove spaces*/ + if (str2num(buf, &num) != 0) + { xprintf("'%s' cannot be converted to a number.\n", buf); + return 1; + } + if (sql->ref[i] > 0) + mpl_tab_set_num(dca, sql->ref[i], num); + } + else + { if (sql->ref[i] > 0) + mpl_tab_set_str(dca, sql->ref[i], strtrim(buf)); + } + } + } + return 0; +} + +int db_mysql_write(TABDCA *dca, void *link) +{ + struct db_mysql *sql; + char *part; + char *query; + char *template; + char num[50]; + int k; + int len; + int nf; + + sql = (struct db_mysql *) link; + xassert(sql != NULL); + xassert(sql->mode == 'W'); + + len = strlen(sql->query); + template = (char *) xmalloc( (len + 1) * sizeof(char) ); + strcpy(template, sql->query); + + nf = mpl_tab_num_flds(dca); + for (k = 1; k <= nf; k++) + { switch (mpl_tab_get_type(dca, k)) + { case 'N': + len += 20; + break; + case 'S': + len += db_escaped_string_length(mpl_tab_get_str(dca, k)); + len += 2; + break; + default: + xassert(dca != dca); + } + } + query = xmalloc( (len + 1 ) * sizeof(char) ); + query[0] = 0x00; + for (k = 1, part = strtok (template, "?"); (part != NULL); + part = strtok (NULL, "?"), k++) + { + if (k > nf) break; + strcat( query, part ); + switch (mpl_tab_get_type(dca, k)) + { case 'N': +#if 0 /* 02/XI-2010 by xypron */ + sprintf(num, "%-18g",mpl_tab_get_num(dca, k)); +#else + sprintf(num, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k)); +#endif + strcat( query, num ); + break; + case 'S': + strcat( query, "'"); + db_escape_string( query + strlen(query), + mpl_tab_get_str(dca, k) ); + strcat( query, "'"); + break; + default: + xassert(dca != dca); + } + } + if (part != NULL) + strcat(query, part); + if (dl_mysql_query(sql->con, query)) + { + xprintf("db_mysql_write: Query\n\"%s\"\nfailed.\n", query); + xprintf("%s\n",dl_mysql_error(sql->con)); + xfree(query); + xfree(template); + return 1; + } + + xfree(query); + xfree(template); + return 0; + } + +int db_mysql_close(TABDCA *dca, void *link) +{ + struct db_mysql *sql; + + sql = (struct db_mysql *) link; + xassert(sql != NULL); + dl_mysql_close(sql->con); + if ( sql->mode == 'W' ) + xfree(sql->query); + xfree(sql); + dca->link = NULL; + return 0; +} + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpsql.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpsql.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,64 @@ +/* glpsql.h */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Author: Heinrich Schuchardt . +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPSQL_H +#define GLPSQL_H + +#define db_iodbc_open _glp_db_iodbc_open +void *db_iodbc_open(TABDCA *dca, int mode); +/* open iODBC database connection */ + +#define db_iodbc_read _glp_db_iodbc_read +int db_iodbc_read(TABDCA *dca, void *link); +/* read data from iODBC */ + +#define db_iodbc_write _glp_db_iodbc_write +int db_iodbc_write(TABDCA *dca, void *link); +/* write data to iODBC */ + +#define db_iodbc_close _glp_db_iodbc_close +int db_iodbc_close(TABDCA *dca, void *link); +/* close iODBC database connection */ + +#define db_mysql_open _glp_db_mysql_open +void *db_mysql_open(TABDCA *dca, int mode); +/* open MySQL database connection */ + +#define db_mysql_read _glp_db_mysql_read +int db_mysql_read(TABDCA *dca, void *link); +/* read data from MySQL */ + +#define db_mysql_write _glp_db_mysql_write +int db_mysql_write(TABDCA *dca, void *link); +/* write data to MySQL */ + +#define db_mysql_close _glp_db_mysql_close +int db_mysql_close(TABDCA *dca, void *link); +/* close MySQL database connection */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpssx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpssx.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,418 @@ +/* glpssx.h (simplex method, bignum arithmetic) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPSSX_H +#define GLPSSX_H + +#include "glpbfx.h" +#include "glpenv.h" + +typedef struct SSX SSX; + +struct SSX +{ /* simplex solver workspace */ +/*---------------------------------------------------------------------- +// LP PROBLEM DATA +// +// It is assumed that LP problem has the following statement: +// +// minimize (or maximize) +// +// z = c[1]*x[1] + ... + c[m+n]*x[m+n] + c[0] (1) +// +// subject to equality constraints +// +// x[1] - a[1,1]*x[m+1] - ... - a[1,n]*x[m+n] = 0 +// +// . . . . . . . (2) +// +// x[m] - a[m,1]*x[m+1] + ... - a[m,n]*x[m+n] = 0 +// +// and bounds of variables +// +// l[1] <= x[1] <= u[1] +// +// . . . . . . . (3) +// +// l[m+n] <= x[m+n] <= u[m+n] +// +// where: +// x[1], ..., x[m] - auxiliary variables; +// x[m+1], ..., x[m+n] - structural variables; +// z - objective function; +// c[1], ..., c[m+n] - coefficients of the objective function; +// c[0] - constant term of the objective function; +// a[1,1], ..., a[m,n] - constraint coefficients; +// l[1], ..., l[m+n] - lower bounds of variables; +// u[1], ..., u[m+n] - upper bounds of variables. +// +// Bounds of variables can be finite as well as inifinite. Besides, +// lower and upper bounds can be equal to each other. So the following +// five types of variables are possible: +// +// Bounds of variable Type of variable +// ------------------------------------------------- +// -inf < x[k] < +inf Free (unbounded) variable +// l[k] <= x[k] < +inf Variable with lower bound +// -inf < x[k] <= u[k] Variable with upper bound +// l[k] <= x[k] <= u[k] Double-bounded variable +// l[k] = x[k] = u[k] Fixed variable +// +// Using vector-matrix notations the LP problem (1)-(3) can be written +// as follows: +// +// minimize (or maximize) +// +// z = c * x + c[0] (4) +// +// subject to equality constraints +// +// xR - A * xS = 0 (5) +// +// and bounds of variables +// +// l <= x <= u (6) +// +// where: +// xR - vector of auxiliary variables; +// xS - vector of structural variables; +// x = (xR, xS) - vector of all variables; +// z - objective function; +// c - vector of objective coefficients; +// c[0] - constant term of the objective function; +// A - matrix of constraint coefficients (has m rows +// and n columns); +// l - vector of lower bounds of variables; +// u - vector of upper bounds of variables. +// +// The simplex method makes no difference between auxiliary and +// structural variables, so it is convenient to think the system of +// equality constraints (5) written in a homogeneous form: +// +// (I | -A) * x = 0, (7) +// +// where (I | -A) is an augmented (m+n)xm constraint matrix, I is mxm +// unity matrix whose columns correspond to auxiliary variables, and A +// is the original mxn constraint matrix whose columns correspond to +// structural variables. Note that only the matrix A is stored. +----------------------------------------------------------------------*/ + int m; + /* number of rows (auxiliary variables), m > 0 */ + int n; + /* number of columns (structural variables), n > 0 */ + int *type; /* int type[1+m+n]; */ + /* type[0] is not used; + type[k], 1 <= k <= m+n, is the type of variable x[k]: */ +#define SSX_FR 0 /* free (unbounded) variable */ +#define SSX_LO 1 /* variable with lower bound */ +#define SSX_UP 2 /* variable with upper bound */ +#define SSX_DB 3 /* double-bounded variable */ +#define SSX_FX 4 /* fixed variable */ + mpq_t *lb; /* mpq_t lb[1+m+n]; alias: l */ + /* lb[0] is not used; + lb[k], 1 <= k <= m+n, is an lower bound of variable x[k]; + if x[k] has no lower bound, lb[k] is zero */ + mpq_t *ub; /* mpq_t ub[1+m+n]; alias: u */ + /* ub[0] is not used; + ub[k], 1 <= k <= m+n, is an upper bound of variable x[k]; + if x[k] has no upper bound, ub[k] is zero; + if x[k] is of fixed type, ub[k] is equal to lb[k] */ + int dir; + /* optimization direction (sense of the objective function): */ +#define SSX_MIN 0 /* minimization */ +#define SSX_MAX 1 /* maximization */ + mpq_t *coef; /* mpq_t coef[1+m+n]; alias: c */ + /* coef[0] is a constant term of the objective function; + coef[k], 1 <= k <= m+n, is a coefficient of the objective + function at variable x[k]; + note that auxiliary variables also may have non-zero objective + coefficients */ + int *A_ptr; /* int A_ptr[1+n+1]; */ + int *A_ind; /* int A_ind[A_ptr[n+1]]; */ + mpq_t *A_val; /* mpq_t A_val[A_ptr[n+1]]; */ + /* constraint matrix A (see (5)) in storage-by-columns format */ +/*---------------------------------------------------------------------- +// LP BASIS AND CURRENT BASIC SOLUTION +// +// The LP basis is defined by the following partition of the augmented +// constraint matrix (7): +// +// (B | N) = (I | -A) * Q, (8) +// +// where B is a mxm non-singular basis matrix whose columns correspond +// to basic variables xB, N is a mxn matrix whose columns correspond to +// non-basic variables xN, and Q is a permutation (m+n)x(m+n) matrix. +// +// From (7) and (8) it follows that +// +// (I | -A) * x = (I | -A) * Q * Q' * x = (B | N) * (xB, xN), +// +// therefore +// +// (xB, xN) = Q' * x, (9) +// +// where x is the vector of all variables in the original order, xB is +// a vector of basic variables, xN is a vector of non-basic variables, +// Q' = inv(Q) is a matrix transposed to Q. +// +// Current values of non-basic variables xN[j], j = 1, ..., n, are not +// stored; they are defined implicitly by their statuses as follows: +// +// 0, if xN[j] is free variable +// lN[j], if xN[j] is on its lower bound (10) +// uN[j], if xN[j] is on its upper bound +// lN[j] = uN[j], if xN[j] is fixed variable +// +// where lN[j] and uN[j] are lower and upper bounds of xN[j]. +// +// Current values of basic variables xB[i], i = 1, ..., m, are computed +// as follows: +// +// beta = - inv(B) * N * xN, (11) +// +// where current values of xN are defined by (10). +// +// Current values of simplex multipliers pi[i], i = 1, ..., m (which +// are values of Lagrange multipliers for equality constraints (7) also +// called shadow prices) are computed as follows: +// +// pi = inv(B') * cB, (12) +// +// where B' is a matrix transposed to B, cB is a vector of objective +// coefficients at basic variables xB. +// +// Current values of reduced costs d[j], j = 1, ..., n, (which are +// values of Langrange multipliers for active inequality constraints +// corresponding to non-basic variables) are computed as follows: +// +// d = cN - N' * pi, (13) +// +// where N' is a matrix transposed to N, cN is a vector of objective +// coefficients at non-basic variables xN. +----------------------------------------------------------------------*/ + int *stat; /* int stat[1+m+n]; */ + /* stat[0] is not used; + stat[k], 1 <= k <= m+n, is the status of variable x[k]: */ +#define SSX_BS 0 /* basic variable */ +#define SSX_NL 1 /* non-basic variable on lower bound */ +#define SSX_NU 2 /* non-basic variable on upper bound */ +#define SSX_NF 3 /* non-basic free variable */ +#define SSX_NS 4 /* non-basic fixed variable */ + int *Q_row; /* int Q_row[1+m+n]; */ + /* matrix Q in row-like format; + Q_row[0] is not used; + Q_row[i] = j means that q[i,j] = 1 */ + int *Q_col; /* int Q_col[1+m+n]; */ + /* matrix Q in column-like format; + Q_col[0] is not used; + Q_col[j] = i means that q[i,j] = 1 */ + /* if k-th column of the matrix (I | A) is k'-th column of the + matrix (B | N), then Q_row[k] = k' and Q_col[k'] = k; + if x[k] is xB[i], then Q_row[k] = i and Q_col[i] = k; + if x[k] is xN[j], then Q_row[k] = m+j and Q_col[m+j] = k */ + BFX *binv; + /* invertable form of the basis matrix B */ + mpq_t *bbar; /* mpq_t bbar[1+m]; alias: beta */ + /* bbar[0] is a value of the objective function; + bbar[i], 1 <= i <= m, is a value of basic variable xB[i] */ + mpq_t *pi; /* mpq_t pi[1+m]; */ + /* pi[0] is not used; + pi[i], 1 <= i <= m, is a simplex multiplier corresponding to + i-th row (equality constraint) */ + mpq_t *cbar; /* mpq_t cbar[1+n]; alias: d */ + /* cbar[0] is not used; + cbar[j], 1 <= j <= n, is a reduced cost of non-basic variable + xN[j] */ +/*---------------------------------------------------------------------- +// SIMPLEX TABLE +// +// Due to (8) and (9) the system of equality constraints (7) for the +// current basis can be written as follows: +// +// xB = A~ * xN, (14) +// +// where +// +// A~ = - inv(B) * N (15) +// +// is a mxn matrix called the simplex table. +// +// The revised simplex method uses only two components of A~, namely, +// pivot column corresponding to non-basic variable xN[q] chosen to +// enter the basis, and pivot row corresponding to basic variable xB[p] +// chosen to leave the basis. +// +// Pivot column alfa_q is q-th column of A~, so +// +// alfa_q = A~ * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], (16) +// +// where N[q] is q-th column of the matrix N. +// +// Pivot row alfa_p is p-th row of A~ or, equivalently, p-th column of +// A~', a matrix transposed to A~, so +// +// alfa_p = A~' * e[p] = - N' * inv(B') * e[p] = - N' * rho_p, (17) +// +// where (*)' means transposition, and +// +// rho_p = inv(B') * e[p], (18) +// +// is p-th column of inv(B') or, that is the same, p-th row of inv(B). +----------------------------------------------------------------------*/ + int p; + /* number of basic variable xB[p], 1 <= p <= m, chosen to leave + the basis */ + mpq_t *rho; /* mpq_t rho[1+m]; */ + /* p-th row of the inverse inv(B); see (18) */ + mpq_t *ap; /* mpq_t ap[1+n]; */ + /* p-th row of the simplex table; see (17) */ + int q; + /* number of non-basic variable xN[q], 1 <= q <= n, chosen to + enter the basis */ + mpq_t *aq; /* mpq_t aq[1+m]; */ + /* q-th column of the simplex table; see (16) */ +/*--------------------------------------------------------------------*/ + int q_dir; + /* direction in which non-basic variable xN[q] should change on + moving to the adjacent vertex of the polyhedron: + +1 means that xN[q] increases + -1 means that xN[q] decreases */ + int p_stat; + /* non-basic status which should be assigned to basic variable + xB[p] when it has left the basis and become xN[q] */ + mpq_t delta; + /* actual change of xN[q] in the adjacent basis (it has the same + sign as q_dir) */ +/*--------------------------------------------------------------------*/ + int it_lim; + /* simplex iterations limit; if this value is positive, it is + decreased by one each time when one simplex iteration has been + performed, and reaching zero value signals the solver to stop + the search; negative value means no iterations limit */ + int it_cnt; + /* simplex iterations count; this count is increased by one each + time when one simplex iteration has been performed */ + double tm_lim; + /* searching time limit, in seconds; if this value is positive, + it is decreased each time when one simplex iteration has been + performed by the amount of time spent for the iteration, and + reaching zero value signals the solver to stop the search; + negative value means no time limit */ + double out_frq; + /* output frequency, in seconds; this parameter specifies how + frequently the solver sends information about the progress of + the search to the standard output */ + glp_long tm_beg; + /* starting time of the search, in seconds; the total time of the + search is the difference between xtime() and tm_beg */ + glp_long tm_lag; + /* the most recent time, in seconds, at which the progress of the + the search was displayed */ +}; + +#define ssx_create _glp_ssx_create +#define ssx_factorize _glp_ssx_factorize +#define ssx_get_xNj _glp_ssx_get_xNj +#define ssx_eval_bbar _glp_ssx_eval_bbar +#define ssx_eval_pi _glp_ssx_eval_pi +#define ssx_eval_dj _glp_ssx_eval_dj +#define ssx_eval_cbar _glp_ssx_eval_cbar +#define ssx_eval_rho _glp_ssx_eval_rho +#define ssx_eval_row _glp_ssx_eval_row +#define ssx_eval_col _glp_ssx_eval_col +#define ssx_chuzc _glp_ssx_chuzc +#define ssx_chuzr _glp_ssx_chuzr +#define ssx_update_bbar _glp_ssx_update_bbar +#define ssx_update_pi _glp_ssx_update_pi +#define ssx_update_cbar _glp_ssx_update_cbar +#define ssx_change_basis _glp_ssx_change_basis +#define ssx_delete _glp_ssx_delete + +#define ssx_phase_I _glp_ssx_phase_I +#define ssx_phase_II _glp_ssx_phase_II +#define ssx_driver _glp_ssx_driver + +SSX *ssx_create(int m, int n, int nnz); +/* create simplex solver workspace */ + +int ssx_factorize(SSX *ssx); +/* factorize the current basis matrix */ + +void ssx_get_xNj(SSX *ssx, int j, mpq_t x); +/* determine value of non-basic variable */ + +void ssx_eval_bbar(SSX *ssx); +/* compute values of basic variables */ + +void ssx_eval_pi(SSX *ssx); +/* compute values of simplex multipliers */ + +void ssx_eval_dj(SSX *ssx, int j, mpq_t dj); +/* compute reduced cost of non-basic variable */ + +void ssx_eval_cbar(SSX *ssx); +/* compute reduced costs of all non-basic variables */ + +void ssx_eval_rho(SSX *ssx); +/* compute p-th row of the inverse */ + +void ssx_eval_row(SSX *ssx); +/* compute pivot row of the simplex table */ + +void ssx_eval_col(SSX *ssx); +/* compute pivot column of the simplex table */ + +void ssx_chuzc(SSX *ssx); +/* choose pivot column */ + +void ssx_chuzr(SSX *ssx); +/* choose pivot row */ + +void ssx_update_bbar(SSX *ssx); +/* update values of basic variables */ + +void ssx_update_pi(SSX *ssx); +/* update simplex multipliers */ + +void ssx_update_cbar(SSX *ssx); +/* update reduced costs of non-basic variables */ + +void ssx_change_basis(SSX *ssx); +/* change current basis to adjacent one */ + +void ssx_delete(SSX *ssx); +/* delete simplex solver workspace */ + +int ssx_phase_I(SSX *ssx); +/* find primal feasible solution */ + +int ssx_phase_II(SSX *ssx); +/* find optimal solution */ + +int ssx_driver(SSX *ssx); +/* base driver to exact simplex method */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpssx01.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpssx01.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,839 @@ +/* glpssx01.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glpssx.h" +#define xfault xerror + +/*---------------------------------------------------------------------- +// ssx_create - create simplex solver workspace. +// +// This routine creates the workspace used by simplex solver routines, +// and returns a pointer to it. +// +// Parameters m, n, and nnz specify, respectively, the number of rows, +// columns, and non-zero constraint coefficients. +// +// This routine only allocates the memory for the workspace components, +// so the workspace needs to be saturated by data. */ + +SSX *ssx_create(int m, int n, int nnz) +{ SSX *ssx; + int i, j, k; + if (m < 1) + xfault("ssx_create: m = %d; invalid number of rows\n", m); + if (n < 1) + xfault("ssx_create: n = %d; invalid number of columns\n", n); + if (nnz < 0) + xfault("ssx_create: nnz = %d; invalid number of non-zero const" + "raint coefficients\n", nnz); + ssx = xmalloc(sizeof(SSX)); + ssx->m = m; + ssx->n = n; + ssx->type = xcalloc(1+m+n, sizeof(int)); + ssx->lb = xcalloc(1+m+n, sizeof(mpq_t)); + for (k = 1; k <= m+n; k++) mpq_init(ssx->lb[k]); + ssx->ub = xcalloc(1+m+n, sizeof(mpq_t)); + for (k = 1; k <= m+n; k++) mpq_init(ssx->ub[k]); + ssx->coef = xcalloc(1+m+n, sizeof(mpq_t)); + for (k = 0; k <= m+n; k++) mpq_init(ssx->coef[k]); + ssx->A_ptr = xcalloc(1+n+1, sizeof(int)); + ssx->A_ptr[n+1] = nnz+1; + ssx->A_ind = xcalloc(1+nnz, sizeof(int)); + ssx->A_val = xcalloc(1+nnz, sizeof(mpq_t)); + for (k = 1; k <= nnz; k++) mpq_init(ssx->A_val[k]); + ssx->stat = xcalloc(1+m+n, sizeof(int)); + ssx->Q_row = xcalloc(1+m+n, sizeof(int)); + ssx->Q_col = xcalloc(1+m+n, sizeof(int)); + ssx->binv = bfx_create_binv(); + ssx->bbar = xcalloc(1+m, sizeof(mpq_t)); + for (i = 0; i <= m; i++) mpq_init(ssx->bbar[i]); + ssx->pi = xcalloc(1+m, sizeof(mpq_t)); + for (i = 1; i <= m; i++) mpq_init(ssx->pi[i]); + ssx->cbar = xcalloc(1+n, sizeof(mpq_t)); + for (j = 1; j <= n; j++) mpq_init(ssx->cbar[j]); + ssx->rho = xcalloc(1+m, sizeof(mpq_t)); + for (i = 1; i <= m; i++) mpq_init(ssx->rho[i]); + ssx->ap = xcalloc(1+n, sizeof(mpq_t)); + for (j = 1; j <= n; j++) mpq_init(ssx->ap[j]); + ssx->aq = xcalloc(1+m, sizeof(mpq_t)); + for (i = 1; i <= m; i++) mpq_init(ssx->aq[i]); + mpq_init(ssx->delta); + return ssx; +} + +/*---------------------------------------------------------------------- +// ssx_factorize - factorize the current basis matrix. +// +// This routine computes factorization of the current basis matrix B +// and returns the singularity flag. If the matrix B is non-singular, +// the flag is zero, otherwise non-zero. */ + +static int basis_col(void *info, int j, int ind[], mpq_t val[]) +{ /* this auxiliary routine provides row indices and numeric values + of non-zero elements in j-th column of the matrix B */ + SSX *ssx = info; + int m = ssx->m; + int n = ssx->n; + int *A_ptr = ssx->A_ptr; + int *A_ind = ssx->A_ind; + mpq_t *A_val = ssx->A_val; + int *Q_col = ssx->Q_col; + int k, len, ptr; + xassert(1 <= j && j <= m); + k = Q_col[j]; /* x[k] = xB[j] */ + xassert(1 <= k && k <= m+n); + /* j-th column of the matrix B is k-th column of the augmented + constraint matrix (I | -A) */ + if (k <= m) + { /* it is a column of the unity matrix I */ + len = 1, ind[1] = k, mpq_set_si(val[1], 1, 1); + } + else + { /* it is a column of the original constraint matrix -A */ + len = 0; + for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) + { len++; + ind[len] = A_ind[ptr]; + mpq_neg(val[len], A_val[ptr]); + } + } + return len; +} + +int ssx_factorize(SSX *ssx) +{ int ret; + ret = bfx_factorize(ssx->binv, ssx->m, basis_col, ssx); + return ret; +} + +/*---------------------------------------------------------------------- +// ssx_get_xNj - determine value of non-basic variable. +// +// This routine determines the value of non-basic variable xN[j] in the +// current basic solution defined as follows: +// +// 0, if xN[j] is free variable +// lN[j], if xN[j] is on its lower bound +// uN[j], if xN[j] is on its upper bound +// lN[j] = uN[j], if xN[j] is fixed variable +// +// where lN[j] and uN[j] are lower and upper bounds of xN[j]. */ + +void ssx_get_xNj(SSX *ssx, int j, mpq_t x) +{ int m = ssx->m; + int n = ssx->n; + mpq_t *lb = ssx->lb; + mpq_t *ub = ssx->ub; + int *stat = ssx->stat; + int *Q_col = ssx->Q_col; + int k; + xassert(1 <= j && j <= n); + k = Q_col[m+j]; /* x[k] = xN[j] */ + xassert(1 <= k && k <= m+n); + switch (stat[k]) + { case SSX_NL: + /* xN[j] is on its lower bound */ + mpq_set(x, lb[k]); break; + case SSX_NU: + /* xN[j] is on its upper bound */ + mpq_set(x, ub[k]); break; + case SSX_NF: + /* xN[j] is free variable */ + mpq_set_si(x, 0, 1); break; + case SSX_NS: + /* xN[j] is fixed variable */ + mpq_set(x, lb[k]); break; + default: + xassert(stat != stat); + } + return; +} + +/*---------------------------------------------------------------------- +// ssx_eval_bbar - compute values of basic variables. +// +// This routine computes values of basic variables xB in the current +// basic solution as follows: +// +// beta = - inv(B) * N * xN, +// +// where B is the basis matrix, N is the matrix of non-basic columns, +// xN is a vector of current values of non-basic variables. */ + +void ssx_eval_bbar(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + mpq_t *coef = ssx->coef; + int *A_ptr = ssx->A_ptr; + int *A_ind = ssx->A_ind; + mpq_t *A_val = ssx->A_val; + int *Q_col = ssx->Q_col; + mpq_t *bbar = ssx->bbar; + int i, j, k, ptr; + mpq_t x, temp; + mpq_init(x); + mpq_init(temp); + /* bbar := 0 */ + for (i = 1; i <= m; i++) + mpq_set_si(bbar[i], 0, 1); + /* bbar := - N * xN = - N[1] * xN[1] - ... - N[n] * xN[n] */ + for (j = 1; j <= n; j++) + { ssx_get_xNj(ssx, j, x); + if (mpq_sgn(x) == 0) continue; + k = Q_col[m+j]; /* x[k] = xN[j] */ + if (k <= m) + { /* N[j] is a column of the unity matrix I */ + mpq_sub(bbar[k], bbar[k], x); + } + else + { /* N[j] is a column of the original constraint matrix -A */ + for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) + { mpq_mul(temp, A_val[ptr], x); + mpq_add(bbar[A_ind[ptr]], bbar[A_ind[ptr]], temp); + } + } + } + /* bbar := inv(B) * bbar */ + bfx_ftran(ssx->binv, bbar, 0); +#if 1 + /* compute value of the objective function */ + /* bbar[0] := c[0] */ + mpq_set(bbar[0], coef[0]); + /* bbar[0] := bbar[0] + sum{i in B} cB[i] * xB[i] */ + for (i = 1; i <= m; i++) + { k = Q_col[i]; /* x[k] = xB[i] */ + if (mpq_sgn(coef[k]) == 0) continue; + mpq_mul(temp, coef[k], bbar[i]); + mpq_add(bbar[0], bbar[0], temp); + } + /* bbar[0] := bbar[0] + sum{j in N} cN[j] * xN[j] */ + for (j = 1; j <= n; j++) + { k = Q_col[m+j]; /* x[k] = xN[j] */ + if (mpq_sgn(coef[k]) == 0) continue; + ssx_get_xNj(ssx, j, x); + mpq_mul(temp, coef[k], x); + mpq_add(bbar[0], bbar[0], temp); + } +#endif + mpq_clear(x); + mpq_clear(temp); + return; +} + +/*---------------------------------------------------------------------- +// ssx_eval_pi - compute values of simplex multipliers. +// +// This routine computes values of simplex multipliers (shadow prices) +// pi in the current basic solution as follows: +// +// pi = inv(B') * cB, +// +// where B' is a matrix transposed to the basis matrix B, cB is a vector +// of objective coefficients at basic variables xB. */ + +void ssx_eval_pi(SSX *ssx) +{ int m = ssx->m; + mpq_t *coef = ssx->coef; + int *Q_col = ssx->Q_col; + mpq_t *pi = ssx->pi; + int i; + /* pi := cB */ + for (i = 1; i <= m; i++) mpq_set(pi[i], coef[Q_col[i]]); + /* pi := inv(B') * cB */ + bfx_btran(ssx->binv, pi); + return; +} + +/*---------------------------------------------------------------------- +// ssx_eval_dj - compute reduced cost of non-basic variable. +// +// This routine computes reduced cost d[j] of non-basic variable xN[j] +// in the current basic solution as follows: +// +// d[j] = cN[j] - N[j] * pi, +// +// where cN[j] is an objective coefficient at xN[j], N[j] is a column +// of the augmented constraint matrix (I | -A) corresponding to xN[j], +// pi is the vector of simplex multipliers (shadow prices). */ + +void ssx_eval_dj(SSX *ssx, int j, mpq_t dj) +{ int m = ssx->m; + int n = ssx->n; + mpq_t *coef = ssx->coef; + int *A_ptr = ssx->A_ptr; + int *A_ind = ssx->A_ind; + mpq_t *A_val = ssx->A_val; + int *Q_col = ssx->Q_col; + mpq_t *pi = ssx->pi; + int k, ptr, end; + mpq_t temp; + mpq_init(temp); + xassert(1 <= j && j <= n); + k = Q_col[m+j]; /* x[k] = xN[j] */ + xassert(1 <= k && k <= m+n); + /* j-th column of the matrix N is k-th column of the augmented + constraint matrix (I | -A) */ + if (k <= m) + { /* it is a column of the unity matrix I */ + mpq_sub(dj, coef[k], pi[k]); + } + else + { /* it is a column of the original constraint matrix -A */ + mpq_set(dj, coef[k]); + for (ptr = A_ptr[k-m], end = A_ptr[k-m+1]; ptr < end; ptr++) + { mpq_mul(temp, A_val[ptr], pi[A_ind[ptr]]); + mpq_add(dj, dj, temp); + } + } + mpq_clear(temp); + return; +} + +/*---------------------------------------------------------------------- +// ssx_eval_cbar - compute reduced costs of all non-basic variables. +// +// This routine computes the vector of reduced costs pi in the current +// basic solution for all non-basic variables, including fixed ones. */ + +void ssx_eval_cbar(SSX *ssx) +{ int n = ssx->n; + mpq_t *cbar = ssx->cbar; + int j; + for (j = 1; j <= n; j++) + ssx_eval_dj(ssx, j, cbar[j]); + return; +} + +/*---------------------------------------------------------------------- +// ssx_eval_rho - compute p-th row of the inverse. +// +// This routine computes p-th row of the matrix inv(B), where B is the +// current basis matrix. +// +// p-th row of the inverse is computed using the following formula: +// +// rho = inv(B') * e[p], +// +// where B' is a matrix transposed to B, e[p] is a unity vector, which +// contains one in p-th position. */ + +void ssx_eval_rho(SSX *ssx) +{ int m = ssx->m; + int p = ssx->p; + mpq_t *rho = ssx->rho; + int i; + xassert(1 <= p && p <= m); + /* rho := 0 */ + for (i = 1; i <= m; i++) mpq_set_si(rho[i], 0, 1); + /* rho := e[p] */ + mpq_set_si(rho[p], 1, 1); + /* rho := inv(B') * rho */ + bfx_btran(ssx->binv, rho); + return; +} + +/*---------------------------------------------------------------------- +// ssx_eval_row - compute pivot row of the simplex table. +// +// This routine computes p-th (pivot) row of the current simplex table +// A~ = - inv(B) * N using the following formula: +// +// A~[p] = - N' * inv(B') * e[p] = - N' * rho[p], +// +// where N' is a matrix transposed to the matrix N, rho[p] is p-th row +// of the inverse inv(B). */ + +void ssx_eval_row(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + int *A_ptr = ssx->A_ptr; + int *A_ind = ssx->A_ind; + mpq_t *A_val = ssx->A_val; + int *Q_col = ssx->Q_col; + mpq_t *rho = ssx->rho; + mpq_t *ap = ssx->ap; + int j, k, ptr; + mpq_t temp; + mpq_init(temp); + for (j = 1; j <= n; j++) + { /* ap[j] := - N'[j] * rho (inner product) */ + k = Q_col[m+j]; /* x[k] = xN[j] */ + if (k <= m) + mpq_neg(ap[j], rho[k]); + else + { mpq_set_si(ap[j], 0, 1); + for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) + { mpq_mul(temp, A_val[ptr], rho[A_ind[ptr]]); + mpq_add(ap[j], ap[j], temp); + } + } + } + mpq_clear(temp); + return; +} + +/*---------------------------------------------------------------------- +// ssx_eval_col - compute pivot column of the simplex table. +// +// This routine computes q-th (pivot) column of the current simplex +// table A~ = - inv(B) * N using the following formula: +// +// A~[q] = - inv(B) * N[q], +// +// where N[q] is q-th column of the matrix N corresponding to chosen +// non-basic variable xN[q]. */ + +void ssx_eval_col(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + int *A_ptr = ssx->A_ptr; + int *A_ind = ssx->A_ind; + mpq_t *A_val = ssx->A_val; + int *Q_col = ssx->Q_col; + int q = ssx->q; + mpq_t *aq = ssx->aq; + int i, k, ptr; + xassert(1 <= q && q <= n); + /* aq := 0 */ + for (i = 1; i <= m; i++) mpq_set_si(aq[i], 0, 1); + /* aq := N[q] */ + k = Q_col[m+q]; /* x[k] = xN[q] */ + if (k <= m) + { /* N[q] is a column of the unity matrix I */ + mpq_set_si(aq[k], 1, 1); + } + else + { /* N[q] is a column of the original constraint matrix -A */ + for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) + mpq_neg(aq[A_ind[ptr]], A_val[ptr]); + } + /* aq := inv(B) * aq */ + bfx_ftran(ssx->binv, aq, 1); + /* aq := - aq */ + for (i = 1; i <= m; i++) mpq_neg(aq[i], aq[i]); + return; +} + +/*---------------------------------------------------------------------- +// ssx_chuzc - choose pivot column. +// +// This routine chooses non-basic variable xN[q] whose reduced cost +// indicates possible improving of the objective function to enter it +// in the basis. +// +// Currently the standard (textbook) pricing is used, i.e. that +// non-basic variable is preferred which has greatest reduced cost (in +// magnitude). +// +// If xN[q] has been chosen, the routine stores its number q and also +// sets the flag q_dir that indicates direction in which xN[q] has to +// change (+1 means increasing, -1 means decreasing). +// +// If the choice cannot be made, because the current basic solution is +// dual feasible, the routine sets the number q to 0. */ + +void ssx_chuzc(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + int dir = (ssx->dir == SSX_MIN ? +1 : -1); + int *Q_col = ssx->Q_col; + int *stat = ssx->stat; + mpq_t *cbar = ssx->cbar; + int j, k, s, q, q_dir; + double best, temp; + /* nothing is chosen so far */ + q = 0, q_dir = 0, best = 0.0; + /* look through the list of non-basic variables */ + for (j = 1; j <= n; j++) + { k = Q_col[m+j]; /* x[k] = xN[j] */ + s = dir * mpq_sgn(cbar[j]); + if ((stat[k] == SSX_NF || stat[k] == SSX_NL) && s < 0 || + (stat[k] == SSX_NF || stat[k] == SSX_NU) && s > 0) + { /* reduced cost of xN[j] indicates possible improving of + the objective function */ + temp = fabs(mpq_get_d(cbar[j])); + xassert(temp != 0.0); + if (q == 0 || best < temp) + q = j, q_dir = - s, best = temp; + } + } + ssx->q = q, ssx->q_dir = q_dir; + return; +} + +/*---------------------------------------------------------------------- +// ssx_chuzr - choose pivot row. +// +// This routine looks through elements of q-th column of the simplex +// table and chooses basic variable xB[p] which should leave the basis. +// +// The choice is based on the standard (textbook) ratio test. +// +// If xB[p] has been chosen, the routine stores its number p and also +// sets its non-basic status p_stat which should be assigned to xB[p] +// when it has left the basis and become xN[q]. +// +// Special case p < 0 means that xN[q] is double-bounded variable and +// it reaches its opposite bound before any basic variable does that, +// so the current basis remains unchanged. +// +// If the choice cannot be made, because xN[q] can infinitely change in +// the feasible direction, the routine sets the number p to 0. */ + +void ssx_chuzr(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + int *type = ssx->type; + mpq_t *lb = ssx->lb; + mpq_t *ub = ssx->ub; + int *Q_col = ssx->Q_col; + mpq_t *bbar = ssx->bbar; + int q = ssx->q; + mpq_t *aq = ssx->aq; + int q_dir = ssx->q_dir; + int i, k, s, t, p, p_stat; + mpq_t teta, temp; + mpq_init(teta); + mpq_init(temp); + xassert(1 <= q && q <= n); + xassert(q_dir == +1 || q_dir == -1); + /* nothing is chosen so far */ + p = 0, p_stat = 0; + /* look through the list of basic variables */ + for (i = 1; i <= m; i++) + { s = q_dir * mpq_sgn(aq[i]); + if (s < 0) + { /* xB[i] decreases */ + k = Q_col[i]; /* x[k] = xB[i] */ + t = type[k]; + if (t == SSX_LO || t == SSX_DB || t == SSX_FX) + { /* xB[i] has finite lower bound */ + mpq_sub(temp, bbar[i], lb[k]); + mpq_div(temp, temp, aq[i]); + mpq_abs(temp, temp); + if (p == 0 || mpq_cmp(teta, temp) > 0) + { p = i; + p_stat = (t == SSX_FX ? SSX_NS : SSX_NL); + mpq_set(teta, temp); + } + } + } + else if (s > 0) + { /* xB[i] increases */ + k = Q_col[i]; /* x[k] = xB[i] */ + t = type[k]; + if (t == SSX_UP || t == SSX_DB || t == SSX_FX) + { /* xB[i] has finite upper bound */ + mpq_sub(temp, bbar[i], ub[k]); + mpq_div(temp, temp, aq[i]); + mpq_abs(temp, temp); + if (p == 0 || mpq_cmp(teta, temp) > 0) + { p = i; + p_stat = (t == SSX_FX ? SSX_NS : SSX_NU); + mpq_set(teta, temp); + } + } + } + /* if something has been chosen and the ratio test indicates + exact degeneracy, the search can be finished */ + if (p != 0 && mpq_sgn(teta) == 0) break; + } + /* if xN[q] is double-bounded, check if it can reach its opposite + bound before any basic variable */ + k = Q_col[m+q]; /* x[k] = xN[q] */ + if (type[k] == SSX_DB) + { mpq_sub(temp, ub[k], lb[k]); + if (p == 0 || mpq_cmp(teta, temp) > 0) + { p = -1; + p_stat = -1; + mpq_set(teta, temp); + } + } + ssx->p = p; + ssx->p_stat = p_stat; + /* if xB[p] has been chosen, determine its actual change in the + adjacent basis (it has the same sign as q_dir) */ + if (p != 0) + { xassert(mpq_sgn(teta) >= 0); + if (q_dir > 0) + mpq_set(ssx->delta, teta); + else + mpq_neg(ssx->delta, teta); + } + mpq_clear(teta); + mpq_clear(temp); + return; +} + +/*---------------------------------------------------------------------- +// ssx_update_bbar - update values of basic variables. +// +// This routine recomputes the current values of basic variables for +// the adjacent basis. +// +// The simplex table for the current basis is the following: +// +// xB[i] = sum{j in 1..n} alfa[i,j] * xN[q], i = 1,...,m +// +// therefore +// +// delta xB[i] = alfa[i,q] * delta xN[q], i = 1,...,m +// +// where delta xN[q] = xN.new[q] - xN[q] is the change of xN[q] in the +// adjacent basis, and delta xB[i] = xB.new[i] - xB[i] is the change of +// xB[i]. This gives formulae for recomputing values of xB[i]: +// +// xB.new[p] = xN[q] + delta xN[q] +// +// (because xN[q] becomes xB[p] in the adjacent basis), and +// +// xB.new[i] = xB[i] + alfa[i,q] * delta xN[q], i != p +// +// for other basic variables. */ + +void ssx_update_bbar(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + mpq_t *bbar = ssx->bbar; + mpq_t *cbar = ssx->cbar; + int p = ssx->p; + int q = ssx->q; + mpq_t *aq = ssx->aq; + int i; + mpq_t temp; + mpq_init(temp); + xassert(1 <= q && q <= n); + if (p < 0) + { /* xN[q] is double-bounded and goes to its opposite bound */ + /* nop */; + } + else + { /* xN[q] becomes xB[p] in the adjacent basis */ + /* xB.new[p] = xN[q] + delta xN[q] */ + xassert(1 <= p && p <= m); + ssx_get_xNj(ssx, q, temp); + mpq_add(bbar[p], temp, ssx->delta); + } + /* update values of other basic variables depending on xN[q] */ + for (i = 1; i <= m; i++) + { if (i == p) continue; + /* xB.new[i] = xB[i] + alfa[i,q] * delta xN[q] */ + if (mpq_sgn(aq[i]) == 0) continue; + mpq_mul(temp, aq[i], ssx->delta); + mpq_add(bbar[i], bbar[i], temp); + } +#if 1 + /* update value of the objective function */ + /* z.new = z + d[q] * delta xN[q] */ + mpq_mul(temp, cbar[q], ssx->delta); + mpq_add(bbar[0], bbar[0], temp); +#endif + mpq_clear(temp); + return; +} + +/*---------------------------------------------------------------------- +-- ssx_update_pi - update simplex multipliers. +-- +-- This routine recomputes the vector of simplex multipliers for the +-- adjacent basis. */ + +void ssx_update_pi(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + mpq_t *pi = ssx->pi; + mpq_t *cbar = ssx->cbar; + int p = ssx->p; + int q = ssx->q; + mpq_t *aq = ssx->aq; + mpq_t *rho = ssx->rho; + int i; + mpq_t new_dq, temp; + mpq_init(new_dq); + mpq_init(temp); + xassert(1 <= p && p <= m); + xassert(1 <= q && q <= n); + /* compute d[q] in the adjacent basis */ + mpq_div(new_dq, cbar[q], aq[p]); + /* update the vector of simplex multipliers */ + for (i = 1; i <= m; i++) + { if (mpq_sgn(rho[i]) == 0) continue; + mpq_mul(temp, new_dq, rho[i]); + mpq_sub(pi[i], pi[i], temp); + } + mpq_clear(new_dq); + mpq_clear(temp); + return; +} + +/*---------------------------------------------------------------------- +// ssx_update_cbar - update reduced costs of non-basic variables. +// +// This routine recomputes the vector of reduced costs of non-basic +// variables for the adjacent basis. */ + +void ssx_update_cbar(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + mpq_t *cbar = ssx->cbar; + int p = ssx->p; + int q = ssx->q; + mpq_t *ap = ssx->ap; + int j; + mpq_t temp; + mpq_init(temp); + xassert(1 <= p && p <= m); + xassert(1 <= q && q <= n); + /* compute d[q] in the adjacent basis */ + /* d.new[q] = d[q] / alfa[p,q] */ + mpq_div(cbar[q], cbar[q], ap[q]); + /* update reduced costs of other non-basic variables */ + for (j = 1; j <= n; j++) + { if (j == q) continue; + /* d.new[j] = d[j] - (alfa[p,j] / alfa[p,q]) * d[q] */ + if (mpq_sgn(ap[j]) == 0) continue; + mpq_mul(temp, ap[j], cbar[q]); + mpq_sub(cbar[j], cbar[j], temp); + } + mpq_clear(temp); + return; +} + +/*---------------------------------------------------------------------- +// ssx_change_basis - change current basis to adjacent one. +// +// This routine changes the current basis to the adjacent one swapping +// basic variable xB[p] and non-basic variable xN[q]. */ + +void ssx_change_basis(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + int *type = ssx->type; + int *stat = ssx->stat; + int *Q_row = ssx->Q_row; + int *Q_col = ssx->Q_col; + int p = ssx->p; + int q = ssx->q; + int p_stat = ssx->p_stat; + int k, kp, kq; + if (p < 0) + { /* special case: xN[q] goes to its opposite bound */ + xassert(1 <= q && q <= n); + k = Q_col[m+q]; /* x[k] = xN[q] */ + xassert(type[k] == SSX_DB); + switch (stat[k]) + { case SSX_NL: + stat[k] = SSX_NU; + break; + case SSX_NU: + stat[k] = SSX_NL; + break; + default: + xassert(stat != stat); + } + } + else + { /* xB[p] leaves the basis, xN[q] enters the basis */ + xassert(1 <= p && p <= m); + xassert(1 <= q && q <= n); + kp = Q_col[p]; /* x[kp] = xB[p] */ + kq = Q_col[m+q]; /* x[kq] = xN[q] */ + /* check non-basic status of xB[p] which becomes xN[q] */ + switch (type[kp]) + { case SSX_FR: + xassert(p_stat == SSX_NF); + break; + case SSX_LO: + xassert(p_stat == SSX_NL); + break; + case SSX_UP: + xassert(p_stat == SSX_NU); + break; + case SSX_DB: + xassert(p_stat == SSX_NL || p_stat == SSX_NU); + break; + case SSX_FX: + xassert(p_stat == SSX_NS); + break; + default: + xassert(type != type); + } + /* swap xB[p] and xN[q] */ + stat[kp] = (char)p_stat, stat[kq] = SSX_BS; + Q_row[kp] = m+q, Q_row[kq] = p; + Q_col[p] = kq, Q_col[m+q] = kp; + /* update factorization of the basis matrix */ + if (bfx_update(ssx->binv, p)) + { if (ssx_factorize(ssx)) + xassert(("Internal error: basis matrix is singular", 0)); + } + } + return; +} + +/*---------------------------------------------------------------------- +// ssx_delete - delete simplex solver workspace. +// +// This routine deletes the simplex solver workspace freeing all the +// memory allocated to this object. */ + +void ssx_delete(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + int nnz = ssx->A_ptr[n+1]-1; + int i, j, k; + xfree(ssx->type); + for (k = 1; k <= m+n; k++) mpq_clear(ssx->lb[k]); + xfree(ssx->lb); + for (k = 1; k <= m+n; k++) mpq_clear(ssx->ub[k]); + xfree(ssx->ub); + for (k = 0; k <= m+n; k++) mpq_clear(ssx->coef[k]); + xfree(ssx->coef); + xfree(ssx->A_ptr); + xfree(ssx->A_ind); + for (k = 1; k <= nnz; k++) mpq_clear(ssx->A_val[k]); + xfree(ssx->A_val); + xfree(ssx->stat); + xfree(ssx->Q_row); + xfree(ssx->Q_col); + bfx_delete_binv(ssx->binv); + for (i = 0; i <= m; i++) mpq_clear(ssx->bbar[i]); + xfree(ssx->bbar); + for (i = 1; i <= m; i++) mpq_clear(ssx->pi[i]); + xfree(ssx->pi); + for (j = 1; j <= n; j++) mpq_clear(ssx->cbar[j]); + xfree(ssx->cbar); + for (i = 1; i <= m; i++) mpq_clear(ssx->rho[i]); + xfree(ssx->rho); + for (j = 1; j <= n; j++) mpq_clear(ssx->ap[j]); + xfree(ssx->ap); + for (i = 1; i <= m; i++) mpq_clear(ssx->aq[i]); + xfree(ssx->aq); + mpq_clear(ssx->delta); + xfree(ssx); + return; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpssx02.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpssx02.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,479 @@ +/* glpssx02.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#include "glpenv.h" +#include "glpssx.h" + +static void show_progress(SSX *ssx, int phase) +{ /* this auxiliary routine displays information about progress of + the search */ + int i, def = 0; + for (i = 1; i <= ssx->m; i++) + if (ssx->type[ssx->Q_col[i]] == SSX_FX) def++; + xprintf("%s%6d: %s = %22.15g (%d)\n", phase == 1 ? " " : "*", + ssx->it_cnt, phase == 1 ? "infsum" : "objval", + mpq_get_d(ssx->bbar[0]), def); +#if 0 + ssx->tm_lag = utime(); +#else + ssx->tm_lag = xtime(); +#endif + return; +} + +/*---------------------------------------------------------------------- +// ssx_phase_I - find primal feasible solution. +// +// This routine implements phase I of the primal simplex method. +// +// On exit the routine returns one of the following codes: +// +// 0 - feasible solution found; +// 1 - problem has no feasible solution; +// 2 - iterations limit exceeded; +// 3 - time limit exceeded. +----------------------------------------------------------------------*/ + +int ssx_phase_I(SSX *ssx) +{ int m = ssx->m; + int n = ssx->n; + int *type = ssx->type; + mpq_t *lb = ssx->lb; + mpq_t *ub = ssx->ub; + mpq_t *coef = ssx->coef; + int *A_ptr = ssx->A_ptr; + int *A_ind = ssx->A_ind; + mpq_t *A_val = ssx->A_val; + int *Q_col = ssx->Q_col; + mpq_t *bbar = ssx->bbar; + mpq_t *pi = ssx->pi; + mpq_t *cbar = ssx->cbar; + int *orig_type, orig_dir; + mpq_t *orig_lb, *orig_ub, *orig_coef; + int i, k, ret; + /* save components of the original LP problem, which are changed + by the routine */ + orig_type = xcalloc(1+m+n, sizeof(int)); + orig_lb = xcalloc(1+m+n, sizeof(mpq_t)); + orig_ub = xcalloc(1+m+n, sizeof(mpq_t)); + orig_coef = xcalloc(1+m+n, sizeof(mpq_t)); + for (k = 1; k <= m+n; k++) + { orig_type[k] = type[k]; + mpq_init(orig_lb[k]); + mpq_set(orig_lb[k], lb[k]); + mpq_init(orig_ub[k]); + mpq_set(orig_ub[k], ub[k]); + } + orig_dir = ssx->dir; + for (k = 0; k <= m+n; k++) + { mpq_init(orig_coef[k]); + mpq_set(orig_coef[k], coef[k]); + } + /* build an artificial basic solution, which is primal feasible, + and also build an auxiliary objective function to minimize the + sum of infeasibilities for the original problem */ + ssx->dir = SSX_MIN; + for (k = 0; k <= m+n; k++) mpq_set_si(coef[k], 0, 1); + mpq_set_si(bbar[0], 0, 1); + for (i = 1; i <= m; i++) + { int t; + k = Q_col[i]; /* x[k] = xB[i] */ + t = type[k]; + if (t == SSX_LO || t == SSX_DB || t == SSX_FX) + { /* in the original problem x[k] has lower bound */ + if (mpq_cmp(bbar[i], lb[k]) < 0) + { /* which is violated */ + type[k] = SSX_UP; + mpq_set(ub[k], lb[k]); + mpq_set_si(lb[k], 0, 1); + mpq_set_si(coef[k], -1, 1); + mpq_add(bbar[0], bbar[0], ub[k]); + mpq_sub(bbar[0], bbar[0], bbar[i]); + } + } + if (t == SSX_UP || t == SSX_DB || t == SSX_FX) + { /* in the original problem x[k] has upper bound */ + if (mpq_cmp(bbar[i], ub[k]) > 0) + { /* which is violated */ + type[k] = SSX_LO; + mpq_set(lb[k], ub[k]); + mpq_set_si(ub[k], 0, 1); + mpq_set_si(coef[k], +1, 1); + mpq_add(bbar[0], bbar[0], bbar[i]); + mpq_sub(bbar[0], bbar[0], lb[k]); + } + } + } + /* now the initial basic solution should be primal feasible due + to changes of bounds of some basic variables, which turned to + implicit artifical variables */ + /* compute simplex multipliers and reduced costs */ + ssx_eval_pi(ssx); + ssx_eval_cbar(ssx); + /* display initial progress of the search */ + show_progress(ssx, 1); + /* main loop starts here */ + for (;;) + { /* display current progress of the search */ +#if 0 + if (utime() - ssx->tm_lag >= ssx->out_frq - 0.001) +#else + if (xdifftime(xtime(), ssx->tm_lag) >= ssx->out_frq - 0.001) +#endif + show_progress(ssx, 1); + /* we do not need to wait until all artificial variables have + left the basis */ + if (mpq_sgn(bbar[0]) == 0) + { /* the sum of infeasibilities is zero, therefore the current + solution is primal feasible for the original problem */ + ret = 0; + break; + } + /* check if the iterations limit has been exhausted */ + if (ssx->it_lim == 0) + { ret = 2; + break; + } + /* check if the time limit has been exhausted */ +#if 0 + if (ssx->tm_lim >= 0.0 && ssx->tm_lim <= utime() - ssx->tm_beg) +#else + if (ssx->tm_lim >= 0.0 && + ssx->tm_lim <= xdifftime(xtime(), ssx->tm_beg)) +#endif + { ret = 3; + break; + } + /* choose non-basic variable xN[q] */ + ssx_chuzc(ssx); + /* if xN[q] cannot be chosen, the sum of infeasibilities is + minimal but non-zero; therefore the original problem has no + primal feasible solution */ + if (ssx->q == 0) + { ret = 1; + break; + } + /* compute q-th column of the simplex table */ + ssx_eval_col(ssx); + /* choose basic variable xB[p] */ + ssx_chuzr(ssx); + /* the sum of infeasibilities cannot be negative, therefore + the auxiliary lp problem cannot have unbounded solution */ + xassert(ssx->p != 0); + /* update values of basic variables */ + ssx_update_bbar(ssx); + if (ssx->p > 0) + { /* compute p-th row of the inverse inv(B) */ + ssx_eval_rho(ssx); + /* compute p-th row of the simplex table */ + ssx_eval_row(ssx); + xassert(mpq_cmp(ssx->aq[ssx->p], ssx->ap[ssx->q]) == 0); + /* update simplex multipliers */ + ssx_update_pi(ssx); + /* update reduced costs of non-basic variables */ + ssx_update_cbar(ssx); + } + /* xB[p] is leaving the basis; if it is implicit artificial + variable, the corresponding residual vanishes; therefore + bounds of this variable should be restored to the original + values */ + if (ssx->p > 0) + { k = Q_col[ssx->p]; /* x[k] = xB[p] */ + if (type[k] != orig_type[k]) + { /* x[k] is implicit artificial variable */ + type[k] = orig_type[k]; + mpq_set(lb[k], orig_lb[k]); + mpq_set(ub[k], orig_ub[k]); + xassert(ssx->p_stat == SSX_NL || ssx->p_stat == SSX_NU); + ssx->p_stat = (ssx->p_stat == SSX_NL ? SSX_NU : SSX_NL); + if (type[k] == SSX_FX) ssx->p_stat = SSX_NS; + /* nullify the objective coefficient at x[k] */ + mpq_set_si(coef[k], 0, 1); + /* since coef[k] has been changed, we need to compute + new reduced cost of x[k], which it will have in the + adjacent basis */ + /* the formula d[j] = cN[j] - pi' * N[j] is used (note + that the vector pi is not changed, because it depends + on objective coefficients at basic variables, but in + the adjacent basis, for which the vector pi has been + just recomputed, x[k] is non-basic) */ + if (k <= m) + { /* x[k] is auxiliary variable */ + mpq_neg(cbar[ssx->q], pi[k]); + } + else + { /* x[k] is structural variable */ + int ptr; + mpq_t temp; + mpq_init(temp); + mpq_set_si(cbar[ssx->q], 0, 1); + for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) + { mpq_mul(temp, pi[A_ind[ptr]], A_val[ptr]); + mpq_add(cbar[ssx->q], cbar[ssx->q], temp); + } + mpq_clear(temp); + } + } + } + /* jump to the adjacent vertex of the polyhedron */ + ssx_change_basis(ssx); + /* one simplex iteration has been performed */ + if (ssx->it_lim > 0) ssx->it_lim--; + ssx->it_cnt++; + } + /* display final progress of the search */ + show_progress(ssx, 1); + /* restore components of the original problem, which were changed + by the routine */ + for (k = 1; k <= m+n; k++) + { type[k] = orig_type[k]; + mpq_set(lb[k], orig_lb[k]); + mpq_clear(orig_lb[k]); + mpq_set(ub[k], orig_ub[k]); + mpq_clear(orig_ub[k]); + } + ssx->dir = orig_dir; + for (k = 0; k <= m+n; k++) + { mpq_set(coef[k], orig_coef[k]); + mpq_clear(orig_coef[k]); + } + xfree(orig_type); + xfree(orig_lb); + xfree(orig_ub); + xfree(orig_coef); + /* return to the calling program */ + return ret; +} + +/*---------------------------------------------------------------------- +// ssx_phase_II - find optimal solution. +// +// This routine implements phase II of the primal simplex method. +// +// On exit the routine returns one of the following codes: +// +// 0 - optimal solution found; +// 1 - problem has unbounded solution; +// 2 - iterations limit exceeded; +// 3 - time limit exceeded. +----------------------------------------------------------------------*/ + +int ssx_phase_II(SSX *ssx) +{ int ret; + /* display initial progress of the search */ + show_progress(ssx, 2); + /* main loop starts here */ + for (;;) + { /* display current progress of the search */ +#if 0 + if (utime() - ssx->tm_lag >= ssx->out_frq - 0.001) +#else + if (xdifftime(xtime(), ssx->tm_lag) >= ssx->out_frq - 0.001) +#endif + show_progress(ssx, 2); + /* check if the iterations limit has been exhausted */ + if (ssx->it_lim == 0) + { ret = 2; + break; + } + /* check if the time limit has been exhausted */ +#if 0 + if (ssx->tm_lim >= 0.0 && ssx->tm_lim <= utime() - ssx->tm_beg) +#else + if (ssx->tm_lim >= 0.0 && + ssx->tm_lim <= xdifftime(xtime(), ssx->tm_beg)) +#endif + { ret = 3; + break; + } + /* choose non-basic variable xN[q] */ + ssx_chuzc(ssx); + /* if xN[q] cannot be chosen, the current basic solution is + dual feasible and therefore optimal */ + if (ssx->q == 0) + { ret = 0; + break; + } + /* compute q-th column of the simplex table */ + ssx_eval_col(ssx); + /* choose basic variable xB[p] */ + ssx_chuzr(ssx); + /* if xB[p] cannot be chosen, the problem has no dual feasible + solution (i.e. unbounded) */ + if (ssx->p == 0) + { ret = 1; + break; + } + /* update values of basic variables */ + ssx_update_bbar(ssx); + if (ssx->p > 0) + { /* compute p-th row of the inverse inv(B) */ + ssx_eval_rho(ssx); + /* compute p-th row of the simplex table */ + ssx_eval_row(ssx); + xassert(mpq_cmp(ssx->aq[ssx->p], ssx->ap[ssx->q]) == 0); +#if 0 + /* update simplex multipliers */ + ssx_update_pi(ssx); +#endif + /* update reduced costs of non-basic variables */ + ssx_update_cbar(ssx); + } + /* jump to the adjacent vertex of the polyhedron */ + ssx_change_basis(ssx); + /* one simplex iteration has been performed */ + if (ssx->it_lim > 0) ssx->it_lim--; + ssx->it_cnt++; + } + /* display final progress of the search */ + show_progress(ssx, 2); + /* return to the calling program */ + return ret; +} + +/*---------------------------------------------------------------------- +// ssx_driver - base driver to exact simplex method. +// +// This routine is a base driver to a version of the primal simplex +// method using exact (bignum) arithmetic. +// +// On exit the routine returns one of the following codes: +// +// 0 - optimal solution found; +// 1 - problem has no feasible solution; +// 2 - problem has unbounded solution; +// 3 - iterations limit exceeded (phase I); +// 4 - iterations limit exceeded (phase II); +// 5 - time limit exceeded (phase I); +// 6 - time limit exceeded (phase II); +// 7 - initial basis matrix is exactly singular. +----------------------------------------------------------------------*/ + +int ssx_driver(SSX *ssx) +{ int m = ssx->m; + int *type = ssx->type; + mpq_t *lb = ssx->lb; + mpq_t *ub = ssx->ub; + int *Q_col = ssx->Q_col; + mpq_t *bbar = ssx->bbar; + int i, k, ret; + ssx->tm_beg = xtime(); + /* factorize the initial basis matrix */ + if (ssx_factorize(ssx)) + { xprintf("Initial basis matrix is singular\n"); + ret = 7; + goto done; + } + /* compute values of basic variables */ + ssx_eval_bbar(ssx); + /* check if the initial basic solution is primal feasible */ + for (i = 1; i <= m; i++) + { int t; + k = Q_col[i]; /* x[k] = xB[i] */ + t = type[k]; + if (t == SSX_LO || t == SSX_DB || t == SSX_FX) + { /* x[k] has lower bound */ + if (mpq_cmp(bbar[i], lb[k]) < 0) + { /* which is violated */ + break; + } + } + if (t == SSX_UP || t == SSX_DB || t == SSX_FX) + { /* x[k] has upper bound */ + if (mpq_cmp(bbar[i], ub[k]) > 0) + { /* which is violated */ + break; + } + } + } + if (i > m) + { /* no basic variable violates its bounds */ + ret = 0; + goto skip; + } + /* phase I: find primal feasible solution */ + ret = ssx_phase_I(ssx); + switch (ret) + { case 0: + ret = 0; + break; + case 1: + xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n"); + ret = 1; + break; + case 2: + xprintf("ITERATIONS LIMIT EXCEEDED; SEARCH TERMINATED\n"); + ret = 3; + break; + case 3: + xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); + ret = 5; + break; + default: + xassert(ret != ret); + } + /* compute values of basic variables (actually only the objective + value needs to be computed) */ + ssx_eval_bbar(ssx); +skip: /* compute simplex multipliers */ + ssx_eval_pi(ssx); + /* compute reduced costs of non-basic variables */ + ssx_eval_cbar(ssx); + /* if phase I failed, do not start phase II */ + if (ret != 0) goto done; + /* phase II: find optimal solution */ + ret = ssx_phase_II(ssx); + switch (ret) + { case 0: + xprintf("OPTIMAL SOLUTION FOUND\n"); + ret = 0; + break; + case 1: + xprintf("PROBLEM HAS UNBOUNDED SOLUTION\n"); + ret = 2; + break; + case 2: + xprintf("ITERATIONS LIMIT EXCEEDED; SEARCH TERMINATED\n"); + ret = 4; + break; + case 3: + xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); + ret = 6; + break; + default: + xassert(ret != ret); + } +done: /* decrease the time limit by the spent amount of time */ + if (ssx->tm_lim >= 0.0) +#if 0 + { ssx->tm_lim -= utime() - ssx->tm_beg; +#else + { ssx->tm_lim -= xdifftime(xtime(), ssx->tm_beg); +#endif + if (ssx->tm_lim < 0.0) ssx->tm_lim = 0.0; + } + return ret; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glpstd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glpstd.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,43 @@ +/* glpstd.h (standard C headers) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPSTD_H +#define GLPSTD_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glptsp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glptsp.c Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,667 @@ +/* glptsp.c */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#define _GLPSTD_ERRNO +#define _GLPSTD_STDIO +#include "glpenv.h" +#include "glptsp.h" +#define xfault xerror + +/*---------------------------------------------------------------------- +-- tsp_read_data - read TSP instance data. +-- +-- *Synopsis* +-- +-- #include "glptsp.h" +-- TSP *tsp_read_data(char *fname); +-- +-- *Description* +-- +-- The routine tsp_read_data reads a TSP (or related problem) instance +-- data from the text file, whose name is the character string fname. +-- +-- For detailed description of the format recognized by the routine see +-- the report: G.Reinelt, TSPLIB 95. +-- +-- *Returns* +-- +-- If no error occurred, the routine tsp_read_data returns a pointer to +-- the TSP instance data block, which contains loaded data. In the case +-- of error the routine prints an error message and returns NULL. */ + +struct dsa +{ /* dynamic storage area used by the routine tsp_read_data */ + char *fname; + /* name of the input text file */ + FILE *fp; + /* stream assigned to the input text file */ + int seqn; + /* line sequential number */ + int c; + /* current character */ + char token[255+1]; + /* current token */ +}; + +static int get_char(struct dsa *dsa) +{ dsa->c = fgetc(dsa->fp); + if (ferror(dsa->fp)) + { xprintf("%s:%d: read error - %s\n", + dsa->fname, dsa->seqn, strerror(errno)); + return 1; + } + if (feof(dsa->fp)) + dsa->c = EOF; + else if (dsa->c == '\n') + dsa->seqn++; + else if (isspace(dsa->c)) + dsa->c = ' '; + else if (iscntrl(dsa->c)) + { xprintf("%s:%d: invalid control character 0x%02X\n", + dsa->fname, dsa->seqn, dsa->c); + return 1; + } + return 0; +} + +static int skip_spaces(struct dsa *dsa, int across) +{ while (dsa->c == ' ' || (across && dsa->c == '\n')) + if (get_char(dsa)) return 1; + return 0; +} + +static int scan_keyword(struct dsa *dsa) +{ int len = 0; + if (skip_spaces(dsa, 0)) return 1; + dsa->token[0] = '\0'; + while (isalnum(dsa->c) || dsa->c == '_') + { if (len == 31) + { xprintf("%s:%d: keyword `%s...' too long\n", dsa->fname, + dsa->seqn, dsa->token); + return 1; + } + dsa->token[len++] = (char)dsa->c, dsa->token[len] = '\0'; + if (get_char(dsa)) return 1; + } + if (len == 0) + { xprintf("%s:%d: missing keyword\n", dsa->fname, dsa->seqn); + return 1; + } + return 0; +} + +static int check_colon(struct dsa *dsa) +{ if (skip_spaces(dsa, 0)) return 1; + if (dsa->c != ':') + { xprintf("%s:%d: missing colon after `%s'\n", dsa->fname, + dsa->seqn, dsa->token); + return 1; + } + if (get_char(dsa)) return 1; + return 0; +} + +static int scan_token(struct dsa *dsa, int across) +{ int len = 0; + if (skip_spaces(dsa, across)) return 1; + dsa->token[0] = '\0'; + while (!(dsa->c == EOF || dsa->c == '\n' || dsa->c == ' ')) + { if (len == 255) + { dsa->token[31] = '\0'; + xprintf("%s:%d: token `%s...' too long\n", dsa->fname, + dsa->seqn, dsa->token); + return 1; + } + dsa->token[len++] = (char)dsa->c, dsa->token[len] = '\0'; + if (get_char(dsa)) return 1; + } + return 0; +} + +static int check_newline(struct dsa *dsa) +{ if (skip_spaces(dsa, 0)) return 1; + if (!(dsa->c == EOF || dsa->c == '\n')) + { xprintf("%s:%d: extra symbols detected\n", dsa->fname, + dsa->seqn); + return 1; + } + if (get_char(dsa)) return 1; + return 0; +} + +static int scan_comment(struct dsa *dsa) +{ int len = 0; + if (skip_spaces(dsa, 0)) return 1; + dsa->token[0] = '\0'; + while (!(dsa->c == EOF || dsa->c == '\n')) + { if (len == 255) + { xprintf("%s:%d: comment too long\n", dsa->fname, dsa->seqn) + ; + return 1; + } + dsa->token[len++] = (char)dsa->c, dsa->token[len] = '\0'; + if (get_char(dsa)) return 1; + } + return 0; +} + +static int scan_integer(struct dsa *dsa, int across, int *val) +{ if (scan_token(dsa, across)) return 1; + if (strlen(dsa->token) == 0) + { xprintf("%s:%d: missing integer\n", dsa->fname, dsa->seqn); + return 1; + } + if (str2int(dsa->token, val)) + { xprintf("%s:%d: integer `%s' invalid\n", dsa->fname, dsa->seqn + , dsa->token); + return 1; + } + return 0; +} + +static int scan_number(struct dsa *dsa, int across, double *val) +{ if (scan_token(dsa, across)) return 1; + if (strlen(dsa->token) == 0) + { xprintf("%s:%d: missing number\n", dsa->fname, dsa->seqn); + return 1; + } + if (str2num(dsa->token, val)) + { xprintf("%s:%d: number `%s' invalid\n", dsa->fname, dsa->seqn, + dsa->token); + return 1; + } + return 0; +} + +TSP *tsp_read_data(char *fname) +{ struct dsa _dsa, *dsa = &_dsa; + TSP *tsp = NULL; + dsa->fname = fname; + xprintf("tsp_read_data: reading TSP data from `%s'...\n", + dsa->fname); + dsa->fp = fopen(dsa->fname, "r"); + if (dsa->fp == NULL) + { xprintf("tsp_read_data: unable to open `%s' - %s\n", + dsa->fname, strerror(errno)); + goto fail; + } + tsp = xmalloc(sizeof(TSP)); + tsp->name = NULL; + tsp->type = TSP_UNDEF; + tsp->comment = NULL; + tsp->dimension = 0; + tsp->edge_weight_type = TSP_UNDEF; + tsp->edge_weight_format = TSP_UNDEF; + tsp->display_data_type = TSP_UNDEF; + tsp->node_x_coord = NULL; + tsp->node_y_coord = NULL; + tsp->dply_x_coord = NULL; + tsp->dply_y_coord = NULL; + tsp->tour = NULL; + tsp->edge_weight = NULL; + dsa->seqn = 1; + if (get_char(dsa)) goto fail; +loop: if (scan_keyword(dsa)) goto fail; + if (strcmp(dsa->token, "NAME") == 0) + { if (tsp->name != NULL) + { xprintf("%s:%d: NAME entry multiply defined\n", dsa->fname, + dsa->seqn); + goto fail; + } + if (check_colon(dsa)) goto fail; + if (scan_token(dsa, 0)) goto fail; + if (strlen(dsa->token) == 0) + { xprintf("%s:%d: NAME entry incomplete\n", dsa->fname, + dsa->seqn); + goto fail; + } + tsp->name = xmalloc(strlen(dsa->token) + 1); + strcpy(tsp->name, dsa->token); + xprintf("tsp_read_data: NAME: %s\n", tsp->name); + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "TYPE") == 0) + { if (tsp->type != TSP_UNDEF) + { xprintf("%s:%d: TYPE entry multiply defined\n", dsa->fname, + dsa->seqn); + goto fail; + } + if (check_colon(dsa)) goto fail; + if (scan_keyword(dsa)) goto fail; + if (strcmp(dsa->token, "TSP") == 0) + tsp->type = TSP_TSP; + else if (strcmp(dsa->token, "ATSP") == 0) + tsp->type = TSP_ATSP; + else if (strcmp(dsa->token, "TOUR") == 0) + tsp->type = TSP_TOUR; + else + { xprintf("%s:%d: data type `%s' not recognized\n", + dsa->fname, dsa->seqn, dsa->token); + goto fail; + } + xprintf("tsp_read_data: TYPE: %s\n", dsa->token); + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "COMMENT") == 0) + { if (tsp->comment != NULL) + { xprintf("%s:%d: COMMENT entry multiply defined\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_colon(dsa)) goto fail; + if (scan_comment(dsa)) goto fail; + tsp->comment = xmalloc(strlen(dsa->token) + 1); + strcpy(tsp->comment, dsa->token); + xprintf("tsp_read_data: COMMENT: %s\n", tsp->comment); + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "DIMENSION") == 0) + { if (tsp->dimension != 0) + { xprintf("%s:%d: DIMENSION entry multiply defined\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_colon(dsa)) goto fail; + if (scan_integer(dsa, 0, &tsp->dimension)) goto fail; + if (tsp->dimension < 1) + { xprintf("%s:%d: invalid dimension\n", dsa->fname, + dsa->seqn); + goto fail; + } + xprintf("tsp_read_data: DIMENSION: %d\n", tsp->dimension); + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "EDGE_WEIGHT_TYPE") == 0) + { if (tsp->edge_weight_type != TSP_UNDEF) + { xprintf("%s:%d: EDGE_WEIGHT_TYPE entry multiply defined\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_colon(dsa)) goto fail; + if (scan_keyword(dsa)) goto fail; + if (strcmp(dsa->token, "GEO") == 0) + tsp->edge_weight_type = TSP_GEO; + else if (strcmp(dsa->token, "EUC_2D") == 0) + tsp->edge_weight_type = TSP_EUC_2D; + else if (strcmp(dsa->token, "ATT") == 0) + tsp->edge_weight_type = TSP_ATT; + else if (strcmp(dsa->token, "EXPLICIT") == 0) + tsp->edge_weight_type = TSP_EXPLICIT; + else if (strcmp(dsa->token, "CEIL_2D") == 0) + tsp->edge_weight_type = TSP_CEIL_2D; + else + { xprintf("%s:%d: edge weight type `%s' not recognized\n", + dsa->fname, dsa->seqn, dsa->token); + goto fail; + } + xprintf("tsp_read_data: EDGE_WEIGHT_TYPE: %s\n", dsa->token); + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "EDGE_WEIGHT_FORMAT") == 0) + { if (tsp->edge_weight_format != TSP_UNDEF) + { xprintf( + "%s:%d: EDGE_WEIGHT_FORMAT entry multiply defined\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_colon(dsa)) goto fail; + if (scan_keyword(dsa)) goto fail; + if (strcmp(dsa->token, "UPPER_ROW") == 0) + tsp->edge_weight_format = TSP_UPPER_ROW; + else if (strcmp(dsa->token, "FULL_MATRIX") == 0) + tsp->edge_weight_format = TSP_FULL_MATRIX; + else if (strcmp(dsa->token, "FUNCTION") == 0) + tsp->edge_weight_format = TSP_FUNCTION; + else if (strcmp(dsa->token, "LOWER_DIAG_ROW") == 0) + tsp->edge_weight_format = TSP_LOWER_DIAG_ROW; + else + { xprintf("%s:%d: edge weight format `%s' not recognized\n", + dsa->fname, dsa->seqn, dsa->token); + goto fail; + } + xprintf("tsp_read_data: EDGE_WEIGHT_FORMAT: %s\n", dsa->token); + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "DISPLAY_DATA_TYPE") == 0) + { if (tsp->display_data_type != TSP_UNDEF) + { xprintf("%s:%d: DISPLAY_DATA_TYPE entry multiply defined\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_colon(dsa)) goto fail; + if (scan_keyword(dsa)) goto fail; + if (strcmp(dsa->token, "COORD_DISPLAY") == 0) + tsp->display_data_type = TSP_COORD_DISPLAY; + else if (strcmp(dsa->token, "TWOD_DISPLAY") == 0) + tsp->display_data_type = TSP_TWOD_DISPLAY; + else + { xprintf("%s:%d: display data type `%s' not recognized\n", + dsa->fname, dsa->seqn, dsa->token); + goto fail; + } + xprintf("tsp_read_data: DISPLAY_DATA_TYPE: %s\n", dsa->token); + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "NODE_COORD_SECTION") == 0) + { int n = tsp->dimension, k, node; + if (n == 0) + { xprintf("%s:%d: DIMENSION entry not specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (tsp->node_x_coord != NULL) + { xprintf("%s:%d: NODE_COORD_SECTION multiply specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_newline(dsa)) goto fail; + tsp->node_x_coord = xcalloc(1+n, sizeof(double)); + tsp->node_y_coord = xcalloc(1+n, sizeof(double)); + for (node = 1; node <= n; node++) + tsp->node_x_coord[node] = tsp->node_y_coord[node] = DBL_MAX; + for (k = 1; k <= n; k++) + { if (scan_integer(dsa, 0, &node)) goto fail; + if (!(1 <= node && node <= n)) + { xprintf("%s:%d: invalid node number %d\n", dsa->fname, + dsa->seqn, node); + goto fail; + } + if (tsp->node_x_coord[node] != DBL_MAX) + { xprintf("%s:%d: node number %d multiply specified\n", + dsa->fname, dsa->seqn, node); + goto fail; + } + if (scan_number(dsa, 0, &tsp->node_x_coord[node])) + goto fail; + if (scan_number(dsa, 0, &tsp->node_y_coord[node])) + goto fail; + if (check_newline(dsa)) goto fail; + } + } + else if (strcmp(dsa->token, "DISPLAY_DATA_SECTION") == 0) + { int n = tsp->dimension, k, node; + if (n == 0) + { xprintf("%s:%d: DIMENSION entry not specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (tsp->dply_x_coord != NULL) + { xprintf("%s:%d: DISPLAY_DATA_SECTION multiply specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_newline(dsa)) goto fail; + tsp->dply_x_coord = xcalloc(1+n, sizeof(double)); + tsp->dply_y_coord = xcalloc(1+n, sizeof(double)); + for (node = 1; node <= n; node++) + tsp->dply_x_coord[node] = tsp->dply_y_coord[node] = DBL_MAX; + for (k = 1; k <= n; k++) + { if (scan_integer(dsa, 0, &node)) goto fail; + if (!(1 <= node && node <= n)) + { xprintf("%s:%d: invalid node number %d\n", dsa->fname, + dsa->seqn, node); + goto fail; + } + if (tsp->dply_x_coord[node] != DBL_MAX) + { xprintf("%s:%d: node number %d multiply specified\n", + dsa->fname, dsa->seqn, node); + goto fail; + } + if (scan_number(dsa, 0, &tsp->dply_x_coord[node])) + goto fail; + if (scan_number(dsa, 0, &tsp->dply_y_coord[node])) + goto fail; + if (check_newline(dsa)) goto fail; + } + } + else if (strcmp(dsa->token, "TOUR_SECTION") == 0) + { int n = tsp->dimension, k, node; + if (n == 0) + { xprintf("%s:%d: DIMENSION entry not specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (tsp->tour != NULL) + { xprintf("%s:%d: TOUR_SECTION multiply specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_newline(dsa)) goto fail; + tsp->tour = xcalloc(1+n, sizeof(int)); + for (k = 1; k <= n; k++) + { if (scan_integer(dsa, 1, &node)) goto fail; + if (!(1 <= node && node <= n)) + { xprintf("%s:%d: invalid node number %d\n", dsa->fname, + dsa->seqn, node); + goto fail; + } + tsp->tour[k] = node; + } + if (scan_integer(dsa, 1, &node)) goto fail; + if (node != -1) + { xprintf("%s:%d: extra node(s) detected\n", dsa->fname, + dsa->seqn); + goto fail; + } + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "EDGE_WEIGHT_SECTION") == 0) + { int n = tsp->dimension, i, j, temp; + if (n == 0) + { xprintf("%s:%d: DIMENSION entry not specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (tsp->edge_weight_format == TSP_UNDEF) + { xprintf("%s:%d: EDGE_WEIGHT_FORMAT entry not specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (tsp->edge_weight != NULL) + { xprintf("%s:%d: EDGE_WEIGHT_SECTION multiply specified\n", + dsa->fname, dsa->seqn); + goto fail; + } + if (check_newline(dsa)) goto fail; + tsp->edge_weight = xcalloc(1+n*n, sizeof(int)); + switch (tsp->edge_weight_format) + { case TSP_FULL_MATRIX: + for (i = 1; i <= n; i++) + { for (j = 1; j <= n; j++) + { if (scan_integer(dsa, 1, &temp)) goto fail; + tsp->edge_weight[(i - 1) * n + j] = temp; + } + } + break; + case TSP_UPPER_ROW: + for (i = 1; i <= n; i++) + { tsp->edge_weight[(i - 1) * n + i] = 0; + for (j = i + 1; j <= n; j++) + { if (scan_integer(dsa, 1, &temp)) goto fail; + tsp->edge_weight[(i - 1) * n + j] = temp; + tsp->edge_weight[(j - 1) * n + i] = temp; + } + } + break; + case TSP_LOWER_DIAG_ROW: + for (i = 1; i <= n; i++) + { for (j = 1; j <= i; j++) + { if (scan_integer(dsa, 1, &temp)) goto fail; + tsp->edge_weight[(i - 1) * n + j] = temp; + tsp->edge_weight[(j - 1) * n + i] = temp; + } + } + break; + default: + goto fail; + } + if (check_newline(dsa)) goto fail; + } + else if (strcmp(dsa->token, "EOF") == 0) + { if (check_newline(dsa)) goto fail; + goto done; + } + else + { xprintf("%s:%d: keyword `%s' not recognized\n", dsa->fname, + dsa->seqn, dsa->token); + goto fail; + } + goto loop; +done: xprintf("tsp_read_data: %d lines were read\n", dsa->seqn-1); + fclose(dsa->fp); + return tsp; +fail: if (tsp != NULL) + { if (tsp->name != NULL) xfree(tsp->name); + if (tsp->comment != NULL) xfree(tsp->comment); + if (tsp->node_x_coord != NULL) xfree(tsp->node_x_coord); + if (tsp->node_y_coord != NULL) xfree(tsp->node_y_coord); + if (tsp->dply_x_coord != NULL) xfree(tsp->dply_x_coord); + if (tsp->dply_y_coord != NULL) xfree(tsp->dply_y_coord); + if (tsp->tour != NULL) xfree(tsp->tour); + if (tsp->edge_weight != NULL) xfree(tsp->edge_weight); + xfree(tsp); + } + if (dsa->fp != NULL) fclose(dsa->fp); + return NULL; +} + +/*---------------------------------------------------------------------- +-- tsp_free_data - free TSP instance data. +-- +-- *Synopsis* +-- +-- #include "glptsp.h" +-- void tsp_free_data(TSP *tsp); +-- +-- *Description* +-- +-- The routine tsp_free_data frees all the memory allocated to the TSP +-- instance data block, which the parameter tsp points to. */ + +void tsp_free_data(TSP *tsp) +{ if (tsp->name != NULL) xfree(tsp->name); + if (tsp->comment != NULL) xfree(tsp->comment); + if (tsp->node_x_coord != NULL) xfree(tsp->node_x_coord); + if (tsp->node_y_coord != NULL) xfree(tsp->node_y_coord); + if (tsp->dply_x_coord != NULL) xfree(tsp->dply_x_coord); + if (tsp->dply_y_coord != NULL) xfree(tsp->dply_y_coord); + if (tsp->tour != NULL) xfree(tsp->tour); + if (tsp->edge_weight != NULL) xfree(tsp->edge_weight); + xfree(tsp); + return; +} + +/*---------------------------------------------------------------------- +-- tsp_distance - compute distance between two nodes. +-- +-- *Synopsis* +-- +-- #include "glptsp.h" +-- int tsp_distance(TSP *tsp, int i, int j); +-- +-- *Description* +-- +-- The routine tsp_distance computes the distance between i-th and j-th +-- nodes for the TSP instance, which tsp points to. +-- +-- *Returns* +-- +-- The routine tsp_distance returns the computed distance. */ + +#define nint(x) ((int)((x) + 0.5)) + +static double rad(double x) +{ /* convert input coordinate to longitude/latitude, in radians */ + double pi = 3.141592, deg, min; + deg = (int)x; + min = x - deg; + return pi * (deg + 5.0 * min / 3.0) / 180.0; +} + +int tsp_distance(TSP *tsp, int i, int j) +{ int n = tsp->dimension, dij; + if (!(tsp->type == TSP_TSP || tsp->type == TSP_ATSP)) + xfault("tsp_distance: invalid TSP instance\n"); + if (!(1 <= i && i <= n && 1 <= j && j <= n)) + xfault("tsp_distance: node number out of range\n"); + switch (tsp->edge_weight_type) + { case TSP_UNDEF: + xfault("tsp_distance: edge weight type not specified\n"); + case TSP_EXPLICIT: + if (tsp->edge_weight == NULL) + xfault("tsp_distance: edge weights not specified\n"); + dij = tsp->edge_weight[(i - 1) * n + j]; + break; + case TSP_EUC_2D: + if (tsp->node_x_coord == NULL || tsp->node_y_coord == NULL) + xfault("tsp_distance: node coordinates not specified\n"); + { double xd, yd; + xd = tsp->node_x_coord[i] - tsp->node_x_coord[j]; + yd = tsp->node_y_coord[i] - tsp->node_y_coord[j]; + dij = nint(sqrt(xd * xd + yd * yd)); + } + break; + case TSP_CEIL_2D: + if (tsp->node_x_coord == NULL || tsp->node_y_coord == NULL) + xfault("tsp_distance: node coordinates not specified\n"); + { double xd, yd; + xd = tsp->node_x_coord[i] - tsp->node_x_coord[j]; + yd = tsp->node_y_coord[i] - tsp->node_y_coord[j]; + dij = (int)ceil(sqrt(xd * xd + yd * yd)); + } + break; + case TSP_GEO: + if (tsp->node_x_coord == NULL || tsp->node_y_coord == NULL) + xfault("tsp_distance: node coordinates not specified\n"); + { double rrr = 6378.388; + double latitude_i = rad(tsp->node_x_coord[i]); + double latitude_j = rad(tsp->node_x_coord[j]); + double longitude_i = rad(tsp->node_y_coord[i]); + double longitude_j = rad(tsp->node_y_coord[j]); + double q1 = cos(longitude_i - longitude_j); + double q2 = cos(latitude_i - latitude_j); + double q3 = cos(latitude_i + latitude_j); + dij = (int)(rrr * acos(0.5 * ((1.0 + q1) * q2 - + (1.0 - q1) *q3)) + 1.0); + } + break; + case TSP_ATT: + if (tsp->node_x_coord == NULL || tsp->node_y_coord == NULL) + xfault("tsp_distance: node coordinates not specified\n"); + { int tij; + double xd, yd, rij; + xd = tsp->node_x_coord[i] - tsp->node_x_coord[j]; + yd = tsp->node_y_coord[i] - tsp->node_y_coord[j]; + rij = sqrt((xd * xd + yd * yd) / 10.0); + tij = nint(rij); + if (tij < rij) dij = tij + 1; else dij = tij; + } + break; + default: + xassert(tsp->edge_weight_type != tsp->edge_weight_type); + } + return dij; +} + +/* eof */ diff -r d59bea55db9b -r c445c931472f src/glptsp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glptsp.h Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,104 @@ +/* glptsp.h (TSP format) */ + +/*********************************************************************** +* This code is part of GLPK (GNU Linear Programming Kit). +* +* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +* 2009, 2010 Andrew Makhorin, Department for Applied Informatics, +* Moscow Aviation Institute, Moscow, Russia. All rights reserved. +* E-mail: . +* +* GLPK is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GLPK is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GLPK. If not, see . +***********************************************************************/ + +#ifndef GLPTSP_H +#define GLPTSP_H + +typedef struct TSP TSP; + +struct TSP +{ /* TSP (or related problem) instance in the format described in + the report [G.Reinelt, TSPLIB 95] */ + /*--------------------------------------------------------------*/ + /* the specification part */ + char *name; + /* identifies the data file */ + int type; + /* specifies the type of data: */ +#define TSP_UNDEF 0 /* undefined */ +#define TSP_TSP 1 /* symmetric TSP */ +#define TSP_ATSP 2 /* asymmetric TSP */ +#define TSP_TOUR 3 /* collection of tours */ + char *comment; + /* additional comments (usually the name of the contributor or + creator of the problem instance is given here) */ + int dimension; + /* for a TSP or ATSP, the dimension is the number of its nodes + for a TOUR it is the dimension of the corresponding problem */ + int edge_weight_type; + /* specifies how the edge weights (or distances) are given: */ +#define TSP_UNDEF 0 /* undefined */ +#define TSP_EXPLICIT 1 /* listed explicitly */ +#define TSP_EUC_2D 2 /* Eucl. distances in 2-D */ +#define TSP_CEIL_2D 3 /* Eucl. distances in 2-D rounded up */ +#define TSP_GEO 4 /* geographical distances */ +#define TSP_ATT 5 /* special distance function */ + int edge_weight_format; + /* describes the format of the edge weights if they are given + explicitly: */ +#define TSP_UNDEF 0 /* undefined */ +#define TSP_FUNCTION 1 /* given by a function */ +#define TSP_FULL_MATRIX 2 /* given by a full matrix */ +#define TSP_UPPER_ROW 3 /* upper triangulat matrix (row-wise + without diagonal entries) */ +#define TSP_LOWER_DIAG_ROW 4 /* lower triangular matrix (row-wise + including diagonal entries) */ + int display_data_type; + /* specifies how a graphical display of the nodes can be + obtained: */ +#define TSP_UNDEF 0 /* undefined */ +#define TSP_COORD_DISPLAY 1 /* display is generated from the node + coordinates */ +#define TSP_TWOD_DISPLAY 2 /* explicit coordinates in 2-D are + given */ + /*--------------------------------------------------------------*/ + /* data part */ + /* NODE_COORD_SECTION: */ + double *node_x_coord; /* double node_x_coord[1+dimension]; */ + double *node_y_coord; /* double node_y_coord[1+dimension]; */ + /* DISPLAY_DATA_SECTION: */ + double *dply_x_coord; /* double dply_x_coord[1+dimension]; */ + double *dply_y_coord; /* double dply_y_coord[1+dimension]; */ + /* TOUR_SECTION: */ + int *tour; /* int tour[1+dimension]; */ + /* EDGE_WEIGHT_SECTION: */ + int *edge_weight; /* int edge_weight[1+dimension*dimension]; */ +}; + +#define tsp_read_data _glp_tsp_read_data +#define tsp_free_data _glp_tsp_free_data +#define tsp_distance _glp_tsp_distance + +TSP *tsp_read_data(char *fname); +/* read TSP instance data */ + +void tsp_free_data(TSP *tsp); +/* free TSP instance data */ + +int tsp_distance(TSP *tsp, int i, int j); +/* compute distance between two nodes */ + +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f w32/Build_GLPK_with_VC10.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/Build_GLPK_with_VC10.bat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,11 @@ +rem Build GLPK with Microsoft Visual Studio Express 2010 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files\Microsoft Visual Studio 10.0\VC" + +call %HOME%\vcvarsall.bat x86 +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC +%HOME%\bin\nmake.exe /f Makefile_VC check + +pause diff -r d59bea55db9b -r c445c931472f w32/Build_GLPK_with_VC10_DLL.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/Build_GLPK_with_VC10_DLL.bat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,11 @@ +rem Build GLPK DLL with Microsoft Visual Studio Express 2010 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files\Microsoft Visual Studio 10.0\VC" + +call %HOME%\vcvarsall.bat x86 +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC_DLL +%HOME%\bin\nmake.exe /f Makefile_VC_DLL check + +pause diff -r d59bea55db9b -r c445c931472f w32/Build_GLPK_with_VC9.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/Build_GLPK_with_VC9.bat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,11 @@ +rem Build GLPK with Microsoft Visual Studio Express 2008 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files\Microsoft Visual Studio 9.0\VC" + +call %HOME%\bin\vcvars32.bat +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC +%HOME%\bin\nmake.exe /f Makefile_VC check + +pause diff -r d59bea55db9b -r c445c931472f w32/Build_GLPK_with_VC9_DLL.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/Build_GLPK_with_VC9_DLL.bat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,11 @@ +rem Build GLPK DLL with Microsoft Visual Studio Express 2008 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files\Microsoft Visual Studio 9.0\VC" + +call %HOME%\bin\vcvars32.bat +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC_DLL +%HOME%\bin\nmake.exe /f Makefile_VC_DLL check + +pause diff -r d59bea55db9b -r c445c931472f w32/Makefile_VC --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/Makefile_VC Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,132 @@ +# Build GLPK with Microsoft Visual Studio Express + +CFLAGS = /I. /I..\include /I..\src /I..\src\amd /I..\src\colamd \ + /DHAVE_CONFIG_H /D_CRT_SECURE_NO_WARNINGS /nologo /W3 /O2 + +OBJSET = \ +..\src\glpapi01.obj \ +..\src\glpapi02.obj \ +..\src\glpapi03.obj \ +..\src\glpapi04.obj \ +..\src\glpapi05.obj \ +..\src\glpapi06.obj \ +..\src\glpapi07.obj \ +..\src\glpapi08.obj \ +..\src\glpapi09.obj \ +..\src\glpapi10.obj \ +..\src\glpapi11.obj \ +..\src\glpapi12.obj \ +..\src\glpapi13.obj \ +..\src\glpapi14.obj \ +..\src\glpapi15.obj \ +..\src\glpapi16.obj \ +..\src\glpapi17.obj \ +..\src\glpapi18.obj \ +..\src\glpapi19.obj \ +..\src\glpavl.obj \ +..\src\glpbfd.obj \ +..\src\glpbfx.obj \ +..\src\glpcpx.obj \ +..\src\glpdmp.obj \ +..\src\glpdmx.obj \ +..\src\glpenv01.obj \ +..\src\glpenv02.obj \ +..\src\glpenv03.obj \ +..\src\glpenv04.obj \ +..\src\glpenv05.obj \ +..\src\glpenv06.obj \ +..\src\glpenv07.obj \ +..\src\glpenv08.obj \ +..\src\glpfhv.obj \ +..\src\glpgmp.obj \ +..\src\glphbm.obj \ +..\src\glpini01.obj \ +..\src\glpini02.obj \ +..\src\glpios01.obj \ +..\src\glpios02.obj \ +..\src\glpios03.obj \ +..\src\glpios04.obj \ +..\src\glpios05.obj \ +..\src\glpios06.obj \ +..\src\glpios07.obj \ +..\src\glpios08.obj \ +..\src\glpios09.obj \ +..\src\glpios10.obj \ +..\src\glpios11.obj \ +..\src\glpios12.obj \ +..\src\glpipm.obj \ +..\src\glplib01.obj \ +..\src\glplib02.obj \ +..\src\glplib03.obj \ +..\src\glplpf.obj \ +..\src\glplpx01.obj \ +..\src\glplpx02.obj \ +..\src\glplpx03.obj \ +..\src\glpluf.obj \ +..\src\glplux.obj \ +..\src\glpmat.obj \ +..\src\glpmpl01.obj \ +..\src\glpmpl02.obj \ +..\src\glpmpl03.obj \ +..\src\glpmpl04.obj \ +..\src\glpmpl05.obj \ +..\src\glpmpl06.obj \ +..\src\glpmps.obj \ +..\src\glpnet01.obj \ +..\src\glpnet02.obj \ +..\src\glpnet03.obj \ +..\src\glpnet04.obj \ +..\src\glpnet05.obj \ +..\src\glpnet06.obj \ +..\src\glpnet07.obj \ +..\src\glpnet08.obj \ +..\src\glpnet09.obj \ +..\src\glpnpp01.obj \ +..\src\glpnpp02.obj \ +..\src\glpnpp03.obj \ +..\src\glpnpp04.obj \ +..\src\glpnpp05.obj \ +..\src\glpqmd.obj \ +..\src\glprgr.obj \ +..\src\glprng01.obj \ +..\src\glprng02.obj \ +..\src\glpscf.obj \ +..\src\glpscl.obj \ +..\src\glpsdf.obj \ +..\src\glpspm.obj \ +..\src\glpspx01.obj \ +..\src\glpspx02.obj \ +..\src\glpsql.obj \ +..\src\glpssx01.obj \ +..\src\glpssx02.obj \ +..\src\glptsp.obj \ +..\src\amd\amd_1.obj \ +..\src\amd\amd_2.obj \ +..\src\amd\amd_aat.obj \ +..\src\amd\amd_control.obj \ +..\src\amd\amd_defaults.obj \ +..\src\amd\amd_dump.obj \ +..\src\amd\amd_info.obj \ +..\src\amd\amd_order.obj \ +..\src\amd\amd_post_tree.obj \ +..\src\amd\amd_postorder.obj \ +..\src\amd\amd_preprocess.obj \ +..\src\amd\amd_valid.obj \ +..\src\colamd\colamd.obj + +.c.obj: + cl.exe $(CFLAGS) /Fo$*.obj /c $*.c + +all: glpk.lib glpsol.exe + +glpk.lib: $(OBJSET) + lib.exe /out:glpk.lib ..\src\*.obj ..\src\amd\*.obj \ + ..\src\colamd\*.obj + +glpsol.exe: ..\examples\glpsol.obj glpk.lib + cl.exe $(CFLAGS) /Feglpsol.exe ..\examples\glpsol.obj \ + glpk.lib + +check: glpsol.exe + .\glpsol.exe --version + .\glpsol.exe --mps ..\examples\plan.mps diff -r d59bea55db9b -r c445c931472f w32/Makefile_VC_DLL --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/Makefile_VC_DLL Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,132 @@ +# Build GLPK DLL with Microsoft Visual Studio Express + +CFLAGS = /I. /I..\include /I..\src /I..\src\amd /I..\src\colamd \ + /DHAVE_CONFIG_H /D_CRT_SECURE_NO_WARNINGS /nologo /W3 /O2 + +OBJSET = \ +..\src\glpapi01.obj \ +..\src\glpapi02.obj \ +..\src\glpapi03.obj \ +..\src\glpapi04.obj \ +..\src\glpapi05.obj \ +..\src\glpapi06.obj \ +..\src\glpapi07.obj \ +..\src\glpapi08.obj \ +..\src\glpapi09.obj \ +..\src\glpapi10.obj \ +..\src\glpapi11.obj \ +..\src\glpapi12.obj \ +..\src\glpapi13.obj \ +..\src\glpapi14.obj \ +..\src\glpapi15.obj \ +..\src\glpapi16.obj \ +..\src\glpapi17.obj \ +..\src\glpapi18.obj \ +..\src\glpapi19.obj \ +..\src\glpavl.obj \ +..\src\glpbfd.obj \ +..\src\glpbfx.obj \ +..\src\glpcpx.obj \ +..\src\glpdmp.obj \ +..\src\glpdmx.obj \ +..\src\glpenv01.obj \ +..\src\glpenv02.obj \ +..\src\glpenv03.obj \ +..\src\glpenv04.obj \ +..\src\glpenv05.obj \ +..\src\glpenv06.obj \ +..\src\glpenv07.obj \ +..\src\glpenv08.obj \ +..\src\glpfhv.obj \ +..\src\glpgmp.obj \ +..\src\glphbm.obj \ +..\src\glpini01.obj \ +..\src\glpini02.obj \ +..\src\glpios01.obj \ +..\src\glpios02.obj \ +..\src\glpios03.obj \ +..\src\glpios04.obj \ +..\src\glpios05.obj \ +..\src\glpios06.obj \ +..\src\glpios07.obj \ +..\src\glpios08.obj \ +..\src\glpios09.obj \ +..\src\glpios10.obj \ +..\src\glpios11.obj \ +..\src\glpios12.obj \ +..\src\glpipm.obj \ +..\src\glplib01.obj \ +..\src\glplib02.obj \ +..\src\glplib03.obj \ +..\src\glplpf.obj \ +..\src\glplpx01.obj \ +..\src\glplpx02.obj \ +..\src\glplpx03.obj \ +..\src\glpluf.obj \ +..\src\glplux.obj \ +..\src\glpmat.obj \ +..\src\glpmpl01.obj \ +..\src\glpmpl02.obj \ +..\src\glpmpl03.obj \ +..\src\glpmpl04.obj \ +..\src\glpmpl05.obj \ +..\src\glpmpl06.obj \ +..\src\glpmps.obj \ +..\src\glpnet01.obj \ +..\src\glpnet02.obj \ +..\src\glpnet03.obj \ +..\src\glpnet04.obj \ +..\src\glpnet05.obj \ +..\src\glpnet06.obj \ +..\src\glpnet07.obj \ +..\src\glpnet08.obj \ +..\src\glpnet09.obj \ +..\src\glpnpp01.obj \ +..\src\glpnpp02.obj \ +..\src\glpnpp03.obj \ +..\src\glpnpp04.obj \ +..\src\glpnpp05.obj \ +..\src\glpqmd.obj \ +..\src\glprgr.obj \ +..\src\glprng01.obj \ +..\src\glprng02.obj \ +..\src\glpscf.obj \ +..\src\glpscl.obj \ +..\src\glpsdf.obj \ +..\src\glpspm.obj \ +..\src\glpspx01.obj \ +..\src\glpspx02.obj \ +..\src\glpsql.obj \ +..\src\glpssx01.obj \ +..\src\glpssx02.obj \ +..\src\glptsp.obj \ +..\src\amd\amd_1.obj \ +..\src\amd\amd_2.obj \ +..\src\amd\amd_aat.obj \ +..\src\amd\amd_control.obj \ +..\src\amd\amd_defaults.obj \ +..\src\amd\amd_dump.obj \ +..\src\amd\amd_info.obj \ +..\src\amd\amd_order.obj \ +..\src\amd\amd_post_tree.obj \ +..\src\amd\amd_postorder.obj \ +..\src\amd\amd_preprocess.obj \ +..\src\amd\amd_valid.obj \ +..\src\colamd\colamd.obj + +.c.obj: + cl.exe $(CFLAGS) /Fo$*.obj /c $*.c + +all: glpk_4_45.dll glpsol.exe + +glpk_4_45.dll: $(OBJSET) + cl.exe $(CFLAGS) /LD /Feglpk_4_45.dll ..\src\*.obj \ + ..\src\amd\*.obj ..\src\colamd\*.obj glpk_4_45.def + +glpsol.exe: ..\examples\glpsol.obj glpk_4_45.dll + cl.exe $(CFLAGS) /Feglpsol.exe ..\examples\glpsol.obj \ + glpk_4_45.lib + +check: glpsol.exe + .\glpsol.exe --version + .\glpsol.exe --mps ..\examples\plan.mps diff -r d59bea55db9b -r c445c931472f w32/config_VC --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/config_VC Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,13 @@ +/* GLPK configuration file (Microsoft Visual Studio Express) */ + +#define __WOE__ 1 + +#define ODBC_DLNAME "odbc32.dll" +/* ODBC shared library name if this feature is enabled */ + +#if 0 +#define MYSQL_DLNAME "libmysql.dll" +/* MySQL shared library name if this feature is enabled */ +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f w32/glpk_4_45.def --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/glpk_4_45.def Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1062 @@ +LIBRARY glpk_4_45 +VERSION 4.45 +DESCRIPTION "GNU Linear Programming Kit" +EXPORTS +;; from amd_1 ;; + _glp_amd_1 +;; from amd_2 ;; + _glp_amd_2 +;; from amd_aat ;; + _glp_amd_aat +;; from amd_control ;; + _glp_amd_control +;; from amd_defaults ;; + _glp_amd_defaults +;; from amd_dump ;; +;; from amd_info ;; + _glp_amd_info +;; from amd_order ;; + _glp_amd_order +;; from amd_post_tree ;; + _glp_amd_post_tree +;; from amd_postorder ;; + _glp_amd_postorder +;; from amd_preprocess ;; + _glp_amd_preprocess +;; from amd_valid ;; + _glp_amd_valid +;; from colamd ;; + _glp_colamd + _glp_colamd_recommended + _glp_colamd_report + _glp_colamd_set_defaults + _glp_symamd + _glp_symamd_report +;; from glpapi01 ;; + glp_add_cols + glp_add_rows + glp_check_dup + glp_copy_prob + glp_create_prob + glp_del_cols + glp_del_rows + glp_delete_prob + glp_erase_prob + glp_load_matrix + glp_set_col_bnds + glp_set_col_name + glp_set_mat_col + glp_set_mat_row + glp_set_obj_coef + glp_set_obj_dir + glp_set_obj_name + glp_set_prob_name + glp_set_row_bnds + glp_set_row_name + glp_sort_matrix +;; from glpapi02 ;; + glp_get_col_lb + glp_get_col_name + glp_get_col_type + glp_get_col_ub + glp_get_mat_col + glp_get_mat_row + glp_get_num_cols + glp_get_num_nz + glp_get_num_rows + glp_get_obj_coef + glp_get_obj_dir + glp_get_obj_name + glp_get_prob_name + glp_get_row_lb + glp_get_row_name + glp_get_row_type + glp_get_row_ub +;; from glpapi03 ;; + glp_create_index + glp_delete_index + glp_find_col + glp_find_row +;; from glpapi04 ;; + glp_get_rii + glp_get_sjj + glp_set_rii + glp_set_sjj + glp_unscale_prob +;; from glpapi05 ;; + glp_set_col_stat + glp_set_row_stat + glp_std_basis +;; from glpapi06 ;; + glp_get_col_dual + glp_get_col_prim + glp_get_col_stat + glp_get_dual_stat + glp_get_obj_val + glp_get_prim_stat + glp_get_row_dual + glp_get_row_prim + glp_get_row_stat + glp_get_status + glp_get_unbnd_ray + glp_init_smcp + glp_simplex +;; from glpapi07 ;; + glp_exact +;; from glpapi08 ;; + glp_init_iptcp + glp_interior + glp_ipt_col_dual + glp_ipt_col_prim + glp_ipt_obj_val + glp_ipt_row_dual + glp_ipt_row_prim + glp_ipt_status +;; from glpapi09 ;; + glp_get_col_kind + glp_get_num_bin + glp_get_num_int + glp_init_iocp + glp_intopt + glp_mip_col_val + glp_mip_obj_val + glp_mip_row_val + glp_mip_status + glp_set_col_kind +;; from glpapi10 ;; + _glp_check_kkt +;; from glpapi11 ;; + glp_print_ipt + glp_print_mip + glp_print_ranges + glp_print_sol + glp_read_ipt + glp_read_mip + glp_read_sol + glp_write_ipt + glp_write_mip + glp_write_sol +;; from glpapi12 ;; + _glp_analyze_row + glp_analyze_bound + glp_analyze_coef + glp_bf_exists + glp_bf_updated + glp_btran + glp_dual_rtest + glp_eval_tab_col + glp_eval_tab_row + glp_factorize + glp_ftran + glp_get_bfcp + glp_get_bhead + glp_get_col_bind + glp_get_row_bind + glp_prim_rtest + glp_set_bfcp + glp_transform_col + glp_transform_row + glp_warm_up +;; from glpapi13 ;; + glp_ios_add_row + glp_ios_best_node + glp_ios_branch_upon + glp_ios_can_branch + glp_ios_clear_pool + glp_ios_curr_node + glp_ios_del_row + glp_ios_get_prob + glp_ios_heur_sol + glp_ios_mip_gap + glp_ios_next_node + glp_ios_node_bound + glp_ios_node_data + glp_ios_node_level + glp_ios_pool_size + glp_ios_prev_node + glp_ios_reason + glp_ios_row_attr + glp_ios_select_node + glp_ios_terminate + glp_ios_tree_size + glp_ios_up_node +;; from glpapi14 ;; + _glp_mpl_init_rand + glp_mpl_alloc_wksp + glp_mpl_build_prob + glp_mpl_free_wksp + glp_mpl_generate + glp_mpl_postsolve + glp_mpl_read_data + glp_mpl_read_model +;; from glpapi15 ;; + glp_add_arc + glp_add_vertices + glp_create_graph + glp_create_v_index + glp_del_arc + glp_del_vertices + glp_delete_graph + glp_delete_v_index + glp_erase_graph + glp_find_vertex + glp_read_graph + glp_set_graph_name + glp_set_vertex_name + glp_write_graph +;; from glpapi16 ;; + glp_strong_comp + glp_top_sort + glp_weak_comp +;; from glpapi17 ;; + glp_asnprob_hall + glp_asnprob_lp + glp_asnprob_okalg + glp_check_asnprob + glp_cpp + glp_maxflow_ffalg + glp_maxflow_lp + glp_mincost_lp + glp_mincost_okalg +;; from glpapi18 ;; + glp_wclique_exact +;; from glpapi19 ;; + glp_main +;; from glpavl ;; + _glp_avl_create_tree + _glp_avl_delete_node + _glp_avl_delete_tree + _glp_avl_find_node + _glp_avl_get_node_link + _glp_avl_get_node_type + _glp_avl_insert_node + _glp_avl_set_node_link + _glp_avl_set_node_type + _glp_avl_strcmp +;; from glpbfd ;; + _glp_bfd_btran + _glp_bfd_create_it + _glp_bfd_delete_it + _glp_bfd_factorize + _glp_bfd_ftran + _glp_bfd_get_count + _glp_bfd_set_parm + _glp_bfd_update_it +;; from glpbfx ;; + _glp_bfx_btran + _glp_bfx_create_binv + _glp_bfx_delete_binv + _glp_bfx_factorize + _glp_bfx_ftran + _glp_bfx_update +;; from glpcpx ;; + glp_init_cpxcp + glp_read_lp + glp_write_lp +;; from glpdmp ;; + _glp_dmp_create_pool + _glp_dmp_delete_pool + _glp_dmp_free_atom + _glp_dmp_get_atom + _glp_dmp_in_use +;; from glpdmx ;; + glp_read_asnprob + glp_read_ccdata + glp_read_maxflow + glp_read_mincost + glp_read_prob + glp_write_asnprob + glp_write_ccdata + glp_write_maxflow + glp_write_mincost + glp_write_prob +;; from glpenv01 ;; + _glp_get_env_ptr + glp_free_env + glp_init_env + glp_version +;; from glpenv02 ;; + _glp_tls_get_ptr + _glp_tls_set_ptr +;; from glpenv03 ;; + glp_close_tee + glp_open_tee + glp_printf + glp_term_hook + glp_term_out + glp_vprintf +;; from glpenv04 ;; + glp_assert_ + glp_error_ + glp_error_hook +;; from glpenv05 ;; + glp_calloc + glp_free + glp_malloc + glp_mem_limit + glp_mem_usage +;; from glpenv06 ;; + glp_difftime + glp_time +;; from glpenv07 ;; + _glp_lib_err_msg + _glp_lib_xerrmsg + _glp_lib_xfclose + _glp_lib_xfeof + _glp_lib_xferror + _glp_lib_xfflush + _glp_lib_xfgetc + _glp_lib_xfopen + _glp_lib_xfprintf + _glp_lib_xfputc +;; from glpenv08 ;; + _glp_xdlclose + _glp_xdlopen + _glp_xdlsym +;; from glpfhv ;; + _glp_fhv_btran + _glp_fhv_create_it + _glp_fhv_delete_it + _glp_fhv_factorize + _glp_fhv_ftran + _glp_fhv_h_solve + _glp_fhv_update_it +;; from glpgmp ;; + _glp_gmp_free_atom + _glp_gmp_free_mem + _glp_gmp_get_atom + _glp_gmp_get_work + _glp_gmp_pool_count + _glp_mpq_abs + _glp_mpq_add + _glp_mpq_canonicalize + _glp_mpq_clear + _glp_mpq_cmp + _glp_mpq_div + _glp_mpq_get_d + _glp_mpq_init + _glp_mpq_mul + _glp_mpq_neg + _glp_mpq_out_str + _glp_mpq_set + _glp_mpq_set_d + _glp_mpq_set_si + _glp_mpq_sgn + _glp_mpq_sub + _glp_mpz_abs + _glp_mpz_add + _glp_mpz_clear + _glp_mpz_cmp + _glp_mpz_div + _glp_mpz_gcd + _glp_mpz_get_d + _glp_mpz_get_d_2exp + _glp_mpz_init + _glp_mpz_mul + _glp_mpz_neg + _glp_mpz_out_str + _glp_mpz_set + _glp_mpz_set_si + _glp_mpz_sgn + _glp_mpz_sub + _glp_mpz_swap +;; from glphbm ;; + _glp_hbm_free_mat + _glp_hbm_read_mat +;; from glpini01 ;; + glp_adv_basis +;; from glpini02 ;; + glp_cpx_basis +;; from glpios01 ;; + _glp_ios_add_row + _glp_ios_best_node + _glp_ios_clear_pool + _glp_ios_clone_node + _glp_ios_create_pool + _glp_ios_create_tree + _glp_ios_del_row + _glp_ios_delete_node + _glp_ios_delete_pool + _glp_ios_delete_tree + _glp_ios_eval_degrad + _glp_ios_find_row + _glp_ios_freeze_node + _glp_ios_is_hopeful + _glp_ios_relative_gap + _glp_ios_revive_node + _glp_ios_round_bound + _glp_ios_solve_node +;; from glpios02 ;; + _glp_ios_preprocess_node +;; from glpios03 ;; + _glp_ios_driver +;; from glpios04 ;; + _glp_ios_check_vec + _glp_ios_clean_vec + _glp_ios_clear_vec + _glp_ios_copy_vec + _glp_ios_create_vec + _glp_ios_delete_vec + _glp_ios_get_vj + _glp_ios_linear_comb + _glp_ios_set_vj +;; from glpios05 ;; + _glp_ios_gmi_gen +;; from glpios06 ;; + _glp_ios_mir_gen + _glp_ios_mir_init + _glp_ios_mir_term +;; from glpios07 ;; + _glp_ios_cov_gen +;; from glpios08 ;; + _glp_ios_clq_gen + _glp_ios_clq_init + _glp_ios_clq_term +;; from glpios09 ;; + _glp_ios_choose_var + _glp_ios_pcost_branch + _glp_ios_pcost_free + _glp_ios_pcost_init + _glp_ios_pcost_update +;; from glpios10 ;; + _glp_ios_feas_pump +;; from glpios11 ;; + _glp_ios_process_cuts +;; from glpios12 ;; + _glp_ios_choose_node +;; from glpipm ;; + _glp_ipm_solve +;; from glplib01 ;; + _glp_lib_bigdiv + _glp_lib_bigmul +;; from glplib02 ;; + _glp_lib_xladd + _glp_lib_xlcmp + _glp_lib_xldiv + _glp_lib_xlmul + _glp_lib_xlneg + _glp_lib_xlset + _glp_lib_xlsub + _glp_lib_xltoa + _glp_lib_xltod +;; from glplib03 ;; + _glp_lib_fp2rat + _glp_lib_gcd + _glp_lib_gcdn + _glp_lib_jdate + _glp_lib_jday + _glp_lib_lcm + _glp_lib_lcmn + _glp_lib_round2n + _glp_lib_str2int + _glp_lib_str2num + _glp_lib_strrev + _glp_lib_strspx + _glp_lib_strtrim +;; from glplpf ;; + _glp_lpf_btran + _glp_lpf_create_it + _glp_lpf_delete_it + _glp_lpf_factorize + _glp_lpf_ftran + _glp_lpf_update_it +;; from glplpx01 ;; + _glp_lpx_add_cols + _glp_lpx_add_rows + _glp_lpx_adv_basis + _glp_lpx_check_int + _glp_lpx_check_kkt + _glp_lpx_cpx_basis + _glp_lpx_create_index + _glp_lpx_create_prob + _glp_lpx_del_cols + _glp_lpx_del_rows + _glp_lpx_delete_index + _glp_lpx_delete_prob + _glp_lpx_dual_ratio_test + _glp_lpx_eval_tab_col + _glp_lpx_eval_tab_row + _glp_lpx_exact + _glp_lpx_find_col + _glp_lpx_find_row + _glp_lpx_get_class + _glp_lpx_get_col_bnds + _glp_lpx_get_col_dual + _glp_lpx_get_col_info + _glp_lpx_get_col_kind + _glp_lpx_get_col_lb + _glp_lpx_get_col_name + _glp_lpx_get_col_prim + _glp_lpx_get_col_stat + _glp_lpx_get_col_type + _glp_lpx_get_col_ub + _glp_lpx_get_dual_stat + _glp_lpx_get_int_parm + _glp_lpx_get_mat_col + _glp_lpx_get_mat_row + _glp_lpx_get_num_bin + _glp_lpx_get_num_cols + _glp_lpx_get_num_int + _glp_lpx_get_num_nz + _glp_lpx_get_num_rows + _glp_lpx_get_obj_coef + _glp_lpx_get_obj_dir + _glp_lpx_get_obj_name + _glp_lpx_get_obj_val + _glp_lpx_get_prim_stat + _glp_lpx_get_prob_name + _glp_lpx_get_ray_info + _glp_lpx_get_real_parm + _glp_lpx_get_row_bnds + _glp_lpx_get_row_dual + _glp_lpx_get_row_info + _glp_lpx_get_row_lb + _glp_lpx_get_row_name + _glp_lpx_get_row_prim + _glp_lpx_get_row_stat + _glp_lpx_get_row_type + _glp_lpx_get_row_ub + _glp_lpx_get_status + _glp_lpx_integer + _glp_lpx_interior + _glp_lpx_intopt + _glp_lpx_ipt_col_dual + _glp_lpx_ipt_col_prim + _glp_lpx_ipt_obj_val + _glp_lpx_ipt_row_dual + _glp_lpx_ipt_row_prim + _glp_lpx_ipt_status + _glp_lpx_is_b_avail + _glp_lpx_load_matrix + _glp_lpx_main + _glp_lpx_mip_col_val + _glp_lpx_mip_obj_val + _glp_lpx_mip_row_val + _glp_lpx_mip_status + _glp_lpx_prim_ratio_test + _glp_lpx_print_ips + _glp_lpx_print_mip + _glp_lpx_print_prob + _glp_lpx_print_sens_bnds + _glp_lpx_print_sol + _glp_lpx_read_bas + _glp_lpx_read_cpxlp + _glp_lpx_read_freemps + _glp_lpx_read_model + _glp_lpx_read_mps + _glp_lpx_reset_parms + _glp_lpx_scale_prob + _glp_lpx_set_class + _glp_lpx_set_col_bnds + _glp_lpx_set_col_kind + _glp_lpx_set_col_name + _glp_lpx_set_col_stat + _glp_lpx_set_int_parm + _glp_lpx_set_mat_col + _glp_lpx_set_mat_row + _glp_lpx_set_obj_coef + _glp_lpx_set_obj_dir + _glp_lpx_set_obj_name + _glp_lpx_set_prob_name + _glp_lpx_set_real_parm + _glp_lpx_set_row_bnds + _glp_lpx_set_row_name + _glp_lpx_set_row_stat + _glp_lpx_simplex + _glp_lpx_std_basis + _glp_lpx_transform_col + _glp_lpx_transform_row + _glp_lpx_unscale_prob + _glp_lpx_warm_up + _glp_lpx_write_bas + _glp_lpx_write_cpxlp + _glp_lpx_write_freemps + _glp_lpx_write_mps +;; from glplpx02 ;; + _glp_put_mip_soln + _glp_put_solution +;; from glplpx03 ;; + _glp_lpx_write_pb +;; from glpluf ;; + _glp_luf_a_solve + _glp_luf_create_it + _glp_luf_defrag_sva + _glp_luf_delete_it + _glp_luf_enlarge_col + _glp_luf_enlarge_row + _glp_luf_f_solve + _glp_luf_factorize + _glp_luf_v_solve +;; from glplux ;; + _glp_lux_create + _glp_lux_decomp + _glp_lux_delete + _glp_lux_f_solve + _glp_lux_solve + _glp_lux_v_solve +;; from glpmat ;; + _glp_mat_adat_numeric + _glp_mat_adat_symbolic + _glp_mat_amd_order1 + _glp_mat_check_fvs + _glp_mat_check_pattern + _glp_mat_chol_numeric + _glp_mat_chol_symbolic + _glp_mat_min_degree + _glp_mat_symamd_ord + _glp_mat_transpose + _glp_mat_u_solve + _glp_mat_ut_solve +;; from glpmpl01 ;; + _glp_mpl_append_block + _glp_mpl_append_char + _glp_mpl_append_slot + _glp_mpl_arg_list_len + _glp_mpl_branched_expression + _glp_mpl_check_statement + _glp_mpl_close_scope + _glp_mpl_constraint_statement + _glp_mpl_create_arg_list + _glp_mpl_create_block + _glp_mpl_create_domain + _glp_mpl_display_statement + _glp_mpl_domain_arity + _glp_mpl_elemset_argument + _glp_mpl_end_statement + _glp_mpl_enter_context + _glp_mpl_error_dimension + _glp_mpl_error_following + _glp_mpl_error_preceding + _glp_mpl_expand_arg_list + _glp_mpl_expression_0 + _glp_mpl_expression_1 + _glp_mpl_expression_10 + _glp_mpl_expression_11 + _glp_mpl_expression_12 + _glp_mpl_expression_13 + _glp_mpl_expression_2 + _glp_mpl_expression_3 + _glp_mpl_expression_4 + _glp_mpl_expression_5 + _glp_mpl_expression_6 + _glp_mpl_expression_7 + _glp_mpl_expression_8 + _glp_mpl_expression_9 + _glp_mpl_expression_list + _glp_mpl_for_statement + _glp_mpl_function_reference + _glp_mpl_get_char + _glp_mpl_get_token + _glp_mpl_indexing_expression + _glp_mpl_is_keyword + _glp_mpl_is_reserved + _glp_mpl_iterated_expression + _glp_mpl_literal_set + _glp_mpl_make_binary + _glp_mpl_make_code + _glp_mpl_make_ternary + _glp_mpl_make_unary + _glp_mpl_model_section + _glp_mpl_numeric_argument + _glp_mpl_numeric_literal + _glp_mpl_object_reference + _glp_mpl_objective_statement + _glp_mpl_parameter_statement + _glp_mpl_primary_expression + _glp_mpl_print_context + _glp_mpl_printf_statement + _glp_mpl_set_expression + _glp_mpl_set_statement + _glp_mpl_simple_statement + _glp_mpl_solve_statement + _glp_mpl_string_literal + _glp_mpl_subscript_list + _glp_mpl_symbolic_argument + _glp_mpl_table_statement + _glp_mpl_unget_token + _glp_mpl_variable_statement +;; from glpmpl02 ;; + _glp_mpl_create_slice + _glp_mpl_data_section + _glp_mpl_delete_slice + _glp_mpl_expand_slice + _glp_mpl_fake_slice + _glp_mpl_is_literal + _glp_mpl_is_number + _glp_mpl_is_symbol + _glp_mpl_matrix_format + _glp_mpl_parameter_data + _glp_mpl_plain_format + _glp_mpl_read_number + _glp_mpl_read_slice + _glp_mpl_read_symbol + _glp_mpl_read_value + _glp_mpl_select_parameter + _glp_mpl_select_set + _glp_mpl_set_data + _glp_mpl_set_default + _glp_mpl_simple_format + _glp_mpl_slice_arity + _glp_mpl_slice_dimen + _glp_mpl_tabbing_format + _glp_mpl_tabular_format +;; from glpmpl03 ;; + _glp_mpl_add_member + _glp_mpl_add_tuple + _glp_mpl_arelset_member + _glp_mpl_arelset_size + _glp_mpl_assign_dummy_index + _glp_mpl_build_subtuple + _glp_mpl_check_elem_set + _glp_mpl_check_then_add + _glp_mpl_check_value_num + _glp_mpl_check_value_sym + _glp_mpl_clean_check + _glp_mpl_clean_code + _glp_mpl_clean_constraint + _glp_mpl_clean_display + _glp_mpl_clean_domain + _glp_mpl_clean_for + _glp_mpl_clean_parameter + _glp_mpl_clean_printf + _glp_mpl_clean_set + _glp_mpl_clean_statement + _glp_mpl_clean_table + _glp_mpl_clean_variable + _glp_mpl_compare_strings + _glp_mpl_compare_symbols + _glp_mpl_compare_tuples + _glp_mpl_concat_symbols + _glp_mpl_constant_term + _glp_mpl_copy_elemset + _glp_mpl_copy_formula + _glp_mpl_copy_string + _glp_mpl_copy_symbol + _glp_mpl_copy_tuple + _glp_mpl_create_arelset + _glp_mpl_create_array + _glp_mpl_create_elemset + _glp_mpl_create_string + _glp_mpl_create_symbol_num + _glp_mpl_create_symbol_str + _glp_mpl_create_tuple + _glp_mpl_delete_array + _glp_mpl_delete_elemset + _glp_mpl_delete_formula + _glp_mpl_delete_string + _glp_mpl_delete_symbol + _glp_mpl_delete_tuple + _glp_mpl_delete_value + _glp_mpl_enter_domain_block + _glp_mpl_eval_elemset + _glp_mpl_eval_formula + _glp_mpl_eval_logical + _glp_mpl_eval_member_con + _glp_mpl_eval_member_num + _glp_mpl_eval_member_set + _glp_mpl_eval_member_sym + _glp_mpl_eval_member_var + _glp_mpl_eval_numeric + _glp_mpl_eval_symbolic + _glp_mpl_eval_tuple + _glp_mpl_eval_whole_con + _glp_mpl_eval_whole_par + _glp_mpl_eval_whole_set + _glp_mpl_eval_whole_var + _glp_mpl_eval_within_domain + _glp_mpl_execute_check + _glp_mpl_execute_display + _glp_mpl_execute_for + _glp_mpl_execute_printf + _glp_mpl_execute_statement + _glp_mpl_execute_table + _glp_mpl_expand_tuple + _glp_mpl_fetch_string + _glp_mpl_find_member + _glp_mpl_find_tuple + _glp_mpl_format_symbol + _glp_mpl_format_tuple + _glp_mpl_fp_add + _glp_mpl_fp_atan + _glp_mpl_fp_atan2 + _glp_mpl_fp_cos + _glp_mpl_fp_div + _glp_mpl_fp_exp + _glp_mpl_fp_idiv + _glp_mpl_fp_irand224 + _glp_mpl_fp_less + _glp_mpl_fp_log + _glp_mpl_fp_log10 + _glp_mpl_fp_mod + _glp_mpl_fp_mul + _glp_mpl_fp_normal + _glp_mpl_fp_normal01 + _glp_mpl_fp_power + _glp_mpl_fp_round + _glp_mpl_fp_sin + _glp_mpl_fp_sqrt + _glp_mpl_fp_sub + _glp_mpl_fp_trunc + _glp_mpl_fp_uniform01 + _glp_mpl_free_dca + _glp_mpl_get_domain_tuple + _glp_mpl_is_member + _glp_mpl_linear_comb + _glp_mpl_loop_within_domain + _glp_mpl_out_of_domain + _glp_mpl_reduce_terms + _glp_mpl_remove_constant + _glp_mpl_set_cross + _glp_mpl_set_diff + _glp_mpl_set_inter + _glp_mpl_set_symdiff + _glp_mpl_set_union + _glp_mpl_single_variable + _glp_mpl_tab_get_arg + _glp_mpl_tab_get_name + _glp_mpl_tab_get_num + _glp_mpl_tab_get_str + _glp_mpl_tab_get_type + _glp_mpl_tab_num_args + _glp_mpl_tab_num_flds + _glp_mpl_tab_set_num + _glp_mpl_tab_set_str + _glp_mpl_take_member_con + _glp_mpl_take_member_num + _glp_mpl_take_member_set + _glp_mpl_take_member_sym + _glp_mpl_take_member_var + _glp_mpl_tuple_dimen + _glp_mpl_uniform + _glp_mpl_update_dummy_indices +;; from glpmpl04 ;; + _glp_mpl_alloc_content + _glp_mpl_build_problem + _glp_mpl_clean_model + _glp_mpl_close_input + _glp_mpl_error + _glp_mpl_flush_output + _glp_mpl_generate + _glp_mpl_generate_model + _glp_mpl_get_col_bnds + _glp_mpl_get_col_kind + _glp_mpl_get_col_name + _glp_mpl_get_mat_row + _glp_mpl_get_num_cols + _glp_mpl_get_num_rows + _glp_mpl_get_prob_name + _glp_mpl_get_row_bnds + _glp_mpl_get_row_c0 + _glp_mpl_get_row_kind + _glp_mpl_get_row_name + _glp_mpl_has_solve_stmt + _glp_mpl_initialize + _glp_mpl_open_input + _glp_mpl_open_output + _glp_mpl_postsolve + _glp_mpl_postsolve_model + _glp_mpl_put_col_soln + _glp_mpl_put_row_soln + _glp_mpl_read_char + _glp_mpl_read_data + _glp_mpl_read_model + _glp_mpl_terminate + _glp_mpl_warning + _glp_mpl_write_char + _glp_mpl_write_text +;; from glpmpl05 ;; + _glp_mpl_fn_gmtime + _glp_mpl_fn_str2time + _glp_mpl_fn_time2str +;; from glpmpl06 ;; + _glp_mpl_tab_drv_close + _glp_mpl_tab_drv_open + _glp_mpl_tab_drv_read + _glp_mpl_tab_drv_write +;; from glpmps ;; + glp_init_mpscp + glp_read_mps + glp_write_mps +;; from glpnet01 ;; + _glp_mc21a +;; from glpnet02 ;; + _glp_mc13d +;; from glpnet03 ;; + glp_netgen +;; from glpnet04 ;; + glp_gridgen +;; from glpnet05 ;; + glp_rmfgen +;; from glpnet06 ;; + _glp_okalg +;; from glpnet07 ;; + _glp_ffalg +;; from glpnet08 ;; + _glp_wclique +;; from glpnet09 ;; + _glp_kellerman +;; from glpnpp01 ;; + _glp_npp_activate_col + _glp_npp_activate_row + _glp_npp_add_aij + _glp_npp_add_col + _glp_npp_add_row + _glp_npp_build_prob + _glp_npp_col_nnz + _glp_npp_create_wksp + _glp_npp_deactivate_col + _glp_npp_deactivate_row + _glp_npp_del_aij + _glp_npp_del_col + _glp_npp_del_row + _glp_npp_delete_wksp + _glp_npp_erase_row + _glp_npp_insert_col + _glp_npp_insert_row + _glp_npp_load_prob + _glp_npp_postprocess + _glp_npp_push_tse + _glp_npp_remove_col + _glp_npp_remove_row + _glp_npp_row_nnz + _glp_npp_unload_sol +;; from glpnpp02 ;; + _glp_npp_dbnd_col + _glp_npp_fixed_col + _glp_npp_free_col + _glp_npp_free_row + _glp_npp_geq_row + _glp_npp_lbnd_col + _glp_npp_leq_row + _glp_npp_make_equality + _glp_npp_make_fixed + _glp_npp_ubnd_col +;; from glpnpp03 ;; + _glp_npp_analyze_row + _glp_npp_empty_col + _glp_npp_empty_row + _glp_npp_eq_doublet + _glp_npp_eq_singlet + _glp_npp_forcing_row + _glp_npp_implied_bounds + _glp_npp_implied_free + _glp_npp_implied_lower + _glp_npp_implied_slack + _glp_npp_implied_upper + _glp_npp_implied_value + _glp_npp_inactive_bound + _glp_npp_ineq_singlet +;; from glpnpp04 ;; + _glp_npp_binarize_prob + _glp_npp_hidden_covering + _glp_npp_hidden_packing + _glp_npp_implied_packing + _glp_npp_is_covering + _glp_npp_is_packing + _glp_npp_is_partitioning + _glp_npp_reduce_ineq_coef +;; from glpnpp05 ;; + _glp_npp_clean_prob + _glp_npp_improve_bounds + _glp_npp_integer + _glp_npp_process_col + _glp_npp_process_prob + _glp_npp_process_row + _glp_npp_simplex +;; from glpqmd ;; + _glp_qmd_genqmd + _glp_qmd_qmdmrg + _glp_qmd_qmdqt + _glp_qmd_qmdrch + _glp_qmd_qmdupd +;; from glprgr ;; + _glp_rgr_write_bmp16 +;; from glprng01 ;; + _glp_rng_create_rand + _glp_rng_delete_rand + _glp_rng_init_rand + _glp_rng_next_rand + _glp_rng_unif_rand +;; from glprng02 ;; + _glp_rng_unif_01 + _glp_rng_uniform +;; from glpscf ;; + _glp_scf_create_it + _glp_scf_delete_it + _glp_scf_reset_it + _glp_scf_solve_it + _glp_scf_update_exp +;; from glpscl ;; + glp_scale_prob +;; from glpsdf ;; + glp_sdf_close_file + glp_sdf_error + glp_sdf_line + glp_sdf_open_file + glp_sdf_read_int + glp_sdf_read_item + glp_sdf_read_num + glp_sdf_read_text + glp_sdf_set_jump + glp_sdf_warning +;; from glpspm ;; + _glp_spm_add_mat + _glp_spm_add_num + _glp_spm_add_sym + _glp_spm_check_per + _glp_spm_count_nnz + _glp_spm_create_mat + _glp_spm_create_per + _glp_spm_delete_mat + _glp_spm_delete_per + _glp_spm_drop_zeros + _glp_spm_mul_mat + _glp_spm_mul_num + _glp_spm_mul_sym + _glp_spm_new_elem + _glp_spm_read_hbm + _glp_spm_read_mat + _glp_spm_show_mat + _glp_spm_test_mat_d + _glp_spm_test_mat_e + _glp_spm_transpose + _glp_spm_write_mat +;; from glpspx01 ;; + _glp_spx_primal +;; from glpspx02 ;; + _glp_spx_dual +;; from glpsql ;; + _glp_db_iodbc_close + _glp_db_iodbc_open + _glp_db_iodbc_read + _glp_db_iodbc_write + _glp_db_mysql_close + _glp_db_mysql_open + _glp_db_mysql_read + _glp_db_mysql_write +;; from glpssx01 ;; + _glp_ssx_change_basis + _glp_ssx_chuzc + _glp_ssx_chuzr + _glp_ssx_create + _glp_ssx_delete + _glp_ssx_eval_bbar + _glp_ssx_eval_cbar + _glp_ssx_eval_col + _glp_ssx_eval_dj + _glp_ssx_eval_pi + _glp_ssx_eval_rho + _glp_ssx_eval_row + _glp_ssx_factorize + _glp_ssx_get_xNj + _glp_ssx_update_bbar + _glp_ssx_update_cbar + _glp_ssx_update_pi +;; from glpssx02 ;; + _glp_ssx_driver + _glp_ssx_phase_I + _glp_ssx_phase_II +;; from glptsp ;; + _glp_tsp_distance + _glp_tsp_free_data + _glp_tsp_read_data diff -r d59bea55db9b -r c445c931472f w32/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w32/readme.txt Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,24 @@ +This directory contains batch files and other stuff which you can use +to build GLPK for 32-bit Windows with the native C/C++ compilers. + +Before running the batch file do the following: + +1. Make sure that you have installed the compiler you are going to use + to build GLPK. + +2. Look into corresponding batch file (just right-click it and choose + 'Edit' in the popup menu; DO NOT choose 'Open'). Make sure that HOME + variable specifies correct path to the compiler directory; if not, + make necessary changes. + +To run the batch file just double-click it and wait a bit while the +Make utility does its job. The message 'OPTIMAL SOLUTION FOUND' in the +MS-DOS window means that all is OK. If you do not see it, something is +wrong. + +Once GLPK has been successfully built, there must appear two files in +this directory: + +glpk.lib, which is the GLPK object library, and + +glpsol.exe, which is the stand-alone GLPK LP/MIP solver. diff -r d59bea55db9b -r c445c931472f w64/Build_GLPK_with_VC10.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/Build_GLPK_with_VC10.bat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,11 @@ +rem Build GLPK with Microsoft Visual Studio Express 2010 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC" + +call %HOME%\vcvarsall.bat x64 +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC +%HOME%\bin\nmake.exe /f Makefile_VC check + +pause diff -r d59bea55db9b -r c445c931472f w64/Build_GLPK_with_VC10_DLL.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/Build_GLPK_with_VC10_DLL.bat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,11 @@ +rem Build GLPK DLL with Microsoft Visual Studio Express 2010 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC" + +call %HOME%\vcvarsall.bat x64 +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC_DLL +%HOME%\bin\nmake.exe /f Makefile_VC_DLL check + +pause diff -r d59bea55db9b -r c445c931472f w64/Build_GLPK_with_VC9.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/Build_GLPK_with_VC9.bat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,11 @@ +rem Build GLPK with Microsoft Visual Studio Express 2008 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC" + +call %HOME%\bin\vcvars64.bat +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC +%HOME%\bin\nmake.exe /f Makefile_VC check + +pause diff -r d59bea55db9b -r c445c931472f w64/Build_GLPK_with_VC9_DLL.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/Build_GLPK_with_VC9_DLL.bat Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,11 @@ +rem Build GLPK DLL with Microsoft Visual Studio Express 2008 + +rem NOTE: Make sure that HOME variable specifies correct path +set HOME="C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC" + +call %HOME%\bin\vcvars64.bat +copy config_VC config.h +%HOME%\bin\nmake.exe /f Makefile_VC_DLL +%HOME%\bin\nmake.exe /f Makefile_VC_DLL check + +pause diff -r d59bea55db9b -r c445c931472f w64/config_VC --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/config_VC Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,13 @@ +/* GLPK configuration file (Microsoft Visual Studio Express) */ + +#define __WOE__ 1 + +#define ODBC_DLNAME "odbc32.dll" +/* ODBC shared library name if this feature is enabled */ + +#if 0 +#define MYSQL_DLNAME "libmysql.dll" +/* MySQL shared library name if this feature is enabled */ +#endif + +/* eof */ diff -r d59bea55db9b -r c445c931472f w64/glpk_4_45.def --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/glpk_4_45.def Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,1062 @@ +LIBRARY glpk_4_45 +VERSION 4.45 +DESCRIPTION "GNU Linear Programming Kit" +EXPORTS +;; from amd_1 ;; + _glp_amd_1 +;; from amd_2 ;; + _glp_amd_2 +;; from amd_aat ;; + _glp_amd_aat +;; from amd_control ;; + _glp_amd_control +;; from amd_defaults ;; + _glp_amd_defaults +;; from amd_dump ;; +;; from amd_info ;; + _glp_amd_info +;; from amd_order ;; + _glp_amd_order +;; from amd_post_tree ;; + _glp_amd_post_tree +;; from amd_postorder ;; + _glp_amd_postorder +;; from amd_preprocess ;; + _glp_amd_preprocess +;; from amd_valid ;; + _glp_amd_valid +;; from colamd ;; + _glp_colamd + _glp_colamd_recommended + _glp_colamd_report + _glp_colamd_set_defaults + _glp_symamd + _glp_symamd_report +;; from glpapi01 ;; + glp_add_cols + glp_add_rows + glp_check_dup + glp_copy_prob + glp_create_prob + glp_del_cols + glp_del_rows + glp_delete_prob + glp_erase_prob + glp_load_matrix + glp_set_col_bnds + glp_set_col_name + glp_set_mat_col + glp_set_mat_row + glp_set_obj_coef + glp_set_obj_dir + glp_set_obj_name + glp_set_prob_name + glp_set_row_bnds + glp_set_row_name + glp_sort_matrix +;; from glpapi02 ;; + glp_get_col_lb + glp_get_col_name + glp_get_col_type + glp_get_col_ub + glp_get_mat_col + glp_get_mat_row + glp_get_num_cols + glp_get_num_nz + glp_get_num_rows + glp_get_obj_coef + glp_get_obj_dir + glp_get_obj_name + glp_get_prob_name + glp_get_row_lb + glp_get_row_name + glp_get_row_type + glp_get_row_ub +;; from glpapi03 ;; + glp_create_index + glp_delete_index + glp_find_col + glp_find_row +;; from glpapi04 ;; + glp_get_rii + glp_get_sjj + glp_set_rii + glp_set_sjj + glp_unscale_prob +;; from glpapi05 ;; + glp_set_col_stat + glp_set_row_stat + glp_std_basis +;; from glpapi06 ;; + glp_get_col_dual + glp_get_col_prim + glp_get_col_stat + glp_get_dual_stat + glp_get_obj_val + glp_get_prim_stat + glp_get_row_dual + glp_get_row_prim + glp_get_row_stat + glp_get_status + glp_get_unbnd_ray + glp_init_smcp + glp_simplex +;; from glpapi07 ;; + glp_exact +;; from glpapi08 ;; + glp_init_iptcp + glp_interior + glp_ipt_col_dual + glp_ipt_col_prim + glp_ipt_obj_val + glp_ipt_row_dual + glp_ipt_row_prim + glp_ipt_status +;; from glpapi09 ;; + glp_get_col_kind + glp_get_num_bin + glp_get_num_int + glp_init_iocp + glp_intopt + glp_mip_col_val + glp_mip_obj_val + glp_mip_row_val + glp_mip_status + glp_set_col_kind +;; from glpapi10 ;; + _glp_check_kkt +;; from glpapi11 ;; + glp_print_ipt + glp_print_mip + glp_print_ranges + glp_print_sol + glp_read_ipt + glp_read_mip + glp_read_sol + glp_write_ipt + glp_write_mip + glp_write_sol +;; from glpapi12 ;; + _glp_analyze_row + glp_analyze_bound + glp_analyze_coef + glp_bf_exists + glp_bf_updated + glp_btran + glp_dual_rtest + glp_eval_tab_col + glp_eval_tab_row + glp_factorize + glp_ftran + glp_get_bfcp + glp_get_bhead + glp_get_col_bind + glp_get_row_bind + glp_prim_rtest + glp_set_bfcp + glp_transform_col + glp_transform_row + glp_warm_up +;; from glpapi13 ;; + glp_ios_add_row + glp_ios_best_node + glp_ios_branch_upon + glp_ios_can_branch + glp_ios_clear_pool + glp_ios_curr_node + glp_ios_del_row + glp_ios_get_prob + glp_ios_heur_sol + glp_ios_mip_gap + glp_ios_next_node + glp_ios_node_bound + glp_ios_node_data + glp_ios_node_level + glp_ios_pool_size + glp_ios_prev_node + glp_ios_reason + glp_ios_row_attr + glp_ios_select_node + glp_ios_terminate + glp_ios_tree_size + glp_ios_up_node +;; from glpapi14 ;; + _glp_mpl_init_rand + glp_mpl_alloc_wksp + glp_mpl_build_prob + glp_mpl_free_wksp + glp_mpl_generate + glp_mpl_postsolve + glp_mpl_read_data + glp_mpl_read_model +;; from glpapi15 ;; + glp_add_arc + glp_add_vertices + glp_create_graph + glp_create_v_index + glp_del_arc + glp_del_vertices + glp_delete_graph + glp_delete_v_index + glp_erase_graph + glp_find_vertex + glp_read_graph + glp_set_graph_name + glp_set_vertex_name + glp_write_graph +;; from glpapi16 ;; + glp_strong_comp + glp_top_sort + glp_weak_comp +;; from glpapi17 ;; + glp_asnprob_hall + glp_asnprob_lp + glp_asnprob_okalg + glp_check_asnprob + glp_cpp + glp_maxflow_ffalg + glp_maxflow_lp + glp_mincost_lp + glp_mincost_okalg +;; from glpapi18 ;; + glp_wclique_exact +;; from glpapi19 ;; + glp_main +;; from glpavl ;; + _glp_avl_create_tree + _glp_avl_delete_node + _glp_avl_delete_tree + _glp_avl_find_node + _glp_avl_get_node_link + _glp_avl_get_node_type + _glp_avl_insert_node + _glp_avl_set_node_link + _glp_avl_set_node_type + _glp_avl_strcmp +;; from glpbfd ;; + _glp_bfd_btran + _glp_bfd_create_it + _glp_bfd_delete_it + _glp_bfd_factorize + _glp_bfd_ftran + _glp_bfd_get_count + _glp_bfd_set_parm + _glp_bfd_update_it +;; from glpbfx ;; + _glp_bfx_btran + _glp_bfx_create_binv + _glp_bfx_delete_binv + _glp_bfx_factorize + _glp_bfx_ftran + _glp_bfx_update +;; from glpcpx ;; + glp_init_cpxcp + glp_read_lp + glp_write_lp +;; from glpdmp ;; + _glp_dmp_create_pool + _glp_dmp_delete_pool + _glp_dmp_free_atom + _glp_dmp_get_atom + _glp_dmp_in_use +;; from glpdmx ;; + glp_read_asnprob + glp_read_ccdata + glp_read_maxflow + glp_read_mincost + glp_read_prob + glp_write_asnprob + glp_write_ccdata + glp_write_maxflow + glp_write_mincost + glp_write_prob +;; from glpenv01 ;; + _glp_get_env_ptr + glp_free_env + glp_init_env + glp_version +;; from glpenv02 ;; + _glp_tls_get_ptr + _glp_tls_set_ptr +;; from glpenv03 ;; + glp_close_tee + glp_open_tee + glp_printf + glp_term_hook + glp_term_out + glp_vprintf +;; from glpenv04 ;; + glp_assert_ + glp_error_ + glp_error_hook +;; from glpenv05 ;; + glp_calloc + glp_free + glp_malloc + glp_mem_limit + glp_mem_usage +;; from glpenv06 ;; + glp_difftime + glp_time +;; from glpenv07 ;; + _glp_lib_err_msg + _glp_lib_xerrmsg + _glp_lib_xfclose + _glp_lib_xfeof + _glp_lib_xferror + _glp_lib_xfflush + _glp_lib_xfgetc + _glp_lib_xfopen + _glp_lib_xfprintf + _glp_lib_xfputc +;; from glpenv08 ;; + _glp_xdlclose + _glp_xdlopen + _glp_xdlsym +;; from glpfhv ;; + _glp_fhv_btran + _glp_fhv_create_it + _glp_fhv_delete_it + _glp_fhv_factorize + _glp_fhv_ftran + _glp_fhv_h_solve + _glp_fhv_update_it +;; from glpgmp ;; + _glp_gmp_free_atom + _glp_gmp_free_mem + _glp_gmp_get_atom + _glp_gmp_get_work + _glp_gmp_pool_count + _glp_mpq_abs + _glp_mpq_add + _glp_mpq_canonicalize + _glp_mpq_clear + _glp_mpq_cmp + _glp_mpq_div + _glp_mpq_get_d + _glp_mpq_init + _glp_mpq_mul + _glp_mpq_neg + _glp_mpq_out_str + _glp_mpq_set + _glp_mpq_set_d + _glp_mpq_set_si + _glp_mpq_sgn + _glp_mpq_sub + _glp_mpz_abs + _glp_mpz_add + _glp_mpz_clear + _glp_mpz_cmp + _glp_mpz_div + _glp_mpz_gcd + _glp_mpz_get_d + _glp_mpz_get_d_2exp + _glp_mpz_init + _glp_mpz_mul + _glp_mpz_neg + _glp_mpz_out_str + _glp_mpz_set + _glp_mpz_set_si + _glp_mpz_sgn + _glp_mpz_sub + _glp_mpz_swap +;; from glphbm ;; + _glp_hbm_free_mat + _glp_hbm_read_mat +;; from glpini01 ;; + glp_adv_basis +;; from glpini02 ;; + glp_cpx_basis +;; from glpios01 ;; + _glp_ios_add_row + _glp_ios_best_node + _glp_ios_clear_pool + _glp_ios_clone_node + _glp_ios_create_pool + _glp_ios_create_tree + _glp_ios_del_row + _glp_ios_delete_node + _glp_ios_delete_pool + _glp_ios_delete_tree + _glp_ios_eval_degrad + _glp_ios_find_row + _glp_ios_freeze_node + _glp_ios_is_hopeful + _glp_ios_relative_gap + _glp_ios_revive_node + _glp_ios_round_bound + _glp_ios_solve_node +;; from glpios02 ;; + _glp_ios_preprocess_node +;; from glpios03 ;; + _glp_ios_driver +;; from glpios04 ;; + _glp_ios_check_vec + _glp_ios_clean_vec + _glp_ios_clear_vec + _glp_ios_copy_vec + _glp_ios_create_vec + _glp_ios_delete_vec + _glp_ios_get_vj + _glp_ios_linear_comb + _glp_ios_set_vj +;; from glpios05 ;; + _glp_ios_gmi_gen +;; from glpios06 ;; + _glp_ios_mir_gen + _glp_ios_mir_init + _glp_ios_mir_term +;; from glpios07 ;; + _glp_ios_cov_gen +;; from glpios08 ;; + _glp_ios_clq_gen + _glp_ios_clq_init + _glp_ios_clq_term +;; from glpios09 ;; + _glp_ios_choose_var + _glp_ios_pcost_branch + _glp_ios_pcost_free + _glp_ios_pcost_init + _glp_ios_pcost_update +;; from glpios10 ;; + _glp_ios_feas_pump +;; from glpios11 ;; + _glp_ios_process_cuts +;; from glpios12 ;; + _glp_ios_choose_node +;; from glpipm ;; + _glp_ipm_solve +;; from glplib01 ;; + _glp_lib_bigdiv + _glp_lib_bigmul +;; from glplib02 ;; + _glp_lib_xladd + _glp_lib_xlcmp + _glp_lib_xldiv + _glp_lib_xlmul + _glp_lib_xlneg + _glp_lib_xlset + _glp_lib_xlsub + _glp_lib_xltoa + _glp_lib_xltod +;; from glplib03 ;; + _glp_lib_fp2rat + _glp_lib_gcd + _glp_lib_gcdn + _glp_lib_jdate + _glp_lib_jday + _glp_lib_lcm + _glp_lib_lcmn + _glp_lib_round2n + _glp_lib_str2int + _glp_lib_str2num + _glp_lib_strrev + _glp_lib_strspx + _glp_lib_strtrim +;; from glplpf ;; + _glp_lpf_btran + _glp_lpf_create_it + _glp_lpf_delete_it + _glp_lpf_factorize + _glp_lpf_ftran + _glp_lpf_update_it +;; from glplpx01 ;; + _glp_lpx_add_cols + _glp_lpx_add_rows + _glp_lpx_adv_basis + _glp_lpx_check_int + _glp_lpx_check_kkt + _glp_lpx_cpx_basis + _glp_lpx_create_index + _glp_lpx_create_prob + _glp_lpx_del_cols + _glp_lpx_del_rows + _glp_lpx_delete_index + _glp_lpx_delete_prob + _glp_lpx_dual_ratio_test + _glp_lpx_eval_tab_col + _glp_lpx_eval_tab_row + _glp_lpx_exact + _glp_lpx_find_col + _glp_lpx_find_row + _glp_lpx_get_class + _glp_lpx_get_col_bnds + _glp_lpx_get_col_dual + _glp_lpx_get_col_info + _glp_lpx_get_col_kind + _glp_lpx_get_col_lb + _glp_lpx_get_col_name + _glp_lpx_get_col_prim + _glp_lpx_get_col_stat + _glp_lpx_get_col_type + _glp_lpx_get_col_ub + _glp_lpx_get_dual_stat + _glp_lpx_get_int_parm + _glp_lpx_get_mat_col + _glp_lpx_get_mat_row + _glp_lpx_get_num_bin + _glp_lpx_get_num_cols + _glp_lpx_get_num_int + _glp_lpx_get_num_nz + _glp_lpx_get_num_rows + _glp_lpx_get_obj_coef + _glp_lpx_get_obj_dir + _glp_lpx_get_obj_name + _glp_lpx_get_obj_val + _glp_lpx_get_prim_stat + _glp_lpx_get_prob_name + _glp_lpx_get_ray_info + _glp_lpx_get_real_parm + _glp_lpx_get_row_bnds + _glp_lpx_get_row_dual + _glp_lpx_get_row_info + _glp_lpx_get_row_lb + _glp_lpx_get_row_name + _glp_lpx_get_row_prim + _glp_lpx_get_row_stat + _glp_lpx_get_row_type + _glp_lpx_get_row_ub + _glp_lpx_get_status + _glp_lpx_integer + _glp_lpx_interior + _glp_lpx_intopt + _glp_lpx_ipt_col_dual + _glp_lpx_ipt_col_prim + _glp_lpx_ipt_obj_val + _glp_lpx_ipt_row_dual + _glp_lpx_ipt_row_prim + _glp_lpx_ipt_status + _glp_lpx_is_b_avail + _glp_lpx_load_matrix + _glp_lpx_main + _glp_lpx_mip_col_val + _glp_lpx_mip_obj_val + _glp_lpx_mip_row_val + _glp_lpx_mip_status + _glp_lpx_prim_ratio_test + _glp_lpx_print_ips + _glp_lpx_print_mip + _glp_lpx_print_prob + _glp_lpx_print_sens_bnds + _glp_lpx_print_sol + _glp_lpx_read_bas + _glp_lpx_read_cpxlp + _glp_lpx_read_freemps + _glp_lpx_read_model + _glp_lpx_read_mps + _glp_lpx_reset_parms + _glp_lpx_scale_prob + _glp_lpx_set_class + _glp_lpx_set_col_bnds + _glp_lpx_set_col_kind + _glp_lpx_set_col_name + _glp_lpx_set_col_stat + _glp_lpx_set_int_parm + _glp_lpx_set_mat_col + _glp_lpx_set_mat_row + _glp_lpx_set_obj_coef + _glp_lpx_set_obj_dir + _glp_lpx_set_obj_name + _glp_lpx_set_prob_name + _glp_lpx_set_real_parm + _glp_lpx_set_row_bnds + _glp_lpx_set_row_name + _glp_lpx_set_row_stat + _glp_lpx_simplex + _glp_lpx_std_basis + _glp_lpx_transform_col + _glp_lpx_transform_row + _glp_lpx_unscale_prob + _glp_lpx_warm_up + _glp_lpx_write_bas + _glp_lpx_write_cpxlp + _glp_lpx_write_freemps + _glp_lpx_write_mps +;; from glplpx02 ;; + _glp_put_mip_soln + _glp_put_solution +;; from glplpx03 ;; + _glp_lpx_write_pb +;; from glpluf ;; + _glp_luf_a_solve + _glp_luf_create_it + _glp_luf_defrag_sva + _glp_luf_delete_it + _glp_luf_enlarge_col + _glp_luf_enlarge_row + _glp_luf_f_solve + _glp_luf_factorize + _glp_luf_v_solve +;; from glplux ;; + _glp_lux_create + _glp_lux_decomp + _glp_lux_delete + _glp_lux_f_solve + _glp_lux_solve + _glp_lux_v_solve +;; from glpmat ;; + _glp_mat_adat_numeric + _glp_mat_adat_symbolic + _glp_mat_amd_order1 + _glp_mat_check_fvs + _glp_mat_check_pattern + _glp_mat_chol_numeric + _glp_mat_chol_symbolic + _glp_mat_min_degree + _glp_mat_symamd_ord + _glp_mat_transpose + _glp_mat_u_solve + _glp_mat_ut_solve +;; from glpmpl01 ;; + _glp_mpl_append_block + _glp_mpl_append_char + _glp_mpl_append_slot + _glp_mpl_arg_list_len + _glp_mpl_branched_expression + _glp_mpl_check_statement + _glp_mpl_close_scope + _glp_mpl_constraint_statement + _glp_mpl_create_arg_list + _glp_mpl_create_block + _glp_mpl_create_domain + _glp_mpl_display_statement + _glp_mpl_domain_arity + _glp_mpl_elemset_argument + _glp_mpl_end_statement + _glp_mpl_enter_context + _glp_mpl_error_dimension + _glp_mpl_error_following + _glp_mpl_error_preceding + _glp_mpl_expand_arg_list + _glp_mpl_expression_0 + _glp_mpl_expression_1 + _glp_mpl_expression_10 + _glp_mpl_expression_11 + _glp_mpl_expression_12 + _glp_mpl_expression_13 + _glp_mpl_expression_2 + _glp_mpl_expression_3 + _glp_mpl_expression_4 + _glp_mpl_expression_5 + _glp_mpl_expression_6 + _glp_mpl_expression_7 + _glp_mpl_expression_8 + _glp_mpl_expression_9 + _glp_mpl_expression_list + _glp_mpl_for_statement + _glp_mpl_function_reference + _glp_mpl_get_char + _glp_mpl_get_token + _glp_mpl_indexing_expression + _glp_mpl_is_keyword + _glp_mpl_is_reserved + _glp_mpl_iterated_expression + _glp_mpl_literal_set + _glp_mpl_make_binary + _glp_mpl_make_code + _glp_mpl_make_ternary + _glp_mpl_make_unary + _glp_mpl_model_section + _glp_mpl_numeric_argument + _glp_mpl_numeric_literal + _glp_mpl_object_reference + _glp_mpl_objective_statement + _glp_mpl_parameter_statement + _glp_mpl_primary_expression + _glp_mpl_print_context + _glp_mpl_printf_statement + _glp_mpl_set_expression + _glp_mpl_set_statement + _glp_mpl_simple_statement + _glp_mpl_solve_statement + _glp_mpl_string_literal + _glp_mpl_subscript_list + _glp_mpl_symbolic_argument + _glp_mpl_table_statement + _glp_mpl_unget_token + _glp_mpl_variable_statement +;; from glpmpl02 ;; + _glp_mpl_create_slice + _glp_mpl_data_section + _glp_mpl_delete_slice + _glp_mpl_expand_slice + _glp_mpl_fake_slice + _glp_mpl_is_literal + _glp_mpl_is_number + _glp_mpl_is_symbol + _glp_mpl_matrix_format + _glp_mpl_parameter_data + _glp_mpl_plain_format + _glp_mpl_read_number + _glp_mpl_read_slice + _glp_mpl_read_symbol + _glp_mpl_read_value + _glp_mpl_select_parameter + _glp_mpl_select_set + _glp_mpl_set_data + _glp_mpl_set_default + _glp_mpl_simple_format + _glp_mpl_slice_arity + _glp_mpl_slice_dimen + _glp_mpl_tabbing_format + _glp_mpl_tabular_format +;; from glpmpl03 ;; + _glp_mpl_add_member + _glp_mpl_add_tuple + _glp_mpl_arelset_member + _glp_mpl_arelset_size + _glp_mpl_assign_dummy_index + _glp_mpl_build_subtuple + _glp_mpl_check_elem_set + _glp_mpl_check_then_add + _glp_mpl_check_value_num + _glp_mpl_check_value_sym + _glp_mpl_clean_check + _glp_mpl_clean_code + _glp_mpl_clean_constraint + _glp_mpl_clean_display + _glp_mpl_clean_domain + _glp_mpl_clean_for + _glp_mpl_clean_parameter + _glp_mpl_clean_printf + _glp_mpl_clean_set + _glp_mpl_clean_statement + _glp_mpl_clean_table + _glp_mpl_clean_variable + _glp_mpl_compare_strings + _glp_mpl_compare_symbols + _glp_mpl_compare_tuples + _glp_mpl_concat_symbols + _glp_mpl_constant_term + _glp_mpl_copy_elemset + _glp_mpl_copy_formula + _glp_mpl_copy_string + _glp_mpl_copy_symbol + _glp_mpl_copy_tuple + _glp_mpl_create_arelset + _glp_mpl_create_array + _glp_mpl_create_elemset + _glp_mpl_create_string + _glp_mpl_create_symbol_num + _glp_mpl_create_symbol_str + _glp_mpl_create_tuple + _glp_mpl_delete_array + _glp_mpl_delete_elemset + _glp_mpl_delete_formula + _glp_mpl_delete_string + _glp_mpl_delete_symbol + _glp_mpl_delete_tuple + _glp_mpl_delete_value + _glp_mpl_enter_domain_block + _glp_mpl_eval_elemset + _glp_mpl_eval_formula + _glp_mpl_eval_logical + _glp_mpl_eval_member_con + _glp_mpl_eval_member_num + _glp_mpl_eval_member_set + _glp_mpl_eval_member_sym + _glp_mpl_eval_member_var + _glp_mpl_eval_numeric + _glp_mpl_eval_symbolic + _glp_mpl_eval_tuple + _glp_mpl_eval_whole_con + _glp_mpl_eval_whole_par + _glp_mpl_eval_whole_set + _glp_mpl_eval_whole_var + _glp_mpl_eval_within_domain + _glp_mpl_execute_check + _glp_mpl_execute_display + _glp_mpl_execute_for + _glp_mpl_execute_printf + _glp_mpl_execute_statement + _glp_mpl_execute_table + _glp_mpl_expand_tuple + _glp_mpl_fetch_string + _glp_mpl_find_member + _glp_mpl_find_tuple + _glp_mpl_format_symbol + _glp_mpl_format_tuple + _glp_mpl_fp_add + _glp_mpl_fp_atan + _glp_mpl_fp_atan2 + _glp_mpl_fp_cos + _glp_mpl_fp_div + _glp_mpl_fp_exp + _glp_mpl_fp_idiv + _glp_mpl_fp_irand224 + _glp_mpl_fp_less + _glp_mpl_fp_log + _glp_mpl_fp_log10 + _glp_mpl_fp_mod + _glp_mpl_fp_mul + _glp_mpl_fp_normal + _glp_mpl_fp_normal01 + _glp_mpl_fp_power + _glp_mpl_fp_round + _glp_mpl_fp_sin + _glp_mpl_fp_sqrt + _glp_mpl_fp_sub + _glp_mpl_fp_trunc + _glp_mpl_fp_uniform01 + _glp_mpl_free_dca + _glp_mpl_get_domain_tuple + _glp_mpl_is_member + _glp_mpl_linear_comb + _glp_mpl_loop_within_domain + _glp_mpl_out_of_domain + _glp_mpl_reduce_terms + _glp_mpl_remove_constant + _glp_mpl_set_cross + _glp_mpl_set_diff + _glp_mpl_set_inter + _glp_mpl_set_symdiff + _glp_mpl_set_union + _glp_mpl_single_variable + _glp_mpl_tab_get_arg + _glp_mpl_tab_get_name + _glp_mpl_tab_get_num + _glp_mpl_tab_get_str + _glp_mpl_tab_get_type + _glp_mpl_tab_num_args + _glp_mpl_tab_num_flds + _glp_mpl_tab_set_num + _glp_mpl_tab_set_str + _glp_mpl_take_member_con + _glp_mpl_take_member_num + _glp_mpl_take_member_set + _glp_mpl_take_member_sym + _glp_mpl_take_member_var + _glp_mpl_tuple_dimen + _glp_mpl_uniform + _glp_mpl_update_dummy_indices +;; from glpmpl04 ;; + _glp_mpl_alloc_content + _glp_mpl_build_problem + _glp_mpl_clean_model + _glp_mpl_close_input + _glp_mpl_error + _glp_mpl_flush_output + _glp_mpl_generate + _glp_mpl_generate_model + _glp_mpl_get_col_bnds + _glp_mpl_get_col_kind + _glp_mpl_get_col_name + _glp_mpl_get_mat_row + _glp_mpl_get_num_cols + _glp_mpl_get_num_rows + _glp_mpl_get_prob_name + _glp_mpl_get_row_bnds + _glp_mpl_get_row_c0 + _glp_mpl_get_row_kind + _glp_mpl_get_row_name + _glp_mpl_has_solve_stmt + _glp_mpl_initialize + _glp_mpl_open_input + _glp_mpl_open_output + _glp_mpl_postsolve + _glp_mpl_postsolve_model + _glp_mpl_put_col_soln + _glp_mpl_put_row_soln + _glp_mpl_read_char + _glp_mpl_read_data + _glp_mpl_read_model + _glp_mpl_terminate + _glp_mpl_warning + _glp_mpl_write_char + _glp_mpl_write_text +;; from glpmpl05 ;; + _glp_mpl_fn_gmtime + _glp_mpl_fn_str2time + _glp_mpl_fn_time2str +;; from glpmpl06 ;; + _glp_mpl_tab_drv_close + _glp_mpl_tab_drv_open + _glp_mpl_tab_drv_read + _glp_mpl_tab_drv_write +;; from glpmps ;; + glp_init_mpscp + glp_read_mps + glp_write_mps +;; from glpnet01 ;; + _glp_mc21a +;; from glpnet02 ;; + _glp_mc13d +;; from glpnet03 ;; + glp_netgen +;; from glpnet04 ;; + glp_gridgen +;; from glpnet05 ;; + glp_rmfgen +;; from glpnet06 ;; + _glp_okalg +;; from glpnet07 ;; + _glp_ffalg +;; from glpnet08 ;; + _glp_wclique +;; from glpnet09 ;; + _glp_kellerman +;; from glpnpp01 ;; + _glp_npp_activate_col + _glp_npp_activate_row + _glp_npp_add_aij + _glp_npp_add_col + _glp_npp_add_row + _glp_npp_build_prob + _glp_npp_col_nnz + _glp_npp_create_wksp + _glp_npp_deactivate_col + _glp_npp_deactivate_row + _glp_npp_del_aij + _glp_npp_del_col + _glp_npp_del_row + _glp_npp_delete_wksp + _glp_npp_erase_row + _glp_npp_insert_col + _glp_npp_insert_row + _glp_npp_load_prob + _glp_npp_postprocess + _glp_npp_push_tse + _glp_npp_remove_col + _glp_npp_remove_row + _glp_npp_row_nnz + _glp_npp_unload_sol +;; from glpnpp02 ;; + _glp_npp_dbnd_col + _glp_npp_fixed_col + _glp_npp_free_col + _glp_npp_free_row + _glp_npp_geq_row + _glp_npp_lbnd_col + _glp_npp_leq_row + _glp_npp_make_equality + _glp_npp_make_fixed + _glp_npp_ubnd_col +;; from glpnpp03 ;; + _glp_npp_analyze_row + _glp_npp_empty_col + _glp_npp_empty_row + _glp_npp_eq_doublet + _glp_npp_eq_singlet + _glp_npp_forcing_row + _glp_npp_implied_bounds + _glp_npp_implied_free + _glp_npp_implied_lower + _glp_npp_implied_slack + _glp_npp_implied_upper + _glp_npp_implied_value + _glp_npp_inactive_bound + _glp_npp_ineq_singlet +;; from glpnpp04 ;; + _glp_npp_binarize_prob + _glp_npp_hidden_covering + _glp_npp_hidden_packing + _glp_npp_implied_packing + _glp_npp_is_covering + _glp_npp_is_packing + _glp_npp_is_partitioning + _glp_npp_reduce_ineq_coef +;; from glpnpp05 ;; + _glp_npp_clean_prob + _glp_npp_improve_bounds + _glp_npp_integer + _glp_npp_process_col + _glp_npp_process_prob + _glp_npp_process_row + _glp_npp_simplex +;; from glpqmd ;; + _glp_qmd_genqmd + _glp_qmd_qmdmrg + _glp_qmd_qmdqt + _glp_qmd_qmdrch + _glp_qmd_qmdupd +;; from glprgr ;; + _glp_rgr_write_bmp16 +;; from glprng01 ;; + _glp_rng_create_rand + _glp_rng_delete_rand + _glp_rng_init_rand + _glp_rng_next_rand + _glp_rng_unif_rand +;; from glprng02 ;; + _glp_rng_unif_01 + _glp_rng_uniform +;; from glpscf ;; + _glp_scf_create_it + _glp_scf_delete_it + _glp_scf_reset_it + _glp_scf_solve_it + _glp_scf_update_exp +;; from glpscl ;; + glp_scale_prob +;; from glpsdf ;; + glp_sdf_close_file + glp_sdf_error + glp_sdf_line + glp_sdf_open_file + glp_sdf_read_int + glp_sdf_read_item + glp_sdf_read_num + glp_sdf_read_text + glp_sdf_set_jump + glp_sdf_warning +;; from glpspm ;; + _glp_spm_add_mat + _glp_spm_add_num + _glp_spm_add_sym + _glp_spm_check_per + _glp_spm_count_nnz + _glp_spm_create_mat + _glp_spm_create_per + _glp_spm_delete_mat + _glp_spm_delete_per + _glp_spm_drop_zeros + _glp_spm_mul_mat + _glp_spm_mul_num + _glp_spm_mul_sym + _glp_spm_new_elem + _glp_spm_read_hbm + _glp_spm_read_mat + _glp_spm_show_mat + _glp_spm_test_mat_d + _glp_spm_test_mat_e + _glp_spm_transpose + _glp_spm_write_mat +;; from glpspx01 ;; + _glp_spx_primal +;; from glpspx02 ;; + _glp_spx_dual +;; from glpsql ;; + _glp_db_iodbc_close + _glp_db_iodbc_open + _glp_db_iodbc_read + _glp_db_iodbc_write + _glp_db_mysql_close + _glp_db_mysql_open + _glp_db_mysql_read + _glp_db_mysql_write +;; from glpssx01 ;; + _glp_ssx_change_basis + _glp_ssx_chuzc + _glp_ssx_chuzr + _glp_ssx_create + _glp_ssx_delete + _glp_ssx_eval_bbar + _glp_ssx_eval_cbar + _glp_ssx_eval_col + _glp_ssx_eval_dj + _glp_ssx_eval_pi + _glp_ssx_eval_rho + _glp_ssx_eval_row + _glp_ssx_factorize + _glp_ssx_get_xNj + _glp_ssx_update_bbar + _glp_ssx_update_cbar + _glp_ssx_update_pi +;; from glpssx02 ;; + _glp_ssx_driver + _glp_ssx_phase_I + _glp_ssx_phase_II +;; from glptsp ;; + _glp_tsp_distance + _glp_tsp_free_data + _glp_tsp_read_data diff -r d59bea55db9b -r c445c931472f w64/makefile_VC --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/makefile_VC Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,132 @@ +# Build GLPK with Microsoft Visual Studio Express + +CFLAGS = /I. /I..\include /I..\src /I..\src\amd /I..\src\colamd \ + /DHAVE_CONFIG_H /D_CRT_SECURE_NO_WARNINGS /nologo /W3 /O2 + +OBJSET = \ +..\src\glpapi01.obj \ +..\src\glpapi02.obj \ +..\src\glpapi03.obj \ +..\src\glpapi04.obj \ +..\src\glpapi05.obj \ +..\src\glpapi06.obj \ +..\src\glpapi07.obj \ +..\src\glpapi08.obj \ +..\src\glpapi09.obj \ +..\src\glpapi10.obj \ +..\src\glpapi11.obj \ +..\src\glpapi12.obj \ +..\src\glpapi13.obj \ +..\src\glpapi14.obj \ +..\src\glpapi15.obj \ +..\src\glpapi16.obj \ +..\src\glpapi17.obj \ +..\src\glpapi18.obj \ +..\src\glpapi19.obj \ +..\src\glpavl.obj \ +..\src\glpbfd.obj \ +..\src\glpbfx.obj \ +..\src\glpcpx.obj \ +..\src\glpdmp.obj \ +..\src\glpdmx.obj \ +..\src\glpenv01.obj \ +..\src\glpenv02.obj \ +..\src\glpenv03.obj \ +..\src\glpenv04.obj \ +..\src\glpenv05.obj \ +..\src\glpenv06.obj \ +..\src\glpenv07.obj \ +..\src\glpenv08.obj \ +..\src\glpfhv.obj \ +..\src\glpgmp.obj \ +..\src\glphbm.obj \ +..\src\glpini01.obj \ +..\src\glpini02.obj \ +..\src\glpios01.obj \ +..\src\glpios02.obj \ +..\src\glpios03.obj \ +..\src\glpios04.obj \ +..\src\glpios05.obj \ +..\src\glpios06.obj \ +..\src\glpios07.obj \ +..\src\glpios08.obj \ +..\src\glpios09.obj \ +..\src\glpios10.obj \ +..\src\glpios11.obj \ +..\src\glpios12.obj \ +..\src\glpipm.obj \ +..\src\glplib01.obj \ +..\src\glplib02.obj \ +..\src\glplib03.obj \ +..\src\glplpf.obj \ +..\src\glplpx01.obj \ +..\src\glplpx02.obj \ +..\src\glplpx03.obj \ +..\src\glpluf.obj \ +..\src\glplux.obj \ +..\src\glpmat.obj \ +..\src\glpmpl01.obj \ +..\src\glpmpl02.obj \ +..\src\glpmpl03.obj \ +..\src\glpmpl04.obj \ +..\src\glpmpl05.obj \ +..\src\glpmpl06.obj \ +..\src\glpmps.obj \ +..\src\glpnet01.obj \ +..\src\glpnet02.obj \ +..\src\glpnet03.obj \ +..\src\glpnet04.obj \ +..\src\glpnet05.obj \ +..\src\glpnet06.obj \ +..\src\glpnet07.obj \ +..\src\glpnet08.obj \ +..\src\glpnet09.obj \ +..\src\glpnpp01.obj \ +..\src\glpnpp02.obj \ +..\src\glpnpp03.obj \ +..\src\glpnpp04.obj \ +..\src\glpnpp05.obj \ +..\src\glpqmd.obj \ +..\src\glprgr.obj \ +..\src\glprng01.obj \ +..\src\glprng02.obj \ +..\src\glpscf.obj \ +..\src\glpscl.obj \ +..\src\glpsdf.obj \ +..\src\glpspm.obj \ +..\src\glpspx01.obj \ +..\src\glpspx02.obj \ +..\src\glpsql.obj \ +..\src\glpssx01.obj \ +..\src\glpssx02.obj \ +..\src\glptsp.obj \ +..\src\amd\amd_1.obj \ +..\src\amd\amd_2.obj \ +..\src\amd\amd_aat.obj \ +..\src\amd\amd_control.obj \ +..\src\amd\amd_defaults.obj \ +..\src\amd\amd_dump.obj \ +..\src\amd\amd_info.obj \ +..\src\amd\amd_order.obj \ +..\src\amd\amd_post_tree.obj \ +..\src\amd\amd_postorder.obj \ +..\src\amd\amd_preprocess.obj \ +..\src\amd\amd_valid.obj \ +..\src\colamd\colamd.obj + +.c.obj: + cl.exe $(CFLAGS) /Fo$*.obj /c $*.c + +all: glpk.lib glpsol.exe + +glpk.lib: $(OBJSET) + lib.exe /out:glpk.lib ..\src\*.obj ..\src\amd\*.obj \ + ..\src\colamd\*.obj + +glpsol.exe: ..\examples\glpsol.obj glpk.lib + cl.exe $(CFLAGS) /Feglpsol.exe ..\examples\glpsol.obj \ + glpk.lib + +check: glpsol.exe + .\glpsol.exe --version + .\glpsol.exe --mps ..\examples\plan.mps diff -r d59bea55db9b -r c445c931472f w64/makefile_VC_DLL --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/makefile_VC_DLL Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,132 @@ +# Build GLPK DLL with Microsoft Visual Studio Express + +CFLAGS = /I. /I..\include /I..\src /I..\src\amd /I..\src\colamd \ + /DHAVE_CONFIG_H /D_CRT_SECURE_NO_WARNINGS /nologo /W3 /O2 + +OBJSET = \ +..\src\glpapi01.obj \ +..\src\glpapi02.obj \ +..\src\glpapi03.obj \ +..\src\glpapi04.obj \ +..\src\glpapi05.obj \ +..\src\glpapi06.obj \ +..\src\glpapi07.obj \ +..\src\glpapi08.obj \ +..\src\glpapi09.obj \ +..\src\glpapi10.obj \ +..\src\glpapi11.obj \ +..\src\glpapi12.obj \ +..\src\glpapi13.obj \ +..\src\glpapi14.obj \ +..\src\glpapi15.obj \ +..\src\glpapi16.obj \ +..\src\glpapi17.obj \ +..\src\glpapi18.obj \ +..\src\glpapi19.obj \ +..\src\glpavl.obj \ +..\src\glpbfd.obj \ +..\src\glpbfx.obj \ +..\src\glpcpx.obj \ +..\src\glpdmp.obj \ +..\src\glpdmx.obj \ +..\src\glpenv01.obj \ +..\src\glpenv02.obj \ +..\src\glpenv03.obj \ +..\src\glpenv04.obj \ +..\src\glpenv05.obj \ +..\src\glpenv06.obj \ +..\src\glpenv07.obj \ +..\src\glpenv08.obj \ +..\src\glpfhv.obj \ +..\src\glpgmp.obj \ +..\src\glphbm.obj \ +..\src\glpini01.obj \ +..\src\glpini02.obj \ +..\src\glpios01.obj \ +..\src\glpios02.obj \ +..\src\glpios03.obj \ +..\src\glpios04.obj \ +..\src\glpios05.obj \ +..\src\glpios06.obj \ +..\src\glpios07.obj \ +..\src\glpios08.obj \ +..\src\glpios09.obj \ +..\src\glpios10.obj \ +..\src\glpios11.obj \ +..\src\glpios12.obj \ +..\src\glpipm.obj \ +..\src\glplib01.obj \ +..\src\glplib02.obj \ +..\src\glplib03.obj \ +..\src\glplpf.obj \ +..\src\glplpx01.obj \ +..\src\glplpx02.obj \ +..\src\glplpx03.obj \ +..\src\glpluf.obj \ +..\src\glplux.obj \ +..\src\glpmat.obj \ +..\src\glpmpl01.obj \ +..\src\glpmpl02.obj \ +..\src\glpmpl03.obj \ +..\src\glpmpl04.obj \ +..\src\glpmpl05.obj \ +..\src\glpmpl06.obj \ +..\src\glpmps.obj \ +..\src\glpnet01.obj \ +..\src\glpnet02.obj \ +..\src\glpnet03.obj \ +..\src\glpnet04.obj \ +..\src\glpnet05.obj \ +..\src\glpnet06.obj \ +..\src\glpnet07.obj \ +..\src\glpnet08.obj \ +..\src\glpnet09.obj \ +..\src\glpnpp01.obj \ +..\src\glpnpp02.obj \ +..\src\glpnpp03.obj \ +..\src\glpnpp04.obj \ +..\src\glpnpp05.obj \ +..\src\glpqmd.obj \ +..\src\glprgr.obj \ +..\src\glprng01.obj \ +..\src\glprng02.obj \ +..\src\glpscf.obj \ +..\src\glpscl.obj \ +..\src\glpsdf.obj \ +..\src\glpspm.obj \ +..\src\glpspx01.obj \ +..\src\glpspx02.obj \ +..\src\glpsql.obj \ +..\src\glpssx01.obj \ +..\src\glpssx02.obj \ +..\src\glptsp.obj \ +..\src\amd\amd_1.obj \ +..\src\amd\amd_2.obj \ +..\src\amd\amd_aat.obj \ +..\src\amd\amd_control.obj \ +..\src\amd\amd_defaults.obj \ +..\src\amd\amd_dump.obj \ +..\src\amd\amd_info.obj \ +..\src\amd\amd_order.obj \ +..\src\amd\amd_post_tree.obj \ +..\src\amd\amd_postorder.obj \ +..\src\amd\amd_preprocess.obj \ +..\src\amd\amd_valid.obj \ +..\src\colamd\colamd.obj + +.c.obj: + cl.exe $(CFLAGS) /Fo$*.obj /c $*.c + +all: glpk_4_45.dll glpsol.exe + +glpk_4_45.dll: $(OBJSET) + cl.exe $(CFLAGS) /LD /Feglpk_4_45.dll ..\src\*.obj \ + ..\src\amd\*.obj ..\src\colamd\*.obj glpk_4_45.def + +glpsol.exe: ..\examples\glpsol.obj glpk_4_45.dll + cl.exe $(CFLAGS) /Feglpsol.exe ..\examples\glpsol.obj \ + glpk_4_45.lib + +check: glpsol.exe + .\glpsol.exe --version + .\glpsol.exe --mps ..\examples\plan.mps diff -r d59bea55db9b -r c445c931472f w64/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w64/readme.txt Mon Dec 06 13:09:21 2010 +0100 @@ -0,0 +1,24 @@ +This directory contains batch files and other stuff which you can use +to build GLPK for 64-bit Windows with the native C/C++ compilers. + +Before running the batch file do the following: + +1. Make sure that you have installed the compiler you are going to use + to build GLPK. + +2. Look into corresponding batch file (just right-click it and choose + 'Edit' in the popup menu; DO NOT choose 'Open'). Make sure that HOME + variable specifies correct path to the compiler directory; if not, + make necessary changes. + +To run the batch file just double-click it and wait a bit while the +Make utility does its job. The message 'OPTIMAL SOLUTION FOUND' in the +MS-DOS window means that all is OK. If you do not see it, something is +wrong. + +Once GLPK has been successfully built, there must appear two files in +this directory: + +glpk.lib, which is the GLPK object library, and + +glpsol.exe, which is the stand-alone GLPK LP/MIP solver.