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

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

Misc bug fixes that were introduced by the introduction of variable_map
data, which is now called symbol_map.

Note: some tests still fail, due to the fact that pico_convert does not
generate symbol mapping information. This is being resolved.

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