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

Last change on this file since 1983 was 1983, checked in by jwatson, 10 years ago

Added "warm_start_capable" query method to OptSolver? base class. Default is to return False.

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