source: trunk/coopr/opt/base/solution.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: 10.2 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__ = ['SolverResults', 'SolverStatus', 'ProblemSense', 'SolutionStatus', 'Solution']
12
13from pyutilib.misc import format_io
14from pyutilib.math import as_number
15from pyutilib.enum import Enum
16
17import sys
18import StringIO
19
20SolverStatus = Enum('error', 'warning', 'ok', 'aborted')
21ProblemSense = Enum('unknown', 'minimize', 'maximize')
22SolutionStatus = Enum('unbounded', 'globallyOptimal', 'locallyOptimal',
23                            'optimal', 'bestSoFar', 'feasible', 'infeasible',
24                            'stoppedByLimit', 'unsure', 'error', 'other')
25
26
27class _SolutionSuffix(object):
28
29    def __init__(self, description):
30        self._info = []
31        self._index = {}
32        self._count = {}
33        self.description = description
34
35    def add(self, index, value):
36        """ NOTE: this function is deprecated """
37        self.__setitem__(index, value)
38
39    def __getitem__(self, key):
40        if isinstance(key,basestring):
41            return self._index[key]
42        return self._count[key]
43
44    def __setitem__(self, index, value):
45        if isinstance(index, basestring):
46            index = index.replace('(','[')
47            index = index.replace(')',']')
48        if type(value) is str:
49           tmp = eval(value)
50        else:
51           tmp = value
52        self._count[len(self._info)]=tmp
53        self._info.append( (index, tmp) )
54        self._index[index]=tmp
55        #
56        # If the index has the format x(1,a) or x[3,4,5]
57        # then create a dictionary attribute 'x', which maps
58        # the tuple values to the corresponding value.
59        #
60        if isinstance(index, basestring):
61            if '[' in index:
62                pieces = index.split('[')
63                name = pieces[0]
64                rest = None
65                # when a variable is indexed by more than one parameter, you will
66                # see identifiers of the form "x((a,b))" instead of the "x(a)"
67                # one-dimensional form. this is due to the process of "stringifying"
68                # the index, which is fine. it just requires a bit of ugliness in
69                # the string splitting process.
70                if index.count("]") == 2:
71                   rest = pieces[2]
72                else:
73                   rest = pieces[1]
74                # we're always taking the first part of the index,
75                # so even in the two (or greater) dimensional case
76                # such as "x((a,b))", the first piece is what we want.
77                tpl = rest.split(']')[0]
78                tokens = tpl.split(',')
79                for i in xrange(len(tokens)):
80                    tokens[i] = as_number(tokens[i])
81                try:
82                    var = getattr(self, name)
83                except Exception, err:
84                    setattr(self, name, {})
85                    var = getattr(self, name)
86                if len(tokens) == 1:
87                    var[ tokens[0] ] = tmp
88                else:
89                    var[ tuple(tokens) ] = tmp
90            else:
91                setattr(self, index, tmp)
92           
93           
94
95    def __len__(self):
96        return len(self._info)
97
98    def __iter__(self):
99        return self._info.__iter__()
100
101    def pprint(self, ostream=None):
102        if ostream is None:
103           ostream = sys.stdout
104        if len(self._info) != 0:
105           print >>ostream, "  "+self.description
106           flag=True
107           for (name,value) in self._info:
108             if value != 0:
109                print >>ostream, "        "+str(name)+"\t"+format_io(value)
110                flag=False
111           if flag:
112              print >>ostream,   "        No nonzero values"
113
114
115class Solution(object):
116
117    def __init__(self):
118        self.__dict__["_suffix"] = []
119        self.value = None
120        self.gap = None
121        self.status = SolutionStatus.unsure
122        self.add_suffix("variable","Primal Variables")
123        self.add_suffix("dual","Dual Variables")
124        self.add_suffix("slack","Constraint Slacks")
125        self.add_suffix("constraint","Constraint Values")
126
127    def __getattr__(self, name):
128        try:
129            if name in self.__dict__['_suffix']:
130                return self.__dict__["_"+name]
131        except KeyError:
132            pass
133        try:
134           return self.__dict__[name]
135        except KeyError:
136           raise AttributeError, "Unknown attribute "+str(name)
137
138    def __setattr__(self, name, val):
139        if name in self._suffix:
140           raise AttributeError, "Cannot set attribute '"+name+"' which is reserved for suffix array"
141        self.__dict__[name]=val
142
143    def add_suffix(self,name,description):
144        self._suffix.append(name)
145        setattr(self, "_"+name, _SolutionSuffix(description))
146
147    def pprint(self, ostream=None, only_variables=False):
148        if ostream is None:
149           ostream = sys.stdout
150        print >>ostream, "----------------------------------------------------------"
151        print >>ostream, "------  Solution "+ str(self._counter+1)
152        print >>ostream, "----------------------------------------------------------"
153        tmp = self.__dict__.keys()
154        tmp.sort()
155        for key in tmp:
156          if key[0] != "_":
157             if key != "value":
158                print >>ostream, " ",key+":", format_io(getattr(self, key))
159             else:
160                tmp = getattr(self,key)
161                if isinstance(tmp, dict):
162                   print >>ostream, "  Objectives"
163                   for vkey in tmp:
164                     print >>ostream, "        "+str(vkey)+"\t"+format_io(tmp[vkey])
165                else:
166                   print >>ostream, "  value:",format_io(tmp)
167                   
168        for item in self._suffix:
169            if only_variables and item != "variable":
170                continue
171            getattr(self,"_"+item).pprint(ostream=ostream)
172
173
174class SolutionSet(object):
175
176    def __init__(self):
177        self._solution={}
178        self._counter=0
179        self._ndx=[]
180
181    def __len__(self):
182        return len(self._solution)
183
184    def __getitem__(self,i):
185        return self._solution[i]
186
187    def __call__(self,i=0):
188        return self._solution[self._ndx[i]]
189
190    def create(self):
191        tmp = Solution()
192        self.insert(tmp)
193        return tmp
194
195    def insert(self,solution):
196        solution._counter = self._counter
197        self._solution[self._counter] = solution
198        self._ndx.append(self._counter)
199        self._counter += 1
200
201    def delete(self,i):
202        del self._solution[i]
203        self._ndx.remove(i)
204
205
206class _Data_Container(object):
207    pass
208
209
210class SolverResults(object):
211
212    def __init__(self):
213        self.solver = _Data_Container()
214        self.problem = _Data_Container()
215        self.solution = SolutionSet()
216        self.initialize()
217        self.variable_map = None 
218
219    def initialize(self):
220        #
221        # Standard solver information
222        #
223        self.solver.status=SolverStatus.ok
224        self.solver.systime=None
225        self.solver.usrtime=None
226        #
227        # Standard problem information
228        #
229        self.problem.name=None
230        self.problem.sense=ProblemSense.unknown
231        self.problem.num_variables=None
232        self.problem.num_constraints=None
233        self.problem.num_objectives=None
234        self.problem.lower_bound=None
235        self.problem.upper_bound=None
236
237    def __str__(self):
238        output = StringIO.StringIO()
239        self.write(ostream=output, num=sys.maxint)
240        return output.getvalue()
241       
242    def write(self, filename=None, num=1, ostream=None, times=True, only_variables=False):
243        if ostream is None:
244           ostream = sys.stdout
245        if not filename is None:
246           OUTPUT=open(filename,"w")
247           self.write(num=num,ostream=OUTPUT,times=times)
248           OUTPUT.close()
249           return
250
251        print >>ostream, "=========================================================="
252        print >>ostream, "---  Solver Results                                    ---"
253        print >>ostream, "=========================================================="
254        print >>ostream, "----------------------------------------------------------"
255        print >>ostream, "------  Problem Information                         ------"
256        print >>ostream, "----------------------------------------------------------"
257        flag=False
258        tmp = self.problem.__dict__.keys()
259        tmp.sort()
260        for key in tmp:
261          if key[0] != "_":
262             print >>ostream, " ",key+":", format_io(getattr(self.problem, key))
263             flag=True
264        if not flag:        # pragma:nocover
265           print >>ostream, "  No Info"
266        if self.variable_map is not None:
267           print >>ostream, "  Variable map is available"
268        #
269        print >>ostream, "----------------------------------------------------------"
270        print >>ostream, "------  Solver Information                          ------"
271        print >>ostream, "----------------------------------------------------------"
272        flag=False
273        tmp = self.solver.__dict__.keys()
274        tmp.sort()
275        for key in tmp:
276          if key[0] != "_":
277             if times or not key in ["systime","usrtime"]:
278                print >>ostream, " ",key+":", format_io(getattr(self.solver, key))
279             flag=True
280        if not flag:        # pragma:nocover
281           print >>ostream, "  No Info"
282        #
283        if not num is None:
284           print >>ostream, "  num_solutions: "+str(len(self.solution))
285           i=0
286           while i<min(num, len(self.solution)):
287             self.solution(i).pprint(ostream, only_variables=only_variables)
288             i += 1
289        else:
290           print >>ostream, "  num_solutions to display: 0"
291        print >>ostream, "----------------------------------------------------------"
292
293    def read(self, filename=None):          #pragma:nocover
294        raise IOError, "SolverResults.read is not defined!"
295
Note: See TracBrowser for help on using the repository browser.