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

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

Changes to clarify what solver outputs entail. Now, logfiles
are processed whenever available, and other data is processed with
an appropriate reader. The exception is 'soln' files, which
generically refers to solver-specific solution results. These are
parsed with a solver method.

File size: 7.7 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__all__ = ['ISolverRegistration', 'SolverRegistration',
12            'IOptSolver', 'OptSolver', 'SolverFactory']
13
14import os
15import sys
16from convert import convert_problem
17from formats import ResultsFormat, ProblemFormat
18import results
19from coopr.opt.results import SolverResults, SolverStatus
20
21from pyutilib.enum import Enum
22from pyutilib.plugin.core import *
23from pyutilib.plugin.config import *
24import pyutilib.common
25import pyutilib.misc
26
27
28class ISolverRegistration(Interface):
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
38class SolverRegistration(Plugin):
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
50    def create(self, **kwds):
51        return self._cls(**kwds)
52
53
54def SolverFactory(solver_name=None, **kwds):
55    ep = ExtensionPoint(ISolverRegistration)
56    if solver_name is None:
57        names = map(lambda x:x.name, ep())
58        names.sort()
59        return names
60    service = ep.service(solver_name)
61    if service is None:
62        ##print "Unknown solver=" + solver_name + " - no plugin registered for this solver type"
63        return None
64    else:
65        return ep.service(solver_name).create(**kwds)
66
67
68class IOptSolver(Interface):
69    """Interface class for creating optimization solvers"""
70
71    def available(self, exception_flag=True):
72        """Determine if this optimizer is available."""
73
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
84class OptSolver(ManagedPlugin):
85    """A generic optimization solver"""
86
87    implements(IOptSolver)
88
89    def __init__(self, **kwds):
90        """ Constructor """
91        ManagedPlugin.__init__(self,**kwds)
92        #
93        # The 'type' is the class type of the solver instance
94        #
95        if "type" in kwds:
96            self.type = kwds["type"]
97        else:                           #pragma:nocover
98            raise PluginError, "Expected option 'type' for OptSolver constructor"
99        #
100        # The 'name' is either the class type of the solver instance, or a
101        # assigned name.
102        #
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:
110            if self.type is None:           # pragma:nocover
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)
116        declare_option("options", cls=DictOption, section=self.name, doc=self._doc)
117        if 'options' in kwds:
118            for key in kwds['options']:
119                setattr(self.options,key,kwds['options'][key])
120        self._symbol_map=None
121        self._problem_format=None
122        self._results_format=None
123        self._valid_problem_formats=[]
124        self._valid_result_formats={}
125        self.results_reader=None
126        self.problem=None
127        self._assert_available=False
128
129    def available(self, exception_flag=True):
130        """ True if the solver is available """
131        if self._assert_available:
132            return True
133        tmp = self.enabled()
134        if exception_flag and not tmp:
135            raise pyutilib.common.ApplicationError, "OptSolver plugin %s is disabled" % self.name
136        return tmp
137
138    def solve(self, *args, **kwds):
139        """ Solve the problem """
140        self._presolve(*args, **kwds)
141        self._apply_solver()
142        result = self._postsolve()
143        result.symbol_map = self._symbol_map
144        return result
145
146    def _presolve(self, *args, **kwds):
147        self.available()
148        self._timelimit=None
149        self.tee=None
150        for key in kwds:
151          if key == "pformat":
152             self._problem_format=kwds[key]
153          elif key == "rformat":
154             self._results_format=kwds[key]
155          elif key == "logfile":
156             self.log_file=kwds[key]
157          elif key == "solnfile":
158             self.soln_file=kwds[key]
159          elif key == "timelimit":
160             self._timelimit=kwds[key]
161          elif key == "tee":
162             self.tee=kwds[key]
163          elif key == "options":
164             self.set_options(kwds[key])
165          elif key == "available":
166             self._assert_available=True
167          else:
168             raise ValueError, "Unknown option="+key+" for solver="+self.type
169
170        (self._problem_files,self._problem_format,self._symbol_map) = self._convert_problem(args, self._problem_format, self._valid_problem_formats)
171        if self._results_format is None:
172           self._results_format= self._default_results_format(self._problem_format)
173        #
174        # Disabling this check for now.  A solver doesn't have just _one_ results format.
175        #
176        #if self._results_format not in self._valid_result_formats[self._problem_format]:
177        #   raise ValueError, "Results format `"+str(self._results_format)+"' cannot be used with problem format `"+str(self._problem_format)+"' in solver "+self.name
178        if self._results_format == ResultsFormat.soln:
179            self.results_reader = None
180        else:
181            self.results_reader = results.ReaderFactory(self._results_format)
182
183    def _apply_solver(self):
184        """The routine that performs the solve"""
185        raise NotImplementedError       #pragma:nocover
186       
187    def _postsolve(self):
188        """The routine that does solve post-processing"""
189        return self.results
190       
191    def _convert_problem(self, args, pformat, valid_pformats):
192        #
193        # If the problem is not None, then we assume that it has already
194        # been appropriately defined.  Either it's a string name of the
195        # problem we want to solve, or its a functor object that we can
196        # evaluate directly.
197        #
198        if self.problem is not None:
199           return (self.problem,ProblemFormat.colin_optproblem,None)
200        #
201        # Otherwise, we try to convert the object explicitly.
202        #
203        return convert_problem(args, pformat, valid_pformats)
204
205    def _default_results_format(self, prob_format):
206        """Returns the default results format for different problem
207            formats.
208        """
209        return ResultsFormat.results
210
211    def reset(self):
212        """
213        Reset the state of the solver
214        """
215        pass
216
217    def set_options(self, istr):
218        istr = istr.strip()
219        if istr is '':
220            return
221        tokens = pyutilib.misc.quote_split('[ ]+',istr)
222        for token in tokens:
223            index = token.find('=')
224            if index is -1:
225                raise ValueError, "Solver options must have the form option=value"
226            try:
227                val = eval(token[(index+1):])
228            except:
229                val = token[(index+1):]
230            setattr(self.options, token[:index], val)
231           
232           
233   
Note: See TracBrowser for help on using the repository browser.