source: trunk/coopr/opt/reader/OS.py @ 1768

Last change on this file since 1768 was 1768, checked in by wehart, 11 years ago

Rework of Coopr to use the new PyUtilib? package decomposition.

NOTE: to use Coopr with this update, we need to work with a new version of coopr_install.

File size: 15.6 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#
12# Utility classes for dealing with the COIN-OR optimization
13# services
14#
15
16import os
17import sys
18from xml.dom import minidom, Node
19import xml
20from pyutilib.enum import Enum
21from pyutilib.misc import get_xml_text
22from coopr.opt.base import SolutionStatus
23
24#
25# A class for reading/writing OSrL file
26#
27class OSrL(object):
28
29    GeneralStatus = Enum('error', 'warning', 'success')
30    SolutionStatus = Enum('unbounded', 'globallyOptimal', 'locallyOptimal', 
31                        'optimal', 'bestSoFar', 'feasible', 'infeasible', 
32                        'stoppedByLimit', 'unsure', 'error', 'other')
33
34    class ResultHeader(object):
35        def __init__(self):
36            self.generalStatus=OSrL.GeneralStatus.error
37            self.serviceURI=None
38            self.serviceName=None
39            self.instanceName=None
40            self.time=None
41            self.message=None
42            self.jobID=None
43
44        def _parse_xml(self, node):
45            for child in node.childNodes:
46              if child.nodeType == Node.ELEMENT_NODE:
47                 if child.nodeName in ["generalStatus","serviceURI","serviceName","instanceName","time","message", "jobID"]:
48                    setattr(self,child.nodeName, get_xml_text(child))
49
50        def validate(self):
51            if self.generalStatus not in OSrL.GeneralStatus:
52               raise ValueError, "General status value '"+str(self.generalStatus)+"' is not in OSrL.GeneralStatus"
53
54        #def __repr__(self):
55            #return "OSrL ResultHeader"
56           
57        #__str__ = __repr__
58
59        def write(self,ostream=None, prefix=""):
60            if ostream is None:
61               ostream = sys.stdout
62            self.validate()
63            os.write(ostream.fileno(), prefix)
64            os.write(ostream.fileno(), "<ResultHeader>\n")
65            #
66            os.write(ostream.fileno(), prefix+"  <generalStatus>")
67            os.write(ostream.fileno(), str(self.generalStatus))
68            os.write(ostream.fileno(), "</generalStatus>\n")
69            #
70            if self.serviceURI is not None:
71               os.write(ostream.fileno(), prefix+"  <serviceURI>"+str(self.serviceURI)+"</serviceURI>\n")
72            #
73            os.write(ostream.fileno(), prefix+"  <serviceName>")
74            if self.serviceName is None:
75               os.write(ostream.fileno(), "unknown")
76            else:
77               os.write(ostream.fileno(), str(self.serviceName))
78            os.write(ostream.fileno(), "</serviceName>\n")
79            #
80            os.write(ostream.fileno(), prefix+"  <instanceName>")
81            if self.instanceName is None:
82               os.write(ostream.fileno(), "unknown")
83            else:
84               os.write(ostream.fileno(), str(self.instanceName))
85            os.write(ostream.fileno(), "</instanceName>\n")
86            #
87            if self.time is not None:
88               os.write(ostream.fileno(), prefix+"  <time>"+str(self.time)+"</time>\n")
89            #
90            if self.message is not None:
91               os.write(ostream.fileno(), prefix+"  <message>"+str(self.message)+"</message>\n")
92            #
93            if self.jobID is not None:
94               os.write(ostream.fileno(), prefix+"  <jobID>"+str(self.jobID)+"</jobID>\n")
95            #
96            os.write(ostream.fileno(), prefix)
97            os.write(ostream.fileno(), "</ResultHeader>\n")
98
99
100    class Solution(object):
101        def __init__(self):
102            self.status_type=OSrL.SolutionStatus.unsure
103            self.status_description=None
104            self.message=None
105            self.variable={}
106            self.dual={}
107            self.objective={}
108            self.other={}
109
110        def _parse_xml(self, node):
111            for child in node.childNodes:
112              if child.nodeType == Node.ELEMENT_NODE:
113                 if child.nodeName.lower() == "message":
114                    self.message = get_xml_text(child)
115
116                 elif child.nodeName.lower() == "status":
117                    for (name,value) in child.attributes.items():
118                      if name == "type":
119                             self.status_type = str(value)
120                      elif name == "description":
121                             self.status_description = str(value)
122                 elif child.nodeName.lower() == "variables":
123                    for gchild in child.childNodes:
124                      if gchild.nodeType == Node.ELEMENT_NODE and\
125                         gchild.nodeName.lower() == "values":
126                         for ggchild in gchild.childNodes:
127                           if ggchild.nodeType == Node.ELEMENT_NODE and\
128                              ggchild.nodeName.lower() == "var":
129                              index=None
130                              for (name,value) in ggchild.attributes.items():
131                                if name == "idx":
132                                   index = int(value)
133                              self.variable[index] = get_xml_text(ggchild)
134                 elif child.nodeName.lower() == "objectives":
135                    for gchild in child.childNodes:
136                      if gchild.nodeType == Node.ELEMENT_NODE and\
137                         gchild.nodeName.lower() == "values":
138                         for ggchild in gchild.childNodes:
139                           if ggchild.nodeType == Node.ELEMENT_NODE and\
140                              ggchild.nodeName.lower() == "obj":
141                              index=None
142                              for (name,value) in ggchild.attributes.items():
143                                  if name == "idx":
144                                   index = int(value)
145                              self.objective[index] = get_xml_text(ggchild)
146                 elif child.nodeName.lower() == "constraints":
147                    for gchild in child.childNodes:
148                      if gchild.nodeType == Node.ELEMENT_NODE and\
149                         gchild.nodeName.lower() == "dualvalues":
150                         for ggchild in gchild.childNodes:
151                           if ggchild.nodeType == Node.ELEMENT_NODE and\
152                              ggchild.nodeName.lower() == "con":
153                              index=None
154                              for (name,value) in ggchild.attributes.items():
155                                  if name == "idx":
156                                   index = int(value)
157                              self.dual[index] = get_xml_text(ggchild)
158
159        def validate(self):
160            if self.status_type not in OSrL.SolutionStatus:
161               raise ValueError, "Solution status value '"+str(self.status_type)+"' is not in OSrL.SolutionStatus"
162            for key in self.variable:
163              if type(key) is not int or (type(key) is int and key < 0):
164                 raise KeyError, "Solution variables index '"+str(key)+"' is not an nonnegative integer"
165            for key in self.dual:
166              if type(key) is not int or (type(key) is int and key < 0):
167                 raise KeyError, "Solution constraint index '"+str(key)+"' is not an nonnegative integer"
168            for key in self.objective:
169              if type(key) is not int or (type(key) is int and key >= 0):
170                 raise KeyError, "Solution objective index '"+str(key)+"' is not a negative integer"
171
172        def write(self,ostream=None, prefix=""):
173            if ostream is None:
174               ostream = sys.stdout
175            self.validate()
176            os.write(ostream.fileno(), prefix)
177            os.write(ostream.fileno(), "<solution>\n")
178            #
179            os.write(ostream.fileno(), prefix+"  <status type=\""+str(self.status_type)+"\"")
180            if self.status_description is not None:
181               os.write(ostream.fileno(), " description=\""+str(self.status_description)+"\"")
182            os.write(ostream.fileno(), " />\n")
183            #
184            if len(self.variable) > 0:
185               os.write(ostream.fileno(), prefix+"  <variables>\n")
186               os.write(ostream.fileno(), prefix+"    <values>\n")
187               tmp = self.variable.keys()
188               tmp.sort()
189               for ndx in tmp:
190                 os.write(ostream.fileno(), prefix+"      <var idx=\""+str(ndx)+"\">"+str(self.variable[ndx])+"</var>\n")
191               os.write(ostream.fileno(), prefix+"    </values>\n")
192               os.write(ostream.fileno(), prefix+"  </variables>\n")
193            #
194            if len(self.objective) > 0:
195               os.write(ostream.fileno(), prefix+"  <objectives>\n")
196               os.write(ostream.fileno(), prefix+"    <values>\n")
197               tmp = self.objective.keys()
198               tmp.sort()
199               for ndx in tmp:
200                 os.write(ostream.fileno(), prefix+"      <obj idx=\""+str(ndx)+"\">"+str(self.objective[ndx])+"</obj>\n")
201               os.write(ostream.fileno(), prefix+"    </values>\n")
202               os.write(ostream.fileno(), prefix+"  </objectives>\n")
203            #
204            if len(self.dual) > 0:
205               os.write(ostream.fileno(), prefix+"  <constraints>\n")
206               os.write(ostream.fileno(), prefix+"    <dualValues>\n")
207               tmp = self.dual.keys()
208               tmp.sort()
209               for ndx in tmp:
210                 os.write(ostream.fileno(), prefix+"      <con idx=\""+str(ndx)+"\">"+str(self.dual[ndx])+"</con>\n")
211               os.write(ostream.fileno(), prefix+"    </dualValues>\n")
212               os.write(ostream.fileno(), prefix+"  </constraints>\n")
213            #
214            os.write(ostream.fileno(), prefix)
215            os.write(ostream.fileno(), "</solution>\n")
216
217
218    def __init__(self):
219        self.header = OSrL.ResultHeader()
220        self.numVariables=0
221        self.numConstraints=0
222        self.numObjectives=0
223        self.solution = []
224
225    def add_solution(self):
226        tmp = OSrL.Solution()
227        self.solution.append(tmp)
228        return tmp
229
230    def __len__(self):
231        return len(self.solution)
232
233    def __iter__(self):
234        return self.solution.__iter__()
235
236    def validate(self):
237        self.header.validate()
238        for soln in self.solution:
239          soln.validate()
240
241    def write(self,ostream=None, prefix=""):
242        if ostream is None:
243           ostream = sys.stdout
244        self.validate()
245        if type(ostream) is str:
246           self._tmpfile = ostream
247           ostream = open(self._tmpfile,"w")
248        else:
249           self._tmpfile = None
250
251        os.write(ostream.fileno(), prefix)
252        os.write(ostream.fileno(), "<OSrL>\n")
253
254        self.header.write(ostream,prefix+"  ")
255
256        os.write(ostream.fileno(), prefix+"  <ResultData>\n")
257        os.write(ostream.fileno(), prefix+"    <optimization\n")
258        os.write(ostream.fileno(), prefix+"      numberOfSolutions=\""+str(len(self.solution))+"\"\n")
259        os.write(ostream.fileno(), prefix+"      numberOfVariables=\""+str(self.numVariables)+"\"\n")
260        os.write(ostream.fileno(), prefix+"      numberOfObjectives=\""+str(self.numObjectives)+"\"\n")
261        os.write(ostream.fileno(), prefix+"      numberOfConstraints=\""+str(self.numConstraints)+"\"\n")
262        os.write(ostream.fileno(), prefix+"    >\n")
263
264        for soln in self.solution:
265          soln.write(ostream, prefix+"      ")
266
267        os.write(ostream.fileno(), prefix+"    </optimization>\n")
268        os.write(ostream.fileno(), prefix+"  </ResultData>\n")
269
270        os.write(ostream.fileno(), prefix)
271        os.write(ostream.fileno(), "</OSrL>\n")
272        if self._tmpfile is not None:
273           ostream.close()
274
275    def read(self, filename):
276        try:
277          doc = minidom.parse(filename)
278        except xml.parsers.expat.ExpatError, e:
279             raise xml.parsers.expat.ExpatError, "Problem parsing XML file " + filename+": "+str(e)
280        except Exception, e:
281             raise
282        try:
283          self._parse_xml(doc.documentElement)
284        except:                                 #pragma:nocover
285          raise IOError, "Problem initializing with data from the XML file " + filename
286        self.validate()
287
288    def _parse_xml(self, node):
289        for child in node.childNodes:
290          if child.nodeType == Node.ELEMENT_NODE and \
291             child.nodeName.lower() == "resultheader":
292             self.header._parse_xml(child)
293          if child.nodeType == Node.ELEMENT_NODE and \
294             child.nodeName.lower() == "resultdata":
295             for gchild in child.childNodes:
296               if gchild.nodeType == Node.ELEMENT_NODE and \
297                  gchild.nodeName.lower() == "optimization":
298                  for (name,value) in gchild.attributes.items():
299                      if name == "numberOfSolutions":
300                            self.numSolutions = str(value)
301                      elif name == "numberOfObjectives":
302                            self.numObjectives = str(value)
303                      elif name == "numberOfVariables":
304                            self.numVariables = str(value)
305                      elif name == "numberOfConstraints":
306                            self.numConstraints = str(value)
307                  for ggchild in gchild.childNodes:
308                    if ggchild.nodeType == Node.ELEMENT_NODE and \
309                       ggchild.nodeName.lower() == "solution":
310                       tmp = self.add_solution()
311                       tmp._parse_xml(ggchild)
312
313
314
315
316from coopr.opt.base import results
317from coopr.opt.base.formats import *
318from coopr.opt.base.solution import SolverResults, Solution
319
320class ResultsReader_osrl(results.AbstractResultsReader):
321    """
322    Class that reads in an OSrL results file and generates a
323    SolverResults object.
324    """
325
326    def __init__(self, name=None):
327        results.AbstractResultsReader.__init__(self,ResultsFormat.osrl)
328        if not name is None:
329            self.name = name
330
331    def __call__(self, filename, res=None):
332        if res is None:
333            res = SolverResults() 
334        osrl = OSrL()
335        osrl.read(filename)
336        #
337        # Transfer solver info into the SolverResults object
338       
339        ##res.solver.status = SolverResults.SolverStatus(osrl.generalStatus)
340        res.solver.serviceURI = osrl.header.serviceURI
341        res.solver.serviceName = osrl.header.serviceName
342        res.solver.instanceName = osrl.header.instanceName
343        res.solver.systime = osrl.header.time
344        res.solver.message = osrl.header.message
345        res.solver.id = osrl.header.jobID
346        res.problem.name = osrl.header.instanceName
347        #
348        # Transfer solution info into the SolverResults object
349       
350        for soln in osrl:
351          #
352          # We create a solution, and then populate its data
353          # The solution object is managed by the SolverResults instance
354          #
355          solution = res.solution.create()
356          solution.status = SolutionStatus(soln.status_type)
357          solution.status_description = soln.status_description
358          solution.message = soln.message
359          for key in soln.variable:
360            solution.variable.add(key, soln.variable[key])
361          for key in soln.dual:
362            solution.dual.add(key, soln.dual[key])
363          solution.value = soln.objective
364          solution.other=soln.other
365          res.problem.num_constraints = len(soln.dual) 
366          res.problem.num_variables = len(soln.variable) 
367          res.problem.num_objectives = len(soln.objective) 
368        return res
369
370
371results.ReaderRegistration(str(ResultsFormat.osrl), ResultsReader_osrl)
372
Note: See TracBrowser for help on using the repository browser.