source: coopr.pyomo/stable/coopr/pyomo/io/cpxlp.py @ 3182

Last change on this file since 3182 was 3182, checked in by wehart, 10 years ago

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

........

r3116 | jwatson | 2010-10-18 14:08:00 -0600 (Mon, 18 Oct 2010) | 3 lines


First of a few commits involving an experimental linear compaction of the expression tree associated with a constraint, in order to save both memory and time.

........

r3117 | jwatson | 2010-10-18 14:09:13 -0600 (Mon, 18 Oct 2010) | 3 lines


Renaming linear representation module.

........

r3119 | jwatson | 2010-10-18 21:06:55 -0600 (Mon, 18 Oct 2010) | 3 lines


More exhaustive additions relating to linear expression encodings. No functional change to pyomo unless the --linearize-expressions options is specified. All tests are passing.

........

r3121 | jwatson | 2010-10-20 12:02:31 -0600 (Wed, 20 Oct 2010) | 3 lines


Fixing white-space issue when writing quadratic terms in LP files. Gurobi 4.0 beta wants more white-space than CPLEX, which is easy enough to accomodate.

........

r3123 | jwatson | 2010-10-20 13:02:06 -0600 (Wed, 20 Oct 2010) | 3 lines


Changing "integer" section label with the more technically correct "general" in the LP file writer. The former is not recognized by Gurobi, and actually does not appear in the CPLEX documentation either. I'm not sure where we got this, but the fix corrects/bypasses the issue entirely.

........

r3130 | wehart | 2010-10-20 20:44:17 -0600 (Wed, 20 Oct 2010) | 2 lines


Update of coopr.pyomo CHANGELOG for the 2.4 release.

........

r3132 | prsteel | 2010-10-21 15:30:40 -0600 (Thu, 21 Oct 2010) | 2 lines


Adding myself as a developer.

........

r3133 | jwatson | 2010-10-21 15:41:58 -0600 (Thu, 21 Oct 2010) | 5 lines


Various updates, mostly relating to linear expression representations debugging. Initial tests indicate very significant speed and memory reductions (at least for PH), with regression testing performed against a few initial benchmarks. No functional change if linear expressions are not enabled, which is the default


On the variable front, I have added output of variable Status in the pprint(), to facilitate debugging.

........

r3136 | khunter | 2010-10-22 10:19:32 -0600 (Fri, 22 Oct 2010) | 2 lines


Add name as a developer.

........

r3140 | jwatson | 2010-10-22 15:47:33 -0600 (Fri, 22 Oct 2010) | 3 lines


When constructing a linear representation, dump the canonical representation on the ConstraintData? class if it exists - these take up a large amount of space as well.

........

r3141 | jwatson | 2010-10-22 16:54:49 -0600 (Fri, 22 Oct 2010) | 3 lines


Added a "nochecking" keyword argument to Params. Intended strictly for use by algorithm developers, it by-passes the call to _valid_indexed_component, which was taking a huge amount of time in various PySP algorithms. Use sparingly and judiciously! May also apply in the near future to other aspects of setitem.

........

r3142 | jwatson | 2010-10-22 17:07:45 -0600 (Fri, 22 Oct 2010) | 3 lines


Suppression of index validation in Param::setitem when nochecking is enabled.

........

r3144 | jwatson | 2010-10-22 22:15:52 -0600 (Fri, 22 Oct 2010) | 3 lines


Update of test output baseline to reflect addition of variable status output in pretty-print.

........

r3149 | wehart | 2010-10-23 22:59:06 -0600 (Sat, 23 Oct 2010) | 3 lines


Changes to allow RangeSet? instances to be defined with
floating point start, stop and step arguments.

........

r3150 | wehart | 2010-10-23 22:59:31 -0600 (Sat, 23 Oct 2010) | 2 lines


Allowing string values to be elements of a Boolean set.

........

r3151 | wehart | 2010-10-23 23:00:47 -0600 (Sat, 23 Oct 2010) | 2 lines


Removing the name_str() method, which I didn't mean to commit.

........

r3153 | jwatson | 2010-10-24 22:14:15 -0600 (Sun, 24 Oct 2010) | 5 lines


Adding logic for LP writer when dealing with linearized expressions involving fixed variables.


Added "has_discrete_variables" method to PyomoModel?, to support query by MIP plugins - which often need this method to determine the type of solution information (e.g., duals) to extract.

........

r3158 | claird | 2010-10-25 12:01:21 -0600 (Mon, 25 Oct 2010) | 1 line


Added exception for multiple objectives - not currently handled by the NL writier

........

r3159 | claird | 2010-10-25 12:03:18 -0600 (Mon, 25 Oct 2010) | 1 line


Commented out second objective function so there are not multiple objectives in the example

........

r3166 | jwatson | 2010-10-26 19:42:59 -0600 (Tue, 26 Oct 2010) | 3 lines


Various performance-related enhancements, including a rework of NumericValue? and NumericConstant? internals.

........

r3167 | jwatson | 2010-10-26 20:05:25 -0600 (Tue, 26 Oct 2010) | 3 lines


Restoring older version of rangeset.py - the latest commit caused all kinds of test failures, for reasons I haven't had time to explore.

........

r3171 | jwatson | 2010-10-28 21:02:40 -0600 (Thu, 28 Oct 2010) | 3 lines


Fixing bug observed by John regarding passing of keyword arguments to the Constraint base class initializer.

........

r3181 | wehart | 2010-10-28 21:45:53 -0600 (Thu, 28 Oct 2010) | 2 lines


Updating changelog

........

File size: 30.0 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# Problem Writer for CPLEX LP Format Files
13#
14
15import math
16
17from coopr.opt import ProblemFormat
18from coopr.opt.base import AbstractProblemWriter
19from coopr.pyomo.base import BooleanSet, Constraint, expr, IntegerSet
20from coopr.pyomo.base import Objective, NumericConstant, SOSConstraint, Var
21from coopr.pyomo.base import VarStatus, value
22from coopr.pyomo.base.numtypes import minimize, maximize
23from coopr.pyomo.expr import is_constant, is_nonlinear, is_quadratic
24
25from pyutilib.component.core import alias
26from pyutilib.misc import deprecated
27
28
29def convert_name(namestr):
30   
31    return namestr.replace('[','(').replace(']',')')
32
33class ProblemWriter_cpxlp(AbstractProblemWriter):
34
35    alias('cpxlp')
36    alias('lp')
37
38    def __init__(self):
39       
40        AbstractProblemWriter.__init__(self,ProblemFormat.cpxlp)
41
42        # temporarily placing attributes used in extensive-form writing
43        # here, for eventual migration to the base class.
44        self._output_objectives = True
45        self._output_constraints = True
46        self._output_variables = True
47            # means we're outputting *some* variables - types determined by
48            # following flags.
49
50        # building on the above, partition out the variables that should
51        # be written if the _output_variables attribute is set to True.
52        # this is useful when writing components of multiple models to a
53        # single LP file. unfortunately, the CPLEX LP format requires all
54        # integer and binary (and continuous) variables to appear in a
55        # single continuous block.
56        #
57        # SOS constraints must come after Bounds, General, Binary, and
58        # Semi-Continuous sections
59        #
60        self._output_continuous_variables = True
61        self._output_integer_variables = True
62        self._output_binary_variables = True
63
64        # do I pre-pend the model name to all identifiers I output?
65        # useful in cases where you are writing components of multiple
66        # models to a single output LP file.
67        self._output_prefixes = False
68
69    def __call__(self, model, filename):
70       
71        if filename is None:
72           filename = model.name + ".lp"
73        OUTPUT=open(filename,"w")
74        self._print_model_LP(model,OUTPUT)
75        OUTPUT.close()
76        return filename, None
77
78    def _get_bound(self, exp):
79       
80        if isinstance(exp,expr._IdentityExpression):
81           return self._get_bound(exp._args[0])
82        elif exp.is_constant():
83           return exp()
84        else:
85           raise ValueError, "ERROR: nonconstant bound: " + str(exp)
86           return None
87
88
89    @staticmethod
90    def _collect_key ( a ):
91       
92        return a[0]
93
94
95    @staticmethod
96    def _no_label_error ( var ):
97       
98        msg  = "Unable to find label for variable '%s'.\n"                    \
99        'Possibly uninstantiated model.  Do any constraint or objective  '     \
100        'rules reference the original Model() object, as opposed to the  '     \
101        'passed model object? Alternatively, if you are developing code, '     \
102        'has the model instance been pre-processed?'
103
104        raise ValueError, msg % str(var)
105
106    def _print_expr_linear(self, x, OUTPUT, **kwargs):
107       
108        """
109        Return a expression as a string in LP format.
110
111        Note that this function does not handle any differences in LP format
112        interpretation by the solvers (e.g. CPlex vs GLPK).  That decision is
113        left up to the caller.
114
115        required arguments:
116          x: A Pyomo linear encoding of an expression to write in LP format
117
118        optional keyword arguments:
119          is_objective: set True if printing a quadratic objective.  Required
120             for CPlex LP quadratic handling differences between objectives
121             and constraints. (boolean)
122
123          print_offset: print expression offset [deprecated] (boolean)
124        """
125
126        is_objective = kwargs.pop('is_objective', False)
127        print_offset = kwargs.pop('print_offset', False)
128
129        constant_term = x[0]
130        linear_terms = x[1]
131
132        name_to_coefficient_map = {}
133
134        for coefficient, var_value in linear_terms:
135
136           if var_value.fixed is True:
137
138              constant_term += (coefficient * var_value.value)
139
140           else:
141
142              prefix = ""
143              if self._output_prefixes is True:
144                 parent_var = var_value.var
145                 prefix = convert_name(parent_var.model.name) + "_"
146
147              if not var_value.label:
148                 self._no_label_error(var_value)
149
150              name = prefix + convert_name(var_value.label)
151
152              # due to potential disabling of expression simplification,
153              # variables might appear more than once - condense coefficients.
154              if name_to_coefficient_map.has_key(name) is False:
155                 name_to_coefficient_map[name] = 0.0 
156              name_to_coefficient_map[name] += coefficient
157
158        sorted_names = sorted(name_to_coefficient_map.keys())
159
160        for name in sorted_names:
161     
162           coefficient = name_to_coefficient_map[name]
163
164           sign = '+'
165           if coefficient < 0: sign = '-'
166           print >>OUTPUT, '%s%f %s' % (sign, math.fabs(coefficient), name)
167
168        if print_offset and (constant_term != 0.0):
169            sign = '+'
170            if constant_term < 0: sign = '-'
171            print >>OUTPUT, '%s%f %s' % (sign, math.fabs(offset), 'ONE_VAR_CONSTANT')
172
173        return constant_term
174
175
176    def _print_expr_canonical(self, x, OUTPUT, **kwargs):
177       
178        """
179        Return a expression as a string in LP format.
180
181        Note that this function does not handle any differences in LP format
182        interpretation by the solvers (e.g. CPlex vs GLPK).  That decision is
183        left up to the caller.
184
185        required arguments:
186          x: A Pyomo canonical expression to write in LP format
187
188        optional keyword arguments:
189          is_objective: set True if printing a quadratic objective.  Required
190             for CPlex LP quadratic handling differences between objectives
191             and constraints. (boolean)
192
193          print_offset: print expression offset [deprecated] (boolean)
194        """
195
196        is_objective = kwargs.pop('is_objective', False)
197        print_offset = kwargs.pop('print_offset', False)
198
199        #
200        # Linear
201        #
202        if 1 in x:
203
204           var_hashes = x[1].keys()
205
206           name_to_coefficient_map = {}
207
208           for this_hash in var_hashes:
209
210              var_value = x[-1][this_hash.keys()[0]]
211              coefficient = x[1][this_hash]
212
213              prefix = ""
214
215              if self._output_prefixes is True:
216                 parent_var = var_value.var
217                 prefix = convert_name(parent_var.model.name) + "_"
218
219              label = var_value.label
220              if not label:
221                 self._no_label_error(var_value)
222 
223              name = prefix + convert_name(label)             
224
225              name_to_coefficient_map[name] = coefficient
226
227           sorted_names = sorted(name_to_coefficient_map.keys())
228   
229           for name in sorted_names:           
230
231              coef = name_to_coefficient_map[name]
232              sign = '+'
233              if coef < 0: sign = '-'
234              print >>OUTPUT, '%s%f %s' % (sign, math.fabs(coef), name)
235
236        #
237        # Quadratic
238        #
239        if 2 in x:
240            quadterms = list()
241            keys = sorted( x[2].keys() )
242            for id in keys:
243                coef = x[2][id]
244
245                sign = '+'
246                if coef < 0: sign = '-'
247                if is_objective:
248                    coef *= 2
249                # times 2 because LP format requires /2 for all the quadratic
250                # terms /of the objective only/.  Discovered the last bit thru
251                # trial and error.  Obnoxious.
252                # Ref: ILog CPlex 8.0 User's Manual, p197.
253
254                coef = str( math.fabs(coef) ) + ' '
255
256                quad = list()
257                for var in id:
258                    label = x[-1][var].label
259                    if not label:
260                        self._no_label_error( x[-1][var] )
261
262                    name = convert_name( label ) # like X(3,1),
263                    quad.append( name )
264
265                term = ' ^2' # same var (e.g. X**2) ...
266                if ( 2 == len(quad) ):  # ... unless we've 2 vars
267                    term = ' * ' + quad[1] # different vars (e.g. X*Y)
268                term = sign + coef + quad[0] + term
269                quadterms.append( term )
270
271            quad_output = '+ [ %s ]' % ' '.join( quadterms )
272            if is_objective:
273                quad_output += ' / 2'
274            # divide by 2 because LP format requires /2 for all the quadratic
275            # terms.  Weird.  Ref: ILog CPlex 8.0 User's Manual, p197
276
277            output.append( quad_output )
278
279        #
280        # Constant offset
281        #
282        offset=0.0
283        if 0 in x:
284            offset = x[0][None]
285        if print_offset and offset != 0.0:
286            sign = '+'
287            if offset < 0: sign = '-'
288            print >>OUTPUT, '%s%f %s' % (sign, math.fabs(offset), 'ONE_VAR_CONSTANT')
289
290        #
291        # Return constant offset
292        #
293        return offset
294
295
296#    @deprecated
297    def _print_quadterm(self, x, is_minimizing, OUTPUT):
298       
299        # The LP format doesn't allow for expression of constant terms in the
300        # objective.  A work-around involves tracking the sum of constant terms
301        # in the quadratic quadratic terms, and then writing that out with a
302        # dummy variable forced equal to one.
303        print >>OUTPUT, ""
304        for arg in x._args:
305            if isinstance(arg,expr._ProductExpression):
306                # NOTE: We need to handle quadratics defined with the 'pow'
307                # term here.
308                # WARNING: The following code is very specific to progressive
309                # hedging, with a very specific format assumed.  We do need to
310                # handle more general expressions, but we'll worry about that
311                # at a latter time.
312                blend = arg._numerator[0]()
313
314                if blend is 1:
315
316                   rho = arg._numerator[1]()
317
318                   pow_expression = arg._numerator[2]
319
320                   base = pow_expression._args[0]
321                   exponent = pow_expression._args[1]
322
323                   if not isinstance(base,expr._SumExpression):
324                       msg = 'Quadratic term base must be a _SumExpression'
325                       raise ValueError, msg
326
327                   if not isinstance(exponent,NumericConstant):
328                       msg = 'Quadratic term exponent must be a NumericConstant'
329                       raise ValueError, msg
330
331                   variable = base._args[0]
332                   offset = base._args[1]
333                   if variable.status is not VarStatus.unused:
334
335                      sign = '-'
336                      if is_minimizing is True:
337                          sign = '+'
338                      print >>OUTPUT, '%s [ %s %s^2 ] / 2' % (
339                        sign,
340                        str(rho),
341                        convert_name(variable.label)
342                      )
343
344                      sign = '-'
345                      if (is_minimizing is True) == (offset.value <  0):
346                          sign = '+'
347                      print >>OUTPUT, '%s %s %s' % (
348                         sign,
349                         str(abs(rho*offset.value)),
350                         convert_name( variable.label )
351                      )
352
353                      objective_offset = (rho * offset.value*offset.value /2.0)
354                      fmt = ' -%s ONE_VAR_CONSTANT'
355                      if is_minimizing is True: fmt = ' +%s ONE_VAR_CONSTANT'
356                      print >>OUTPUT, fmt % str(objective_offset)
357
358            elif isinstance(arg,NumericConstant):
359                # this is the "0.0" element that forms the initial expression
360                # -- the quadratic sub-expressions aren't known to the presolve
361                # routines. ideally unnecessary - hacked in for now.
362                pass
363
364            else:
365                msg = '%s\nUnknown expression sub-type found in quadratic '   \
366                      'objective expression'
367                raise ValueError, msg % `arg`
368
369
370    @staticmethod
371    def printSOS(con, name, OUTPUT, index=None):
372       
373        """
374        Returns the SOS constraint (as a string) associated with con.
375        If specified, index is passed to con.sos_set().
376
377        Arguments:
378        con    The SOS constraint object
379        name   The name of the variable
380        OUTPUT The output stream
381        index  [Optional] the index to pass to the sets indexing the variables.
382        """
383
384        # The name of the variable being indexed
385        varName = str(con.sos_vars())
386
387        # The list of variable names to be printed, including indices
388        varNames = []
389
390        # Get all the variables
391        if index is None:
392            tmpSet = con.sos_set()
393        else:
394            tmpSet = con.sos_set()[index]
395        for x in tmpSet:
396            strX = str(x)
397            if strX[0] == "(":
398                # its a tuple, remove whitespace
399                varNames.append(varName + strX.replace(" ",""))
400            else:
401                # its a single number, add parenthesis
402                varNames.append(varName + "(" + strX + ")")
403
404        conNameIndex = ""
405        if index is not None:
406            conNameIndex = str(index)
407
408        print >>OUTPUT, '%s%s: S%s::' % (name, conNameIndex, con.sos_level())
409
410        # We need to 'weight' each variable
411        # For now we just increment a counter
412        for i in range(0, len(varNames)):
413            print >>OUTPUT, '%s:%f' % (varNames[i], i+1)
414
415    def _print_model_LP(self, model, OUTPUT):
416
417        _obj = model.active_components(Objective)
418
419        supports_quadratic = model.has_capability('quadratic')
420
421        #
422        # Objective
423        #
424        if self._output_objectives is True:
425           
426           printed_quadterm = False
427           if len(_obj) == 0:
428               msg = "ERROR: No objectives defined for input model '%s'; "    \
429                     ' cannot write legal LP file'
430               raise ValueError, msg % str( model.name )
431
432           if _obj[ _obj.keys()[0] ].sense == maximize:
433              print >>OUTPUT, "max "
434           else:
435              print >>OUTPUT, "min "
436
437           obj = _obj[ _obj.keys()[0] ]
438           obj_keys = obj.keys()
439           if len(obj_keys) > 1:
440               keys = obj.name + ' (indexed by: %s)' % ', '.join(map(str, obj_keys))
441
442               msg = "More than one objective defined for input model '%s'; " \
443                     'Cannot write legal LP file\n'                           \
444                     'Objectives: %s'
445               raise ValueError, msg % ( str(model.name), keys )
446
447           for key in obj_keys:
448
449                if is_constant(obj[key].repn):
450
451                    print ("Warning: Constant objective detected, replacing " +
452                           "with a placeholder to prevent solver failure.")
453                   
454                    print >>OUTPUT, "obj: +0.0 ONE_VAR_CONSTANT"
455
456                    # Skip the remaining logic of the section
457                    continue
458
459                if is_quadratic( obj[key].repn ):
460                    if not supports_quadratic:
461                        msg  = 'Solver unable to handle quadratic '           \
462                               "expressions.  Objective at issue: '%s%%s'"
463                        if key is None: msg %= (obj.name, '')
464                        else:           msg %= (obj.name, '[%s]' % key )
465
466                        raise ValueError, msg
467
468                elif is_nonlinear( obj[key].repn ):
469                    msg  = "Cannot write legal LP file.  Objective '%s%%s' "  \
470                           'has nonlinear terms that are not quadratic.'
471                    if key is None: msg %= (obj.name, '')
472                    else:           msg %= (obj.name, '[%s]' % key )
473                    raise ValueError, msg
474
475                print >>OUTPUT, "obj: "
476
477                offset = self._print_expr_canonical(obj[key].repn,
478                                                    OUTPUT,
479                                                    print_offset=True,
480                                                    is_objective=True)
481
482           if obj._quad_subexpr is not None:
483               self._print_quadterm(obj._quad_subexpr, (_obj[ _obj.keys()[0] ].sense == minimize), OUTPUT)
484               printed_quadterm = True
485               
486           print >>OUTPUT, ""
487
488        # Constraints
489        #
490        # If there are no non-trivial constraints, you'll end up with an empty
491        # constraint block. CPLEX is OK with this, but GLPK isn't. And
492        # eliminating the constraint block (i.e., the "s.t." line) causes GLPK
493        # to whine elsewhere. output a warning if the constraint block is empty,
494        # so users can quickly determine the cause of the solve failure.
495
496        if self._output_constraints is True:
497
498           # for now, if this routine isn't writing everything, then assume a
499           # meta-level handler is dealing with the writing of the transitional
500           # elements - these should probably be done in the form of
501           # "end_objective()" and "end_constraint()" helper methods.
502           if self._output_objectives == self._output_variables == True:
503               print >>OUTPUT, "s.t."
504               print >>OUTPUT, ""
505
506           active_constraints = model.active_components(Constraint)
507           have_nontrivial = False
508           for constraint in active_constraints.values():
509             if constraint.trivial:
510                continue
511
512             have_nontrivial=True
513             for index in constraint:
514
515               constraint_data = constraint[index]
516               if not constraint_data.active:
517                    continue
518
519               # if expression trees have been linearized, then there won't be a canonical
520               # expression (repn) attribute on the constraint data object.
521               if (hasattr(constraint_data, "repn") is True) and (constraint_data.repn is not None):
522
523                  # There are conditions, e.g., when fixing variables, under which
524                  # a constraint block might be empty.  Ignore these, for both
525                  # practical reasons and the fact that the CPLEX LP format
526                  # requires a variable in the constraint body.  It is also
527                  # possible that the body of the constraint consists of only a
528                  # constant, in which case the "variable" of
529                  if is_constant(constraint_data.repn):
530                      # this happens *all* the time in many applications,
531                      # including PH - so suppress the warning.
532                      #
533                      #msg = 'WARNING: ignoring constraint %s[%s] which is ' \
534                      #      'constant'
535                      #print msg % (str(C),str(cndx))
536                      continue
537   
538                  if is_quadratic( constraint_data.repn ):
539                      if not supports_quadratic:
540                          msg  = 'Solver unable to handle quadratic expressions.'\
541                                 "  Constraint at issue: '%s%%s'"
542                          msg %= constraint.name
543                          if cndx is None: msg %= ''
544                          else: msg %= '[%s]' % cndx
545   
546                          raise ValueError, msg
547
548                  elif is_nonlinear( constraint_data.repn ):
549                      msg = "Cannot write legal LP file.  Constraint '%s%%s' "   \
550                            'has a body with nonlinear terms.'
551                      if cndx is None: msg %= ( constraint.name, '')
552                      else:            msg %= ( constraint.name, '[%s]' % cndx )
553   
554                      raise ValueError, msg
555
556               prefix = ""
557               if self._output_prefixes is True:
558                   if constraint.model is None:
559                      msg = "Constraint '%s' has no model attribute - no "   \
560                            'label prefix can be assigned'
561                      raise RuntimeError, msg % constraint_data.label
562                   prefix = constraint.model.name+"_"
563
564               con_name = convert_name(constraint_data.label)
565
566               if constraint_data._equality:
567                  print >>OUTPUT, '%sc_e_%s_:' % (prefix, con_name)
568                  if constraint_data.lin_body is not None:
569                     offset = self._print_expr_linear(constraint_data.lin_body, OUTPUT)               
570                  else:
571                     offset = self._print_expr_canonical(constraint_data.repn, OUTPUT)               
572                  bound = constraint_data.lower
573                  bound = str(self._get_bound(bound) - offset)
574                  print >>OUTPUT, "=", bound
575                  print >>OUTPUT, ""
576               else:
577                  # TBD: ENCAPSULATE THE IF-ELSE INTO A SINGLE UTILITY METHOD
578                  # TBD: MAKE THE _data and C[ndx] calls consistent - everything should just reference the data directly
579                  if constraint_data.lower is not None:
580                     print >>OUTPUT, '%sc_l_%s_:' % (prefix, con_name)
581                     if constraint_data.lin_body is not None:
582                        offset = self._print_expr_linear(constraint_data.lin_body, OUTPUT)                                   
583                     else:
584                        offset = self._print_expr_canonical(constraint_data.repn, OUTPUT)                                   
585                     bound = constraint_data.lower
586                     bound = str(self._get_bound(bound) - offset)
587                     print >>OUTPUT, ">=", bound
588                     print >>OUTPUT, ""                     
589                  if constraint_data.upper is not None:
590                     print >>OUTPUT, '%sc_u_%s_:' % (prefix, con_name)
591                     if constraint_data.lin_body is not None:
592                        offset = self._print_expr_linear(constraint_data.lin_body, OUTPUT)                                                         
593                     else:
594                        offset = self._print_expr_canonical(constraint_data.repn, OUTPUT)                                                         
595                     bound = constraint_data.upper
596                     bound = str(self._get_bound(bound) - offset)
597                     print >>OUTPUT, "<=", bound
598                     print >>OUTPUT, ""                                         
599
600           if not have_nontrivial:
601               print 'WARNING: Empty constraint block written in LP format '  \
602                     '- solver may error'
603
604           # the CPLEX LP format doesn't allow constants in the objective (or
605           # constraint body), which is a bit silly.  To avoid painful
606           # book-keeping, we introduce the following "variable", constrained
607           # to the value 1.  This is used when quadratic terms are present.
608           # worst-case, if not used, is that CPLEX easily pre-processes it out.
609           prefix = ""
610           if self._output_prefixes is True:
611              prefix = model.name + "_"
612           print >>OUTPUT, '%sc_e_ONE_VAR_CONSTANT: ' % prefix
613           print >>OUTPUT, '%sONE_VAR_CONSTANT = 1.0' % prefix
614           print >>OUTPUT, ""
615
616        #
617        # Bounds
618        #
619
620        if self._output_variables is True:
621
622           # For now, if this routine isn't writing everything, then assume a
623           # meta-level handler is dealing with the writing of the transitional
624           # elements - these should probably be done in the form of
625           # "end_objective()" and "end_constraint()" helper methods.
626           if True == self._output_objectives == self._output_constraints:
627               print >>OUTPUT, "bounds "
628
629           # Scan all variables even if we're only writing a subset of them.
630           # required because we don't store maps by variable type currently.
631
632           # Track the number of integer and binary variables, so you can
633           # output their status later.
634           niv = nbv = 0
635           VAR = model.active_components(Var)
636           for var in VAR.values():
637               for ndx in var:
638                   v = var[ndx]
639                   if isinstance(v.domain, IntegerSet):   niv += 1
640                   elif isinstance(v.domain, BooleanSet): nbv += 1
641
642               if self._output_continuous_variables is True:
643
644                   for ndx in var._varval.keys():
645                       if not var._varval[ndx].active:
646                           continue
647                       prefix = ""
648                       if self._output_prefixes is True:
649                           prefix = convert_name(var.model.name)+"_"
650
651                       # if the variable isn't referenced in the model, don't
652                       # output bounds...
653                       if var[ndx].id != -1:
654                           # in the CPLEX LP file format, the default variable
655                           # bounds are 0 and +inf.  These bounds are in
656                           # conflict with Pyomo, which assumes -inf and inf
657                           # (which we would argue is more rational).
658                           print >>OUTPUT,"   ",
659                           if var[ndx].lb is not None:
660                             print >>OUTPUT, str(value(var[ndx].lb())), "<= ",
661                           else:
662                             print >>OUTPUT, " -inf <= ",
663                           name_to_output = prefix+convert_name(var[ndx].label)
664                           if name_to_output == "e":
665                               msg = 'Attempting to write variable with name' \
666                                     "'e' in a CPLEX LP formatted file - "    \
667                                     'will cause a parse failure due to '     \
668                                     'confusion with numeric values '         \
669                                     'expressed in scientific notation'
670                               raise ValueError, msg
671                           print >>OUTPUT, name_to_output,
672                           if var[ndx].ub is not None:
673                               print >>OUTPUT, " <=", str(value(var[ndx].ub()))
674                           else:
675                               print >>OUTPUT, " <= +inf"
676
677           if (niv > 0) and (self._output_integer_variables is True):
678
679              # If we're outputting the whole model, then assume we can output
680              # the "general" header. If not, then assume a meta-level process
681              # is taking care of it.
682              if True == self._output_objectives == self._output_constraints:
683                 print >>OUTPUT, "general"
684
685              prefix = ""
686              if self._output_prefixes is True:
687                 prefix = convert_name(var.model.name)+"_"
688
689              for var in VAR.values():
690                for ndx in var.integer_keys():
691                   if not var[ndx].active:
692                      continue
693                   if var[ndx].id != -1: # skip if variable not referenced
694                       var_name = prefix+convert_name(var[ndx].label)
695                       print >>OUTPUT, ' ', var_name
696
697           if (nbv > 0) and (self._output_binary_variables is True):
698
699              # If we're outputting the whole model, then assume we can output
700              # the "binary" header. if not, then assume a meta-level process
701              # is taking care of it.
702              if True == self._output_objectives == self._output_constraints:
703                 print >>OUTPUT, "binary"
704
705              prefix = ""
706              if self._output_prefixes is True:
707                 prefix = convert_name(var.model.name)+"_"
708
709              for var in VAR.values():
710                for ndx in var.binary_keys():
711                   if not var[ndx].active:
712                       continue
713                   if var[ndx].id != -1: # skip if variable not referenced
714                       var_name = prefix+convert_name(var[ndx].label)
715                       print >>OUTPUT, ' ', var_name
716
717
718        # SOS constraints
719        #
720        # For now, we write out SOS1 and SOS2 constraints in the cplex format
721        #
722        # All Component objects are stored in model._component, which is a
723        # dictionary of {class: {objName: object}}.
724        #
725        # Consider the variable X,
726        #
727        #   model.X = Var(...)
728        #
729        # We print X to CPLEX format as X(i,j,k,...) where i, j, k, ... are the
730        # indices of X.
731        #
732        # TODO: Allow users to specify the variables coefficients for custom
733        # branching/set orders
734        sosn = model.has_capability("sosn")
735        sos1 = model.has_capability("sos1")
736        sos2 = model.has_capability("sos2")
737
738        if sosn or sos1 or sos2:
739            writtenSOS = False
740            constrs = model._component.get(SOSConstraint, {})
741            for name in constrs:
742                con = constrs[name]
743                level = con.sos_level()
744                if (level == 1 and sos1) or (level == 2 and sos2) or (sosn):
745                    if writtenSOS == False:
746                        print >>OUTPUT, "SOS"
747                        writtenSOS = True
748                    masterIndex = con.sos_set_set()
749                    if None in masterIndex:
750                        # A single constraint
751                        self.printSOS(con, name, OUTPUT)
752                    else:
753                        # A series of indexed constraints
754                        for index in masterIndex:
755                            self.printSOS(con, name, OUTPUT, index)
756
757        #
758        # wrap-up
759        #
760        if self._output_objectives == self._output_constraints == True:
761           #
762           # End
763           #
764           print >>OUTPUT, "end "
Note: See TracBrowser for help on using the repository browser.