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

Last change on this file since 1740 was 1740, checked in by jwatson, 11 years ago

Improved option diagnostic for situations when solvers are passed in options they don't know about.

Improved output readability in WW PH extension.

Provided option to disable warm-start in PH, as (1) some solvers are buggy and (2) we were providing the keyword in iteration 0 where it is not needed in any case.

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