1# ___________________________________________________________________________ 2# 3# Pyomo: Python Optimization Modeling Objects 4# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC 5# Under the terms of Contract DE-NA0003525 with National Technology and 6# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain 7# rights in this software. 8# This software is distributed under the 3-clause BSD License. 9# ___________________________________________________________________________ 10 11__all__ = ['SolverResults'] 12 13import math 14import sys 15import copy 16import json 17import logging 18import os.path 19 20from pyomo.common.dependencies import yaml, yaml_load_args, yaml_available 21import pyomo.opt 22from pyomo.opt.results.container import (undefined, 23 ignore, 24 ListContainer, 25 MapContainer) 26import pyomo.opt.results.solution 27from pyomo.opt.results.solution import default_print_options as dpo 28import pyomo.opt.results.problem 29import pyomo.opt.results.solver 30 31from io import StringIO 32 33logger = logging.getLogger(__name__) 34 35class SolverResults(MapContainer): 36 37 undefined = undefined 38 default_print_options = dpo 39 40 def __init__(self): 41 MapContainer.__init__(self) 42 self._sections = [] 43 self._descriptions = {} 44 self.add('problem', 45 ListContainer(pyomo.opt.results.problem.ProblemInformation), 46 False, 47 "Problem Information") 48 self.add('solver', 49 ListContainer(pyomo.opt.results.solver.SolverInformation), 50 False, 51 "Solver Information") 52 self.add('solution', 53 pyomo.opt.results.solution.SolutionSet(), 54 False, 55 "Solution Information") 56 57 def add(self, name, value, active, description): 58 self.declare(name, value=value, active=active) 59 tmp = self._convert(name) 60 self._sections.append(tmp) 61 self._descriptions[tmp]=description 62 63 def json_repn(self, options=None): 64 if options is None: 65 return self._repn_(SolverResults.default_print_options) 66 else: 67 return self._repn_(options) 68 69 def _repn_(self, option): 70 if not option.schema and not self._active and not self._required: 71 return ignore 72 tmp = {} 73 for key in self._sections: 74 rep = dict.__getitem__(self, key)._repn_(option) 75 if not rep == ignore: 76 tmp[key] = rep 77 return tmp 78 79 def write(self, **kwds): 80 _fmt = kwds.pop('format', None) 81 if _fmt: 82 _fmt = _fmt.lower() 83 fname = kwds.pop('filename', None) 84 85 if fname: 86 ext = os.path.splitext(fname)[1].lstrip('.') 87 normalized_ext = { 88 'json': 'json', 89 'jsn': 'json', 90 'yaml': 'yaml', 91 'yml': 'yaml', 92 }.get(ext, None) 93 if not _fmt: 94 _fmt = normalized_ext 95 elif normalized_ext and _fmt != normalized_ext: 96 logger.warning( 97 "writing results to file (%s) using what appears " 98 "to be an incompatible format (%s)" % (fname, _fmt)) 99 with open(fname, "w") as OUTPUT: 100 kwds['ostream'] = OUTPUT 101 kwds['format'] = _fmt 102 self.write(**kwds) 103 else: 104 if not _fmt: 105 _fmt = 'yaml' 106 if _fmt == 'yaml': 107 self.write_yaml(**kwds) 108 elif _fmt == 'json': 109 self.write_json(**kwds) 110 else: 111 raise ValueError("Unknown results file format: %s" % (_fmt,)) 112 113 def write_json(self, **kwds): 114 if 'ostream' in kwds: 115 ostream = kwds['ostream'] 116 del kwds['ostream'] 117 else: 118 ostream = sys.stdout 119 120 option = copy.copy(SolverResults.default_print_options) 121 # TODO: verify that we need this for-loop 122 for key in kwds: 123 setattr(option,key,kwds[key]) 124 repn = self.json_repn(option) 125 126 for soln in repn.get('Solution', []): 127 for data in ['Variable', 'Constraint', 'Objective']: 128 remove = set() 129 if data not in soln: 130 continue 131 data_value = soln[data] 132 if not isinstance(data_value,dict): 133 continue 134 if not data_value: 135 # a variable/constraint/objective may have no 136 # entries, e.g., if duals or slacks weren't 137 # extracted in a solution. 138 soln[data] = "No values" 139 continue 140 for kk,vv in data_value.items(): 141 # TODO: remove this if-block. This is a hack 142 if not type(vv) is dict: 143 vv = {'Value':vv} 144 tmp = {} 145 for k,v in vv.items(): 146 # TODO: remove this if-block. This is a hack 147 if v is not None and math.fabs(v) > 1e-16: 148 tmp[k] = v 149 if len(tmp) > 0: 150 soln[data][kk] = tmp 151 else: 152 remove.add((data,kk)) 153 for item in remove: 154 del soln[item[0]][item[1]] 155 json.dump(repn, ostream, indent=4, sort_keys=True) 156 157 def write_yaml(self, **kwds): 158 if 'ostream' in kwds: 159 ostream = kwds['ostream'] 160 del kwds['ostream'] 161 else: 162 ostream = sys.stdout 163 164 option = copy.copy(SolverResults.default_print_options) 165 # TODO: verify that we need this for-loop 166 for key in kwds: 167 setattr(option,key,kwds[key]) 168 repn = self._repn_(option) 169 170 ostream.write("# ==========================================================\n") 171 ostream.write("# = Solver Results =\n") 172 ostream.write("# ==========================================================\n") 173 for i in range(len(self._order)): 174 key = self._order[i] 175 if not key in repn: 176 continue 177 item = dict.__getitem__(self,key) 178 ostream.write("# ----------------------------------------------------------\n") 179 ostream.write("# %s\n" % self._descriptions[key]) 180 ostream.write("# ----------------------------------------------------------\n") 181 ostream.write(key+": ") 182 if isinstance(item, ListContainer): 183 item.pprint(ostream, option, prefix="", repn=repn[key]) 184 else: 185 item.pprint(ostream, option, prefix=" ", repn=repn[key]) 186 187 def read(self, **kwds): 188 if 'istream' in kwds: 189 istream = kwds['istream'] 190 del kwds['istream'] 191 else: 192 ostream = sys.stdin 193 if 'filename' in kwds: 194 INPUT=open(kwds['filename'],"r") 195 del kwds['filename'] 196 kwds['istream']=INPUT 197 self.read(**kwds) 198 INPUT.close() 199 return 200 201 if not 'format' in kwds or kwds['format'] == 'yaml': 202 repn = yaml.load(istream, **yaml_load_args) 203 else: 204 repn = json.load(istream) 205 for i in range(len(self._order)): 206 key = self._order[i] 207 if not key in repn: 208 continue 209 item = dict.__getitem__(self,key) 210 item.load(repn[key]) 211 212 def __repr__(self): 213 return str(self._repn_(SolverResults.default_print_options)) 214 215 def __str__(self): 216 ostream = StringIO() 217 option=SolverResults.default_print_options 218 self.pprint(ostream, option, repn=self._repn_(option)) 219 return ostream.getvalue() 220 221 222if __name__ == '__main__': 223 results = SolverResults() 224 results.write(schema=True) 225 #print results 226