1# ___________________________________________________________________________ 2# 3# Pyomo: Python Optimization Modeling Objects 4# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC 5# Under the terms of Contract DE-NA0003525 with National Technology and 6# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain 7# rights in this software. 8# This software is distributed under the 3-clause BSD License. 9# ___________________________________________________________________________ 10 11# 12# Problem Writer for BARON .bar Format Files 13# 14 15import itertools 16import logging 17import math 18from io import StringIO 19 20from pyomo.common.backports import nullcontext 21from pyomo.common.collections import OrderedSet 22from pyomo.opt import ProblemFormat 23from pyomo.opt.base import AbstractProblemWriter, WriterFactory 24from pyomo.core.expr.numvalue import ( 25 value, native_numeric_types, native_types, nonpyomo_leaf_types, 26) 27from pyomo.core.expr import current as EXPR 28from pyomo.core.base import (SortComponents, 29 SymbolMap, 30 ShortNameLabeler, 31 NumericLabeler, 32 Constraint, 33 Objective, 34 Param) 35from pyomo.core.base.component import ActiveComponent 36#CLH: EXPORT suffixes "constraint_types" and "branching_priorities" 37# pass their respective information to the .bar file 38import pyomo.core.base.suffix 39import pyomo.core.kernel.suffix 40from pyomo.core.kernel.block import IBlock 41from pyomo.repn.util import valid_expr_ctypes_minlp, \ 42 valid_active_ctypes_minlp, ftoa 43 44logger = logging.getLogger('pyomo.core') 45 46# 47# A visitor pattern that creates a string for an expression 48# that is compatible with the BARON syntax. 49# 50class ToBaronVisitor(EXPR.ExpressionValueVisitor): 51 52 def __init__(self, variables, smap): 53 super(ToBaronVisitor, self).__init__() 54 self.variables = variables 55 self.smap = smap 56 57 def visit(self, node, values): 58 """ Visit nodes that have been expanded """ 59 tmp = [] 60 for i,val in enumerate(values): 61 arg = node._args_[i] 62 63 if arg is None: 64 tmp.append('Undefined') # TODO: coverage 65 else: 66 parens = False 67 if val and val[0] in '-+': 68 parens = True 69 elif arg.__class__ in native_numeric_types: 70 pass 71 elif arg.__class__ in nonpyomo_leaf_types: 72 val = "'{0}'".format(val) 73 elif arg.is_expression_type(): 74 if node._precedence() < arg._precedence(): 75 parens = True 76 elif node._precedence() == arg._precedence(): 77 if i == 0: 78 parens = node._associativity() != 1 79 elif i == len(node._args_)-1: 80 parens = node._associativity() != -1 81 else: 82 parens = True 83 if parens: 84 tmp.append("({0})".format(val)) 85 else: 86 tmp.append(val) 87 88 if node.__class__ in EXPR.NPV_expression_types: 89 return ftoa(value(node)) 90 91 if node.__class__ is EXPR.LinearExpression: 92 for v in node.linear_vars: 93 self.variables.add(id(v)) 94 95 if node.__class__ in { 96 EXPR.ProductExpression, EXPR.MonomialTermExpression}: 97 if tmp[0] in node._to_string.minus_one: 98 return "- {0}".format(tmp[1]) 99 if tmp[0] in node._to_string.one: 100 return tmp[1] 101 return "{0} * {1}".format(tmp[0], tmp[1]) 102 elif node.__class__ is EXPR.PowExpression: 103 x,y = node.args 104 if type(x) not in native_types and not x.is_fixed() and \ 105 type(y) not in native_types and not y.is_fixed(): 106 # Per the BARON manual, x ^ y is allowed as long as x 107 # and y are not both variables 108 return "exp(({1}) * log({0}))".format(tmp[0], tmp[1]) 109 else: 110 return "{0} ^ {1}".format(tmp[0], tmp[1]) 111 elif node.__class__ is EXPR.UnaryFunctionExpression: 112 if node.name == "sqrt": 113 return "{0} ^ 0.5".format(tmp[0]) 114 elif node.name == 'log10': 115 return "{0} * log({1})".format(math.log10(math.e), tmp[0]) 116 elif node.name in {'exp','log'}: 117 return node._to_string(tmp, None, self.smap, True) 118 else: 119 raise RuntimeError( 120 'The BARON .BAR format does not support the unary ' 121 'function "%s".' % (node.name,)) 122 elif node.__class__ is EXPR.AbsExpression: 123 return "({0} ^ 2) ^ 0.5".format(tmp[0]) 124 else: 125 return node._to_string(tmp, None, self.smap, True) 126 127 def visiting_potential_leaf(self, node): 128 """ 129 Visiting a potential leaf. 130 131 Return True if the node is not expanded. 132 """ 133 #print("ISLEAF") 134 #print(node.__class__) 135 if node is None: 136 return True, None 137 138 if node.__class__ in native_types: 139 return True, ftoa(node) 140 141 if node.is_expression_type(): 142 # we will descend into this, so type checking will happen later 143 return False, None 144 145 if node.is_component_type(): 146 _ctype = node.ctype 147 if _ctype not in valid_expr_ctypes_minlp: 148 # Make sure all components in active constraints 149 # are basic ctypes we know how to deal with. 150 raise RuntimeError( 151 "Unallowable component '%s' of type %s found in an active " 152 "constraint or objective.\nThe GAMS writer cannot export " 153 "expressions with this component type." 154 % (node.name, _ctype.__name__)) 155 156 if node.is_variable_type(): 157 if node.fixed: 158 return True, ftoa(value(node)) 159 else: 160 self.variables.add(id(node)) 161 label = self.smap.getSymbol(node) 162 return True, label 163 164 return True, ftoa(value(node)) 165 166 167def expression_to_string(expr, variables, labeler=None, smap=None): 168 if labeler is not None: 169 if smap is None: 170 smap = SymbolMap() 171 smap.default_labeler = labeler 172 visitor = ToBaronVisitor(variables, smap) 173 return visitor.dfs_postorder_stack(expr) 174 175 176 177# TODO: The to_string function is handy, but the fact that 178# it calls .name under the hood for all components 179# everywhere they are used will present ENORMOUS 180# overhead for components that have a large index set. 181# It might be worth adding an extra keyword to that 182# function that takes a "labeler" or "symbol_map" for 183# writing non-expression components. 184 185@WriterFactory.register('bar', 'Generate the corresponding BARON BAR file.') 186class ProblemWriter_bar(AbstractProblemWriter): 187 188 def __init__(self): 189 190 AbstractProblemWriter.__init__(self, ProblemFormat.bar) 191 192 def _write_equations_section(self, 193 model, 194 output_file, 195 all_blocks_list, 196 active_components_data_var, 197 symbol_map, 198 c_labeler, 199 output_fixed_variable_bounds, 200 skip_trivial_constraints, 201 sorter): 202 203 referenced_variable_ids = OrderedSet() 204 205 def _skip_trivial(constraint_data): 206 if skip_trivial_constraints: 207 if constraint_data._linear_canonical_form: 208 repn = constraint_data.canonical_form() 209 if (repn.variables is None) or \ 210 (len(repn.variables) == 0): 211 return True 212 elif constraint_data.body.polynomial_degree() == 0: 213 return True 214 return False 215 216 # 217 # Check for active suffixes to export 218 # 219 if isinstance(model, IBlock): 220 suffix_gen = lambda b: ((suf.storage_key, suf) \ 221 for suf in pyomo.core.kernel.suffix.\ 222 export_suffix_generator(b, 223 active=True, 224 descend_into=False)) 225 else: 226 suffix_gen = lambda b: pyomo.core.base.suffix.\ 227 active_export_suffix_generator(b) 228 r_o_eqns = [] 229 c_eqns = [] 230 l_eqns = [] 231 branching_priorities_suffixes = [] 232 for block in all_blocks_list: 233 for name, suffix in suffix_gen(block): 234 if name in {'branching_priorities', 'priority'}: 235 branching_priorities_suffixes.append(suffix) 236 elif name == 'constraint_types': 237 for constraint_data, constraint_type in suffix.items(): 238 if not _skip_trivial(constraint_data): 239 if constraint_type.lower() == 'relaxationonly': 240 r_o_eqns.append(constraint_data) 241 elif constraint_type.lower() == 'convex': 242 c_eqns.append(constraint_data) 243 elif constraint_type.lower() == 'local': 244 l_eqns.append(constraint_data) 245 else: 246 raise ValueError( 247 "A suffix '%s' contained an invalid value: %s\n" 248 "Choices are: [relaxationonly, convex, local]" 249 % (suffix.name, constraint_type)) 250 else: 251 if block is block.model(): 252 if block.name == 'unknown': 253 _location = 'model' 254 else: 255 _location = "model '%s'" % (block.name,) 256 else: 257 _location = "block '%s'" % (block.name,) 258 259 raise ValueError( 260 "The BARON writer can not export suffix with name '%s'. " 261 "Either remove it from the %s or deactivate it." 262 % (name, _location)) 263 264 non_standard_eqns = r_o_eqns + c_eqns + l_eqns 265 266 # 267 # EQUATIONS 268 # 269 270 #Equation Declaration 271 n_roeqns = len(r_o_eqns) 272 n_ceqns = len(c_eqns) 273 n_leqns = len(l_eqns) 274 eqns = [] 275 276 # Alias the constraints by declaration order since Baron does not 277 # include the constraint names in the solution file. It is important 278 # that this alias not clash with any real constraint labels, hence 279 # the use of the ".c<integer>" template. It is not possible to declare 280 # a component having this type of name when using standard syntax. 281 # There are ways to do it, but it is unlikely someone will. 282 order_counter = 0 283 alias_template = ".c%d" 284 output_file.write('EQUATIONS ') 285 output_file.write("c_e_FIX_ONE_VAR_CONST__") 286 order_counter += 1 287 for block in all_blocks_list: 288 289 for constraint_data in block.component_data_objects(Constraint, 290 active=True, 291 sort=sorter, 292 descend_into=False): 293 294 if (not constraint_data.has_lb()) and \ 295 (not constraint_data.has_ub()): 296 assert not constraint_data.equality 297 continue # non-binding, so skip 298 299 if (not _skip_trivial(constraint_data)) and \ 300 (constraint_data not in non_standard_eqns): 301 302 eqns.append(constraint_data) 303 304 con_symbol = symbol_map.createSymbol(constraint_data, c_labeler) 305 assert not con_symbol.startswith('.') 306 assert con_symbol != "c_e_FIX_ONE_VAR_CONST__" 307 308 symbol_map.alias(constraint_data, 309 alias_template % order_counter) 310 output_file.write(", "+str(con_symbol)) 311 order_counter += 1 312 313 output_file.write(";\n\n") 314 315 if n_roeqns > 0: 316 output_file.write('RELAXATION_ONLY_EQUATIONS ') 317 for i, constraint_data in enumerate(r_o_eqns): 318 con_symbol = symbol_map.createSymbol(constraint_data, c_labeler) 319 assert not con_symbol.startswith('.') 320 assert con_symbol != "c_e_FIX_ONE_VAR_CONST__" 321 symbol_map.alias(constraint_data, 322 alias_template % order_counter) 323 if i == n_roeqns-1: 324 output_file.write(str(con_symbol)+';\n\n') 325 else: 326 output_file.write(str(con_symbol)+', ') 327 order_counter += 1 328 329 if n_ceqns > 0: 330 output_file.write('CONVEX_EQUATIONS ') 331 for i, constraint_data in enumerate(c_eqns): 332 con_symbol = symbol_map.createSymbol(constraint_data, c_labeler) 333 assert not con_symbol.startswith('.') 334 assert con_symbol != "c_e_FIX_ONE_VAR_CONST__" 335 symbol_map.alias(constraint_data, 336 alias_template % order_counter) 337 if i == n_ceqns-1: 338 output_file.write(str(con_symbol)+';\n\n') 339 else: 340 output_file.write(str(con_symbol)+', ') 341 order_counter += 1 342 343 if n_leqns > 0: 344 output_file.write('LOCAL_EQUATIONS ') 345 for i, constraint_data in enumerate(l_eqns): 346 con_symbol = symbol_map.createSymbol(constraint_data, c_labeler) 347 assert not con_symbol.startswith('.') 348 assert con_symbol != "c_e_FIX_ONE_VAR_CONST__" 349 symbol_map.alias(constraint_data, 350 alias_template % order_counter) 351 if i == n_leqns-1: 352 output_file.write(str(con_symbol)+';\n\n') 353 else: 354 output_file.write(str(con_symbol)+', ') 355 order_counter += 1 356 357 # Create a dictionary of baron variable names to match to the 358 # strings that constraint.to_string() prints. An important 359 # note is that the variable strings are padded by spaces so 360 # that whole variable names are recognized, and simple 361 # variable names are not identified inside longer names. 362 # Example: ' x[1] ' -> ' x3 ' 363 #FIXME: 7/18/14 CLH: This may cause mistakes if spaces in 364 # variable names are allowed 365 if isinstance(model, IBlock): 366 mutable_param_gen = lambda b: \ 367 b.components(ctype=Param, 368 descend_into=False) 369 else: 370 def mutable_param_gen(b): 371 for param in block.component_objects(Param): 372 if param.mutable and param.is_indexed(): 373 param_data_iter = \ 374 (param_data for index, param_data 375 in param.items()) 376 elif not param.is_indexed(): 377 param_data_iter = iter([param]) 378 else: 379 param_data_iter = iter([]) 380 381 for param_data in param_data_iter: 382 yield param_data 383 384 if False: 385 # 386 # This was part of a merge from master that caused 387 # test failures. But commenting this out didn't cause additional failures!?! 388 # 389 vstring_to_var_dict = {} 390 vstring_to_bar_dict = {} 391 pstring_to_bar_dict = {} 392 for block in all_blocks_list: 393 for var_data in active_components_data_var[id(block)]: 394 variable_stream = StringIO() 395 var_data.to_string(ostream=variable_stream, verbose=False) 396 variable_string = variable_stream.getvalue() 397 variable_string = ' '+variable_string+' ' 398 vstring_to_var_dict[variable_string] = var_data 399 if output_fixed_variable_bounds or (not var_data.fixed): 400 vstring_to_bar_dict[variable_string] = \ 401 ' '+object_symbol_dictionary[id(var_data)]+' ' 402 else: 403 assert var_data.value is not None 404 vstring_to_bar_dict[variable_string] = \ 405 ftoa(var_data.value) 406 407 for param_data in mutable_param_gen(block): 408 param_stream = StringIO() 409 param_data.to_string(ostream=param_stream, verbose=False) 410 param_string = param_stream.getvalue() 411 412 param_string = ' '+param_string+' ' 413 pstring_to_bar_dict[param_string] = ftoa(param_data()) 414 415 # Equation Definition 416 output_file.write('c_e_FIX_ONE_VAR_CONST__: ONE_VAR_CONST__ == 1;\n'); 417 for constraint_data in itertools.chain(eqns, 418 r_o_eqns, 419 c_eqns, 420 l_eqns): 421 422 variables = OrderedSet() 423 #print(symbol_map.byObject.keys()) 424 eqn_body = expression_to_string(constraint_data.body, variables, smap=symbol_map) 425 #print(symbol_map.byObject.keys()) 426 referenced_variable_ids.update(variables) 427 428 if len(variables) == 0: 429 assert not skip_trivial_constraints 430 eqn_body += " + 0 * ONE_VAR_CONST__ " 431 432 # 7/29/14 CLH: 433 #FIXME: Baron doesn't handle many of the 434 # intrinsic_functions available in pyomo. The 435 # error message given by baron is also very 436 # weak. Either a function here to re-write 437 # unallowed expressions or a way to track solver 438 # capability by intrinsic_expression would be 439 # useful. 440 ########################## 441 442 con_symbol = symbol_map.byObject[id(constraint_data)] 443 output_file.write(str(con_symbol) + ': ') 444 445 # Fill in the left and right hand side (constants) of 446 # the equations 447 448 # Equality constraint 449 if constraint_data.equality: 450 eqn_lhs = '' 451 eqn_rhs = ' == ' + ftoa(constraint_data.upper) 452 453 # Greater than constraint 454 elif not constraint_data.has_ub(): 455 eqn_rhs = ' >= ' + ftoa(constraint_data.lower) 456 eqn_lhs = '' 457 458 # Less than constraint 459 elif not constraint_data.has_lb(): 460 eqn_rhs = ' <= ' + ftoa(constraint_data.upper) 461 eqn_lhs = '' 462 463 # Double-sided constraint 464 elif constraint_data.has_lb() and \ 465 constraint_data.has_ub(): 466 eqn_lhs = ftoa(constraint_data.lower) + \ 467 ' <= ' 468 eqn_rhs = ' <= ' + ftoa(constraint_data.upper) 469 470 eqn_string = eqn_lhs + eqn_body + eqn_rhs + ';\n' 471 output_file.write(eqn_string) 472 473 # 474 # OBJECTIVE 475 # 476 477 output_file.write("\nOBJ: ") 478 479 n_objs = 0 480 for block in all_blocks_list: 481 482 for objective_data in block.component_data_objects(Objective, 483 active=True, 484 sort=sorter, 485 descend_into=False): 486 487 n_objs += 1 488 if n_objs > 1: 489 raise ValueError("The BARON writer has detected multiple active " 490 "objective functions on model %s, but " 491 "currently only handles a single objective." 492 % (model.name)) 493 494 # create symbol 495 symbol_map.createSymbol(objective_data, c_labeler) 496 symbol_map.alias(objective_data, "__default_objective__") 497 498 if objective_data.is_minimizing(): 499 output_file.write("minimize ") 500 else: 501 output_file.write("maximize ") 502 503 variables = OrderedSet() 504 #print(symbol_map.byObject.keys()) 505 obj_string = expression_to_string(objective_data.expr, variables, smap=symbol_map) 506 #print(symbol_map.byObject.keys()) 507 referenced_variable_ids.update(variables) 508 509 510 output_file.write(obj_string+";\n\n") 511 #referenced_variable_ids.update(symbol_map.byObject.keys()) 512 513 return referenced_variable_ids, branching_priorities_suffixes 514 515 def __call__(self, 516 model, 517 output_filename, 518 solver_capability, 519 io_options): 520 if output_filename is None: 521 output_filename = model.name + ".bar" 522 523 # If the user provides a file name, we will use the opened file 524 # as a context manager to ensure it gets closed. If the user 525 # provided something else (e.g., a file-like object), we will 526 # use a nullcontext manager to prevent closing it. 527 if isinstance(output_filename, str): 528 output_file = open(output_filename, "w") 529 else: 530 output_file = nullcontext(output_filename) 531 532 with output_file as FILE: 533 symbol_map = self._write_bar_file( 534 model, FILE, solver_capability, io_options) 535 536 return output_filename, symbol_map 537 538 539 def _write_bar_file(self, 540 model, 541 output_file, 542 solver_capability, 543 io_options): 544 # Make sure not to modify the user's dictionary, they may be 545 # reusing it outside of this call 546 io_options = dict(io_options) 547 548 # NOTE: io_options is a simple dictionary of keyword-value 549 # pairs specific to this writer. 550 symbolic_solver_labels = \ 551 io_options.pop("symbolic_solver_labels", False) 552 labeler = io_options.pop("labeler", None) 553 554 # How much effort do we want to put into ensuring the 555 # LP file is written deterministically for a Pyomo model: 556 # 0 : None 557 # 1 : sort keys of indexed components (default) 558 # 2 : sort keys AND sort names (over declaration order) 559 file_determinism = io_options.pop("file_determinism", 1) 560 561 sorter = SortComponents.unsorted 562 if file_determinism >= 1: 563 sorter = sorter | SortComponents.indices 564 if file_determinism >= 2: 565 sorter = sorter | SortComponents.alphabetical 566 567 output_fixed_variable_bounds = \ 568 io_options.pop("output_fixed_variable_bounds", False) 569 570 # Skip writing constraints whose body section is fixed (i.e., 571 # no variables) 572 skip_trivial_constraints = \ 573 io_options.pop("skip_trivial_constraints", False) 574 575 # Note: Baron does not allow specification of runtime 576 # option outside of this file, so we add support 577 # for them here 578 solver_options = io_options.pop("solver_options", {}) 579 580 if len(io_options): 581 raise ValueError( 582 "ProblemWriter_baron_writer passed unrecognized io_options:\n\t" + 583 "\n\t".join("%s = %s" % (k,v) for k,v in io_options.items())) 584 585 if symbolic_solver_labels and (labeler is not None): 586 raise ValueError("Baron problem writer: Using both the " 587 "'symbolic_solver_labels' and 'labeler' " 588 "I/O options is forbidden") 589 590 # Make sure there are no strange ActiveComponents. The expression 591 # walker will handle strange things in constraints later. 592 model_ctypes = model.collect_ctypes(active=True) 593 invalids = set() 594 for t in (model_ctypes - valid_active_ctypes_minlp): 595 if issubclass(t, ActiveComponent): 596 invalids.add(t) 597 if len(invalids): 598 invalids = [t.__name__ for t in invalids] 599 raise RuntimeError( 600 "Unallowable active component(s) %s.\nThe BARON writer cannot " 601 "export models with this component type." % 602 ", ".join(invalids)) 603 604 # Process the options. Rely on baron to catch 605 # and reset bad option values 606 output_file.write("OPTIONS {\n") 607 summary_found = False 608 if len(solver_options): 609 for key, val in solver_options.items(): 610 if (key.lower() == 'summary'): 611 summary_found = True 612 if key.endswith("Name"): 613 output_file.write(key+": \""+str(val)+"\";\n") 614 else: 615 output_file.write(key+": "+str(val)+";\n") 616 if not summary_found: 617 # The 'summary option is defaulted to 0, so that no 618 # summary file is generated in the directory where the 619 # user calls baron. Check if a user explicitly asked for 620 # a summary file. 621 output_file.write("Summary: 0;\n") 622 output_file.write("}\n\n") 623 624 if symbolic_solver_labels: 625 # Note that the Var and Constraint labelers must use the 626 # same labeler, so that we can correctly detect name 627 # collisions (which can arise when we truncate the labels to 628 # the max allowable length. BARON requires all identifiers 629 # to start with a letter. We will (randomly) choose "s_" 630 # (for 'shortened') 631 v_labeler = c_labeler = ShortNameLabeler( 632 15, prefix='s_', suffix='_', caseInsensitive=True, 633 legalRegex='^[a-zA-Z]') 634 elif labeler is None: 635 v_labeler = NumericLabeler('x') 636 c_labeler = NumericLabeler('c') 637 else: 638 v_labeler = c_labeler = labeler 639 640 symbol_map = SymbolMap() 641 symbol_map.default_labeler = v_labeler 642 #sm_bySymbol = symbol_map.bySymbol 643 644 # Cache the list of model blocks so we don't have to call 645 # model.block_data_objects() many many times, which is slow 646 # for indexed blocks 647 all_blocks_list = list(model.block_data_objects(active=True, 648 sort=sorter, 649 descend_into=True)) 650 active_components_data_var = {} 651 #for block in all_blocks_list: 652 # tmp = active_components_data_var[id(block)] = \ 653 # list(obj for obj in block.component_data_objects(Var, 654 # sort=sorter, 655 # descend_into=False)) 656 # create_symbols_func(symbol_map, tmp, labeler) 657 658 # GAH: Not sure this is necessary, and also it would break for 659 # non-mutable indexed params so I am commenting out for now. 660 #for param_data in active_components_data(block, Param, sort=sorter): 661 #instead of checking if param_data.mutable: 662 #if not param_data.is_constant(): 663 # create_symbol_func(symbol_map, param_data, labeler) 664 665 #symbol_map_variable_ids = set(symbol_map.byObject.keys()) 666 #object_symbol_dictionary = symbol_map.byObject 667 668 # 669 # Go through the objectives and constraints and generate 670 # the output so that we can obtain the set of referenced 671 # variables. 672 # 673 equation_section_stream = StringIO() 674 referenced_variable_ids, branching_priorities_suffixes = \ 675 self._write_equations_section( 676 model, 677 equation_section_stream, 678 all_blocks_list, 679 active_components_data_var, 680 symbol_map, 681 c_labeler, 682 output_fixed_variable_bounds, 683 skip_trivial_constraints, 684 sorter) 685 686 # 687 # BINARY_VARIABLES, INTEGER_VARIABLES, POSITIVE_VARIABLES, VARIABLES 688 # 689 690 BinVars = [] 691 IntVars = [] 692 PosVars = [] 693 Vars = [] 694 for vid in referenced_variable_ids: 695 name = symbol_map.byObject[vid] 696 var_data = symbol_map.bySymbol[name]() 697 698 if var_data.is_continuous(): 699 if var_data.has_lb() and (value(var_data.lb) >= 0): 700 TypeList = PosVars 701 else: 702 TypeList = Vars 703 elif var_data.is_binary(): 704 TypeList = BinVars 705 elif var_data.is_integer(): 706 TypeList = IntVars 707 else: 708 assert False 709 TypeList.append(name) 710 711 if len(BinVars) > 0: 712 BinVars.sort() 713 output_file.write('BINARY_VARIABLES ') 714 output_file.write(", ".join(BinVars)) 715 output_file.write(';\n\n') 716 717 if len(IntVars) > 0: 718 IntVars.sort() 719 output_file.write('INTEGER_VARIABLES ') 720 output_file.write(", ".join(IntVars)) 721 output_file.write(';\n\n') 722 723 PosVars.append('ONE_VAR_CONST__') 724 PosVars.sort() 725 output_file.write('POSITIVE_VARIABLES ') 726 output_file.write(", ".join(PosVars)) 727 output_file.write(';\n\n') 728 729 if len(Vars) > 0: 730 Vars.sort() 731 output_file.write('VARIABLES ') 732 output_file.write(", ".join(Vars)) 733 output_file.write(';\n\n') 734 735 # 736 # LOWER_BOUNDS 737 # 738 739 lbounds = {} 740 for vid in referenced_variable_ids: 741 name = symbol_map.byObject[vid] 742 var_data = symbol_map.bySymbol[name]() 743 744 if var_data.fixed: 745 if output_fixed_variable_bounds: 746 var_data_lb = ftoa(var_data.value) 747 else: 748 var_data_lb = None 749 else: 750 var_data_lb = None 751 if var_data.has_lb(): 752 var_data_lb = ftoa(var_data.lb) 753 754 if var_data_lb is not None: 755 name_to_output = symbol_map.getSymbol(var_data) 756 lbounds[name_to_output] = '%s: %s;\n' % ( 757 name_to_output, var_data_lb) 758 759 if len(lbounds) > 0: 760 output_file.write("LOWER_BOUNDS{\n") 761 output_file.write("".join( lbounds[key] for key in sorted(lbounds.keys()) ) ) 762 output_file.write("}\n\n") 763 lbounds = None 764 765 # 766 # UPPER_BOUNDS 767 # 768 769 ubounds = {} 770 for vid in referenced_variable_ids: 771 name = symbol_map.byObject[vid] 772 var_data = symbol_map.bySymbol[name]() 773 774 if var_data.fixed: 775 if output_fixed_variable_bounds: 776 var_data_ub = ftoa(var_data.value) 777 else: 778 var_data_ub = None 779 else: 780 var_data_ub = None 781 if var_data.has_ub(): 782 var_data_ub = ftoa(var_data.ub) 783 784 if var_data_ub is not None: 785 name_to_output = symbol_map.getSymbol(var_data) 786 ubounds[name_to_output] = '%s: %s;\n' % ( 787 name_to_output, var_data_ub) 788 789 if len(ubounds) > 0: 790 output_file.write("UPPER_BOUNDS{\n") 791 output_file.write("".join( ubounds[key] for key in sorted(ubounds.keys()) ) ) 792 output_file.write("}\n\n") 793 ubounds = None 794 795 # 796 # BRANCHING_PRIORITIES 797 # 798 799 # Specifying priorities requires that the pyomo model has established an 800 # EXTERNAL, float suffix called 'branching_priorities' on the model 801 # object, indexed by the relevant variable 802 BranchingPriorityHeader = False 803 for suffix in branching_priorities_suffixes: 804 for var, priority in suffix.items(): 805 if var.is_indexed(): 806 var_iter = var.values() 807 else: 808 var_iter = (var,) 809 for var_data in var_iter: 810 if id(var_data) not in referenced_variable_ids: 811 continue 812 if priority is not None: 813 if not BranchingPriorityHeader: 814 output_file.write('BRANCHING_PRIORITIES{\n') 815 BranchingPriorityHeader = True 816 output_file.write( "%s: %s;\n" % ( 817 symbol_map.getSymbol(var_data), priority)) 818 819 if BranchingPriorityHeader: 820 output_file.write("}\n\n") 821 822 # 823 # Now write the objective and equations section 824 # 825 output_file.write(equation_section_stream.getvalue()) 826 827 # 828 # STARTING_POINT 829 # 830 output_file.write('STARTING_POINT{\nONE_VAR_CONST__: 1;\n') 831 tmp = {} 832 for vid in referenced_variable_ids: 833 name = symbol_map.byObject[vid] 834 var_data = symbol_map.bySymbol[name]() 835 836 starting_point = var_data.value 837 if starting_point is not None: 838 var_name = symbol_map.getSymbol(var_data) 839 tmp[var_name] = "%s: %s;\n" % ( 840 var_name, ftoa(starting_point)) 841 842 output_file.write("".join( tmp[key] for key in sorted(tmp.keys()) )) 843 output_file.write('}\n\n') 844 845 return symbol_map 846 847