source: coopr.opt/trunk/coopr/opt/base/solver.py @ 2259

Last change on this file since 2259 was 2201, checked in by wehart, 10 years ago

Update to Coopr to account for changes in PyUtilib? package names.

File size: 8.9 KB
RevLine 
[1010]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.
[1217]8#  For more information, see the Coopr README.txt file.
[1010]9#  _________________________________________________________________________
10
[1161]11__all__ = ['ISolverRegistration', 'SolverRegistration',
[1459]12            'IOptSolver', 'OptSolver', 'SolverFactory']
[1161]13
[100]14import os
15import sys
[862]16from convert import convert_problem
[914]17from formats import ResultsFormat, ProblemFormat
[1161]18import results
[1900]19from coopr.opt.results import SolverResults, SolverStatus
[1772]20
[1768]21from pyutilib.enum import Enum
[2201]22from pyutilib.component.core import *
23from pyutilib.component.config import *
[1768]24import pyutilib.common
25import pyutilib.misc
[100]26
[2167]27import time
[1161]28
[2167]29
[1162]30class ISolverRegistration(Interface):
[1161]31    """An interface for accessing"""
32
33    def create(self, name=None):
34        """Create a solver, optionally specifying the name"""
35
36    def type(self):
37        """The type of solver supported by this service"""
38
39
[1162]40class SolverRegistration(Plugin):
[1161]41
42    implements(ISolverRegistration)
43
44    def __init__(self, type, cls):
45        self.name = type
46        self._type = type
47        self._cls = cls
48
49    def type(self):
50        return self._type
51
[1244]52    def create(self, **kwds):
53        return self._cls(**kwds)
[1161]54
55
[1244]56def SolverFactory(solver_name=None, **kwds):
[1161]57    ep = ExtensionPoint(ISolverRegistration)
[1244]58    if solver_name is None:
[1897]59        names = map(lambda x:x.name, ep())
60        names.sort()
61        return names
[1244]62    service = ep.service(solver_name)
[1200]63    if service is None:
[1484]64        ##print "Unknown solver=" + solver_name + " - no plugin registered for this solver type"
[1244]65        return None
[1200]66    else:
[1244]67        return ep.service(solver_name).create(**kwds)
[1161]68
69
[1162]70class IOptSolver(Interface):
[1161]71    """Interface class for creating optimization solvers"""
72
[1679]73    def available(self, exception_flag=True):
74        """Determine if this optimizer is available."""
[1161]75
[1679]76    def solve(self, *args, **kwds):
77        """Perform optimization and return an SolverResults object."""
78
79    def reset(self):
80        """Reset the state of an optimizer"""
81
82    def set_options(self, istr):
83        """Set the options in the optimizer from a string."""
84
85
[1165]86class OptSolver(ManagedPlugin):
[100]87    """A generic optimization solver"""
88
[1162]89    implements(IOptSolver)
[1161]90
91    def __init__(self, **kwds):
[862]92        """ Constructor """
[1165]93        ManagedPlugin.__init__(self,**kwds)
[1244]94        #
95        # The 'type' is the class type of the solver instance
96        #
[1161]97        if "type" in kwds:
98            self.type = kwds["type"]
[1244]99        else:                           #pragma:nocover
[1161]100            raise PluginError, "Expected option 'type' for OptSolver constructor"
[1244]101        #
102        # The 'name' is either the class type of the solver instance, or a
103        # assigned name.
104        #
[1161]105        if "name" in kwds:
106            self.name = kwds["name"]
107        else:
108            self.name = self.type
109        if "doc" in kwds:
110            self._doc = kwds["doc"]
111        else:
[1244]112            if self.type is None:           # pragma:nocover
[1161]113                self._doc = ""
114            elif self.name == self.type:
115                self._doc = "%s OptSolver" % self.name
116            else:
117                self._doc = "%s OptSolver (type %s)" % (self.name,self.type)
[1248]118        declare_option("options", cls=DictOption, section=self.name, doc=self._doc)
[1167]119        if 'options' in kwds:
120            for key in kwds['options']:
121                setattr(self.options,key,kwds['options'][key])
[1794]122        self._symbol_map=None
[100]123        self._problem_format=None
124        self._results_format=None
[862]125        self._valid_problem_formats=[]
[873]126        self._valid_result_formats={}
[100]127        self.results_reader=None
[913]128        self.problem=None
[1459]129        self._assert_available=False
[2167]130        self._report_timing = False # timing statistics are always collected, but optionally reported.
[100]131
[2033]132        # a fairly universal solver feature, at least when dealing
133        # with problems containing integers. promoted to a base class
134        # attribute to allow each sub-solver to automatically write
135        # out the appropriate option. default is None, meaning
136        # unassigned. currently not allowing the mipgap to be over-ridden
137        # via an argument to the solve() method, mainly because we don't
138        # want to persistence of this parameter to be violated.
139        self.mipgap = None
140
[100]141    def available(self, exception_flag=True):
[862]142        """ True if the solver is available """
[1459]143        if self._assert_available:
144            return True
[1244]145        tmp = self.enabled()
146        if exception_flag and not tmp:
[1768]147            raise pyutilib.common.ApplicationError, "OptSolver plugin %s is disabled" % self.name
[1244]148        return tmp
[100]149
[1983]150    def warm_start_capable(self):
151       """ True is the solver can accept a warm-start solution """
152       return False
153
[862]154    def solve(self, *args, **kwds):
155        """ Solve the problem """
[2167]156        initial_time = time.time()
[1459]157        self._presolve(*args, **kwds)
[2167]158        presolve_completion_time = time.time()
[1459]159        self._apply_solver()
[2167]160        solve_completion_time = time.time()
[1772]161        result = self._postsolve()
[2167]162        postsolve_completion_time = time.time()
[1794]163        result.symbol_map = self._symbol_map
[2167]164        if self._report_timing is True:
165           print "Presolve time="+str(presolve_completion_time-initial_time)
166           print "Solve time="+str(solve_completion_time - presolve_completion_time)
167           print "Postsolve time="+str(postsolve_completion_time-solve_completion_time)
[1772]168        return result
[1459]169
170    def _presolve(self, *args, **kwds):
[880]171        self.available()
[1459]172        self._timelimit=None
[1597]173        self.tee=None
[862]174        for key in kwds:
175          if key == "pformat":
176             self._problem_format=kwds[key]
177          elif key == "rformat":
178             self._results_format=kwds[key]
[890]179          elif key == "logfile":
180             self.log_file=kwds[key]
181          elif key == "solnfile":
182             self.soln_file=kwds[key]
183          elif key == "timelimit":
[1459]184             self._timelimit=kwds[key]
[1594]185          elif key == "tee":
186             self.tee=kwds[key]
[1429]187          elif key == "options":
188             self.set_options(kwds[key])
[1459]189          elif key == "available":
190             self._assert_available=True
[862]191          else:
[1740]192             raise ValueError, "Unknown option="+key+" for solver="+self.type
[911]193
[1794]194        (self._problem_files,self._problem_format,self._symbol_map) = self._convert_problem(args, self._problem_format, self._valid_problem_formats)
[872]195        if self._results_format is None:
196           self._results_format= self._default_results_format(self._problem_format)
[1918]197        #
198        # Disabling this check for now.  A solver doesn't have just _one_ results format.
199        #
200        #if self._results_format not in self._valid_result_formats[self._problem_format]:
201        #   raise ValueError, "Results format `"+str(self._results_format)+"' cannot be used with problem format `"+str(self._problem_format)+"' in solver "+self.name
[1938]202        if self._results_format == ResultsFormat.soln:
[1167]203            self.results_reader = None
204        else:
205            self.results_reader = results.ReaderFactory(self._results_format)
[100]206
[1459]207    def _apply_solver(self):
208        """The routine that performs the solve"""
[884]209        raise NotImplementedError       #pragma:nocover
[1459]210       
211    def _postsolve(self):
212        """The routine that does solve post-processing"""
213        return self.results
214       
[890]215    def _convert_problem(self, args, pformat, valid_pformats):
[913]216        #
217        # If the problem is not None, then we assume that it has already
218        # been appropriately defined.  Either it's a string name of the
219        # problem we want to solve, or its a functor object that we can
220        # evaluate directly.
221        #
222        if self.problem is not None:
[1794]223           return (self.problem,ProblemFormat.colin_optproblem,None)
[913]224        #
[914]225        # Otherwise, we try to convert the object explicitly.
[913]226        #
[890]227        return convert_problem(args, pformat, valid_pformats)
228
[872]229    def _default_results_format(self, prob_format):
[1459]230        """Returns the default results format for different problem
[872]231            formats.
232        """
[913]233        return ResultsFormat.results
[100]234
[914]235    def reset(self):
236        """
237        Reset the state of the solver
238        """
239        pass
[100]240
[1679]241    def set_options(self, istr):
242        istr = istr.strip()
243        if istr is '':
[1429]244            return
[1768]245        tokens = pyutilib.misc.quote_split('[ ]+',istr)
[1429]246        for token in tokens:
247            index = token.find('=')
248            if index is -1:
249                raise ValueError, "Solver options must have the form option=value"
250            try:
251                val = eval(token[(index+1):])
252            except:
253                val = token[(index+1):]
254            setattr(self.options, token[:index], val)
255           
256           
257   
Note: See TracBrowser for help on using the repository browser.