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

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

Updates to allow for proper handling of variable maps in cases where formats cannot handle fully qualified,
human-readable variable names. Many ripple effects, as problem writers now must return a variable map (or None,
if not applicable. CBC now properly handles and reads NL and SOL input/output combinations!

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._variable_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.variable_map = self._variable_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._variable_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)
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.