source: coopr.plugins/trunk/coopr/plugins/solvers/CPLEX.py @ 5778

Last change on this file since 5778 was 5778, checked in by jwatson, 8 years ago

Fixing issue with CPLEX plugin where only non-zero slacks and duals were being added to the results object, resulting in "None" values on the instance when solutions were loaded.

File size: 29.4 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.results import *
16from coopr.opt.solver import *
17import mockmip
18import pyutilib.services
19import pyutilib.common
20import pyutilib.misc
21import pyutilib.component.core
22import string
23import re
24import xml.dom.minidom
25import time
26import logging
27
28
29class CPLEX(OptSolver):
30    """The CPLEX LP/MIP solver
31    """
32
33    pyutilib.component.core.alias('cplex', doc='The CPLEX MIP solver')
34
35    def __new__(cls, *args, **kwds):
36        try:
37            mode = kwds['solver_io']
38            if mode is None:
39                mode = 'lp'
40            del kwds['solver_io']
41        except KeyError:
42            mode = 'lp'
43        #
44        if mode  == 'lp':
45            return SolverFactory('_cplex_shell', **kwds)
46        if mode == 'python':
47            opt = SolverFactory('_cplex_direct', **kwds)
48            if opt is None:
49                logging.getLogger('coopr.plugins').error('Python API for CPLEX is not installed')
50                return
51            return opt
52        #
53        if mode == 'os':
54            opt = SolverFactory('_ossolver', **kwds)
55        elif mode == 'nl':
56            opt = SolverFactory('_asl', **kwds)
57        else:
58            logging.getLogger('coopr.plugins').error('Unknown IO type: %s' % mode)
59            return
60        opt.set_options('solver=cplexamp')
61        return opt
62
63
64class CPLEXSHELL(ILMLicensedSystemCallSolver):
65    """Shell interface to the CPLEX LP/MIP solver
66    """
67
68    pyutilib.component.core.alias('_cplex_shell', doc='Shell interface to the CPLEX LP/MIP solver')
69
70    def __init__(self, **kwds):
71        #
72        # Call base class constructor
73        #
74        kwds['type'] = 'cplex'
75        ILMLicensedSystemCallSolver.__init__(self, **kwds)
76
77        # NOTE: eventually both of the following attributes should be migrated to a common base class.
78        # is the current solve warm-started? a transient data member to communicate state information
79        # across the _presolve, _apply_solver, and _postsolve methods.
80        self.warm_start_solve = False
81        # related to the above, the temporary name of the MST warm-start file (if any).
82        self.warm_start_file_name = None
83
84        #
85        # Define valid problem formats and associated results formats
86        #
87        self._valid_problem_formats=[ProblemFormat.cpxlp, ProblemFormat.mps]
88        self._valid_result_formats={}
89        self._valid_result_formats[ProblemFormat.cpxlp] = [ResultsFormat.soln]
90        self._valid_result_formats[ProblemFormat.mps] = [ResultsFormat.soln]
91
92        # Note: Undefined capabilities default to 'None'
93        self._capabilities = pyutilib.misc.Options()
94        self._capabilities.linear = True
95        self._capabilities.quadratic = True
96        self._capabilities.integer = True
97        self._capabilities.sos1 = True
98        self._capabilities.sos2 = True
99
100    #
101    # CPLEX has a simple, easy-to-use warm-start capability.
102    #
103    def warm_start_capable(self):
104        return True
105
106    #
107    # write a warm-start file in the CPLEX MST format.
108    #
109    def warm_start(self, instance):
110
111        def _error_labeler(obj):
112            raise KeyError(
113                "CPLEX Warm Start Error: instance contains an object, %s, "
114                "that is not found in the symbol map.  Does the instance "
115                "match the provided symbol map?" % ( obj.name, ))
116
117        from coopr.pyomo.base import Var, VarStatus
118
119        doc = xml.dom.minidom.Document()
120        root_element = doc.createElement("CPLEXSolution")
121        root_element.setAttribute("version","1.0")
122        doc.appendChild(root_element)
123
124        # currently not populated.
125        header_element = doc.createElement("header")
126        # currently not populated.
127        quality_element = doc.createElement("quality")
128        # definitely populated!
129        variables_element = doc.createElement("variables")
130
131        root_element.appendChild(header_element)
132        root_element.appendChild(quality_element)
133        root_element.appendChild(variables_element)
134
135        # for each variable in the symbol_map, add a child to the
136        # variables element.  Both continuous and discrete are accepted
137        # (and required, depending on other options), according to the
138        # CPLEX manual.  Note, this assumes that the symbol_map matches
139        # the instance...
140        output_index = 0
141        for block in instance.all_blocks():
142            for variable in block.active_components(Var).itervalues():
143                for index in variable:
144                    var = variable[index]
145                    if var.status == VarStatus.unused or var.value is None \
146                            or var.fixed:
147                        continue
148                    name = self._symbol_map.getSymbol(var, _error_labeler)
149
150                    variable_element = doc.createElement("variable")
151                    variable_element.setAttribute("name", name)
152                    variable_element.setAttribute("index", str(output_index))
153                    variable_element.setAttribute("value", str(var.value))
154                    variables_element.appendChild(variable_element)
155                    output_index = output_index + 1
156
157        mst_file = open(self.warm_start_file_name,'w')
158        doc.writexml(mst_file, indent="    ", newl="\n")
159        mst_file.close()
160
161    # over-ride presolve to extract the warm-start keyword, if specified.
162    def _presolve(self, *args, **kwds):
163
164        # if the first argument is a string (representing a filename),
165        # then we don't have an instance => the solver is being applied
166        # to a file.
167        self.warm_start_solve = kwds.pop( 'warmstart', False )
168
169        # the input argument can currently be one of two things: an instance or a filename.
170        # if a filename is provided and a warm-start is indicated, we go ahead and
171        # create the temporary file - assuming that the user has already, via some external
172        # mechanism, invoked warm_start() with a instance to create the warm start file.
173        if (self.warm_start_solve is True) and (isinstance(args[0],basestring) is True):
174            pass # we assume the user knows what they are doing...
175        elif (self.warm_start_solve is True) and (isinstance(args[0],basestring) is False) and (args[0].has_discrete_variables() is True):
176           # assign the name of the warm start file *before* calling the base class
177           # presolve - the base class method ends up creating the command line,
178           # and the warm start file-name is (obviously) needed there.
179           self.warm_start_file_name = pyutilib.services.TempfileManager.create_tempfile(suffix = '.cplex.mst')
180        else:
181           self.warm_start_file_name = None
182
183        # let the base class handle any remaining keywords/actions.
184        ILMLicensedSystemCallSolver._presolve(self, *args, **kwds)
185
186        # NB: we must let the base class presolve run first so that the
187        # symbol_map is actually constructed!
188
189        if (len(args) > 0) and (isinstance(args[0],basestring) is False):
190
191            # write the warm-start file - currently only supports MIPs.
192            # we only know how to deal with a single problem instance.
193            if self.warm_start_solve is True:
194
195                if len(args) != 1:
196                    raise ValueError(
197                        "CPLEX _presolve method can only handle a single "
198                        "problem instance - %s were supplied" % (len(args),))
199
200                if args[0].has_discrete_variables() is True:
201                    start_time = time.time()
202                    self.warm_start(args[0])
203                    end_time = time.time()
204                    if self._report_timing is True:
205                        print "Warm start write time= %.2f seconds" % (end_time-start_time)
206
207    def executable(self):
208        executable = pyutilib.services.registered_executable("cplex")
209        if executable is None:
210            pyutilib.component.core.PluginGlobals.env().log.error("Could not locate the 'cplex' executable, which is required for solver %s" % self.name)
211            self.enable = False
212            return None
213        return executable.get_path()
214
215    def create_command_line(self,executable,problem_files):
216
217        #
218        # Define log file
219        # The log file in CPLEX contains the solution trace, but the solver status can be found in the solution file.
220        #
221        self.log_file = pyutilib.services.TempfileManager.create_tempfile(suffix = '.cplex.log')
222
223        #
224        # Define solution file
225        # As indicated above, contains (in XML) both the solution and solver status.
226        #
227        self.soln_file = pyutilib.services.TempfileManager.create_tempfile(suffix = '.cplex.sol')
228
229        #
230        # Define results file
231        #
232        if self._results_format is None or self._results_format == ResultsFormat.soln:
233            self.results_file = self.soln_file
234        elif self._results_format == ResultsFormat.sol:
235            self.results_file = self.sol_file
236
237        #
238        # Write the CPLEX execution script
239        #
240        script = "set logfile %s\n" % ( self.log_file, )
241        if self._timelimit is not None and self._timelimit > 0.0:
242            script += "set timelimit %s\n" % ( self._timelimit, )
243        if (self.options.mipgap is not None) and (self.options.mipgap > 0.0):
244            script += "set mip tolerances mipgap %s\n" % ( self.options.mipgap, )
245        for key in self.options:
246            if key == 'relax_integrality' or key == 'mipgap':
247                continue
248            elif isinstance(self.options[key],basestring) and ' ' in self.options[key]:
249                opt = " ".join(key.split('_'))+" "+str(self.options[key])
250            else:
251                opt = " ".join(key.split('_'))+" "+str(self.options[key])
252            script += "set %s\n" % ( opt, )
253        script += "read %s\n" % ( problem_files[0], )
254
255        # if we're dealing with an LP, the MST file will be empty.
256        if (self.warm_start_solve is True) and (self.warm_start_file_name is not None):
257            script += "read %s\n" % ( self.warm_start_file_name, )
258
259        if 'relax_integrality' in self.options:
260            script += "change problem lp\n"
261
262        script += "display problem stats\n"
263        script += "optimize\n"
264        script += "write %s\n" % ( self.soln_file, )
265        script += "quit\n"
266
267        # dump the script and warm-start file names for the
268        # user if we're keeping files around.
269        if self.keepFiles:
270            script_fname = pyutilib.services.TempfileManager.create_tempfile(suffix = '.cplex.script')
271            tmp = open(script_fname,'w')
272            tmp.write(script)
273            tmp.close()
274           
275            print "Solver script file=" + script_fname
276            if (self.warm_start_solve is True) and (self.warm_start_file_name is not None):
277                print "Solver warm-start file=" + self.warm_start_file_name
278
279        #
280        # Define command line
281        #
282        # JDS: I am not sure why we did this test... mostly because if
283        # it ever returned false, 'proc' was never initialized, resulting
284        # in an exception.
285        #if self._problem_format in [ProblemFormat.cpxlp, ProblemFormat.mps]:
286        #    proc = self._timer + " " + self.executable()
287        cmd = [self.executable()]
288        if self._timer:
289            cmd.insert(0, self._timer)
290        return pyutilib.misc.Bunch( cmd=cmd, script=script, 
291                                    log_file=self.log_file, env=None )
292
293    def process_logfile(self):
294        """
295        Process logfile
296        """
297        results = SolverResults()
298        results.problem.number_of_variables = None
299        results.problem.number_of_nonzeros = None
300        #
301        # Process logfile
302        #
303        OUTPUT = open(self.log_file)
304        output = "".join(OUTPUT.readlines())
305        OUTPUT.close()
306        #
307        # It is generally useful to know the CPLEX version number for logfile parsing.
308        #
309        cplex_version = None
310
311        #
312        # Parse logfile lines
313        #
314        for line in output.split("\n"):
315            tokens = re.split('[ \t]+',line.strip())
316            if len(tokens) > 3 and tokens[0] == "CPLEX" and tokens[1] == "Error":
317            # IMPT: See below - cplex can generate an error line and then terminate fine, e.g., in CPLEX 12.1.
318            #       To handle these cases, we should be specifying some kind of termination criterion always
319            #       in the course of parsing a log file (we aren't doing so currently - just in some conditions).
320                results.solver.status=SolverStatus.error
321                results.solver.error = " ".join(tokens)
322            elif len(tokens) >= 3 and tokens[0] == "ILOG" and tokens[1] == "CPLEX":
323                cplex_version = tokens[2].rstrip(',')
324            elif len(tokens) >= 3 and tokens[0] == "Variables":
325                if results.problem.number_of_variables is None: # CPLEX 11.2 and subsequent versions have two Variables sections in the log file output.
326                    results.problem.number_of_variables = int(tokens[2])
327            # In CPLEX 11 (and presumably before), there was only a single line output to
328            # indicate the constriant count, e.g., "Linear constraints : 16 [Less: 7, Greater: 6, Equal: 3]".
329            # In CPLEX 11.2 (or somewhere in between 11 and 11.2 - I haven't bothered to track it down
330            # in that detail), there is another instance of this line prefix in the min/max problem statistics
331            # block - which we don't care about. In this case, the line looks like: "Linear constraints :" and
332            # that's all.
333            elif len(tokens) >= 4 and tokens[0] == "Linear" and tokens[1] == "constraints":
334                results.problem.number_of_constraints = int(tokens[3])
335            elif len(tokens) >= 3 and tokens[0] == "Nonzeros":
336                if results.problem.number_of_nonzeros is None: # CPLEX 11.2 and subsequent has two Nonzeros sections.
337                    results.problem.number_of_nonzeros = int(tokens[2])
338            elif len(tokens) >= 5 and tokens[4] == "MINIMIZE":
339                results.problem.sense = ProblemSense.minimize
340            elif len(tokens) >= 5 and tokens[4] == "MAXIMIZE":
341                results.problem.sense = ProblemSense.maximize
342            elif len(tokens) >= 4 and tokens[0] == "Solution" and tokens[1] == "time" and tokens[2] == "=":
343                # technically, I'm not sure if this is CPLEX user time or user+system - CPLEX doesn't appear
344                # to differentiate, and I'm not sure we can always provide a break-down.
345                results.solver.user_time = float(tokens[3])
346            elif len(tokens) >= 4 and tokens[0] == "Dual" and tokens[1] == "simplex" and tokens[3] == "Optimal:":
347                results.solver.termination_condition = TerminationCondition.optimal
348                results.solver.termination_message = ' '.join(tokens)
349            elif len(tokens) >= 4 and tokens[0] == "Barrier" and tokens[2] == "Optimal:":
350                results.solver.termination_condition = TerminationCondition.optimal
351                results.solver.termination_message = ' '.join(tokens)
352            elif len(tokens) >= 4 and tokens[0] == "Dual" and tokens[3] == "Infeasible:":
353                results.solver.termination_condition = TerminationCondition.infeasible
354                results.solver.termination_message = ' '.join(tokens)               
355            elif len(tokens) >= 4 and tokens[0] == "MIP" and tokens[2] == "Integer" and tokens[3] == "infeasible.":
356                # if CPLEX has previously printed an error message, reduce it to a warning -
357                # there is a strong indication it recovered, but we can't be sure.
358                if results.solver.status == SolverStatus.error:
359                    results.solver.status = SolverStatus.warning
360                else:
361                    results.solver.status = SolverStatus.ok
362                results.solver.termination_condition = TerminationCondition.infeasible
363                results.solver.termination_message = ' '.join(tokens)
364            # for the case below, CPLEX sometimes reports "true" optimal (the first case)
365            # and other times within-tolerance optimal (the second case).
366            elif (len(tokens) >= 4 and tokens[0] == "MIP" and tokens[2] == "Integer" and tokens[3] == "optimal") or \
367                 (len(tokens) >= 4 and tokens[0] == "MIP" and tokens[2] == "Integer" and tokens[3] == "optimal,"):
368                # if CPLEX has previously printed an error message, reduce it to a warning -
369                # there is a strong indication it recovered, but we can't be sure.
370                if results.solver.status == SolverStatus.error:
371                    results.solver.status = SolverStatus.warning
372                else:
373                    results.solver.status = SolverStatus.ok
374                results.solver.termination_condition = TerminationCondition.optimal
375                results.solver.termination_message = ' '.join(tokens)
376            elif len(tokens) >= 3 and tokens[0] == "Presolve" and tokens[2] == "Infeasible.":
377                # if CPLEX has previously printed an error message, reduce it to a warning -
378                # there is a strong indication it recovered, but we can't be sure.
379                if results.solver.status == SolverStatus.error:
380                    results.solver.status = SolverStatus.warning
381                else:
382                    results.solver.status = SolverStatus.ok
383                results.solver.termination_condition = TerminationCondition.infeasible
384                results.solver.termination_message = ' '.join(tokens)
385            elif (len(tokens) == 6 and tokens[2] == "Integer" and tokens[3] == "infeasible" and tokens[5] == "unbounded.") or (len(tokens) >= 5 and tokens[0] == "Presolve" and tokens[2] == "Unbounded" and tokens[4] == "infeasible."):
386                # if CPLEX has previously printed an error message, reduce it to a warning -
387                # there is a strong indication it recovered, but we can't be sure.
388                if results.solver.status == SolverStatus.error:
389                    results.solver.status = SolverStatus.warning
390                else:
391                    results.solver.status = SolverStatus.ok
392                # It isn't clear whether we can determine if the problem is unbounded from
393                # CPLEX's output.
394                results.solver.termination_condition = TerminationCondition.unbounded
395                results.solver.termination_message = ' '.join(tokens)
396
397        try:
398            results.solver.termination_message = pyutilib.misc.yaml_fix(results.solver.termination_message)
399        except:
400            pass
401        return results
402
403    def process_soln_file(self,results):
404
405        # the only suffixes that we extract from CPLEX are
406        # constraint duals, constraint slacks, and variable
407        # reduced-costs. scan through the solver suffix list
408        # and throw an exception if the user has specified
409        # any others.
410        extract_duals = False
411        extract_slacks = False
412        extract_reduced_costs = False
413        for suffix in self.suffixes:
414            flag=False
415            if re.match(suffix,"dual"):
416                extract_duals = True
417                flag=True
418            if re.match(suffix,"slack"):
419                extract_slacks = True
420                flag=True
421            if re.match(suffix,"rc"):
422                extract_reduced_costs = True
423                flag=True
424            if not flag:
425                raise RuntimeError,"***The CPLEX solver plugin cannot extract solution suffix="+suffix
426
427        lp_solution = False
428        if not os.path.exists(self.soln_file):
429            return
430
431        soln = Solution()
432        soln.objective['__default_objective__'].value=None
433        soln_variable = soln.variable # caching for efficiency
434        INPUT = open(self.soln_file,"r")
435        results.problem.number_of_objectives=1
436        mip_problem=False
437        for line in INPUT:
438            line = line.strip()
439            line = line.lstrip('<?/')
440            line = line.rstrip('/>?')
441            tokens=line.split(' ')
442
443            if tokens[0] == "variable":
444                variable_name = None
445                variable_value = None
446                variable_reduced_cost = None
447                variable_status = None
448                for i in xrange(1,len(tokens)):
449                    field_name =  tokens[i].split('=')[0]
450                    field_value = tokens[i].split('=')[1].lstrip("\"").rstrip("\"")
451                    if field_name == "name":
452                        variable_name = field_value
453                    elif field_name == "value":
454                        variable_value = field_value
455                    elif (extract_reduced_costs is True) and (field_name == "reducedCost"):
456                        variable_reduced_cost = field_value
457                    elif (extract_reduced_costs is True) and (field_name == "status"):
458                        variable_status = field_value
459
460                # skip the "constant-one" variable, used to capture/retain objective offsets in the CPLEX LP format.
461                if variable_name != "ONE_VAR_CONSTANT":
462                    variable = soln_variable[variable_name] = {"Value" : float(variable_value), "Id" : len(soln_variable)}
463                    if (variable_reduced_cost is not None) and (extract_reduced_costs is True):
464                        try:
465                            variable["Rc"] = float(variable_reduced_cost)
466                            if variable_status is not None:
467                                if variable_status == "LL":
468                                    variable["Lrc"] = float(variable_reduced_cost)
469                                else:
470                                    variable["Lrc"] = 0.0
471                                if variable_status == "UL":
472                                    variable["Urc"] = float(variable_reduced_cost)
473                                else:
474                                    variable["Urc"] = 0.0
475                        except:
476                            raise ValueError, "Unexpected reduced-cost value="+str(variable_reduced_cost)+" encountered for variable="+variable_name
477            elif (tokens[0] == "constraint") and ((extract_duals is True) or (extract_slacks is True)):
478                constraint_name = None
479                constraint_dual = None
480                constaint = None # cache the solution constraint reference, as the getattr is expensive.
481                for i in xrange(1,len(tokens)):
482                    field_name =  tokens[i].split('=')[0]
483                    field_value = tokens[i].split('=')[1].lstrip("\"").rstrip("\"")
484                    if field_name == "name":
485                        constraint_name = field_value
486                        constraint = soln.constraint[constraint_name]
487                    elif (extract_duals is True) and (field_name == "dual"): # for LPs
488                        constraint.dual = float(field_value)
489                    elif (extract_slacks is True) and (field_name == "slack"): # for MIPs
490                        constraint.slack = float(field_value)
491            elif tokens[0].startswith("problemName"):
492                filename = (string.strip(tokens[0].split('=')[1])).lstrip("\"").rstrip("\"")
493                #print "HERE",filename
494                results.problem.name = os.path.basename(filename)
495                if '.' in results.problem.name:
496                    results.problem.name = results.problem.name.split('.')[0]
497                tINPUT=open(filename,"r")
498                for tline in tINPUT:
499                    tline = tline.strip()
500                    if tline == "":
501                        continue
502                    tokens = re.split('[\t ]+',tline)
503                    if tokens[0][0] in ['\\', '*']:
504                        continue
505                    elif tokens[0] == "NAME":
506                        results.problem.name = tokens[1]
507                    else:
508                        sense = tokens[0].lower()
509                        if sense in ['max','maximize']:
510                            results.problem.sense = ProblemSense.maximize
511                        if sense in ['min','minimize']:
512                            results.problem.sense = ProblemSense.minimize
513                    break
514                tINPUT.close()
515
516            elif tokens[0].startswith("objectiveValue"):
517                objective_value = (string.strip(tokens[0].split('=')[1])).lstrip("\"").rstrip("\"")
518                soln.objective['__default_objective__'].value = float(objective_value)
519            elif tokens[0].startswith("solutionStatusValue"):
520               pieces = string.split(tokens[0],"=")
521               solution_status = eval(pieces[1])
522               # solution status = 1 => optimal
523               # solution status = 3 => infeasible
524               if soln.status == SolutionStatus.unknown:
525                  if solution_status == 1:
526                    soln.status = SolutionStatus.optimal
527                  elif solution_status == 3:
528                    soln.status = SolutionStatus.infeasible
529                    soln.gap = None                 
530                  elif solution_status >= 4: # we are flagging these as "error".
531                    soln.status = SolutionStatus.error
532                    soln.gap = None
533            elif tokens[0].startswith("solutionStatusString"):
534                solution_status = (string.strip(" ".join(tokens).split('=')[1])).lstrip("\"").rstrip("\"")
535                if solution_status in ["optimal", "integer optimal solution", "integer optimal, tolerance"]:
536                    soln.status = SolutionStatus.optimal
537                    soln.gap = 0.0
538                    results.problem.lower_bound = soln.objective['__default_objective__'].value
539                    results.problem.upper_bound = soln.objective['__default_objective__'].value
540                    mip_problem=True
541                elif solution_status in ["infeasible"]:
542                    soln.status = SolutionStatus.infeasible
543                    soln.gap = None
544            elif tokens[0].startswith("MIPNodes"):
545                if mip_problem:
546                    n = eval(eval(string.strip(" ".join(tokens).split('=')[1])).lstrip("\"").rstrip("\""))
547                    results.solver.statistics.branch_and_bound.number_of_created_subproblems=n
548                    results.solver.statistics.branch_and_bound.number_of_bounded_subproblems=n
549
550
551        if not results.solver.status is SolverStatus.error and \
552            results.solver.termination_condition in [TerminationCondition.unknown,
553                        #TerminationCondition.maxIterations,
554                        #TerminationCondition.minFunctionValue,
555                        #TerminationCondition.minStepLength,
556                        TerminationCondition.globallyOptimal,
557                        TerminationCondition.locallyOptimal,
558                        TerminationCondition.optimal,
559                        #TerminationCondition.maxEvaluations,
560                        TerminationCondition.other]:
561                results.solution.insert(soln)
562        INPUT.close()
563
564    def _postsolve(self):
565
566        # take care of the annoying (and empty) CPLEX temporary files in the current directory.
567        # this approach doesn't seem overly efficient, but python os module functions don't
568        # accept regular expression directly.
569        filename_list = os.listdir(".")
570        for filename in filename_list:
571            # CPLEX temporary files come in two flavors - cplex.log and clone*.log.
572            # the latter is the case for multi-processor environments.
573            # IMPT: trap the possible exception raised by the file not existing.
574            #       this can occur in pyro environments where > 1 workers are
575            #       running CPLEX, and were started from the same directory.
576            #       these logs don't matter anyway (we redirect everything),
577            #       and are largely an annoyance.
578            try:
579                if  re.match('cplex\.log', filename) != None:
580                    os.remove(filename)
581                elif re.match('clone\d+\.log', filename) != None:
582                    os.remove(filename)
583            except OSError:
584                pass
585
586        # let the base class deal with returning results.
587        return ILMLicensedSystemCallSolver._postsolve(self)
588
589
590class MockCPLEX(CPLEXSHELL,mockmip.MockMIP):
591    """A Mock CPLEX solver used for testing
592    """
593
594    pyutilib.component.core.alias('_mock_cplex')
595
596    def __init__(self, **kwds):
597        try:
598            CPLEXSHELL.__init__(self, **kwds)
599        except pyutilib.common.ApplicationError: #pragma:nocover
600            pass                        #pragma:nocover
601        mockmip.MockMIP.__init__(self,"cplex")
602
603    def available(self, exception_flag=True):
604        return CPLEXSHELL.available(self,exception_flag)
605
606    def create_command_line(self,executable,problem_files):
607        command = CPLEXSHELL.create_command_line(self,executable,problem_files)
608        mockmip.MockMIP.create_command_line(self,executable,problem_files)
609        return command
610
611    def executable(self):
612        return mockmip.MockMIP.executable(self)
613
614    def _execute_command(self,cmd):
615        return mockmip.MockMIP._execute_command(self,cmd)
616
617
618pyutilib.services.register_executable(name="cplex")
619pyutilib.services.register_executable(name="cplexamp")
620
Note: See TracBrowser for help on using the repository browser.