source: coopr.opt/stable/2.3/coopr/opt/base/solver.py @ 2313

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

Merged revisions 2234-2312 via svnmerge from
https://software.sandia.gov/svn/public/coopr/coopr.opt/trunk

........

r2234 | wehart | 2010-02-01 19:34:02 -0700 (Mon, 01 Feb 2010) | 2 lines


Updating changelog.

........

r2236 | wehart | 2010-02-01 19:38:27 -0700 (Mon, 01 Feb 2010) | 2 lines


Tagging coopr.opt 2.3 release

........

r2250 | wehart | 2010-02-03 13:42:06 -0700 (Wed, 03 Feb 2010) | 3 lines


Update to descriptions of solver status and associted termination
conditions.

........

r2251 | wehart | 2010-02-03 13:56:43 -0700 (Wed, 03 Feb 2010) | 3 lines


Changes due to refinement of termination condition management
Bug fix and update to baseline data.

........

r2257 | wehart | 2010-02-03 22:45:40 -0700 (Wed, 03 Feb 2010) | 6 lines


Augmenting the SolverResults? logic to support default values. This
capability is currently disabled, but I'm leaving it in in case we
want to use this in the future.


Misc fix to the activation logic.

........

r2258 | wehart | 2010-02-03 23:33:12 -0700 (Wed, 03 Feb 2010) | 4 lines


Adding a subpackage for optimization-specific testing tools.
Currently, this contains a customized PyUnit? testing class
that contains new testing functions.

........

r2259 | wehart | 2010-02-04 08:58:44 -0700 (Thu, 04 Feb 2010) | 4 lines


Adding new termination conditions.


Fixing problem setting up testing utilities when YAML is not installed.

........

r2263 | wehart | 2010-02-05 08:18:57 -0700 (Fri, 05 Feb 2010) | 6 lines


Misc rework of the converter utility to recognize that *.py files have
Pyomo models.


NOTE: If we start working with PuLP, then we might have an issue
distinguishing PuLP vs Pyomo Python files.

........

r2280 | jwatson | 2010-02-08 11:26:49 -0700 (Mon, 08 Feb 2010) | 1 line


Adding a suffixes attribute to the base solver class. This is a list of suffixes that the solver will - assuming it can - load into a solution. Examples are rc, dual, slack, etc. I modified the derived solver plugin classes to throw an exception if provided a suffix that it can't handle. I have little doubt that this change will cause some tests to fail - I'll look at this in a bit. The change was motivated by the expense of querying a solution, loading a solution, and shipping a lot of unnecessary information across the network.

........

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