source: coopr.pyomo/stable/2.3/coopr/pyomo/base/PyomoModel.py @ 2433

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

Merged revisions 2388-2432 via svnmerge from
https://software.sandia.gov/svn/public/coopr/coopr.pyomo/trunk

........

r2399 | wehart | 2010-02-24 11:36:56 -0700 (Wed, 24 Feb 2010) | 2 lines


Adding a work-around when pstats cannot be imported.

........

r2400 | wehart | 2010-02-24 14:51:08 -0700 (Wed, 24 Feb 2010) | 2 lines


Misc documentation updates.

........

r2408 | wehart | 2010-02-28 05:41:04 -0700 (Sun, 28 Feb 2010) | 3 lines


Allow an earlier version of Python. I'm still not sure this is a good idea,
but it's necessary for Jython.

........

r2409 | wehart | 2010-02-28 05:42:33 -0700 (Sun, 28 Feb 2010) | 7 lines


Rework of profiling imports.


Refinement of Pyomo command-line parsing, which is more
specific now.


Adding automatic import of pyomo to package.

........

r2415 | jwatson | 2010-03-08 20:53:13 -0700 (Mon, 08 Mar 2010) | 3 lines


Significantly improved performance of PyomoModel? _clear_attribute method, mainly by eliminate unnecessary calls to it through _setattr_exec_.

........

r2416 | jwatson | 2010-03-09 16:45:22 -0700 (Tue, 09 Mar 2010) | 5 lines


Modified the AMPL dat file parser to instantiate the lexer and yaccer objects only once, for the lifetime of the module.


They were being inadvertently created at each invocation, which pyomo users wouldn't notice. But PySP users, who can be creating 1K or more instances, do!

........

r2422 | wehart | 2010-03-11 16:01:09 -0700 (Thu, 11 Mar 2010) | 3 lines


Rework of unit tests to (a) import pyutilib.th as 'unittest' and
(b) employ test skipping.

........

r2430 | wehart | 2010-03-11 23:38:22 -0700 (Thu, 11 Mar 2010) | 2 lines


Reworking class decorators.

........

  • Property svn:executable set to *
File size: 19.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
11import copy
12import re
13import sys
14
15from plugin import *
16from numvalue import *
17
18import pyutilib.component.core
19from pyutilib.math import *
20from pyutilib.misc import quote_split, tuplize, Container
21
22from coopr.opt import ProblemFormat, ResultsFormat, guess_format
23from coopr.pyomo.base.var import _VarValue
24import coopr.opt
25from PyomoModelData import ModelData
26import pyomo
27
28from component import Component
29from sets import Set, _ProductSet, _SetContainer, _BaseSet
30from rangeset import RangeSet
31from var import Var
32from constraint import Objective, Constraint, ConstraintData
33from param import Param
34
35
36class Model(Component):
37    """
38    The MINLP model that will be analyzed by the user.
39    """
40
41    preprocessor_ep = pyutilib.component.core.ExtensionPoint(IPyomoPresolver)
42
43
44    def __init__(self, name="unknown"):
45        """Constructor"""
46        Component.__init__(self, ctype=Model)
47        self._defer_construction=True
48        #
49        # Define component dictionary: component type -> instance
50        #
51        self._component={}
52        for item in pyutilib.component.core.ExtensionPoint(IModelComponent):
53            self._component[ item.cls() ] = {}
54        #
55        # A list of the declarations, in the order that they are
56        # specified.
57        #
58        self._declarations=[]
59        # the _name_varmap and _name_conmap dictionarys are assigned when
60        # the problem definition file is written, i.e., via the write()
61        # command. intent is to map names that will be coming back from
62        # a solver into Pyomo variables. Values are of type _VarValue.
63        self._name_varmap={}
64        self._name_conmap={}
65        self._name_objmap={}
66        self.name=name
67        self.tmpctr=0
68        self._varctr=0
69        self._preprocessors = ["simple_preprocessor"]
70        #
71        # Dictionaries that map from id to Variable/Constraints
72        #
73        self._var = {}
74        self._con = {}
75        self._obj = {}
76        #
77        # Model statistics
78        #
79        self.statistics = Container()
80
81    def concrete_mode(self):
82        self._defer_construction=False
83
84    def symbolic_mode(self):
85        self._defer_construction=True
86
87    def components(self, ctype=None):
88        if ctype is None:
89            return self._component
90        if ctype in self._component:
91             return self._component[ctype]
92        raise KeyError, "Unknown component type: %s" % str(ctype)
93
94    def nvariables(self):
95        return self.statistics.number_of_variables
96
97    def variable(self, name):
98        if isinstance(name,basestring):
99            return self._name_varmap[name]
100        else:
101            return self._var[name-1]
102
103    def variables(self):
104        return self._name_varmap
105
106    def nconstraints(self):
107        return self.statistics.number_of_constraints
108
109    def constraint(self, name):
110        if isinstance(name,basestring):
111            return self._name_conmap[name]
112        else:
113            return self._con[name-1]
114
115    def constraints(self):
116        return self._name_conmap
117
118    def nobjectives(self):
119        return self.statistics.number_of_objectives
120
121    def objective(self, name):
122        if isinstance(name,basestring):
123            return self._name_objmap[name]
124        else:
125            return self._obj[name-1]
126
127    def objectives(self):
128        return self._name_objmap
129
130
131    def active_components(self, _ctype=None):
132        tmp = {}
133        if _ctype is None:
134            for ctype in self._component:
135                tmp[ctype]={}
136        elif _ctype in self._component:
137            tmp[_ctype]={}
138        else:
139            raise KeyError, "Unknown component type: %s" % str(ctype)
140        for ctype in tmp:
141            for name in self._component[ctype]:
142                comp = self._component[ctype][name]
143                if comp.active:
144                    tmp[ctype][name] = comp
145        if not _ctype is None:
146            return tmp[_ctype]
147        print "ACTIVE",tmp
148        return tmp
149
150    def num_used_variables(self):
151        return len(self._var)
152
153
154    def valid_problem_types(self):
155        return [ProblemFormat.pyomo]
156
157
158    def _add_temporary_set(self,val):
159        if val._index_set is not None:
160            ctr=0
161            for tset in val._index_set:
162                if tset.name == "_unknown_":
163                    self._construct_temporary_set(tset,val.name+"_index_"+str(ctr))
164                ctr+=1
165            val._index = self._construct_temporary_set(val._index_set,val.name+"_index")
166        if isinstance(val._index,_SetContainer) and \
167            val._index.name == "_unknown_":
168            self._construct_temporary_set(val._index,val.name+"_index")
169        if val.domain is not None and val.domain.name == "_unknown_":
170            self._construct_temporary_set(val.domain,val.name+"_domain")
171
172
173    def _construct_temporary_set(self, obj, name):
174        if type(obj) is tuple:
175           if len(obj) == 1:                #pragma:nocover
176                  raise Exception, "Unexpected temporary set construction"
177           else:
178                  tobj=_ProductSet(*obj)
179                  setattr(self,name,tobj)
180                  tobj.virtual=True
181                  return tobj
182        if isinstance(obj,_BaseSet):
183           setattr(self,name,obj)
184           return obj
185
186
187    def _clear_attribute(self,name):
188        #
189        # cleanup the pre-existing model attribute
190        #
191        i=0
192        for decl in self._declarations:
193            if decl.name == name:
194                self.__dict__[name]=None
195                del self._declarations[i]
196                for item in self._component:
197                    if name in self._component[item]:
198                        del self._component[item][name]
199                        break
200                break
201            i += 1
202
203    def _setattr_exec(self,name,val):
204        if name in self.__dict__:
205           self._clear_attribute(name)
206        val.name=name
207        self._add_temporary_set(val)
208        self._component[val.type()][name]=val
209        self._declarations.append(val)
210        self.__dict__[name]=val
211        if not val.type() is Model:
212            val.model=self
213        if not self._defer_construction:
214            val.construct(None)
215
216    def __setattr__(self,name,val):
217        """Set attributes"""
218        #
219        # Set Model Declaration
220        #
221        if name != "_component":
222            #
223            # If this is a component type, then simply set it
224            #
225            if isinstance(val,Component):
226                self._setattr_exec(name,val)
227                return
228        #
229        # Try to set the value.  This may fail if the attribute does not already
230        # exist in this model, if the set_value function is not defined, or if
231        # a bad value is provided.  In the latter case, a ValueError will be thrown, which
232        # we raise.  Otherwise, this is an object that we need to set directly.
233        #
234        try:
235            self.__dict__[name].set_value(val)
236        except ValueError, e:
237            raise
238        except Exception, e:
239            self.__dict__[name]=val
240
241
242    def create(self, filename=None, name=None):
243        """
244        Create a concrete instance of this Model, possibly using data
245        read in from a file.
246        """
247        if self._defer_construction:
248            instance = self.clone()
249            instance.load(filename)
250        else:
251            instance = self
252        instance.preprocess()
253        if not name is None:
254            instance.name=name
255        return instance
256
257
258    def clone(self):
259        """Create a copy of this model"""
260        for declaration in self._declarations:
261          declaration.model = None
262        instance = copy.deepcopy(self)
263        for declaration in self._declarations:
264          declaration.model = self
265        for declaration in instance._declarations:
266          declaration.model = instance
267        return instance
268
269
270    def reset(self):
271        for declaration in self._declarations:
272            declaration.reset()
273
274
275    def preprocess(self):
276        """Apply the preprocess plugins defined by the user"""
277        for item in self._preprocessors:
278            self = Model.preprocessor_ep.service(item).preprocess(self)
279       
280
281    def solution(self):
282        """Return the solution"""
283        soln = []
284        for i in range(len(self._var)):
285            soln.append( self._var[i].value )
286        return soln
287
288
289    def load(self, arg):
290        """ Load the model with data from a file or a Solution object """
291        if arg is None or type(arg) is str:
292           self._load_model_data(ModelData(filename=arg,model=self))
293           return True
294        elif type(arg) is ModelData:
295           self._load_model_data(arg)
296           return True
297        elif type(arg) is coopr.opt.SolverResults:
298           # if the solver status not one of either OK or Warning, then error.
299           if (arg.solver.status != coopr.opt.SolverStatus.ok) and (arg.solver.status != coopr.opt.SolverStatus.warning):
300              raise ValueError, "Cannot load a SolverResults object with bad status: "+str(arg.solver.status)
301           # but if there is a warning, print out a warning, as someone should probably take a look!
302           if (arg.solver.status == coopr.opt.SolverStatus.warning):
303              print "WARNING - Loading a SolverResults object with a warning status"
304           if len(arg.solution) > 0:
305              self._load_solution(arg.solution(0),symbol_map=arg.symbol_map)
306              return True
307           else:
308              return False
309        elif type(arg) is coopr.opt.Solution:
310           self._load_solution(arg)
311           return True
312        elif type(arg) in (tuple, list):
313           self._load_solution(arg)
314           return True
315        else:
316           raise ValueError, "Cannot load model with object of type "+str(type(arg))
317
318
319    def store_info(self, results):
320        """ Store model information into a SolverResults object """
321        results.problem.name = self.name
322        results.problem.number_of_variables = self.statistics.number_of_variables
323        results.problem.number_of_binary_variables = self.statistics.number_of_binary_variables
324        results.problem.number_of_integer_variables = self.statistics.number_of_integer_variables
325        results.problem.number_of_continuous_variables = self.statistics.number_of_continuous_variables
326        results.problem.number_of_constraints = self.statistics.number_of_constraints
327        results.problem.number_of_objectives = self.statistics.number_of_objectives
328
329    def _tuplize(self, data, setobj):
330        if data is None:            #pragma:nocover
331           return None
332        if setobj.dimen == 1:
333           return data
334        ans = {}
335        for key in data:
336          if type(data[key][0]) is tuple:
337             return data
338          ans[key] = tuplize(data[key], setobj.dimen, setobj.name)
339        return ans
340
341
342    def _load_model_data(self,modeldata):
343        """
344        Load declarations from a ModelData object.
345        """
346        #print "HERE _load_model_data",self._declarations
347        for declaration in self._declarations:
348            if declaration.type() is Model:
349                continue
350            tmp = declaration.name
351            if tmp in modeldata._default.keys():
352                if declaration.type() is Set:
353                    declaration.default = self._tuplize(modeldata._default[tmp],declaration)
354                else:
355                    declaration.default = modeldata._default[tmp]
356            if tmp in modeldata._data.keys():
357                #print "HERE", declaration.name, str(declaration.dimen),modeldata._data[tmp]
358                if declaration.type() is Set:
359                    data = self._tuplize(modeldata._data[tmp],declaration)
360                else:
361                    data = modeldata._data[tmp]
362            else:
363                data = None
364            if pyomo.debug("generate"):           #pragma:nocover
365                print "About to generate '"+declaration.name+"' with data: "+str(data)
366                self.pprint()
367            declaration.construct(data)
368            try:
369                pass
370            except Exception, err:
371                print "Error constructing declaration "+str(declaration.name)+" from data="+str(data)
372                print "ERROR: "+str(err)
373                raise err
374
375    def _load_solution(self, soln, symbol_map=None):
376        """
377        Load a solution.
378        """
379        # Load list or tuple into the variables, based on their order
380        if type(soln) in (list, tuple):
381            if len(soln) != len(self._var):
382                raise ValueError, "Attempting to load a list/tuple solution into a model, but its length is %d while the model has %d variables" % (len(soln), len(self._var))
383            for i in range(len(soln)):
384                self._var[i].value = soln[i]
385            return
386        #
387        # Load variable data
388        #
389        for name, entry in soln.variable.items():
390          #
391          # NOTE: the following is a hack, to handle the ONE_VAR_CONSTANT variable
392          #       that is necessary for the objective constant-offset terms.
393          #       probably should create a dummy variable in the model map at the same
394          #       time the objective expression is being constructed.
395          #
396          if name != "ONE_VAR_CONSTANT":
397             # translate the name first if there is a variable map associated with the input solution.
398             if symbol_map is not None:
399                name = symbol_map[name]
400             if name not in self._name_varmap:
401                names=""
402                raise KeyError, "Variable name '"+name+"' is not in model "+self.name+".  Valid variable names: "+str(self._name_varmap.keys())
403             if not isinstance(self._name_varmap[name],_VarValue):
404                raise TypeError, "Variable '"+name+"' in model '"+self.name+"' is type "+str(type(self._name_varmap[name]))
405             if self._name_varmap[name].fixed is True:
406                raise TypeError, "Variable '"+name+"' in model '"+self.name+"' is currently fixed - new value is not expected in solution"
407
408             var_value = self._name_varmap[name]
409             for _key in entry.keys():
410                key = _key[0].lower() + _key[1:] 
411                if key == 'value':
412                    var_value.value = entry.value
413                elif not key == 'id':
414                    setattr(var_value, key, getattr(entry, _key))
415        #
416        # Load constraint data
417        #
418        for name,entry in soln.constraint.items():
419             #
420             # This is a hack - see above.
421             #
422             if name.endswith('ONE_VAR_CONSTANT'):
423                continue
424             if symbol_map is not None:
425                name = symbol_map[name]
426             #
427             # This is a hack.  Is there a standard convention for constraint
428             # names that we can apply to all data formats?
429             #
430             if name[0:2] == 'c_':
431                _name = name[4:-1]
432             else:
433                _name = name
434
435             if _name not in self._name_conmap:
436                names=""
437                raise KeyError, "Constraint name '"+name+"' is not in model "+self.name+".  Valid constraint names: "+str(self._name_conmap.keys())
438             if not isinstance(self._name_conmap[_name],ConstraintData):
439                raise TypeError, "Constraint '"+name+"' in model '"+self.name+"' is type "+str(type(self._name_conmap[_name]))
440
441             constraint_data = self._name_conmap[_name]
442             for _key in entry.keys():
443                key = _key[0].lower() + _key[1:] 
444                if key == 'value':
445                    constraint_data.value = entry.value
446                elif not key == 'id':
447                    setattr(constraint_data, key, getattr(entry, _key))
448
449    def write(self,filename=None,format=ProblemFormat.cpxlp):
450        """
451        Write the model to a file, with a given format.
452        """
453        if format is None and not filename is None:
454            #
455            # Guess the format
456            #
457            format = guess_format(filename)
458        problem_writer = coopr.opt.WriterFactory(format)
459        if problem_writer is None:
460           raise ValueError, "Cannot write model in format \"" + str(format) + "\": no model writer registered for that format"
461        (fname, symbol_map) = problem_writer(self, filename)
462        if pyomo.debug("verbose"):
463           print "Writing model "+self.name+" to file '"+str(fname)+"'  with format "+str(format)
464        return fname, symbol_map
465
466
467    def pprint(self, filename=None, ostream=None):
468        """
469        Print a summary of the model info
470        """
471        if ostream is None:
472           ostream = sys.stdout
473        if filename is not None:
474           OUTPUT=open(filename,"w")
475           self.pprint(ostream=OUTPUT)
476           OUTPUT.close()
477           return
478        if ostream is None:
479           ostream = sys.stdout
480        #
481        # We hard-code the order of the core Pyomo modeling
482        # components, to ensure that the output follows the logical
483        # expected by a user.
484        #
485        items = [Set, RangeSet, Param, Var, Objective, Constraint]
486        for item in pyutilib.component.core.ExtensionPoint(IModelComponent):
487            if not item in items:
488                items.append(item)
489        for item in items:
490            if not item in self._component:
491                continue
492            keys = self._component[item].keys()
493            keys.sort()
494            #
495            # NOTE: these conditional checks should not be hard-coded.
496            #
497            print >>ostream, len(keys), item.__name__+" Declarations"
498            for key in keys:
499                self._component[item][key].pprint(ostream)
500            print >>ostream, ""
501        #
502        # Model Order
503        #
504        print >>ostream, len(self._declarations),"Declarations:",
505        for i in range(0,len(self._declarations)):
506          print >>ostream, self._declarations[i].name,
507        print >>ostream, ""
508
509
510    def display(self, filename=None, ostream=None):
511        """
512        Print the Pyomo model in a verbose format.
513        """
514        if filename is not None:
515           OUTPUT=open(filename,"w")
516           self.display(ostream=OUTPUT)
517           OUTPUT.close()
518           return
519        if ostream is None:
520           ostream = sys.stdout
521        print >>ostream, "Model "+self.name
522        print >>ostream, ""
523        print >>ostream, "  Variables:"
524        VAR = self.active_components(Var)
525        if len(VAR) == 0:
526            print >>ostream, "    None"
527        else:
528            for ndx in VAR:
529                VAR[ndx].display(prefix="    ",ostream=ostream)
530        print >>ostream, ""
531        print >>ostream, "  Objectives:"
532        OBJ = self.active_components(Objective)
533        if len(OBJ) == 0:
534            print >>ostream, "    None"
535        else:
536            for ndx in OBJ:
537                OBJ[ndx].display(prefix="    ",ostream=ostream)
538        print >>ostream, ""
539        CON = self.active_components(Constraint)
540        print >>ostream, "  Constraints:"
541        if len(CON) == 0:
542            print >>ostream, "    None"
543        else:
544            for ndx in CON:
545                CON[ndx].display(prefix="    ",ostream=ostream)
546
547ComponentRegistration("Model", Model, "Model objects can be used as a component of other models.")
Note: See TracBrowser for help on using the repository browser.