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

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

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

........

r3188 | jwatson | 2010-10-29 08:09:35 -0600 (Fri, 29 Oct 2010) | 3 lines


Eliminating initial domain check when constructing numeric constants and the default domain (Reals) is specified.

........

r3203 | jwatson | 2010-10-29 14:46:18 -0600 (Fri, 29 Oct 2010) | 3 lines


Fixing bugs in has_discrete_variables() method of PyomoModel?.

........

r3211 | jdsiiro | 2010-11-01 14:49:11 -0600 (Mon, 01 Nov 2010) | 4 lines


bugfixes for Blocks:

  • avoid infinite loop when adding a block to a model
  • support pretty printing of user-defined components

........

r3212 | jdsiiro | 2010-11-02 15:17:13 -0600 (Tue, 02 Nov 2010) | 5 lines


  • cleaning up the management of Block._parent_block and Component.model attributes. Adding & removing blocks now updates the model attribute on all children
  • renaming Block._setattr_exec -> Block._add_component

........

r3213 | jdsiiro | 2010-11-03 10:47:06 -0600 (Wed, 03 Nov 2010) | 2 lines


Bugfix to the PyomoLogHandler? for python 2.4 compatibility

........

r3214 | jdsiiro | 2010-11-03 15:20:47 -0600 (Wed, 03 Nov 2010) | 3 lines


There is no point logging a warning when the problem is encountered
generating a logging.info message: log the warning at the info level.

........

r3215 | wehart | 2010-11-04 23:05:17 -0600 (Thu, 04 Nov 2010) | 3 lines


Adding a simple knapsack example to illustrate the difference between
a concrete and abstract model.

........

r3216 | jwatson | 2010-11-05 09:39:19 -0600 (Fri, 05 Nov 2010) | 3 lines


Fixing error diagnostic when indexing a variable with a bad index.

........

r3219 | jwatson | 2010-11-05 16:01:23 -0600 (Fri, 05 Nov 2010) | 3 lines


Supressing a validation test with NumericConstant?. If the user specifies a value, we are (now) assuming it is actually a numeric value - otherwise, the domain check significantly inflates the run-time associated with expression tree creation. This needs to be revisited in the Coopr 2.5 re-write.

........

r3226 | wehart | 2010-11-06 21:32:59 -0600 (Sat, 06 Nov 2010) | 2 lines


Setting up example, which was never converted.

........

r3233 | wehart | 2010-11-12 15:56:28 -0700 (Fri, 12 Nov 2010) | 4 lines


Migrating OS-specific functionality into coopr.os


Adding coopr.os to the dev.ini config file.

........

r3242 | wehart | 2010-11-13 01:28:57 -0700 (Sat, 13 Nov 2010) | 4 lines


Type fix.


Updating error message.

........

r3244 | wehart | 2010-11-13 10:44:26 -0700 (Sat, 13 Nov 2010) | 2 lines


Skipping OSiL writer when not defined.

........

r3246 | wehart | 2010-11-13 10:54:15 -0700 (Sat, 13 Nov 2010) | 2 lines


bug fix.

........

r3247 | wehart | 2010-11-13 11:14:01 -0700 (Sat, 13 Nov 2010) | 2 lines


Bug fix.

........

r3248 | jwatson | 2010-11-17 13:51:47 -0700 (Wed, 17 Nov 2010) | 3 lines


Interim fixes to output of quadratic terms in LP writer - more to do, but at least the basic examples now work.

........

r3254 | jwatson | 2010-11-19 13:19:19 -0700 (Fri, 19 Nov 2010) | 3 lines


Fixed bug in LP writer involving quadratic terms involving two distinct variables. Added two new quadratic examples.

........

r3257 | jwatson | 2010-11-19 13:59:35 -0700 (Fri, 19 Nov 2010) | 3 lines


Fixing diagnostic error message when attempting to solve quadratic programs with GLPK - code for generating message was not syntatically legal.

........

r3268 | jwatson | 2010-12-01 15:08:28 -0700 (Wed, 01 Dec 2010) | 3 lines


Fixing issues with the Piecewise construct when breakpoints and slopes are generated via rules. Works now (on a sample of size 1 - the newly added example5.py) for non-indexed rules, likely broken for indexed breakpoint/slope rules.

........

r3272 | jwatson | 2010-12-02 13:53:51 -0700 (Thu, 02 Dec 2010) | 3 lines


Adding omitted pprint() method for SOS constraints - identified while debugging a piecewise issue.

........

r3274 | jwatson | 2010-12-02 16:32:29 -0700 (Thu, 02 Dec 2010) | 3 lines


Adding example of Piecewise construct using breakpoint and slope rules, as opposed to explicit/direct lists.

........

r3276 | jwatson | 2010-12-03 14:06:40 -0700 (Fri, 03 Dec 2010) | 3 lines


Some progress toward functional indexed Piecewise components.

........

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
241            print >>OUTPUT, "+ ["
242
243            for id in sorted(x[2].keys()):
244
245                coefficient = x[2][id]
246                sign = '+'
247                if coefficient < 0: 
248                   sign = '-'
249                   coefficient = math.fabs(coefficient)
250
251                if is_objective:
252                    coefficient *= 2
253                # times 2 because LP format requires /2 for all the quadratic
254                # terms /of the objective only/.  Discovered the last bit thru
255                # trial and error.  Obnoxious.
256                # Ref: ILog CPlex 8.0 User's Manual, p197.
257
258                print >>OUTPUT, sign, coefficient,
259
260                term_variables = []
261                for var in id:
262                    label = x[-1][var].label
263                    if not label:
264                        self._no_label_error(x[-1][var])
265
266                    name = convert_name(label) # like X(3,1),
267                    term_variables.append(name)
268
269                if len(term_variables) == 2:
270                   print >>OUTPUT, term_variables[0],"*",term_variables[1],
271                else:
272                   print >>OUTPUT, term_variables[0],"^ 2",
273               
274            print >>OUTPUT, ""
275
276            print >>OUTPUT, "]",
277            if is_objective:
278                print >>OUTPUT, ' / 2'
279                # divide by 2 because LP format requires /2 for all the quadratic
280                # terms.  Weird.  Ref: ILog CPlex 8.0 User's Manual, p197
281            else:
282               print >>OUTPUT, ""
283
284
285        #
286        # Constant offset
287        #
288        offset=0.0
289        if 0 in x:
290            offset = x[0][None]
291        if print_offset and offset != 0.0:
292            sign = '+'
293            if offset < 0: sign = '-'
294            print >>OUTPUT, '%s%f %s' % (sign, math.fabs(offset), 'ONE_VAR_CONSTANT')
295
296        #
297        # Return constant offset
298        #
299        return offset
300
301
302#    @deprecated
303    def _print_quadterm(self, x, is_minimizing, OUTPUT):
304       
305        # The LP format doesn't allow for expression of constant terms in the
306        # objective.  A work-around involves tracking the sum of constant terms
307        # in the quadratic quadratic terms, and then writing that out with a
308        # dummy variable forced equal to one.
309        print >>OUTPUT, ""
310        for arg in x._args:
311            if isinstance(arg,expr._ProductExpression):
312                # NOTE: We need to handle quadratics defined with the 'pow'
313                # term here.
314                # WARNING: The following code is very specific to progressive
315                # hedging, with a very specific format assumed.  We do need to
316                # handle more general expressions, but we'll worry about that
317                # at a latter time.
318                blend = arg._numerator[0]()
319
320                if blend is 1:
321
322                   rho = arg._numerator[1]()
323
324                   pow_expression = arg._numerator[2]
325
326                   base = pow_expression._args[0]
327                   exponent = pow_expression._args[1]
328
329                   if not isinstance(base,expr._SumExpression):
330                       msg = 'Quadratic term base must be a _SumExpression'
331                       raise ValueError, msg
332
333                   if not isinstance(exponent,NumericConstant):
334                       msg = 'Quadratic term exponent must be a NumericConstant'
335                       raise ValueError, msg
336
337                   variable = base._args[0]
338                   offset = base._args[1]
339                   if variable.status is not VarStatus.unused:
340
341                      sign = '-'
342                      if is_minimizing is True:
343                          sign = '+'
344                      print >>OUTPUT, '%s [ %s %s^2 ] / 2' % (
345                        sign,
346                        str(rho),
347                        convert_name(variable.label)
348                      )
349
350                      sign = '-'
351                      if (is_minimizing is True) == (offset.value <  0):
352                          sign = '+'
353                      print >>OUTPUT, '%s %s %s' % (
354                         sign,
355                         str(abs(rho*offset.value)),
356                         convert_name( variable.label )
357                      )
358
359                      objective_offset = (rho * offset.value*offset.value /2.0)
360                      fmt = ' -%s ONE_VAR_CONSTANT'
361                      if is_minimizing is True: fmt = ' +%s ONE_VAR_CONSTANT'
362                      print >>OUTPUT, fmt % str(objective_offset)
363
364            elif isinstance(arg,NumericConstant):
365                # this is the "0.0" element that forms the initial expression
366                # -- the quadratic sub-expressions aren't known to the presolve
367                # routines. ideally unnecessary - hacked in for now.
368                pass
369
370            else:
371                msg = '%s\nUnknown expression sub-type found in quadratic '   \
372                      'objective expression'
373                raise ValueError, msg % `arg`
374
375
376    @staticmethod
377    def printSOS(con, name, OUTPUT, index=None):
378       
379        """
380        Returns the SOS constraint (as a string) associated with con.
381        If specified, index is passed to con.sos_set().
382
383        Arguments:
384        con    The SOS constraint object
385        name   The name of the variable
386        OUTPUT The output stream
387        index  [Optional] the index to pass to the sets indexing the variables.
388        """
389
390        # The name of the variable being indexed
391        varName = str(con.sos_vars())
392
393        # The list of variable names to be printed, including indices
394        varNames = []
395
396        # Get all the variables
397        if index is None:
398            tmpSet = con.sos_set()
399        else:
400            tmpSet = con.sos_set()[index]
401        for x in tmpSet:
402            strX = str(x)
403            if strX[0] == "(":
404                # its a tuple, remove whitespace
405                varNames.append(varName + strX.replace(" ",""))
406            else:
407                # its a single number, add parenthesis
408                varNames.append(varName + "(" + strX + ")")
409
410        conNameIndex = ""
411        if index is not None:
412            conNameIndex = str(index)
413
414        print >>OUTPUT, '%s%s: S%s::' % (name, conNameIndex, con.sos_level())
415
416        # We need to 'weight' each variable
417        # For now we just increment a counter
418        for i in range(0, len(varNames)):
419            print >>OUTPUT, '%s:%f' % (varNames[i], i+1)
420
421    def _print_model_LP(self, model, OUTPUT):
422
423        _obj = model.active_components(Objective)
424
425        supports_quadratic = model.has_capability('quadratic')
426
427        #
428        # Objective
429        #
430        if self._output_objectives is True:
431           
432           printed_quadterm = False
433           if len(_obj) == 0:
434               msg = "ERROR: No objectives defined for input model '%s'; "    \
435                     ' cannot write legal LP file'
436               raise ValueError, msg % str( model.name )
437
438           if _obj[ _obj.keys()[0] ].sense == maximize:
439              print >>OUTPUT, "max "
440           else:
441              print >>OUTPUT, "min "
442
443           obj = _obj[ _obj.keys()[0] ]
444           obj_keys = obj.keys()
445           if len(obj_keys) > 1:
446               keys = obj.name + ' (indexed by: %s)' % ', '.join(map(str, obj_keys))
447
448               msg = "More than one objective defined for input model '%s'; " \
449                     'Cannot write legal LP file\n'                           \
450                     'Objectives: %s'
451               raise ValueError, msg % ( str(model.name), keys )
452
453           for key in obj_keys:
454
455                if is_constant(obj[key].repn):
456
457                    print ("Warning: Constant objective detected, replacing " +
458                           "with a placeholder to prevent solver failure.")
459                   
460                    print >>OUTPUT, "obj: +0.0 ONE_VAR_CONSTANT"
461
462                    # Skip the remaining logic of the section
463                    continue
464
465                if is_quadratic( obj[key].repn ):
466                    if not supports_quadratic:
467                        msg  = 'Solver unable to handle quadratic '           \
468                               "objective expressions.  Objective at issue: %s%s."
469                        if key is None:
470                           msg %= (obj.name, " ")
471                        else:
472                           msg %= (obj.name, '[%s]' % key )
473                        raise ValueError, msg
474
475                elif is_nonlinear( obj[key].repn ):
476                    msg  = "Cannot write legal LP file.  Objective '%s%%s' "  \
477                           'has nonlinear terms that are not quadratic.'
478                    if key is None: msg %= (obj.name, '')
479                    else:           msg %= (obj.name, '[%s]' % key )
480                    raise ValueError, msg
481
482                print >>OUTPUT, "obj: "
483
484                offset = self._print_expr_canonical(obj[key].repn,
485                                                    OUTPUT,
486                                                    print_offset=True,
487                                                    is_objective=True)
488
489           if obj._quad_subexpr is not None:
490               self._print_quadterm(obj._quad_subexpr, (_obj[ _obj.keys()[0] ].sense == minimize), OUTPUT)
491               printed_quadterm = True
492               
493           print >>OUTPUT, ""
494
495        # Constraints
496        #
497        # If there are no non-trivial constraints, you'll end up with an empty
498        # constraint block. CPLEX is OK with this, but GLPK isn't. And
499        # eliminating the constraint block (i.e., the "s.t." line) causes GLPK
500        # to whine elsewhere. output a warning if the constraint block is empty,
501        # so users can quickly determine the cause of the solve failure.
502
503        if self._output_constraints is True:
504
505           # for now, if this routine isn't writing everything, then assume a
506           # meta-level handler is dealing with the writing of the transitional
507           # elements - these should probably be done in the form of
508           # "end_objective()" and "end_constraint()" helper methods.
509           if self._output_objectives == self._output_variables == True:
510               print >>OUTPUT, "s.t."
511               print >>OUTPUT, ""
512
513           active_constraints = model.active_components(Constraint)
514           have_nontrivial = False
515           for constraint in active_constraints.values():
516             if constraint.trivial:
517                continue
518
519             have_nontrivial=True
520             for index in constraint:
521
522               constraint_data = constraint[index]
523               if not constraint_data.active:
524                    continue
525
526               # if expression trees have been linearized, then there won't be a canonical
527               # expression (repn) attribute on the constraint data object.
528               if (hasattr(constraint_data, "repn") is True) and (constraint_data.repn is not None):
529
530                  # There are conditions, e.g., when fixing variables, under which
531                  # a constraint block might be empty.  Ignore these, for both
532                  # practical reasons and the fact that the CPLEX LP format
533                  # requires a variable in the constraint body.  It is also
534                  # possible that the body of the constraint consists of only a
535                  # constant, in which case the "variable" of
536                  if is_constant(constraint_data.repn):
537                      # this happens *all* the time in many applications,
538                      # including PH - so suppress the warning.
539                      #
540                      #msg = 'WARNING: ignoring constraint %s[%s] which is ' \
541                      #      'constant'
542                      #print msg % (str(C),str(cndx))
543                      continue
544   
545                  if is_quadratic( constraint_data.repn ):
546                      if not supports_quadratic:
547                          msg  = 'Solver unable to handle quadratic expressions.'\
548                                 "  Constraint at issue: '%s%%s'"
549                          msg %= constraint.name
550                          if cndx is None: msg %= ''
551                          else: msg %= '[%s]' % cndx
552   
553                          raise ValueError, msg
554
555                  elif is_nonlinear( constraint_data.repn ):
556                      msg = "Cannot write legal LP file.  Constraint '%s%%s' "   \
557                            'has a body with nonlinear terms.'
558                      if cndx is None: msg %= ( constraint.name, '')
559                      else:            msg %= ( constraint.name, '[%s]' % cndx )
560   
561                      raise ValueError, msg
562
563               prefix = ""
564               if self._output_prefixes is True:
565                   if constraint.model is None:
566                      msg = "Constraint '%s' has no model attribute - no "   \
567                            'label prefix can be assigned'
568                      raise RuntimeError, msg % constraint_data.label
569                   prefix = constraint.model.name+"_"
570
571               con_name = convert_name(constraint_data.label)
572
573               if constraint_data._equality:
574                  print >>OUTPUT, '%sc_e_%s_:' % (prefix, con_name)
575                  if constraint_data.lin_body is not None:
576                     offset = self._print_expr_linear(constraint_data.lin_body, OUTPUT)               
577                  else:
578                     offset = self._print_expr_canonical(constraint_data.repn, OUTPUT)               
579                  bound = constraint_data.lower
580                  bound = str(self._get_bound(bound) - offset)
581                  print >>OUTPUT, "=", bound
582                  print >>OUTPUT, ""
583               else:
584                  # TBD: ENCAPSULATE THE IF-ELSE INTO A SINGLE UTILITY METHOD
585                  # TBD: MAKE THE _data and C[ndx] calls consistent - everything should just reference the data directly
586                  if constraint_data.lower is not None:
587                     print >>OUTPUT, '%sc_l_%s_:' % (prefix, con_name)
588                     if constraint_data.lin_body is not None:
589                        offset = self._print_expr_linear(constraint_data.lin_body, OUTPUT)                                   
590                     else:
591                        offset = self._print_expr_canonical(constraint_data.repn, OUTPUT)                                   
592                     bound = constraint_data.lower
593                     bound = str(self._get_bound(bound) - offset)
594                     print >>OUTPUT, ">=", bound
595                     print >>OUTPUT, ""                     
596                  if constraint_data.upper is not None:
597                     print >>OUTPUT, '%sc_u_%s_:' % (prefix, con_name)
598                     if constraint_data.lin_body is not None:
599                        offset = self._print_expr_linear(constraint_data.lin_body, OUTPUT)                                                         
600                     else:
601                        offset = self._print_expr_canonical(constraint_data.repn, OUTPUT)                                                         
602                     bound = constraint_data.upper
603                     bound = str(self._get_bound(bound) - offset)
604                     print >>OUTPUT, "<=", bound
605                     print >>OUTPUT, ""                                         
606
607           if not have_nontrivial:
608               print 'WARNING: Empty constraint block written in LP format '  \
609                     '- solver may error'
610
611           # the CPLEX LP format doesn't allow constants in the objective (or
612           # constraint body), which is a bit silly.  To avoid painful
613           # book-keeping, we introduce the following "variable", constrained
614           # to the value 1.  This is used when quadratic terms are present.
615           # worst-case, if not used, is that CPLEX easily pre-processes it out.
616           prefix = ""
617           if self._output_prefixes is True:
618              prefix = model.name + "_"
619           print >>OUTPUT, '%sc_e_ONE_VAR_CONSTANT: ' % prefix
620           print >>OUTPUT, '%sONE_VAR_CONSTANT = 1.0' % prefix
621           print >>OUTPUT, ""
622
623        #
624        # Bounds
625        #
626
627        if self._output_variables is True:
628
629           # For now, if this routine isn't writing everything, then assume a
630           # meta-level handler is dealing with the writing of the transitional
631           # elements - these should probably be done in the form of
632           # "end_objective()" and "end_constraint()" helper methods.
633           if True == self._output_objectives == self._output_constraints:
634               print >>OUTPUT, "bounds "
635
636           # Scan all variables even if we're only writing a subset of them.
637           # required because we don't store maps by variable type currently.
638
639           # Track the number of integer and binary variables, so you can
640           # output their status later.
641           niv = nbv = 0
642           VAR = model.active_components(Var)
643           for var in VAR.values():
644               for ndx in var:
645                   v = var[ndx]
646                   if isinstance(v.domain, IntegerSet):   niv += 1
647                   elif isinstance(v.domain, BooleanSet): nbv += 1
648
649               if self._output_continuous_variables is True:
650
651                   for ndx in var._varval.keys():
652                       if not var._varval[ndx].active:
653                           continue
654                       prefix = ""
655                       if self._output_prefixes is True:
656                           prefix = convert_name(var.model.name)+"_"
657
658                       # if the variable isn't referenced in the model, don't
659                       # output bounds...
660                       if var[ndx].id != -1:
661                           # in the CPLEX LP file format, the default variable
662                           # bounds are 0 and +inf.  These bounds are in
663                           # conflict with Pyomo, which assumes -inf and inf
664                           # (which we would argue is more rational).
665                           print >>OUTPUT,"   ",
666                           if var[ndx].lb is not None:
667                             print >>OUTPUT, str(value(var[ndx].lb())), "<= ",
668                           else:
669                             print >>OUTPUT, " -inf <= ",
670                           name_to_output = prefix+convert_name(var[ndx].label)
671                           if name_to_output == "e":
672                               msg = 'Attempting to write variable with name' \
673                                     "'e' in a CPLEX LP formatted file - "    \
674                                     'will cause a parse failure due to '     \
675                                     'confusion with numeric values '         \
676                                     'expressed in scientific notation'
677                               raise ValueError, msg
678                           print >>OUTPUT, name_to_output,
679                           if var[ndx].ub is not None:
680                               print >>OUTPUT, " <=", str(value(var[ndx].ub()))
681                           else:
682                               print >>OUTPUT, " <= +inf"
683
684           if (niv > 0) and (self._output_integer_variables is True):
685
686              # If we're outputting the whole model, then assume we can output
687              # the "general" header. If not, then assume a meta-level process
688              # is taking care of it.
689              if True == self._output_objectives == self._output_constraints:
690                 print >>OUTPUT, "general"
691
692              prefix = ""
693              if self._output_prefixes is True:
694                 prefix = convert_name(var.model.name)+"_"
695
696              for var in VAR.values():
697                for ndx in var.integer_keys():
698                   if not var[ndx].active:
699                      continue
700                   if var[ndx].id != -1: # skip if variable not referenced
701                       var_name = prefix+convert_name(var[ndx].label)
702                       print >>OUTPUT, ' ', var_name
703
704           if (nbv > 0) and (self._output_binary_variables is True):
705
706              # If we're outputting the whole model, then assume we can output
707              # the "binary" header. if not, then assume a meta-level process
708              # is taking care of it.
709              if True == self._output_objectives == self._output_constraints:
710                 print >>OUTPUT, "binary"
711
712              prefix = ""
713              if self._output_prefixes is True:
714                 prefix = convert_name(var.model.name)+"_"
715
716              for var in VAR.values():
717                for ndx in var.binary_keys():
718                   if not var[ndx].active:
719                       continue
720                   if var[ndx].id != -1: # skip if variable not referenced
721                       var_name = prefix+convert_name(var[ndx].label)
722                       print >>OUTPUT, ' ', var_name
723
724
725        # SOS constraints
726        #
727        # For now, we write out SOS1 and SOS2 constraints in the cplex format
728        #
729        # All Component objects are stored in model._component, which is a
730        # dictionary of {class: {objName: object}}.
731        #
732        # Consider the variable X,
733        #
734        #   model.X = Var(...)
735        #
736        # We print X to CPLEX format as X(i,j,k,...) where i, j, k, ... are the
737        # indices of X.
738        #
739        # TODO: Allow users to specify the variables coefficients for custom
740        # branching/set orders
741        sosn = model.has_capability("sosn")
742        sos1 = model.has_capability("sos1")
743        sos2 = model.has_capability("sos2")
744
745        if sosn or sos1 or sos2:
746            writtenSOS = False
747            constrs = model._component.get(SOSConstraint, {})
748            for name in constrs:
749                con = constrs[name]
750                level = con.sos_level()
751                if (level == 1 and sos1) or (level == 2 and sos2) or (sosn):
752                    if writtenSOS == False:
753                        print >>OUTPUT, "SOS"
754                        writtenSOS = True
755                    masterIndex = con.sos_set_set()
756                    if None in masterIndex:
757                        # A single constraint
758                        self.printSOS(con, name, OUTPUT)
759                    else:
760                        # A series of indexed constraints
761                        for index in masterIndex:
762                            self.printSOS(con, name, OUTPUT, index)
763
764        #
765        # wrap-up
766        #
767        if self._output_objectives == self._output_constraints == True:
768           #
769           # End
770           #
771           print >>OUTPUT, "end "
Note: See TracBrowser for help on using the repository browser.