source: coopr.pyomo/trunk/coopr/pyomo/io/cpxlp.py @ 4092

Revision 4092, 31.8 KB checked in by jwatson, 3 years ago (diff)

Fixing issue in LP writer - if a single quadratic term is supplied, the code was behaving badly.

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