source: trunk/coopr/plugins/mip/GLPK.py @ 1768

Last change on this file since 1768 was 1768, checked in by wehart, 11 years ago

Rework of Coopr to use the new PyUtilib? package decomposition.

NOTE: to use Coopr with this update, we need to work with a new version of coopr_install.

File size: 14.0 KB
Line 
1#  _________________________________________________________________________
2#
3#  Coopr: A COmmon Optimization Python Repository
4#  Copyright (c) 2008 Sandia Corporation.
5#  This software is distributed under the BSD License.
6#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7#  the U.S. Government retains certain rights in this software.
8#  For more information, see the Coopr README.txt file.
9#  _________________________________________________________________________
10
11
12import os
13import re
14from coopr.opt.base import *
15from coopr.opt.solver import *
16import mockmip
17import pyutilib.services
18import pyutilib.misc
19import pyutilib.common
20import pyutilib.plugin.core
21
22
23class GLPK(SystemCallSolver):
24    """The GLPK LP/MIP solver
25    """
26
27    def __init__(self, **kwds):
28        #
29        # Call base constructor
30        #
31        kwds['type'] = 'glpk'
32        SystemCallSolver.__init__(self, **kwds)
33        #
34        # Valid problem formats, and valid results for each format
35        #
36        self._valid_problem_formats=[ProblemFormat.mod, ProblemFormat.cpxlp, ProblemFormat.mps]
37        self._valid_result_formats={}
38        self._valid_result_formats[ProblemFormat.mod] = [ResultsFormat.log]
39        self._valid_result_formats[ProblemFormat.cpxlp] = [ResultsFormat.log]
40        self._valid_result_formats[ProblemFormat.mps] = [ResultsFormat.log]
41
42    def executable(self):
43        executable = pyutilib.services.registered_executable("glpsol")
44        if executable is None:
45            pyutilib.plugin.core.PluginGlobals.env().log.error("Could not locate the 'glpsol' executable, which is required for solver %s" % self.name)
46            self.enable = False
47            return None
48        return executable.get_path()
49
50    def create_command_line(self,executable,problem_files):
51        #
52        # Define log file
53        #
54        if self.log_file is None:
55           self.log_file = pyutilib.services.TempfileManager.create_tempfile(suffix = '.glpk.log')
56
57        #
58        # Define solution file
59        #
60        if self.soln_file is None:
61            self.soln_file = pyutilib.services.TempfileManager.create_tempfile(suffix = '.glpk.soln')
62
63        #
64        # Define results file
65        #
66        if self._results_format is None or self._results_format == ResultsFormat.log:
67           self.results_file = self.log_file
68           
69        #
70        # Define command line
71        #
72        if self._timelimit is not None and self._timelimit > 0.0:
73           timing = " --tmlim "+str(self._timelimit)+" "
74        else:
75           timing = ""
76        if self._problem_format == ProblemFormat.cpxlp:
77            problem=" --cpxlp " + problem_files[0]
78        elif self._problem_format == ProblemFormat.mps:
79            problem=" --freemps " + problem_files[0]
80        elif self._problem_format == ProblemFormat.mod:
81            problem=" --math " + problem_files[0]
82            for filename in problem_files[1:]:
83                problem += " --data " + filename
84        opt = ""
85        for key in self.options:
86            if isinstance(self.options[key],basestring) and ' ' in self.options[key]:
87                opt += " --"+key+" \""+str(self.options[key])+"\""
88            else:
89                opt += " --"+key+" "+str(self.options[key])
90        proc = self._timer + " " + executable + opt + " " + timing + " --output " + self.soln_file + problem
91        return pyutilib.misc.Bunch(cmd=proc, log_file=self.log_file, env=None)
92
93    def process_logfile(self):
94        """
95        Process logfile
96        """
97        results = SolverResults()
98        #
99        # Initial values
100        #
101        results.solver.ncreated=0
102        results.solver.nbounded=0
103        soln = results.solution.create()
104        #
105        # Process logfile
106        #
107        OUTPUT = open(self.log_file)
108        output = "".join(OUTPUT.readlines())
109        OUTPUT.close()
110        #
111        # Parse logfile lines
112        #
113        for line in output.split("\n"):
114          tokens = re.split('[ \t]+',line.strip())
115          #print "LINE:", tokens
116          if len(tokens) > 4 and tokens[1] == "objval":
117             soln.value = tokens[3]
118          elif len(tokens) > 3 and tokens[0] == "Objective" and tokens[1] == "value":
119             soln.value = tokens[3]
120          elif len(tokens) > 4 and tokens[0] == "!" and tokens[2] == "objval":
121             soln.value = tokens[4]
122          elif len(tokens) > 4 and tokens[0] == "+" and tokens[2] == "objval":
123             soln.value = tokens[4]
124          elif len(tokens) > 4 and tokens[0] == "*" and tokens[2] == "objval":
125             soln.value = tokens[4]
126          elif len(tokens) > 4 and tokens[0] == "+" and tokens[2] == "mip" and tokens[4] == "not":
127             soln.value = "unknown"
128             results.problem.lower_bound = tokens[8]
129          elif len(tokens) > 4 and tokens[0] == "+" and tokens[1] == "mip" and tokens[4] == "not":
130             soln.value = "unknown"
131             results.problem.lower_bound = tokens[7]
132          elif len(tokens) > 4 and tokens[0] == "+" and tokens[2] == "mip" and tokens[4] != "not":
133             soln.value = tokens[4]
134             if tokens[6] != "tree":
135                results.problem.lower_bound = tokens[6]
136          elif len(tokens) > 4 and tokens[0] == "+" and tokens[1] == "mip" and tokens[4] != "not":
137             soln.value = tokens[3]
138             results.problem.lower_bound = tokens[5]
139          elif len(tokens) == 6 and tokens[0] == "OPTIMAL" and tokens[1] == "SOLUTION" and tokens[5] == "PRESOLVER":
140             results.solver.ncreated = 0
141             results.solver.nbounded = 0
142             soln.status = SolutionStatus.optimal
143          elif len(tokens) == 7 and tokens[1] == "OPTIMAL" and tokens[2] == "SOLUTION" and tokens[6] == "PRESOLVER":
144             results.solver.ncreated = 0
145             results.solver.nbounded = 0
146             soln.status = SolutionStatus.optimal
147          elif len(tokens) > 10 and tokens[0] == "+" and tokens[8] == "empty":
148             results.solver.ncreated = tokens[11][:-1]
149             results.solver.nbounded = tokens[11][:-1]
150          elif len(tokens) > 9 and tokens[0] == "+" and tokens[7] == "empty":
151             results.solver.ncreated = tokens[10][:-1]
152             results.solver.nbounded = tokens[10][:-1]
153          elif len(tokens) == 2 and tokens[0] == "sys":
154             results.solver.systime=tokens[1]
155          elif len(tokens) == 2 and tokens[0] == "user":
156             results.solver.usrtime=tokens[1]
157          elif len(tokens) > 2 and tokens[0] == "OPTIMAL" and tokens[1] == "SOLUTION":
158             soln.status = SolutionStatus.optimal
159          elif len(tokens) > 2 and tokens[0] == "INTEGER" and tokens[1] == "OPTIMAL":
160             soln.status = SolutionStatus.optimal
161          elif len(tokens) > 2 and tokens[0] == "TIME" and tokens[2] == "EXCEEDED;":
162             soln.status = SolutionStatus.stoppedByLimit
163        if results.problem.upper_bound == "inf":
164           results.problem.upper_bound = "Infinity"
165        if results.problem.lower_bound == "-inf":
166           results.problem.lower_bound = "-Infinity"
167        try:
168            tmp = eval(results.problem.upper_bound.strip())
169            results.problem.upper_bound = str(tmp)
170        except:
171            pass
172        try:
173            tmp = eval(results.problem.lower_bound.strip())
174            results.problem.lower_bound = str(tmp)
175        except:
176            pass
177        try:
178            tmp = eval(soln.value.strip())
179            soln.value = str(tmp)
180        except:
181            pass
182        if soln.status is SolutionStatus.optimal:
183           soln.gap=0.0
184        elif soln.status is SolutionStatus.stoppedByLimit:
185           soln.gap = "Infinity" # until proven otherwise
186           if "lower_bound" in dir(results.problem):
187              if results.problem.lower_bound is "-Infinity":
188                 soln.gap="Infinity"
189              elif not results.problem.lower_bound is None:
190                 if "upper_bound" not in dir(results.problem):
191                    gap="Infinity"
192                 elif results.problem.upper_bound is None:
193                    gap="Infinity"
194                 else:
195                    soln.gap=eval(soln.value) - eval(results.problem.lower_bound)
196           elif "upper_bound" in dir(results.problem):
197              if results.problem.upper_bound is "Infinity":
198                 soln.gap="Infinity"
199              elif not results.problem.upper_bound is None:
200                 soln.gap=eval(results.problem.upper_bound) - eval(soln.value)
201        if results.solver.status is SolverStatus.error:
202           results.solution.delete(0)
203        return results
204
205    def process_other_data(self,results):
206        lp_solution=False
207        if not os.path.exists(self.soln_file):
208           return
209        soln = results.solution(0)
210        INPUT = open(self.soln_file,"r")
211        state=0
212        results.problem.num_objectives=1
213        for line in INPUT:
214          tokens = re.split('[ \t]+',line.strip())
215          ##print "LINE",line,len(tokens)
216          ##print "TOKENS",tokens
217          if state==0:
218             #
219             # Processing initial header
220             #
221             if len(tokens) == 2 and tokens[0] == "Problem:":
222                results.problem.name = tokens[1]
223             elif len(tokens) == 2 and tokens[0] == "Rows:":
224                results.problem.num_constraints = tokens[1]
225             elif len(tokens) >= 2 and tokens[0] == "Columns:":
226                results.problem.num_variables = tokens[1]
227             elif len(tokens) == 2 and tokens[0] == "Non-zeros:":
228                results.problem.num_nonzeros = tokens[1]
229             elif len(tokens) >= 2 and tokens[0] == "Status:":
230                if tokens[1] == "OPTIMAL":
231                   soln.status = SolutionStatus.optimal
232                elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "NON-OPTIMAL":
233                   soln.status = SolutionStatus.bestSoFar
234                elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "OPTIMAL":
235                   soln.status = SolutionStatus.optimal
236                elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "UNDEFINED":
237                   soln.status = SolutionStatus.unbounded                   
238                else:
239                   print "GLPK WARNING: unknown status: "+" ".join(tokens[1:])
240             elif len(tokens) >= 2 and tokens[0] == "Objective:":
241                if tokens[4] == "(MINimum)":
242                   results.problem.sense = ProblemSense.minimize
243                else:
244                   results.problem.sense = ProblemSense.maximize
245                soln.value=tokens[3]
246                if soln.status is SolutionStatus.optimal:
247                   if tokens[4] == "(MINimum)":
248                        results.problem.lower_bound = soln.value
249                        if "upper_bound" in dir(results.problem):
250                            del results.problem.upper_bound
251                   else:
252                        results.problem.upper_bound = soln.value
253                        if "lower_bound" in dir(results.problem):
254                            del results.problem.lower_bound
255             elif len(tokens) > 1 and tokens[0] == "No.":
256                if tokens[1] == "Row":
257                   state=1
258                else:
259                   state=2
260          elif state==1:
261             #
262             # Process Constraint Info
263             #
264             if len(tokens) == 0:
265                continue
266             if len(tokens) > 1 and tokens[0] == "No.":
267                state=2
268                if line[65:78].strip() == "Marginal":
269                   lp_solution=True
270             else:
271                tmp=line[0:6]
272                tmp = tmp.strip()
273                if tmp == "------":
274                   continue
275                if tmp != "":
276                   tmpname = tokens[1]
277                   if len(tokens) == 2:
278                      continue
279                tmp=line[65:78]
280                tmp=tmp.strip()
281                ##print "HERE",tmp
282                if tmp=="":
283                   if lp_solution:
284                      soln.dual.add( tmpname,0.0 )
285                else:
286                   if tmp.strip() == "< eps":
287                      soln.dual.add( tmpname,0.0 )
288                   else:
289                      soln.dual.add( tmpname,tmp )
290          elif state==2:
291             #
292             # Process Variable Info
293             #
294             if len(tokens) == 1:
295                state=-1
296                continue
297             if True:
298                tmp=line[0:6]
299                tmp = tmp.strip()
300                if tmp == "------":
301                   continue
302                if tmp != "":
303                   tmpname = tokens[1]
304                   if len(tokens) == 2:
305                      continue
306                tmp=line[23:36]
307                tmp=tmp.strip()
308                if tmp=="":
309                   soln.variable.add( tmpname,0.0 )
310                else:
311                   soln.variable.add( tmpname,tmp )
312          if state==-1:
313             break
314        INPUT.close()
315
316
317
318class MockGLPK(GLPK,mockmip.MockMIP):
319    """A Mock GLPK solver used for testing
320    """
321
322    def __init__(self, **kwds):
323        try:
324           GLPK.__init__(self, **kwds)
325        except pyutilib.common.ApplicationError: #pragma:nocover
326           pass                        #pragma:nocover
327        mockmip.MockMIP.__init__(self,"glpk")
328
329    def available(self, exception_flag=True):
330        return GLPK.available(self,exception_flag)
331
332    def create_command_line(self,executable,problem_files):
333        command = GLPK.create_command_line(self,executable,problem_files)
334        mockmip.MockMIP.create_command_line(self,executable,problem_files)
335        return command
336
337    def executable(self):
338        return mockmip.MockMIP.executable(self)
339
340    def _execute_command(self,cmd):
341        return mockmip.MockMIP._execute_command(self,cmd)
342
343    def _convert_problem(self,args,pformat,valid_pformats):
344        if pformat in [ProblemFormat.mps,ProblemFormat.cpxlp]:
345           return (args,pformat)
346        else:
347           return (args,ProblemFormat.cpxlp)
348
349
350pyutilib.services.register_executable(name="glpsol")
351SolverRegistration("glpk", GLPK)
352SolverRegistration("_mock_glpk", MockGLPK)
Note: See TracBrowser for help on using the repository browser.