Changeset 5795


Ignore:
Timestamp:
May 20, 2012 9:53:55 PM (7 years ago)
Author:
wehart
Message:

Migrating model xform stuff out of PyomoModel?.py

Location:
coopr.pyomo/trunk/coopr/pyomo
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • coopr.pyomo/trunk/coopr/pyomo/base/PyomoModel.py

    r5794 r5795  
    118118        process = kwargs.pop('process', None)
    119119        if process is None:
    120             return coopr.core.CooprAPIFactory(self._create)({}, model=self, **kwargs).instance
     120            data = coopr.core.CooprAPIFactory(self._create)({}, model=self, **kwargs)
    121121        else:
    122             return coopr.core.CooprAPIFactory(process)({}, model=self, **kwargs).instance
     122            data = coopr.core.CooprAPIFactory(process)({}, model=self, **kwargs)
     123        return data.instance
    123124
    124125    def clone(self):
    125126        """Create a copy of this model"""
     127        # TODO: check that this works recursively for nested models
    126128        self._model = None
    127129        for block in self.all_blocks():
     
    180182    def reset(self):
    181183        # TODO: check that this works recursively for nested models
    182        
    183184        for name in self.components:
    184185            self.components[name].reset()
     
    192193            self = Model.preprocessor_ep.service(item).preprocess(self)
    193194
    194     def solution(selfNone):
     195    def Xsolution(selfNone):
    195196        """Return the solution"""
    196197        soln = []
     
    260261                else:
    261262                    tmp[(var_value.component().name,var_value.index)] = (var_value.name, entry)
    262                 #vars[var_value.name] = entry
    263263            for key in sorted(tmp.keys()):
    264264                value = tmp[key]
     
    325325             allow_consistent_values_for_fixed_vars=False,
    326326             profile_memory=0, ignore_invalid_labels=False):
    327         """ Load the model with data from a file or a Solution object """
     327        """
     328        Load the model with data from a file or a Solution object
     329        """
    328330        if arg is None or type(arg) is str:
    329331            self._load_model_data(ModelData(filename=arg,model=self), namespaces, profile_memory=profile_memory)
     
    367369                ignore_invalid_labels=ignore_invalid_labels )
    368370            return True
    369         #elif type(arg) in (tuple, list):
    370         #    self._load_solution(
    371         #        arg,
    372         #        allow_consistent_values_for_fixed_vars
    373         #        = allow_consistent_values_for_fixed_vars,
    374         #        ignore_invalid_labels=ignore_invalid_labels )
    375         #    return True
    376371        else:
    377372            msg = "Cannot load model with object of type '%s'"
    378373            raise ValueError, msg % str( type(arg) )
    379374
    380     def store_info(self, results):
    381         """ Store model information into a SolverResults object """
     375    def Xstore_info(self, results):
     376        """
     377        Store model information into a SolverResults object
     378        """
    382379        results.problem.name = self.name
    383380
     
    410407        Load declarations from a ModelData object.
    411408        """
    412 
     409        #
    413410        # As we are primarily generating objects here (and acyclic ones
    414411        # at that), there is no need to run the GC until the entire
    415412        # model is created.  Simple reference-counting should be
    416413        # sufficient to keep memory use under control.
     414        #
    417415        suspend_gc = PauseGC()
    418 
     416        #
     417        # Unlike the standard method in the pympler summary module, the tracker
     418        # doesn't print 0-byte entries to pad out the limit.
     419        #
    419420        profile_memory = kwds.get('profile_memory', 0)
    420 
    421         # for pretty-printing summaries - unlike the standard
    422         # method in the pympler summary module, the tracker
    423         # doesn't print 0-byte entries to pad out the limit.
    424421        if (pympler_available is True) and (profile_memory >= 2):
    425422            memory_tracker = tracker.SummaryTracker()
    426 
     423        #
    427424        # Do some error checking
     425        #
    428426        for namespace in namespaces:
    429427            if not namespace is None and not namespace in modeldata._data:
    430428                msg = "Cannot access undefined namespace: '%s'"
    431429                raise IOError, msg % namespace
    432 
     430        #
    433431        # Initialize each component in order.
    434         components = [key for key in self.components]
    435 
    436         for component_name in components:
    437 
     432        #
     433        for component_name in self.components:
    438434            if (pympler_available is True) and (profile_memory >= 2):
    439435                memory_tracker.create_summary()
    440 
     436            #
    441437            if self.components[component_name].type() is Model:
    442438                continue
     
    459455                            var.domain = IntegerSet()
    460456                            var.domain.name = domain_name
    461                             self.active_components(Var)[key] = var
     457                            self.active_components(Var)[component_name] = var
    462458
    463459            declaration = self.components[component_name]
     
    631627        """
    632628        Write the model to a file, with a given format.
     629
     630        TODO: verify that this method needs to return the filename and symbol_map.
     631        TODO: these should be returned in a Bunch() object.
    633632        """
    634633        if format is None and not filename is None:
    635634            #
    636             # Guess the format
     635            # Guess the format if none is specified
    637636            #
    638637            format = guess_format(filename)
    639         #print "X",str(format),coopr.opt.WriterFactory.services()
     638        if solver_capability is None:
     639            solver_capability = lambda x: True
     640        #
    640641        problem_writer = coopr.opt.WriterFactory(format)
    641642        if problem_writer is None:
    642             msg = "Cannot write model in format '%s': no model writer "      \
    643                   "registered for that format"
    644             raise ValueError, msg % str(format)
    645 
    646         if solver_capability is None:
    647             solver_capability = lambda x: True
     643            raise ValueError, \
     644                    "Cannot write model in format '%s': no model writer " \
     645                    "registered for that format" \
     646                    % str(format)
     647        #
    648648        (fname, symbol_map) = problem_writer(self, filename, solver_capability)
     649        #
    649650        if __debug__:
    650651            if logger.isEnabledFor(logging.DEBUG):
    651652                logger.debug("Writing model '%s' to file '%s' with format %s", self.name, str(fname), str(format))
    652653        return fname, symbol_map
    653 
    654     def to_standard_form(self):
    655         """
    656         Produces a standard-form representation of the model. Returns
    657         the coefficient matrix (A), the cost vector (c), and the
    658         constraint vector (b), where the 'standard form' problem is
    659 
    660         min/max c'x
    661         s.t.    Ax = b
    662                 x >= 0
    663 
    664         All three returned values are instances of the array.array
    665         class, and store Python floats (C doubles).
    666         """
    667 
    668         from coopr.pyomo.expr import generate_canonical_repn
    669 
    670 
    671         # We first need to create an map of all variables to their column
    672         # number
    673         colID = {}
    674         ID2name = {}
    675         id = 0
    676         tmp = self.variables().keys()
    677         tmp.sort()
    678 
    679         for v in tmp:
    680             colID[v] = id
    681             ID2name[id] = v
    682             id += 1
    683 
    684         # First we go through the constraints and introduce slack and excess
    685         # variables to eliminate inequality constraints
    686         #
    687         # N.B. Structure heirarchy:
    688         #
    689         # active_components: {class: {attr_name: object}}
    690         # object -> Constraint: ._data: {ndx: _ConstraintData}
    691         # _ConstraintData: .lower, .body, .upper
    692         #
    693         # So, altogether, we access a lower bound via
    694         #
    695         # model.active_components()[Constraint]['con_name']['index'].lower
    696         #
    697         # {le,ge,eq}Constraints are
    698         # {constraint_name: {index: {variable_or_none: coefficient}} objects
    699         # that represent each constraint. None in the innermost dictionary
    700         # represents the constant term.
    701         #
    702         # i.e.
    703         #
    704         # min  x1 + 2*x2 +          x4
    705         # s.t. x1                         = 1
    706         #           x2   + 3*x3          <= -1
    707         #      x1 +                 x4   >= 3
    708         #      x1 + 2*x2 +      +   3*x4 >= 0
    709         #
    710         #
    711         # would be represented as (modulo the names of the variables,
    712         # constraints, and indices)
    713         #
    714         # eqConstraints = {'c1': {None: {'x1':1, None:-1}}}
    715         # leConstraints = {'c2': {None: {'x2':1, 'x3':3, None:1}}}
    716         # geConstraints = {'c3': {None: {'x1':1, 'x4':1, None:-3}},
    717         #                  'c4': {None: {'x1':1, 'x2':2, 'x4':1, None:0}}}
    718         #
    719         # Note the we have the luxury of dealing only with linear terms.
    720         var_id_map = {}
    721         leConstraints = {}
    722         geConstraints = {}
    723         eqConstraints = {}
    724         objectives = {}
    725         # For each registered component
    726         for c in self.active_components():
    727 
    728             # Get all subclasses of Constraint
    729             if issubclass(c, Constraint):
    730                 cons = self.active_components(c)
    731 
    732                 # Get the name of the constraint, and the constraint set itself
    733                 for con_set_name in cons:
    734                     con_set = cons[con_set_name]
    735 
    736                     # For each indexed constraint in the constraint set
    737                     for ndx in con_set._data:
    738                         con = con_set._data[ndx]
    739 
    740                         # Process the body
    741                         terms = self._process_canonical_repn(
    742                             generate_canonical_repn(con.body, var_id_map))
    743 
    744                         # Process the bounds of the constraint
    745                         if con._equality:
    746                             # Equality constraint, only check lower bound
    747                             lb = self._process_canonical_repn(
    748                                 generate_canonical_repn(con.lower, var_id_map))
    749 
    750                             # Update terms
    751                             for k in lb:
    752                                 v = lb[k]
    753                                 if k in terms:
    754                                     terms[k] -= v
    755                                 else:
    756                                     terms[k] = -v
    757 
    758                             # Add constraint to equality constraints
    759                             eqConstraints[(con_set_name, ndx)] = terms
    760                         else:
    761 
    762                             # Process upper bounds (<= constraints)
    763                             if con.upper is not None:
    764                                 # Less than or equal to constraint
    765                                 tmp = dict(terms)
    766 
    767                                 ub = self._process_canonical_repn(
    768                                     generate_canonical_repn(con.upper, var_id_map))
    769 
    770                                 # Update terms
    771                                 for k in ub:
    772                                     if k in terms:
    773                                         tmp[k] -= ub[k]
    774                                     else:
    775                                         tmp[k] = -ub[k]
    776 
    777                                 # Add constraint to less than or equal to
    778                                 # constraints
    779                                 leConstraints[(con_set_name, ndx)] = tmp
    780 
    781                             # Process lower bounds (>= constraints)
    782                             if con.lower is not None:
    783                                 # Less than or equal to constraint
    784                                 tmp = dict(terms)
    785 
    786                                 lb = self._process_canonical_repn(
    787                                     generate_canonical_repn(con.lower, var_id_map))
    788 
    789                                 # Update terms
    790                                 for k in lb:
    791                                     if k in terms:
    792                                         tmp[k] -= lb[k]
    793                                     else:
    794                                         tmp[k] = -lb[k]
    795 
    796                                 # Add constraint to less than or equal to
    797                                 # constraints
    798                                 geConstraints[(con_set_name, ndx)] = tmp
    799             elif issubclass(c, Objective):
    800                 # Process objectives
    801                 objs = self.active_components(c)
    802 
    803                 # Get the name of the objective, and the objective set itself
    804                 for obj_set_name in objs:
    805                     obj_set = objs[obj_set_name]
    806 
    807                     # For each indexed objective in the objective set
    808                     for ndx in obj_set._data:
    809                         obj = obj_set._data[ndx]
    810                         # Process the objective
    811                         terms = self._process_canonical_repn(
    812                             generate_canonical_repn(obj.expr, var_id_map))
    813 
    814                         objectives[(obj_set_name, ndx)] = terms
    815 
    816 
    817         # We now have all the constraints. Add a slack variable for every
    818         # <= constraint and an excess variable for every >= constraint.
    819         nSlack = len(leConstraints)
    820         nExcess = len(geConstraints)
    821 
    822         nConstraints = len(leConstraints) + len(geConstraints) + \
    823                        len(eqConstraints)
    824         nVariables = len(colID) + nSlack + nExcess
    825         nRegVariables = len(colID)
    826 
    827         # Make the arrays
    828         coefficients = array.array("d", [0]*nConstraints*nVariables)
    829         constraints = array.array("d", [0]*nConstraints)
    830         costs = array.array("d", [0]*nVariables)
    831 
    832         # Populate the coefficient matrix
    833         constraintID = 0
    834 
    835         # Add less than or equal to constraints
    836         for ndx in leConstraints:
    837             con = leConstraints[ndx]
    838             for termKey in con:
    839                 coef = con[termKey]
    840 
    841                 if termKey is None:
    842                     # Constraint coefficient
    843                     constraints[constraintID] = -coef
    844                 else:
    845                     # Variable coefficient
    846                     col = colID[termKey]
    847                     coefficients[constraintID*nVariables + col] = coef
    848 
    849             # Add the slack
    850             coefficients[constraintID*nVariables + nRegVariables + \
    851                         constraintID] = 1
    852             constraintID += 1
    853 
    854         # Add greater than or equal to constraints
    855         for ndx in geConstraints:
    856             con = geConstraints[ndx]
    857             for termKey in con:
    858                 coef = con[termKey]
    859 
    860                 if termKey is None:
    861                     # Constraint coefficient
    862                     constraints[constraintID] = -coef
    863                 else:
    864                     # Variable coefficient
    865                     col = colID[termKey]
    866                     coefficients[constraintID*nVariables + col] = coef
    867 
    868             # Add the slack
    869             coefficients[constraintID*nVariables + nRegVariables + \
    870                         constraintID] = -1
    871             constraintID += 1
    872 
    873         # Add equality constraints
    874         for ndx in eqConstraints:
    875             con = eqConstraints[ndx]
    876             for termKey in con:
    877                 coef = con[termKey]
    878 
    879                 if termKey is None:
    880                     # Constraint coefficient
    881                     constraints[constraintID] = -coef
    882                 else:
    883                     # Variable coefficient
    884                     col = colID[termKey]
    885                     coefficients[constraintID*nVariables + col] = coef
    886 
    887             constraintID += 1
    888 
    889         # Determine cost coefficients
    890         for obj_name in objectives:
    891             obj = objectives[obj_name]()
    892             for var in obj:
    893                 costs[colID[var]] = obj[var]
    894 
    895         # Print the model
    896         #
    897         # The goal is to print
    898         #
    899         #         var1   var2   var3   ...
    900         #       +--                     --+
    901         #       | cost1  cost2  cost3  ...|
    902         #       +--                     --+
    903         #       +--                     --+ +-- --+
    904         # con1  | coef11 coef12 coef13 ...| | eq1 |
    905         # con2  | coef21 coef22 coef23 ...| | eq2 |
    906         # con2  | coef31 coef32 coef33 ...| | eq3 |
    907         #  .    |   .      .      .   .   | |  .  |
    908         #  .    |   .      .      .    .  | |  .  |
    909         #  .    |   .      .      .     . | |  .  |
    910 
    911         constraintPadding = 2
    912         numFmt = "% 1.4f"
    913         altFmt = "% 1.1g"
    914         maxColWidth = max(len(numFmt % 0.0), len(altFmt % 0.0))
    915         maxConstraintColWidth = max(len(numFmt % 0.0), len(altFmt % 0.0))
    916 
    917         # Generate constraint names
    918         maxConNameLen = 0
    919         conNames = []
    920         for name in leConstraints:
    921             strName = str(name)
    922             if len(strName) > maxConNameLen:
    923                 maxConNameLen = len(strName)
    924             conNames.append(strName)
    925         for name in geConstraints:
    926             strName = str(name)
    927             if len(strName) > maxConNameLen:
    928                 maxConNameLen = len(strName)
    929             conNames.append(strName)
    930         for name in eqConstraints:
    931             strName = str(name)
    932             if len(strName) > maxConNameLen:
    933                 maxConNameLen = len(strName)
    934             conNames.append(strName)
    935 
    936         # Generate the variable names
    937         varNames = [None]*len(colID)
    938         for name in colID:
    939             tmp_name = " " + name
    940             if len(tmp_name) > maxColWidth:
    941                 maxColWidth = len(tmp_name)
    942             varNames[colID[name]] = tmp_name
    943         for i in xrange(0, nSlack):
    944             tmp_name = " _slack_%i" % i
    945             if len(tmp_name) > maxColWidth:
    946                 maxColWidth = len(tmp_name)
    947             varNames.append(tmp_name)
    948         for i in xrange(0, nExcess):
    949             tmp_name = " _excess_%i" % i
    950             if len(tmp_name) > maxColWidth:
    951                 maxColWidth = len(tmp_name)
    952             varNames.append(tmp_name)
    953 
    954         # Variable names
    955         line = " "*maxConNameLen + (" "*constraintPadding) + " "
    956         for col in xrange(0, nVariables):
    957             # Format entry
    958             token = varNames[col]
    959 
    960             # Pad with trailing whitespace
    961             token += " "*(maxColWidth - len(token))
    962 
    963             # Add to line
    964             line += " " + token + " "
    965         print line
    966 
    967         # Cost vector
    968         print " "*maxConNameLen + (" "*constraintPadding) + "+--" + \
    969               " "*((maxColWidth+2)*nVariables - 4) + "--+"
    970         line = " "*maxConNameLen + (" "*constraintPadding) + "|"
    971         for col in xrange(0, nVariables):
    972             # Format entry
    973             token = numFmt % costs[col]
    974             if len(token) > maxColWidth:
    975                 token = altFmt % costs[col]
    976 
    977             # Pad with trailing whitespace
    978             token += " "*(maxColWidth - len(token))
    979 
    980             # Add to line
    981             line += " " + token + " "
    982         line += "|"
    983         print line
    984         print " "*maxConNameLen + (" "*constraintPadding) + "+--" + \
    985               " "*((maxColWidth+2)*nVariables - 4) + "--+"
    986 
    987         # Constraints
    988         print " "*maxConNameLen + (" "*constraintPadding) + "+--" + \
    989               " "*((maxColWidth+2)*nVariables - 4) + "--+" + \
    990               (" "*constraintPadding) + "+--" + \
    991               (" "*(maxConstraintColWidth-1)) + "--+"
    992         for row in xrange(0, nConstraints):
    993             # Print constraint name
    994             line = conNames[row] + (" "*constraintPadding) + (" "*(maxConNameLen - len(conNames[row]))) + "|"
    995 
    996             # Print each coefficient
    997             for col in xrange(0, nVariables):
    998                 # Format entry
    999                 token = numFmt % coefficients[nVariables*row + col]
    1000                 if len(token) > maxColWidth:
    1001                     token = altFmt % coefficients[nVariables*row + col]
    1002 
    1003                 # Pad with trailing whitespace
    1004                 token += " "*(maxColWidth - len(token))
    1005 
    1006                 # Add to line
    1007                 line += " " + token + " "
    1008 
    1009             line += "|" + (" "*constraintPadding) + "|"
    1010 
    1011             # Add constraint vector
    1012             token = numFmt % constraints[row]
    1013             if len(token) > maxConstraintColWidth:
    1014                 token = altFmt % constraints[row]
    1015 
    1016             # Pad with trailing whitespace
    1017             token += " "*(maxConstraintColWidth - len(token))
    1018 
    1019             line += " " + token + "  |"
    1020             print line
    1021         print " "*maxConNameLen + (" "*constraintPadding) + "+--" + \
    1022               " "*((maxColWidth+2)*nVariables - 4) + "--+" + \
    1023               (" "*constraintPadding) + "+--" + (" "*(maxConstraintColWidth-1))\
    1024               + "--+"
    1025 
    1026         return (coefficients, costs, constraints)
    1027 
    1028     def _process_canonical_repn(self, expr):
    1029         """
    1030         Returns a dictionary of {var_name_or_None: coef} values
    1031         """
    1032 
    1033         terms = {}
    1034 
    1035         # Get the variables from the canonical representation
    1036         vars = expr.pop(-1, {})
    1037 
    1038         # Find the linear terms
    1039         linear = expr.pop(1, {})
    1040         for k in linear:
    1041             # FrozeDicts don't support (k, v)-style iteration
    1042             v = linear[k]
    1043 
    1044             # There's exactly 1 variable in each term
    1045             terms[vars[k.keys()[0]].label] = v
    1046 
    1047         # Get the constant term, if present
    1048         const = expr.pop(0, {})
    1049         if None in const:
    1050             terms[None] = const[None]
    1051 
    1052         if len(expr) != 0:
    1053             raise TypeError, "Nonlinear terms in expression"
    1054 
    1055         return terms
    1056654
    1057655
Note: See TracChangeset for help on using the changeset viewer.